From ca24fb4b6f7a7b34002bc4cc27b7b2de8b2b77cf Mon Sep 17 00:00:00 2001 From: chasem Date: Mon, 8 Sep 2025 12:05:54 -0500 Subject: [PATCH 01/49] resolving requests missing types --- .gitignore | 10 +- .pre-commit-config.yaml | 1 + pyproject.toml | 3 + tfbpapi/AbstractAPI.py | 2 +- tfbpapi/AbstractHfAPI.py | 410 +++++++++++++++++++++++++++ tfbpapi/BindingManualQCAPI.py | 2 +- tfbpapi/DtoAPI.py | 2 +- tfbpapi/ExpressionManualQCAPI.py | 2 +- tfbpapi/HfCacheManager.py | 271 ++++++++++++++++++ tfbpapi/RankResponseAPI.py | 2 +- tfbpapi/UnivariateModelsAPI.py | 2 +- tfbpapi/tests/conftest.py | 26 ++ tfbpapi/tests/conftests.py | 0 tfbpapi/tests/data/cache_info.pkl | Bin 0 -> 123975 bytes tfbpapi/tests/test_HfCacheManager.py | 55 ++++ tfbpapi/tests/test_ParamsDict.py | 2 +- 16 files changed, 781 insertions(+), 9 deletions(-) create mode 100644 tfbpapi/AbstractHfAPI.py create mode 100644 tfbpapi/HfCacheManager.py create mode 100644 tfbpapi/tests/conftest.py delete mode 100644 tfbpapi/tests/conftests.py create mode 100644 tfbpapi/tests/data/cache_info.pkl create mode 100644 tfbpapi/tests/test_HfCacheManager.py diff --git a/.gitignore b/.gitignore index 3eeb70f..cf9aeb9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,14 @@ -#mac files +# claude +CLAUDE.md +.claude/ +claude_output/ +claude_logs/ + +# mac files **/.DS_Store # Dataset directory -data/ +./data/ # logs **/logs/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0961d95..e8284d5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -62,6 +62,7 @@ repos: rev: v1.15.0 hooks: - id: mypy + additional_dependencies: [types-requests] - repo: https://github.com/markdownlint/markdownlint rev: v0.12.0 diff --git a/pyproject.toml b/pyproject.toml index 27e077a..56c6f30 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,12 +18,15 @@ aioresponses = "^0.7.8" numpy = "^2.2.5" dotenv = "^0.9.9" pandas = "^2.3.1" +huggingface-hub = "^0.34.4" +types-requests = "^2.32.4.20250809" [tool.poetry.group.dev.dependencies] pytest = "^8.3.5" pytest-snapshot = "^0.9.0" pytest-asyncio = "^0.26.0" +types-requests = "^2.32.4.20250809" [tool.pytest.ini_options] diff --git a/tfbpapi/AbstractAPI.py b/tfbpapi/AbstractAPI.py index 19c4eb6..39bd10a 100644 --- a/tfbpapi/AbstractAPI.py +++ b/tfbpapi/AbstractAPI.py @@ -5,7 +5,7 @@ from typing import Any import pandas as pd -import requests # type: ignore +import requests from tfbpapi.Cache import Cache from tfbpapi.ParamsDict import ParamsDict diff --git a/tfbpapi/AbstractHfAPI.py b/tfbpapi/AbstractHfAPI.py new file mode 100644 index 0000000..14db82f --- /dev/null +++ b/tfbpapi/AbstractHfAPI.py @@ -0,0 +1,410 @@ +import logging +import os +from abc import ABC, abstractmethod +from collections.abc import Mapping +from pathlib import Path +from typing import Any, Literal + +import requests +from huggingface_hub import hf_hub_download, repo_info, snapshot_download +from huggingface_hub.constants import HF_HUB_CACHE +from requests import HTTPError + + +class RepoTooLargeError(ValueError): + """Raised when repository exceeds auto-download threshold.""" + + pass + + +class AbstractHfAPI(ABC): + """Abstract base class for creating Hugging Face API clients.""" + + # TODO: can revision be set to "latest" by default? + def __init__( + self, + repo_id: str, + repo_type: Literal["model", "dataset", "space"] = "dataset", + token: str | None = None, + cache_dir: str | Path = HF_HUB_CACHE, + ): + """ + Initialize the HF-backed API client. + + :param repo_id: The repo identifier on HF (e.g., "user/dataset"). Eg, + "BrentLab/yeast_genome_resources" + :param token: Optional. Not necessary for public repos. May be set via the + HF_TOKEN environment variable. + :param repo_type: One of {"model", "dataset", "space"}. Defaults to "dataset". + :param cache_dir: HF cache_dir for hf_hub_download and snapshot_download (see + huggingface_hub docs). May be passed via the HF_CACHE_DIR environmental + variable. If not set, the default HF cache directory is used. + + """ + self.logger = logging.getLogger(self.__class__.__name__) + + # Let user input override env var, but use the env var if available + resolved_token = token or os.getenv("HF_TOKEN", None) + resolved_cache_dir = cache_dir or os.getenv("HF_CACHE_DIR", HF_HUB_CACHE) + if isinstance(resolved_cache_dir, str): + resolved_cache_dir = Path(resolved_cache_dir) + + self.token = resolved_token + self.repo_id = repo_id + self.repo_type = repo_type + self.cache_dir = resolved_cache_dir + + @property + def token(self) -> str | None: + return self._token + + @token.setter + def token(self, value: str | None) -> None: + # TODO: if a token is provided, then validate that it works. Only necessary + # if token is not None of course + self._token = value + + @property + def repo_id(self) -> str: + return self._repo_id + + @repo_id.setter + def repo_id(self, value: str) -> None: + self._repo_id = value + try: + self._get_dataset_size(value) + except (HTTPError, ValueError) as e: + self.logger.warning(f"Could not validate repo_id {value}: {e}") + self.logger.info( + "Repo validation skipped - will be checked on first download" + ) + + @property + def cache_dir(self) -> Path: + return self._cache_dir + + @cache_dir.setter + def cache_dir(self, value: str | Path) -> None: + """Set the cache directory for huggingface_hub downloads.""" + path = Path(value) + if not path.exists(): + raise FileNotFoundError(f"Cache directory does not exist: {path}") + self._cache_dir = path + + @property + def size(self) -> dict[str, Any] | None: + """Size information from the HF Dataset Server API (if available).""" + return self._size if hasattr(self, "_size") else None + + @size.setter + def size(self, value: dict[str, Any]) -> None: + self._size = value + + @property + def snapshot_path(self) -> Path | None: + """Path to the last downloaded snapshot (if any).""" + return self._snapshot_path if hasattr(self, "_snapshot_path") else None + + @snapshot_path.setter + def snapshot_path(self, value: str | Path | None) -> None: + if value is None: + self._snapshot_path = None + else: + self._snapshot_path = Path(value) + + def _get_dataset_size(self, repo_id: str | None = None) -> None: + """ + Get dataset size information from HuggingFace Dataset Server API. + + :returns: Dict containing size information with additional metadata about + completeness + :raises requests.HTTPError: If the API request fails + :raises ValueError: If the dataset doesn't exist or isn't accessible + + """ + repo_id = repo_id or self.repo_id + url = f"https://datasets-server.huggingface.co/size?dataset={repo_id}" + + headers = {} + if self.token: + headers["Authorization"] = f"Bearer {self.token}" + + response = requests.get(url, headers=headers) + response.raise_for_status() + + data = response.json() + + # Check if size determination was partial + is_partial = data.get("partial", False) + + if is_partial: + self.logger.warning( + f"Size information for {repo_id} is incomplete. " + "The dataset is too large for complete size determination. " + "Reported numbers may be lower than actual size." + ) + + # Add metadata about completeness to the response + if "size" in data and "dataset" in data["size"]: + data["size"]["dataset"]["size_determination_complete"] = not is_partial + data["size"]["dataset"]["size_warning"] = ( + "Partial size only - actual dataset may be larger" + if is_partial + else "Complete size information" + ) + + self.size = data + + def _check_repo_size_and_decide_strategy( + self, + auto_download_threshold_mb: float, + force_full_download: bool, + allow_patterns: list[str] | str | None, + ignore_patterns: list[str] | str | None, + **kwargs, + ) -> tuple[list[str] | str | None, list[str] | str | None]: + """ + Check repo size and decide download strategy. + + Returns: + Tuple of (allow_patterns, ignore_patterns) to use for download + + """ + if force_full_download or auto_download_threshold_mb <= 0: + return None, None + + try: + # Get repo info to estimate size + revision = kwargs.get("revision") + if revision is not None: + info = repo_info( + repo_id=self.repo_id, + repo_type=self.repo_type, + token=self.token, + revision=str(revision), + ) + else: + info = repo_info( + repo_id=self.repo_id, + repo_type=self.repo_type, + token=self.token, + ) + + # Estimate total size from siblings (files in repo) + total_size_bytes = sum( + getattr(sibling, "size", 0) or 0 + for sibling in getattr(info, "siblings", []) + ) + total_size_mb = total_size_bytes / (1024 * 1024) + + self.logger.info(f"Estimated repo size: {total_size_mb:.2f} MB") + + # If small enough, download everything + if total_size_mb <= auto_download_threshold_mb: + self.logger.info( + f"Repo size ({total_size_mb:.2f} MB) under threshold " + f"({auto_download_threshold_mb} MB), downloading full repo" + ) + return None, None + else: + raise RepoTooLargeError( + f"Repo size ({total_size_mb:.2f} MB) exceeds threshold " + f"({auto_download_threshold_mb} MB). Use a selective download " + "method via `files`, `allow_patterns` or `ignore_patterns`, " + "or increase `auto_download_threshold_mb` and try again." + ) + + except Exception as e: + self.logger.warning( + f"Could not determine repo size: {e}. Proceeding with " + f"pattern-based download." + ) + return allow_patterns, ignore_patterns + + def _download_single_file( + self, + filename: str, + dry_run: bool = False, + **kwargs, + ) -> Path: + """ + Download a single file using hf_hub_download. + + :param filename: File to download + :param dry_run: If True, log what would be downloaded without downloading + :param kwargs: Additional arguments passed directly to hf_hub_download + :return: Path to the downloaded file + + """ + self.logger.info(f"Downloading single file: {filename}") + + if dry_run: + self.logger.info(f"[DRY RUN] Would download {filename} from {self.repo_id}") + return Path("dry_run_path") + + # Build base arguments + hf_kwargs = { + "repo_id": self.repo_id, + "repo_type": self.repo_type, + "filename": filename, + "token": self.token, + **kwargs, # User kwargs override defaults + } + + # Set cache_dir only if local_dir not specified + if "local_dir" not in hf_kwargs and self.cache_dir is not None: + hf_kwargs["cache_dir"] = str(self.cache_dir) + + # Ensure string conversion for Path-like arguments + if "local_dir" in hf_kwargs and hf_kwargs["local_dir"] is not None: + hf_kwargs["local_dir"] = str(hf_kwargs["local_dir"]) + if "cache_dir" in hf_kwargs and hf_kwargs["cache_dir"] is not None: + hf_kwargs["cache_dir"] = str(hf_kwargs["cache_dir"]) + + file_path = hf_hub_download(**hf_kwargs) + self._snapshot_path = Path(file_path).parent + return Path(file_path) + + def _download_snapshot( + self, + dry_run: bool = False, + **kwargs, + ) -> Path: + """ + Download repository snapshot using snapshot_download. + + :param dry_run: If True, log what would be downloaded without downloading + :param kwargs: Additional arguments passed directly to snapshot_download + :return: Path to the downloaded snapshot + + """ + # Log download plan + if dry_run: + self.logger.info(f"[DRY RUN] Would download from {self.repo_id}:") + self.logger.info(f" - allow_patterns: {kwargs.get('allow_patterns')}") + self.logger.info(f" - ignore_patterns: {kwargs.get('ignore_patterns')}") + return Path("dry_run_path") + + # Execute snapshot download + self.logger.info( + f"Downloading repo snapshot with patterns - " + f"allow: {kwargs.get('allow_patterns')}, " + f"ignore: {kwargs.get('ignore_patterns')}" + ) + + # Build base arguments + # note that kwargs passed into this method will override defaults, + # including repo_id, etc + snapshot_kwargs = { + "repo_id": self.repo_id, + "repo_type": self.repo_type, + "token": self.token, + **kwargs, + } + + # Set cache_dir only if local_dir not specified and cache_dir wasn't passed in + # by user + if ( + "local_dir" not in snapshot_kwargs + and "cache_dir" not in snapshot_kwargs + and self.cache_dir is not None + ): + snapshot_kwargs["cache_dir"] = str(self.cache_dir) + + # if allow_patterns or ignore_patterns are strings, convert to list + for pattern_key in ["allow_patterns", "ignore_patterns"]: + if ( + pattern_key in snapshot_kwargs + and snapshot_kwargs[pattern_key] is not None + ): + patterns = snapshot_kwargs[pattern_key] + if isinstance(patterns, str): + snapshot_kwargs[pattern_key] = [patterns] + + snapshot_path = snapshot_download(**snapshot_kwargs) + self.snapshot_path = Path(snapshot_path) + return self.snapshot_path + + def download( + self, + *, + files: list[str] | str | None = None, + force_full_download: bool = False, + auto_download_threshold_mb: float = 100.0, + dry_run: bool = False, + **kwargs, + ) -> Path: + """ + Download dataset by file, patterns or if the dataset is small enough, the entire + repo. + + :param files: Specific file(s) to download. If provided, uses hf_hub_download + :param force_full_download: If True, always download entire repo regardless of + size + :param auto_download_threshold_mb: Auto-download full repo if estimated size < + this (MB) + :param dry_run: If True, log what would be downloaded without actually + downloading + :param kwargs: Additional arguments passed to hf_hub_download or + snapshot_download. Common args: revision, local_dir, cache_dir, + local_files_only, allow_patterns, ignore_patterns, etc. + :return: Path to downloaded content (file or directory). + + """ + # Handle single file download + if files is not None: + if isinstance(files, str): + files = [files] + + if len(files) == 1: + return self._download_single_file( + filename=files[0], + dry_run=dry_run, + **kwargs, + ) + else: + # Multiple specific files - use filtered snapshot_download + self.logger.info(f"Downloading specific files: {files}") + if kwargs.get("allow_patterns") is not None: + self.logger.warning( + "`allow_patterns` will be overridden by `files` argument" + ) + kwargs["allow_patterns"] = files + + # Check repo size and adjust download strategy if needed + allow_patterns, ignore_patterns = self._check_repo_size_and_decide_strategy( + auto_download_threshold_mb=auto_download_threshold_mb, + force_full_download=force_full_download, + allow_patterns=kwargs.get("allow_patterns"), + ignore_patterns=kwargs.get("ignore_patterns"), + **kwargs, + ) + + # Update kwargs with determined patterns + if allow_patterns is not None: + kwargs["allow_patterns"] = allow_patterns + if ignore_patterns is not None: + kwargs["ignore_patterns"] = ignore_patterns + + # Execute snapshot download + return self._download_snapshot(dry_run=dry_run, **kwargs) + + @abstractmethod + def parse_datacard(self, *args: Any, **kwargs: Any) -> Mapping[str, Any]: + """ + Abstract method to parse a datacard from the downloaded content. + + Must be implemented by subclasses. + + """ + raise NotImplementedError("Subclasses must implement this method.") + + @abstractmethod + def query(self, *args: Any, **kwargs: Any) -> Any: + """ + Abstract method to query the API. + + Must be implemented by subclasses. + + """ + raise NotImplementedError("Subclasses must implement this method.") diff --git a/tfbpapi/BindingManualQCAPI.py b/tfbpapi/BindingManualQCAPI.py index df4169b..02b6a21 100644 --- a/tfbpapi/BindingManualQCAPI.py +++ b/tfbpapi/BindingManualQCAPI.py @@ -2,7 +2,7 @@ from typing import Any import pandas as pd -import requests # type: ignore +import requests from tfbpapi.AbstractRecordsOnlyAPI import AbstractRecordsOnlyAPI diff --git a/tfbpapi/DtoAPI.py b/tfbpapi/DtoAPI.py index bc8d404..b8780b6 100644 --- a/tfbpapi/DtoAPI.py +++ b/tfbpapi/DtoAPI.py @@ -6,7 +6,7 @@ import aiohttp import pandas as pd -import requests # type: ignore +import requests from tfbpapi.AbstractRecordsOnlyAPI import AbstractRecordsOnlyAPI diff --git a/tfbpapi/ExpressionManualQCAPI.py b/tfbpapi/ExpressionManualQCAPI.py index 80023e6..15ff863 100644 --- a/tfbpapi/ExpressionManualQCAPI.py +++ b/tfbpapi/ExpressionManualQCAPI.py @@ -2,7 +2,7 @@ from typing import Any import pandas as pd -import requests # type: ignore +import requests from tfbpapi.AbstractRecordsOnlyAPI import AbstractRecordsOnlyAPI diff --git a/tfbpapi/HfCacheManager.py b/tfbpapi/HfCacheManager.py new file mode 100644 index 0000000..95a0e02 --- /dev/null +++ b/tfbpapi/HfCacheManager.py @@ -0,0 +1,271 @@ +import logging +from datetime import datetime, timedelta +from typing import Literal + +from huggingface_hub import scan_cache_dir +from huggingface_hub.utils import DeleteCacheStrategy + + +class HFCacheManager: + """Cache memory management for Hugging Face Hub cache.""" + + def __init__(self, logger: logging.Logger | None = None): + self.logger = logger or logging.getLogger(__name__) + + def clean_cache_by_age( + self, + max_age_days: int = 30, + dry_run: bool = True, + ) -> DeleteCacheStrategy: + """ + Clean cache entries older than specified age. + + :param max_age_days: Remove revisions older than this many days + :param dry_run: If True, show what would be deleted without executing + size_threshold: Only delete if total cache size exceeds this (e.g., "10GB") + + :return: DeleteCacheStrategy object that can be executed + + """ + cache_info = scan_cache_dir() + cutoff_date = datetime.now() - timedelta(days=max_age_days) + + old_revisions = [] + for repo in cache_info.repos: + for revision in repo.revisions: + # Check if revision is older than cutoff + revision_date = datetime.fromtimestamp(revision.last_modified) + if revision_date < cutoff_date: + old_revisions.append(revision.commit_hash) + self.logger.debug( + f"Marking for deletion: {revision.commit_hash} " + f"(last modified: {revision.last_modified})" + ) + + if not old_revisions: + self.logger.info("No old revisions found to delete") + # return None + + delete_strategy = cache_info.delete_revisions(*old_revisions) + + self.logger.info( + f"Found {len(old_revisions)} old revisions. " + f"Will free {delete_strategy.expected_freed_size_str}" + ) + + if not dry_run: + delete_strategy.execute() + self.logger.info( + f"Cache cleanup completed. Freed " + f"{delete_strategy.expected_freed_size_str}" + ) + else: + self.logger.info("Dry run completed. Use dry_run=False to execute deletion") + + return delete_strategy + + def clean_cache_by_size( + self, + target_size: str, + strategy: Literal[ + "oldest_first", "largest_first", "least_used" + ] = "oldest_first", + dry_run: bool = True, + ) -> DeleteCacheStrategy: + """ + Clean cache to reach target size by removing revisions. + + :param target_size: Target cache size (e.g., "5GB", "500MB") + :param strategy: Deletion strategy - "oldest_first", "largest_first", + "least_used" + :param dry_run: If True, show what would be deleted without executing + + :return: DeleteCacheStrategy object that can be executed + + """ + cache_info = scan_cache_dir() + current_size = cache_info.size_on_disk + target_bytes = self._parse_size_string(target_size) + + if current_size <= target_bytes: + self.logger.info( + f"Cache size ({cache_info.size_on_disk_str}) already below " + f"target ({target_size})" + ) + + bytes_to_free = current_size - target_bytes + + # Get all revisions sorted by strategy + all_revisions = [] + for repo in cache_info.repos: + for revision in repo.revisions: + all_revisions.append(revision) + + # Sort revisions based on strategy + if strategy == "oldest_first": + all_revisions.sort(key=lambda r: r.last_modified) + elif strategy == "largest_first": + all_revisions.sort(key=lambda r: r.size_on_disk, reverse=True) + elif strategy == "least_used": + # Use last_modified as proxy for usage + all_revisions.sort(key=lambda r: r.last_modified) + else: + raise ValueError(f"Unknown strategy: {strategy}") + + # Select revisions to delete + revisions_to_delete = [] + freed_bytes = 0 + + for revision in all_revisions: + if freed_bytes >= bytes_to_free: + break + revisions_to_delete.append(revision.commit_hash) + freed_bytes += revision.size_on_disk + + if not revisions_to_delete: + self.logger.warning("No revisions selected for deletion") + + delete_strategy = cache_info.delete_revisions(*revisions_to_delete) + + self.logger.info( + f"Selected {len(revisions_to_delete)} revisions for deletion. " + f"Will free {delete_strategy.expected_freed_size_str}" + ) + + if not dry_run: + delete_strategy.execute() + self.logger.info( + f"Cache cleanup completed. Freed " + f"{delete_strategy.expected_freed_size_str}" + ) + else: + self.logger.info("Dry run completed. Use dry_run=False to execute deletion") + + return delete_strategy + + def clean_unused_revisions( + self, keep_latest: int = 2, dry_run: bool = True + ) -> DeleteCacheStrategy: + """ + Clean unused revisions, keeping only the latest N revisions per repo. + + :param keep_latest: Number of latest revisions to keep per repo + :param dry_run: If True, show what would be deleted without executing + :return: DeleteCacheStrategy object that can be executed + + """ + cache_info = scan_cache_dir() + revisions_to_delete = [] + + for repo in cache_info.repos: + # Sort revisions by last modified (newest first) + sorted_revisions = sorted( + repo.revisions, key=lambda r: r.last_modified, reverse=True + ) + + # Keep the latest N, mark the rest for deletion + if len(sorted_revisions) > keep_latest: + old_revisions = sorted_revisions[keep_latest:] + for revision in old_revisions: + revisions_to_delete.append(revision.commit_hash) + self.logger.debug( + f"Marking old revision for deletion: {repo.repo_id} - " + f"{revision.commit_hash}" + ) + + delete_strategy = cache_info.delete_revisions(*revisions_to_delete) + + self.logger.info( + f"Found {len(revisions_to_delete)} unused revisions. " + f"Will free {delete_strategy.expected_freed_size_str}" + ) + + if not dry_run: + delete_strategy.execute() + self.logger.info( + f"Cache cleanup completed. Freed " + f"{delete_strategy.expected_freed_size_str}" + ) + else: + self.logger.info("Dry run completed. Use dry_run=False to execute deletion") + + return delete_strategy + + def auto_clean_cache( + self, + max_age_days: int = 30, + max_total_size: str = "10GB", + keep_latest_per_repo: int = 2, + dry_run: bool = True, + ) -> list[DeleteCacheStrategy]: + """ + Automated cache cleaning with multiple strategies. + + :param max_age_days: Remove revisions older than this + :param max_total_size: Target maximum cache size + :param keep_latest_per_repo: Keep this many latest revisions per repo + :param dry_run: If True, show what would be deleted without executing + :return: List of DeleteCacheStrategy objects that were executed + + """ + strategies_executed = [] + + self.logger.info("Starting automated cache cleanup...") + + # Step 1: Remove very old revisions + strategy = self.clean_cache_by_age(max_age_days=max_age_days, dry_run=dry_run) + if strategy: + strategies_executed.append(strategy) + + # Step 2: Remove unused revisions (keep only latest per repo) + strategy = self.clean_unused_revisions( + keep_latest=keep_latest_per_repo, dry_run=dry_run + ) + if strategy: + strategies_executed.append(strategy) + + # Step 3: If still over size limit, remove more aggressively + cache_info = scan_cache_dir() + if cache_info.size_on_disk > self._parse_size_string(max_total_size): + strategy = self.clean_cache_by_size( + target_size=max_total_size, strategy="oldest_first", dry_run=dry_run + ) + if strategy: + strategies_executed.append(strategy) + + total_freed = sum(s.expected_freed_size for s in strategies_executed) + self.logger.info( + f"Automated cleanup complete. Total freed: " + f"{self._format_bytes(total_freed)}" + ) + + return strategies_executed + + def _parse_size_string(self, size_str: str) -> int: + """Parse size string like '10GB' to bytes.""" + size_str = size_str.upper().strip() + + # Check longer units first to avoid partial matches + multipliers = {"TB": 1024**4, "GB": 1024**3, "MB": 1024**2, "KB": 1024, "B": 1} + + for unit, multiplier in multipliers.items(): + if size_str.endswith(unit): + number = float(size_str[: -len(unit)]) + return int(number * multiplier) + + # If no unit specified, assume bytes + return int(size_str) + + def _format_bytes(self, bytes_size: int) -> str: + """Format bytes into human readable string.""" + if bytes_size == 0: + return "0B" + + # iterate over common units, dividing by 1024 each time, to find an + # appropriate unit. Default to TB if the size is very large + size = float(bytes_size) + for unit in ["B", "KB", "MB", "GB", "TB"]: + if size < 1024.0: + return f"{size:.1f}{unit}" + size /= 1024.0 + return f"{size:.1f}TB" diff --git a/tfbpapi/RankResponseAPI.py b/tfbpapi/RankResponseAPI.py index 6ed3330..e12e0f2 100644 --- a/tfbpapi/RankResponseAPI.py +++ b/tfbpapi/RankResponseAPI.py @@ -8,7 +8,7 @@ import aiohttp import pandas as pd -from requests import Response, delete, post # type: ignore +from requests import Response, delete, post from requests_toolbelt import MultipartEncoder from tfbpapi.AbstractRecordsAndFilesAPI import ( diff --git a/tfbpapi/UnivariateModelsAPI.py b/tfbpapi/UnivariateModelsAPI.py index d3bc632..57cc303 100644 --- a/tfbpapi/UnivariateModelsAPI.py +++ b/tfbpapi/UnivariateModelsAPI.py @@ -6,7 +6,7 @@ import aiohttp import pandas as pd -import requests # type: ignore +import requests from tfbpapi.AbstractRecordsOnlyAPI import AbstractRecordsOnlyAPI diff --git a/tfbpapi/tests/conftest.py b/tfbpapi/tests/conftest.py new file mode 100644 index 0000000..47b7b94 --- /dev/null +++ b/tfbpapi/tests/conftest.py @@ -0,0 +1,26 @@ +import pickle +from pathlib import Path +from unittest.mock import patch + +import pytest + + +@pytest.fixture +def mock_cache_info(): + """Load real cache data from pickle file.""" + cache_file = Path(__file__).parent / "data" / "cache_info.pkl" + + if not cache_file.exists(): + pytest.skip( + "test_cache_data.pkl not found. Run cache data generation script first." + ) + + with open(cache_file, "rb") as f: + return pickle.load(f) + + +@pytest.fixture +def mock_scan_cache_dir(mock_cache_info): + """Mock scan_cache_dir to return our pickled cache data.""" + with patch("huggingface_hub.scan_cache_dir", return_value=mock_cache_info): + yield mock_cache_info diff --git a/tfbpapi/tests/conftests.py b/tfbpapi/tests/conftests.py deleted file mode 100644 index e69de29..0000000 diff --git a/tfbpapi/tests/data/cache_info.pkl b/tfbpapi/tests/data/cache_info.pkl new file mode 100644 index 0000000000000000000000000000000000000000..7906d8e30dd63514636537b3083c3ee20c7995e7 GIT binary patch literal 123975 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 literal 0 HcmV?d00001 diff --git a/tfbpapi/tests/test_HfCacheManager.py b/tfbpapi/tests/test_HfCacheManager.py new file mode 100644 index 0000000..0a4e472 --- /dev/null +++ b/tfbpapi/tests/test_HfCacheManager.py @@ -0,0 +1,55 @@ +from tfbpapi.HfCacheManager import HFCacheManager + + +def test_parse_size_string(): + """Test size string parsing.""" + cache_manager = HFCacheManager() + assert cache_manager._parse_size_string("10KB") == 10 * 1024 + assert cache_manager._parse_size_string("5MB") == 5 * 1024**2 + assert cache_manager._parse_size_string("2GB") == 2 * 1024**3 + assert cache_manager._parse_size_string("1TB") == 1 * 1024**4 + assert cache_manager._parse_size_string("500") == 500 + + +def test_clean_cache_by_age(mock_scan_cache_dir): + """Test age-based cache cleaning.""" + + cache_manager = HFCacheManager() + + # This will use the mocked scan_cache_dir + strategy = cache_manager.clean_cache_by_age(max_age_days=1, dry_run=True) + + # Test your logic + assert strategy is not None or len(mock_scan_cache_dir.repos) == 0 + if strategy: + assert len(strategy.repos) >= 0 + assert strategy.expected_freed_size >= 0 + + +def test_clean_cache_by_size(mock_scan_cache_dir): + """Test size-based cache cleaning.""" + + cache_manager = HFCacheManager() + + # Test with a very small target to force cleanup + strategy = cache_manager.clean_cache_by_size(target_size="1GB", dry_run=True) + + if mock_scan_cache_dir.size_on_disk > 1024: # If cache has data + assert strategy is not None + assert len(strategy.repos) > 0 + + +def test_auto_clean_cache(mock_scan_cache_dir): + """Test automated cache cleaning.""" + + cache_manager = HFCacheManager() + + strategies = cache_manager.auto_clean_cache( + max_age_days=1, + max_total_size="1KB", + keep_latest_per_repo=1, + dry_run=True, + ) + + # Should return a list of strategies + assert isinstance(strategies, list) diff --git a/tfbpapi/tests/test_ParamsDict.py b/tfbpapi/tests/test_ParamsDict.py index ee5a246..cccaeda 100644 --- a/tfbpapi/tests/test_ParamsDict.py +++ b/tfbpapi/tests/test_ParamsDict.py @@ -1,5 +1,5 @@ import pytest -import requests # type: ignore +import requests import responses from tfbpapi.ParamsDict import ParamsDict From c75f6d39c9a34a6af5e2b5f766f7c507a343248d Mon Sep 17 00:00:00 2001 From: chasem Date: Mon, 8 Sep 2025 14:37:39 -0500 Subject: [PATCH 02/49] adding tests for AbstractHfAPI --- tfbpapi/AbstractHfAPI.py | 247 +++++++--------- tfbpapi/tests/test_AbstractHfAPI.py | 435 ++++++++++++++++++++++++++++ 2 files changed, 540 insertions(+), 142 deletions(-) create mode 100644 tfbpapi/tests/test_AbstractHfAPI.py diff --git a/tfbpapi/AbstractHfAPI.py b/tfbpapi/AbstractHfAPI.py index 14db82f..58e0884 100644 --- a/tfbpapi/AbstractHfAPI.py +++ b/tfbpapi/AbstractHfAPI.py @@ -6,27 +6,27 @@ from typing import Any, Literal import requests -from huggingface_hub import hf_hub_download, repo_info, snapshot_download +from huggingface_hub import hf_hub_download, snapshot_download from huggingface_hub.constants import HF_HUB_CACHE from requests import HTTPError +# Constants +MB_TO_BYTES = 1024 * 1024 + class RepoTooLargeError(ValueError): """Raised when repository exceeds auto-download threshold.""" - pass - class AbstractHfAPI(ABC): """Abstract base class for creating Hugging Face API clients.""" - # TODO: can revision be set to "latest" by default? def __init__( self, repo_id: str, repo_type: Literal["model", "dataset", "space"] = "dataset", token: str | None = None, - cache_dir: str | Path = HF_HUB_CACHE, + cache_dir: str | Path | None = None, ): """ Initialize the HF-backed API client. @@ -39,20 +39,17 @@ def __init__( :param cache_dir: HF cache_dir for hf_hub_download and snapshot_download (see huggingface_hub docs). May be passed via the HF_CACHE_DIR environmental variable. If not set, the default HF cache directory is used. + :raises FileNotFoundError: If the specified cache_dir does not exist. """ self.logger = logging.getLogger(self.__class__.__name__) - # Let user input override env var, but use the env var if available - resolved_token = token or os.getenv("HF_TOKEN", None) - resolved_cache_dir = cache_dir or os.getenv("HF_CACHE_DIR", HF_HUB_CACHE) - if isinstance(resolved_cache_dir, str): - resolved_cache_dir = Path(resolved_cache_dir) - - self.token = resolved_token + self.token = token or os.getenv("HF_TOKEN", None) self.repo_id = repo_id self.repo_type = repo_type - self.cache_dir = resolved_cache_dir + self.cache_dir = Path( + cache_dir if cache_dir else os.getenv("HF_CACHE_DIR", HF_HUB_CACHE) + ) @property def token(self) -> str | None: @@ -70,14 +67,19 @@ def repo_id(self) -> str: @repo_id.setter def repo_id(self, value: str) -> None: + """ + Set the repo_id. + + This setter also calls _get_dataset_size to fetch size info and validate that + the repo exists and is accessible. No error is raised if the repo is not + accessible, but an error is logged. + + """ self._repo_id = value try: - self._get_dataset_size(value) + self._get_dataset_size(self._repo_id) except (HTTPError, ValueError) as e: - self.logger.warning(f"Could not validate repo_id {value}: {e}") - self.logger.info( - "Repo validation skipped - will be checked on first download" - ) + self.logger.error(f"Could not reach {value}: {e}") @property def cache_dir(self) -> Path: @@ -85,16 +87,27 @@ def cache_dir(self) -> Path: @cache_dir.setter def cache_dir(self, value: str | Path) -> None: - """Set the cache directory for huggingface_hub downloads.""" + """ + Set the cache directory for huggingface_hub downloads. + + :raises FileNotFoundError: If the specified directory does not exist. + + """ path = Path(value) if not path.exists(): - raise FileNotFoundError(f"Cache directory does not exist: {path}") + raise FileNotFoundError(f"Cache directory {path} does not exist") self._cache_dir = path @property def size(self) -> dict[str, Any] | None: - """Size information from the HF Dataset Server API (if available).""" - return self._size if hasattr(self, "_size") else None + """ + Size information from the HF Dataset Server API. + + This reaches the /size endpoint. See + https://github.com/huggingface/dataset-viewer/blob/8f0ae65f0ff64791111d37a725af437c3c752daf/docs/source/size.md + + """ + return getattr(self, "_size", None) @size.setter def size(self, value: dict[str, Any]) -> None: @@ -103,14 +116,40 @@ def size(self, value: dict[str, Any]) -> None: @property def snapshot_path(self) -> Path | None: """Path to the last downloaded snapshot (if any).""" - return self._snapshot_path if hasattr(self, "_snapshot_path") else None + return getattr(self, "_snapshot_path", None) @snapshot_path.setter def snapshot_path(self, value: str | Path | None) -> None: - if value is None: - self._snapshot_path = None - else: - self._snapshot_path = Path(value) + self._snapshot_path = None if value is None else Path(value) + + def _get_dataset_size_mb(self) -> float: + """Get dataset size in MB, returning inf if not available.""" + if not self.size: + return float("inf") + return ( + self.size.get("size", {}) + .get("dataset", {}) + .get("num_bytes_original_files", float("inf")) + / MB_TO_BYTES + ) + + def _ensure_str_paths(self, kwargs: dict[str, Any]) -> None: + """Ensure Path-like arguments are converted to strings.""" + for key in ["local_dir", "cache_dir"]: + if key in kwargs and kwargs[key] is not None: + kwargs[key] = str(kwargs[key]) + + def _build_auth_headers(self) -> dict[str, str]: + """Build authentication headers if token is available.""" + return {"Authorization": f"Bearer {self.token}"} if self.token else {} + + def _normalize_patterns(self, kwargs: dict[str, Any]) -> None: + """Convert string patterns to lists.""" + for pattern_key in ["allow_patterns", "ignore_patterns"]: + if pattern_key in kwargs and kwargs[pattern_key] is not None: + patterns = kwargs[pattern_key] + if isinstance(patterns, str): + kwargs[pattern_key] = [patterns] def _get_dataset_size(self, repo_id: str | None = None) -> None: """ @@ -125,11 +164,7 @@ def _get_dataset_size(self, repo_id: str | None = None) -> None: repo_id = repo_id or self.repo_id url = f"https://datasets-server.huggingface.co/size?dataset={repo_id}" - headers = {} - if self.token: - headers["Authorization"] = f"Bearer {self.token}" - - response = requests.get(url, headers=headers) + response = requests.get(url, headers=self._build_auth_headers()) response.raise_for_status() data = response.json() @@ -155,72 +190,6 @@ def _get_dataset_size(self, repo_id: str | None = None) -> None: self.size = data - def _check_repo_size_and_decide_strategy( - self, - auto_download_threshold_mb: float, - force_full_download: bool, - allow_patterns: list[str] | str | None, - ignore_patterns: list[str] | str | None, - **kwargs, - ) -> tuple[list[str] | str | None, list[str] | str | None]: - """ - Check repo size and decide download strategy. - - Returns: - Tuple of (allow_patterns, ignore_patterns) to use for download - - """ - if force_full_download or auto_download_threshold_mb <= 0: - return None, None - - try: - # Get repo info to estimate size - revision = kwargs.get("revision") - if revision is not None: - info = repo_info( - repo_id=self.repo_id, - repo_type=self.repo_type, - token=self.token, - revision=str(revision), - ) - else: - info = repo_info( - repo_id=self.repo_id, - repo_type=self.repo_type, - token=self.token, - ) - - # Estimate total size from siblings (files in repo) - total_size_bytes = sum( - getattr(sibling, "size", 0) or 0 - for sibling in getattr(info, "siblings", []) - ) - total_size_mb = total_size_bytes / (1024 * 1024) - - self.logger.info(f"Estimated repo size: {total_size_mb:.2f} MB") - - # If small enough, download everything - if total_size_mb <= auto_download_threshold_mb: - self.logger.info( - f"Repo size ({total_size_mb:.2f} MB) under threshold " - f"({auto_download_threshold_mb} MB), downloading full repo" - ) - return None, None - else: - raise RepoTooLargeError( - f"Repo size ({total_size_mb:.2f} MB) exceeds threshold " - f"({auto_download_threshold_mb} MB). Use a selective download " - "method via `files`, `allow_patterns` or `ignore_patterns`, " - "or increase `auto_download_threshold_mb` and try again." - ) - - except Exception as e: - self.logger.warning( - f"Could not determine repo size: {e}. Proceeding with " - f"pattern-based download." - ) - return allow_patterns, ignore_patterns - def _download_single_file( self, filename: str, @@ -248,7 +217,7 @@ def _download_single_file( "repo_type": self.repo_type, "filename": filename, "token": self.token, - **kwargs, # User kwargs override defaults + **kwargs, } # Set cache_dir only if local_dir not specified @@ -256,10 +225,7 @@ def _download_single_file( hf_kwargs["cache_dir"] = str(self.cache_dir) # Ensure string conversion for Path-like arguments - if "local_dir" in hf_kwargs and hf_kwargs["local_dir"] is not None: - hf_kwargs["local_dir"] = str(hf_kwargs["local_dir"]) - if "cache_dir" in hf_kwargs and hf_kwargs["cache_dir"] is not None: - hf_kwargs["cache_dir"] = str(hf_kwargs["cache_dir"]) + self._ensure_str_paths(hf_kwargs) file_path = hf_hub_download(**hf_kwargs) self._snapshot_path = Path(file_path).parent @@ -303,7 +269,6 @@ def _download_snapshot( } # Set cache_dir only if local_dir not specified and cache_dir wasn't passed in - # by user if ( "local_dir" not in snapshot_kwargs and "cache_dir" not in snapshot_kwargs @@ -311,15 +276,8 @@ def _download_snapshot( ): snapshot_kwargs["cache_dir"] = str(self.cache_dir) - # if allow_patterns or ignore_patterns are strings, convert to list - for pattern_key in ["allow_patterns", "ignore_patterns"]: - if ( - pattern_key in snapshot_kwargs - and snapshot_kwargs[pattern_key] is not None - ): - patterns = snapshot_kwargs[pattern_key] - if isinstance(patterns, str): - snapshot_kwargs[pattern_key] = [patterns] + # Convert string patterns to lists + self._normalize_patterns(snapshot_kwargs) snapshot_path = snapshot_download(**snapshot_kwargs) self.snapshot_path = Path(snapshot_path) @@ -327,7 +285,6 @@ def _download_snapshot( def download( self, - *, files: list[str] | str | None = None, force_full_download: bool = False, auto_download_threshold_mb: float = 100.0, @@ -351,42 +308,48 @@ def download( :return: Path to downloaded content (file or directory). """ - # Handle single file download + dataset_size_mb = self._get_dataset_size_mb() + if dataset_size_mb <= auto_download_threshold_mb or force_full_download: + self.logger.info( + f"Dataset size ({dataset_size_mb:.2f} MB) is below the auto-download " + f"threshold of {auto_download_threshold_mb} MB. Downloading entire " + "repo." + ) + files = None + kwargs.pop("allow_patterns", None) + kwargs.pop("ignore_patterns", None) + elif ( + not files + and not kwargs.get("allow_patterns") + and not kwargs.get("ignore_patterns") + ): + excess_size_mb = dataset_size_mb - auto_download_threshold_mb + raise RepoTooLargeError( + f"Dataset size ({dataset_size_mb:.2f} MB) exceeds the " + f"auto-download threshold of {auto_download_threshold_mb} MB by " + f"{excess_size_mb:.2f} MB. To download the dataset, either " + "specify specific files or patterns to download, " + "set force_full_download=True or increase the " + "`auto_download_threshold_mb`." + ) + # Handle specific file downloads if files is not None: - if isinstance(files, str): - files = [files] - - if len(files) == 1: + if isinstance(files, str) or (isinstance(files, list) and len(files) == 1): + # Single file + filename = files if isinstance(files, str) else files[0] + self.logger.info(f"Preparing to download single file: {filename}") return self._download_single_file( - filename=files[0], - dry_run=dry_run, - **kwargs, + filename=filename, dry_run=dry_run, **kwargs ) - else: - # Multiple specific files - use filtered snapshot_download - self.logger.info(f"Downloading specific files: {files}") + elif isinstance(files, list) and len(files) > 1: + # Multiple files - use snapshot_download with allow_patterns if kwargs.get("allow_patterns") is not None: self.logger.warning( - "`allow_patterns` will be overridden by `files` argument" + "Both 'files' and 'allow_patterns' were provided. " + "'files' will take precedence." ) kwargs["allow_patterns"] = files - # Check repo size and adjust download strategy if needed - allow_patterns, ignore_patterns = self._check_repo_size_and_decide_strategy( - auto_download_threshold_mb=auto_download_threshold_mb, - force_full_download=force_full_download, - allow_patterns=kwargs.get("allow_patterns"), - ignore_patterns=kwargs.get("ignore_patterns"), - **kwargs, - ) - - # Update kwargs with determined patterns - if allow_patterns is not None: - kwargs["allow_patterns"] = allow_patterns - if ignore_patterns is not None: - kwargs["ignore_patterns"] = ignore_patterns - - # Execute snapshot download return self._download_snapshot(dry_run=dry_run, **kwargs) @abstractmethod diff --git a/tfbpapi/tests/test_AbstractHfAPI.py b/tfbpapi/tests/test_AbstractHfAPI.py new file mode 100644 index 0000000..6f28eea --- /dev/null +++ b/tfbpapi/tests/test_AbstractHfAPI.py @@ -0,0 +1,435 @@ +import tempfile +from collections.abc import Mapping +from pathlib import Path +from typing import Any +from unittest.mock import Mock, patch + +import pytest +from requests import HTTPError + +from tfbpapi.AbstractHfAPI import AbstractHfAPI, RepoTooLargeError + + +class TestHfAPI(AbstractHfAPI): + """Concrete implementation of AbstractHfAPI for testing.""" + + def parse_datacard(self, *args: Any, **kwargs: Any) -> Mapping[str, Any]: + """Test implementation of parse_datacard.""" + return {"test": "datacard"} + + def query(self, *args: Any, **kwargs: Any) -> Any: + """Test implementation of query.""" + return {"test": "query"} + + +@pytest.fixture +def mock_hf_hub_download(): + """Mock hf_hub_download to return a fake path.""" + with patch("tfbpapi.AbstractHfAPI.hf_hub_download") as mock: + mock.return_value = "/fake/path/to/file.txt" + yield mock + + +@pytest.fixture +def mock_snapshot_download(): + """Mock snapshot_download to return a fake path.""" + with patch("tfbpapi.AbstractHfAPI.snapshot_download") as mock: + mock.return_value = "/fake/path/to/snapshot" + yield mock + + +@pytest.fixture +def mock_repo_info(): + """Mock repo_info to return fake repo information.""" + with patch("tfbpapi.AbstractHfAPI.repo_info") as mock: + # Create a mock with siblings attribute + mock_info = Mock() + mock_info.siblings = [ + Mock(size=1024 * 1024), # 1MB file + Mock(size=512 * 1024), # 512KB file + Mock(size=None), # File with no size + ] + mock.return_value = mock_info + yield mock + + +@pytest.fixture +def mock_requests_get(): + """Mock requests.get for dataset size API calls.""" + with patch("tfbpapi.AbstractHfAPI.requests.get") as mock: + # Create a mock response + mock_response = Mock() + mock_response.json.return_value = { + "size": { + "dataset": { + "num_bytes_original_files": 10 * 1024 * 1024, # 10MB + "size_determination_complete": True, + } + }, + "partial": False, + } + mock_response.raise_for_status.return_value = None + mock.return_value = mock_response + yield mock + + +@pytest.fixture +def mock_dataset_size_call(): + """Mock the _get_dataset_size call to prevent real API calls during init.""" + with patch.object(AbstractHfAPI, "_get_dataset_size") as mock: + yield mock + + +@pytest.fixture +def temp_cache_dir(): + """Create a temporary directory for cache testing.""" + with tempfile.TemporaryDirectory() as temp_dir: + yield Path(temp_dir) + + +class TestAbstractHfAPI: + """Test cases for AbstractHfAPI.""" + + def test_init_basic(self, temp_cache_dir, mock_dataset_size_call): + """Test basic initialization.""" + api = TestHfAPI( + repo_id="test/repo", + repo_type="dataset", + token="test-token", + cache_dir=temp_cache_dir, + ) + + assert api.repo_id == "test/repo" + assert api.repo_type == "dataset" + assert api.token == "test-token" + assert api.cache_dir == temp_cache_dir + + def test_init_with_env_vars( + self, temp_cache_dir, monkeypatch, mock_dataset_size_call + ): + """Test initialization with environment variables.""" + monkeypatch.setenv("HF_TOKEN", "env-token") + monkeypatch.setenv("HF_CACHE_DIR", str(temp_cache_dir)) + + api = TestHfAPI(repo_id="test/repo") + + assert api.token == "env-token" + assert api.cache_dir == temp_cache_dir + + def test_init_user_overrides_env(self, temp_cache_dir, monkeypatch): + """Test that user parameters override environment variables.""" + monkeypatch.setenv("HF_TOKEN", "env-token") + + api = TestHfAPI( + repo_id="test/repo", token="user-token", cache_dir=temp_cache_dir + ) + + assert api.token == "user-token" + assert api.cache_dir == temp_cache_dir + + def test_cache_dir_setter_valid(self, temp_cache_dir): + """Test cache_dir setter with valid directory.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + + new_cache_dir = temp_cache_dir / "new_cache" + new_cache_dir.mkdir() + + api.cache_dir = new_cache_dir + assert api.cache_dir == new_cache_dir + + def test_cache_dir_setter_invalid(self, temp_cache_dir): + """Test cache_dir setter with invalid directory.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + + invalid_dir = temp_cache_dir / "nonexistent" + + with pytest.raises( + FileNotFoundError, match="Cache directory .* does not exist" + ): + api.cache_dir = invalid_dir + + @patch("tfbpapi.AbstractHfAPI.requests.get") + def test_get_dataset_size_success(self, mock_get, temp_cache_dir): + """Test successful dataset size retrieval.""" + mock_response = Mock() + mock_response.json.return_value = { + "size": {"dataset": {"num_bytes": 1024}}, + "partial": False, + } + mock_response.raise_for_status.return_value = None + mock_get.return_value = mock_response + + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + api._get_dataset_size() + + assert api.size is not None + assert not api.size.get("partial", True) + + @patch("tfbpapi.AbstractHfAPI.requests.get") + def test_get_dataset_size_partial(self, mock_get, temp_cache_dir): + """Test dataset size retrieval with partial results.""" + mock_response = Mock() + mock_response.json.return_value = { + "size": {"dataset": {"num_bytes": 1024}}, + "partial": True, + } + mock_response.raise_for_status.return_value = None + mock_get.return_value = mock_response + + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + api._get_dataset_size() + + assert api.size["partial"] is True # type: ignore[index] + assert "size_warning" in api.size["size"]["dataset"] # type: ignore[index] + + def test_repo_id_setter_success(self, mock_requests_get, temp_cache_dir): + """Test successful repo_id setting.""" + api = TestHfAPI(repo_id="initial/repo", cache_dir=temp_cache_dir) + + api.repo_id = "new/repo" + assert api.repo_id == "new/repo" + mock_requests_get.assert_called() + + @patch("tfbpapi.AbstractHfAPI.requests.get") + def test_repo_id_setter_failure(self, mock_get, temp_cache_dir): + """Test repo_id setting with API failure.""" + mock_get.side_effect = HTTPError("Repository not found") + + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + + # Should not raise, but should log error + api.repo_id = "nonexistent/repo" + assert api.repo_id == "nonexistent/repo" + + def test_get_dataset_size_mb(self, temp_cache_dir): + """Test dataset size calculation in MB.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + + # Test with no size data + assert api._get_dataset_size_mb() == float("inf") + + # Test with size data + api.size = { + "size": {"dataset": {"num_bytes_original_files": 2 * 1024 * 1024}} # 2MB + } + assert api._get_dataset_size_mb() == 2.0 + + def test_build_auth_headers(self, temp_cache_dir): + """Test authentication header building.""" + # Without token + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + api.token = None + assert api._build_auth_headers() == {} + + # With token + api.token = "test-token" + headers = api._build_auth_headers() + assert headers == {"Authorization": "Bearer test-token"} + + def test_ensure_str_paths(self, temp_cache_dir): + """Test path string conversion.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + + kwargs = { + "local_dir": Path("/some/path"), + "cache_dir": Path("/cache/path"), + "other_param": "unchanged", + } + + api._ensure_str_paths(kwargs) + + assert kwargs["local_dir"] == "/some/path" + assert kwargs["cache_dir"] == "/cache/path" + assert kwargs["other_param"] == "unchanged" + + def test_normalize_patterns(self, temp_cache_dir): + """Test pattern normalization.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + + kwargs = { + "allow_patterns": "*.txt", + "ignore_patterns": ["*.log", "*.tmp"], + "other_param": "unchanged", + } + + api._normalize_patterns(kwargs) + + assert kwargs["allow_patterns"] == ["*.txt"] + assert kwargs["ignore_patterns"] == ["*.log", "*.tmp"] + assert kwargs["other_param"] == "unchanged" + + def test_download_single_file(self, mock_hf_hub_download, temp_cache_dir): + """Test single file download.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + + result = api._download_single_file("test.txt") + + assert result == Path("/fake/path/to/file.txt") + mock_hf_hub_download.assert_called_once() + + # Check that correct arguments were passed + call_args = mock_hf_hub_download.call_args[1] + assert call_args["repo_id"] == "test/repo" + assert call_args["filename"] == "test.txt" + + def test_download_single_file_dry_run(self, mock_hf_hub_download, temp_cache_dir): + """Test single file download with dry run.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + + result = api._download_single_file("test.txt", dry_run=True) + + assert result == Path("dry_run_path") + mock_hf_hub_download.assert_not_called() + + def test_download_snapshot(self, mock_snapshot_download, temp_cache_dir): + """Test snapshot download.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + + result = api._download_snapshot(allow_patterns=["*.txt"]) + + assert result == Path("/fake/path/to/snapshot") + mock_snapshot_download.assert_called_once() + + def test_download_snapshot_dry_run(self, mock_snapshot_download, temp_cache_dir): + """Test snapshot download with dry run.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + + result = api._download_snapshot(dry_run=True, allow_patterns=["*.txt"]) + + assert result == Path("dry_run_path") + mock_snapshot_download.assert_not_called() + + def test_download_single_file_string(self, temp_cache_dir): + """Test download with single file as string.""" + # Create API instance by bypassing problematic initialization + api = TestHfAPI.__new__(TestHfAPI) # Create without calling __init__ + + # Manually set the required attributes + api._repo_id = "test/repo" + api.repo_type = "dataset" + api.token = None + api._cache_dir = temp_cache_dir + api.size = { + "size": {"dataset": {"num_bytes_original_files": 1024}} + } # Small size + api.logger = Mock() # Mock logger to avoid issues + + with patch("tfbpapi.AbstractHfAPI.hf_hub_download") as mock_hf_download: + # Configure the mock to return a fake path + mock_hf_download.return_value = "/fake/path/to/file.txt" + + result = api.download(files="test.txt", auto_download_threshold_mb=0) + + assert result == Path("/fake/path/to/file.txt") + mock_hf_download.assert_called_once() + + # Verify the call arguments + call_args = mock_hf_download.call_args[1] + assert call_args["repo_id"] == "test/repo" + assert call_args["filename"] == "test.txt" + + def test_download_single_file_list( + self, mock_hf_hub_download, temp_cache_dir, mock_dataset_size_call + ): + """Test download with single file as list.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + api.size = { + "size": {"dataset": {"num_bytes_original_files": 1024}} + } # Small size + + result = api.download(files=["test.txt"], auto_download_threshold_mb=0) + + assert result == Path("/fake/path/to/file.txt") + mock_hf_hub_download.assert_called_once() + + def test_download_multiple_files(self, mock_snapshot_download, temp_cache_dir): + """Test download with multiple files.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + api.size = { + "size": {"dataset": {"num_bytes_original_files": 1024}} + } # Small size + + result = api.download(files=["test1.txt", "test2.txt"]) + + assert result == Path("/fake/path/to/snapshot") + mock_snapshot_download.assert_called_once() + + def test_download_force_full(self, mock_snapshot_download, temp_cache_dir): + """Test download with force_full_download=True.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + api.size = { + "size": {"dataset": {"num_bytes_original_files": 1000 * 1024 * 1024}} + } # Large size + + result = api.download(force_full_download=True) + + assert result == Path("/fake/path/to/snapshot") + mock_snapshot_download.assert_called_once() + + def test_download_repo_too_large(self, temp_cache_dir): + """Test download with repo too large error.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + api.size = { + "size": {"dataset": {"num_bytes_original_files": 1000 * 1024 * 1024}} + } # 1GB + + with pytest.raises(RepoTooLargeError, match="Dataset size .* exceeds"): + api.download(auto_download_threshold_mb=10) + + def test_download_small_repo_auto(self, mock_snapshot_download, temp_cache_dir): + """Test download with small repo under threshold.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + api.size = { + "size": {"dataset": {"num_bytes_original_files": 5 * 1024 * 1024}} + } # 5MB + + result = api.download(auto_download_threshold_mb=10) + + assert result == Path("/fake/path/to/snapshot") + mock_snapshot_download.assert_called_once() + + def test_snapshot_path_property(self, temp_cache_dir): + """Test snapshot_path property getter and setter.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + + # Initially None + assert api.snapshot_path is None + + # Set to path + test_path = "/some/path" + api.snapshot_path = Path(test_path) + assert api.snapshot_path == Path(test_path) + + # Set to None + api.snapshot_path = None + assert api.snapshot_path is None + + def test_size_property(self, temp_cache_dir): + """Test size property getter and setter.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + + # Initially None + assert api.size is None + + # Set size data + size_data = {"size": {"dataset": {"num_bytes": 1024}}} + api.size = size_data + assert api.size == size_data + + def test_abstract_methods_implemented(self, temp_cache_dir): + """Test that abstract methods are properly implemented.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + + # Test parse_datacard + result = api.parse_datacard() + assert result == {"test": "datacard"} + + # Test query + result = api.query() + assert result == {"test": "query"} + + def test_repo_too_large_error(self): + """Test RepoTooLargeError exception.""" + error = RepoTooLargeError("Test error message") + assert str(error) == "Test error message" + assert isinstance(error, ValueError) From c8e0c39188f362b492914d7fab7cf975e5670d58 Mon Sep 17 00:00:00 2001 From: chasem Date: Mon, 8 Sep 2025 14:41:46 -0500 Subject: [PATCH 03/49] removing windows-2019 from ci --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c340f25..8007bd3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,6 @@ jobs: ubuntu-24.04, macos-13, windows-2022, - windows-2019, ] python-version: ["3.11"] runs-on: ${{ matrix.os }} From c164f32fec434d28233bdcb33aa5da73776f22b9 Mon Sep 17 00:00:00 2001 From: chasem Date: Wed, 10 Sep 2025 18:02:14 -0500 Subject: [PATCH 04/49] working on adding rank response functionality --- .pre-commit-config.yaml | 2 +- docs/AbstractAPI.md | 1 + docs/AbstractHfAPI.md | 1 + docs/AbstractRecordsAndFilesAPI.md | 1 + docs/AbstractRecordsOnlyAPI.md | 1 + docs/BindingAPI.md | 1 + docs/BindingConcatenatedAPI.md | 1 + docs/BindingManualQCAPI.md | 1 + docs/Cache.md | 1 + docs/CallingCardsBackgroundAPI.md | 1 + docs/DataSourceAPI.md | 1 + docs/ExpressionAPI.md | 1 + docs/ExpressionManualQCAPI.md | 2 + docs/FileFormatAPI.md | 1 + docs/GenomicFeatureAPI.md | 1 + docs/HfCacheManager.md | 1 + docs/ParamsDict.md | 1 + docs/PromoterSetAPI.md | 1 + docs/PromoterSetSigAPI.md | 1 + docs/RegulatorAPI.md | 1 + docs/index.md | 89 + docs/metric_arrays.md | 1 + docs/rank_transforms.md | 17 + docs/tutorials/database_interface.ipynb | 2266 +++++++++++++++++++ mkdocs.yml | 199 +- pyproject.toml | 8 + tfbpapi/HfQueryAPI.py | 363 +++ tfbpapi/HfRankResponse.py | 482 ++++ tfbpapi/IncrementalAnalysisDB.py | 340 +++ tfbpapi/tests/test_HfQueryAPI.py | 530 +++++ tfbpapi/tests/test_HfRankResponse.py | 505 +++++ tfbpapi/tests/test_IncrementalAnalysisDB.py | 341 +++ 32 files changed, 5110 insertions(+), 53 deletions(-) create mode 100644 docs/AbstractAPI.md create mode 100644 docs/AbstractHfAPI.md create mode 100644 docs/AbstractRecordsAndFilesAPI.md create mode 100644 docs/AbstractRecordsOnlyAPI.md create mode 100644 docs/BindingAPI.md create mode 100644 docs/BindingConcatenatedAPI.md create mode 100644 docs/BindingManualQCAPI.md create mode 100644 docs/Cache.md create mode 100644 docs/CallingCardsBackgroundAPI.md create mode 100644 docs/DataSourceAPI.md create mode 100644 docs/ExpressionAPI.md create mode 100644 docs/ExpressionManualQCAPI.md create mode 100644 docs/FileFormatAPI.md create mode 100644 docs/GenomicFeatureAPI.md create mode 100644 docs/HfCacheManager.md create mode 100644 docs/ParamsDict.md create mode 100644 docs/PromoterSetAPI.md create mode 100644 docs/PromoterSetSigAPI.md create mode 100644 docs/RegulatorAPI.md create mode 100644 docs/index.md create mode 100644 docs/metric_arrays.md create mode 100644 docs/rank_transforms.md create mode 100644 docs/tutorials/database_interface.ipynb create mode 100644 tfbpapi/HfQueryAPI.py create mode 100644 tfbpapi/HfRankResponse.py create mode 100644 tfbpapi/IncrementalAnalysisDB.py create mode 100644 tfbpapi/tests/test_HfQueryAPI.py create mode 100644 tfbpapi/tests/test_HfRankResponse.py create mode 100644 tfbpapi/tests/test_IncrementalAnalysisDB.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e8284d5..e45b42f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,4 +1,4 @@ -exclude: ^docs/|devcontainer.json|.*/snapshots/ +exclude: ^docs/|devcontainer.json|.*/snapshots/|mkdocs.yml default_stages: [commit] default_language_version: diff --git a/docs/AbstractAPI.md b/docs/AbstractAPI.md new file mode 100644 index 0000000..ada18a1 --- /dev/null +++ b/docs/AbstractAPI.md @@ -0,0 +1 @@ +::: tfbpapi.AbstractAPI.AbstractAPI diff --git a/docs/AbstractHfAPI.md b/docs/AbstractHfAPI.md new file mode 100644 index 0000000..45dea40 --- /dev/null +++ b/docs/AbstractHfAPI.md @@ -0,0 +1 @@ +::: tfbpapi.AbstractHfAPI.AbstractHfAPI diff --git a/docs/AbstractRecordsAndFilesAPI.md b/docs/AbstractRecordsAndFilesAPI.md new file mode 100644 index 0000000..395c787 --- /dev/null +++ b/docs/AbstractRecordsAndFilesAPI.md @@ -0,0 +1 @@ +::: tfbpapi.AbstractRecordsAndFilesAPI.AbstractRecordsAndFilesAPI diff --git a/docs/AbstractRecordsOnlyAPI.md b/docs/AbstractRecordsOnlyAPI.md new file mode 100644 index 0000000..fc0726a --- /dev/null +++ b/docs/AbstractRecordsOnlyAPI.md @@ -0,0 +1 @@ +::: tfbpapi.AbstractRecordsOnlyAPI.AbstractRecordsOnlyAPI diff --git a/docs/BindingAPI.md b/docs/BindingAPI.md new file mode 100644 index 0000000..7f4c555 --- /dev/null +++ b/docs/BindingAPI.md @@ -0,0 +1 @@ +::: tfbpapi.BindingAPI.BindingAPI diff --git a/docs/BindingConcatenatedAPI.md b/docs/BindingConcatenatedAPI.md new file mode 100644 index 0000000..4d68aea --- /dev/null +++ b/docs/BindingConcatenatedAPI.md @@ -0,0 +1 @@ +::: tfbpapi.BindingConcatenatedAPI.BindingConcatenatedAPI diff --git a/docs/BindingManualQCAPI.md b/docs/BindingManualQCAPI.md new file mode 100644 index 0000000..a29d3ca --- /dev/null +++ b/docs/BindingManualQCAPI.md @@ -0,0 +1 @@ +::: tfbpapi.BindingManualQCAPI.BindingManualQCAPI diff --git a/docs/Cache.md b/docs/Cache.md new file mode 100644 index 0000000..3c2ed52 --- /dev/null +++ b/docs/Cache.md @@ -0,0 +1 @@ +::: tfbpapi.Cache.Cache diff --git a/docs/CallingCardsBackgroundAPI.md b/docs/CallingCardsBackgroundAPI.md new file mode 100644 index 0000000..5f5c24a --- /dev/null +++ b/docs/CallingCardsBackgroundAPI.md @@ -0,0 +1 @@ +::: tfbpapi.CallingCardsBackgroundAPI.CallingCardsBackgroundAPI diff --git a/docs/DataSourceAPI.md b/docs/DataSourceAPI.md new file mode 100644 index 0000000..b50cc38 --- /dev/null +++ b/docs/DataSourceAPI.md @@ -0,0 +1 @@ +::: tfbpapi.DataSourceAPI.DataSourceAPI diff --git a/docs/ExpressionAPI.md b/docs/ExpressionAPI.md new file mode 100644 index 0000000..9397868 --- /dev/null +++ b/docs/ExpressionAPI.md @@ -0,0 +1 @@ +::: tfbpapi.ExpressionAPI.ExpressionAPI diff --git a/docs/ExpressionManualQCAPI.md b/docs/ExpressionManualQCAPI.md new file mode 100644 index 0000000..094b037 --- /dev/null +++ b/docs/ExpressionManualQCAPI.md @@ -0,0 +1,2 @@ +::: tfbpapi.ExpressionManualQCAPI.ExpressionManualQCAPI + diff --git a/docs/FileFormatAPI.md b/docs/FileFormatAPI.md new file mode 100644 index 0000000..12adc28 --- /dev/null +++ b/docs/FileFormatAPI.md @@ -0,0 +1 @@ +::: tfbpapi.FileFormatAPI.FileFormatAPI diff --git a/docs/GenomicFeatureAPI.md b/docs/GenomicFeatureAPI.md new file mode 100644 index 0000000..c66339c --- /dev/null +++ b/docs/GenomicFeatureAPI.md @@ -0,0 +1 @@ +::: tfbpapi.GenomicFeatureAPI.GenomicFeatureAPI diff --git a/docs/HfCacheManager.md b/docs/HfCacheManager.md new file mode 100644 index 0000000..89d5c77 --- /dev/null +++ b/docs/HfCacheManager.md @@ -0,0 +1 @@ +::: tfbpapi.HfCacheManager.HfCacheManager diff --git a/docs/ParamsDict.md b/docs/ParamsDict.md new file mode 100644 index 0000000..057048a --- /dev/null +++ b/docs/ParamsDict.md @@ -0,0 +1 @@ +::: tfbpapi.ParamsDict.ParamsDict diff --git a/docs/PromoterSetAPI.md b/docs/PromoterSetAPI.md new file mode 100644 index 0000000..b2c311c --- /dev/null +++ b/docs/PromoterSetAPI.md @@ -0,0 +1 @@ +::: tfbpapi.PromoterSetAPI.PromoterSetAPI diff --git a/docs/PromoterSetSigAPI.md b/docs/PromoterSetSigAPI.md new file mode 100644 index 0000000..f07120c --- /dev/null +++ b/docs/PromoterSetSigAPI.md @@ -0,0 +1 @@ +::: tfbpapi.PromoterSetSigAPI.PromoterSetSigAPI diff --git a/docs/RegulatorAPI.md b/docs/RegulatorAPI.md new file mode 100644 index 0000000..0217a92 --- /dev/null +++ b/docs/RegulatorAPI.md @@ -0,0 +1 @@ +::: tfbpapi.RegulatorAPI.RegulatorAPI diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..b58e762 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,89 @@ +# tfbpapi Documentation + +## Development Commands + +### Testing +- Run tests: `poetry run pytest` +- Run specific test: `poetry run pytest tfbpapi/tests/test_[module_name].py` +- Run tests with coverage: `poetry run pytest --cov=tfbpapi` + +### Linting and Formatting +- Run all pre-commit checks: `poetry run pre-commit run --all-files` +- Format code with Black: `poetry run black tfbpapi/` +- Sort imports with isort: `poetry run isort tfbpapi/` +- Type check with mypy: `poetry run mypy tfbpapi/` +- Lint with flake8: `poetry run flake8 tfbpapi/` + +### Installation +- Install dependencies: `poetry install` +- Install pre-commit hooks: `poetry run pre-commit install` + +## Architecture + +This is a Python package for interacting with django.tfbindingandmodeling.com APIs. The codebase follows an object-oriented architecture with abstract base classes providing common functionality. + +### Core Architecture Classes + +- **AbstractAPI** (`tfbpapi/AbstractAPI.py`): Base class for all API clients with token authentication, caching (via `Cache` class), parameter validation (via `ParamsDict` class), and abstract CRUD methods. + +- **AbstractRecordsAndFilesAPI** (`tfbpapi/AbstractRecordsAndFilesAPI.py`): Extends `AbstractAPI` for endpoints that serve both record metadata and associated data files. Handles tarball extraction, CSV parsing, and file caching. + +- **AbstractRecordsOnlyAPI** (`tfbpapi/AbstractRecordsOnlyAPI.py`): Extends `AbstractAPI` for endpoints that only serve record metadata without file storage. + +- **AbstractHfAPI** (`tfbpapi/AbstractHfAPI.py`): Abstract base for Hugging Face Hub integration, providing repository management functionality. + +### Concrete API Classes + +All concrete API classes inherit from either `AbstractRecordsAndFilesAPI` or `AbstractRecordsOnlyAPI`: + +- `BindingAPI`, `ExpressionAPI`, `PromoterSetAPI` - Record and file APIs +- `DataSourceAPI`, `RegulatorAPI`, `GenomicFeatureAPI` - Records only APIs + +### Utility Classes + +- **Cache** (`tfbpapi/Cache.py`): TTL-based caching for API responses +- **ParamsDict** (`tfbpapi/ParamsDict.py`): Parameter validation against allowed keys +- **HfCacheManager** (`tfbpapi/HfCacheManager.py`): Hugging Face cache cleanup utilities + +### Data Processing Utilities + +- **metric_arrays** (`tfbpapi/metric_arrays.py`): Array-based metric calculations +- **rank_transforms** (`tfbpapi/rank_transforms.py`): Statistical rank transformation functions + +## Configuration + +- Uses Poetry for dependency management +- Python 3.11 required +- Black formatter with 88-character line length +- Pre-commit hooks include Black, isort, flake8, mypy, and various file checks +- pytest with snapshot testing support +- Environment variables: `BASE_URL`, `TOKEN`, `HF_TOKEN`, `HF_CACHE_DIR` + +## Testing Patterns + +- Tests use pytest with async support (`pytest-asyncio`) +- Snapshot testing with `pytest-snapshot` for API response validation +- Test fixtures in `tfbpapi/tests/conftest.py` +- Mock HTTP responses using `aioresponses` and `responses` libraries + +### mkdocs + +#### Commands + +After building the environment with poetry, you can use `poetry run` or a poetry shell +to execute the following: + +* `mkdocs new [dir-name]` - Create a new project. +* `mkdocs serve` - Start the live-reloading docs server. +* `mkdocs build` - Build the documentation site. +* `mkdocs -h` - Print help message and exit. + +#### Project layout + + mkdocs.yml # The configuration file. + docs/ + index.md # The documentation homepage. + ... # Other markdown pages, images and other files. + +To update the gh-pages documentation, use `poetry run mkdocs gh-deply` + diff --git a/docs/metric_arrays.md b/docs/metric_arrays.md new file mode 100644 index 0000000..0b78beb --- /dev/null +++ b/docs/metric_arrays.md @@ -0,0 +1 @@ +::: tfbpapi.metric_arrays.metric_arrays diff --git a/docs/rank_transforms.md b/docs/rank_transforms.md new file mode 100644 index 0000000..a3b55d4 --- /dev/null +++ b/docs/rank_transforms.md @@ -0,0 +1,17 @@ +

Rank Transforms

+ +

Shifted Negative Log Ranks

+ +::: tfbpapi.rank_transforms.shifted_negative_log_ranks + +

Stable Rank

+ +::: tfbpapi.rank_transforms.stable_rank + +

Rank by p-value

+ +::: tfbpapi.rank_transforms.rank_by_pvalue + +

Transform

+ +::: tfbpapi.rank_transforms.transform \ No newline at end of file diff --git a/docs/tutorials/database_interface.ipynb b/docs/tutorials/database_interface.ipynb new file mode 100644 index 0000000..79e41f7 --- /dev/null +++ b/docs/tutorials/database_interface.ipynb @@ -0,0 +1,2266 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# The Database Interface Classes\n", + "\n", + "For each API endpoint exposed in the Django app, there is a corresponding class that\n", + "provide methods to execute CRUD operations asynchronously.\n", + "\n", + "There are two types of API endpoints -- those that contain only records data, and \n", + "those that store both records and pointers to files.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Connecting to the Database\n", + "\n", + "The database currently runs on HTCF service partition. This is a single\n", + "node with 8 CPU and 30 GB that is meant for long running low resource jobs.\n", + "The components that need to run are a postgres database, a redis instance and the\n", + "django app. As long as these components are running on the service partition, \n", + "you can connect via an ssh tunnel with:\n", + "\n", + "```bash\n", + "ssh username@login.htcf.wustl.edu -N -L 8001:n240:8000\n", + "```\n", + "\n", + "where the `8001:n240:8000` takes the form of `local_port:cluster_node:app_port`. The\n", + "django app will always be served on port `8000`, and `n240` is the only service\n", + "partition node. You may choose a different local port.\n", + "\n", + "If you do this and cannot connect, let me know and I'll check the status of the jobs \n", + "on the cluster.\n", + "\n", + "### Database username and password\n", + "\n", + "Once you have a tunnel, you can access the database frontend at `127.0.0.1:8001`\n", + "(or a different local port, if you changed that number). If you haven't already\n", + "signed up, you'll need to click the 'sign up' button and follow\n", + "the instructions. The e-mail server is not hooked up at the moment, so when it says\n", + "\"see the e-mail\", send a slack message and let me know. I'll give you a link to\n", + "complete the sign up process. After that, you can just use the \"sign in\" button.\n", + "\n", + "For computational tasks, including generating rank response data, celery workers must\n", + "be launched on the HTCF general partition. There is currently a script that is meant to\n", + "monitor the redis queue and launch/kill these workers automatically, but this\n", + "functionality is new and largely untested. You can monitor the workers/tasks if you\n", + "create another tunnel with:\n", + "\n", + "```bash\n", + "ssh username@login.htcf.wustl.edu -N -L 8002:n240:5555\n", + "```\n", + "You'd access this dashboard at `127.0.0.1:5555`\n", + "\n", + "The login is currently:\n", + "\n", + "```raw\n", + "username: \"flower\"\n", + "password: \"daisy\"\n", + "```\n", + "(yes, really -- the security comes from the fact that you need to login with HTCF)\n", + "\n", + "### Configuring the Database Interface Classes\n", + "\n", + "The database classes expect the following environmental variables to be set. \n", + "\n", + "```raw\n", + "BASE_URL='http://127.0.0.1:8001'\n", + "TOKEN=''\n", + "BINDING_URL='http://127.0.0.1:8001/api/binding'\n", + "BINDINGMANUALQC_URL='http://127.0.0.1:8001/api/bindingmanualqc'\n", + "CALLINGCARDSBACKGROUND_URL='http://127.0.0.1:8001/api/callingcardsbackground'\n", + "DATASOURCE_URL='http://127.0.0.1:8001/api/datasource'\n", + "EXPRESSION_URL='http://127.0.0.1:8001/api/expression'\n", + "EXPRESSIONMANUALQC_URL='http://127.0.0.1:8001/api/expressionmanualqc'\n", + "FILEFORMAT_URL='http://127.0.0.1:8001/api/fileformat'\n", + "GENOMICFEATURE_URL='http://127.0.0.1:8001/api/genomicfeature'\n", + "PROMOTERSET_URL='http://127.0.0.1:8001/api/promoterset'\n", + "PROMOTERSETSIG_URL='http://127.0.0.1:8001/api/promotersetsig'\n", + "REGULATOR_URL='http://127.0.0.1:8001/api/regulator'\n", + "```\n", + "\n", + "This can be achieved in the package during development with a `.env` file at the\n", + "top most level of the package. The `.env` file is loaded in the package `__init__.py`.\n", + "\n", + "If you are importing `yeastdnnexplorer` into a different environment, then you'll \n", + "need to add the package `dotenv` and execute `load_dotenv(dotenv_path=env_path)`. If\n", + "the `.env` file is in the same `PWD` in which you execute that command, there is no\n", + "need to specify a path.\n", + "\n", + "### Token Authentication\n", + "\n", + "Once you have a username and password to the database, you can retrieve your token. \n", + "Make sure that you put this token, at least, in a `.env` file, and make sure that \n", + "`.env` file is in your `.gitignore`.\n", + "\n", + "Alternatively, you could retrieve and store in memory the token at the beginning of \n", + "each session -- this is more secure if you are not using a `.env` file. \n", + "\n", + "The `.env` file is already in the `yeastddnexplorer` `.gitignore`\n", + "\n", + "```bash\n", + "curl -X 'POST' \\\n", + " 'http://127.0.0.1:8001/auth-token/' \\\n", + " -H 'accept: application/json' \\\n", + " -H 'Content-Type: application/json' \\\n", + " -d '{\n", + " \"username\": \"username\",\n", + " \"password\": \"password\"\n", + "}'\n", + "```\n", + "\n", + "Or with python:\n", + "\n", + "```python\n", + "import requests\n", + "\n", + "url = \"http://127.0.0.1:8001/auth-token/\"\n", + "headers = {\n", + " \"accept\": \"application/json\",\n", + " \"Content-Type\": \"application/json\",\n", + "}\n", + "data = {\n", + " \"username\": \"username\",\n", + " \"password\": \"password\",\n", + "}\n", + "\n", + "response = requests.post(url, json=data, headers=headers)\n", + "print(response.text)\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Using the Interface Classes" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from yeastdnnexplorer.interface import *\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Records Only Endpoints\n", + "\n", + "The records only endpoints are:\n", + "\n", + "- BindingManualQC\n", + "\n", + "- DataSource\n", + "\n", + "- ExpressionManualQC\n", + "\n", + "- FileFormat\n", + "\n", + "- GenomicFeature\n", + "\n", + "- PromoterSetSig\n", + "\n", + "- Regulator\n", + "\n", + "When the `read()` method is called on the corresponding API classes, a dataframe will\n", + "be returned in the response.\n", + "\n", + "All of the `read()` methods, for both types of API endpoints, return the result of\n", + "a callable. By default, the callable returns a dictionary with two keys: `metadata` and\n", + "`data`. For response only tables, the `metadata` value will be the records from the\n", + "database as a pandas dataframe and the `data` will be `None.\n", + "\n", + "### Example -- RegulatorAPI" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
iduploadermodifierregulator_locus_tagregulator_symbolupload_datemodified_dateunder_developmentnotesgenomicfeature
01chasemchasemYAL051WOAF12024-07-012024-07-01T12:47:18.619129-05:00Falsenone24
12chasemchasemYBL103CRTG32024-07-012024-07-01T12:47:19.667722-05:00Falsenone140
23chasemchasemYBL066CSEF12024-07-012024-07-01T12:47:20.523161-05:00Falsenone186
34chasemchasemYBL054WTOD62024-07-012024-07-01T12:47:21.309606-05:00Falsenone199
45chasemchasemYBL052CSAS32024-07-012024-07-01T12:47:22.161007-05:00Falsenone201
.................................
18091810chasemchasemYOR262WGPN22024-07-012024-07-01T14:14:36.164403-05:00Falsenone6387
18101811chasemchasemYPR190CRPC822024-07-012024-07-01T14:14:38.921261-05:00Falsenone7070
18111812chasemchasemYPL228WCET12024-07-012024-07-01T14:15:51.518999-05:00Falsenone6603
18121813chasemchasemYKL049CCSE42024-07-012024-07-01T14:15:56.555122-05:00Falsenone4083
18131814chasemchasemYMR168CCEP32024-07-012024-07-01T14:22:14.060524-05:00Falsenone5258
\n", + "

1814 rows × 10 columns

\n", + "
" + ], + "text/plain": [ + " id uploader modifier regulator_locus_tag regulator_symbol upload_date \\\n", + "0 1 chasem chasem YAL051W OAF1 2024-07-01 \n", + "1 2 chasem chasem YBL103C RTG3 2024-07-01 \n", + "2 3 chasem chasem YBL066C SEF1 2024-07-01 \n", + "3 4 chasem chasem YBL054W TOD6 2024-07-01 \n", + "4 5 chasem chasem YBL052C SAS3 2024-07-01 \n", + "... ... ... ... ... ... ... \n", + "1809 1810 chasem chasem YOR262W GPN2 2024-07-01 \n", + "1810 1811 chasem chasem YPR190C RPC82 2024-07-01 \n", + "1811 1812 chasem chasem YPL228W CET1 2024-07-01 \n", + "1812 1813 chasem chasem YKL049C CSE4 2024-07-01 \n", + "1813 1814 chasem chasem YMR168C CEP3 2024-07-01 \n", + "\n", + " modified_date under_development notes \\\n", + "0 2024-07-01T12:47:18.619129-05:00 False none \n", + "1 2024-07-01T12:47:19.667722-05:00 False none \n", + "2 2024-07-01T12:47:20.523161-05:00 False none \n", + "3 2024-07-01T12:47:21.309606-05:00 False none \n", + "4 2024-07-01T12:47:22.161007-05:00 False none \n", + "... ... ... ... \n", + "1809 2024-07-01T14:14:36.164403-05:00 False none \n", + "1810 2024-07-01T14:14:38.921261-05:00 False none \n", + "1811 2024-07-01T14:15:51.518999-05:00 False none \n", + "1812 2024-07-01T14:15:56.555122-05:00 False none \n", + "1813 2024-07-01T14:22:14.060524-05:00 False none \n", + "\n", + " genomicfeature \n", + "0 24 \n", + "1 140 \n", + "2 186 \n", + "3 199 \n", + "4 201 \n", + "... ... \n", + "1809 6387 \n", + "1810 7070 \n", + "1811 6603 \n", + "1812 4083 \n", + "1813 5258 \n", + "\n", + "[1814 rows x 10 columns]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "regulator = RegulatorAPI()\n", + "\n", + "result = await regulator.read()\n", + "result.get(\"metadata\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Record and File Endpoints\n", + "\n", + "The record and file endpoints are the following:\n", + "\n", + "- CallingCardsBackground\n", + "\n", + "- Expression\n", + "\n", + "- PromoterSet\n", + "\n", + "- PromoterSetSig\n", + "\n", + "- RankResponse *\n", + "\n", + "The default `read()` method is the same as the Records only Endpoint API classes.\n", + "However, there is an additional argument, `retrieve_files` which if set to `True`\n", + "will retrieve the file for which each record provides metadata. The return value of\n", + "`read()` is again a callable, and by default the `data` key will store a dictionary\n", + "where the keys correspond to the `id` column in the `metadata`.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
iduploadermodifiersingle_bindingcomposite_bindingfileformatbackgroundupload_datemodified_datefilepromotersourceregulator_symbolregulator_locus_tagconditionrank_recalldata_usablebackground_name
01chasemchasem1.0NaN1NaN2024-07-012024-07-01T13:51:48.611781-05:00https://yeastregulatorydb-htcf-data.s3.amazona...NaNharbison_chipOAF1YAL051WYPDunreviewedunreviewedNaN
12chasemchasem2.0NaN1NaN2024-07-012024-07-01T13:51:49.643452-05:00https://yeastregulatorydb-htcf-data.s3.amazona...NaNharbison_chipPDR3YBL005WYPDunreviewedunreviewedNaN
23chasemchasem3.0NaN1NaN2024-07-012024-07-01T13:51:50.744384-05:00https://yeastregulatorydb-htcf-data.s3.amazona...NaNharbison_chipHIR1YBL008WYPDunreviewedunreviewedNaN
34chasemchasem4.0NaN1NaN2024-07-012024-07-01T13:51:51.507918-05:00https://yeastregulatorydb-htcf-data.s3.amazona...NaNharbison_chipHAP3YBL021CYPDunreviewedunreviewedNaN
45chasemchasem5.0NaN1NaN2024-07-012024-07-01T13:51:52.277595-05:00https://yeastregulatorydb-htcf-data.s3.amazona...NaNharbison_chipTOD6YBL054WYPDunreviewedunreviewedNaN
.........................................................
22377011adminadminNaN145.051.02024-07-302024-07-30T16:39:36.457965-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4.0mitra_ccCBF1YJR060WNaNunreviewedpassad1
22387012adminadminNaN146.051.02024-07-302024-07-30T16:39:36.848168-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4.0mitra_ccGCN4YEL009CNaNunreviewedpassad1
22397013adminadminNaN147.051.02024-07-302024-07-30T16:39:37.234144-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4.0mitra_ccOAF1YAL051WNaNunreviewedpassad1
22407014adminadminNaN148.051.02024-07-302024-07-30T16:39:38.547155-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4.0mitra_ccYOX1YML027WNaNunreviewedpassad1
22417015adminadminNaN149.051.02024-07-302024-07-30T16:39:39.713590-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4.0mitra_ccLEU3YLR451WNaNunreviewedpassad1
\n", + "

2242 rows × 18 columns

\n", + "
" + ], + "text/plain": [ + " id uploader modifier single_binding composite_binding fileformat \\\n", + "0 1 chasem chasem 1.0 NaN 1 \n", + "1 2 chasem chasem 2.0 NaN 1 \n", + "2 3 chasem chasem 3.0 NaN 1 \n", + "3 4 chasem chasem 4.0 NaN 1 \n", + "4 5 chasem chasem 5.0 NaN 1 \n", + "... ... ... ... ... ... ... \n", + "2237 7011 admin admin NaN 145.0 5 \n", + "2238 7012 admin admin NaN 146.0 5 \n", + "2239 7013 admin admin NaN 147.0 5 \n", + "2240 7014 admin admin NaN 148.0 5 \n", + "2241 7015 admin admin NaN 149.0 5 \n", + "\n", + " background upload_date modified_date \\\n", + "0 NaN 2024-07-01 2024-07-01T13:51:48.611781-05:00 \n", + "1 NaN 2024-07-01 2024-07-01T13:51:49.643452-05:00 \n", + "2 NaN 2024-07-01 2024-07-01T13:51:50.744384-05:00 \n", + "3 NaN 2024-07-01 2024-07-01T13:51:51.507918-05:00 \n", + "4 NaN 2024-07-01 2024-07-01T13:51:52.277595-05:00 \n", + "... ... ... ... \n", + "2237 1.0 2024-07-30 2024-07-30T16:39:36.457965-05:00 \n", + "2238 1.0 2024-07-30 2024-07-30T16:39:36.848168-05:00 \n", + "2239 1.0 2024-07-30 2024-07-30T16:39:37.234144-05:00 \n", + "2240 1.0 2024-07-30 2024-07-30T16:39:38.547155-05:00 \n", + "2241 1.0 2024-07-30 2024-07-30T16:39:39.713590-05:00 \n", + "\n", + " file promoter \\\n", + "0 https://yeastregulatorydb-htcf-data.s3.amazona... NaN \n", + "1 https://yeastregulatorydb-htcf-data.s3.amazona... NaN \n", + "2 https://yeastregulatorydb-htcf-data.s3.amazona... NaN \n", + "3 https://yeastregulatorydb-htcf-data.s3.amazona... NaN \n", + "4 https://yeastregulatorydb-htcf-data.s3.amazona... NaN \n", + "... ... ... \n", + "2237 https://yeastregulatorydb-htcf-data.s3.amazona... 4.0 \n", + "2238 https://yeastregulatorydb-htcf-data.s3.amazona... 4.0 \n", + "2239 https://yeastregulatorydb-htcf-data.s3.amazona... 4.0 \n", + "2240 https://yeastregulatorydb-htcf-data.s3.amazona... 4.0 \n", + "2241 https://yeastregulatorydb-htcf-data.s3.amazona... 4.0 \n", + "\n", + " source regulator_symbol regulator_locus_tag condition \\\n", + "0 harbison_chip OAF1 YAL051W YPD \n", + "1 harbison_chip PDR3 YBL005W YPD \n", + "2 harbison_chip HIR1 YBL008W YPD \n", + "3 harbison_chip HAP3 YBL021C YPD \n", + "4 harbison_chip TOD6 YBL054W YPD \n", + "... ... ... ... ... \n", + "2237 mitra_cc CBF1 YJR060W NaN \n", + "2238 mitra_cc GCN4 YEL009C NaN \n", + "2239 mitra_cc OAF1 YAL051W NaN \n", + "2240 mitra_cc YOX1 YML027W NaN \n", + "2241 mitra_cc LEU3 YLR451W NaN \n", + "\n", + " rank_recall data_usable background_name \n", + "0 unreviewed unreviewed NaN \n", + "1 unreviewed unreviewed NaN \n", + "2 unreviewed unreviewed NaN \n", + "3 unreviewed unreviewed NaN \n", + "4 unreviewed unreviewed NaN \n", + "... ... ... ... \n", + "2237 unreviewed pass ad1 \n", + "2238 unreviewed pass ad1 \n", + "2239 unreviewed pass ad1 \n", + "2240 unreviewed pass ad1 \n", + "2241 unreviewed pass ad1 \n", + "\n", + "[2242 rows x 18 columns]" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# First, retrieve only the records -- you'll want to filter these results down before\n", + "# retrieving the files most likely\n", + "pss_api = PromoterSetSigAPI()\n", + "result = await pss_api.read()\n", + "result.get(\"metadata\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Filtering\n", + "\n", + "All API classes have a `params` attribute which stores the filtering parameters\n", + "which will be applied to the HTTP requests." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "pss_api.push_params({\"regulator_symbol\": \"GZF3\",\n", + " \"workflow\": \"nf_core_callingcards_1_0_0\",\n", + " \"data_usable\": \"pass\"})" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "ParamsDict({'regulator_symbol': 'GZF3', 'workflow': 'nf_core_callingcards_1_0_0', 'data_usable': 'pass'})" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pss_api.params" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Retrieving files from a Records and Files Object\n", + "\n", + "To retrieve files from a Records and Files endpoint object, do the following:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
iduploadermodifiersingle_bindingcomposite_bindingfileformatbackgroundupload_datemodified_datefilepromotersourceregulator_symbolregulator_locus_tagconditionrank_recalldata_usablebackground_name
06577adminadmin1837NaN512024-07-012024-07-01T16:43:50.145871-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccGZF3YJL110Cunknownpasspassad1
16580adminadmin1841NaN512024-07-012024-07-01T16:43:50.968078-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccGZF3YJL110Cunknownpasspassad1
26642adminadmin1902NaN512024-07-012024-07-01T16:43:54.969507-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccGZF3YJL110Cunknownpasspassad1
36651adminadmin1911NaN512024-07-012024-07-01T16:43:55.326651-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccGZF3YJL110Cunknownpasspassad1
46717adminadmin1960NaN512024-07-012024-07-01T16:44:00.500038-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccGZF3YJL110Cunknownpasspassad1
\n", + "
" + ], + "text/plain": [ + " id uploader modifier single_binding composite_binding fileformat \\\n", + "0 6577 admin admin 1837 NaN 5 \n", + "1 6580 admin admin 1841 NaN 5 \n", + "2 6642 admin admin 1902 NaN 5 \n", + "3 6651 admin admin 1911 NaN 5 \n", + "4 6717 admin admin 1960 NaN 5 \n", + "\n", + " background upload_date modified_date \\\n", + "0 1 2024-07-01 2024-07-01T16:43:50.145871-05:00 \n", + "1 1 2024-07-01 2024-07-01T16:43:50.968078-05:00 \n", + "2 1 2024-07-01 2024-07-01T16:43:54.969507-05:00 \n", + "3 1 2024-07-01 2024-07-01T16:43:55.326651-05:00 \n", + "4 1 2024-07-01 2024-07-01T16:44:00.500038-05:00 \n", + "\n", + " file promoter source \\\n", + "0 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + "1 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + "2 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + "3 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + "4 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + "\n", + " regulator_symbol regulator_locus_tag condition rank_recall data_usable \\\n", + "0 GZF3 YJL110C unknown pass pass \n", + "1 GZF3 YJL110C unknown pass pass \n", + "2 GZF3 YJL110C unknown pass pass \n", + "3 GZF3 YJL110C unknown pass pass \n", + "4 GZF3 YJL110C unknown pass pass \n", + "\n", + " background_name \n", + "0 ad1 \n", + "1 ad1 \n", + "2 ad1 \n", + "3 ad1 \n", + "4 ad1 " + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# note that retrieve_files is set to True\n", + "result = await pss_api.read(retrieve_files = True)\n", + "\n", + "# the metadata slot is the same as before\n", + "result.get(\"metadata\")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "# but now the data slot is a dictionary where the `id` are keys and the values\n", + "# are the files parsed into pandas dataframes\n", + "result.get(\"data\").get(\"6568\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Filtering on multiple items\n", + "\n", + "Some filters, and eventually all, will accept multiple arguments as a comma\n", + "separated string without spaces. For example:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "regulator_symbol: GZF3,RTG3, workflow: nf_core_callingcards_1_0_0, data_usable: pass\n" + ] + } + ], + "source": [ + "pss_api.push_params({\"regulator_symbol\": \"GZF3,RTG3\"})\n", + "\n", + "print(pss_api.params)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Parameters can be removed one by one" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "regulator_symbol: GZF3,RTG3, workflow: nf_core_callingcards_1_0_0, data_usable: pass\n" + ] + } + ], + "source": [ + "print(pss_api.params)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "regulator_symbol: GZF3,RTG3, workflow: nf_core_callingcards_1_0_0\n" + ] + } + ], + "source": [ + "pss_api.pop_params('data_usable')\n", + "\n", + "print(pss_api.params)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "or cleared entirely" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "pss_api.pop_params(None)\n", + "\n", + "print(pss_api.params)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Another example with Expression\n", + "\n", + "This is used to get an expression_id to use in the RankResponseAPI() below" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
iduploadermodifierregulator_idregulator_locus_tagregulator_symbolsource_nameassayupload_datemodified_date...replicatecontrolmechanismrestrictiontimefilenotesregulatorsourcepromotersetsig_processing
02516chasemchasem3578YJL110CGZF3mcisaac_oeoverexpression2024-07-012024-07-01T13:36:13.814201-05:00...1undefinedgevN15.0https://yeastregulatorydb-htcf-data.s3.amazona...strain_id:SMY156n ; date:201501011357False
12510chasemchasem3578YJL110CGZF3mcisaac_oeoverexpression2024-07-012024-07-01T13:36:08.810276-05:00...1undefinedgevP15.0https://yeastregulatorydb-htcf-data.s3.amazona...strain_id:SMY156 ; date:201501011357False
\n", + "

2 rows × 22 columns

\n", + "
" + ], + "text/plain": [ + " id uploader modifier regulator_id regulator_locus_tag regulator_symbol \\\n", + "0 2516 chasem chasem 3578 YJL110C GZF3 \n", + "1 2510 chasem chasem 3578 YJL110C GZF3 \n", + "\n", + " source_name assay upload_date modified_date \\\n", + "0 mcisaac_oe overexpression 2024-07-01 2024-07-01T13:36:13.814201-05:00 \n", + "1 mcisaac_oe overexpression 2024-07-01 2024-07-01T13:36:08.810276-05:00 \n", + "\n", + " ... replicate control mechanism restriction time \\\n", + "0 ... 1 undefined gev N 15.0 \n", + "1 ... 1 undefined gev P 15.0 \n", + "\n", + " file \\\n", + "0 https://yeastregulatorydb-htcf-data.s3.amazona... \n", + "1 https://yeastregulatorydb-htcf-data.s3.amazona... \n", + "\n", + " notes regulator source \\\n", + "0 strain_id:SMY156n ; date:20150101 135 7 \n", + "1 strain_id:SMY156 ; date:20150101 135 7 \n", + "\n", + " promotersetsig_processing \n", + "0 False \n", + "1 False \n", + "\n", + "[2 rows x 22 columns]" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "expression = ExpressionAPI()\n", + "\n", + "expression.push_params({\"regulator_symbol\": \"GZF3\",\n", + " \"lab\": \"mcisaac\",\n", + " \"time\": \"15\"})\n", + "\n", + "expression_res = await expression.read()\n", + "\n", + "expression_res.get(\"metadata\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Rank Response\n", + "\n", + "The rank response endpoint is slightly different than the others. It is implemented\n", + "asynchronously on the database side, and will run many tasks simultaneously. As such,\n", + "it uses `submit()` and `retrieve()` methods.\n", + "\n", + "Additionally, it is a POST request and all parameters are passed in a json list.\n", + "If you include, for a given dictionary, multiple items to promoetersetsig_ids and/or\n", + "expression_ids, those datasets will be aggregated prior to calculating the rank\n", + "response. The current parameters that may be passed for each are:\n", + "\n", + "- promotersetsig_ids: a list of promotersetsig_ids. If more than 1, then the data\n", + "will be aggregated prior to calcuating rank response\n", + "- expression_ids: a list of expression_ids. If more than 1, then the data\n", + "will be aggregated prior to calcuating rank response\n", + "- expression_effect_colname: name of the column to use for the rank response effect\n", + "- expression_effect_threshold: The threshold to use on abs(effect) to label responsive/\n", + "unresponsive genes\n", + "- expression_pvalue_threshold: the threshold to use below which to label responsive/\n", + "unresponsive genes\n", + "- rank_bin_size: the size of the bins by which rank response is summarized.\n", + "- rank_by_binding_effect: if this is \"true\", then rank by the binding effect first\n", + "rather than pvalue. This is used for harbison_chip and mcisaac_oe\n", + "- summarize_by_rank_bin: if this is set to false, the unsummarized rank response\n", + "(merged binding/response) is returned" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "rr_api = RankResponseAPI()\n", + "\n", + "data = [\n", + " {\n", + " \"promotersetsig_ids\": [\"5555\"],\n", + " \"expression_ids\": [\"2510\"],\n", + " \"rank_by_binding_effect\": \"true\",\n", + " }\n", + "]\n", + "\n", + "group_id = await rr_api.submit(post_dict=data)\n", + "\n", + "result = await rr_api.retrieve(group_id)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
regulator_symbolpromotersetsig_idsexpression_idsn_responsivetotal_expression_genesid
0GZF3555525108106175.0a46a15db-8318-476e-80aa-af5588909eef
\n", + "
" + ], + "text/plain": [ + " regulator_symbol promotersetsig_ids expression_ids n_responsive \\\n", + "0 GZF3 5555 2510 810 \n", + "\n", + " total_expression_genes id \n", + "0 6175.0 a46a15db-8318-476e-80aa-af5588909eef " + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "result.get(\"metadata\")" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
rank_binn_responsive_in_rankrandomn_successesresponse_ratiopvalueci_lowerci_upper
0520.13117420.4000000.1312110.0527450.853367
11030.13117450.5000000.0055100.1870860.812914
21540.13117490.6000000.0000270.3228700.836636
32000.13117490.4500000.0004900.2305780.684722
42520.131174110.4400000.0001490.2440240.650718
53010.131174120.4000000.0002240.2265580.593965
63510.131174130.3714290.0002960.2147320.550769
74000.131174130.3250000.0012780.1857290.491295
84510.131174140.3111110.0013630.1816590.466491
95000.131174140.2800000.0049460.1623110.424905
105510.131174150.2727270.0043780.1613800.409619
116000.131174150.2500000.0115260.1471860.378596
\n", + "
" + ], + "text/plain": [ + " rank_bin n_responsive_in_rank random n_successes response_ratio \\\n", + "0 5 2 0.131174 2 0.400000 \n", + "1 10 3 0.131174 5 0.500000 \n", + "2 15 4 0.131174 9 0.600000 \n", + "3 20 0 0.131174 9 0.450000 \n", + "4 25 2 0.131174 11 0.440000 \n", + "5 30 1 0.131174 12 0.400000 \n", + "6 35 1 0.131174 13 0.371429 \n", + "7 40 0 0.131174 13 0.325000 \n", + "8 45 1 0.131174 14 0.311111 \n", + "9 50 0 0.131174 14 0.280000 \n", + "10 55 1 0.131174 15 0.272727 \n", + "11 60 0 0.131174 15 0.250000 \n", + "\n", + " pvalue ci_lower ci_upper \n", + "0 0.131211 0.052745 0.853367 \n", + "1 0.005510 0.187086 0.812914 \n", + "2 0.000027 0.322870 0.836636 \n", + "3 0.000490 0.230578 0.684722 \n", + "4 0.000149 0.244024 0.650718 \n", + "5 0.000224 0.226558 0.593965 \n", + "6 0.000296 0.214732 0.550769 \n", + "7 0.001278 0.185729 0.491295 \n", + "8 0.001363 0.181659 0.466491 \n", + "9 0.004946 0.162311 0.424905 \n", + "10 0.004378 0.161380 0.409619 \n", + "11 0.011526 0.147186 0.378596 " + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "result.get(\"data\").get(result.get(\"metadata\").id[0])" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "df = result.get(\"data\").get(result.get(\"metadata\").id[0]).iloc[1:50,:]\n", + "\n", + "# Plot\n", + "plt.figure(figsize=(10, 6))\n", + "plt.plot(df['rank_bin'], df['response_ratio'], marker='o')\n", + "plt.title('GZF3 - promotersetsig_id 6577. McIsaac 15 2510')\n", + "plt.xlabel('Rank Bin')\n", + "plt.ylabel('Response Ratio')\n", + "plt.title('Response Ratio vs Rank Bin')\n", + "plt.grid(True)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# CallingCards Aggregated Data\n", + "\n", + "For regulators which have data generated in the Brentlab and processed through the\n", + "nf-core/callingcards:1.0.0 workflow, if that data has been manually (or eventually \n", + "automatically) QC reviewed,\n", + "*and if there are at least 2 samples which are labeled as data_usable*, then there will\n", + "exist a BindingConcatenated record to which both a BindingManualQC record *and* a\n", + "PromoterSetSig record are foreign keyed.\n", + "\n", + "To view the set of samples for which there is aggregated data, you may do the following:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "pss_api.pop_params(None)\n", + "\n", + "pss_api.push_params({\"source_name\": \"brent_nf_cc\", \"aggregated\": \"true\"})\n", + "\n", + "callingcards_aggregated_meta_res = await pss_api.read()\n", + "\n", + "callingcards_aggregated_meta_df = callingcards_aggregated_meta_res.get(\"metadata\")" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
iduploadermodifiersingle_bindingcomposite_bindingfileformatbackgroundupload_datemodified_datefilepromotersourceregulator_symbolregulator_locus_tagrank_recalldata_usablebackground_name
06867adminadminNaN6512024-07-062024-07-06T11:08:33.125644-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccWTM1YOR230Wpasspassad1
16868adminadminNaN3512024-07-062024-07-06T11:08:33.125640-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccMIG2YGL209Wpasspassad1
26869adminadminNaN1512024-07-062024-07-06T11:08:33.119818-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccCAT8YMR280Cpasspassad1
36870adminadminNaN5512024-07-062024-07-06T11:08:33.125605-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccPDR1YGL013Cpasspassad1
46871adminadminNaN4512024-07-062024-07-06T11:08:33.129564-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccPHO4YFR034Cpasspassad1
......................................................
666933adminadminNaN67512024-07-062024-07-06T11:08:45.567814-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccABF2YMR072Wfailpassad1
676934adminadminNaN68512024-07-062024-07-06T11:08:45.921894-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccUSV1YPL230Wpasspassad1
686935adminadminNaN69512024-07-062024-07-06T11:08:46.166036-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccMGA1YGR249Wpasspassad1
696936adminadminNaN70512024-07-062024-07-06T11:08:46.246482-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccCIN5YOR028Cpasspassad1
706937adminadminNaN71512024-07-062024-07-06T11:08:47.987665-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccROX1YPR065Wpasspassad1
\n", + "

71 rows × 17 columns

\n", + "
" + ], + "text/plain": [ + " id uploader modifier single_binding composite_binding fileformat \\\n", + "0 6867 admin admin NaN 6 5 \n", + "1 6868 admin admin NaN 3 5 \n", + "2 6869 admin admin NaN 1 5 \n", + "3 6870 admin admin NaN 5 5 \n", + "4 6871 admin admin NaN 4 5 \n", + ".. ... ... ... ... ... ... \n", + "66 6933 admin admin NaN 67 5 \n", + "67 6934 admin admin NaN 68 5 \n", + "68 6935 admin admin NaN 69 5 \n", + "69 6936 admin admin NaN 70 5 \n", + "70 6937 admin admin NaN 71 5 \n", + "\n", + " background upload_date modified_date \\\n", + "0 1 2024-07-06 2024-07-06T11:08:33.125644-05:00 \n", + "1 1 2024-07-06 2024-07-06T11:08:33.125640-05:00 \n", + "2 1 2024-07-06 2024-07-06T11:08:33.119818-05:00 \n", + "3 1 2024-07-06 2024-07-06T11:08:33.125605-05:00 \n", + "4 1 2024-07-06 2024-07-06T11:08:33.129564-05:00 \n", + ".. ... ... ... \n", + "66 1 2024-07-06 2024-07-06T11:08:45.567814-05:00 \n", + "67 1 2024-07-06 2024-07-06T11:08:45.921894-05:00 \n", + "68 1 2024-07-06 2024-07-06T11:08:46.166036-05:00 \n", + "69 1 2024-07-06 2024-07-06T11:08:46.246482-05:00 \n", + "70 1 2024-07-06 2024-07-06T11:08:47.987665-05:00 \n", + "\n", + " file promoter source \\\n", + "0 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + "1 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + "2 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + "3 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + "4 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + ".. ... ... ... \n", + "66 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + "67 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + "68 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + "69 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + "70 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + "\n", + " regulator_symbol regulator_locus_tag rank_recall data_usable \\\n", + "0 WTM1 YOR230W pass pass \n", + "1 MIG2 YGL209W pass pass \n", + "2 CAT8 YMR280C pass pass \n", + "3 PDR1 YGL013C pass pass \n", + "4 PHO4 YFR034C pass pass \n", + ".. ... ... ... ... \n", + "66 ABF2 YMR072W fail pass \n", + "67 USV1 YPL230W pass pass \n", + "68 MGA1 YGR249W pass pass \n", + "69 CIN5 YOR028C pass pass \n", + "70 ROX1 YPR065W pass pass \n", + "\n", + " background_name \n", + "0 ad1 \n", + "1 ad1 \n", + "2 ad1 \n", + "3 ad1 \n", + "4 ad1 \n", + ".. ... \n", + "66 ad1 \n", + "67 ad1 \n", + "68 ad1 \n", + "69 ad1 \n", + "70 ad1 \n", + "\n", + "[71 rows x 17 columns]" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "callingcards_aggregated_meta_df" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
iduploadermodifiersingle_bindingcomposite_bindingfileformatbackgroundupload_datemodified_datefilepromotersourceregulator_symbolregulator_locus_tagrank_recalldata_usablebackground_name
06873adminadminNaN7512024-07-062024-07-06T11:08:33.117009-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccGZF3YJL110Cpasspassad1
\n", + "
" + ], + "text/plain": [ + " id uploader modifier single_binding composite_binding fileformat \\\n", + "0 6873 admin admin NaN 7 5 \n", + "\n", + " background upload_date modified_date \\\n", + "0 1 2024-07-06 2024-07-06T11:08:33.117009-05:00 \n", + "\n", + " file promoter source \\\n", + "0 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + "\n", + " regulator_symbol regulator_locus_tag rank_recall data_usable background_name \n", + "0 GZF3 YJL110C pass pass ad1 " + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pss_api.push_params({\"regulator_symbol\": \"GZF3\"})\n", + "\n", + "callingcards_res = await pss_api.read()\n", + "\n", + "gzf3_callingcards_res = callingcards_res.get(\"metadata\")\n", + "\n", + "gzf3_callingcards_res\n", + "\n", + "#gzf3_callingcards_res[~gzf3_callingcards_res.composite_binding_id.isna()]" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "data = [\n", + " {\n", + " \"promotersetsig_ids\": [\"6873\"],\n", + " \"expression_ids\": [\"2510\"],\n", + " }\n", + "]\n", + "\n", + "group_id = await rr_api.submit(post_dict=data)\n", + "\n", + "agg_result = await rr_api.retrieve(group_id)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA04AAAIjCAYAAAA0vUuxAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAB/eUlEQVR4nO3deVxU9f7H8ffMsIzIJiKCS4pbhrikhlGZlmuZZd3Ka1lm2820utpqG9mv275velutvJXV9Wq2mHtloqhoibuIO4iCAi5sM+f3hzGJLDPgwAzwej4ePHLO+Z5zPjN8Q95+v+d7TIZhGAIAAAAAVMjs6QIAAAAAwNsRnAAAAADACYITAAAAADhBcAIAAAAAJwhOAAAAAOAEwQkAAAAAnCA4AQAAAIATBCcAAAAAcILgBAAAAABOEJwAAA3S0qVLZTKZtHTpUk+XUifccsstCgwMrPHr7Ny5UyaTSdOnT6/xawFAVRCcAKAapk+fLpPJ5Pjy8fFRy5Ytdcstt2jfvn2eLq9OKvmFueTLbDYrLCxMl112mRITE6t93nfffbde/BJel/vcU089VeZ7GxUVpSuuuEIrVqzwdHkA4BIfTxcAAHXZ008/rejoaOXn52vFihWaPn26li1bppSUFFmtVk+XVyeNGjVKl19+uWw2m7Zu3ap3331Xl1xyiVatWqWuXbtW+XzvvvuuwsPDdcstt5TafvHFF+vEiRPy8/NzU+W1oy73ualTpyowMFB2u1179uzR+++/r4svvlhJSUnq0aOHJKlNmzY6ceKEfH19PVssAJyG4AQAZ+Cyyy5T7969JUm33367wsPD9cILL+jbb7/V9ddf7+Hq6qaePXtq9OjRjtd9+/bVZZddpqlTp+rdd99123XMZrPXB43y1OU+d+211yo8PNzxesSIEYqNjdXXX3/tCE4mk6lOfl8A1H9M1QMAN+rbt68kKTU1tdT2zZs369prr1VYWJisVqt69+6tb7/9tlSboqIiTZkyRR07dpTValXTpk110UUXacGCBY42JfeZ7NixQ0OGDFHjxo3VokULPf300zIMo9T5jh07pvvvv1+tW7eWv7+/zj77bL388stl2plMJk2YMEGzZ89WbGys/P391aVLF82bN69Uu7y8PP3zn/9U27Zt5e/vr4iICA0aNEjJycml2q1cuVJDhw5VSEiIAgIC1K9fP/3222/V+0BV8Wf68ccf69JLL1VERIT8/f0VExOjqVOnlmrTtm1bbdiwQT///LNjmlj//v0lVXyP09dff61evXqpUaNGCg8P1+jRo51OhVu9erVMJpM++eSTMvt++uknmUwmfffdd5Jc/xxdVd7nU1hYqCeffFK9evVSSEiIGjdurL59+2rJkiWlji2ZHvnyyy/rvffeU/v27eXv76/zzjtPq1atcnrtdevWqVmzZurfv7+OHj1a5dojIyMlST4+f/07bnn3OJX0+3379mnEiBEKDAxUs2bN9MADD8hms1X5ugBQHYw4AYAb7dy5U5LUpEkTx7YNGzbowgsvVMuWLfXII4+ocePG+uqrrzRixAj997//1dVXXy3p5H0gzz33nG6//XbFxcUpNzdXq1evVnJysgYNGuQ4n81m09ChQ3X++efrxRdf1Lx585SQkKDi4mI9/fTTkiTDMHTllVdqyZIluu2229SjRw/99NNPevDBB7Vv3z699tprpepetmyZZs2apbvvvltBQUF688039be//U27d+9W06ZNJUl33XWXvvnmG02YMEExMTHKysrSsmXLtGnTJvXs2VOStHjxYl122WXq1auXEhISZDabHQHn119/VVxcnFs+U+nktK8uXbroyiuvlI+Pj+bOnau7775bdrtd48ePlyS9/vrruueeexQYGKjHHntMktS8efMKrzV9+nSNHTtW5513np577jkdOHBAb7zxhn777TetXbtWoaGh5R7Xu3dvtWvXTl999ZXGjBlTat/MmTPVpEkTDRkyxOXP8Uw/n9zcXH3wwQcaNWqU7rjjDuXl5enDDz/UkCFDSk2LK/H5558rLy9P//jHP2QymfTiiy/qmmuu0Y4dOyqcMrdq1SoNGTJEvXv31pw5c9SoUSOntWZnZ0uS7Ha79u3bp//7v/+T1Wp1aaTMZrNpyJAh6tOnj15++WUtXLhQr7zyitq3b69x48Y5PR4AzpgBAKiyjz/+2JBkLFy40Dh48KCxZ88e45tvvjGaNWtm+Pv7G3v27HG0HTBggNG1a1cjPz/fsc1utxsXXHCB0bFjR8e27t27G8OGDav0umPGjDEkGffcc0+pcw0bNszw8/MzDh48aBiGYcyePduQZDzzzDOljr/22msNk8lkbN++3bFNkuHn51dq2++//25IMt566y3HtpCQEGP8+PEV1ma3242OHTsaQ4YMMex2u2P78ePHjejoaGPQoEGVvre0tDRDkjFlyhTj4MGDRkZGhvHrr78a5513niHJ+Prrr0u1P378eJlzDBkyxGjXrl2pbV26dDH69etXpu2SJUsMScaSJUsMwzCMwsJCIyIiwoiNjTVOnDjhaPfdd98Zkownn3yy0vonT55s+Pr6GtnZ2Y5tBQUFRmhoqHHrrbc6tjn7HCtSlT5XXFxsFBQUlDr+8OHDRvPmzUvVUvKZN23atFTdc+bMMSQZc+fOdWwbM2aM0bhxY8MwDGPZsmVGcHCwMWzYsFL9uiIJCQmGpDJfoaGhxrx580q1Lanp448/LnVtScbTTz9dqu25555r9OrVy+n1AcAdmKoHAGdg4MCBatasmVq3bq1rr71WjRs31rfffqtWrVpJOvkv7IsXL9b111+vvLw8HTp0SIcOHVJWVpaGDBmibdu2OaaBhYaGasOGDdq2bZvT606YMMHx55KpdoWFhVq4cKEk6YcffpDFYtG9995b6rj7779fhmHoxx9/LPM+2rdv73jdrVs3BQcHa8eOHY5toaGhWrlypfbv319uTevWrdO2bdt0ww03KCsry/Fejx07pgEDBuiXX36R3W53+t4SEhLUrFkzRUZGqm/fvtq0aZNeeeUVXXvttaXanTrCkZOTo0OHDqlfv37asWOHcnJynF7ndKtXr1ZmZqbuvvvuUvfYDBs2TJ07d9b3339f6fEjR45UUVGRZs2a5dg2f/58HTlyRCNHjnRsc/Y5OuOsz0mSxWJxLHpht9uVnZ2t4uJi9e7du9wpgSNHjiw1YlUy/e/U73+JJUuWaMiQIRowYIBmzZolf39/l2v/73//qwULFmj+/Pn6+OOP1alTJ/3tb3/T8uXLXTr+rrvuKvW6b9++5dYIADWBqXoAcAbeeecdderUSTk5Ofroo4/0yy+/lPpFcvv27TIMQ0888YSeeOKJcs+RmZmpli1b6umnn9ZVV12lTp06KTY2VkOHDtVNN92kbt26lWpvNpvVrl27Uts6deok6a9pW7t27VKLFi0UFBRUqt0555zj2H+qs846q0xdTZo00eHDhx2vX3zxRY0ZM0atW7dWr169dPnll+vmm2921FIS+E6fqnaqnJycMlPuTnfnnXfquuuuU35+vhYvXqw333yz3PtYfvvtNyUkJCgxMVHHjx8vc52QkJBKr3O6ks/k7LPPLrOvc+fOWrZsWaXHd+/eXZ07d9bMmTN12223STo5TS88PFyXXnqpo52zz9EZZ32uxCeffKJXXnlFmzdvVlFRkWN7dHR0mbanf/9Lvkenfv8lKT8/X8OGDVOvXr301Vdflbo3yRUXX3xxqcUhrr32WnXs2FH33HOP1qxZU+mxVqtVzZo1K1Pn6TUCQE0hOAHAGYiLi3OscDZixAhddNFFuuGGG7RlyxbHssuS9MADDzjucTldhw4dJJ38pTI1NVVz5szR/Pnz9cEHH+i1117TtGnTdPvtt9fo+7BYLOVuN05ZSOL6669X37599b///U/z58/XSy+9pBdeeEGzZs3SZZdd5nivL730Upl7aEq48gDVjh07auDAgZKkK664QhaLRY888oguueQSx2edmpqqAQMGqHPnznr11VfVunVr+fn56YcfftBrr73m0shWTRg5cqT+9a9/6dChQwoKCtK3336rUaNGlQoYzj5HZ5z1OUmaMWOGbrnlFo0YMUIPPvigIiIiZLFY9Nxzz5VZZENy7fsvSf7+/rr88ss1Z84czZs3T1dccYXLn015AgMD1adPH82ZM0fHjh1T48aNK2xbUY0AUFuYqgcAblLyi+n+/fv19ttvS5JjFMHX11cDBw4s9+vUUaGwsDCNHTtWX3zxhfbs2aNu3brpqaeeKnUdu91eZnrS1q1bJZ1cRU46+Syc/fv3Ky8vr1S7zZs3O/ZXR1RUlO6++27Nnj1baWlpatq0qf71r39JkmOqX3BwcIXvtTrP5nnssccUFBSkxx9/3LFt7ty5Kigo0Lfffqt//OMfuvzyyzVw4MByFygwmUwuXafkM9myZUuZfVu2bHHpMxs5cqSKi4v13//+Vz/++KNyc3P197//vUy7yj7Hqiivz0nSN998o3bt2mnWrFm66aabNGTIEA0cOFD5+flVvsapTCaT/vOf/2jAgAG67rrryqxIWB3FxcWSVK1V+QCgNhGcAMCN+vfvr7i4OL3++uvKz89XRESE+vfvr3//+99KT08v0/7gwYOOP2dlZZXaFxgYqA4dOqigoKDMcaf+kmwYht5++235+vpqwIABkuR4gOyp7STptddek8lkcmlk41Q2m63MfUMRERFq0aKFo75evXqpffv2evnll8v9JfjU91oVoaGh+sc//qGffvpJ69atk/TX6MOpIyI5OTn6+OOPyxzfuHFjHTlyxOl1evfurYiICE2bNq3UZ/7jjz9q06ZNGjZsmNNznHPOOeratatmzpypmTNnKioqShdffLFjvyufY1Wd3uek8j+flStXKjExsVrXOJWfn59mzZql8847T8OHD1dSUlK1z5Wdna3ly5crMjJSERERZ1wbANQkpuoBgJs9+OCDuu666zR9+nTdddddeuedd3TRRRepa9euuuOOO9SuXTsdOHBAiYmJ2rt3r37//XdJUkxMjPr3769evXopLCxMq1evdixbfSqr1ap58+ZpzJgx6tOnj3788Ud9//33evTRRx33gAwfPlyXXHKJHnvsMe3cuVPdu3fX/PnzNWfOHP3zn/8stRCEK/Ly8tSqVStde+216t69uwIDA7Vw4UKtWrVKr7zyiqST91598MEHuuyyy9SlSxeNHTtWLVu21L59+7RkyRIFBwdr7ty51fpM77vvPr3++ut6/vnn9eWXX2rw4MHy8/PT8OHD9Y9//ENHjx7V+++/r4iIiDIBtVevXpo6daqeeeYZdejQQREREaXuOSrh6+urF154QWPHjlW/fv00atQox3Lkbdu21cSJE12qdeTIkXryySdltVp12223yWz+698oXfkcq+P0PnfFFVdo1qxZuvrqqzVs2DClpaVp2rRpiomJccvITqNGjfTdd9/p0ksv1WWXXaaff/5ZsbGxTo/75ptvFBgYKMMwtH//fn344Yc6fPiwpk2b5vLIIAB4jOcW9AOAuqtkaehVq1aV2Wez2Yz27dsb7du3N4qLiw3DMIzU1FTj5ptvNiIjIw1fX1+jZcuWxhVXXGF88803juOeeeYZIy4uzggNDTUaNWpkdO7c2fjXv/5lFBYWOtqULAmdmppqDB482AgICDCaN29uJCQkGDabrVQdeXl5xsSJE40WLVoYvr6+RseOHY2XXnqp1FLhhnFyOfLylsdu06aNMWbMGMMwTi6r/eCDDxrdu3c3goKCjMaNGxvdu3c33n333TLHrV271rjmmmuMpk2bGv7+/kabNm2M66+/3li0aFGln2nJMtQvvfRSuftvueUWw2KxOJZN//bbb41u3boZVqvVaNu2rfHCCy8YH330kSHJSEtLcxyXkZFhDBs2zAgKCjIkOZYmP3058hIzZ840zj33XMPf398ICwszbrzxRmPv3r2V1n6qbdu2OZbbXrZsWal9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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "agg_df = agg_result.get(\"data\").get(agg_result.get(\"metadata\").id[0]).iloc[1:50,:]\n", + "\n", + "# Plot\n", + "plt.figure(figsize=(10, 6))\n", + "plt.plot(agg_df['rank_bin'], agg_df['response_ratio'], marker='o')\n", + "plt.title('Aggregated GZF3. McIsaac 15 2510')\n", + "plt.xlabel('Rank Bin')\n", + "plt.ylabel('Response Ratio')\n", + "plt.title('Response Ratio vs Rank Bin')\n", + "plt.grid(True)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Caveats\n", + "\n", + "1. I have written the scripts to automatically check the redis queue for work and to \n", + " both launch celery worker nodes, and kill them when they are finished. But, though\n", + " they work if I run them manually, they have not worked when scheduled through a\n", + " cronjob. I'll work with Brian and Eric next week to figure out why.\n", + "\n", + "1. I haven't tested each of the endpoint APIs individually. Help is welcome." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/mkdocs.yml b/mkdocs.yml index a28f581..c367973 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,45 +1,141 @@ site_name: tfbpapi site_description: "A collection of objects and functions to work with calling cards sequencing tools" site_author: "ben mueller , chase mateusiak , michael brent " -site_url: "https://brentlab.github.io/tfbpapi/" +site_url: "https://brentlab.github.io/tfbpapi" repo_url: "https://github.com/brentlab/tfbpapi" -repo_name: "tfbpapi" -edit_uri: "edit/master/docs/" +repo_name: "brentlab/tfbpapi" +edit_uri: "edit/main/docs/" watch: ['tfbpapi', 'docs'] theme: name: material + palette: + # Palette toggle for light mode + - media: "(prefers-color-scheme: light)" + scheme: default + primary: indigo + accent: indigo + toggle: + icon: material/brightness-7 + name: Switch to dark mode + # Palette toggle for dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: indigo + accent: indigo + toggle: + icon: material/brightness-4 + name: Switch to light mode + features: + - navigation.tabs + - navigation.sections + - navigation.expand + - navigation.path + - navigation.top + - search.highlight + - search.share + - search.suggest + - content.code.copy + - content.code.select + - content.code.annotate + - content.action.edit + - content.action.view + icon: + repo: fontawesome/brands/github + edit: material/pencil + view: material/eye plugins: -- search -- autorefs -- section-index -- mkdocs-jupyter: + - search: + separator: '[\s\-,:!=\[\]()"`/]+|\.(?!\d)|&[lg]t;|(?!\b)(?=[A-Z][a-z])' + - autorefs + - section-index + - mkdocs-jupyter: remove_tag_config: - remove_input_tags: - - hide - remove_output_tags: - - hide -- mkdocstrings: - handlers: - python: - paths: [tfbpapi] # search packages in the src folder - merge_init_into_class: True - options: - docstring_style: 'sphinx' + remove_input_tags: + - hide + remove_output_tags: + - hide + execute: false + allow_errors: false + - mkdocstrings: + handlers: + python: + paths: [.] + import: + - https://docs.python.org/3/objects.inv + - https://numpy.org/doc/stable/objects.inv + - https://pandas.pydata.org/docs/objects.inv + options: + docstring_style: sphinx + show_source: true + show_root_heading: true + show_root_toc_entry: true + show_symbol_type_heading: true + show_symbol_type_toc: true + signature_crossrefs: true markdown_extensions: + - abbr + - admonition + - attr_list + - def_list + - footnotes + - md_in_html - smarty + - tables - toc: - permalink: True + permalink: true + title: On this page - sane_lists - pymdownx.arithmatex: generic: true + - pymdownx.betterem: + smart_enable: all + - pymdownx.caret + - pymdownx.details + - pymdownx.emoji: + emoji_generator: !!python/name:material.extensions.emoji.to_svg + emoji_index: !!python/name:material.extensions.emoji.twemoji + - pymdownx.highlight: + anchor_linenums: true + line_spans: __span + pygments_lang_class: true + - pymdownx.inlinehilite + - pymdownx.keys + - pymdownx.magiclink: + normalize_issue_symbols: true + repo_url_shorthand: true + user: brentlab + repo: tfbpapi + - pymdownx.mark + - pymdownx.smartsymbols + - pymdownx.snippets: + auto_append: + - includes/mkdocs.md - pymdownx.superfences: custom_fences: - name: mermaid class: mermaid - format: "!!python/name:pymdownx.superfences.fence_code_format" + format: !!python/name:pymdownx.superfences.fence_code_format + - pymdownx.tabbed: + alternate_style: true + combine_header_slug: true + slugify: !!python/object/apply:pymdownx.slugs.slugify + kwds: + case: lower + - pymdownx.tasklist: + custom_checkbox: true + - pymdownx.tilde + +extra: + social: + - icon: fontawesome/brands/github + link: https://github.com/brentlab/tfbpapi + name: GitHub Repository + version: + provider: mike + default: latest extra_javascript: - javascripts/mathjax.js @@ -47,36 +143,35 @@ extra_javascript: - https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js - js/init-mermaid.js +extra_css: + - stylesheets/extra.css + nav: -- Home: index.md -- Tutorials: - - Database Interface: tutorials/database_interface.ipynb - - LassoCV: tutorials/lassoCV.ipynb - - Interactor Modeling Workflow: tutorials/interactor_modeling_workflow.ipynb -- API: - - Models: - - Overview: ml_models/index.md - - SigmoidModel: ml_models/SigmoidModel.md - - Lasso Modeling: ml_models/lasso_modeling.md - - Database Interface: - - Records Only Classes: - - interface/BindingManualQCAPI.md - - interface/DataSourceAPI.md - - interface/DtoAPI.md - - interface/ExpressionManualQCAPI.md - - interface/FileFormatAPI.md - - interface/GenomicFeatureAPI.md - - interface/RegulatorAPI.md - - Records and Files Classes: - - BindingAPI: interface/BindingAPI.md - - BindingConcatenatedAPI: interface/BindingConcatenatedAPI.md - - CallingCardsBackgroundAPI: interface/CallingCardsBackgroundAPI.md - - ExpressionAPI: interface/ExpressionAPI.md - - PromoterSetAPI: interface/PromoterSetAPI.md - - PromoterSetSigAPI: interface/PromoterSetSigAPI.md - - Developer Classes: - - interface/AbstractAPI.md - - interface/AbstractRecordsAndFilesAPI.md - - interface/AbstractRecordsOnlyAPI.md - - interface/Cache.md - - interface/ParamsDict.md + - Home: index.md + - Tutorials: + - Database tfbpapi: tutorials/database_tfbpapi.ipynb + - API: + - Database tfbpapi: + - Records Only Classes: + - BindingManualQCAPI.md + - DataSourceAPI.md + - DtoAPI.md + - ExpressionManualQCAPI.md + - FileFormatAPI.md + - GenomicFeatureAPI.md + - RegulatorAPI.md + - Records and Files Classes: + - BindingAPI: BindingAPI.md + - BindingConcatenatedAPI: BindingConcatenatedAPI.md + - CallingCardsBackgroundAPI: CallingCardsBackgroundAPI.md + - ExpressionAPI: ExpressionAPI.md + - PromoterSetAPI: PromoterSetAPI.md + - PromoterSetSigAPI: PromoterSetSigAPI.md + - Developer Classes: + - AbstractAPI.md + - AbstractRecordsAndFilesAPI.md + - AbstractRecordsOnlyAPI.md + - AbstractHfAPI.md + - HfCacheManager.md + - Cache.md + - ParamsDict.md diff --git a/pyproject.toml b/pyproject.toml index 56c6f30..916be3d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,6 +20,8 @@ dotenv = "^0.9.9" pandas = "^2.3.1" huggingface-hub = "^0.34.4" types-requests = "^2.32.4.20250809" +datasets = "^4.0.0" +duckdb = "^1.3.2" [tool.poetry.group.dev.dependencies] @@ -27,6 +29,12 @@ pytest = "^8.3.5" pytest-snapshot = "^0.9.0" pytest-asyncio = "^0.26.0" types-requests = "^2.32.4.20250809" +mkdocs = "^1.6.1" +mkdocs-material = "^9.6.19" +mkdocs-autorefs = "^1.4.3" +mkdocs-section-index = "^0.3.10" +mkdocs-jupyter = "^0.25.1" +mkdocstrings = {extras = ["python"], version = "^0.30.0"} [tool.pytest.ini_options] diff --git a/tfbpapi/HfQueryAPI.py b/tfbpapi/HfQueryAPI.py new file mode 100644 index 0000000..5907071 --- /dev/null +++ b/tfbpapi/HfQueryAPI.py @@ -0,0 +1,363 @@ +from pathlib import Path +from typing import Any, Literal + +import duckdb +import pandas as pd +from datasets import Dataset, DatasetDict, load_dataset +from huggingface_hub import DatasetCard + +from .AbstractHfAPI import AbstractHfAPI + + +class HfQueryAPI(AbstractHfAPI): + """ + Concrete implementation of AbstractHfAPI with DuckDB query capabilities. + + This class provides seamless querying of Hugging Face datasets using SQL via DuckDB. + It automatically handles dataset downloading, parsing, and provides a simple query + interface. + + """ + + def __init__( + self, + repo_id: str, + repo_type: Literal["model", "dataset", "space"] = "dataset", + token: str | None = None, + cache_dir: str | Path | None = None, + auto_download_threshold_mb: float = 100.0, + auto_parse_datacard: bool = True, + ): + """ + Initialize the HF Query API client. + + :param repo_id: The repo identifier on HF (e.g., "user/dataset") + :param repo_type: One of {"model", "dataset", "space"}. Defaults to "dataset" + :param token: Optional HF token for private repos + :param cache_dir: HF cache directory for downloads + :param auto_download_threshold_mb: Auto-download threshold in MB + :param auto_parse_datacard: Whether to automatically parse datacard on init + + """ + super().__init__(repo_id, repo_type, token, cache_dir) + self.auto_download_threshold_mb = auto_download_threshold_mb + self._datasets: dict[str, Any] = {} + self._loaded_datasets: dict[str, Dataset | DatasetDict] = {} + self._duckdb_conn = duckdb.connect(":memory:") + self._table_filters: dict[str, str] = {} + + if auto_parse_datacard: + try: + self.datasets = self.parse_datacard() + except Exception as e: + self.logger.warning(f"Failed to auto-parse datacard: {e}") + self._datasets = {} + + @property + def datasets(self) -> dict[str, Any]: + """Parsed dataset configurations from the datacard.""" + return self._datasets + + @datasets.setter + def datasets(self, value: dict[str, Any]) -> None: + """Set the parsed datasets and update available tables.""" + self._datasets = value + self._update_available_tables() + + @property + def available_tables(self) -> list[str]: + """List of available table names for querying.""" + return list(self._datasets.keys()) + + def parse_datacard(self) -> dict[str, Any]: + """ + Parse the dataset card into a standardized format. + + :return: Dict mapping config names to their metadata + + """ + try: + card = DatasetCard.load(self.repo_id, self.repo_type) + data_dict = card.data.to_dict() + except Exception as e: + self.logger.error(f"Failed to load dataset card: {e}") + return {} + + parsed_datasets = {} + + for config in data_dict.get("configs", []): + config_name = config["config_name"] + + # Extract features for filtering/querying + features = {} + if "dataset_info" in config and "features" in config["dataset_info"]: + for feature in config["dataset_info"]["features"]: + features[feature["name"]] = { + "dtype": feature["dtype"], + "description": feature.get("description", ""), + } + + # Extract file paths + data_files = [] + for file_info in config.get("data_files", []): + data_files.append( + { + "path": file_info["path"], + "split": file_info.get("split", "train"), + } + ) + + parsed_datasets[config_name] = { + "features": features, + "data_files": data_files, + "config": config, + "loaded": False, + } + + return parsed_datasets + + def _update_available_tables(self) -> None: + """Update the logger with information about available tables.""" + if self._datasets: + self.logger.info(f"Available tables: {', '.join(self.available_tables)}") + + def _ensure_dataset_loaded(self, table_name: str) -> Dataset | DatasetDict: + """ + Ensure a dataset is loaded and available for querying. + + :param table_name: Name of the dataset configuration + :return: The loaded dataset + :raises ValueError: If table_name is not found + + """ + if table_name not in self._datasets: + raise ValueError( + f"Table '{table_name}' not found. " + f"Available tables: {self.available_tables}" + ) + + if table_name in self._loaded_datasets: + return self._loaded_datasets[table_name] + + # Download the dataset if not already downloaded + if not self.snapshot_path: + self.logger.info(f"Downloading dataset for table '{table_name}'...") + self.download(auto_download_threshold_mb=self.auto_download_threshold_mb) + + # Load the specific dataset configuration + try: + self.logger.info(f"Loading dataset configuration '{table_name}'...") + dataset = load_dataset( + str(self.snapshot_path), name=table_name, keep_in_memory=False + ) + self._loaded_datasets[table_name] = dataset + self._datasets[table_name]["loaded"] = True + + # Register with DuckDB + self._register_dataset_with_duckdb(table_name, dataset) + + return dataset + except Exception as e: + self.logger.error(f"Failed to load dataset '{table_name}': {e}") + raise + + def _register_dataset_with_duckdb( + self, table_name: str, dataset: Dataset | DatasetDict + ) -> None: + """Register a dataset with DuckDB for SQL querying.""" + try: + if isinstance(dataset, DatasetDict): + # Register each split as a separate view + for split_name, split_dataset in dataset.items(): + view_name = ( + f"{table_name}_{split_name}" + if split_name != "train" + else table_name + ) + df = split_dataset.to_pandas() + self._duckdb_conn.register(view_name, df) + self.logger.debug( + f"Registered view '{view_name}' with {len(df)} rows" + ) + else: + # Single dataset + df = dataset.to_pandas() + self._duckdb_conn.register(table_name, df) + self.logger.debug( + f"Registered table '{table_name}' with {len(df)} rows" + ) + except Exception as e: + self.logger.error( + f"Failed to register dataset '{table_name}' with DuckDB: {e}" + ) + raise + + def query(self, sql: str, table_name: str | None = None) -> pd.DataFrame: + """ + Execute a SQL query against the dataset. + + :param sql: SQL query string + :param table_name: Optional table name to ensure is loaded. If not provided, + attempts to infer from the SQL query + :return: Query results as a pandas DataFrame + + """ + # If table_name not provided, try to infer from available tables + if table_name is None: + sql_lower = sql.lower() + for available_table in self.available_tables: + if available_table in sql_lower: + table_name = available_table + break + + # If we found a table name, ensure it's loaded + if table_name: + self._ensure_dataset_loaded(table_name) + elif not self._loaded_datasets: + # If no datasets are loaded and we couldn't infer, + # try to load the first one + if self.available_tables: + self._ensure_dataset_loaded(self.available_tables[0]) + + # Apply any table filters to the query + modified_sql = self._apply_table_filters(sql) + + try: + result = self._duckdb_conn.execute(modified_sql).fetchdf() + self.logger.debug(f"Query returned {len(result)} rows") + return result + except Exception as e: + self.logger.error(f"Query failed: {e}") + if modified_sql != sql: + self.logger.debug(f"Original query: {sql}") + self.logger.debug(f"Modified query: {modified_sql}") + raise + + def describe_table(self, table_name: str) -> pd.DataFrame: + """ + Get information about a table's structure. + + :param table_name: Name of the table to describe + :return: DataFrame with column information + + """ + self._ensure_dataset_loaded(table_name) + return self.query(f"DESCRIBE {table_name}") + + def sample(self, table_name: str, n: int = 5) -> pd.DataFrame: + """ + Get a sample of rows from a table. + + :param table_name: Name of the table to sample + :param n: Number of rows to return + :return: Sample DataFrame + + """ + return self.query(f"SELECT * FROM {table_name} LIMIT {n}", table_name) + + def count(self, table_name: str) -> int: + """ + Get the number of rows in a table. + + :param table_name: Name of the table to count + :return: Number of rows + + """ + result = self.query(f"SELECT COUNT(*) as count FROM {table_name}", table_name) + return result.iloc[0]["count"] + + def get_columns(self, table_name: str) -> list[str]: + """ + Get column names for a table. + + :param table_name: Name of the table + :return: List of column names + + """ + if table_name not in self._datasets: + raise ValueError(f"Table '{table_name}' not found") + + return list(self._datasets[table_name]["features"].keys()) + + def _apply_table_filters(self, sql: str) -> str: + """ + Apply table filters to a SQL query by modifying table references. + + :param sql: Original SQL query + :return: Modified SQL query with filters applied + + """ + if not self._table_filters: + return sql + + modified_sql = sql + + # Apply filters by replacing table references with filtered subqueries + for table_name, filter_condition in self._table_filters.items(): + import re + + # Simple pattern to match table references in FROM and JOIN clauses + pattern = rf"\b{re.escape(table_name)}\b" + replacement = f"(SELECT * FROM {table_name} WHERE {filter_condition})" + + # Only replace if we find the table name in the SQL + if re.search(pattern, modified_sql, re.IGNORECASE): + # Check if it's already wrapped in a filtered subquery to + # avoid double-wrapping + if not re.search( + rf"\(SELECT.*FROM\s+{re.escape(table_name)}\s+WHERE", + modified_sql, + re.IGNORECASE, + ): + modified_sql = re.sub( + pattern, replacement, modified_sql, flags=re.IGNORECASE + ) + + return modified_sql + + def get_table_filter(self, table_name: str) -> str | None: + """ + Get the current filter for a table. + + :param table_name: Name of the table + :return: Current filter SQL condition or None if no filter is set + + """ + return self._table_filters.get(table_name) + + def set_table_filter(self, table_name: str, filter_condition: str) -> None: + """ + Set a filter condition for a table that will be applied to all queries. + + :param table_name: Name of the table + :param filter_condition: SQL WHERE condition (without the WHERE keyword) + + """ + self._table_filters[table_name] = filter_condition + self.logger.debug(f"Set filter for table '{table_name}': {filter_condition}") + + def remove_table_filter(self, table_name: str) -> None: + """ + Remove any filter condition for a table. + + :param table_name: Name of the table + + """ + removed_filter = self._table_filters.pop(table_name, None) + if removed_filter: + self.logger.debug( + f"Removed filter for table '{table_name}': {removed_filter}" + ) + + def close(self) -> None: + """Close the DuckDB connection.""" + if self._duckdb_conn: + self._duckdb_conn.close() + + def __enter__(self): + """Context manager entry.""" + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + """Context manager exit.""" + self.close() diff --git a/tfbpapi/HfRankResponse.py b/tfbpapi/HfRankResponse.py new file mode 100644 index 0000000..b52f6f9 --- /dev/null +++ b/tfbpapi/HfRankResponse.py @@ -0,0 +1,482 @@ +import logging + +import duckdb +import pandas as pd + +from .IncrementalAnalysisDB import IncrementalAnalysisDB + + +class HfRankResponse: + """ + A class to provide an API to compute and analyze "rank response", which is defined + as the cumulative number of responsive targets (e.g., genes) binned by their binding + rank score for each regulator sample pair of binding and perturbation response data. + + Handles multiple dataset comparisons and stores all results in a shared database. + + """ + + def __init__(self, db: IncrementalAnalysisDB): + """ + Initialize RankResponse analyzer with database connection. + + :param db: IncrementalAnalysisDB instance for storing results + + """ + self.db = db + self.logger = logging.getLogger(__name__) + + def compute( + self, + ranking_api, + response_api, + ranking_table: str, + response_table: str, + ranking_score_column: str, + response_column: str, + comparison_id: str | None = None, + regulator_column: str = "regulator_locus_tag", + target_column: str = "target_locus_tag", + bin_size: int = 5, + force_recompute: bool = False, + responsive_condition: str | None = None, + ) -> pd.DataFrame: + """ + Compute rank response for a specific dataset comparison. + + :param ranking_api: API instance for ranking/binding data + :param response_api: API instance for response/perturbation data + :param ranking_table: Name of ranking table in ranking_api + :param response_table: Name of response table in response_api + :param ranking_score_column: Column name for ranking scores + :param response_column: Column name for response values + :param comparison_id: Unique identifier for this comparison (auto-generated if + None) + :param regulator_column: Column name for regulator identifiers + :param target_column: Column name for target identifiers + :param bin_size: Size of ranking bins + :param force_recompute: Whether to recompute existing results + :param responsive_condition: SQL condition to define responsive (default: IS NOT + NULL) + :return: DataFrame with computed results + + """ + # Generate comparison ID if not provided + if comparison_id is None: + comparison_id = f"{ranking_table}_vs_{response_table}" + + table_name = f"rank_response_{comparison_id}" + + # Get all regulators from ranking data + ranking_api._ensure_dataset_loaded(ranking_table) + all_regulators = ranking_api.query( + f"SELECT DISTINCT {regulator_column} FROM {ranking_table}" + )[regulator_column].tolist() + + # Check which regulators already have results + if not force_recompute and self.db.table_exists(table_name): + existing_regulators = set( + self.db.query(f"SELECT DISTINCT {regulator_column} FROM {table_name}")[ + regulator_column + ].tolist() + ) + + new_regulators = [ + reg for reg in all_regulators if reg not in existing_regulators + ] + self.logger.info( + f"Found {len(existing_regulators)} existing regulators, " + f"{len(new_regulators)} new ones" + ) + else: + new_regulators = all_regulators + self.logger.info(f"Computing analysis for {len(new_regulators)} regulators") + + if not new_regulators: + self.logger.info("No new regulators to analyze") + return self.db.query( + f"SELECT * FROM {table_name} ORDER BY " + f"{regulator_column}, {target_column}" + ) + + # Apply filters to focus on new regulators + # For analytical queries, escape and format the values safely + escaped_values = [ + f"'{reg.replace(chr(39), chr(39)+chr(39))}'" for reg in new_regulators + ] + regulator_filter = f"{regulator_column} IN ({', '.join(escaped_values)})" + + # Temporarily add filters for new regulators only + original_ranking_filter = ranking_api.get_table_filter(ranking_table) + original_response_filter = response_api.get_table_filter(response_table) + + new_ranking_filter = regulator_filter + new_response_filter = regulator_filter + + if original_ranking_filter: + new_ranking_filter = f"({original_ranking_filter}) AND ({regulator_filter})" + if original_response_filter: + new_response_filter = ( + f"({original_response_filter}) AND ({regulator_filter})" + ) + + ranking_api.set_table_filter(ranking_table, new_ranking_filter) + response_api.set_table_filter(response_table, new_response_filter) + + try: + # Load filtered data from both APIs + ranking_api._ensure_dataset_loaded(ranking_table) + response_api._ensure_dataset_loaded(response_table) + + ranking_conn = ranking_api._duckdb_conn + response_conn = response_api._duckdb_conn + + # Create temporary connection for analysis + temp_conn = duckdb.connect(":memory:") + + try: + # Get filtered data + ranking_df = ranking_conn.execute( + f"SELECT * FROM {ranking_table}" + ).fetchdf() + response_df = response_conn.execute( + f"SELECT * FROM {response_table}" + ).fetchdf() + + temp_conn.register("ranking_data", ranking_df) + temp_conn.register("response_data", response_df) + + # Execute intermediate analysis SQL + # Set default responsive condition if not provided + if responsive_condition is None: + responsive_condition = f"b.{response_column} IS NOT NULL" + else: + # Replace column references in the condition + responsive_condition = responsive_condition.replace( + response_column, f"b.{response_column}" + ) + + intermediate_sql = f""" + WITH binned_data AS ( + SELECT + a.{regulator_column}, + a.{target_column}, + a.{ranking_score_column}, + b.{response_column}, + CASE WHEN {responsive_condition} THEN 1 ELSE 0 END + AS responsive, + CEILING(ROW_NUMBER() OVER ( + PARTITION BY a.{regulator_column} + ORDER BY a.{regulator_column}, a.{target_column} + ) / {bin_size}.0) * {bin_size} AS bin_label + FROM ranking_data AS a + LEFT JOIN response_data AS b + ON a.{regulator_column} = b.{regulator_column} + AND a.{target_column} = b.{target_column} + ) + SELECT + {regulator_column}, + {target_column}, + {ranking_score_column}, + {response_column}, + responsive, + bin_label, + SUM(responsive) OVER ( + PARTITION BY {regulator_column} + ORDER BY bin_label + RANGE UNBOUNDED PRECEDING + ) AS cumulative_responsive + FROM binned_data + ORDER BY {regulator_column}, bin_label, {target_column} + """ + + new_results = temp_conn.execute(intermediate_sql).fetchdf() + + finally: + temp_conn.close() + + # Save new intermediate results + if len(new_results) > 0: + self.db.append_results( + new_results, + table_name, + analysis_type="response_rate_intermediate", + parameters={ + "ranking_table": ranking_table, + "response_table": response_table, + "ranking_score_column": ranking_score_column, + "response_column": response_column, + "bin_size": bin_size, + "result_type": "intermediate", + }, + description=( + f"Added intermediate data for {len(new_regulators)} " + "new regulators" + ), + deduplicate_on=[regulator_column, target_column], + ) + + self.logger.info( + f"Saved {len(new_results)} intermediate records to database" + ) + + # Return complete results from database + return self.db.query( + f"SELECT * FROM {table_name} ORDER BY {regulator_column}, bin_label, " + f"{target_column}" + ) + + finally: + # Restore original filters + if original_ranking_filter: + ranking_api.set_table_filter(ranking_table, original_ranking_filter) + else: + ranking_api.remove_table_filter(ranking_table) + + if original_response_filter: + response_api.set_table_filter(response_table, original_response_filter) + else: + response_api.remove_table_filter(response_table) + + def get_comparisons(self) -> list[str]: + """ + Get list of all computed comparisons. + + :return: List of comparison identifiers + + """ + tables = self.db.list_tables() + rank_response_tables = [ + table + for table in tables + if table.startswith("rank_response_") and table != "rank_response_metadata" + ] + return [table.replace("rank_response_", "") for table in rank_response_tables] + + def get_bin_summary( + self, + comparison_id: str, + regulator_column: str = "regulator_locus_tag", + bin_size: int = 5, + regulators_filter: list[str] | None = None, + ) -> pd.DataFrame: + """ + Generate bin-level summary for a specific comparison. + + :param comparison_id: Identifier for the comparison to summarize + :param regulator_column: Column name for regulator identifiers + :param bin_size: Bin size used in analysis + :param regulators_filter: Optional list of regulators to include + :return: DataFrame with bin summary results + + """ + intermediate_table_name = f"rank_response_{comparison_id}" + + if not self.db.table_exists(intermediate_table_name): + raise ValueError( + f"Intermediate table '{intermediate_table_name}' does not exist. " + "Run compute() first." + ) + + # Build WHERE clause for regulator filter + where_clause = "" + if regulators_filter: + # For analytical queries, escape and format the values safely + escaped_values = [ + f"'{reg.replace(chr(39), chr(39)+chr(39))}'" + for reg in regulators_filter + ] + where_clause = f"WHERE {regulator_column} IN ({', '.join(escaped_values)})" + + # Generate summary from intermediate data + summary_sql = f""" + SELECT + {regulator_column}, + bin_label, + COUNT(*) as records_in_bin, + SUM(responsive) as responsive_in_bin, + MAX(cumulative_responsive) as cumulative_responsive, + MAX(cumulative_responsive) / bin_label as response_rate + FROM {intermediate_table_name} + {where_clause} + GROUP BY {regulator_column}, bin_label + ORDER BY {regulator_column}, bin_label + """ + + summary_results = self.db.query(summary_sql) + + self.logger.info( + f"Generated summary for {len(summary_results)} regulator-bin combinations" + ) + return summary_results + + def get_regulator_summary( + self, + comparison_id: str, + regulator_column: str = "regulator_locus_tag", + max_bin_label: int | None = None, + ) -> pd.DataFrame: + """ + Generate regulator-level performance summary for a comparison. + + :param comparison_id: Identifier for the comparison + :param regulator_column: Column name for regulator identifiers + :param max_bin_label: Maximum bin label to consider (e.g., 20 for top 20 + targets) + :return: DataFrame with regulator-level summary statistics + + """ + intermediate_table_name = f"rank_response_{comparison_id}" + + if not self.db.table_exists(intermediate_table_name): + raise ValueError( + f"Intermediate table '{intermediate_table_name}' does not exist." + ) + + where_clause = "" + if max_bin_label: + where_clause = f"WHERE bin_label <= {max_bin_label}" + + regulator_summary_sql = f""" + SELECT + {regulator_column}, + COUNT(*) as total_targets, + SUM(responsive) as total_responsive, + COUNT(DISTINCT bin_label) as num_bins, + MAX(cumulative_responsive) as max_cumulative_responsive, + MAX(bin_label) as max_bin_label, + MAX(cumulative_responsive) / MAX(bin_label) + as overall_response_rate, + AVG(CASE WHEN bin_label <= 5 THEN responsive ELSE NULL END) + as top5_response_rate, + AVG(CASE WHEN bin_label <= 10 THEN responsive ELSE NULL END) + as top10_response_rate, + AVG(CASE WHEN bin_label <= 20 THEN responsive ELSE NULL END) + as top20_response_rate + FROM {intermediate_table_name} + {where_clause} + GROUP BY {regulator_column} + ORDER BY overall_response_rate DESC + """ + + return self.db.query(regulator_summary_sql) + + def summarize( + self, + comparison_id: str, + summary_type: str = "bin", + regulator_column: str = "regulator_locus_tag", + bin_size: int = 5, + regulators_filter: list[str] | None = None, + max_bin_label: int | None = None, + ) -> pd.DataFrame: + """ + Generate summary for a specific comparison. + + :param comparison_id: Identifier for the comparison to summarize + :param summary_type: Type of summary ('bin' or 'regulator') + :param regulator_column: Column name for regulator identifiers + :param bin_size: Bin size used in analysis + :param regulators_filter: Optional list of regulators to include + :param max_bin_label: Maximum bin label to consider (for regulator summaries) + :return: DataFrame with summary results + + """ + if summary_type == "bin": + return self.get_bin_summary( + comparison_id=comparison_id, + regulator_column=regulator_column, + bin_size=bin_size, + regulators_filter=regulators_filter, + ) + elif summary_type == "regulator": + return self.get_regulator_summary( + comparison_id=comparison_id, + regulator_column=regulator_column, + max_bin_label=max_bin_label, + ) + else: + raise ValueError(f"Unknown summary type: {summary_type}") + + def query(self, sql: str) -> pd.DataFrame: + """ + Execute custom SQL query on the database. + + :param sql: SQL query to execute + :return: DataFrame with query results + + """ + return self.db.query(sql) + + def get_comparison_data( + self, + comparison_id: str, + regulator_filter: list[str] | None = None, + limit: int | None = None, + ) -> pd.DataFrame: + """ + Get raw data for a specific comparison. + + :param comparison_id: Identifier for the comparison + :param regulator_filter: Optional list of regulators to filter + :param limit: Optional limit on number of records + :return: DataFrame with raw comparison data + + """ + table_name = f"rank_response_{comparison_id}" + + if not self.db.table_exists(table_name): + raise ValueError(f"No results found for comparison '{comparison_id}'") + + filters = {} + if regulator_filter: + filters["regulator_locus_tag"] = regulator_filter + + return self.db.get_results(table_name, filters=filters, limit=limit) + + def compare_across_datasets( + self, + comparison_ids: list[str], + regulator_column: str = "regulator_locus_tag", + metric_columns: list[str] = ["overall_response_rate", "top10_response_rate"], + ) -> pd.DataFrame: + """ + Compare regulator performance across multiple dataset comparisons. + + :param comparison_ids: List of comparison identifiers to compare + :param regulator_column: Column name for regulator identifiers + :param metric_columns: Performance metrics to compare + :return: DataFrame with cross-comparison results + + """ + comparison_data = [] + + for comp_id in comparison_ids: + summary = self.summarize(comp_id, summary_type="regulator") + summary["comparison_id"] = comp_id + comparison_data.append( + summary[[regulator_column, "comparison_id"] + metric_columns] + ) + + if not comparison_data: + return pd.DataFrame() + + # Combine all comparisons + combined = pd.concat(comparison_data, ignore_index=True) + + # Pivot to have comparisons as columns + result_dfs = [] + for metric in metric_columns: + pivot = combined.pivot( + index=regulator_column, columns="comparison_id", values=metric + ) + pivot.columns = [f"{metric}_{col}" for col in pivot.columns] + result_dfs.append(pivot) + + if len(result_dfs) == 1: + return result_dfs[0].reset_index() + else: + final_result = result_dfs[0] + for df in result_dfs[1:]: + final_result = final_result.join(df) + return final_result.reset_index() diff --git a/tfbpapi/IncrementalAnalysisDB.py b/tfbpapi/IncrementalAnalysisDB.py new file mode 100644 index 0000000..9e8d53e --- /dev/null +++ b/tfbpapi/IncrementalAnalysisDB.py @@ -0,0 +1,340 @@ +import logging +from pathlib import Path +from typing import Any + +import duckdb +import pandas as pd + + +class IncrementalAnalysisDB: + """ + Class for managing incremental analysis results in DuckDB. + + Supports appending new results, updating existing ones, and maintaining analysis + metadata for tracking what's been computed. + + """ + + def __init__(self, db_path: str): + """ + Initialize connection to persistent DuckDB database. + + :param db_path: Path to the DuckDB database file + + """ + self.db_path = db_path + Path(db_path).parent.mkdir(parents=True, exist_ok=True) + self.conn = duckdb.connect(db_path) + self.logger = logging.getLogger(__name__) + + # Create metadata table to track analyses + self._ensure_metadata_table() + + def _ensure_metadata_table(self): + """Create metadata table if it doesn't exist.""" + self.conn.execute( + """ + CREATE TABLE IF NOT EXISTS analysis_metadata ( + table_name VARCHAR PRIMARY KEY, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + total_records INTEGER, + analysis_type VARCHAR, + parameters JSON, + description TEXT + ) + """ + ) + + def append_results( + self, + new_results: pd.DataFrame, + table_name: str, + analysis_type: str = "response_rate", + parameters: dict | None = None, + description: str | None = None, + deduplicate_on: list[str] | None = None, + ) -> int: + """ + Append new analysis results to an existing table. + + :param new_results: DataFrame with new results to append + :param table_name: Name of the target table + :param analysis_type: Type of analysis for metadata + :param parameters: Parameters used in the analysis + :param description: Description of the analysis + :param deduplicate_on: Column names to deduplicate on + :return: Number of records added + + """ + if new_results.empty: + self._update_metadata(table_name, 0, analysis_type, parameters, description) + return 0 + + # Handle deduplication if specified + if deduplicate_on and self.table_exists(table_name): + existing_data = self.get_results(table_name) + if not existing_data.empty: + # Remove duplicates based on specified columns + merged = pd.merge( + new_results, + existing_data[deduplicate_on], + on=deduplicate_on, + how="left", + indicator=True, + ) + new_results = merged[merged["_merge"] == "left_only"].drop( + "_merge", axis=1 + ) + + # Insert new data + if not new_results.empty: + self.conn.register("new_data", new_results) + if self.table_exists(table_name): + self.conn.execute(f"INSERT INTO {table_name} SELECT * FROM new_data") + else: + self.conn.execute( + f"CREATE TABLE {table_name} AS SELECT * FROM new_data" + ) + self.conn.unregister("new_data") + + records_added = len(new_results) + self._update_metadata( + table_name, records_added, analysis_type, parameters, description + ) + + return records_added + + def update_results( + self, updated_data: pd.DataFrame, table_name: str, key_columns: list[str] + ) -> int: + """ + Update existing records in a table. + + :param updated_data: DataFrame with updated values + :param table_name: Name of the target table + :param key_columns: Columns to match records on + :return: Number of records updated + + """ + if not self.table_exists(table_name) or updated_data.empty: + return 0 + + records_updated = 0 + self.conn.register("update_data", updated_data) + + # Build SET clause for non-key columns + non_key_columns = [ + col for col in updated_data.columns if col not in key_columns + ] + set_clause = ", ".join( + [f"{col} = update_data.{col}" for col in non_key_columns] + ) + + # Build WHERE clause for key columns + where_clause = " AND ".join( + [f"{table_name}.{col} = update_data.{col}" for col in key_columns] + ) + + update_query = f""" + UPDATE {table_name} + SET {set_clause} + FROM update_data + WHERE {where_clause} + """ + + self.conn.execute(update_query) + records_updated = len(updated_data) + + self.conn.unregister("update_data") + self._update_metadata_timestamp(table_name) + + return records_updated + + def query(self, sql: str) -> pd.DataFrame: + """ + Execute a SQL query and return results as DataFrame. + + :param sql: SQL query to execute + :return: DataFrame with query results + + """ + return self.conn.execute(sql).fetchdf() + + def get_results( + self, + table_name: str, + filters: dict[str, Any] | None = None, + limit: int | None = None, + ) -> pd.DataFrame: + """ + Retrieve results from a table. + + :param table_name: Name of the table to query + :param filters: Optional filters to apply + :param limit: Optional limit on number of records + :return: DataFrame with results + + """ + if not self.table_exists(table_name): + raise ValueError(f"Table {table_name} does not exist") + + query = f"SELECT * FROM {table_name}" + + if filters: + where_conditions = [] + for column, values in filters.items(): + if isinstance(values, list): + values_str = ", ".join( + [f"'{v}'" if isinstance(v, str) else str(v) for v in values] + ) + where_conditions.append(f"{column} IN ({values_str})") + else: + if isinstance(values, str): + where_conditions.append(f"{column} = '{values}'") + else: + where_conditions.append(f"{column} = {values}") + + if where_conditions: + query += " WHERE " + " AND ".join(where_conditions) + + if limit: + query += f" LIMIT {limit}" + + return self.conn.execute(query).fetchdf() + + def table_exists(self, table_name: str) -> bool: + """Check if a table exists in the database.""" + result = self.conn.execute( + """ + SELECT table_name FROM information_schema.tables + WHERE table_name = ? AND table_schema = 'main' + """, + [table_name], + ).fetchall() + return len(result) > 0 + + def drop_table(self, table_name: str) -> None: + """Drop a table and its metadata.""" + if self.table_exists(table_name): + self.conn.execute(f"DROP TABLE {table_name}") + self.conn.execute( + "DELETE FROM analysis_metadata WHERE table_name = ?", [table_name] + ) + + def get_table_info(self, table_name: str) -> dict[str, Any]: + """Get metadata information about a table.""" + if not self.table_exists(table_name): + raise ValueError(f"Table {table_name} does not exist") + + result = self.conn.execute( + """ + SELECT * FROM analysis_metadata WHERE table_name = ? + """, + [table_name], + ).fetchdf() + + if result.empty: + raise ValueError(f"No metadata found for table {table_name}") + + return result.iloc[0].to_dict() + + def list_tables(self) -> list[str]: + """List all tables in the database.""" + result = self.conn.execute( + """ + SELECT table_name FROM information_schema.tables + WHERE table_schema = 'main' + """ + ).fetchall() + return [row[0] for row in result] + + def get_table_schema(self, table_name: str) -> list[dict[str, str]]: + """Get schema information for a table.""" + if not self.table_exists(table_name): + raise ValueError(f"Table {table_name} does not exist") + + result = self.conn.execute(f"DESCRIBE {table_name}").fetchall() + # TODO: fix the mypy ignore/typing + return [ + { + "column_name": row[0], + "column_type": row[1], + "null": row[2], + "key": row[3] if len(row) > 3 else None, # type: ignore + "default": row[4] if len(row) > 4 else None, # type: ignore + "extra": row[5] if len(row) > 5 else None, # type: ignore + } + for row in result + ] + + def close(self) -> None: + """Close the database connection.""" + if hasattr(self, "conn"): + self.conn.close() + + def __enter__(self): + """Context manager entry.""" + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + """Context manager exit.""" + self.close() + + def _update_metadata( + self, + table_name: str, + records_added: int, + analysis_type: str, + parameters: dict | None, + description: str | None, + ) -> None: + """Update or insert metadata for a table.""" + import json + + # Check if metadata exists + existing = self.conn.execute( + """ + SELECT total_records FROM analysis_metadata WHERE table_name = ? + """, + [table_name], + ).fetchall() + + if existing: + # Update existing metadata + new_total = existing[0][0] + records_added + self.conn.execute( + """ + UPDATE analysis_metadata + SET last_updated = CURRENT_TIMESTAMP, total_records = ? + WHERE table_name = ? + """, + [new_total, table_name], + ) + else: + # Insert new metadata + self.conn.execute( + """ + INSERT INTO analysis_metadata + (table_name, total_records, analysis_type, parameters, description) + VALUES (?, ?, ?, ?, ?) + """, + [ + table_name, + records_added, + analysis_type, + json.dumps(parameters) if parameters else None, + description, + ], + ) + + def _update_metadata_timestamp(self, table_name: str) -> None: + """Update the last_updated timestamp for a table.""" + self.conn.execute( + """ + UPDATE analysis_metadata + SET last_updated = CURRENT_TIMESTAMP + WHERE table_name = ? + """, + [table_name], + ) diff --git a/tfbpapi/tests/test_HfQueryAPI.py b/tfbpapi/tests/test_HfQueryAPI.py new file mode 100644 index 0000000..1aeeaea --- /dev/null +++ b/tfbpapi/tests/test_HfQueryAPI.py @@ -0,0 +1,530 @@ +import tempfile +from pathlib import Path +from unittest.mock import Mock, patch + +import pandas as pd +import pytest +from datasets import Dataset, DatasetDict + +from tfbpapi.HfQueryAPI import HfQueryAPI + + +@pytest.fixture +def mock_dataset_card(): + """Mock DatasetCard.load to return fake card data.""" + with patch("tfbpapi.HfQueryAPI.DatasetCard.load") as mock: + mock_card = Mock() + mock_card.data.to_dict.return_value = { + "configs": [ + { + "config_name": "default", + "dataset_info": { + "features": [ + { + "name": "text", + "dtype": "string", + "description": "Input text", + }, + { + "name": "label", + "dtype": "int64", + "description": "Classification label", + }, + ] + }, + "data_files": [ + {"path": "data/train.parquet", "split": "train"}, + {"path": "data/test.parquet", "split": "test"}, + ], + } + ] + } + mock.return_value = mock_card + yield mock + + +@pytest.fixture +def mock_load_dataset(): + """Mock load_dataset to return fake dataset.""" + with patch("tfbpapi.HfQueryAPI.load_dataset") as mock: + # Create mock dataset with sample data + mock_dataset_dict = DatasetDict( + { + "train": Dataset.from_pandas( + pd.DataFrame( + {"text": ["hello", "world", "test"], "label": [0, 1, 0]} + ) + ), + "test": Dataset.from_pandas( + pd.DataFrame({"text": ["sample", "data"], "label": [1, 0]}) + ), + } + ) + mock.return_value = mock_dataset_dict + yield mock + + +@pytest.fixture +def mock_duckdb(): + """Mock DuckDB connection.""" + with patch("tfbpapi.HfQueryAPI.duckdb.connect") as mock_connect: + mock_conn = Mock() + mock_result = Mock() + mock_result.fetchdf.return_value = pd.DataFrame({"count": [3]}) + mock_conn.execute.return_value = mock_result + mock_connect.return_value = mock_conn + yield mock_conn + + +@pytest.fixture +def mock_hf_api_init(): + """Mock AbstractHfAPI initialization to prevent real API calls.""" + # Instead of mocking the __init__, we'll mock the methods that cause issues + with patch( + "tfbpapi.AbstractHfAPI.AbstractHfAPI._get_dataset_size" + ) as mock_get_size: + mock_get_size.return_value = None + yield mock_get_size + + +@pytest.fixture +def temp_cache_dir(): + """Create a temporary directory for cache testing.""" + with tempfile.TemporaryDirectory() as temp_dir: + yield Path(temp_dir) + + +class TestHfQueryAPI: + """Test cases for HfQueryAPI.""" + + def test_init_with_auto_parse( + self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir + ): + """Test initialization with auto_parse_datacard=True.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + # Verify initialization + assert api.auto_download_threshold_mb == 100.0 + assert api._datasets == { + "default": { + "features": { + "text": {"dtype": "string", "description": "Input text"}, + "label": {"dtype": "int64", "description": "Classification label"}, + }, + "data_files": [ + {"path": "data/train.parquet", "split": "train"}, + {"path": "data/test.parquet", "split": "test"}, + ], + "config": { + "config_name": "default", + "dataset_info": { + "features": [ + { + "name": "text", + "dtype": "string", + "description": "Input text", + }, + { + "name": "label", + "dtype": "int64", + "description": "Classification label", + }, + ] + }, + "data_files": [ + {"path": "data/train.parquet", "split": "train"}, + {"path": "data/test.parquet", "split": "test"}, + ], + }, + "loaded": False, + } + } + mock_dataset_card.assert_called_once() + + def test_init_without_auto_parse( + self, mock_hf_api_init, mock_duckdb, temp_cache_dir + ): + """Test initialization with auto_parse_datacard=False.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=False + ) + + assert api._datasets == {} + + def test_init_parse_datacard_failure( + self, mock_hf_api_init, mock_duckdb, temp_cache_dir + ): + """Test initialization when parse_datacard fails.""" + with patch("tfbpapi.HfQueryAPI.DatasetCard.load") as mock_card: + mock_card.side_effect = Exception("Failed to load") + + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + assert api._datasets == {} + # Logger warning should have been called during init + + def test_datasets_property( + self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir + ): + """Test datasets property getter and setter.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + # Test getter + assert "default" in api.datasets + + # Test setter + new_datasets = {"custom": {"features": {}, "data_files": [], "loaded": False}} + api.datasets = new_datasets + assert api.datasets == new_datasets + + def test_available_tables( + self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir + ): + """Test available_tables property.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + assert api.available_tables == ["default"] + + def test_parse_datacard_success( + self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir + ): + """Test successful datacard parsing.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=False + ) + + result = api.parse_datacard() + + assert "default" in result + assert "features" in result["default"] + assert "text" in result["default"]["features"] + assert "label" in result["default"]["features"] + + def test_parse_datacard_failure( + self, mock_hf_api_init, mock_duckdb, temp_cache_dir + ): + """Test datacard parsing failure.""" + with patch("tfbpapi.HfQueryAPI.DatasetCard.load") as mock_card: + mock_card.side_effect = Exception("Load failed") + + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=False + ) + + result = api.parse_datacard() + + assert result == {} + + def test_ensure_dataset_loaded_not_found( + self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir + ): + """Test _ensure_dataset_loaded with non-existent table.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + with pytest.raises(ValueError, match="Table 'nonexistent' not found"): + api._ensure_dataset_loaded("nonexistent") + + def test_ensure_dataset_loaded_already_loaded( + self, + mock_hf_api_init, + mock_dataset_card, + mock_load_dataset, + mock_duckdb, + temp_cache_dir, + ): + """Test _ensure_dataset_loaded when dataset already loaded.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + # Pre-load a dataset + mock_dataset = Mock() + api._loaded_datasets["default"] = mock_dataset + + result = api._ensure_dataset_loaded("default") + assert result == mock_dataset + mock_load_dataset.assert_not_called() + + def test_ensure_dataset_loaded_download_and_load( + self, + mock_hf_api_init, + mock_dataset_card, + mock_load_dataset, + mock_duckdb, + temp_cache_dir, + ): + """Test _ensure_dataset_loaded with download and load.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + # Mock download method - ensure snapshot_path is None initially + api.snapshot_path = None + mock_download = Mock() + mock_download.return_value = Path("/fake/snapshot") + api.download = mock_download # type: ignore + + # Set side effect to simulate download setting snapshot_path + def download_side_effect(**kwargs): + api.snapshot_path = Path("/fake/snapshot") + return Path("/fake/snapshot") + + mock_download.side_effect = download_side_effect + + result = api._ensure_dataset_loaded("default") + + assert result == mock_load_dataset.return_value + mock_download.assert_called_once_with(auto_download_threshold_mb=100.0) + mock_load_dataset.assert_called_once() + assert api._datasets["default"]["loaded"] is True + + def test_query_with_table_name( + self, + mock_hf_api_init, + mock_dataset_card, + mock_load_dataset, + mock_duckdb, + temp_cache_dir, + ): + """Test query with explicit table name.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + # Mock the dataset loading + api.snapshot_path = Path("/fake/snapshot") + + result = api.query("SELECT * FROM default", table_name="default") + + assert isinstance(result, pd.DataFrame) + mock_duckdb.execute.assert_called_once_with("SELECT * FROM default") + + def test_query_infer_table_name( + self, + mock_hf_api_init, + mock_dataset_card, + mock_load_dataset, + mock_duckdb, + temp_cache_dir, + ): + """Test query with table name inference.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + # Mock the dataset loading + api.snapshot_path = Path("/fake/snapshot") + + result = api.query("SELECT * FROM default") + + assert isinstance(result, pd.DataFrame) + mock_duckdb.execute.assert_called_once_with("SELECT * FROM default") + + def test_describe_table( + self, + mock_hf_api_init, + mock_dataset_card, + mock_load_dataset, + mock_duckdb, + temp_cache_dir, + ): + """Test describe_table method.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + # Mock the dataset loading + api.snapshot_path = Path("/fake/snapshot") + + result = api.describe_table("default") + + assert isinstance(result, pd.DataFrame) + mock_duckdb.execute.assert_called_with("DESCRIBE default") + + def test_sample( + self, + mock_hf_api_init, + mock_dataset_card, + mock_load_dataset, + mock_duckdb, + temp_cache_dir, + ): + """Test sample method.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + # Mock the dataset loading + api.snapshot_path = Path("/fake/snapshot") + + result = api.sample("default", n=3) + + assert isinstance(result, pd.DataFrame) + mock_duckdb.execute.assert_called_with("SELECT * FROM default LIMIT 3") + + def test_count( + self, + mock_hf_api_init, + mock_dataset_card, + mock_load_dataset, + mock_duckdb, + temp_cache_dir, + ): + """Test count method.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + # Mock the dataset loading + api.snapshot_path = Path("/fake/snapshot") + + result = api.count("default") + + assert result == 3 + mock_duckdb.execute.assert_called_with("SELECT COUNT(*) as count FROM default") + + def test_get_columns( + self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir + ): + """Test get_columns method.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + result = api.get_columns("default") + assert result == ["text", "label"] + + def test_get_columns_not_found( + self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir + ): + """Test get_columns with non-existent table.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + with pytest.raises(ValueError, match="Table 'nonexistent' not found"): + api.get_columns("nonexistent") + + def test_context_manager( + self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir + ): + """Test context manager functionality.""" + with HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) as api: + assert api is not None + + # Verify close was called + mock_duckdb.close.assert_called_once() + + def test_close( + self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir + ): + """Test close method.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + api.close() + mock_duckdb.close.assert_called_once() + + def test_table_filters_basic( + self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir + ): + """Test basic table filter functionality.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + # Test setting and getting filters + assert api.get_table_filter("default") is None + + api.set_table_filter("default", "text = 'test'") + assert api.get_table_filter("default") == "text = 'test'" + + # Test removing filters + api.remove_table_filter("default") + assert api.get_table_filter("default") is None + + def test_table_filters_query_modification( + self, + mock_hf_api_init, + mock_dataset_card, + mock_load_dataset, + mock_duckdb, + temp_cache_dir, + ): + """Test that table filters modify queries correctly.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + # Mock the dataset loading + api.snapshot_path = Path("/fake/snapshot") + + # Set a filter + api.set_table_filter("default", "label = 1") + + # Execute a query + api.query("SELECT * FROM default", table_name="default") + + # Verify the query was modified to include the filter + expected_sql = "SELECT * FROM (SELECT * FROM default WHERE label = 1)" + mock_duckdb.execute.assert_called_once_with(expected_sql) + + def test_table_filters_no_modification_when_no_filters( + self, + mock_hf_api_init, + mock_dataset_card, + mock_load_dataset, + mock_duckdb, + temp_cache_dir, + ): + """Test that queries are not modified when no filters are set.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + # Mock the dataset loading + api.snapshot_path = Path("/fake/snapshot") + + # Execute a query without any filters + original_sql = "SELECT * FROM default" + api.query(original_sql, table_name="default") + + # Verify the query was not modified + mock_duckdb.execute.assert_called_once_with(original_sql) + + def test_apply_table_filters_method( + self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir + ): + """Test the _apply_table_filters method directly.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + # Test with no filters + sql = "SELECT * FROM default" + assert api._apply_table_filters(sql) == sql + + # Test with filter applied + api.set_table_filter("default", "text LIKE '%test%'") + modified_sql = api._apply_table_filters(sql) + expected = "SELECT * FROM (SELECT * FROM default WHERE text LIKE '%test%')" + assert modified_sql == expected + + # Test that already filtered queries don't get double-wrapped + already_filtered = "SELECT * FROM (SELECT * FROM default WHERE existing = 1)" + result = api._apply_table_filters(already_filtered) + # Should not modify since it already contains a filtered subquery + assert result == already_filtered diff --git a/tfbpapi/tests/test_HfRankResponse.py b/tfbpapi/tests/test_HfRankResponse.py new file mode 100644 index 0000000..922af97 --- /dev/null +++ b/tfbpapi/tests/test_HfRankResponse.py @@ -0,0 +1,505 @@ +import tempfile +from pathlib import Path +from unittest.mock import MagicMock, patch + +import pandas as pd +import pytest + +from tfbpapi.HfRankResponse import HfRankResponse +from tfbpapi.IncrementalAnalysisDB import IncrementalAnalysisDB + + +@pytest.fixture +def temp_db_path(): + """Create temporary database path for testing.""" + with tempfile.TemporaryDirectory() as temp_dir: + db_path = Path(temp_dir) / "test.db" + yield str(db_path) + + +@pytest.fixture +def analysis_db(temp_db_path): + """Create IncrementalAnalysisDB instance for testing.""" + return IncrementalAnalysisDB(temp_db_path) + + +@pytest.fixture +def rank_response(analysis_db): + """Create HfRankResponse instance for testing.""" + return HfRankResponse(analysis_db) + + +@pytest.fixture +def mock_ranking_api(): + """Create mock ranking API.""" + api = MagicMock() + api.get_table_filter.return_value = None + api.query.return_value = pd.DataFrame( + { + "regulator_locus_tag": ["REG1", "REG2", "REG3"], + } + ) + api._duckdb_conn.execute.return_value.fetchdf.return_value = pd.DataFrame( + { + "regulator_locus_tag": ["REG1", "REG1", "REG2", "REG2", "REG3"], + "target_locus_tag": ["TGT1", "TGT2", "TGT1", "TGT3", "TGT1"], + "binding_score": [10.5, 8.2, 9.1, 7.8, 6.5], + } + ) + return api + + +@pytest.fixture +def mock_response_api(): + """Create mock response API.""" + api = MagicMock() + api.get_table_filter.return_value = None + api._duckdb_conn.execute.return_value.fetchdf.return_value = pd.DataFrame( + { + "regulator_locus_tag": ["REG1", "REG1", "REG2"], + "target_locus_tag": ["TGT1", "TGT2", "TGT1"], + "log2fc": [2.1, -1.5, 1.8], + } + ) + return api + + +@pytest.fixture +def sample_computed_data(): + """Create sample computed rank response data.""" + return pd.DataFrame( + { + "regulator_locus_tag": ["REG1", "REG1", "REG1", "REG2", "REG2", "REG3"], + "target_locus_tag": ["TGT1", "TGT2", "TGT3", "TGT1", "TGT2", "TGT1"], + "binding_score": [10.5, 8.2, 7.1, 9.1, 7.8, 6.5], + "log2fc": [2.1, -1.5, None, 1.8, None, None], + "responsive": [1, 1, 0, 1, 0, 0], + "bin_label": [5, 5, 10, 5, 10, 5], + "cumulative_responsive": [2, 2, 2, 1, 1, 0], + } + ) + + +class TestHfRankResponse: + + def test_init(self, analysis_db): + """Test HfRankResponse initialization.""" + rr = HfRankResponse(analysis_db) + assert rr.db == analysis_db + assert rr.logger is not None + + def test_get_comparisons_empty(self, rank_response): + """Test getting comparisons when none exist.""" + comparisons = rank_response.get_comparisons() + assert comparisons == [] + + def test_get_comparisons_with_data(self, rank_response, sample_computed_data): + """Test getting comparisons with existing data.""" + # Add some test data + rank_response.db.append_results( + sample_computed_data, "rank_response_test_comparison_1" + ) + rank_response.db.append_results( + sample_computed_data, "rank_response_test_comparison_2" + ) + + comparisons = rank_response.get_comparisons() + assert "test_comparison_1" in comparisons + assert "test_comparison_2" in comparisons + assert len(comparisons) == 2 + + @patch("tfbpapi.HfRankResponse.duckdb.connect") + def test_compute_new_comparison( + self, + mock_duckdb_connect, + rank_response, + mock_ranking_api, + mock_response_api, + sample_computed_data, + ): + """Test computing a new comparison.""" + # Mock the temporary connection + mock_temp_conn = MagicMock() + mock_duckdb_connect.return_value = mock_temp_conn + mock_temp_conn.execute.return_value.fetchdf.return_value = sample_computed_data + + result = rank_response.compute( + ranking_api=mock_ranking_api, + response_api=mock_response_api, + ranking_table="binding_data", + response_table="expression_data", + ranking_score_column="binding_score", + response_column="log2fc", + comparison_id="test_comp", + bin_size=5, + ) + + # Verify APIs were called correctly + mock_ranking_api._ensure_dataset_loaded.assert_called() + mock_response_api._ensure_dataset_loaded.assert_called() + + # Verify data was stored + assert rank_response.db.table_exists("rank_response_test_comp") + + # Verify result + assert not result.empty + assert "regulator_locus_tag" in result.columns + + def test_compute_auto_generated_comparison_id( + self, rank_response, mock_ranking_api, mock_response_api + ): + """Test compute with auto-generated comparison ID.""" + with patch("tfbpapi.HfRankResponse.duckdb.connect") as mock_duckdb: + mock_temp_conn = MagicMock() + mock_duckdb.return_value = mock_temp_conn + mock_temp_conn.execute.return_value.fetchdf.return_value = pd.DataFrame( + { + "regulator_locus_tag": ["REG1"], + "target_locus_tag": ["TGT1"], + "binding_score": [10.0], + "log2fc": [2.0], + "responsive": [1], + "bin_label": [5], + "cumulative_responsive": [1], + } + ) + + rank_response.compute( + ranking_api=mock_ranking_api, + response_api=mock_response_api, + ranking_table="binding_table", + response_table="expression_table", + ranking_score_column="binding_score", + response_column="log2fc", + ) + + # Should create table with auto-generated ID + assert rank_response.db.table_exists( + "rank_response_binding_table_vs_expression_table" + ) + + def test_compute_incremental_update( + self, rank_response, mock_ranking_api, mock_response_api, sample_computed_data + ): + """Test incremental computation with existing data.""" + # First, add some existing data + rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") + + # Mock APIs to return new regulators + mock_ranking_api.query.return_value = pd.DataFrame( + { + "regulator_locus_tag": ["REG1", "REG2", "REG3", "REG4"], # REG4 is new + } + ) + + with patch("tfbpapi.HfRankResponse.duckdb.connect") as mock_duckdb: + mock_temp_conn = MagicMock() + mock_duckdb.return_value = mock_temp_conn + # Return data for new regulator only + mock_temp_conn.execute.return_value.fetchdf.return_value = pd.DataFrame( + { + "regulator_locus_tag": ["REG4"], + "target_locus_tag": ["TGT1"], + "binding_score": [5.0], + "log2fc": [1.0], + "responsive": [1], + "bin_label": [5], + "cumulative_responsive": [1], + } + ) + + result = rank_response.compute( + ranking_api=mock_ranking_api, + response_api=mock_response_api, + ranking_table="binding_data", + response_table="expression_data", + ranking_score_column="binding_score", + response_column="log2fc", + comparison_id="test_comp", + ) + + # Should have data for all regulators now + regulators = set(result["regulator_locus_tag"].unique()) + assert "REG1" in regulators + assert "REG4" in regulators + + def test_get_bin_summary(self, rank_response, sample_computed_data): + """Test generating bin-level summary.""" + # Add test data + rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") + + summary = rank_response.get_bin_summary("test_comp") + + assert not summary.empty + assert "regulator_locus_tag" in summary.columns + assert "bin_label" in summary.columns + assert "records_in_bin" in summary.columns + assert "responsive_in_bin" in summary.columns + assert "cumulative_responsive" in summary.columns + assert "response_rate" in summary.columns + + def test_get_bin_summary_with_filter(self, rank_response, sample_computed_data): + """Test bin summary with regulator filter.""" + rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") + + summary = rank_response.get_bin_summary( + "test_comp", regulators_filter=["REG1", "REG2"] + ) + + assert not summary.empty + regulators = set(summary["regulator_locus_tag"].unique()) + assert regulators.issubset({"REG1", "REG2"}) + assert "REG3" not in regulators + + def test_get_regulator_summary(self, rank_response, sample_computed_data): + """Test generating regulator-level summary.""" + rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") + + summary = rank_response.get_regulator_summary("test_comp") + + assert not summary.empty + assert "regulator_locus_tag" in summary.columns + assert "total_targets" in summary.columns + assert "total_responsive" in summary.columns + assert "overall_response_rate" in summary.columns + assert "top5_response_rate" in summary.columns + assert "top10_response_rate" in summary.columns + assert "top20_response_rate" in summary.columns + + def test_get_regulator_summary_with_max_bin( + self, rank_response, sample_computed_data + ): + """Test regulator summary with max bin limit.""" + rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") + + summary = rank_response.get_regulator_summary("test_comp", max_bin_label=5) + + assert not summary.empty + # Should only include data from bins <= 5 + + def test_summarize_bin_type(self, rank_response, sample_computed_data): + """Test summarize method with bin type.""" + rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") + + summary = rank_response.summarize("test_comp", summary_type="bin") + + assert not summary.empty + assert "bin_label" in summary.columns + + def test_summarize_regulator_type(self, rank_response, sample_computed_data): + """Test summarize method with regulator type.""" + rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") + + summary = rank_response.summarize("test_comp", summary_type="regulator") + + assert not summary.empty + assert "overall_response_rate" in summary.columns + + def test_summarize_invalid_type(self, rank_response, sample_computed_data): + """Test summarize with invalid summary type.""" + rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") + + with pytest.raises(ValueError, match="Unknown summary type"): + rank_response.summarize("test_comp", summary_type="invalid") + + def test_query_method(self, rank_response, sample_computed_data): + """Test direct SQL query method.""" + rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") + + result = rank_response.query( + "SELECT COUNT(*) as count FROM rank_response_test_comp" + ) + + assert not result.empty + assert result.iloc[0]["count"] == len(sample_computed_data) + + def test_get_comparison_data(self, rank_response, sample_computed_data): + """Test getting raw comparison data.""" + rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") + + data = rank_response.get_comparison_data("test_comp") + + assert not data.empty + assert len(data) == len(sample_computed_data) + + def test_get_comparison_data_with_filter(self, rank_response, sample_computed_data): + """Test getting comparison data with regulator filter.""" + rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") + + data = rank_response.get_comparison_data("test_comp", regulator_filter=["REG1"]) + + assert not data.empty + assert all(data["regulator_locus_tag"] == "REG1") + + def test_get_comparison_data_with_limit(self, rank_response, sample_computed_data): + """Test getting comparison data with limit.""" + rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") + + data = rank_response.get_comparison_data("test_comp", limit=3) + + assert len(data) == 3 + + def test_compare_across_datasets(self, rank_response, sample_computed_data): + """Test comparing across multiple datasets.""" + # Add data for multiple comparisons + rank_response.db.append_results(sample_computed_data, "rank_response_comp1") + rank_response.db.append_results(sample_computed_data, "rank_response_comp2") + + comparison = rank_response.compare_across_datasets(["comp1", "comp2"]) + + assert not comparison.empty + assert "regulator_locus_tag" in comparison.columns + # Should have columns for each comparison and metric + assert any("overall_response_rate_" in col for col in comparison.columns) + + def test_compare_across_datasets_empty(self, rank_response): + """Test comparing across datasets with no data.""" + comparison = rank_response.compare_across_datasets([]) + + assert comparison.empty + + def test_compare_across_datasets_custom_metrics( + self, rank_response, sample_computed_data + ): + """Test comparing with custom metric columns.""" + rank_response.db.append_results(sample_computed_data, "rank_response_comp1") + + comparison = rank_response.compare_across_datasets( + ["comp1"], metric_columns=["top5_response_rate"] + ) + + assert not comparison.empty + assert any("top5_response_rate_" in col for col in comparison.columns) + + def test_nonexistent_comparison_error(self, rank_response): + """Test error handling for nonexistent comparisons.""" + with pytest.raises(ValueError, match="No results found"): + rank_response.get_comparison_data("nonexistent") + + with pytest.raises(ValueError, match="does not exist"): + rank_response.get_bin_summary("nonexistent") + + with pytest.raises(ValueError, match="does not exist"): + rank_response.get_regulator_summary("nonexistent") + + def test_compute_with_existing_filters( + self, rank_response, mock_ranking_api, mock_response_api + ): + """Test compute when APIs already have filters set.""" + # Set existing filters + mock_ranking_api.get_table_filter.return_value = "existing_filter = 'value'" + mock_response_api.get_table_filter.return_value = "another_filter = 'value'" + + with patch("tfbpapi.HfRankResponse.duckdb.connect") as mock_duckdb: + mock_temp_conn = MagicMock() + mock_duckdb.return_value = mock_temp_conn + mock_temp_conn.execute.return_value.fetchdf.return_value = pd.DataFrame( + { + "regulator_locus_tag": ["REG1"], + "target_locus_tag": ["TGT1"], + "binding_score": [10.0], + "log2fc": [2.0], + "responsive": [1], + "bin_label": [5], + "cumulative_responsive": [1], + } + ) + + rank_response.compute( + ranking_api=mock_ranking_api, + response_api=mock_response_api, + ranking_table="binding_data", + response_table="expression_data", + ranking_score_column="binding_score", + response_column="log2fc", + comparison_id="with_filters", + ) + + # Verify filters were combined with AND + calls = mock_ranking_api.set_table_filter.call_args_list + assert len(calls) > 0 + combined_filter = calls[0][0][1] # Second argument of first call + assert "existing_filter = 'value'" in combined_filter + assert "AND" in combined_filter + + @patch("tfbpapi.HfRankResponse.logging.getLogger") + def test_logging_setup(self, mock_get_logger, analysis_db): + """Test that logging is properly configured.""" + mock_logger = MagicMock() + mock_get_logger.return_value = mock_logger + + rr = HfRankResponse(analysis_db) + + mock_get_logger.assert_called_once_with("tfbpapi.HfRankResponse") + assert rr.logger == mock_logger + + def test_compute_with_custom_responsive_condition( + self, rank_response, mock_ranking_api, mock_response_api + ): + """Test compute with custom responsive condition.""" + with patch("tfbpapi.HfRankResponse.duckdb.connect") as mock_duckdb: + mock_temp_conn = MagicMock() + mock_duckdb.return_value = mock_temp_conn + mock_temp_conn.execute.return_value.fetchdf.return_value = pd.DataFrame( + { + "regulator_locus_tag": ["REG1"], + "target_locus_tag": ["TGT1"], + "binding_score": [10.0], + "log2fc": [2.0], + "responsive": [1], + "bin_label": [5], + "cumulative_responsive": [1], + } + ) + + rank_response.compute( + ranking_api=mock_ranking_api, + response_api=mock_response_api, + ranking_table="binding_data", + response_table="expression_data", + ranking_score_column="binding_score", + response_column="log2fc", + comparison_id="custom_responsive", + responsive_condition="log2fc IS NOT NULL AND log2fc != 0", + ) + + # Verify the SQL contains the custom responsive condition + sql_calls = mock_temp_conn.execute.call_args_list + assert len(sql_calls) > 0 + executed_sql = sql_calls[0][0][0] + assert "b.log2fc IS NOT NULL AND b.log2fc != 0" in executed_sql + + def test_compute_with_default_responsive_condition( + self, rank_response, mock_ranking_api, mock_response_api + ): + """Test compute with default responsive condition (IS NOT NULL).""" + with patch("tfbpapi.HfRankResponse.duckdb.connect") as mock_duckdb: + mock_temp_conn = MagicMock() + mock_duckdb.return_value = mock_temp_conn + mock_temp_conn.execute.return_value.fetchdf.return_value = pd.DataFrame( + { + "regulator_locus_tag": ["REG1"], + "target_locus_tag": ["TGT1"], + "binding_score": [10.0], + "log2fc": [2.0], + "responsive": [1], + "bin_label": [5], + "cumulative_responsive": [1], + } + ) + + rank_response.compute( + ranking_api=mock_ranking_api, + response_api=mock_response_api, + ranking_table="binding_data", + response_table="expression_data", + ranking_score_column="binding_score", + response_column="log2fc", + comparison_id="default_responsive", + ) + + # Verify the SQL contains the default responsive condition + sql_calls = mock_temp_conn.execute.call_args_list + assert len(sql_calls) > 0 + executed_sql = sql_calls[0][0][0] + assert "b.log2fc IS NOT NULL" in executed_sql diff --git a/tfbpapi/tests/test_IncrementalAnalysisDB.py b/tfbpapi/tests/test_IncrementalAnalysisDB.py new file mode 100644 index 0000000..0fbb1e1 --- /dev/null +++ b/tfbpapi/tests/test_IncrementalAnalysisDB.py @@ -0,0 +1,341 @@ +import json +import tempfile +from pathlib import Path +from unittest.mock import MagicMock, patch + +import pandas as pd +import pytest + +from tfbpapi.IncrementalAnalysisDB import IncrementalAnalysisDB + + +@pytest.fixture +def temp_db_path(): + """Create temporary database path for testing.""" + with tempfile.TemporaryDirectory() as temp_dir: + db_path = Path(temp_dir) / "test.db" + yield str(db_path) + + +@pytest.fixture +def sample_dataframe(): + """Create sample DataFrame for testing.""" + return pd.DataFrame( + {"id": [1, 2, 3], "name": ["A", "B", "C"], "value": [10.5, 20.3, 15.7]} + ) + + +@pytest.fixture +def analysis_db(temp_db_path): + """Create IncrementalAnalysisDB instance for testing.""" + return IncrementalAnalysisDB(temp_db_path) + + +class TestIncrementalAnalysisDB: + + def test_init_creates_database_and_metadata_table(self, temp_db_path): + """Test that initialization creates the database file and metadata table.""" + db = IncrementalAnalysisDB(temp_db_path) + + # Check database file exists + assert Path(temp_db_path).exists() + + # Check metadata table exists + result = db.conn.execute( + """ + SELECT table_name FROM information_schema.tables + WHERE table_name='analysis_metadata' AND table_schema='main' + """ + ).fetchall() + assert len(result) == 1 + + db.conn.close() + + def test_init_creates_parent_directories(self): + """Test that initialization creates parent directories if they don't exist.""" + with tempfile.TemporaryDirectory() as temp_dir: + nested_path = Path(temp_dir) / "nested" / "path" / "test.db" + db = IncrementalAnalysisDB(str(nested_path)) + + assert nested_path.parent.exists() + assert nested_path.exists() + db.conn.close() + + def test_append_results_new_table(self, analysis_db, sample_dataframe): + """Test appending results to a new table.""" + records_added = analysis_db.append_results( + new_results=sample_dataframe, + table_name="test_table", + analysis_type="test_analysis", + parameters={"param1": "value1"}, + description="Test description", + ) + + assert records_added == 3 + + # Check data was inserted + result = analysis_db.conn.execute("SELECT * FROM test_table").fetchdf() + pd.testing.assert_frame_equal(result, sample_dataframe) + + # Check metadata was inserted + metadata = analysis_db.conn.execute( + """ + SELECT * FROM analysis_metadata WHERE table_name = 'test_table' + """ + ).fetchdf() + + assert len(metadata) == 1 + assert metadata.iloc[0]["analysis_type"] == "test_analysis" + assert metadata.iloc[0]["total_records"] == 3 + assert json.loads(metadata.iloc[0]["parameters"]) == {"param1": "value1"} + assert metadata.iloc[0]["description"] == "Test description" + + def test_append_results_existing_table(self, analysis_db, sample_dataframe): + """Test appending results to an existing table.""" + # First append + analysis_db.append_results(sample_dataframe, "test_table") + + # Second append with new data + new_data = pd.DataFrame( + {"id": [4, 5], "name": ["D", "E"], "value": [25.1, 30.9]} + ) + + records_added = analysis_db.append_results(new_data, "test_table") + assert records_added == 2 + + # Check total records + result = analysis_db.conn.execute( + "SELECT COUNT(*) as count FROM test_table" + ).fetchdf() + assert result.iloc[0]["count"] == 5 + + # Check metadata updated + metadata = analysis_db.conn.execute( + """ + SELECT total_records FROM analysis_metadata WHERE table_name = 'test_table' + """ + ).fetchdf() + assert metadata.iloc[0]["total_records"] == 5 + + def test_append_results_with_deduplication(self, analysis_db): + """Test appending results with deduplication.""" + initial_data = pd.DataFrame( + {"id": [1, 2, 3], "name": ["A", "B", "C"], "value": [10.5, 20.3, 15.7]} + ) + + analysis_db.append_results(initial_data, "test_table", deduplicate_on=["id"]) + + # Append data with some duplicates + new_data = pd.DataFrame( + { + "id": [2, 3, 4], # 2 and 3 are duplicates + "name": ["B2", "C2", "D"], + "value": [20.3, 15.7, 25.1], + } + ) + + records_added = analysis_db.append_results( + new_data, "test_table", deduplicate_on=["id"] + ) + + # Only record with id=4 should be added + assert records_added == 1 + + # Check total records + result = analysis_db.conn.execute( + "SELECT COUNT(*) as count FROM test_table" + ).fetchdf() + assert result.iloc[0]["count"] == 4 + + def test_update_results(self, analysis_db, sample_dataframe): + """Test updating existing results.""" + # First insert data + analysis_db.append_results(sample_dataframe, "test_table") + + # Update data + updated_data = pd.DataFrame( + {"id": [1, 2], "name": ["A_updated", "B_updated"], "value": [100.5, 200.3]} + ) + + records_updated = analysis_db.update_results( + updated_data, "test_table", key_columns=["id"] + ) + + assert records_updated == 2 + + # Check data was updated + result = analysis_db.conn.execute( + """ + SELECT * FROM test_table WHERE id IN (1, 2) ORDER BY id + """ + ).fetchdf() + + assert result.iloc[0]["name"] == "A_updated" + assert result.iloc[1]["name"] == "B_updated" + + def test_get_results(self, analysis_db, sample_dataframe): + """Test retrieving results.""" + analysis_db.append_results(sample_dataframe, "test_table") + + # Get all results + result = analysis_db.get_results("test_table") + pd.testing.assert_frame_equal(result, sample_dataframe) + + # Get results with filter + filtered_result = analysis_db.get_results("test_table", filters={"id": [1, 2]}) + + expected = sample_dataframe[sample_dataframe["id"].isin([1, 2])] + pd.testing.assert_frame_equal(filtered_result.reset_index(drop=True), expected) + + def test_get_results_with_limit(self, analysis_db, sample_dataframe): + """Test retrieving results with limit.""" + analysis_db.append_results(sample_dataframe, "test_table") + + result = analysis_db.get_results("test_table", limit=2) + assert len(result) == 2 + + def test_query_method(self, analysis_db, sample_dataframe): + """Test direct SQL query execution.""" + analysis_db.append_results(sample_dataframe, "test_table") + + # Test basic query + result = analysis_db.query("SELECT * FROM test_table") + assert len(result) == len(sample_dataframe) + pd.testing.assert_frame_equal(result, sample_dataframe) + + # Test query with WHERE clause + result = analysis_db.query("SELECT * FROM test_table WHERE id = 1") + assert len(result) == 1 + assert result.iloc[0]["id"] == 1 + + # Test query with aggregation + result = analysis_db.query("SELECT COUNT(*) as count FROM test_table") + assert result.iloc[0]["count"] == len(sample_dataframe) + + # Test query with complex SQL + result = analysis_db.query( + """ + SELECT name, AVG(value) as avg_value + FROM test_table + GROUP BY name + ORDER BY name + """ + ) + assert len(result) == 3 # Should have 3 distinct names + assert "avg_value" in result.columns + + def test_table_exists(self, analysis_db, sample_dataframe): + """Test checking if table exists.""" + assert not analysis_db.table_exists("test_table") + + analysis_db.append_results(sample_dataframe, "test_table") + assert analysis_db.table_exists("test_table") + + def test_drop_table(self, analysis_db, sample_dataframe): + """Test dropping a table.""" + analysis_db.append_results(sample_dataframe, "test_table") + assert analysis_db.table_exists("test_table") + + analysis_db.drop_table("test_table") + assert not analysis_db.table_exists("test_table") + + # Check metadata was also removed + metadata = analysis_db.conn.execute( + """ + SELECT * FROM analysis_metadata WHERE table_name = 'test_table' + """ + ).fetchdf() + assert len(metadata) == 0 + + def test_get_table_info(self, analysis_db, sample_dataframe): + """Test getting table information.""" + analysis_db.append_results( + sample_dataframe, + "test_table", + analysis_type="test_analysis", + parameters={"param1": "value1"}, + description="Test description", + ) + + info = analysis_db.get_table_info("test_table") + + assert info["table_name"] == "test_table" + assert info["total_records"] == 3 + assert info["analysis_type"] == "test_analysis" + assert json.loads(info["parameters"]) == {"param1": "value1"} + assert info["description"] == "Test description" + + def test_list_tables(self, analysis_db, sample_dataframe): + """Test listing all tables.""" + # Initially should be empty (except metadata table) + tables = analysis_db.list_tables() + assert "analysis_metadata" in tables + + # Add some tables + analysis_db.append_results(sample_dataframe, "table1") + analysis_db.append_results(sample_dataframe, "table2") + + tables = analysis_db.list_tables() + assert "table1" in tables + assert "table2" in tables + assert "analysis_metadata" in tables + + def test_get_table_schema(self, analysis_db, sample_dataframe): + """Test getting table schema.""" + analysis_db.append_results(sample_dataframe, "test_table") + + schema = analysis_db.get_table_schema("test_table") + + # Should have columns from sample dataframe + column_names = [col["column_name"] for col in schema] + assert "id" in column_names + assert "name" in column_names + assert "value" in column_names + + def test_close_connection(self, analysis_db): + """Test closing database connection.""" + analysis_db.close() + + # Connection should be closed + with pytest.raises(Exception): + analysis_db.conn.execute("SELECT 1") + + def test_context_manager(self, temp_db_path, sample_dataframe): + """Test using IncrementalAnalysisDB as context manager.""" + with IncrementalAnalysisDB(temp_db_path) as db: + db.append_results(sample_dataframe, "test_table") + assert db.table_exists("test_table") + + # Connection should be closed after context exit + with pytest.raises(Exception): + db.conn.execute("SELECT 1") + + @patch("tfbpapi.IncrementalAnalysisDB.logging.getLogger") + def test_logging_setup(self, mock_get_logger, temp_db_path): + """Test that logging is properly configured.""" + mock_logger = MagicMock() + mock_get_logger.return_value = mock_logger + + db = IncrementalAnalysisDB(temp_db_path) + + mock_get_logger.assert_called_once_with("tfbpapi.IncrementalAnalysisDB") + assert db.logger == mock_logger + db.conn.close() + + def test_error_handling_nonexistent_table(self, analysis_db): + """Test error handling for operations on nonexistent tables.""" + with pytest.raises(Exception): + analysis_db.get_results("nonexistent_table") + + with pytest.raises(Exception): + analysis_db.get_table_info("nonexistent_table") + + def test_empty_dataframe_append(self, analysis_db): + """Test appending empty DataFrame.""" + empty_df = pd.DataFrame() + + records_added = analysis_db.append_results(empty_df, "empty_table") + assert records_added == 0 + + # Table should not be created for empty DataFrame + assert not analysis_db.table_exists("empty_table") From 0494d09523bea1f0ae65d449b89a906960845f72 Mon Sep 17 00:00:00 2001 From: chasem Date: Wed, 17 Sep 2025 20:23:04 -0500 Subject: [PATCH 05/49] this is getting closer to what im after. datainfo is close to finished. documentation works and is updated --- docs/AbstractAPI.md | 1 - docs/AbstractHfAPI.md | 1 - docs/AbstractRecordsAndFilesAPI.md | 1 - docs/AbstractRecordsOnlyAPI.md | 1 - docs/BindingAPI.md | 1 - docs/BindingConcatenatedAPI.md | 1 - docs/BindingManualQCAPI.md | 1 - docs/Cache.md | 1 - docs/CallingCardsBackgroundAPI.md | 1 - docs/DataSourceAPI.md | 1 - docs/ExpressionAPI.md | 1 - docs/ExpressionManualQCAPI.md | 2 - docs/FileFormatAPI.md | 1 - docs/GenomicFeatureAPI.md | 1 - docs/HfCacheManager.md | 2 +- docs/HfQueryAPI.md | 1 + docs/HfRankResponse.md | 1 + docs/IncrementalAnalysisDB.md | 1 + docs/ParamsDict.md | 1 - docs/PromoterSetAPI.md | 1 - docs/PromoterSetSigAPI.md | 1 - docs/RegulatorAPI.md | 1 - docs/datainfo.md | 133 + docs/errors.md | 1 + docs/huggingface_datacard.md | 315 +++ docs/index.md | 49 +- docs/metric_arrays.md | 1 - docs/rank_transforms.md | 17 - docs/tutorials/datacard_tutorial.ipynb | 759 ++++++ mkdocs.yml | 44 +- pyproject.toml | 2 +- tfbpapi/AbstractAPI.py | 230 -- tfbpapi/AbstractHfAPI.py | 373 --- tfbpapi/AbstractRecordsAndFilesAPI.py | 314 --- tfbpapi/AbstractRecordsOnlyAPI.py | 82 - tfbpapi/BindingAPI.py | 62 - tfbpapi/BindingConcatenatedAPI.py | 62 - tfbpapi/BindingManualQCAPI.py | 106 - tfbpapi/Cache.py | 29 - tfbpapi/CallingCardsBackgroundAPI.py | 56 - tfbpapi/DataSourceAPI.py | 48 - tfbpapi/DtoAPI.py | 295 --- tfbpapi/ExpressionAPI.py | 66 - tfbpapi/ExpressionManualQCAPI.py | 103 - tfbpapi/FileFormatAPI.py | 57 - tfbpapi/GenomicFeatureAPI.py | 60 - tfbpapi/HfQueryAPI.py | 2163 +++++++++++++++-- tfbpapi/ParamsDict.py | 156 -- tfbpapi/PromoterSetAPI.py | 46 - tfbpapi/PromoterSetSigAPI.py | 68 - tfbpapi/RankResponseAPI.py | 286 --- tfbpapi/RegulatorAPI.py | 53 - tfbpapi/UnivariateModelsAPI.py | 202 -- tfbpapi/__init__.py | 39 - tfbpapi/datainfo/__init__.py | 23 + tfbpapi/datainfo/datacard.py | 339 +++ tfbpapi/datainfo/fetchers.py | 224 ++ tfbpapi/datainfo/models.py | 250 ++ tfbpapi/errors.py | 214 ++ tfbpapi/metric_arrays.py | 162 -- tfbpapi/rank_transforms.py | 154 -- tfbpapi/tests/data/cache_info.pkl | Bin 123975 -> 0 bytes tfbpapi/tests/datainfo/__init__.py | 1 + tfbpapi/tests/datainfo/conftest.py | 302 +++ tfbpapi/tests/datainfo/test_datacard.py | 459 ++++ tfbpapi/tests/datainfo/test_fetchers.py | 422 ++++ tfbpapi/tests/datainfo/test_models.py | 481 ++++ tfbpapi/tests/snapshots/__init__.py | 0 .../promotersetsig_records_and_files.tar.gz | Bin 590168 -> 0 bytes .../tests/snapshots/snap_test_AbstractAPI.py | 20 - .../snap_test_AbstractRecordsAndFilesAPI.py | 15 - .../cache_get_after_delete | 1 - .../test_cache_operations/cache_get_after_set | 1 - .../test_cache_operations/cache_list | 1 - .../pop_params_after_all_removed | 1 - .../pop_params_after_one_removed | 1 - .../test_push_params/push_params | 1 - tfbpapi/tests/test_AbstractAPI.py | 94 - tfbpapi/tests/test_AbstractHfAPI.py | 435 ---- .../tests/test_AbstractRecordsAndFilesAPI.py | 284 --- tfbpapi/tests/test_AbstractRecordsOnlyAPI.py | 71 - tfbpapi/tests/test_Cache.py | 66 - tfbpapi/tests/test_HfQueryAPI.py | 530 ---- tfbpapi/tests/test_ParamsDict.py | 96 - tfbpapi/tests/test_metric_arrays.py | 194 -- tfbpapi/tests/test_rank_transforms.py | 80 - 86 files changed, 5956 insertions(+), 5267 deletions(-) delete mode 100644 docs/AbstractAPI.md delete mode 100644 docs/AbstractHfAPI.md delete mode 100644 docs/AbstractRecordsAndFilesAPI.md delete mode 100644 docs/AbstractRecordsOnlyAPI.md delete mode 100644 docs/BindingAPI.md delete mode 100644 docs/BindingConcatenatedAPI.md delete mode 100644 docs/BindingManualQCAPI.md delete mode 100644 docs/Cache.md delete mode 100644 docs/CallingCardsBackgroundAPI.md delete mode 100644 docs/DataSourceAPI.md delete mode 100644 docs/ExpressionAPI.md delete mode 100644 docs/ExpressionManualQCAPI.md delete mode 100644 docs/FileFormatAPI.md delete mode 100644 docs/GenomicFeatureAPI.md create mode 100644 docs/HfQueryAPI.md create mode 100644 docs/HfRankResponse.md create mode 100644 docs/IncrementalAnalysisDB.md delete mode 100644 docs/ParamsDict.md delete mode 100644 docs/PromoterSetAPI.md delete mode 100644 docs/PromoterSetSigAPI.md delete mode 100644 docs/RegulatorAPI.md create mode 100644 docs/datainfo.md create mode 100644 docs/errors.md create mode 100644 docs/huggingface_datacard.md delete mode 100644 docs/metric_arrays.md delete mode 100644 docs/rank_transforms.md create mode 100644 docs/tutorials/datacard_tutorial.ipynb delete mode 100644 tfbpapi/AbstractAPI.py delete mode 100644 tfbpapi/AbstractHfAPI.py delete mode 100644 tfbpapi/AbstractRecordsAndFilesAPI.py delete mode 100644 tfbpapi/AbstractRecordsOnlyAPI.py delete mode 100644 tfbpapi/BindingAPI.py delete mode 100644 tfbpapi/BindingConcatenatedAPI.py delete mode 100644 tfbpapi/BindingManualQCAPI.py delete mode 100644 tfbpapi/Cache.py delete mode 100644 tfbpapi/CallingCardsBackgroundAPI.py delete mode 100644 tfbpapi/DataSourceAPI.py delete mode 100644 tfbpapi/DtoAPI.py delete mode 100644 tfbpapi/ExpressionAPI.py delete mode 100644 tfbpapi/ExpressionManualQCAPI.py delete mode 100644 tfbpapi/FileFormatAPI.py delete mode 100644 tfbpapi/GenomicFeatureAPI.py delete mode 100644 tfbpapi/ParamsDict.py delete mode 100644 tfbpapi/PromoterSetAPI.py delete mode 100644 tfbpapi/PromoterSetSigAPI.py delete mode 100644 tfbpapi/RankResponseAPI.py delete mode 100644 tfbpapi/RegulatorAPI.py delete mode 100644 tfbpapi/UnivariateModelsAPI.py create mode 100644 tfbpapi/datainfo/__init__.py create mode 100644 tfbpapi/datainfo/datacard.py create mode 100644 tfbpapi/datainfo/fetchers.py create mode 100644 tfbpapi/datainfo/models.py create mode 100644 tfbpapi/errors.py delete mode 100644 tfbpapi/metric_arrays.py delete mode 100644 tfbpapi/rank_transforms.py delete mode 100644 tfbpapi/tests/data/cache_info.pkl create mode 100644 tfbpapi/tests/datainfo/__init__.py create mode 100644 tfbpapi/tests/datainfo/conftest.py create mode 100644 tfbpapi/tests/datainfo/test_datacard.py create mode 100644 tfbpapi/tests/datainfo/test_fetchers.py create mode 100644 tfbpapi/tests/datainfo/test_models.py delete mode 100644 tfbpapi/tests/snapshots/__init__.py delete mode 100644 tfbpapi/tests/snapshots/promotersetsig_records_and_files.tar.gz delete mode 100644 tfbpapi/tests/snapshots/snap_test_AbstractAPI.py delete mode 100644 tfbpapi/tests/snapshots/snap_test_AbstractRecordsAndFilesAPI.py delete mode 100644 tfbpapi/tests/snapshots/test_AbstractAPI/test_cache_operations/cache_get_after_delete delete mode 100644 tfbpapi/tests/snapshots/test_AbstractAPI/test_cache_operations/cache_get_after_set delete mode 100644 tfbpapi/tests/snapshots/test_AbstractAPI/test_cache_operations/cache_list delete mode 100644 tfbpapi/tests/snapshots/test_AbstractAPI/test_pop_params/pop_params_after_all_removed delete mode 100644 tfbpapi/tests/snapshots/test_AbstractAPI/test_pop_params/pop_params_after_one_removed delete mode 100644 tfbpapi/tests/snapshots/test_AbstractAPI/test_push_params/push_params delete mode 100644 tfbpapi/tests/test_AbstractAPI.py delete mode 100644 tfbpapi/tests/test_AbstractHfAPI.py delete mode 100644 tfbpapi/tests/test_AbstractRecordsAndFilesAPI.py delete mode 100644 tfbpapi/tests/test_AbstractRecordsOnlyAPI.py delete mode 100644 tfbpapi/tests/test_Cache.py delete mode 100644 tfbpapi/tests/test_HfQueryAPI.py delete mode 100644 tfbpapi/tests/test_ParamsDict.py delete mode 100644 tfbpapi/tests/test_metric_arrays.py delete mode 100644 tfbpapi/tests/test_rank_transforms.py diff --git a/docs/AbstractAPI.md b/docs/AbstractAPI.md deleted file mode 100644 index ada18a1..0000000 --- a/docs/AbstractAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.AbstractAPI.AbstractAPI diff --git a/docs/AbstractHfAPI.md b/docs/AbstractHfAPI.md deleted file mode 100644 index 45dea40..0000000 --- a/docs/AbstractHfAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.AbstractHfAPI.AbstractHfAPI diff --git a/docs/AbstractRecordsAndFilesAPI.md b/docs/AbstractRecordsAndFilesAPI.md deleted file mode 100644 index 395c787..0000000 --- a/docs/AbstractRecordsAndFilesAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.AbstractRecordsAndFilesAPI.AbstractRecordsAndFilesAPI diff --git a/docs/AbstractRecordsOnlyAPI.md b/docs/AbstractRecordsOnlyAPI.md deleted file mode 100644 index fc0726a..0000000 --- a/docs/AbstractRecordsOnlyAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.AbstractRecordsOnlyAPI.AbstractRecordsOnlyAPI diff --git a/docs/BindingAPI.md b/docs/BindingAPI.md deleted file mode 100644 index 7f4c555..0000000 --- a/docs/BindingAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.BindingAPI.BindingAPI diff --git a/docs/BindingConcatenatedAPI.md b/docs/BindingConcatenatedAPI.md deleted file mode 100644 index 4d68aea..0000000 --- a/docs/BindingConcatenatedAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.BindingConcatenatedAPI.BindingConcatenatedAPI diff --git a/docs/BindingManualQCAPI.md b/docs/BindingManualQCAPI.md deleted file mode 100644 index a29d3ca..0000000 --- a/docs/BindingManualQCAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.BindingManualQCAPI.BindingManualQCAPI diff --git a/docs/Cache.md b/docs/Cache.md deleted file mode 100644 index 3c2ed52..0000000 --- a/docs/Cache.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.Cache.Cache diff --git a/docs/CallingCardsBackgroundAPI.md b/docs/CallingCardsBackgroundAPI.md deleted file mode 100644 index 5f5c24a..0000000 --- a/docs/CallingCardsBackgroundAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.CallingCardsBackgroundAPI.CallingCardsBackgroundAPI diff --git a/docs/DataSourceAPI.md b/docs/DataSourceAPI.md deleted file mode 100644 index b50cc38..0000000 --- a/docs/DataSourceAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.DataSourceAPI.DataSourceAPI diff --git a/docs/ExpressionAPI.md b/docs/ExpressionAPI.md deleted file mode 100644 index 9397868..0000000 --- a/docs/ExpressionAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.ExpressionAPI.ExpressionAPI diff --git a/docs/ExpressionManualQCAPI.md b/docs/ExpressionManualQCAPI.md deleted file mode 100644 index 094b037..0000000 --- a/docs/ExpressionManualQCAPI.md +++ /dev/null @@ -1,2 +0,0 @@ -::: tfbpapi.ExpressionManualQCAPI.ExpressionManualQCAPI - diff --git a/docs/FileFormatAPI.md b/docs/FileFormatAPI.md deleted file mode 100644 index 12adc28..0000000 --- a/docs/FileFormatAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.FileFormatAPI.FileFormatAPI diff --git a/docs/GenomicFeatureAPI.md b/docs/GenomicFeatureAPI.md deleted file mode 100644 index c66339c..0000000 --- a/docs/GenomicFeatureAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.GenomicFeatureAPI.GenomicFeatureAPI diff --git a/docs/HfCacheManager.md b/docs/HfCacheManager.md index 89d5c77..6aa53a3 100644 --- a/docs/HfCacheManager.md +++ b/docs/HfCacheManager.md @@ -1 +1 @@ -::: tfbpapi.HfCacheManager.HfCacheManager +::: tfbpapi.HfCacheManager.HFCacheManager diff --git a/docs/HfQueryAPI.md b/docs/HfQueryAPI.md new file mode 100644 index 0000000..357ea30 --- /dev/null +++ b/docs/HfQueryAPI.md @@ -0,0 +1 @@ +::: tfbpapi.HfQueryAPI.HfQueryAPI \ No newline at end of file diff --git a/docs/HfRankResponse.md b/docs/HfRankResponse.md new file mode 100644 index 0000000..e2ffd53 --- /dev/null +++ b/docs/HfRankResponse.md @@ -0,0 +1 @@ +::: tfbpapi.HfRankResponse.HfRankResponse \ No newline at end of file diff --git a/docs/IncrementalAnalysisDB.md b/docs/IncrementalAnalysisDB.md new file mode 100644 index 0000000..7dc07d7 --- /dev/null +++ b/docs/IncrementalAnalysisDB.md @@ -0,0 +1 @@ +::: tfbpapi.IncrementalAnalysisDB.IncrementalAnalysisDB \ No newline at end of file diff --git a/docs/ParamsDict.md b/docs/ParamsDict.md deleted file mode 100644 index 057048a..0000000 --- a/docs/ParamsDict.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.ParamsDict.ParamsDict diff --git a/docs/PromoterSetAPI.md b/docs/PromoterSetAPI.md deleted file mode 100644 index b2c311c..0000000 --- a/docs/PromoterSetAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.PromoterSetAPI.PromoterSetAPI diff --git a/docs/PromoterSetSigAPI.md b/docs/PromoterSetSigAPI.md deleted file mode 100644 index f07120c..0000000 --- a/docs/PromoterSetSigAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.PromoterSetSigAPI.PromoterSetSigAPI diff --git a/docs/RegulatorAPI.md b/docs/RegulatorAPI.md deleted file mode 100644 index 0217a92..0000000 --- a/docs/RegulatorAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.RegulatorAPI.RegulatorAPI diff --git a/docs/datainfo.md b/docs/datainfo.md new file mode 100644 index 0000000..e159e7b --- /dev/null +++ b/docs/datainfo.md @@ -0,0 +1,133 @@ +# DataInfo Package + +The `datainfo` package provides dataset information management for HuggingFace datasets. It enables exploration of dataset metadata, structure, and relationships without loading actual genomic data. + +## Overview + +The datainfo package consists of three main components: + +- **DataCard**: High-level interface for exploring dataset metadata +- **Fetchers**: Low-level components for retrieving data from HuggingFace Hub +- **Models**: Pydantic models for validation and type safety + +## Main Interface + +### DataCard +::: tfbpapi.datainfo.datacard.DataCard + options: + show_root_heading: true + show_source: true + +The `DataCard` class is the primary interface for exploring HuggingFace datasets. It provides methods to: + +- Discover dataset configurations and types +- Explore feature schemas and data types +- Understand metadata relationships +- Extract field values and experimental conditions +- Navigate partitioned dataset structures + +## Data Models + +### Core Models +::: tfbpapi.datainfo.models.DatasetCard + options: + show_root_heading: true + +::: tfbpapi.datainfo.models.DatasetConfig + options: + show_root_heading: true + +::: tfbpapi.datainfo.models.FeatureInfo + options: + show_root_heading: true + +### Dataset Types +::: tfbpapi.datainfo.models.DatasetType + options: + show_root_heading: true + +### Relationship Models +::: tfbpapi.datainfo.models.MetadataRelationship + options: + show_root_heading: true + +::: tfbpapi.datainfo.models.ExtractedMetadata + options: + show_root_heading: true + +## Data Fetchers + +### HuggingFace Integration +::: tfbpapi.datainfo.fetchers.HfDataCardFetcher + options: + show_root_heading: true + +::: tfbpapi.datainfo.fetchers.HfRepoStructureFetcher + options: + show_root_heading: true + +::: tfbpapi.datainfo.fetchers.HfSizeInfoFetcher + options: + show_root_heading: true + +## Usage Examples + +### Basic Dataset Exploration + +```python +from tfbpapi.datainfo import DataCard + +# Initialize DataCard for a repository +card = DataCard('BrentLab/rossi_2021') + +# Get repository overview +repo_info = card.get_repository_info() +print(f"Dataset: {repo_info['pretty_name']}") +print(f"Configurations: {repo_info['num_configs']}") + +# Explore configurations +for config in card.configs: + print(f"{config.config_name}: {config.dataset_type.value}") +``` + +### Understanding Dataset Structure + +```python +# Get detailed config information +config_info = card.explore_config('metadata') +print(f"Features: {config_info['num_features']}") + +# Check for partitioned data +if 'partitioning' in config_info: + partition_info = config_info['partitioning'] + print(f"Partitioned by: {partition_info['partition_by']}") +``` + +### Metadata Relationships + +```python +# Discover metadata relationships +relationships = card.get_metadata_relationships() +for rel in relationships: + print(f"{rel.data_config} -> {rel.metadata_config} ({rel.relationship_type})") +``` + +## Integration with HfQueryAPI + +The datainfo package is designed to work seamlessly with `HfQueryAPI` for efficient data loading: + +```python +from tfbpapi import HfQueryAPI +from tfbpapi.datainfo import DataCard + +# Explore dataset structure first +card = DataCard('BrentLab/rossi_2021') +config_info = card.explore_config('genome_map') + +# Use insights to load data efficiently +query_api = HfQueryAPI('BrentLab/rossi_2021') +data = query_api.get_pandas('genome_map', + filters={'run_accession': 'SRR123456'}) +``` + +For a complete tutorial, see the [DataCard Tutorial](tutorials/datacard_tutorial.ipynb). \ No newline at end of file diff --git a/docs/errors.md b/docs/errors.md new file mode 100644 index 0000000..cfbf1df --- /dev/null +++ b/docs/errors.md @@ -0,0 +1 @@ +::: tfbpapi.errors \ No newline at end of file diff --git a/docs/huggingface_datacard.md b/docs/huggingface_datacard.md new file mode 100644 index 0000000..916d6d4 --- /dev/null +++ b/docs/huggingface_datacard.md @@ -0,0 +1,315 @@ +# HuggingFace Dataset Card Format + +This document describes the expected YAML metadata format for HuggingFace dataset repositories used with the tfbpapi package. The metadata is defined in the repository's README.md file frontmatter and provides structured information about the dataset configuration and contents. + +## Required Top-Level Fields + +### Basic Metadata +```yaml +license: mit # Dataset license +language: # Languages (usually 'en' for scientific data) +- en +tags: # Descriptive tags for discoverability +- biology +- genomics +- yeast +- transcription-factors +pretty_name: "Dataset Name" # Human-readable dataset name +size_categories: # Dataset size category +- 100KRank Transforms - -

Shifted Negative Log Ranks

- -::: tfbpapi.rank_transforms.shifted_negative_log_ranks - -

Stable Rank

- -::: tfbpapi.rank_transforms.stable_rank - -

Rank by p-value

- -::: tfbpapi.rank_transforms.rank_by_pvalue - -

Transform

- -::: tfbpapi.rank_transforms.transform \ No newline at end of file diff --git a/docs/tutorials/datacard_tutorial.ipynb b/docs/tutorials/datacard_tutorial.ipynb new file mode 100644 index 0000000..071397b --- /dev/null +++ b/docs/tutorials/datacard_tutorial.ipynb @@ -0,0 +1,759 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# DataCard Tutorial: Exploring HuggingFace Genomics Datasets\n", + "\n", + "The `DataCard` class provides an easy-to-use interface for exploring HuggingFace dataset metadata without loading the actual genomic data. This is particularly useful for:\n", + "\n", + "- Understanding dataset structure and available configurations\n", + "- Exploring experimental conditions and regulators\n", + "- Discovering metadata relationships\n", + "- Planning data analysis workflows\n", + "\n", + "In this tutorial, we'll explore the **BrentLab/rossi_2021** dataset, which contains ChIP-exo data for transcription factor binding in yeast." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1. Getting Started\n", + "\n", + "First, let's import the DataCard class and initialize it with our target dataset." + ] + }, + { + "cell_type": "code", + "execution_count": 132, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Repository: BrentLab/rossi_2021\n" + ] + } + ], + "source": [ + "from tfbpapi.datainfo import DataCard\n", + "\n", + "# Initialize DataCard with the Rossi 2021 dataset\n", + "# try this with mahendrawada_2025, which is more complex\n", + "card = DataCard('BrentLab/rossi_2021')\n", + "\n", + "print(f\"Repository: {card.repo_id}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2. Repository Overview\n", + "\n", + "Let's start by getting a high-level overview of the dataset." + ] + }, + { + "cell_type": "code", + "execution_count": 133, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Repository Information:\n", + "========================================\n", + "repo_id : BrentLab/rossi_2021\n", + "pretty_name : Rossi ChIP-exo 2021\n", + "license : mit\n", + "tags : ['transcription-factor', 'binding', 'chipexo', 'genomics', 'biology']\n", + "language : ['en']\n", + "size_categories : None\n", + "num_configs : 2\n", + "dataset_types : ['metadata', 'genome_map']\n", + "total_files : 1237\n", + "last_modified : 2025-09-18T00:59:17+00:00\n", + "has_default_config : True\n" + ] + } + ], + "source": [ + "# Get repository information\n", + "repo_info = card.get_repository_info()\n", + "\n", + "print(\"Repository Information:\")\n", + "print(\"=\" * 40)\n", + "for key, value in repo_info.items():\n", + " print(f\"{key:20}: {value}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 134, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dataset Summary:\n", + "==================================================\n", + "Dataset: Rossi ChIP-exo 2021\n", + "Repository: BrentLab/rossi_2021\n", + "License: mit\n", + "Configurations: 2\n", + "Dataset Types: metadata, genome_map\n", + "Tags: transcription-factor, binding, chipexo, genomics, biology\n", + "\n", + "Configurations:\n", + " - metadata: metadata (default)\n", + " Metadata describing the tagged regulator in each experiment\n", + " - genome_map: genome_map\n", + " ChIP-exo 5' tag coverage data partitioned by sample accession\n" + ] + } + ], + "source": [ + "# Get a human-readable summary\n", + "print(\"Dataset Summary:\")\n", + "print(\"=\" * 50)\n", + "print(card.summary())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3. Exploring Configurations\n", + "\n", + "Datasets can have multiple configurations representing different types of data. Let's explore what's available in this dataset." + ] + }, + { + "cell_type": "code", + "execution_count": 135, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of configurations: 2\n", + "\n", + "Configuration details:\n", + "\n", + "• metadata:\n", + " Type: metadata\n", + " Default: True\n", + " Description: Metadata describing the tagged regulator in each experiment\n", + " Features: 4\n", + "\n", + "• genome_map:\n", + " Type: genome_map\n", + " Default: False\n", + " Description: ChIP-exo 5' tag coverage data partitioned by sample accession\n", + " Features: 3\n" + ] + } + ], + "source": [ + "# List all configurations\n", + "print(f\"Number of configurations: {len(card.configs)}\")\n", + "print(\"\\nConfiguration details:\")\n", + "\n", + "for config in card.configs:\n", + " print(f\"\\n• {config.config_name}:\")\n", + " print(f\" Type: {config.dataset_type.value}\")\n", + " print(f\" Default: {config.default}\")\n", + " print(f\" Description: {config.description}\")\n", + " print(f\" Features: {len(config.dataset_info.features)}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Understanding Dataset Types\n", + "\n", + "The Rossi 2021 dataset contains two types of configurations:\n", + "\n", + "- **`metadata`**: Experimental metadata describing each ChIP-exo sample\n", + "- **`genome_map`**: Position-level ChIP-exo tag coverage data\n", + "\n", + "Let's explore each configuration in detail." + ] + }, + { + "cell_type": "code", + "execution_count": 136, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Metadata Configuration Details:\n", + "========================================\n", + "Config name: metadata\n", + "Dataset type: metadata\n", + "Number of features: 4\n", + "Default config: True\n", + "\n", + "Features in metadata config:\n", + " • regulator_locus_tag (string ): Systematic gene name (ORF identifier) of the transcription factor\n", + " • regulator_symbol (string ): Standard gene symbol of the transcription factor\n", + " • run_accession (string ): GEO run accession identifier for the sample\n", + " • yeastepigenome_id (string ): Sample identifier used by yeastepigenome.org\n" + ] + } + ], + "source": [ + "# Explore the metadata configuration\n", + "metadata_info = card.explore_config('metadata')\n", + "\n", + "print(\"Metadata Configuration Details:\")\n", + "print(\"=\" * 40)\n", + "print(f\"Config name: {metadata_info['config_name']}\")\n", + "print(f\"Dataset type: {metadata_info['dataset_type']}\")\n", + "print(f\"Number of features: {metadata_info['num_features']}\")\n", + "print(f\"Default config: {metadata_info['is_default']}\")\n", + "\n", + "print(\"\\nFeatures in metadata config:\")\n", + "for feature in metadata_info['features']:\n", + " print(f\" • {feature['name']:20} ({feature['dtype']:10}): {feature['description']}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 137, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Genome Map Configuration Details:\n", + "========================================\n", + "Config name: genome_map\n", + "Dataset type: genome_map\n", + "Number of features: 3\n", + "\n", + "Features in genome_map config:\n", + " • chr (string ): Chromosome name (e.g., chrI, chrII, etc.)\n", + " • pos (int32 ): Genomic position of the 5' tag\n", + " • pileup (int32 ): Depth of coverage (number of 5' tags) at this genomic position\n", + "\n", + "Partitioning Information:\n", + " Enabled: True\n", + " Partition by: ['run_accession']\n", + " Path template: genome_map/accession={run_accession}/*.parquet\n" + ] + } + ], + "source": [ + "# Explore the genome_map configuration\n", + "genome_map_info = card.explore_config('genome_map')\n", + "\n", + "print(\"Genome Map Configuration Details:\")\n", + "print(\"=\" * 40)\n", + "print(f\"Config name: {genome_map_info['config_name']}\")\n", + "print(f\"Dataset type: {genome_map_info['dataset_type']}\")\n", + "print(f\"Number of features: {genome_map_info['num_features']}\")\n", + "\n", + "print(\"\\nFeatures in genome_map config:\")\n", + "for feature in genome_map_info['features']:\n", + " print(f\" • {feature['name']:15} ({feature['dtype']:10}): {feature['description']}\")\n", + "\n", + "# Check if this config has partitioning\n", + "if 'partitioning' in genome_map_info:\n", + " print(\"\\nPartitioning Information:\")\n", + " partitioning = genome_map_info['partitioning']\n", + " print(f\" Enabled: {partitioning['enabled']}\")\n", + " print(f\" Partition by: {partitioning['partition_by']}\")\n", + " print(f\" Path template: {partitioning['path_template']}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4. Understanding Data Relationships\n", + "\n", + "The DataCard can help you understand how different configurations relate to each other, particularly metadata relationships." + ] + }, + { + "cell_type": "code", + "execution_count": 138, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 1 metadata relationships:\n", + "\n", + "Relationship details:\n", + " • genome_map -> metadata\n", + " Type: explicit\n", + " (Metadata config explicitly specifies which data configs it applies to)\n" + ] + } + ], + "source": [ + "# Explore metadata relationships\n", + "relationships = card.get_metadata_relationships()\n", + "\n", + "print(f\"Found {len(relationships)} metadata relationships:\")\n", + "print(\"\\nRelationship details:\")\n", + "\n", + "for rel in relationships:\n", + " print(f\" • {rel.data_config} -> {rel.metadata_config}\")\n", + " print(f\" Type: {rel.relationship_type}\")\n", + "\n", + " if rel.relationship_type == \"explicit\":\n", + " print(\" (Metadata config explicitly specifies which data configs it applies to)\")\n", + " elif rel.relationship_type == \"embedded\":\n", + " print(\" (Metadata is embedded within the data config itself)\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 5. Exploring Dataset Contents\n", + "\n", + "Now let's explore what experimental data is available in this dataset." + ] + }, + { + "cell_type": "code", + "execution_count": 139, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Metadata configurations: ['metadata']\n", + "Data configurations: ['genome_map']\n", + "\n", + "Default configuration: metadata\n" + ] + } + ], + "source": [ + "# Get different config types\n", + "from tfbpapi.datainfo.models import DatasetType\n", + "\n", + "# Find metadata configs\n", + "metadata_configs = card.get_configs_by_type(DatasetType.METADATA)\n", + "print(f\"Metadata configurations: {[c.config_name for c in metadata_configs]}\")\n", + "\n", + "# Find data configs\n", + "data_configs = card.get_configs_by_type(DatasetType.GENOME_MAP)\n", + "print(f\"Data configurations: {[c.config_name for c in data_configs]}\")\n", + "\n", + "# Get the default config\n", + "default_config = card.dataset_card.get_default_config()\n", + "if default_config:\n", + " print(f\"\\nDefault configuration: {default_config.config_name}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Extracting Field Values\n", + "\n", + "For exploration purposes, we can extract unique values from specific fields. This is particularly useful for understanding what experimental conditions or regulators are available." + ] + }, + { + "cell_type": "code", + "execution_count": 140, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 0 unique run accessions:\n", + "No accessions found (might require partition-based extraction)\n" + ] + } + ], + "source": [ + "# Try to extract run accession information\n", + "try:\n", + " accessions = card.get_field_values('metadata', 'run_accession')\n", + " print(f\"Found {len(accessions)} unique run accessions:\")\n", + " if accessions:\n", + " sample_accessions = sorted(list(accessions))[:5]\n", + " print(f\"Sample accessions: {sample_accessions}...\")\n", + " else:\n", + " print(\"No accessions found (might require partition-based extraction)\")\n", + "except Exception as e:\n", + " print(f\"Could not extract accession values: {e}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 6. Working with Partitioned Data\n", + "\n", + "Many genomics datasets are partitioned for efficient storage and querying. Let's explore how partitioning works in this dataset." + ] + }, + { + "cell_type": "code", + "execution_count": 141, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Partitioning Details:\n", + "==============================\n", + "Enabled: True\n", + "Partition columns: ['run_accession']\n", + "Path template: genome_map/accession={run_accession}/*.parquet\n", + "\n", + "This means:\n", + "• The genome map data is split into separate files for each run_accession\n", + "• Files are organized as: genome_map/accession={run_accession}/*.parquet\n", + "• This allows efficient querying of specific experimental runs\n" + ] + } + ], + "source": [ + "# Check partitioning details for the genome_map config\n", + "genome_map_config = card.get_config('genome_map')\n", + "\n", + "if genome_map_config and genome_map_config.dataset_info.partitioning:\n", + " part_info = genome_map_config.dataset_info.partitioning\n", + "\n", + " print(\"Partitioning Details:\")\n", + " print(\"=\" * 30)\n", + " print(f\"Enabled: {part_info.enabled}\")\n", + " print(f\"Partition columns: {part_info.partition_by}\")\n", + " print(f\"Path template: {part_info.path_template}\")\n", + "\n", + " print(\"\\nThis means:\")\n", + " print(\"• The genome map data is split into separate files for each run_accession\")\n", + " print(\"• Files are organized as: genome_map/accession={run_accession}/*.parquet\")\n", + " print(\"• This allows efficient querying of specific experimental runs\")\n", + "else:\n", + " print(\"No partitioning information found.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 7. Understanding Data Files\n", + "\n", + "Let's examine how the data files are organized within each configuration." + ] + }, + { + "cell_type": "code", + "execution_count": 142, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Data files for 'metadata' config:\n", + "----------------------------------------\n", + " File 1:\n", + " Split: train\n", + " Path: rossi_2021_metadata.parquet\n", + "\n", + "Data files for 'genome_map' config:\n", + "----------------------------------------\n", + " File 1:\n", + " Split: train\n", + " Path: genome_map/*/*.parquet\n", + " - This is a glob pattern that matches multiple files\n" + ] + } + ], + "source": [ + "# Examine data files for each configuration\n", + "for config in card.configs:\n", + " print(f\"\\nData files for '{config.config_name}' config:\")\n", + " print(\"-\" * 40)\n", + "\n", + " for i, data_file in enumerate(config.data_files):\n", + " print(f\" File {i+1}:\")\n", + " print(f\" Split: {data_file.split}\")\n", + " print(f\" Path: {data_file.path}\")\n", + "\n", + " # Explain path patterns\n", + " if '*' in data_file.path:\n", + " print(f\" - This is a glob pattern that matches multiple files\")\n", + " if '=' in data_file.path:\n", + " print(f\" - This uses partitioned directory structure\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 8. Practical Use Cases\n", + "\n", + "Here are some common scenarios where DataCard is useful:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Use Case 1: Finding Datasets with Specific Data Types" + ] + }, + { + "cell_type": "code", + "execution_count": 143, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Available dataset types: ['metadata', 'genome_map']\n", + "\n", + "Has genome-wide binding data: True\n" + ] + } + ], + "source": [ + "# Check what types of data are available\n", + "available_types = [config.dataset_type.value for config in card.configs]\n", + "print(f\"Available dataset types: {available_types}\")\n", + "\n", + "# Check if this dataset has genome-wide binding data\n", + "has_genome_map = any(config.dataset_type == DatasetType.GENOME_MAP for config in card.configs)\n", + "print(f\"\\nHas genome-wide binding data: {has_genome_map}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Use Case 2: Understanding Data Schema Before Loading" + ] + }, + { + "cell_type": "code", + "execution_count": 144, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Genome Map Data Schema:\n", + "==============================\n", + "Column: chr\n", + " Type: string\n", + " Description: Chromosome name (e.g., chrI, chrII, etc.)\n", + "\n", + "Column: pos\n", + " Type: int32\n", + " Description: Genomic position of the 5' tag\n", + "\n", + "Column: pileup\n", + " Type: int32\n", + " Description: Depth of coverage (number of 5' tags) at this genomic position\n", + "\n", + "This tells us:\n", + "• 'chr' column contains chromosome names (string)\n", + "• 'pos' column contains genomic positions (int32)\n", + "• 'pileup' column contains tag counts (int32)\n", + "• Data represents 5' tag coverage from ChIP-exo experiments\n" + ] + } + ], + "source": [ + "# Before loading large genome map data, understand its structure\n", + "genome_config = card.get_config('genome_map')\n", + "\n", + "if genome_config:\n", + " print(\"Genome Map Data Schema:\")\n", + " print(\"=\" * 30)\n", + "\n", + " for feature in genome_config.dataset_info.features:\n", + " print(f\"Column: {feature.name}\")\n", + " print(f\" Type: {feature.dtype}\")\n", + " print(f\" Description: {feature.description}\")\n", + " print()\n", + "\n", + " print(\"This tells us:\")\n", + " print(\"• 'chr' column contains chromosome names (string)\")\n", + " print(\"• 'pos' column contains genomic positions (int32)\")\n", + " print(\"• 'pileup' column contains tag counts (int32)\")\n", + " print(\"• Data represents 5' tag coverage from ChIP-exo experiments\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Use Case 3: Planning Efficient Data Access" + ] + }, + { + "cell_type": "code", + "execution_count": 145, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Data Access Strategy:\n", + "=========================\n", + "• Data is partitioned by 'run_accession'\n", + "• To load data for a specific experiment, filter by run_accession\n", + "• This avoids loading data from all experiments\n", + "• Path pattern: genome_map/accession={run_accession}/*.parquet\n", + "\n", + "Example workflow:\n", + "1. Use metadata config to find interesting run_accessions\n", + "2. Load only genome_map data for those specific accessions\n", + "3. Analyze position-level binding data for selected experiments\n" + ] + } + ], + "source": [ + "# Understanding partitioning helps plan efficient queries\n", + "if genome_config and genome_config.dataset_info.partitioning:\n", + " print(\"Data Access Strategy:\")\n", + " print(\"=\" * 25)\n", + " print(\"• Data is partitioned by 'run_accession'\")\n", + " print(\"• To load data for a specific experiment, filter by run_accession\")\n", + " print(\"• This avoids loading data from all experiments\")\n", + " print(\"• Path pattern: genome_map/accession={run_accession}/*.parquet\")\n", + "\n", + " print(\"\\nExample workflow:\")\n", + " print(\"1. Use metadata config to find interesting run_accessions\")\n", + " print(\"2. Load only genome_map data for those specific accessions\")\n", + " print(\"3. Analyze position-level binding data for selected experiments\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 9. Error Handling and Troubleshooting\n", + "\n", + "The DataCard class includes validation and error handling. Here are some common scenarios:" + ] + }, + { + "cell_type": "code", + "execution_count": 146, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Non-existent config result: None\n", + "Error accessing non-existent field: Field 'nonexistent_field' not found in config 'metadata'\n", + "Config 'some_config' not found in this dataset\n" + ] + } + ], + "source": [ + "# Handling missing configurations\n", + "missing_config = card.get_config('nonexistent_config')\n", + "print(f\"Non-existent config result: {missing_config}\")\n", + "\n", + "# Handling missing fields\n", + "try:\n", + " invalid_field = card.get_field_values('metadata', 'nonexistent_field')\n", + "except Exception as e:\n", + " print(f\"Error accessing non-existent field: {e}\")\n", + "\n", + "# Checking if config exists before using\n", + "config_name = 'some_config'\n", + "if card.get_config(config_name):\n", + " print(f\"Config '{config_name}' exists\")\n", + "else:\n", + " print(f\"Config '{config_name}' not found in this dataset\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 10. Summary and Next Steps\n", + "\n", + "The DataCard class provides a powerful way to explore HuggingFace genomics datasets before committing to loading large amounts of data. \n", + "\n", + "### Key Takeaways:\n", + "\n", + "1. **Dataset Structure**: The Rossi 2021 dataset contains both experimental metadata and genome-wide ChIP-exo binding data\n", + "2. **Partitioning**: Data is efficiently partitioned by experimental run for fast access\n", + "3. **Metadata Relationships**: The system automatically understands how metadata relates to data configs\n", + "4. **Schema Discovery**: You can understand data types and structure before loading\n", + "\n", + "### Next Steps:\n", + "\n", + "- Use `HfQueryAPI` to load specific subsets of the data based on your exploration\n", + "- Apply filters based on experimental conditions discovered through DataCard\n", + "- Combine multiple datasets that have compatible schemas\n", + "\n", + "### Example Integration with HfQueryAPI:\n", + "\n", + "```python\n", + "from tfbpapi import HfQueryAPI\n", + "\n", + "# After exploring with DataCard, load specific data\n", + "query_api = HfQueryAPI('BrentLab/rossi_2021')\n", + "\n", + "# Load metadata for planning\n", + "metadata_df = query_api.get_pandas('metadata')\n", + "\n", + "# Load genome map data for specific experiments\n", + "# (using partition filters based on DataCard exploration)\n", + "genome_data = query_api.get_pandas('genome_map', \n", + " filters={'run_accession': 'SRR123456'})\n", + "```" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "tfbpapi-py3.11 (3.11.9)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/mkdocs.yml b/mkdocs.yml index c367973..85960a2 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,5 +1,5 @@ site_name: tfbpapi -site_description: "A collection of objects and functions to work with calling cards sequencing tools" +site_description: "Python API for querying and analyzing genomic datasets from HuggingFace Hub" site_author: "ben mueller , chase mateusiak , michael brent " site_url: "https://brentlab.github.io/tfbpapi" repo_url: "https://github.com/brentlab/tfbpapi" @@ -62,7 +62,7 @@ plugins: handlers: python: paths: [.] - import: + inventories: - https://docs.python.org/3/objects.inv - https://numpy.org/doc/stable/objects.inv - https://pandas.pydata.org/docs/objects.inv @@ -149,29 +149,17 @@ extra_css: nav: - Home: index.md - Tutorials: - - Database tfbpapi: tutorials/database_tfbpapi.ipynb - - API: - - Database tfbpapi: - - Records Only Classes: - - BindingManualQCAPI.md - - DataSourceAPI.md - - DtoAPI.md - - ExpressionManualQCAPI.md - - FileFormatAPI.md - - GenomicFeatureAPI.md - - RegulatorAPI.md - - Records and Files Classes: - - BindingAPI: BindingAPI.md - - BindingConcatenatedAPI: BindingConcatenatedAPI.md - - CallingCardsBackgroundAPI: CallingCardsBackgroundAPI.md - - ExpressionAPI: ExpressionAPI.md - - PromoterSetAPI: PromoterSetAPI.md - - PromoterSetSigAPI: PromoterSetSigAPI.md - - Developer Classes: - - AbstractAPI.md - - AbstractRecordsAndFilesAPI.md - - AbstractRecordsOnlyAPI.md - - AbstractHfAPI.md - - HfCacheManager.md - - Cache.md - - ParamsDict.md + - "DataCard: Exploring Datasets": tutorials/datacard_tutorial.ipynb + - API Reference: + - Core Components: + - HfQueryAPI: HfQueryAPI.md + - HfRankResponse: HfRankResponse.md + - IncrementalAnalysisDB: IncrementalAnalysisDB.md + - Cache Management: + - HfCacheManager: HfCacheManager.md + - Dataset Information: + - DataInfo Package: datainfo.md + - Error Handling: + - Custom Exceptions: errors.md + - HuggingFace Configuration: + - HuggingFace Dataset Card Format: huggingface_datacard.md diff --git a/pyproject.toml b/pyproject.toml index 916be3d..72a4b01 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,11 +22,11 @@ huggingface-hub = "^0.34.4" types-requests = "^2.32.4.20250809" datasets = "^4.0.0" duckdb = "^1.3.2" +pydantic = "^2.11.9" [tool.poetry.group.dev.dependencies] pytest = "^8.3.5" -pytest-snapshot = "^0.9.0" pytest-asyncio = "^0.26.0" types-requests = "^2.32.4.20250809" mkdocs = "^1.6.1" diff --git a/tfbpapi/AbstractAPI.py b/tfbpapi/AbstractAPI.py deleted file mode 100644 index 39bd10a..0000000 --- a/tfbpapi/AbstractAPI.py +++ /dev/null @@ -1,230 +0,0 @@ -import logging -import os -from abc import ABC, abstractmethod -from collections.abc import Coroutine -from typing import Any - -import pandas as pd -import requests - -from tfbpapi.Cache import Cache -from tfbpapi.ParamsDict import ParamsDict - - -class AbstractAPI(ABC): - """ - Abstract base class for creating API clients that require token authentication. - - This class provides a template for connecting to a cache for caching API responses, - validating parameters against a list of valid keys, and provides an interface for - CRUD operations. - - """ - - def __init__( - self, - url: str = "", - token: str = "", - **kwargs, - ): - """ - Initialize the API client. - - :param url: The API endpoint URL. Defaults to the `BASE_URL` - environment variable. - :param token: The authentication token. Defaults to the `TOKEN` - environment variable. - :param valid_param_keys: A list of valid parameter keys for the API. - :param params: A ParamsDict object containing parameters for the API request. - :param cache: a Cache object for caching API responses. - :param kwargs: Additional keyword arguments that may be passed on to the - ParamsDict and Cache constructors. - - """ - self.logger = logging.getLogger(self.__class__.__name__) - self._token = token or os.getenv("TOKEN", "") - self.url = url or os.getenv("BASE_URL", "") - self.params = ParamsDict( - params=kwargs.pop("params", {}), - valid_keys=kwargs.pop("valid_keys", []), - ) - self.cache = Cache( - maxsize=kwargs.pop("maxsize", 100), ttl=kwargs.pop("ttl", 300) - ) - - @property - def header(self) -> dict[str, str]: - """The HTTP authorization header.""" - return { - "Authorization": f"token {self.token}", - "Content-Type": "application/json", - } - - @property - def url(self) -> str: - """The URL for the API.""" - return self._url # type: ignore - - @url.setter - def url(self, value: str) -> None: - if not value: - self._url = None - elif hasattr(self, "token") and self.token: - # validate the URL with the new token - self._is_valid_url(value) - self._url = value - else: - self.logger.warning("No token provided: URL un-validated") - self._url = value - - @property - def token(self) -> str: - """The authentication token for the API.""" - return self._token - - @token.setter - def token(self, value: str) -> None: - self._token = value - # validate the URL with the new token - if hasattr(self, "url") and self.url: - self.logger.info("Validating URL with new token") - self._is_valid_url(self.url) - - @property - def cache(self) -> Cache: - """The cache object for caching API responses.""" - return self._cache - - @cache.setter - def cache(self, value: Cache) -> None: - self._cache = value - - @property - def params(self) -> ParamsDict: - """The ParamsDict object containing parameters for the API request.""" - return self._params - - @params.setter - def params(self, value: ParamsDict) -> None: - self._params = value - - def push_params(self, params: dict[str, Any]) -> None: - """Adds or updates parameters in the ParamsDict.""" - try: - self.params.update(params) - except KeyError as e: - self.logger.error(f"Error updating parameters: {e}") - - def pop_params(self, keys: list[str] | None = None) -> None: - """Removes parameters from the ParamsDict.""" - if keys is None: - self.params.clear() - return - if keys is not None and not isinstance(keys, list): - keys = [keys] - for key in keys: - del self.params[key] - - @abstractmethod - def create(self, data: dict[str, Any], **kwargs) -> Any: - """Placeholder for the create method.""" - raise NotImplementedError( - f"`create()` is not implemented for {self.__class__.__name__}" - ) - - @abstractmethod - def read(self, **kwargs) -> Any: - """Placeholder for the read method.""" - raise NotImplementedError( - f"`read()` is not implemented for {self.__class__.__name__}" - ) - - @abstractmethod - def update(self, df: pd.DataFrame, **kwargs) -> Any: - """Placeholder for the update method.""" - raise NotImplementedError( - f"`update()` is not implemented for {self.__class__.__name__}" - ) - - @abstractmethod - def delete(self, id: str, **kwargs) -> Any: - """Placeholder for the delete method.""" - raise NotImplementedError( - f"`delete()` is not implemented for {self.__class__.__name__}" - ) - - @abstractmethod - def submit(self, post_dict: dict[str, Any], **kwargs) -> Any: - """Placeholder for the submit method.""" - raise NotImplementedError( - f"`submit()` is not implemented for {self.__class__.__name__}" - ) - - @abstractmethod - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Coroutine[Any, Any, Any]: - """Placeholder for the retrieve method.""" - raise NotImplementedError( - f"`retrieve()` is not implemented for {self.__class__.__name__}" - ) - - def _is_valid_url(self, url: str) -> None: - """ - Confirms that the URL is valid and the header authorization is appropriate. - - :param url: The URL to validate. - :type url: str - :raises ValueError: If the URL is invalid or the token is not set. - - """ - try: - # note that with allow_redirect=True the result can be a 300 status code - # which is not an error, and then another request to the redirected URL - response = requests.head(url, headers=self.header, allow_redirects=True) - if response.status_code != 200: - raise ValueError("Invalid URL or token provided. Check both.") - except requests.RequestException as e: - raise AttributeError(f"Error validating URL: {e}") from e - except AttributeError as e: - self.logger.error(f"Error validating URL: {e}") - - def _cache_get(self, key: str, default: Any = None) -> Any: - """ - Get a value from the cache if configured. - - :param key: The key to retrieve from the cache. - :type key: str - :param default: The default value to return if the key is not found. - :type default: any, optional - :return: The value from the cache or the default value. - :rtype: any - - """ - return self.cache.get(key, default=default) - - def _cache_set(self, key: str, value: Any) -> None: - """ - Set a value in the cache if configured. - - :param key: The key to set in the cache. - :type key: str - :param value: The value to set in the cache. - :type value: any - - """ - self.cache.set(key, value) - - def _cache_list(self) -> list[str]: - """List keys in the cache if configured.""" - return self.cache.list() - - def _cache_delete(self, key: str) -> None: - """ - Delete a key from the cache if configured. - - :param key: The key to delete from the cache. - :type key: str - - """ - self.cache.delete(key) diff --git a/tfbpapi/AbstractHfAPI.py b/tfbpapi/AbstractHfAPI.py deleted file mode 100644 index 58e0884..0000000 --- a/tfbpapi/AbstractHfAPI.py +++ /dev/null @@ -1,373 +0,0 @@ -import logging -import os -from abc import ABC, abstractmethod -from collections.abc import Mapping -from pathlib import Path -from typing import Any, Literal - -import requests -from huggingface_hub import hf_hub_download, snapshot_download -from huggingface_hub.constants import HF_HUB_CACHE -from requests import HTTPError - -# Constants -MB_TO_BYTES = 1024 * 1024 - - -class RepoTooLargeError(ValueError): - """Raised when repository exceeds auto-download threshold.""" - - -class AbstractHfAPI(ABC): - """Abstract base class for creating Hugging Face API clients.""" - - def __init__( - self, - repo_id: str, - repo_type: Literal["model", "dataset", "space"] = "dataset", - token: str | None = None, - cache_dir: str | Path | None = None, - ): - """ - Initialize the HF-backed API client. - - :param repo_id: The repo identifier on HF (e.g., "user/dataset"). Eg, - "BrentLab/yeast_genome_resources" - :param token: Optional. Not necessary for public repos. May be set via the - HF_TOKEN environment variable. - :param repo_type: One of {"model", "dataset", "space"}. Defaults to "dataset". - :param cache_dir: HF cache_dir for hf_hub_download and snapshot_download (see - huggingface_hub docs). May be passed via the HF_CACHE_DIR environmental - variable. If not set, the default HF cache directory is used. - :raises FileNotFoundError: If the specified cache_dir does not exist. - - """ - self.logger = logging.getLogger(self.__class__.__name__) - - self.token = token or os.getenv("HF_TOKEN", None) - self.repo_id = repo_id - self.repo_type = repo_type - self.cache_dir = Path( - cache_dir if cache_dir else os.getenv("HF_CACHE_DIR", HF_HUB_CACHE) - ) - - @property - def token(self) -> str | None: - return self._token - - @token.setter - def token(self, value: str | None) -> None: - # TODO: if a token is provided, then validate that it works. Only necessary - # if token is not None of course - self._token = value - - @property - def repo_id(self) -> str: - return self._repo_id - - @repo_id.setter - def repo_id(self, value: str) -> None: - """ - Set the repo_id. - - This setter also calls _get_dataset_size to fetch size info and validate that - the repo exists and is accessible. No error is raised if the repo is not - accessible, but an error is logged. - - """ - self._repo_id = value - try: - self._get_dataset_size(self._repo_id) - except (HTTPError, ValueError) as e: - self.logger.error(f"Could not reach {value}: {e}") - - @property - def cache_dir(self) -> Path: - return self._cache_dir - - @cache_dir.setter - def cache_dir(self, value: str | Path) -> None: - """ - Set the cache directory for huggingface_hub downloads. - - :raises FileNotFoundError: If the specified directory does not exist. - - """ - path = Path(value) - if not path.exists(): - raise FileNotFoundError(f"Cache directory {path} does not exist") - self._cache_dir = path - - @property - def size(self) -> dict[str, Any] | None: - """ - Size information from the HF Dataset Server API. - - This reaches the /size endpoint. See - https://github.com/huggingface/dataset-viewer/blob/8f0ae65f0ff64791111d37a725af437c3c752daf/docs/source/size.md - - """ - return getattr(self, "_size", None) - - @size.setter - def size(self, value: dict[str, Any]) -> None: - self._size = value - - @property - def snapshot_path(self) -> Path | None: - """Path to the last downloaded snapshot (if any).""" - return getattr(self, "_snapshot_path", None) - - @snapshot_path.setter - def snapshot_path(self, value: str | Path | None) -> None: - self._snapshot_path = None if value is None else Path(value) - - def _get_dataset_size_mb(self) -> float: - """Get dataset size in MB, returning inf if not available.""" - if not self.size: - return float("inf") - return ( - self.size.get("size", {}) - .get("dataset", {}) - .get("num_bytes_original_files", float("inf")) - / MB_TO_BYTES - ) - - def _ensure_str_paths(self, kwargs: dict[str, Any]) -> None: - """Ensure Path-like arguments are converted to strings.""" - for key in ["local_dir", "cache_dir"]: - if key in kwargs and kwargs[key] is not None: - kwargs[key] = str(kwargs[key]) - - def _build_auth_headers(self) -> dict[str, str]: - """Build authentication headers if token is available.""" - return {"Authorization": f"Bearer {self.token}"} if self.token else {} - - def _normalize_patterns(self, kwargs: dict[str, Any]) -> None: - """Convert string patterns to lists.""" - for pattern_key in ["allow_patterns", "ignore_patterns"]: - if pattern_key in kwargs and kwargs[pattern_key] is not None: - patterns = kwargs[pattern_key] - if isinstance(patterns, str): - kwargs[pattern_key] = [patterns] - - def _get_dataset_size(self, repo_id: str | None = None) -> None: - """ - Get dataset size information from HuggingFace Dataset Server API. - - :returns: Dict containing size information with additional metadata about - completeness - :raises requests.HTTPError: If the API request fails - :raises ValueError: If the dataset doesn't exist or isn't accessible - - """ - repo_id = repo_id or self.repo_id - url = f"https://datasets-server.huggingface.co/size?dataset={repo_id}" - - response = requests.get(url, headers=self._build_auth_headers()) - response.raise_for_status() - - data = response.json() - - # Check if size determination was partial - is_partial = data.get("partial", False) - - if is_partial: - self.logger.warning( - f"Size information for {repo_id} is incomplete. " - "The dataset is too large for complete size determination. " - "Reported numbers may be lower than actual size." - ) - - # Add metadata about completeness to the response - if "size" in data and "dataset" in data["size"]: - data["size"]["dataset"]["size_determination_complete"] = not is_partial - data["size"]["dataset"]["size_warning"] = ( - "Partial size only - actual dataset may be larger" - if is_partial - else "Complete size information" - ) - - self.size = data - - def _download_single_file( - self, - filename: str, - dry_run: bool = False, - **kwargs, - ) -> Path: - """ - Download a single file using hf_hub_download. - - :param filename: File to download - :param dry_run: If True, log what would be downloaded without downloading - :param kwargs: Additional arguments passed directly to hf_hub_download - :return: Path to the downloaded file - - """ - self.logger.info(f"Downloading single file: {filename}") - - if dry_run: - self.logger.info(f"[DRY RUN] Would download {filename} from {self.repo_id}") - return Path("dry_run_path") - - # Build base arguments - hf_kwargs = { - "repo_id": self.repo_id, - "repo_type": self.repo_type, - "filename": filename, - "token": self.token, - **kwargs, - } - - # Set cache_dir only if local_dir not specified - if "local_dir" not in hf_kwargs and self.cache_dir is not None: - hf_kwargs["cache_dir"] = str(self.cache_dir) - - # Ensure string conversion for Path-like arguments - self._ensure_str_paths(hf_kwargs) - - file_path = hf_hub_download(**hf_kwargs) - self._snapshot_path = Path(file_path).parent - return Path(file_path) - - def _download_snapshot( - self, - dry_run: bool = False, - **kwargs, - ) -> Path: - """ - Download repository snapshot using snapshot_download. - - :param dry_run: If True, log what would be downloaded without downloading - :param kwargs: Additional arguments passed directly to snapshot_download - :return: Path to the downloaded snapshot - - """ - # Log download plan - if dry_run: - self.logger.info(f"[DRY RUN] Would download from {self.repo_id}:") - self.logger.info(f" - allow_patterns: {kwargs.get('allow_patterns')}") - self.logger.info(f" - ignore_patterns: {kwargs.get('ignore_patterns')}") - return Path("dry_run_path") - - # Execute snapshot download - self.logger.info( - f"Downloading repo snapshot with patterns - " - f"allow: {kwargs.get('allow_patterns')}, " - f"ignore: {kwargs.get('ignore_patterns')}" - ) - - # Build base arguments - # note that kwargs passed into this method will override defaults, - # including repo_id, etc - snapshot_kwargs = { - "repo_id": self.repo_id, - "repo_type": self.repo_type, - "token": self.token, - **kwargs, - } - - # Set cache_dir only if local_dir not specified and cache_dir wasn't passed in - if ( - "local_dir" not in snapshot_kwargs - and "cache_dir" not in snapshot_kwargs - and self.cache_dir is not None - ): - snapshot_kwargs["cache_dir"] = str(self.cache_dir) - - # Convert string patterns to lists - self._normalize_patterns(snapshot_kwargs) - - snapshot_path = snapshot_download(**snapshot_kwargs) - self.snapshot_path = Path(snapshot_path) - return self.snapshot_path - - def download( - self, - files: list[str] | str | None = None, - force_full_download: bool = False, - auto_download_threshold_mb: float = 100.0, - dry_run: bool = False, - **kwargs, - ) -> Path: - """ - Download dataset by file, patterns or if the dataset is small enough, the entire - repo. - - :param files: Specific file(s) to download. If provided, uses hf_hub_download - :param force_full_download: If True, always download entire repo regardless of - size - :param auto_download_threshold_mb: Auto-download full repo if estimated size < - this (MB) - :param dry_run: If True, log what would be downloaded without actually - downloading - :param kwargs: Additional arguments passed to hf_hub_download or - snapshot_download. Common args: revision, local_dir, cache_dir, - local_files_only, allow_patterns, ignore_patterns, etc. - :return: Path to downloaded content (file or directory). - - """ - dataset_size_mb = self._get_dataset_size_mb() - if dataset_size_mb <= auto_download_threshold_mb or force_full_download: - self.logger.info( - f"Dataset size ({dataset_size_mb:.2f} MB) is below the auto-download " - f"threshold of {auto_download_threshold_mb} MB. Downloading entire " - "repo." - ) - files = None - kwargs.pop("allow_patterns", None) - kwargs.pop("ignore_patterns", None) - elif ( - not files - and not kwargs.get("allow_patterns") - and not kwargs.get("ignore_patterns") - ): - excess_size_mb = dataset_size_mb - auto_download_threshold_mb - raise RepoTooLargeError( - f"Dataset size ({dataset_size_mb:.2f} MB) exceeds the " - f"auto-download threshold of {auto_download_threshold_mb} MB by " - f"{excess_size_mb:.2f} MB. To download the dataset, either " - "specify specific files or patterns to download, " - "set force_full_download=True or increase the " - "`auto_download_threshold_mb`." - ) - # Handle specific file downloads - if files is not None: - if isinstance(files, str) or (isinstance(files, list) and len(files) == 1): - # Single file - filename = files if isinstance(files, str) else files[0] - self.logger.info(f"Preparing to download single file: {filename}") - return self._download_single_file( - filename=filename, dry_run=dry_run, **kwargs - ) - elif isinstance(files, list) and len(files) > 1: - # Multiple files - use snapshot_download with allow_patterns - if kwargs.get("allow_patterns") is not None: - self.logger.warning( - "Both 'files' and 'allow_patterns' were provided. " - "'files' will take precedence." - ) - kwargs["allow_patterns"] = files - - return self._download_snapshot(dry_run=dry_run, **kwargs) - - @abstractmethod - def parse_datacard(self, *args: Any, **kwargs: Any) -> Mapping[str, Any]: - """ - Abstract method to parse a datacard from the downloaded content. - - Must be implemented by subclasses. - - """ - raise NotImplementedError("Subclasses must implement this method.") - - @abstractmethod - def query(self, *args: Any, **kwargs: Any) -> Any: - """ - Abstract method to query the API. - - Must be implemented by subclasses. - - """ - raise NotImplementedError("Subclasses must implement this method.") diff --git a/tfbpapi/AbstractRecordsAndFilesAPI.py b/tfbpapi/AbstractRecordsAndFilesAPI.py deleted file mode 100644 index 87f99ad..0000000 --- a/tfbpapi/AbstractRecordsAndFilesAPI.py +++ /dev/null @@ -1,314 +0,0 @@ -import csv -import gzip -import os -import tarfile -import tempfile -from collections.abc import Callable -from io import BytesIO -from typing import Any - -import aiohttp -import pandas as pd - -from tfbpapi.AbstractAPI import AbstractAPI - - -class AbstractRecordsAndFilesAPI(AbstractAPI): - """ - Abstract class to interact with both the records and the data stored in the `file` - field. - - The return for this class must be records, against the `/export` - endpoint when `retrieve_files` is False. When `retrieve_files` is True, the cache - should be checked first. If the file doesn't exist there, it should be retrieved - from the database against the `/record_table_and_files` endpoint. The file should - be a tarball with the metadata.csv and the file associated with the record, - where the file is named according to the `id` field in metadata.csv. Data files - should be `.csv.gz`. - - """ - - def __init__(self, **kwargs): - """ - Initialize the AbstractRecordsAndFilesAPI object. This will serve as an - interface to an endpoint that can serve both records and files, and cache the - file/retrieve from the cache if it exists. - - :param kwargs: parameters to pass to AbstractAPI. - - """ - self.export_url_suffix = kwargs.pop("export_url_suffix", "export") - self.export_files_url_suffix = kwargs.pop( - "export_files_url_suffix", "record_table_and_files" - ) - super().__init__(**kwargs) - - @property - def export_url_suffix(self) -> str: - """The URL suffix for exporting records.""" - return self._export_url_suffix - - @export_url_suffix.setter - def export_url_suffix(self, value: str) -> None: - self._export_url_suffix = value - - @property - def export_files_url_suffix(self) -> str: - """The URL suffix for exporting files.""" - return self._export_files_url_suffix - - @export_files_url_suffix.setter - def export_files_url_suffix(self, value: str) -> None: - self._export_files_url_suffix = value - - def _detect_delimiter(self, file_path: str, sample_size: int = 1024) -> str: - """ - Detect the delimiter of a CSV file. - - :param file_path: The path to the CSV file. - :type file_path: str - :param sample_size: The number of bytes to read from the file to detect the - delimiter. Defaults to 1024. - :type sample_size: int - :return: The delimiter of the CSV file. - :rtype: str - :raises FileNotFoundError: If the file does not exist. - :raises gzip.BadGzipFile: If the file is not a valid gzip file. - :raises _csv.Error: If the CSV sniffer cannot determine the delimiter. - - """ - try: - # by default, open() uses newline=False, which opens the file - # in universal newline mode and translates all new line characters - # to '\n' - file = ( - gzip.open(file_path, "rt") - if file_path.endswith(".gz") - else open(file_path) - ) - except FileNotFoundError as exc: - raise FileNotFoundError(f"File {file_path} not found.") from exc - - sample = file.read(sample_size) - - # In order to avoid errors in the csv sniffer, attempt to find the - # last newline character in the string - last_newline_index = sample.rfind("\n") - # if a newline character is found, trim the sample to the last newline - if last_newline_index != -1: - # Trim to the last complete line - sample = sample[:last_newline_index] - - sniffer = csv.Sniffer() - dialect = sniffer.sniff(sample) - delimiter = dialect.delimiter - - file.close() - - return delimiter - - async def read( - self, - callback: Callable[ - [pd.DataFrame, dict[str, Any] | None, Any], Any - ] = lambda metadata, data, cache, **kwargs: ( - {"metadata": metadata, "data": data} - ), - retrieve_files: bool = False, - **kwargs, - ) -> Any: - """ - Retrieve data from the endpoint according to the `retrieve_files` parameter. If - `retrieve_files` is False, the records will be returned as a dataframe. If - `retrieve_files` is True, the files associated with the records will be - retrieved either from the local cache or from the database. Note that a user can - select which effect_colname and pvalue_colname is used for a genomicfile (see - database documentation for more details). If one or both of those are present in - the params, and retrieve_file is true, then that column name is added to the - cache_key. Eg if record 1 is being retrieved from mcisaac data with - effect_colname "log2_raio", then the cache_key for that data will be - "1_log2_ratio". The default effect colname, which is set by the database, will - be stored with only the record id as the cache_key. - - :param callback: The function to call with the metadata. Signature must - include `metadata`, `data`, and `cache`. - :type callback: Callable[[pd.DataFrame, dict[str, Any] | None, Any], Any] - :param retrieve_files: Boolean. Whether to retrieve the files associated with - the records. Defaults to False. - :type retrieve_files: bool - :param kwargs: The following kwargs are used by the read() function. Any - others are passed onto the callback function - - timeout: The timeout for the GET request. Defaults to 120. - - :return: The result of the callback function. - :rtype: Any - - :raises ValueError: If the callback function does not have the correct - signature. - :raises aiohttp.ClientError: If there is an error in the GET request. - :raises pd.errors.ParserError: If there is an error reading the request - - """ - if not callable(callback) or {"metadata", "data", "cache"} - set( - callback.__code__.co_varnames - ): - raise ValueError( - "The callback must be a callable function with `metadata`, `data`, ", - "and `cache` as parameters.", - ) - - export_url = f"{self.url.rstrip('/')}/{self.export_url_suffix}" - self.logger.debug("read() export_url: %s", export_url) - - timeout = aiohttp.ClientTimeout(kwargs.pop("timeout", 120)) - async with aiohttp.ClientSession(timeout=timeout) as session: - try: - async with session.get( - export_url, headers=self.header, params=self.params - ) as response: - response.raise_for_status() - content = await response.content.read() - with gzip.GzipFile(fileobj=BytesIO(content)) as f: - records_df = pd.read_csv(f) - - if not retrieve_files: - return callback(records_df, None, self.cache, **kwargs) - else: - data_list = await self._retrieve_files(session, records_df) - return callback( - records_df, - data_list, - self.cache, - **kwargs, - ) - - except aiohttp.ClientError as e: - self.logger.error(f"Error in GET request: {e}") - raise - except pd.errors.ParserError as e: - self.logger.error(f"Error reading request content: {e}") - raise - - async def _retrieve_files( - self, session: aiohttp.ClientSession, records_df: pd.DataFrame - ) -> dict[str, pd.DataFrame]: - """ - Retrieve files associated with the records either from the local cache or from - the database. - - :param session: The aiohttp ClientSession. - :type session: aiohttp.ClientSession - :param records_df: The DataFrame containing the records. - :type records_df: pd.DataFrame - :return: A dictionary where the keys are record IDs and the values are - DataFrames of the associated files. - :rtype: dict[str, pd.DataFrame] - - """ - data_list = {} - for record_id in records_df["id"]: - data_list[str(record_id)] = await self._retrieve_file(session, record_id) - return data_list - - async def _retrieve_file( - self, session: aiohttp.ClientSession, record_id: int - ) -> pd.DataFrame: - """ - Retrieve a file associated with a record either from the local cache or from the - database. - - :param session: The aiohttp ClientSession. - :type session: aiohttp.ClientSession - :param record_id: The ID of the record. - :type record_id: int - :return: A DataFrame containing the file's data. - :rtype: pd.DataFrame - :raises FileNotFoundError: If the file is not found in the tar archive. - :raises ValueError: If the delimiter is not supported. - - """ - export_files_url = f"{self.url.rstrip('/')}/{self.export_files_url_suffix}" - self.logger.debug("_retrieve_file() export_url: %s", export_files_url) - - # set key for local cache - cache_key = str(record_id) - if "effect_colname" in self.params: - cache_key += f"_{self.params['effect_colname']}" - if "pvalue_colname" in self.params: - cache_key += f"_{self.params['pvalue_colname']}" - cached_data = self._cache_get(cache_key) - if cached_data is not None: - self.logger.info(f"cache_key {cache_key} retrieved from cache.") - return pd.read_json(BytesIO(cached_data.encode())) - else: - self.logger.debug(f"cache_key {cache_key} not found in cache.") - - try: - header = self.header.copy() - header["Content-Type"] = "application/gzip" - retrieve_files_params = self.params.copy() - retrieve_files_params.update({"id": record_id}) - async with session.get( - export_files_url, - headers=header, - params=retrieve_files_params, - timeout=120, - ) as response: - response.raise_for_status() - tar_data = await response.read() - - # Create a temporary file for the tarball - tar_file = tempfile.NamedTemporaryFile(delete=False, suffix=".tar.gz") - try: - tar_file.write(tar_data) - tar_file.flush() - tar_file.seek(0) - - # Create a temporary directory for extraction - with tempfile.TemporaryDirectory() as extract_dir: - # Open the tar file and log its contents - with tarfile.open(fileobj=tar_file, mode="r:gz") as tar: - tar_members = tar.getmembers() - self.logger.debug( - f"Tar file contains: " - f"{[member.name for member in tar_members]}", - ) - - # Find the specific file to extract - csv_filename = f"{record_id}.csv.gz" - member = next( - (m for m in tar_members if m.name == csv_filename), None - ) - if member is None: - raise FileNotFoundError( - f"{csv_filename} not found in tar archive" - ) - - # Extract only the specific member - tar.extract(member, path=extract_dir) - - # Read the extracted CSV file - csv_path = os.path.join(extract_dir, csv_filename) - self.logger.debug(f"Extracted file: {csv_path}") - - delimiter = self._detect_delimiter(csv_path) - - # raise an error if the delimiter is not a "," or a "\t" - if delimiter not in [",", "\t"]: - raise ValueError( - f"Delimiter {delimiter} is not supported. " - "Supported delimiters are ',' and '\\t'." - ) - - df = pd.read_csv(csv_path, delimiter=delimiter) - - # Store the data in the cache - self.logger.debug(f"Storing {cache_key} in cache.") - self._cache_set(cache_key, df.to_json()) - finally: - os.unlink(tar_file.name) - - return df - except Exception as e: - self.logger.error(f"Error retrieving file for cache_key {cache_key}: {e}") - raise diff --git a/tfbpapi/AbstractRecordsOnlyAPI.py b/tfbpapi/AbstractRecordsOnlyAPI.py deleted file mode 100644 index 1751ec7..0000000 --- a/tfbpapi/AbstractRecordsOnlyAPI.py +++ /dev/null @@ -1,82 +0,0 @@ -import gzip -import logging -from collections.abc import Callable -from io import BytesIO -from typing import Any - -import aiohttp -import pandas as pd - -from tfbpapi.AbstractAPI import AbstractAPI - - -class AbstractRecordsOnlyAPI(AbstractAPI): - """Abstract class for CRUD operations on records-only (no file storage) - endpoints.""" - - def __init__(self, **kwargs): - """ - Initialize the RecordsOnlyAPI object. - - :param kwargs: Additional parameters to pass to AbstractAPI. - - """ - self.logger = logging.getLogger(__name__) - super().__init__(**kwargs) - - async def read( - self, - callback: Callable[ - [pd.DataFrame, dict[str, Any] | None, Any], Any - ] = lambda metadata, data, cache, **kwargs: { - "metadata": metadata, - "data": data, - }, - export_url_suffix="export", - **kwargs, - ) -> Any: - """ - Retrieve data from the endpoint. The data will be returned as a dataframe. The - callback function must take metadata, data, and cache as parameters. - - :param callback: The function to call with the data. Signature must - include `metadata`, `data`, and `cache` as parameters. - :param export_url_suffix: The URL suffix for the export endpoint. This will - return a response object with a csv file. - :param kwargs: This can be used to pass "params" to the request to use in place - of `self.params`. If those are passed, they will be popped off and then - the remaining kwargs will be passed to the callback function - - """ - if not callable(callback) or {"metadata", "data", "cache"} - set( - callback.__code__.co_varnames - ): - raise ValueError( - "The callback must be a callable function with `metadata`,", - "`data`, and `cache` as parameters.", - ) - - export_url = f"{self.url.rstrip('/')}/{export_url_suffix}" - self.logger.debug("read() export_url: %s", export_url) - - async with aiohttp.ClientSession() as session: - try: - # note that the url and the export suffix are joined such that - # the url is stripped of any trailing slashes and the export suffix is - # added without a leading slash - async with session.get( - export_url, - headers=self.header, - params=kwargs.pop("params", self.params), - ) as response: - response.raise_for_status() - content = await response.content.read() - with gzip.GzipFile(fileobj=BytesIO(content)) as f: - records_df = pd.read_csv(f) - return callback(records_df, None, self.cache, **kwargs) - except aiohttp.ClientError as e: - self.logger.error(f"Error in GET request: {e}") - raise - except pd.errors.ParserError as e: - self.logger.error(f"Error reading request content: {e}") - raise diff --git a/tfbpapi/BindingAPI.py b/tfbpapi/BindingAPI.py deleted file mode 100644 index b766b37..0000000 --- a/tfbpapi/BindingAPI.py +++ /dev/null @@ -1,62 +0,0 @@ -import os -from typing import Any - -import pandas as pd - -from tfbpapi.AbstractRecordsAndFilesAPI import ( - AbstractRecordsAndFilesAPI, -) - - -class BindingAPI(AbstractRecordsAndFilesAPI): - """Class to interact with the BindingAPI endpoint.""" - - def __init__(self, **kwargs) -> None: - """ - Initialize the BindingAPI object. - - :param kwargs: parameters to pass through AbstractRecordsAndFilesAPI to - AbstractAPI. - - """ - valid_param_keys = kwargs.pop( - "valid_param_keys", - [ - "id", - "regulator", - "regulator_locus_tag", - "regulator_symbol", - "batch", - "replicate", - "source", - "source_name", - "source_orig_id", - "strain", - "condition", - "lab", - "assay", - "workflow", - "data_usable", - ], - ) - - url = kwargs.pop("url", os.getenv("BINDING_URL", None)) - - super().__init__(url=url, valid_keys=valid_param_keys, **kwargs) - - def create(self, data: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The BindingAPI does not support create.") - - def update(self, df: pd.DataFrame, **kwargs) -> Any: - raise NotImplementedError("The BindingAPI does not support update.") - - def delete(self, id: str, **kwargs) -> Any: - raise NotImplementedError("The BindingAPI does not support delete.") - - def submit(self, post_dict: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The BindingAPI does not support submit.") - - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Any: - raise NotImplementedError("The BindingAPI does not support retrieve.") diff --git a/tfbpapi/BindingConcatenatedAPI.py b/tfbpapi/BindingConcatenatedAPI.py deleted file mode 100644 index 1ad6aff..0000000 --- a/tfbpapi/BindingConcatenatedAPI.py +++ /dev/null @@ -1,62 +0,0 @@ -import os -from typing import Any - -import pandas as pd - -from tfbpapi.AbstractRecordsAndFilesAPI import ( - AbstractRecordsAndFilesAPI, -) - - -class BindingConcatenatedAPI(AbstractRecordsAndFilesAPI): - """Class to interact with the BindingConcatenatedAPI endpoint.""" - - def __init__(self, **kwargs) -> None: - """ - Initialize the BindingConcatenatedAPI object. - - :param kwargs: parameters to pass through AbstractRecordsAndFilesAPI to - AbstractAPI. - - """ - valid_param_keys = kwargs.pop( - "valid_param_keys", - [ - "id", - "regulator", - "regulator_locus_tag", - "regulator_symbol", - "batch", - "replicate", - "source", - "strain", - "condition", - "lab", - "assay", - "workflow", - "data_usable", - ], - ) - - url = kwargs.pop("url", os.getenv("BINDINGCONCATENATED_URL", None)) - - super().__init__(url=url, valid_keys=valid_param_keys, **kwargs) - - def create(self, data: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The BindingConcatenatedAPI does not support create.") - - def update(self, df: pd.DataFrame, **kwargs) -> Any: - raise NotImplementedError("The BindingConcatenatedAPI does not support update.") - - def delete(self, id: str, **kwargs) -> Any: - raise NotImplementedError("The BindingConcatenatedAPI does not support delete.") - - def submit(self, post_dict: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The BindingConcatenatedAPI does not support submit.") - - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Any: - raise NotImplementedError( - "The BindingConcatenatedAPI does not support retrieve." - ) diff --git a/tfbpapi/BindingManualQCAPI.py b/tfbpapi/BindingManualQCAPI.py deleted file mode 100644 index 02b6a21..0000000 --- a/tfbpapi/BindingManualQCAPI.py +++ /dev/null @@ -1,106 +0,0 @@ -import os -from typing import Any - -import pandas as pd -import requests - -from tfbpapi.AbstractRecordsOnlyAPI import AbstractRecordsOnlyAPI - - -class BindingManualQCAPI(AbstractRecordsOnlyAPI): - """A class to interact with the BindingManualQCAPI endpoint.""" - - def __init__(self, **kwargs): - """ - Initialize the BindingManualQCAPI object. - - :param kwargs: parameters to pass to AbstractAPI via AbstractRecordsOnlyAPI. - - """ - valid_param_keys = kwargs.pop( - "valid_param_keys", - [ - "id", - "binding", - "best_datatype", - "data_usable", - "passing_replicate", - "rank_recall", - "regulator", - "regulator_locus_tag", - "regulator_symbol", - "batch", - "source", - ], - ) - - url = kwargs.pop("url", os.getenv("BINDINGMANUALQC_URL", None)) - if not url: - raise AttributeError( - "url must be provided or the environmental variable ", - "`BINDINGMANUALQC_URL` must be set", - ) - - self.bulk_update_url_suffix = kwargs.pop( - "bulk_update_url_suffix", "bulk-update" - ) - - super().__init__(url=url, valid_param_keys=valid_param_keys, **kwargs) - - @property - def bulk_update_url_suffix(self) -> str: - """The URL suffix for updating multiple records in the same request.""" - return self._bulk_update_url_suffix - - @bulk_update_url_suffix.setter - def bulk_update_url_suffix(self, value: str) -> None: - self._bulk_update_url_suffix = value - - def update(self, df: pd.DataFrame, **kwargs: Any) -> requests.Response: - """ - Update the records in the database. - - :param df: The DataFrame containing the records to update. - :type df: pd.DataFrame - :param kwargs: Additional fields to include in the payload. - :type kwargs: Any - :return: The response from the POST request. - :rtype: requests.Response - :raises requests.RequestException: If the request fails. - - """ - bulk_update_url = ( - f"{self.url.rstrip('/')}/{self.bulk_update_url_suffix.rstrip('/')}/" - ) - - self.logger.debug("bulk_update_url: %s", bulk_update_url) - - # Include additional fields in the payload if provided - payload = {"data": df.to_dict(orient="records")} - payload.update(kwargs) - - try: - response = requests.post( - bulk_update_url, - headers=self.header, - json=payload, - ) - response.raise_for_status() - return response - except requests.RequestException as e: - self.logger.error(f"Error in POST request: {e}") - raise - - def create(self, data: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The BindingManualQCAPI does not support create.") - - def delete(self, id: str, **kwargs) -> Any: - raise NotImplementedError("The BindingManualQCAPI does not support delete.") - - def submit(self, post_dict: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The BindingManualQCAPI does not support submit.") - - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Any: - raise NotImplementedError("The BindingManualQCAPI does not support retrieve.") diff --git a/tfbpapi/Cache.py b/tfbpapi/Cache.py deleted file mode 100644 index 366604d..0000000 --- a/tfbpapi/Cache.py +++ /dev/null @@ -1,29 +0,0 @@ -import logging -from typing import Any - -from cachetools import TTLCache # type: ignore - - -class Cache: - """A caching class that uses cachetools for TTL caching with an LRU eviction - policy.""" - - def __init__(self, maxsize: int = 100, ttl: int = 300): - self.ttl_cache = TTLCache(maxsize=maxsize, ttl=ttl) - self.logger = logging.getLogger(__name__) - - def get(self, key: str, default: Any = None) -> Any: - """Get a value from the cache.""" - return self.ttl_cache.get(key, default) - - def set(self, key: str, value: Any) -> None: - """Set a value in the cache.""" - self.ttl_cache[key] = value - - def list(self) -> list[str]: - """List all keys in the cache.""" - return list(self.ttl_cache.keys()) - - def delete(self, key: str) -> None: - """Delete a key from the cache.""" - self.ttl_cache.pop(key, None) diff --git a/tfbpapi/CallingCardsBackgroundAPI.py b/tfbpapi/CallingCardsBackgroundAPI.py deleted file mode 100644 index f5b7668..0000000 --- a/tfbpapi/CallingCardsBackgroundAPI.py +++ /dev/null @@ -1,56 +0,0 @@ -import os -from typing import Any - -import pandas as pd - -from tfbpapi.AbstractRecordsAndFilesAPI import ( - AbstractRecordsAndFilesAPI, -) - - -class CallingCardsBackgroundAPI(AbstractRecordsAndFilesAPI): - """Class to interact with the CallingCardsBackgroundAPI endpoint.""" - - def __init__(self, **kwargs) -> None: - """ - Initialize the CallingCardsBackgroundAPI object. - - :param kwargs: parameters to pass through AbstractRecordsAndFilesAPI to - AbstractAPI. - - """ - valid_param_keys = kwargs.pop( - "valid_param_keys", - ["id", "name"], - ) - - url = kwargs.pop("url", os.getenv("CALLINGCARDSBACKGROUND_URL", None)) - - super().__init__(url=url, valid_keys=valid_param_keys, **kwargs) - - def create(self, data: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError( - "The CallingCardsBackgroundAPI does not support create." - ) - - def update(self, df: pd.DataFrame, **kwargs) -> Any: - raise NotImplementedError( - "The CallingCardsBackgroundAPI does not support update." - ) - - def delete(self, id: str, **kwargs) -> Any: - raise NotImplementedError( - "The CallingCardsBackgroundAPI does not support delete." - ) - - def submit(self, post_dict: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError( - "The CallingCardsBackgroundAPI does not support submit." - ) - - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Any: - raise NotImplementedError( - "The CallingCardsBackgroundAPI does not support retrieve." - ) diff --git a/tfbpapi/DataSourceAPI.py b/tfbpapi/DataSourceAPI.py deleted file mode 100644 index 0d00785..0000000 --- a/tfbpapi/DataSourceAPI.py +++ /dev/null @@ -1,48 +0,0 @@ -import os -from typing import Any - -import pandas as pd - -from tfbpapi.AbstractRecordsOnlyAPI import AbstractRecordsOnlyAPI - - -class DataSourceAPI(AbstractRecordsOnlyAPI): - """A class to interact with the DataSourceAPI endpoint.""" - - def __init__(self, **kwargs): - """ - Initialize the DataSourceAPI object. - - :param kwargs: parameters to pass to AbstractAPI via AbstractRecordsOnlyAPI. - - """ - valid_param_keys = kwargs.pop( - "valid_param_keys", - ["id", "fileformat_id", "fileformat", "lab", "assay", "workflow"], - ) - - url = kwargs.pop("url", os.getenv("DATASOURCE_URL", None)) - if not url: - raise AttributeError( - "url must be provided or the environmental variable ", - "`DATASOURCE_URL` must be set", - ) - - super().__init__(url=url, valid_keys=valid_param_keys, **kwargs) - - def create(self, data: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The DataSourceAPI does not support create.") - - def update(self, df: pd.DataFrame, **kwargs) -> Any: - raise NotImplementedError("The DataSourceAPI does not support update.") - - def delete(self, id: str, **kwargs) -> Any: - raise NotImplementedError("The DataSourceAPI does not support delete.") - - def submit(self, post_dict: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The DataSourceAPI does not support submit.") - - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Any: - raise NotImplementedError("The DataSourceAPI does not support retrieve.") diff --git a/tfbpapi/DtoAPI.py b/tfbpapi/DtoAPI.py deleted file mode 100644 index b8780b6..0000000 --- a/tfbpapi/DtoAPI.py +++ /dev/null @@ -1,295 +0,0 @@ -import asyncio -import json -import os -import time -from typing import Any - -import aiohttp -import pandas as pd -import requests - -from tfbpapi.AbstractRecordsOnlyAPI import AbstractRecordsOnlyAPI - - -class DtoAPI(AbstractRecordsOnlyAPI): - """ - A class to interact with the DTO API. - - Retrieves dto data from the database. - - """ - - def __init__(self, **kwargs) -> None: - """ - Initialize the DTO object. This will serve as an interface to the DTO endpoint - of both the database and the application cache. - - :param url: The URL of the DTO API - :param kwargs: Additional parameters to pass to AbstractAPI. - - """ - - self.bulk_update_url_suffix = kwargs.pop( - "bulk_update_url_suffix", "bulk-update" - ) - - super().__init__( - url=kwargs.pop("url", os.getenv("DTO_URL", "")), - **kwargs, - ) - - async def read(self, *args, **kwargs) -> Any: - """ - Override the read() method to use a custom callback that parses metadata. - - :param callback: The function to call with the metadata. Defaults to parsing - metadata. - :type callback: Callable[[pd.DataFrame, dict[str, Any] | None, Any], Any] - :return: The result of the callback function. - :rtype: Any - - """ - - # Define the default callback - def dto_callback(metadata, data, cache, **kwargs): - return {"metadata": self.parse_metadata(metadata), "data": data} - - # Explicitly set the callback argument to dto_callback - kwargs["callback"] = dto_callback - - # Call the superclass method with updated kwargs - return await super().read(*args, **kwargs) - - async def submit( - self, - post_dict: dict[str, Any], - **kwargs, - ) -> Any: - """ - Submit a DTO task to the DTO API. - - :param post_dict: The dictionary to submit to the DTO API. The typing needs to - be adjusted -- it can take a list of dictionaries to submit a batch. - :return: The group_task_id of the submitted task. - - """ - # make a post request with the post_dict to dto_url - dto_url = f"{self.url.rstrip('/')}/submit/" - self.logger.debug("dto_url: %s", dto_url) - - async with aiohttp.ClientSession() as session: - async with session.post( - dto_url, headers=self.header, json=post_dict - ) as response: - try: - response.raise_for_status() - except aiohttp.ClientResponseError as e: - self.logger.error( - "Failed to submit DTO task: Status %s, Reason %s", - e.status, - e.message, - ) - raise - result = await response.json() - try: - return result["group_task_id"] - except KeyError: - self.logger.error( - "Expected 'group_task_id' in response: %s", json.dumps(result) - ) - raise - - async def retrieve( - self, - group_task_id: str, - timeout: int = 300, - polling_interval: int = 2, - **kwargs, - ) -> dict[str, pd.DataFrame]: - """ - Periodically check the task status and retrieve the result when the task - completes. - - :param group_task_id: The task ID to retrieve results for. - :param timeout: The maximum time to wait for the task to complete (in seconds). - :param polling_interval: The time to wait between status checks (in seconds). - :return: Records from the DTO API of the successfully completed task. - - """ - # Start time for timeout check - start_time = time.time() - - # Task status URL - status_url = f"{self.url.rstrip('/')}/status/" - - while True: - async with aiohttp.ClientSession() as session: - # Send a GET request to check the task status - async with session.get( - status_url, - headers=self.header, - params={"group_task_id": group_task_id}, - ) as response: - response.raise_for_status() # Raise an error for bad status codes - status_response = await response.json() - - # Check if the task is complete - if status_response.get("status") == "SUCCESS": - - if error_tasks := status_response.get("error_tasks"): - self.logger.error( - f"Tasks {group_task_id} failed: {error_tasks}" - ) - if success_tasks := status_response.get("success_pks"): - params = {"id": ",".join(str(pk) for pk in success_tasks)} - return await self.read(params=params) - elif status_response.get("status") == "FAILURE": - raise Exception( - f"Task {group_task_id} failed: {status_response}" - ) - - # Check if we have reached the timeout - elapsed_time = time.time() - start_time - if elapsed_time > timeout: - raise TimeoutError( - f"Task {group_task_id} did not " - "complete within {timeout} seconds." - ) - - # Wait for the specified polling interval before checking again - await asyncio.sleep(polling_interval) - - def create(self, data: dict[str, Any], **kwargs) -> requests.Response: - raise NotImplementedError("The DTO does not support create.") - - def update(self, df: pd.DataFrame, **kwargs: Any) -> requests.Response: - """ - Update the records in the database. - - :param df: The DataFrame containing the records to update. - :type df: pd.DataFrame - :param kwargs: Additional fields to include in the payload. - :type kwargs: Any - :return: The response from the POST request. - :rtype: requests.Response - :raises requests.RequestException: If the request fails. - - """ - bulk_update_url = ( - f"{self.url.rstrip('/')}/{self.bulk_update_url_suffix.rstrip('/')}/" - ) - - self.logger.debug("bulk_update_url: %s", bulk_update_url) - - # Include additional fields in the payload if provided - payload = {"data": df.to_dict(orient="records")} - payload.update(kwargs) - - try: - response = requests.post( - bulk_update_url, - headers=self.header, - json=payload, - ) - response.raise_for_status() - return response - except requests.RequestException as e: - self.logger.error(f"Error in POST request: {e}") - raise - - def delete(self, id: str, **kwargs) -> Any: - """ - Delete a DTO record from the database. - - :param id: The ID of the DTO record to delete. - :return: A dictionary with a status message indicating success or failure. - - """ - # Include the Authorization header with the token - headers = kwargs.get("headers", {}) - headers["Authorization"] = f"Token {self.token}" - - # Make the DELETE request with the updated headers - response = requests.delete(f"{self.url}/{id}/", headers=headers, **kwargs) - - if response.status_code == 204: - return {"status": "success", "message": "DTO deleted successfully."} - - # Raise an error if the response indicates failure - response.raise_for_status() - - def parse_metadata(self, metadata: pd.DataFrame) -> pd.DataFrame: - """ - Parse the metadata from the DTO API. - - :param metadata: The metadata DataFrame to parse. - :return: The parsed metadata DataFrame. - :raises KeyError: If the metadata DataFrame is missing required columns. - - """ - if metadata.empty: - self.logger.warning("Metadata is empty") - return metadata - - output_columns = [ - "id", - "promotersetsig", - "expression", - "regulator_symbol", - "binding_source", - "expression_source", - "passing_fdr", - "passing_pvalue", - ] - - # required columns are "result" and output_columns - missing_req_columns = [ - col for col in ["result"] + output_columns if col not in metadata.columns - ] - if missing_req_columns: - raise KeyError( - "Metadata is missing required columns: " - "{', '.join(missing_req_columns)}" - ) - - dto_results_list = [] - - # Check and rename keys, logging a warning if a key is missing - keys_to_rename = { - "rank1": "binding_rank_threshold", - "rank2": "perturbation_rank_threshold", - "set1_len": "binding_set_size", - "set2_len": "perturbation_set_size", - } - - for _, row in metadata.iterrows(): - dto_results = json.loads(row.result.replace("'", '"')) - - for old_key, new_key in keys_to_rename.items(): - if old_key in dto_results: - dto_results[new_key] = dto_results.pop(old_key) - else: - self.logger.warning( - f"Key '{old_key}' missing in row with id '{row.id}'." - ) - - dto_results["id"] = row.id - dto_results["promotersetsig"] = row.promotersetsig - dto_results["expression"] = row.expression - dto_results["regulator_symbol"] = row.regulator_symbol - dto_results["binding_source"] = row.binding_source - dto_results["expression_source"] = row.expression_source - dto_results["passing_fdr"] = row.passing_fdr - dto_results["passing_pvalue"] = row.passing_pvalue - - dto_results_list.append(dto_results) - - # Create DataFrame - result_df = pd.DataFrame(dto_results_list) - - # Reorder columns: output_columns first, followed by others - reordered_columns = output_columns + [ - col for col in result_df.columns if col not in output_columns - ] - - return result_df.loc[:, reordered_columns] diff --git a/tfbpapi/ExpressionAPI.py b/tfbpapi/ExpressionAPI.py deleted file mode 100644 index c61e1f7..0000000 --- a/tfbpapi/ExpressionAPI.py +++ /dev/null @@ -1,66 +0,0 @@ -import os -from typing import Any - -import pandas as pd - -from tfbpapi.AbstractRecordsAndFilesAPI import ( - AbstractRecordsAndFilesAPI, -) - - -class ExpressionAPI(AbstractRecordsAndFilesAPI): - """Class to interact with the ExpressionAPI endpoint.""" - - def __init__(self, **kwargs) -> None: - """ - Initialize the ExpressionAPI object. - - :param kwargs: parameters to pass through AbstractRecordsAndFilesAPI to - AbstractAPI. - - """ - valid_param_keys = kwargs.pop( - "valid_param_keys", - [ - "id", - "regulator", - "regulator_locus_tag", - "regulator_symbol", - "batch", - "control", - "mechanism", - "restriction", - "time", - "strain", - "source", - "source_name", - "source_time", - "lab", - "assay", - "workflow", - "effect_colname", - "pvalue_colname", - "preferred_replicate", - ], - ) - - url = kwargs.pop("url", os.getenv("EXPRESSION_URL", None)) - - super().__init__(url=url, valid_keys=valid_param_keys, **kwargs) - - def create(self, data: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The ExpressionAPI does not support create.") - - def update(self, df: pd.DataFrame, **kwargs) -> Any: - raise NotImplementedError("The ExpressionAPI does not support update.") - - def delete(self, id: str, **kwargs) -> Any: - raise NotImplementedError("The ExpressionAPI does not support delete.") - - def submit(self, post_dict: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The ExpressionAPI does not support submit.") - - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Any: - raise NotImplementedError("The ExpressionAPI does not support retrieve.") diff --git a/tfbpapi/ExpressionManualQCAPI.py b/tfbpapi/ExpressionManualQCAPI.py deleted file mode 100644 index 15ff863..0000000 --- a/tfbpapi/ExpressionManualQCAPI.py +++ /dev/null @@ -1,103 +0,0 @@ -import os -from typing import Any - -import pandas as pd -import requests - -from tfbpapi.AbstractRecordsOnlyAPI import AbstractRecordsOnlyAPI - - -class ExpressionManualQCAPI(AbstractRecordsOnlyAPI): - """A class to interact with the ExpressionManualQCAPI endpoint.""" - - def __init__(self, **kwargs): - """ - Initialize the ExpressionManualQCAPI object. - - :param kwargs: parameters to pass to AbstractAPI via AbstractRecordsOnlyAPI. - - """ - valid_param_keys = kwargs.pop( - "valid_param_keys", - [ - "id", - "expression", - "strain_verified", - "regulator_locus_tag", - "regulator_symbol", - "batch", - "replicate", - "control", - "mechanism", - "restriction", - "time", - "source", - "lab", - "assay", - "workflow", - ], - ) - - url = kwargs.pop("url", os.getenv("EXPRESSIONMANUALQC_URL", None)) - if not url: - raise AttributeError( - "url must be provided or the environmental variable ", - "`EXPRESSIONMANUALQC_URL` must be set", - ) - - self.bulk_update_url_suffix = kwargs.pop( - "bulk_update_url_suffix", "bulk-update" - ) - - super().__init__(url=url, valid_keys=valid_param_keys, **kwargs) - - def create(self, data: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The ExpressionManualQCAPI does not support create.") - - def update(self, df: pd.DataFrame, **kwargs: Any) -> requests.Response: - """ - Update the records in the database. - - :param df: The DataFrame containing the records to update. - :type df: pd.DataFrame - :param kwargs: Additional fields to include in the payload. - :type kwargs: Any - :return: The response from the POST request. - :rtype: requests.Response - :raises requests.RequestException: If the request fails. - - """ - bulk_update_url = ( - f"{self.url.rstrip('/')}/{self.bulk_update_url_suffix.rstrip('/')}/" - ) - - self.logger.debug("bulk_update_url: %s", bulk_update_url) - - # Include additional fields in the payload if provided - payload = {"data": df.to_dict(orient="records")} - payload.update(kwargs) - - try: - response = requests.post( - bulk_update_url, - headers=self.header, - json=payload, - ) - response.raise_for_status() - return response - except requests.RequestException as e: - self.logger.error(f"Error in POST request: {e}") - raise - - def delete(self, id: str, **kwargs) -> Any: - raise NotImplementedError("The ExpressionManualQCAPI does not support delete.") - - def submit(self, post_dict: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The ExpressionManualQCAPI does not support submit.") - - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Any: - raise NotImplementedError( - "The ExpressionManualQCAPI does not support retrieve." - ) diff --git a/tfbpapi/FileFormatAPI.py b/tfbpapi/FileFormatAPI.py deleted file mode 100644 index bccdcc1..0000000 --- a/tfbpapi/FileFormatAPI.py +++ /dev/null @@ -1,57 +0,0 @@ -import os -from typing import Any - -import pandas as pd - -from tfbpapi.AbstractRecordsOnlyAPI import AbstractRecordsOnlyAPI - - -class FileFormatAPI(AbstractRecordsOnlyAPI): - """A class to interact with the FileFormatAPI endpoint.""" - - def __init__(self, **kwargs): - """ - Initialize the FileFormatAPI object. - - :param kwargs: parameters to pass to AbstractAPI via AbstractRecordsOnlyAPI. - - """ - valid_param_keys = kwargs.pop( - "valid_param_keys", - [ - "fileformat", - "fields", - "separator", - "feature_identifier_col", - "effect_col", - "default_effect_threshold", - "pval_col", - "default_pvalue_threshold", - ], - ) - - url = kwargs.pop("url", os.getenv("FILEFORMAT_URL", None)) - if not url: - raise AttributeError( - "url must be provided or the environmental variable ", - "`FILEFORMAT_URL` must be set", - ) - - super().__init__(url=url, valid_keys=valid_param_keys, **kwargs) - - def create(self, data: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The FileFormatAPI does not support create.") - - def update(self, df: pd.DataFrame, **kwargs) -> Any: - raise NotImplementedError("The FileFormatAPI does not support update.") - - def delete(self, id: str, **kwargs) -> Any: - raise NotImplementedError("The FileFormatAPI does not support delete.") - - def submit(self, post_dict: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The FileFormatAPI does not support submit.") - - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Any: - raise NotImplementedError("The FileFormatAPI does not support retrieve.") diff --git a/tfbpapi/GenomicFeatureAPI.py b/tfbpapi/GenomicFeatureAPI.py deleted file mode 100644 index 499cb6c..0000000 --- a/tfbpapi/GenomicFeatureAPI.py +++ /dev/null @@ -1,60 +0,0 @@ -import os -from typing import Any - -import pandas as pd - -from tfbpapi.AbstractRecordsOnlyAPI import AbstractRecordsOnlyAPI - - -class GenomicFeatureAPI(AbstractRecordsOnlyAPI): - """A class to interact with the GenomicFeatureAPI endpoint.""" - - def __init__(self, **kwargs): - """ - Initialize the GenomicFeatureAPI object. - - :param kwargs: parameters to pass to AbstractAPI via AbstractRecordsOnlyAPI. - - """ - valid_param_keys = kwargs.pop( - "valid_param_keys", - [ - "id", - "chr", - "start", - "end", - "strand", - "type", - "locus_tag", - "symbol", - "source", - "alias", - "note", - ], - ) - - url = kwargs.pop("url", os.getenv("GENOMICFEATURE_URL", None)) - if not url: - raise AttributeError( - "url must be provided or the environmental variable ", - "`GENOMICFEATURE_URL` must be set", - ) - - super().__init__(url=url, valid_keys=valid_param_keys, **kwargs) - - def create(self, data: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The GenomicFeatureAPI does not support create.") - - def update(self, df: pd.DataFrame, **kwargs) -> Any: - raise NotImplementedError("The GenomicFeatureAPI does not support update.") - - def delete(self, id: str, **kwargs) -> Any: - raise NotImplementedError("The GenomicFeatureAPI does not support delete.") - - def submit(self, post_dict: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The GenomicFeatureAPI does not support submit.") - - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Any: - raise NotImplementedError("The GenomicFeatureAPI does not support retrieve.") diff --git a/tfbpapi/HfQueryAPI.py b/tfbpapi/HfQueryAPI.py index 5907071..98c380a 100644 --- a/tfbpapi/HfQueryAPI.py +++ b/tfbpapi/HfQueryAPI.py @@ -1,23 +1,21 @@ +import logging +import os +import re from pathlib import Path from typing import Any, Literal import duckdb import pandas as pd from datasets import Dataset, DatasetDict, load_dataset -from huggingface_hub import DatasetCard +from huggingface_hub import hf_hub_download, snapshot_download +from huggingface_hub.constants import HF_HUB_CACHE -from .AbstractHfAPI import AbstractHfAPI +from .errors import RepoTooLargeError +from .HfCacheManager import HFCacheManager -class HfQueryAPI(AbstractHfAPI): - """ - Concrete implementation of AbstractHfAPI with DuckDB query capabilities. - - This class provides seamless querying of Hugging Face datasets using SQL via DuckDB. - It automatically handles dataset downloading, parsing, and provides a simple query - interface. - - """ +class HfQueryAPI: + """Hugging Face API client with intelligent downloading and SQL querying.""" def __init__( self, @@ -27,147 +25,704 @@ def __init__( cache_dir: str | Path | None = None, auto_download_threshold_mb: float = 100.0, auto_parse_datacard: bool = True, + enable_cache_management: bool = True, + cache_auto_cleanup: bool = False, + cache_max_age_days: int = 30, + cache_max_size: str = "10GB", ): """ Initialize the HF Query API client. - :param repo_id: The repo identifier on HF (e.g., "user/dataset") - :param repo_type: One of {"model", "dataset", "space"}. Defaults to "dataset" - :param token: Optional HF token for private repos - :param cache_dir: HF cache directory for downloads - :param auto_download_threshold_mb: Auto-download threshold in MB - :param auto_parse_datacard: Whether to automatically parse datacard on init + :param repo_id: Repository identifier (e.g., "user/dataset") + :param repo_type: Type of repository ("dataset", "model", "space") + :param token: HuggingFace token for authentication + :param cache_dir: HF cache_dir for downloads + :param auto_download_threshold_mb: Threshold in MB for auto full download + :param auto_parse_datacard: Whether to auto-parse the datacard on init + :param enable_cache_management: Enable integrated cache management features + :param cache_auto_cleanup: Enable automatic cache cleanup + :param cache_max_age_days: Maximum age in days for cache entries + :param cache_max_size: Maximum total cache size (e.g., "10GB") """ - super().__init__(repo_id, repo_type, token, cache_dir) + self.logger = logging.getLogger(self.__class__.__name__) + + # Initialize data info manager with new architecture + self.data_info = HfDataInfoManager( + repo_id=repo_id, repo_type=repo_type, token=token + ) + + self.cache_dir = Path( + cache_dir if cache_dir else os.getenv("HF_CACHE_DIR", HF_HUB_CACHE) + ) self.auto_download_threshold_mb = auto_download_threshold_mb - self._datasets: dict[str, Any] = {} self._loaded_datasets: dict[str, Dataset | DatasetDict] = {} self._duckdb_conn = duckdb.connect(":memory:") self._table_filters: dict[str, str] = {} + self._partition_cache: dict[str, set[str]] = {} # Track downloaded partitions + + # Initialize cache management + self._cache_manager = None + if enable_cache_management: + self._cache_manager = HFCacheManager(logger=self.logger) + self._cache_auto_cleanup = cache_auto_cleanup + self._cache_max_age_days = cache_max_age_days + self._cache_max_size = cache_max_size if auto_parse_datacard: try: - self.datasets = self.parse_datacard() + self.data_info.parse_datacard() except Exception as e: self.logger.warning(f"Failed to auto-parse datacard: {e}") - self._datasets = {} + self.data_info.clear() + + @property + def repo_id(self) -> str: + return self.data_info.repo_id + + @property + def repo_type(self) -> str: + return self.data_info.repo_type + + @property + def token(self) -> str | None: + return self.data_info.token @property def datasets(self) -> dict[str, Any]: """Parsed dataset configurations from the datacard.""" - return self._datasets + # Convert TableConfig objects to legacy dictionary format for backward compatibility + result = {} + for name, table_config in self.data_info.datasets.items(): + if table_config: + # Convert FeatureInfo objects to legacy format + features = {} + for feat_name, feat_info in table_config.features.items(): + features[feat_name] = { + "dtype": feat_info.dtype, + "description": feat_info.description, + } + + # Convert DataFileInfo objects to legacy format + data_files = [] + for file_info in table_config.data_files: + data_files.append( + {"path": file_info.path, "split": file_info.split} + ) + + result[name] = { + "features": features, + "data_files": data_files, + "config": table_config.config, + "loaded": table_config.downloaded, # Use "loaded" for backward compatibility + "is_partitioned": table_config.is_partitioned, + } + return result @datasets.setter def datasets(self, value: dict[str, Any]) -> None: - """Set the parsed datasets and update available tables.""" - self._datasets = value - self._update_available_tables() + """Set dataset configurations (for backward compatibility).""" + # Clear existing data and register new tables + self.data_info.clear() + # Convert legacy format to TableConfig objects if needed + from .datainfo.models import TableConfig + + table_configs = {} + for name, config in value.items(): + if isinstance(config, TableConfig): + table_configs[name] = config + else: + # Convert from legacy dict format + table_configs[name] = TableConfig( + name=name, + features=config.get("features", {}), + data_files=config.get("data_files", []), + config=config, + downloaded=config.get("loaded", False), # "loaded" was the old name + is_partitioned=config.get("is_partitioned", False), + partition_info=None, + ) + self.data_info._registry.register_tables(table_configs) @property def available_tables(self) -> list[str]: """List of available table names for querying.""" - return list(self._datasets.keys()) + return self.data_info.available_tables + + @property + def cache_manager(self) -> HFCacheManager | None: + """Access to the integrated cache manager for advanced cache operations.""" + return self._cache_manager + + @property + def cache_dir(self) -> Path: + return self._cache_dir + + @cache_dir.setter + def cache_dir(self, value: str | Path) -> None: + """Set the cache directory for huggingface_hub downloads.""" + path = Path(value) + if not path.exists(): + raise FileNotFoundError(f"Cache directory {path} does not exist") + self._cache_dir = path + + @property + def size(self) -> dict[str, Any] | None: + """Size information from the HF Dataset Server API.""" + size_info = self.data_info.get_dataset_size() + if size_info: + return { + "total": size_info.total_bytes, + "total_mb": size_info.total_mb, + "configs": size_info.config_sizes, + } + return None - def parse_datacard(self) -> dict[str, Any]: + @size.setter + def size(self, value: dict[str, Any]) -> None: + """Set size information (for backward compatibility).""" + # Convert legacy format to DatasetSize object + from .datainfo.models import DatasetSize + + dataset_size = DatasetSize.from_hf_size_response(value) + self.data_info._dataset_size = dataset_size + + @property + def snapshot_path(self) -> Path | None: + """Path to the last downloaded snapshot (if any).""" + return getattr(self, "_snapshot_path", None) + + @snapshot_path.setter + def snapshot_path(self, value: str | Path | None) -> None: + self._snapshot_path = None if value is None else Path(value) + + def _build_auth_headers(self) -> dict[str, str]: + """Build authentication headers if token is available.""" + return ( + {"Authorization": f"Bearer {self.data_info.token}"} + if self.data_info.token + else {} + ) + + def _normalize_patterns(self, kwargs: dict[str, Any]) -> None: + """Convert string patterns to lists.""" + for pattern_key in ["allow_patterns", "ignore_patterns"]: + if pattern_key in kwargs and kwargs[pattern_key] is not None: + patterns = kwargs[pattern_key] + if isinstance(patterns, str): + kwargs[pattern_key] = [patterns] + + def download_partitions( + self, table_name: str, partition_values: set[str] | None = None + ) -> Path: """ - Parse the dataset card into a standardized format. + Download specific partitions using the path_template from partitioning metadata. - :return: Dict mapping config names to their metadata + :param table_name: Name of the dataset table + :param partition_values: Specific partition values to download (None for all) + :return: Path to downloaded data """ - try: - card = DatasetCard.load(self.repo_id, self.repo_type) - data_dict = card.data.to_dict() - except Exception as e: - self.logger.error(f"Failed to load dataset card: {e}") - return {} + table_config = self.data_info.get_table_or_raise(table_name) - parsed_datasets = {} + if not table_config.is_partitioned: + raise ValueError(f"Table {table_name} is not configured as partitioned") - for config in data_dict.get("configs", []): - config_name = config["config_name"] + partition_info = self.data_info.get_partition_info(table_name) + if not partition_info: + raise ValueError(f"Table {table_name} missing partition information") - # Extract features for filtering/querying - features = {} - if "dataset_info" in config and "features" in config["dataset_info"]: - for feature in config["dataset_info"]["features"]: - features[feature["name"]] = { - "dtype": feature["dtype"], - "description": feature.get("description", ""), - } + path_template = partition_info.get("path_template") + if not path_template: + raise ValueError( + f"Table {table_name} missing required path_template in partitioning config" + ) - # Extract file paths - data_files = [] - for file_info in config.get("data_files", []): - data_files.append( - { - "path": file_info["path"], - "split": file_info.get("split", "train"), - } - ) + partition_columns = partition_info.get("partition_by", []) + + if partition_values and partition_columns: + # Download specific partitions using path template + patterns = [] + for partition_value in partition_values: + # For single-column partitioning, substitute the first column + if len(partition_columns) == 1: + column = partition_columns[0] + pattern = path_template.replace(f"{{{column}}}", partition_value) + patterns.append(pattern) + else: + # For multi-column partitioning, we'd need more sophisticated logic + # For now, create a wildcard pattern for the specific value + # This is a simplified approach - real implementation would need + # to handle multi-dimensional partition filtering + pattern = path_template + for column in partition_columns: + if f"{{{column}}}" in pattern: + pattern = pattern.replace(f"{{{column}}}", partition_value) + break + patterns.append(pattern) + + self.logger.info( + f"Downloading partitions for {table_name}: {partition_values}" + ) + self.logger.debug(f"Using patterns: {patterns}") + return self.download(allow_patterns=patterns) + else: + # Download all partitions - use the original data files paths + patterns = table_config.get_file_paths() - parsed_datasets[config_name] = { - "features": features, - "data_files": data_files, - "config": config, - "loaded": False, - } + self.logger.info(f"Downloading all partitions for {table_name}") + return self.download(allow_patterns=patterns) - return parsed_datasets + def download( + self, + files: list[str] | str | None = None, + force_full_download: bool = False, + auto_download_threshold_mb: float = 100.0, + dry_run: bool = False, + **kwargs, + ) -> Path: + """Download dataset with intelligent partitioning support.""" + dataset_size_mb = self.data_info.get_dataset_size_mb() + + if dataset_size_mb <= auto_download_threshold_mb or force_full_download: + self.logger.info( + f"Dataset size ({dataset_size_mb:.2f} MB) is below threshold. " + "Downloading entire repo." + ) + files = None + kwargs.pop("allow_patterns", None) + kwargs.pop("ignore_patterns", None) + elif ( + not files + and not kwargs.get("allow_patterns") + and not kwargs.get("ignore_patterns") + ): + excess_size_mb = dataset_size_mb - auto_download_threshold_mb + raise RepoTooLargeError( + f"Dataset size ({dataset_size_mb:.2f} MB) exceeds threshold by " + f"{excess_size_mb:.2f} MB. Specify files, patterns, or set " + "force_full_download=True." + ) + + # Handle specific file downloads + if files is not None: + if isinstance(files, str) or (isinstance(files, list) and len(files) == 1): + filename = files if isinstance(files, str) else files[0] + return self._download_single_file(filename, dry_run=dry_run, **kwargs) + elif isinstance(files, list) and len(files) > 1: + if kwargs.get("allow_patterns") is not None: + self.logger.warning( + "Both 'files' and 'allow_patterns' provided. Using 'files'." + ) + kwargs["allow_patterns"] = files + + return self._download_snapshot(dry_run=dry_run, **kwargs) + + def _download_single_file( + self, filename: str, dry_run: bool = False, **kwargs + ) -> Path: + """Download a single file using hf_hub_download.""" + self.logger.info(f"Downloading single file: {filename}") + + if dry_run: + self.logger.info(f"[DRY RUN] Would download {filename} from {self.repo_id}") + return Path("dry_run_path") + + hf_kwargs = { + "repo_id": self.repo_id, + "repo_type": self.repo_type, + "filename": filename, + "token": self.token, + **kwargs, + } + + if "local_dir" not in hf_kwargs and self.cache_dir is not None: + hf_kwargs["cache_dir"] = str(self.cache_dir) + + for key in ["local_dir", "cache_dir"]: + if key in hf_kwargs and hf_kwargs[key] is not None: + hf_kwargs[key] = str(hf_kwargs[key]) + file_path = hf_hub_download(**hf_kwargs) + self.snapshot_path = Path(file_path).parent + return Path(file_path) + + def _download_snapshot(self, dry_run: bool = False, **kwargs) -> Path: + """Download repository snapshot using snapshot_download.""" + if dry_run: + self.logger.info(f"[DRY RUN] Would download from {self.repo_id}:") + self.logger.info(f" - allow_patterns: {kwargs.get('allow_patterns')}") + self.logger.info(f" - ignore_patterns: {kwargs.get('ignore_patterns')}") + return Path("dry_run_path") + + self.logger.info( + f"Downloading repo snapshot - " + f"allow: {kwargs.get('allow_patterns')}, " + f"ignore: {kwargs.get('ignore_patterns')}" + ) + + snapshot_kwargs = { + "repo_id": self.repo_id, + "repo_type": self.repo_type, + "token": self.token, + **kwargs, + } + + if ( + "local_dir" not in snapshot_kwargs + and "cache_dir" not in snapshot_kwargs + and self.cache_dir is not None + ): + snapshot_kwargs["cache_dir"] = str(self.cache_dir) + + self._normalize_patterns(snapshot_kwargs) + snapshot_path = snapshot_download(**snapshot_kwargs) + self.snapshot_path = Path(snapshot_path) + return self.snapshot_path def _update_available_tables(self) -> None: """Update the logger with information about available tables.""" - if self._datasets: + if self.data_info.available_tables: self.logger.info(f"Available tables: {', '.join(self.available_tables)}") - def _ensure_dataset_loaded(self, table_name: str) -> Dataset | DatasetDict: + def _extract_table_references(self, sql: str) -> set[str]: """ - Ensure a dataset is loaded and available for querying. + Extract all table references from a SQL query. - :param table_name: Name of the dataset configuration - :return: The loaded dataset - :raises ValueError: If table_name is not found + Handles FROM clauses, JOINs, subqueries, and direct parquet file references. + + :param sql: SQL query string + :return: Set of table names/file references found in the query """ - if table_name not in self._datasets: - raise ValueError( - f"Table '{table_name}' not found. " - f"Available tables: {self.available_tables}" + table_refs = set() + + # Remove comments and normalize whitespace + sql_clean = re.sub(r"--.*?\n", " ", sql, flags=re.DOTALL) + sql_clean = re.sub(r"/\*.*?\*/", " ", sql_clean, flags=re.DOTALL) + sql_clean = re.sub(r"\s+", " ", sql_clean.strip()) + + # Pattern to match table references in FROM and JOIN clauses + # This handles: FROM table, FROM read_parquet('file.parquet'), JOIN table ON... + from_pattern = r""" + (?:FROM|JOIN)\s+ # FROM or JOIN keyword + (?: + read_parquet\s*\(\s*['"]([^'"]+)['"][\s)]* # read_parquet('file.parquet') + | + ([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*) # table.name or tablename ) + (?:\s+(?:AS\s+)?[a-zA-Z_][a-zA-Z0-9_]*)? # Optional alias + (?:\s+ON\s+.*?(?=\s+(?:FROM|JOIN|WHERE|GROUP|ORDER|LIMIT|$)))? # Optional ON clause for JOINs + """ + for match in re.finditer(from_pattern, sql_clean, re.IGNORECASE | re.VERBOSE): + parquet_file = match.group(1) + table_name = match.group(2) + + if parquet_file: + # Extract just the filename without extension for parquet files + file_ref = Path(parquet_file).stem + table_refs.add(file_ref) + elif table_name: + # Clean table name (remove schema prefix if present) + clean_name = table_name.split(".")[-1] + table_refs.add(clean_name) + + # Also check for simple table name patterns in case the regex missed something + simple_pattern = r"\b(?:FROM|JOIN)\s+([a-zA-Z_][a-zA-Z0-9_]*)\b" + for match in re.finditer(simple_pattern, sql_clean, re.IGNORECASE): + table_name = match.group(1).lower() + # Filter out SQL keywords and function names + if table_name not in { + "select", + "where", + "group", + "order", + "having", + "limit", + "offset", + "union", + "intersect", + "except", + "read_parquet", + "read_csv", + "read_json", + }: + table_refs.add(table_name) + + return table_refs + + def _resolve_table_to_files(self, table_ref: str) -> list[str]: + """ + Resolve a table reference to specific files that need to be downloaded. + + :param table_ref: Table name or file reference from SQL + :return: List of file paths/patterns needed for this table + + """ + return self.data_info.resolve_table_to_files(table_ref) + + def _ensure_tables_available(self, sql: str) -> set[str]: + """ + Ensure all tables referenced in SQL are available, downloading if necessary. + + :param sql: SQL query string + :return: Set of table names that were processed + + """ + table_refs = self._extract_table_references(sql) + processed_tables = set() + + for table_ref in table_refs: + if self.data_info.has_table(table_ref): + # Table is already known from dataset card + if not self.data_info.is_table_downloaded(table_ref): + self._ensure_dataset_loaded(table_ref, sql) + processed_tables.add(table_ref) + else: + # Try to discover as standalone file + if self._try_discover_standalone_table(table_ref): + processed_tables.add(table_ref) + else: + # File doesn't exist locally, try to download it + files_needed = self._resolve_table_to_files(table_ref) + if self._try_download_specific_files(files_needed, table_ref): + processed_tables.add(table_ref) + else: + self.logger.warning( + f"Could not locate or download table: {table_ref}" + ) + + return processed_tables + + def _try_download_specific_files(self, files: list[str], table_name: str) -> bool: + """ + Attempt to download specific files for a table. + + :param files: List of file paths/patterns to download + :param table_name: Name of the table these files represent + :return: True if download was successful + + """ + try: + # Try downloading specific files + for file_path in files: + try: + # First check if file exists in repo + downloaded_file_path = self._download_single_file( + file_path, dry_run=False + ) + if downloaded_file_path and downloaded_file_path.exists(): + # Register the file as a table if it's a parquet file + if file_path.endswith(".parquet"): + self._register_parquet_as_table( + downloaded_file_path, table_name + ) + return True + except Exception as e: + self.logger.debug(f"Failed to download {file_path}: {e}") + continue + + # If individual file downloads failed, try pattern-based download + if files: + try: + self.download(allow_patterns=files, force_full_download=False) + # After download, try to discover the table again + return self._try_discover_standalone_table(table_name) + except RepoTooLargeError: + self.logger.error( + f"Repository too large to download files for table: {table_name}" + ) + return False + except Exception as e: + self.logger.error( + f"Failed to download files for table {table_name}: {e}" + ) + return False + + return False + except Exception as e: + self.logger.error(f"Error downloading files for table {table_name}: {e}") + return False + + def _register_parquet_as_table(self, parquet_path: Path, table_name: str) -> None: + """Register a parquet file directly as a DuckDB table.""" + create_view_sql = f""" + CREATE OR REPLACE VIEW {table_name} AS + SELECT * FROM read_parquet('{parquet_path}') + """ + self._duckdb_conn.execute(create_view_sql) + + # Add to datasets registry via DataInfo + self.data_info.add_standalone_table(table_name, parquet_path, downloaded=True) + + def _try_discover_standalone_table(self, table_name: str) -> bool: + """ + Try to discover a standalone parquet file as a table. + + :param table_name: The name of the table to discover + :return: True if the table was discovered and registered + + """ + if not self.snapshot_path: + return False + + # Look for parquet file with matching name + parquet_file = self.snapshot_path / f"{table_name}.parquet" + if parquet_file.exists(): + # Register the standalone parquet file with DataInfo + self.data_info.add_standalone_table( + table_name, parquet_file, downloaded=True + ) + + # Register directly with DuckDB + create_view_sql = f""" + CREATE OR REPLACE VIEW {table_name} AS + SELECT * FROM read_parquet('{parquet_file}') + """ + self._duckdb_conn.execute(create_view_sql) + self.logger.debug( + f"Registered standalone parquet file as table: {table_name}" + ) + return True + + return False + + def _ensure_dataset_loaded( + self, table_name: str, sql: str | None = None + ) -> Dataset | DatasetDict | None: + """ + Ensure a dataset is loaded, with intelligent partition downloading. + + :param table_name: Name of the dataset configuration + :param sql: Optional SQL query to determine required partitions + :return: The loaded dataset (None for partitioned datasets that are loaded + directly into DuckDB) + + """ + if not self.data_info.has_table(table_name): + # Try to discover the table as a standalone parquet file + if self._try_discover_standalone_table(table_name): + self.logger.info(f"Discovered standalone table: {table_name}") + else: + raise ValueError( + f"Table '{table_name}' not found. " + f"Available tables: {self.available_tables}" + ) + + dataset_info = self.data_info.get_table_info(table_name) + + # Check if we need to download partitions + if dataset_info and dataset_info.is_partitioned and sql: + required_partitions = self._get_required_partitions(sql, table_name) + + if required_partitions: + # Check if we have these partitions cached + cached_partitions = self._partition_cache.get(table_name, set()) + missing_partitions = required_partitions - cached_partitions + + if missing_partitions: + self.logger.info( + f"Downloading missing partitions: {missing_partitions}" + ) + self._download_partitions(table_name, missing_partitions) + self._partition_cache.setdefault(table_name, set()).update( + missing_partitions + ) + + # Check if dataset is already loaded if table_name in self._loaded_datasets: return self._loaded_datasets[table_name] - # Download the dataset if not already downloaded + # Check if standalone table is already registered + if dataset_info and dataset_info.downloaded: + return None # Standalone tables are registered directly with DuckDB + + # Download if needed if not self.snapshot_path: - self.logger.info(f"Downloading dataset for table '{table_name}'...") - self.download(auto_download_threshold_mb=self.auto_download_threshold_mb) + if dataset_info and dataset_info.is_partitioned and sql: + required_partitions = self._get_required_partitions(sql, table_name) + self._download_partitions(table_name, required_partitions) + else: + self.logger.info(f"Downloading dataset for table '{table_name}'...") + self.download( + auto_download_threshold_mb=self.auto_download_threshold_mb + ) - # Load the specific dataset configuration + # Load the dataset try: self.logger.info(f"Loading dataset configuration '{table_name}'...") - dataset = load_dataset( - str(self.snapshot_path), name=table_name, keep_in_memory=False - ) - self._loaded_datasets[table_name] = dataset - self._datasets[table_name]["loaded"] = True - - # Register with DuckDB - self._register_dataset_with_duckdb(table_name, dataset) - return dataset + if dataset_info and dataset_info.is_partitioned: + # For partitioned datasets, load directly into DuckDB + self._load_partitioned_dataset(table_name) + # Mark as downloaded but don't store in _loaded_datasets since it's in DuckDB + self.data_info.mark_table_downloaded(table_name, True) + return None + else: + # Standard dataset loading + dataset = load_dataset( + str(self.snapshot_path), name=table_name, keep_in_memory=False + ) + self._loaded_datasets[table_name] = dataset + self._register_dataset_with_duckdb(table_name, dataset) + return dataset except Exception as e: self.logger.error(f"Failed to load dataset '{table_name}': {e}") raise + def _load_partitioned_dataset(self, table_name: str) -> None: + """ + Load a partitioned dataset directly into DuckDB without using Hugging Face + datasets. + + This is more efficient for partitioned datasets like genome_map. + + """ + # Find parquet files in the snapshot + if not self.snapshot_path: + raise ValueError("No snapshot path available") + + # Look for parquet files matching the dataset pattern + parquet_files: list[Path] = [] + for file_info in self.data_info.get_table_data_files(table_name): + pattern = file_info["path"] + if "*" in pattern: + # Convert pattern to actual file search + search_pattern = pattern.replace("*", "**") + parquet_files.extend(self.snapshot_path.glob(search_pattern)) + + if not parquet_files: + # Fallback: find all parquet files + parquet_files = list(self.snapshot_path.rglob("*.parquet")) + + if parquet_files: + # Register parquet files directly with DuckDB + file_paths = [str(f) for f in parquet_files] + self.logger.info( + f"Registering {len(file_paths)} parquet files for {table_name}" + ) + + # Create a view that reads from all parquet files + files_str = "', '".join(file_paths) + create_view_sql = f""" + CREATE OR REPLACE VIEW {table_name} AS + SELECT * FROM read_parquet(['{files_str}']) + """ + self._duckdb_conn.execute(create_view_sql) + self.logger.debug( + f"Created view '{table_name}' from {len(file_paths)} parquet files" + ) + else: + raise ValueError( + f"No parquet files found for partitioned dataset {table_name}" + ) + def _register_dataset_with_duckdb( self, table_name: str, dataset: Dataset | DatasetDict ) -> None: - """Register a dataset with DuckDB for SQL querying.""" + """Register a standard dataset with DuckDB for SQL querying.""" try: if isinstance(dataset, DatasetDict): - # Register each split as a separate view for split_name, split_dataset in dataset.items(): view_name = ( f"{table_name}_{split_name}" @@ -180,7 +735,6 @@ def _register_dataset_with_duckdb( f"Registered view '{view_name}' with {len(df)} rows" ) else: - # Single dataset df = dataset.to_pandas() self._duckdb_conn.register(table_name, df) self.logger.debug( @@ -194,32 +748,26 @@ def _register_dataset_with_duckdb( def query(self, sql: str, table_name: str | None = None) -> pd.DataFrame: """ - Execute a SQL query against the dataset. + Execute a SQL query with intelligent table discovery and loading. :param sql: SQL query string - :param table_name: Optional table name to ensure is loaded. If not provided, - attempts to infer from the SQL query + :param table_name: Optional table name to ensure is loaded (legacy parameter) :return: Query results as a pandas DataFrame """ - # If table_name not provided, try to infer from available tables - if table_name is None: - sql_lower = sql.lower() - for available_table in self.available_tables: - if available_table in sql_lower: - table_name = available_table - break + # Use intelligent table discovery to ensure all referenced tables are available + processed_tables = self._ensure_tables_available(sql) - # If we found a table name, ensure it's loaded - if table_name: - self._ensure_dataset_loaded(table_name) - elif not self._loaded_datasets: - # If no datasets are loaded and we couldn't infer, - # try to load the first one - if self.available_tables: - self._ensure_dataset_loaded(self.available_tables[0]) - - # Apply any table filters to the query + # Legacy support: if table_name is provided, ensure it's also available + if table_name and table_name not in processed_tables: + try: + self._ensure_dataset_loaded(table_name, sql) + except ValueError: + # Try to discover as standalone table + if not self._try_discover_standalone_table(table_name): + self.logger.warning(f"Could not load specified table: {table_name}") + + # Apply table filters modified_sql = self._apply_table_filters(sql) try: @@ -231,123 +779,1386 @@ def query(self, sql: str, table_name: str | None = None) -> pd.DataFrame: if modified_sql != sql: self.logger.debug(f"Original query: {sql}") self.logger.debug(f"Modified query: {modified_sql}") + + # If query failed, provide helpful information about available tables + if self.data_info.available_tables: + self.logger.info( + f"Available tables: {', '.join(self.data_info.available_tables)}" + ) + raise - def describe_table(self, table_name: str) -> pd.DataFrame: - """ - Get information about a table's structure. + def _apply_table_filters(self, sql: str) -> str: + """Apply table filters to a SQL query.""" + if not self._table_filters: + return sql - :param table_name: Name of the table to describe - :return: DataFrame with column information + modified_sql = sql + for table_name, filter_condition in self._table_filters.items(): + pattern = rf"\b{re.escape(table_name)}\b" + replacement = f"(SELECT * FROM {table_name} WHERE {filter_condition})" - """ + if re.search(pattern, modified_sql, re.IGNORECASE): + if not re.search( + rf"\(SELECT.*FROM\s+{re.escape(table_name)}\s+WHERE", + modified_sql, + re.IGNORECASE, + ): + modified_sql = re.sub( + pattern, replacement, modified_sql, flags=re.IGNORECASE + ) + + return modified_sql + + def get_table_filter(self, table_name: str) -> str | None: + """Get the current filter for a table.""" + return self._table_filters.get(table_name) + + def set_table_filter(self, table_name: str, filter_condition: str) -> None: + """Set a filter condition for a table.""" + self._table_filters[table_name] = filter_condition + self.logger.debug(f"Set filter for table '{table_name}': {filter_condition}") + + def remove_table_filter(self, table_name: str) -> None: + """Remove any filter condition for a table.""" + removed_filter = self._table_filters.pop(table_name, None) + if removed_filter: + self.logger.debug( + f"Removed filter for table '{table_name}': {removed_filter}" + ) + + # Standard methods + def describe_table(self, table_name: str) -> pd.DataFrame: + """Get information about a table's structure.""" self._ensure_dataset_loaded(table_name) return self.query(f"DESCRIBE {table_name}") def sample(self, table_name: str, n: int = 5) -> pd.DataFrame: + """Get a sample of rows from a table.""" + return self.query(f"SELECT * FROM {table_name} LIMIT {n}", table_name) + + def count(self, table_name: str) -> int: + """Get the number of rows in a table.""" + result = self.query(f"SELECT COUNT(*) as count FROM {table_name}", table_name) + return result.iloc[0]["count"] + + def get_columns(self, table_name: str) -> list[str]: + """Get column names for a table.""" + if not self.data_info.has_table(table_name): + raise ValueError(f"Table '{table_name}' not found") + return list(self.data_info.get_table_features(table_name).keys()) + + # ============== Cache Management Methods ============== + + def get_cache_info(self) -> dict[str, Any]: """ - Get a sample of rows from a table. + Get comprehensive cache information including current repo details. + + :return: Dictionary with cache stats, repo info, and recommendations + """ + if not self._cache_manager: + self.logger.debug("Cache management is disabled") + return {"cache_management": "disabled"} + + self.logger.info("Retrieving comprehensive cache information") - :param table_name: Name of the table to sample - :param n: Number of rows to return - :return: Sample DataFrame + from huggingface_hub import scan_cache_dir + try: + cache_info = scan_cache_dir() + self.logger.debug( + f"Scanned cache directory: {cache_info.size_on_disk_str} in {len(cache_info.repos)} repos" + ) + except Exception as e: + self.logger.error(f"Failed to scan cache directory: {e}") + return {"error": f"Failed to scan cache: {e}"} + + # Find current repo in cache + current_repo_info = None + for repo in cache_info.repos: + if ( + repo.repo_id == self.repo_id + and repo.repo_type.lower() == self.repo_type + ): + current_repo_info = repo + break + + result = { + "cache_management": "enabled", + "cache_directory": str(self.cache_dir), + "total_cache_size": cache_info.size_on_disk_str, + "total_cache_size_bytes": cache_info.size_on_disk, + "total_repos_cached": len(cache_info.repos), + "current_repo": { + "repo_id": self.repo_id, + "repo_type": self.repo_type, + "cached": current_repo_info is not None, + "cache_info": None, + }, + "cache_policies": { + "auto_cleanup": getattr(self, "_cache_auto_cleanup", False), + "max_age_days": getattr(self, "_cache_max_age_days", 30), + "max_size": getattr(self, "_cache_max_size", "10GB"), + }, + "recommendations": [], + } + + if current_repo_info: + result["current_repo"]["cache_info"] = { + "size_on_disk": current_repo_info.size_on_disk_str, + "size_bytes": current_repo_info.size_on_disk, + "nb_files": current_repo_info.nb_files, + "last_accessed": current_repo_info.last_accessed, + "last_modified": current_repo_info.last_modified, + "revisions_count": len(current_repo_info.revisions), + "revisions": [ + { + "commit_hash": rev.commit_hash[:8], + "size_on_disk": rev.size_on_disk_str, + "last_modified": rev.last_modified, + "files_count": len(rev.files), + } + for rev in sorted( + current_repo_info.revisions, + key=lambda r: r.last_modified, + reverse=True, + ) + ], + } + + # Add recommendations with logging + max_size_bytes = self._cache_manager._parse_size_string( + getattr(self, "_cache_max_size", "10GB") + ) + if cache_info.size_on_disk > max_size_bytes: + recommendation = ( + f"Cache size ({cache_info.size_on_disk_str}) exceeds configured limit " + f"({getattr(self, '_cache_max_size', '10GB')}). Consider running cache cleanup." + ) + result["recommendations"].append(recommendation) + self.logger.warning(f"Cache size warning: {recommendation}") + + if len(cache_info.repos) > 50: # Arbitrary threshold + recommendation = ( + f"Large number of cached repos ({len(cache_info.repos)}). " + "Consider cleaning unused repositories." + ) + result["recommendations"].append(recommendation) + self.logger.info(f"Cache optimization suggestion: {recommendation}") + + if current_repo_info: + self.logger.info( + f"Current repo {self.repo_id} found in cache: " + f"{current_repo_info.size_on_disk_str}, {len(current_repo_info.revisions)} revisions" + ) + else: + self.logger.debug(f"Current repo {self.repo_id} not found in cache") + + self.logger.info( + f"Cache info summary: {cache_info.size_on_disk_str} total, " + f"{len(cache_info.repos)} repos, {len(result['recommendations'])} recommendations" + ) + + return result + + def get_repo_cache_info(self, repo_id: str | None = None) -> dict[str, Any]: """ - return self.query(f"SELECT * FROM {table_name} LIMIT {n}", table_name) + Get detailed cache information for a specific repository. - def count(self, table_name: str) -> int: + :param repo_id: Repository ID (defaults to current repo) + :return: Dictionary with detailed repo cache information + """ + if not self._cache_manager: + self.logger.debug("Cache management is disabled") + return {"cache_management": "disabled"} + + target_repo_id = repo_id or self.repo_id + self.logger.info( + f"Retrieving detailed cache information for repo: {target_repo_id}" + ) + + from huggingface_hub import scan_cache_dir + + try: + cache_info = scan_cache_dir() + except Exception as e: + self.logger.error(f"Failed to scan cache directory: {e}") + return {"error": f"Failed to scan cache: {e}"} + + # Find the specified repo + target_repo = None + for repo in cache_info.repos: + if repo.repo_id == target_repo_id: + target_repo = repo + break + + if not target_repo: + self.logger.info(f"Repository {target_repo_id} not found in cache") + return { + "repo_id": target_repo_id, + "cached": False, + "message": "Repository not found in cache", + } + + return { + "repo_id": target_repo_id, + "repo_type": target_repo.repo_type, + "cached": True, + "size_on_disk": target_repo.size_on_disk_str, + "size_bytes": target_repo.size_on_disk, + "files_count": target_repo.nb_files, + "last_accessed": target_repo.last_accessed, + "last_modified": target_repo.last_modified, + "revisions_count": len(target_repo.revisions), + "revisions": [ + { + "commit_hash": rev.commit_hash, + "short_hash": rev.commit_hash[:8], + "size_on_disk": rev.size_on_disk_str, + "size_bytes": rev.size_on_disk, + "last_modified": rev.last_modified, + "files_count": len(rev.files), + "files": [ + { + "name": file.file_name, + "size": file.size_on_disk, + "blob_last_accessed": file.blob_last_accessed, + "blob_last_modified": file.blob_last_modified, + } + for file in sorted(rev.files, key=lambda f: f.file_name) + ], + } + for rev in sorted( + target_repo.revisions, key=lambda r: r.last_modified, reverse=True + ) + ], + } + + self.logger.info( + f"Found repo {target_repo_id} in cache: {target_repo.size_on_disk_str}, " + f"{target_repo.nb_files} files, {len(target_repo.revisions)} revisions" + ) + + return result + + def check_cached_files(self, table_name: str | None = None) -> dict[str, Any]: """ - Get the number of rows in a table. + Check which files for current dataset/table are cached locally. - :param table_name: Name of the table to count - :return: Number of rows + :param table_name: Specific table to check (defaults to all tables) + :return: Dictionary with file cache status + """ + if not self._cache_manager: + self.logger.debug("Cache management is disabled") + return {"cache_management": "disabled"} + + if table_name: + self.logger.info(f"Checking cache status for table: {table_name}") + else: + self.logger.info(f"Checking cache status for all tables in {self.repo_id}") + + from huggingface_hub import _CACHED_NO_EXIST, try_to_load_from_cache + + result = { + "repo_id": self.repo_id, + "cache_directory": str(self.cache_dir), + "tables": {}, + } + + # Check specific table or all tables + tables_to_check = [table_name] if table_name else self.available_tables + self.logger.debug(f"Checking {len(tables_to_check)} tables: {tables_to_check}") + + for table in tables_to_check: + if not self.data_info.has_table(table): + self.logger.warning(f"Table {table} not found in dataset configuration") + result["tables"][table] = { + "exists": False, + "message": "Table not found in dataset configuration", + } + continue + + table_config = self.data_info.get_table_info(table) + if not table_config: + continue + + file_status = {} + for file_info in table_config.data_files: + file_path = file_info.path + + # Check if file is cached + cached_path = try_to_load_from_cache( + repo_id=self.repo_id, + filename=file_path, + repo_type=self.repo_type, + ) + if isinstance(cached_path, str): + # File is cached + file_status[file_path] = { + "cached": True, + "local_path": cached_path, + "split": file_info.split, + } + elif cached_path is _CACHED_NO_EXIST: + # Non-existence is cached (file doesn't exist on Hub) + file_status[file_path] = { + "cached": False, + "exists_on_hub": False, + "split": file_info.split, + } + else: + # File is not cached + file_status[file_path] = { + "cached": False, + "exists_on_hub": True, # Assumed + "split": file_info.split, + } + + cached_count = sum( + 1 for f in file_status.values() if f.get("cached", False) + ) + total_files = len(table_config.data_files) + + result["tables"][table] = { + "exists": True, + "files": file_status, + "total_files": total_files, + "cached_files": cached_count, + "is_partitioned": table_config.is_partitioned, + } + + self.logger.info( + f"Table {table}: {cached_count}/{total_files} files cached " + f"({cached_count/total_files*100:.1f}%)" + ) + + total_tables = len( + [t for t in result["tables"].values() if t.get("exists", False)] + ) + self.logger.info(f"Cache check complete: processed {total_tables} tables") + + return result + + def cleanup_cache( + self, + strategy: str = "auto", + max_age_days: int | None = None, + target_size: str | None = None, + keep_current_repo: bool = True, + dry_run: bool = True, + ) -> dict[str, Any]: """ - result = self.query(f"SELECT COUNT(*) as count FROM {table_name}", table_name) - return result.iloc[0]["count"] + Clean up the HuggingFace Hub cache using various strategies. + + :param strategy: Cleanup strategy - "auto", "age", "size", "unused" + :param max_age_days: Maximum age for cache entries (for "age" strategy) + :param target_size: Target cache size (for "size" strategy, e.g., "5GB") + :param keep_current_repo: Whether to preserve current repo from cleanup + :param dry_run: If True, show what would be deleted without executing + :return: Dictionary with cleanup results and summary + """ + if not self._cache_manager: + self.logger.warning("Cache management is disabled, cannot perform cleanup") + return { + "cache_management": "disabled", + "message": "Cache management is not enabled", + } - def get_columns(self, table_name: str) -> list[str]: + # Use instance defaults if not specified + max_age_days = max_age_days or getattr(self, "_cache_max_age_days", 30) + target_size = target_size or getattr(self, "_cache_max_size", "10GB") + + self.logger.info( + f"Starting cache cleanup: strategy={strategy}, max_age={max_age_days}d, " + f"target_size={target_size}, dry_run={dry_run}, keep_current_repo={keep_current_repo}" + ) + + result = { + "strategy": strategy, + "dry_run": dry_run, + "keep_current_repo": keep_current_repo, + "strategies_executed": [], + "total_freed_bytes": 0, + "total_freed_str": "0B", + } + + try: + if strategy == "auto": + # Multi-strategy automated cleanup + self.logger.info("Executing automated multi-strategy cleanup") + strategies = self._cache_manager.auto_clean_cache( + max_age_days=max_age_days, + max_total_size=target_size, + keep_latest_per_repo=2, + dry_run=dry_run, + ) + total_freed = sum(s.expected_freed_size for s in strategies) + result["strategies_executed"] = [ + { + "type": "auto_cleanup", + "freed_bytes": total_freed, + "freed_str": self._cache_manager._format_bytes(total_freed), + "details": f"Executed {len(strategies)} cleanup strategies", + } + ] + result["total_freed_bytes"] = total_freed + self.logger.info( + f"Auto cleanup {'would free' if dry_run else 'freed'} " + f"{self._cache_manager._format_bytes(total_freed)} using {len(strategies)} strategies" + ) + + elif strategy == "age": + # Age-based cleanup + self.logger.info( + f"Executing age-based cleanup (older than {max_age_days} days)" + ) + delete_strategy = self._cache_manager.clean_cache_by_age( + max_age_days=max_age_days, dry_run=dry_run + ) + result["strategies_executed"] = [ + { + "type": "age_based", + "freed_bytes": delete_strategy.expected_freed_size, + "freed_str": delete_strategy.expected_freed_size_str, + "max_age_days": max_age_days, + } + ] + result["total_freed_bytes"] = delete_strategy.expected_freed_size + self.logger.info( + f"Age-based cleanup {'would free' if dry_run else 'freed'} " + f"{delete_strategy.expected_freed_size_str}" + ) + + elif strategy == "size": + # Size-based cleanup + self.logger.info( + f"Executing size-based cleanup (target: {target_size})" + ) + delete_strategy = self._cache_manager.clean_cache_by_size( + target_size=target_size, strategy="oldest_first", dry_run=dry_run + ) + result["strategies_executed"] = [ + { + "type": "size_based", + "freed_bytes": delete_strategy.expected_freed_size, + "freed_str": delete_strategy.expected_freed_size_str, + "target_size": target_size, + } + ] + result["total_freed_bytes"] = delete_strategy.expected_freed_size + self.logger.info( + f"Size-based cleanup {'would free' if dry_run else 'freed'} " + f"{delete_strategy.expected_freed_size_str} to reach target {target_size}" + ) + + elif strategy == "unused": + # Clean unused revisions + self.logger.info( + "Executing unused revisions cleanup (keeping 2 latest per repo)" + ) + delete_strategy = self._cache_manager.clean_unused_revisions( + keep_latest=2, dry_run=dry_run + ) + result["strategies_executed"] = [ + { + "type": "unused_revisions", + "freed_bytes": delete_strategy.expected_freed_size, + "freed_str": delete_strategy.expected_freed_size_str, + "keep_latest": 2, + } + ] + result["total_freed_bytes"] = delete_strategy.expected_freed_size + self.logger.info( + f"Unused revisions cleanup {'would free' if dry_run else 'freed'} " + f"{delete_strategy.expected_freed_size_str}" + ) + + else: + self.logger.error(f"Unknown cleanup strategy: {strategy}") + return { + "error": f"Unknown cleanup strategy: {strategy}", + "available_strategies": ["auto", "age", "size", "unused"], + } + + result["total_freed_str"] = self._cache_manager._format_bytes( + result["total_freed_bytes"] + ) + + # Add current repo protection info + if keep_current_repo: + result["current_repo_protected"] = { + "repo_id": self.repo_id, + "message": "Current repository was protected from cleanup", + } + self.logger.debug( + f"Current repository {self.repo_id} protected from cleanup" + ) + + # Final summary logging + self.logger.info( + f"Cache cleanup completed: {strategy} strategy, " + f"{'would free' if dry_run else 'freed'} {result['total_freed_str']}" + ) + + return result + + except Exception as e: + self.logger.error(f"Cache cleanup failed: {e}") + return { + "error": f"Cache cleanup failed: {str(e)}", + "strategy": strategy, + "dry_run": dry_run, + } + + def auto_cleanup_cache_if_needed(self) -> dict[str, Any]: """ - Get column names for a table. + Automatically clean cache if configured policies are exceeded. - :param table_name: Name of the table - :return: List of column names + This method is called automatically during operations if auto_cleanup is enabled. + :return: Dictionary with cleanup results or None if no cleanup was needed """ - if table_name not in self._datasets: - raise ValueError(f"Table '{table_name}' not found") + if not self._cache_manager or not getattr(self, "_cache_auto_cleanup", False): + self.logger.debug("Auto-cleanup is disabled") + return {"auto_cleanup": "disabled"} - return list(self._datasets[table_name]["features"].keys()) + self.logger.debug("Checking if auto-cleanup is needed") - def _apply_table_filters(self, sql: str) -> str: + from huggingface_hub import scan_cache_dir + + try: + cache_info = scan_cache_dir() + except Exception as e: + self.logger.error(f"Failed to scan cache for auto-cleanup: {e}") + return {"auto_cleanup": "error", "error": str(e)} + max_size_bytes = self._cache_manager._parse_size_string( + getattr(self, "_cache_max_size", "10GB") + ) + + cleanup_needed = cache_info.size_on_disk > max_size_bytes + + if not cleanup_needed: + self.logger.debug( + f"Auto-cleanup not needed: {cache_info.size_on_disk_str} " + f"< {getattr(self, '_cache_max_size', '10GB')}" + ) + return { + "auto_cleanup": "enabled", + "cleanup_needed": False, + "current_size": cache_info.size_on_disk_str, + "max_size": getattr(self, "_cache_max_size", "10GB"), + } + + self.logger.info( + f"Auto-cleanup triggered: cache size ({cache_info.size_on_disk_str}) " + f"exceeds limit ({getattr(self, '_cache_max_size', '10GB')})" + ) + + cleanup_result = self.cleanup_cache( + strategy="auto", + dry_run=False, # Execute cleanup + keep_current_repo=True, + ) + + cleanup_result.update( + { + "auto_cleanup": "enabled", + "triggered": True, + "reason": "cache_size_exceeded", + "previous_size": cache_info.size_on_disk_str, + } + ) + + return cleanup_result + + def suggest_cache_cleanup(self) -> dict[str, Any]: + """ + Analyze cache and provide cleanup recommendations without executing. + + :return: Dictionary with analysis and recommendations """ - Apply table filters to a SQL query by modifying table references. + if not self._cache_manager: + return {"cache_management": "disabled"} + + from datetime import datetime, timedelta + + from huggingface_hub import scan_cache_dir + + cache_info = scan_cache_dir() + suggestions = { + "cache_analysis": { + "total_size": cache_info.size_on_disk_str, + "total_repos": len(cache_info.repos), + "recommendations": [], + }, + "cleanup_strategies": {}, + } + + # Analyze size + max_size_bytes = self._cache_manager._parse_size_string( + getattr(self, "_cache_max_size", "10GB") + ) + if cache_info.size_on_disk > max_size_bytes: + suggestions["cache_analysis"]["recommendations"].append( + { + "type": "size_exceeded", + "message": f"Cache size ({cache_info.size_on_disk_str}) exceeds " + f"configured limit ({getattr(self, '_cache_max_size', '10GB')})", + "suggested_action": "Run cleanup_cache(strategy='size')", + } + ) + + # Analyze age + cutoff_date = datetime.now() - timedelta( + days=getattr(self, "_cache_max_age_days", 30) + ) + old_repos = [] + for repo in cache_info.repos: + for revision in repo.revisions: + if datetime.fromtimestamp(revision.last_modified) < cutoff_date: + old_repos.append((repo.repo_id, revision.commit_hash[:8])) + + if old_repos: + suggestions["cache_analysis"]["recommendations"].append( + { + "type": "old_revisions", + "message": f"Found {len(old_repos)} old revisions " + f"(older than {getattr(self, '_cache_max_age_days', 30)} days)", + "suggested_action": "Run cleanup_cache(strategy='age')", + } + ) - :param sql: Original SQL query - :return: Modified SQL query with filters applied + # Analyze unused revisions + repos_with_multiple_revisions = [ + repo for repo in cache_info.repos if len(repo.revisions) > 2 + ] + if repos_with_multiple_revisions: + suggestions["cache_analysis"]["recommendations"].append( + { + "type": "multiple_revisions", + "message": f"Found {len(repos_with_multiple_revisions)} repos " + "with multiple cached revisions", + "suggested_action": "Run cleanup_cache(strategy='unused')", + } + ) + + # Dry run different strategies to show potential savings + try: + age_cleanup = self._cache_manager.clean_cache_by_age( + max_age_days=getattr(self, "_cache_max_age_days", 30), dry_run=True + ) + suggestions["cleanup_strategies"]["age_based"] = { + "description": f"Remove revisions older than {getattr(self, '_cache_max_age_days', 30)} days", + "potential_savings": age_cleanup.expected_freed_size_str, + "potential_savings_bytes": age_cleanup.expected_freed_size, + } + + size_cleanup = self._cache_manager.clean_cache_by_size( + target_size=getattr(self, "_cache_max_size", "10GB"), + strategy="oldest_first", + dry_run=True, + ) + suggestions["cleanup_strategies"]["size_based"] = { + "description": f"Reduce cache to {getattr(self, '_cache_max_size', '10GB')}", + "potential_savings": size_cleanup.expected_freed_size_str, + "potential_savings_bytes": size_cleanup.expected_freed_size, + } + + unused_cleanup = self._cache_manager.clean_unused_revisions( + keep_latest=2, dry_run=True + ) + suggestions["cleanup_strategies"]["unused_revisions"] = { + "description": "Remove unused revisions (keep 2 latest per repo)", + "potential_savings": unused_cleanup.expected_freed_size_str, + "potential_savings_bytes": unused_cleanup.expected_freed_size, + } + except Exception as e: + suggestions["error"] = f"Failed to analyze cleanup strategies: {e}" + + return suggestions + + def warm_cache( + self, + repo_ids: list[str] | None = None, + tables: list[str] | None = None, + include_current_repo: bool = True, + dry_run: bool = False, + ) -> dict[str, Any]: """ - if not self._table_filters: - return sql + Pre-download (warm) cache with specified repositories or tables. - modified_sql = sql + :param repo_ids: List of repository IDs to pre-download + :param tables: List of table names from current repo to pre-download + :param include_current_repo: Whether to include current repo if repo_ids specified + :param dry_run: If True, show what would be downloaded without executing + :return: Dictionary with warming results + """ + if not self._cache_manager: + return {"cache_management": "disabled"} + + result = { + "cache_warming": "enabled", + "dry_run": dry_run, + "operations": [], + "total_downloaded": 0, + "errors": [], + } + + # Handle table-specific warming for current repo + if tables: + repo_result = { + "repo_id": self.repo_id, + "type": "table_specific", + "tables": {}, + "success": True, + } - # Apply filters by replacing table references with filtered subqueries - for table_name, filter_condition in self._table_filters.items(): - import re + for table_name in tables: + try: + if not self.data_info.has_table(table_name): + repo_result["tables"][table_name] = { + "status": "error", + "message": "Table not found in dataset configuration", + } + continue + + if not dry_run: + # Download files for this table + self._ensure_dataset_loaded(table_name) + repo_result["tables"][table_name] = { + "status": "downloaded", + "message": "Table files cached successfully", + } + result["total_downloaded"] += 1 + else: + repo_result["tables"][table_name] = { + "status": "would_download", + "message": "Would download table files", + } + + except Exception as e: + error_msg = f"Failed to warm cache for table {table_name}: {e}" + repo_result["tables"][table_name] = { + "status": "error", + "message": str(e), + } + result["errors"].append(error_msg) + repo_result["success"] = False + + result["operations"].append(repo_result) + + # Handle repository-specific warming + if repo_ids or (not tables and include_current_repo): + target_repos = repo_ids or [] + if include_current_repo and self.repo_id not in target_repos: + target_repos.append(self.repo_id) + + for repo_id in target_repos: + repo_result = { + "repo_id": repo_id, + "type": "full_repo", + "success": True, + "message": "", + } + + try: + if not dry_run: + if repo_id == self.repo_id: + # Use current API instance for current repo + downloaded_path = self.download() + repo_result["message"] = ( + f"Repository cached at {downloaded_path}" + ) + else: + # Create temporary API instance for other repos + temp_api = HfQueryAPI( + repo_id=repo_id, + repo_type=self.repo_type, + token=self.token, + cache_dir=self.cache_dir, + enable_cache_management=False, # Avoid recursive cache management + ) + downloaded_path = temp_api.download() + repo_result["message"] = ( + f"Repository cached at {downloaded_path}" + ) + + result["total_downloaded"] += 1 + else: + repo_result["message"] = f"Would download repository {repo_id}" + + except Exception as e: + error_msg = f"Failed to warm cache for repo {repo_id}: {e}" + repo_result["success"] = False + repo_result["message"] = str(e) + result["errors"].append(error_msg) + + result["operations"].append(repo_result) + + if not repo_ids and not tables and not include_current_repo: + result["message"] = "No repositories or tables specified for cache warming" + + return result + + def verify_cache_integrity(self) -> dict[str, Any]: + """ + Verify integrity of cached files and detect corruption. - # Simple pattern to match table references in FROM and JOIN clauses - pattern = rf"\b{re.escape(table_name)}\b" - replacement = f"(SELECT * FROM {table_name} WHERE {filter_condition})" + :return: Dictionary with verification results + """ + if not self._cache_manager: + return {"cache_management": "disabled"} + + import os + + from huggingface_hub import scan_cache_dir + + cache_info = scan_cache_dir() + result = { + "cache_verification": "enabled", + "total_repos_scanned": len(cache_info.repos), + "issues_found": [], + "healthy_repos": [], + "summary": { + "healthy": 0, + "corrupted": 0, + "missing_files": 0, + "symlink_issues": 0, + }, + } + + for repo in cache_info.repos: + repo_issues = [] + + # Check if snapshots directory exists + if not repo.repo_path.exists(): + repo_issues.append( + { + "type": "missing_repo_directory", + "message": f"Repository directory does not exist: {repo.repo_path}", + } + ) + result["summary"]["corrupted"] += 1 + continue + + # Check each revision + for revision in repo.revisions: + if not revision.snapshot_path.exists(): + repo_issues.append( + { + "type": "missing_snapshot", + "revision": revision.commit_hash[:8], + "message": f"Snapshot directory missing: {revision.snapshot_path}", + } + ) + continue + + # Check each file in the revision + for file_info in revision.files: + if not file_info.file_path.exists(): + repo_issues.append( + { + "type": "missing_file", + "revision": revision.commit_hash[:8], + "file": file_info.file_name, + "message": f"File missing: {file_info.file_path}", + } + ) + continue + + # Check if it's a symlink and target exists + if file_info.file_path.is_symlink(): + if not file_info.blob_path.exists(): + repo_issues.append( + { + "type": "broken_symlink", + "revision": revision.commit_hash[:8], + "file": file_info.file_name, + "message": f"Symlink target missing: {file_info.blob_path}", + } + ) + continue + + # Basic file size check + try: + actual_size = os.path.getsize(file_info.file_path) + if actual_size != file_info.size_on_disk: + repo_issues.append( + { + "type": "size_mismatch", + "revision": revision.commit_hash[:8], + "file": file_info.file_name, + "expected_size": file_info.size_on_disk, + "actual_size": actual_size, + "message": f"File size mismatch in {file_info.file_name}", + } + ) + except (OSError, IOError) as e: + repo_issues.append( + { + "type": "access_error", + "revision": revision.commit_hash[:8], + "file": file_info.file_name, + "message": f"Cannot access file: {e}", + } + ) + + # Categorize the repo + if repo_issues: + result["issues_found"].append( + { + "repo_id": repo.repo_id, + "repo_type": repo.repo_type, + "issues": repo_issues, + "issues_count": len(repo_issues), + } + ) - # Only replace if we find the table name in the SQL - if re.search(pattern, modified_sql, re.IGNORECASE): - # Check if it's already wrapped in a filtered subquery to - # avoid double-wrapping - if not re.search( - rf"\(SELECT.*FROM\s+{re.escape(table_name)}\s+WHERE", - modified_sql, - re.IGNORECASE, + # Update summary + if any( + issue["type"] in ["missing_repo_directory", "missing_snapshot"] + for issue in repo_issues ): - modified_sql = re.sub( - pattern, replacement, modified_sql, flags=re.IGNORECASE - ) + result["summary"]["corrupted"] += 1 + elif any(issue["type"] == "missing_file" for issue in repo_issues): + result["summary"]["missing_files"] += 1 + elif any(issue["type"] == "broken_symlink" for issue in repo_issues): + result["summary"]["symlink_issues"] += 1 + else: + result["summary"]["corrupted"] += 1 + else: + result["healthy_repos"].append( + { + "repo_id": repo.repo_id, + "repo_type": repo.repo_type, + "size": repo.size_on_disk_str, + } + ) + result["summary"]["healthy"] += 1 + + # Overall health assessment + total_repos = len(cache_info.repos) + if total_repos > 0: + health_percentage = (result["summary"]["healthy"] / total_repos) * 100 + result["overall_health"] = { + "percentage": round(health_percentage, 1), + "status": ( + "healthy" + if health_percentage > 95 + else "warning" if health_percentage > 80 else "critical" + ), + "recommendation": self._get_health_recommendation(health_percentage), + } - return modified_sql + return result - def get_table_filter(self, table_name: str) -> str | None: + def _get_health_recommendation(self, health_percentage: float) -> str: + """Get recommendation based on cache health percentage.""" + if health_percentage > 95: + return "Cache is in excellent condition" + elif health_percentage > 80: + return "Cache has minor issues. Consider running cleanup to remove problematic entries" + else: + return "Cache has significant issues. Run cleanup_cache() or consider clearing the cache entirely" + + def migrate_cache(self, new_cache_dir: str | Path) -> dict[str, Any]: + """ + Migrate cache to a new directory location. + + :param new_cache_dir: Target directory for cache migration + :return: Dictionary with migration results + """ + if not self._cache_manager: + return {"cache_management": "disabled"} + + import shutil + from pathlib import Path + + new_cache_path = Path(new_cache_dir) + current_cache_path = self.cache_dir + + result = { + "cache_migration": "enabled", + "source": str(current_cache_path), + "destination": str(new_cache_path), + "success": False, + "files_migrated": 0, + "errors": [], + } + + try: + # Validate target directory + if new_cache_path.exists() and list(new_cache_path.iterdir()): + return { + **result, + "error": f"Target directory {new_cache_path} is not empty", + "suggestion": "Choose an empty directory or clear the target directory", + } + + # Create target directory if needed + new_cache_path.mkdir(parents=True, exist_ok=True) + + # Get current cache info + from huggingface_hub import scan_cache_dir + + cache_info = scan_cache_dir() + + if not cache_info.repos: + result.update( + { + "success": True, + "message": "No cached repositories to migrate", + } + ) + # Update cache directory + self.cache_dir = new_cache_path + return result + + # Migrate each repository + migrated_repos = [] + for repo in cache_info.repos: + try: + # Create repo structure in new location + repo_name = repo.repo_path.name + new_repo_path = new_cache_path / repo_name + + # Copy entire repository directory + shutil.copytree(repo.repo_path, new_repo_path, symlinks=True) + migrated_repos.append(repo.repo_id) + result["files_migrated"] += repo.nb_files + + except Exception as e: + error_msg = f"Failed to migrate repo {repo.repo_id}: {e}" + result["errors"].append(error_msg) + + if migrated_repos and not result["errors"]: + # Migration successful, update cache directory + self.cache_dir = new_cache_path + result.update( + { + "success": True, + "migrated_repos": migrated_repos, + "repos_count": len(migrated_repos), + "message": f"Successfully migrated {len(migrated_repos)} repositories", + } + ) + elif migrated_repos: + result.update( + { + "success": True, # Partial success + "migrated_repos": migrated_repos, + "repos_count": len(migrated_repos), + "message": f"Partially migrated {len(migrated_repos)} repositories with {len(result['errors'])} errors", + } + ) + else: + result.update( + { + "success": False, + "message": "Migration failed completely", + } + ) + + except Exception as e: + result.update( + { + "success": False, + "error": f"Migration failed: {str(e)}", + } + ) + + return result + + # ============== Cache Configuration Management ============== + + def configure_cache_policies( + self, + auto_cleanup: bool | None = None, + max_age_days: int | None = None, + max_size: str | None = None, + save_to_env: bool = False, + ) -> dict[str, Any]: + """ + Configure cache management policies for this instance. + + :param auto_cleanup: Enable/disable automatic cache cleanup + :param max_age_days: Maximum age in days for cache entries + :param max_size: Maximum total cache size (e.g., "10GB") + :param save_to_env: Save configuration to environment variables + :return: Dictionary with updated configuration """ - Get the current filter for a table. + if not self._cache_manager: + return {"cache_management": "disabled"} + + # Update instance configuration + if auto_cleanup is not None: + self._cache_auto_cleanup = auto_cleanup + if max_age_days is not None: + self._cache_max_age_days = max_age_days + if max_size is not None: + self._cache_max_size = max_size + + config = { + "cache_management": "enabled", + "configuration_updated": True, + "policies": { + "auto_cleanup": getattr(self, "_cache_auto_cleanup", False), + "max_age_days": getattr(self, "_cache_max_age_days", 30), + "max_size": getattr(self, "_cache_max_size", "10GB"), + }, + "env_variables_updated": False, + } + + # Save to environment variables if requested + if save_to_env: + import os + + env_vars = {} + + if auto_cleanup is not None: + env_var = "TFBPAPI_CACHE_AUTO_CLEANUP" + os.environ[env_var] = str(auto_cleanup).lower() + env_vars[env_var] = str(auto_cleanup).lower() + + if max_age_days is not None: + env_var = "TFBPAPI_CACHE_MAX_AGE_DAYS" + os.environ[env_var] = str(max_age_days) + env_vars[env_var] = str(max_age_days) + + if max_size is not None: + env_var = "TFBPAPI_CACHE_MAX_SIZE" + os.environ[env_var] = max_size + env_vars[env_var] = max_size + + config.update( + { + "env_variables_updated": True, + "env_variables": env_vars, + "note": "Environment variables set for current session. " + "Add them to your shell profile for persistence.", + } + ) - :param table_name: Name of the table - :return: Current filter SQL condition or None if no filter is set + return config + def get_cache_configuration(self) -> dict[str, Any]: """ - return self._table_filters.get(table_name) + Get current cache configuration including environment variables. - def set_table_filter(self, table_name: str, filter_condition: str) -> None: + :return: Dictionary with comprehensive cache configuration """ - Set a filter condition for a table that will be applied to all queries. + import os + + config = { + "cache_management": "enabled" if self._cache_manager else "disabled", + "cache_directory": str(self.cache_dir), + "instance_config": {}, + "environment_config": {}, + "effective_config": {}, + } + + if not self._cache_manager: + return config + + # Instance configuration + config["instance_config"] = { + "auto_cleanup": getattr(self, "_cache_auto_cleanup", False), + "max_age_days": getattr(self, "_cache_max_age_days", 30), + "max_size": getattr(self, "_cache_max_size", "10GB"), + } + + # Environment configuration + env_config = {} + env_vars = { + "TFBPAPI_CACHE_AUTO_CLEANUP": "auto_cleanup", + "TFBPAPI_CACHE_MAX_AGE_DAYS": "max_age_days", + "TFBPAPI_CACHE_MAX_SIZE": "max_size", + "HF_CACHE_DIR": "cache_directory", + "HF_HUB_CACHE": "cache_directory_fallback", + } + + for env_var, config_key in env_vars.items(): + value = os.getenv(env_var) + if value: + env_config[config_key] = value + + config["environment_config"] = env_config + + # Effective configuration (environment overrides instance) + effective = config["instance_config"].copy() + + # Apply environment overrides + if "auto_cleanup" in env_config: + effective["auto_cleanup"] = env_config["auto_cleanup"].lower() in ( + "true", + "1", + "yes", + ) + if "max_age_days" in env_config: + try: + effective["max_age_days"] = int(env_config["max_age_days"]) + except ValueError: + pass + if "max_size" in env_config: + effective["max_size"] = env_config["max_size"] + + config["effective_config"] = effective - :param table_name: Name of the table - :param filter_condition: SQL WHERE condition (without the WHERE keyword) + return config + def reset_cache_configuration( + self, remove_env_vars: bool = False + ) -> dict[str, Any]: """ - self._table_filters[table_name] = filter_condition - self.logger.debug(f"Set filter for table '{table_name}': {filter_condition}") + Reset cache configuration to defaults. - def remove_table_filter(self, table_name: str) -> None: + :param remove_env_vars: Also remove related environment variables + :return: Dictionary with reset results """ - Remove any filter condition for a table. + if not self._cache_manager: + return {"cache_management": "disabled"} + + # Reset instance configuration to defaults + self._cache_auto_cleanup = False + self._cache_max_age_days = 30 + self._cache_max_size = "10GB" + + result = { + "cache_management": "enabled", + "configuration_reset": True, + "new_config": { + "auto_cleanup": False, + "max_age_days": 30, + "max_size": "10GB", + }, + "env_variables_removed": [], + } + + # Remove environment variables if requested + if remove_env_vars: + import os + + env_vars_to_remove = [ + "TFBPAPI_CACHE_AUTO_CLEANUP", + "TFBPAPI_CACHE_MAX_AGE_DAYS", + "TFBPAPI_CACHE_MAX_SIZE", + ] + + for env_var in env_vars_to_remove: + if env_var in os.environ: + del os.environ[env_var] + result["env_variables_removed"].append(env_var) + + if result["env_variables_removed"]: + result["note"] = ( + "Environment variables removed from current session. " + "Remove them from your shell profile for permanent effect." + ) - :param table_name: Name of the table + return result + def apply_cache_policy_from_env(self) -> dict[str, Any]: """ - removed_filter = self._table_filters.pop(table_name, None) - if removed_filter: - self.logger.debug( - f"Removed filter for table '{table_name}': {removed_filter}" - ) + Apply cache policies from environment variables. + + :return: Dictionary with applied configuration + """ + if not self._cache_manager: + return {"cache_management": "disabled"} + + import os + + applied_config = [] + + # Auto cleanup + auto_cleanup_env = os.getenv("TFBPAPI_CACHE_AUTO_CLEANUP") + if auto_cleanup_env: + self._cache_auto_cleanup = auto_cleanup_env.lower() in ("true", "1", "yes") + applied_config.append(f"auto_cleanup: {self._cache_auto_cleanup}") + + # Max age days + max_age_env = os.getenv("TFBPAPI_CACHE_MAX_AGE_DAYS") + if max_age_env: + try: + self._cache_max_age_days = int(max_age_env) + applied_config.append(f"max_age_days: {self._cache_max_age_days}") + except ValueError: + applied_config.append( + f"max_age_days: invalid value '{max_age_env}' (keeping default)" + ) + + # Max size + max_size_env = os.getenv("TFBPAPI_CACHE_MAX_SIZE") + if max_size_env: + self._cache_max_size = max_size_env + applied_config.append(f"max_size: {self._cache_max_size}") + + return { + "cache_management": "enabled", + "environment_config_applied": True, + "applied_settings": applied_config, + "current_config": { + "auto_cleanup": getattr(self, "_cache_auto_cleanup", False), + "max_age_days": getattr(self, "_cache_max_age_days", 30), + "max_size": getattr(self, "_cache_max_size", "10GB"), + }, + } + + def export_cache_configuration(self, format: str = "env") -> dict[str, Any]: + """ + Export current cache configuration in various formats. + + :param format: Export format - "env", "json", "yaml" + :return: Dictionary with exported configuration + """ + if not self._cache_manager: + return {"cache_management": "disabled"} + + current_config = { + "auto_cleanup": getattr(self, "_cache_auto_cleanup", False), + "max_age_days": getattr(self, "_cache_max_age_days", 30), + "max_size": getattr(self, "_cache_max_size", "10GB"), + "cache_directory": str(self.cache_dir), + } + + result = { + "cache_management": "enabled", + "format": format, + "configuration": current_config, + } + + if format == "env": + env_lines = [ + f"export TFBPAPI_CACHE_AUTO_CLEANUP={str(current_config['auto_cleanup']).lower()}", + f"export TFBPAPI_CACHE_MAX_AGE_DAYS={current_config['max_age_days']}", + f"export TFBPAPI_CACHE_MAX_SIZE={current_config['max_size']}", + f"export HF_CACHE_DIR={current_config['cache_directory']}", + ] + result["exported_config"] = "\n".join(env_lines) + result["usage"] = "Add these lines to your ~/.bashrc or ~/.zshrc file" + + elif format == "json": + import json + + result["exported_config"] = json.dumps(current_config, indent=2) + + elif format == "yaml": + yaml_lines = [ + "cache_configuration:", + f" auto_cleanup: {str(current_config['auto_cleanup']).lower()}", + f" max_age_days: {current_config['max_age_days']}", + f" max_size: \"{current_config['max_size']}\"", + f" cache_directory: \"{current_config['cache_directory']}\"", + ] + result["exported_config"] = "\n".join(yaml_lines) + + else: + result["error"] = f"Unsupported format: {format}" + result["supported_formats"] = ["env", "json", "yaml"] + + return result def close(self) -> None: """Close the DuckDB connection.""" diff --git a/tfbpapi/ParamsDict.py b/tfbpapi/ParamsDict.py deleted file mode 100644 index 19f7470..0000000 --- a/tfbpapi/ParamsDict.py +++ /dev/null @@ -1,156 +0,0 @@ -from typing import Any, Union - - -class ParamsDict(dict): - """ - A dictionary subclass that ensures all keys are strings and supports multiple key- - value assignments at once, with validation against a list of valid keys. - - This class is designed to be used for passing parameters to HTTP requests and - extends the base dictionary class, ensuring that insertion order is preserved. - - """ - - def __init__(self, params: dict[str, Any] = {}, valid_keys: list[str] = []) -> None: - """ - Initialize the ParamsDict with optional initial parameters and valid keys. - - :param params: A dictionary of initial parameters. All keys must be strings. - :type params: dict, optional - :param valid_keys: A list of valid keys for validation. - :type valid_keys: list of str, optional - :raises ValueError: If `params` is not a dictionary or if any of the keys - are not strings. - - """ - params = params or {} - valid_keys = valid_keys or [] - if not isinstance(params, dict): - raise ValueError("params must be a dictionary") - if len(params) > 0 and not all(isinstance(k, str) for k in params.keys()): - raise ValueError("params must be a dictionary with string keys") - super().__init__(params) - self._valid_keys = valid_keys - - def __setitem__(self, key: str | list[str], value: Any | list[Any]) -> None: - """ - Set a parameter value or multiple parameter values. - - :param key: The parameter key or a list of parameter keys. - :type key: str or list of str - :param value: The parameter value or a list of parameter values. - :type value: any or list of any - :raises ValueError: If the length of `key` and `value` lists do not match. - :raises KeyError: If `key` is not a string or a list of strings. - - """ - if isinstance(key, str): - self._validate_key(key) - super().__setitem__(key, value) - elif isinstance(key, list) and isinstance(value, list): - if len(key) != len(value): - raise ValueError("Length of keys and values must match") - for k, v in zip(key, value): - if not isinstance(k, str): - raise KeyError("All keys must be strings") - self._validate_key(k) - super().__setitem__(k, v) - else: - raise KeyError("Key must be a string or list of strings") - - def __getitem__(self, key: str | list[str]) -> Union[Any, "ParamsDict"]: - """ - Get a parameter value or a new ParamsDict with specified keys. - - :param key: The parameter key or a list of parameter keys. - :type key: str or list of str - :return: The parameter value or a new ParamsDict with the specified keys. - :rtype: any or ParamsDict - :raises KeyError: If `key` is not a string or a list of strings. - - """ - if isinstance(key, str): - return super().__getitem__(key) - elif isinstance(key, list): - return ParamsDict({k: dict.__getitem__(self, k) for k in key if k in self}) - else: - raise KeyError("Key must be a string or list of strings") - - def __delitem__(self, key: str) -> None: - """ - Delete a parameter by key. - - :param key: The parameter key. - :type key: str - :raises KeyError: If `key` is not a string. - - """ - if isinstance(key, str): - super().__delitem__(key) - else: - raise KeyError("Key must be a string") - - def __repr__(self) -> str: - """ - Return a string representation of the ParamsDict. - - :return: A string representation of the ParamsDict. - :rtype: str - - """ - return f"ParamsDict({super().__repr__()})" - - def __str__(self) -> str: - """ - Return a human-readable string representation of the ParamsDict. - - :return: A human-readable string representation of the ParamsDict. - :rtype: str - - """ - return ", ".join(f"{k}: {v}" for k, v in self.items()) - - def update(self, *args, **kwargs) -> None: - """Update the ParamsDict with the key/value pairs from other, overwriting - existing keys.""" - if args: - other = args[0] - if isinstance(other, dict): - [self._validate_key(k) for k in other.keys()] - for key, value in other.items(): - self.__setitem__(key, value) - else: - [self._validate_key(k) for k, _ in other] - for key, value in other: - self.__setitem__(key, value) - [self._validate_key(k) for k in kwargs.keys()] - for key, value in kwargs.items(): - self.__setitem__(key, value) - - def as_dict(self) -> dict: - """ - Convert the ParamsDict to a standard dictionary. - - :return: A standard dictionary with the same items as the ParamsDict. - :rtype: dict - - """ - return dict(self) - - def _validate_key(self, key: str) -> bool: - """Validate that the key is in the list of valid keys.""" - if self._valid_keys and key not in self._valid_keys: - raise KeyError(f"Invalid parameter key provided: {key}") - return True - - @property - def valid_keys(self) -> list[str]: - """Get the list of valid keys.""" - return self._valid_keys - - @valid_keys.setter - def valid_keys(self, keys: list[str]) -> None: - """Set the list of valid keys.""" - if not all(isinstance(k, str) for k in keys): - raise ValueError("valid_keys must be a list of strings") - self._valid_keys = keys diff --git a/tfbpapi/PromoterSetAPI.py b/tfbpapi/PromoterSetAPI.py deleted file mode 100644 index f747497..0000000 --- a/tfbpapi/PromoterSetAPI.py +++ /dev/null @@ -1,46 +0,0 @@ -import os -from typing import Any - -import pandas as pd - -from tfbpapi.AbstractRecordsAndFilesAPI import ( - AbstractRecordsAndFilesAPI, -) - - -class PromoterSetAPI(AbstractRecordsAndFilesAPI): - """Class to interact with the PromoterSetAPI endpoint.""" - - def __init__(self, **kwargs) -> None: - """ - Initialize the PromoterSetAPI object. - - :param kwargs: parameters to pass through AbstractRecordsAndFilesAPI to - AbstractAPI. - - """ - valid_param_keys = kwargs.pop( - "valid_param_keys", - ["id", "name"], - ) - - url = kwargs.pop("url", os.getenv("PROMOTERSET_URL", None)) - - super().__init__(url=url, valid_keys=valid_param_keys, **kwargs) - - def create(self, data: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The PromoterSetAPI does not support create.") - - def update(self, df: pd.DataFrame, **kwargs) -> Any: - raise NotImplementedError("The PromoterSetAPI does not support update.") - - def delete(self, id: str, **kwargs) -> Any: - raise NotImplementedError("The PromoterSetAPI does not support delete.") - - def submit(self, post_dict: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The PromoterSetAPI does not support submit.") - - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Any: - raise NotImplementedError("The PromoterSetAPI does not support retrieve.") diff --git a/tfbpapi/PromoterSetSigAPI.py b/tfbpapi/PromoterSetSigAPI.py deleted file mode 100644 index e75609e..0000000 --- a/tfbpapi/PromoterSetSigAPI.py +++ /dev/null @@ -1,68 +0,0 @@ -import os -from typing import Any - -import pandas as pd - -from tfbpapi.AbstractRecordsAndFilesAPI import ( - AbstractRecordsAndFilesAPI, -) - - -class PromoterSetSigAPI(AbstractRecordsAndFilesAPI): - """Class to interact with the PromoterSetSigAPI endpoint.""" - - def __init__(self, **kwargs) -> None: - """ - Initialize the PromoterSetSigAPI object. - - :param kwargs: parameters to pass through AbstractRecordsAndFilesAPI to - AbstractAPI. - - """ - valid_param_keys = kwargs.pop( - "valid_param_keys", - [ - "id", - "single_binding", - "composite_binding", - "promoter", - "promoter_name", - "background", - "background_name", - "regulator_locus_tag", - "regulator_symbol", - "batch", - "replicate", - "source", - "source_name", - "lab", - "assay", - "workflow", - "data_usable", - "aggregated", - "condition", - "deduplicate", - "preferred_replicate", - ], - ) - - url = kwargs.pop("url", os.getenv("PROMOTERSETSIG_URL", None)) - - super().__init__(url=url, valid_keys=valid_param_keys, **kwargs) - - def create(self, data: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The PromoterSetSigAPI does not support create.") - - def update(self, df: pd.DataFrame, **kwargs) -> Any: - raise NotImplementedError("The PromoterSetSigAPI does not support update.") - - def delete(self, id: str, **kwargs) -> Any: - raise NotImplementedError("The PromoterSetSigAPI does not support delete.") - - def submit(self, post_dict: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The PromoterSetSigAPI does not support submit.") - - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Any: - raise NotImplementedError("The PromoterSetSigAPI does not support retrieve.") diff --git a/tfbpapi/RankResponseAPI.py b/tfbpapi/RankResponseAPI.py deleted file mode 100644 index e12e0f2..0000000 --- a/tfbpapi/RankResponseAPI.py +++ /dev/null @@ -1,286 +0,0 @@ -import asyncio -import json -import os -import tarfile -import tempfile -import time -from typing import Any - -import aiohttp -import pandas as pd -from requests import Response, delete, post -from requests_toolbelt import MultipartEncoder - -from tfbpapi.AbstractRecordsAndFilesAPI import ( - AbstractRecordsAndFilesAPI, -) - - -class RankResponseAPI(AbstractRecordsAndFilesAPI): - """ - A class to interact with the Rank Response API. - - Retrieves rank response data from the database. - - """ - - def __init__(self, **kwargs) -> None: - """ - Initialize the RankResponseAPI object. This will serve as an interface to the - RankResponse endpoint of both the database and the application cache. - - :param url: The URL of the Rank Response API - :param kwargs: Additional parameters to pass to AbstractAPI. - - """ - super().__init__( - url=kwargs.pop("url", os.getenv("RANKRESPONSE_URL", "")), - **kwargs, - ) - - async def submit( - self, - post_dict: dict[str, Any], - **kwargs, - ) -> Any: - # make a post request with the post_dict to rankresponse_url - rankresponse_url = f"{self.url.rstrip('/')}/submit/" - self.logger.debug("rankresponse_url: %s", rankresponse_url) - - async with aiohttp.ClientSession() as session: - async with session.post( - rankresponse_url, headers=self.header, json=post_dict - ) as response: - response.raise_for_status() - result = await response.json() - try: - return result["group_task_id"] - except KeyError: - self.logger.error( - "Expected 'group_task_id' in response: %s", json.dumps(result) - ) - raise - - async def retrieve( - self, - group_task_id: str, - timeout: int = 300, - polling_interval: int = 2, - **kwargs, - ) -> dict[str, pd.DataFrame]: - """ - Periodically check the task status and retrieve the result when the task - completes. - - :param group_task_id: The task ID to retrieve results for. - :param timeout: The maximum time to wait for the task to complete (in seconds). - :param polling_interval: The time to wait between status checks (in seconds). - :return: Extracted files from the result tarball. - - """ - # Start time for timeout check - start_time = time.time() - - # Task status URL - status_url = f"{self.url.rstrip('/')}/status/" - - while True: - async with aiohttp.ClientSession() as session: - # Send a GET request to check the task status - async with session.get( - status_url, - headers=self.header, - params={"group_task_id": group_task_id}, - ) as response: - response.raise_for_status() # Raise an error for bad status codes - status_response = await response.json() - - # Check if the task is complete - if status_response.get("status") == "SUCCESS": - # Fetch and return the tarball - return await self._download_result(group_task_id) - elif status_response.get("status") == "FAILURE": - raise Exception( - f"Task {group_task_id} failed: {status_response}" - ) - - # Check if we have reached the timeout - elapsed_time = time.time() - start_time - if elapsed_time > timeout: - raise TimeoutError( - f"Task {group_task_id} did not " - "complete within {timeout} seconds." - ) - - # Wait for the specified polling interval before checking again - await asyncio.sleep(polling_interval) - - async def _download_result(self, group_task_id: str) -> Any: - """ - Download the result tarball after the task is successful. - - :param group_task_id: The group_task_id to download the results for. - :return: Extracted metadata and data from the tarball. - - """ - download_url = f"{self.url.rstrip('/')}/retrieve_task/" - - async with aiohttp.ClientSession() as session: - async with session.get( - download_url, - headers=self.header, - params={"group_task_id": group_task_id}, - ) as response: - response.raise_for_status() # Ensure request was successful - tar_data = await response.read() - - # Save tarball to a temporary file or return raw tar content - with tempfile.NamedTemporaryFile( - delete=False, suffix=".tar.gz" - ) as temp_file: - temp_file.write(tar_data) - temp_file.flush() - temp_file.seek(0) - - # Extract and return the content of the tarball - return self._extract_files(temp_file.name) - - def _extract_files(self, tar_path: str) -> dict[str, pd.DataFrame]: - """ - Extract metadata and associated files from a tarball. - - :param tar_path: The path to the tarball file. - :return: A tuple of metadata DataFrame and a dictionary of DataFrames for each - file. - - """ - with tarfile.open(tar_path, mode="r:gz") as tar: - tar_members = tar.getmembers() - - # Extract metadata.json - metadata_member = next( - (m for m in tar_members if m.name == "metadata.json"), None - ) - if metadata_member is None: - raise FileNotFoundError("metadata.json not found in tar archive") - - extracted_file = tar.extractfile(metadata_member) - if extracted_file is None: - raise FileNotFoundError("Failed to extract metadata.json") - - with extracted_file as f: - metadata_dict = json.load(f) - - metadata_df = pd.DataFrame(metadata_dict.values()) - metadata_df["id"] = metadata_dict.keys() - - # Extract CSV files - data = {} - for rr_id in metadata_df["id"]: - csv_filename = f"{rr_id}.csv.gz" - member = next((m for m in tar_members if m.name == csv_filename), None) - if member is None: - raise FileNotFoundError(f"{csv_filename} not found in tar archive") - - extracted_file = tar.extractfile(member) - if extracted_file is None: - raise FileNotFoundError(f"Failed to extract {csv_filename}") - - with extracted_file as f: - data[rr_id] = pd.read_csv(f, compression="gzip") - return {"metadata": metadata_df, "data": data} - - def create(self, data: dict[str, Any], **kwargs) -> Response: - """ - Create a new RankResponse record by uploading a gzipped CSV file. - - :param data: This should be the fields in the RankREsponse model, eg - "promotersetsig_id", "expression_id" and "parameters". - :param kwargs: Additional parameters to pass to the post. This must include a - DataFrame to upload as a CSV file with the keyword `df`, eg `df=my_df`. - - :return: The result of the post request. - - :raises ValueError: If a DataFrame is not provided in the keyword arguments. - :raises TypeError: If the DataFrame provided is not a pandas DataFrame. - - """ - # ensure that the url ends in a slash - rankresponse_url = f"{self.url.rstrip('/')}/" - df = kwargs.pop("df", None) - - if df is None: - raise ValueError( - "A DataFrame must be provided to create " - "a RankResponse via keyword `df`" - ) - if not isinstance(df, pd.DataFrame): - raise TypeError( - f"Expected a DataFrame for keyword `df`, got {type(df).__name__}" - ) - - # Create a temporary gzipped CSV file from the DataFrame - with tempfile.NamedTemporaryFile(suffix=".csv.gz") as temp_file: - df.to_csv(temp_file.name, compression="gzip", index=False) - - # Prepare the file and metadata for upload - with open(temp_file.name, "rb") as file: - multipart_data = MultipartEncoder( - fields={**data, "file": (temp_file.name, file, "application/gzip")} - ) - headers = {**self.header, "Content-Type": multipart_data.content_type} - - # Send the POST request with custom encoded multipart data - response = post(rankresponse_url, headers=headers, data=multipart_data) - - response.raise_for_status() - return response - - def update(self, df: pd.DataFrame, **kwargs) -> Any: - raise NotImplementedError("The RankResponseAPI does not support update.") - - def delete(self, id: str = "", **kwargs) -> Any: - """ - Delete one or more records from the database. - - :param id: The ID of the record to delete. However, you can also pass in - `ids` as a list of IDs to delete multiple records. This is why `id` is optional. - If neither `id` nor `ids` is provided, a ValueError is raised. - - :return: A dictionary with a status message indicating success or failure. - - :raises ValueError: If neither `id` nor `ids` is provided. - - """ - # Include the Authorization header with the token - headers = kwargs.get("headers", {}) - headers["Authorization"] = f"Token {self.token}" - - ids = kwargs.pop("ids", str(id)) - - # Determine if it's a single ID or multiple - if isinstance(ids, str) and str != "": - # Single ID deletion for backward compatibility - response = delete(f"{self.url}/{ids}/", headers=headers, **kwargs) - elif isinstance(ids, list) and ids: - # Bulk delete with a list of IDs - response = delete( - f"{self.url}/delete/", - headers=headers, - json={"ids": ids}, # Send the list of IDs in the request body - **kwargs, - ) - else: - raise ValueError( - "No ID(s) provided for deletion. Either pass a single ID with " - "`id` or a list of IDs with `ids = [1,2, ...]" - ) - - if response.status_code in [200, 204]: - return { - "status": "success", - "message": "RankResponse(s) deleted successfully.", - } - - # Raise an error if the response indicates failure - response.raise_for_status() diff --git a/tfbpapi/RegulatorAPI.py b/tfbpapi/RegulatorAPI.py deleted file mode 100644 index 675c002..0000000 --- a/tfbpapi/RegulatorAPI.py +++ /dev/null @@ -1,53 +0,0 @@ -import os -from typing import Any - -import pandas as pd - -from tfbpapi.AbstractRecordsOnlyAPI import AbstractRecordsOnlyAPI - - -class RegulatorAPI(AbstractRecordsOnlyAPI): - """A class to interact with the RegulatorAPI endpoint.""" - - def __init__(self, **kwargs): - """ - Initialize the RegulatorAPI object. - - :param kwargs: parameters to pass to AbstractAPI via AbstractRecordsOnlyAPI. - - """ - valid_param_keys = kwargs.pop( - "valid_param_keys", - [ - "id", - "regulator_locus_tag", - "regulator_symbol", - "under_development", - ], - ) - - url = kwargs.pop("url", os.getenv("REGULATOR_URL", None)) - if not url: - raise AttributeError( - "url must be provided or the environmental variable ", - "`REGULATOR_URL` must be set", - ) - - super().__init__(url=url, valid_keys=valid_param_keys, **kwargs) - - def create(self, data: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The RegulatorAPI does not support create.") - - def update(self, df: pd.DataFrame, **kwargs) -> Any: - raise NotImplementedError("The RegulatorAPI does not support update.") - - def delete(self, id: str, **kwargs) -> Any: - raise NotImplementedError("The RegulatorAPI does not support delete.") - - def submit(self, post_dict: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The RegulatorAPI does not support submit.") - - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Any: - raise NotImplementedError("The RegulatorAPI does not support retrieve.") diff --git a/tfbpapi/UnivariateModelsAPI.py b/tfbpapi/UnivariateModelsAPI.py deleted file mode 100644 index 57cc303..0000000 --- a/tfbpapi/UnivariateModelsAPI.py +++ /dev/null @@ -1,202 +0,0 @@ -import asyncio -import json -import os -import time -from typing import Any - -import aiohttp -import pandas as pd -import requests - -from tfbpapi.AbstractRecordsOnlyAPI import AbstractRecordsOnlyAPI - - -class UnivariateModelsAPI(AbstractRecordsOnlyAPI): - """ - A class to interact with the UnivariateModels API. - - Retrieves univariatemodels data from the database. - - """ - - def __init__(self, **kwargs) -> None: - """ - Initialize the UnivariateModels object. This will serve as an interface to the - UnivariateModels endpoint of both the database and the application cache. - - :param url: The URL of the UnivariateModels API - :param kwargs: Additional parameters to pass to AbstractAPI. - - """ - - self.bulk_update_url_suffix = kwargs.pop( - "bulk_update_url_suffix", "bulk-update" - ) - - super().__init__( - url=kwargs.pop("url", os.getenv("UNIVARIATEMODELS_URL", "")), - **kwargs, - ) - - async def submit( - self, - post_dict: dict[str, Any], - **kwargs, - ) -> Any: - """ - Submit a UnivariateModels task to the UnivariateModels API. - - :param post_dict: The dictionary to submit to the UnivariateModels API. The - typing needs to be adjusted -- it can take a list of dictionaries to submit - a batch. - :return: The group_task_id of the submitted task. - - """ - # make a post request with the post_dict to univariatemodels_url - univariatemodels_url = f"{self.url.rstrip('/')}/submit/" - self.logger.debug("univariatemodels_url: %s", univariatemodels_url) - - async with aiohttp.ClientSession() as session: - async with session.post( - univariatemodels_url, headers=self.header, json=post_dict - ) as response: - try: - response.raise_for_status() - except aiohttp.ClientResponseError as e: - self.logger.error( - "Failed to submit UnivariateModels task: Status %s, Reason %s", - e.status, - e.message, - ) - raise - result = await response.json() - try: - return result["group_task_id"] - except KeyError: - self.logger.error( - "Expected 'group_task_id' in response: %s", json.dumps(result) - ) - raise - - async def retrieve( - self, - group_task_id: str, - timeout: int = 300, - polling_interval: int = 2, - **kwargs, - ) -> dict[str, pd.DataFrame]: - """ - Periodically check the task status and retrieve the result when the task - completes. - - :param group_task_id: The task ID to retrieve results for. - :param timeout: The maximum time to wait for the task to complete (in seconds). - :param polling_interval: The time to wait between status checks (in seconds). - :return: Records from the UnivariateModels API of the successfully completed - task. - - """ - # Start time for timeout check - start_time = time.time() - - # Task status URL - status_url = f"{self.url.rstrip('/')}/status/" - - while True: - async with aiohttp.ClientSession() as session: - # Send a GET request to check the task status - async with session.get( - status_url, - headers=self.header, - params={"group_task_id": group_task_id}, - ) as response: - response.raise_for_status() # Raise an error for bad status codes - status_response = await response.json() - - # Check if the task is complete - if status_response.get("status") == "SUCCESS": - - if error_tasks := status_response.get("error_tasks"): - self.logger.error( - f"Tasks {group_task_id} failed: {error_tasks}" - ) - if success_tasks := status_response.get("success_pks"): - params = {"id": ",".join(str(pk) for pk in success_tasks)} - return await self.read(params=params) - elif status_response.get("status") == "FAILURE": - raise Exception( - f"Task {group_task_id} failed: {status_response}" - ) - - # Check if we have reached the timeout - elapsed_time = time.time() - start_time - if elapsed_time > timeout: - raise TimeoutError( - f"Task {group_task_id} did not " - "complete within {timeout} seconds." - ) - - # Wait for the specified polling interval before checking again - await asyncio.sleep(polling_interval) - - def create(self, data: dict[str, Any], **kwargs) -> requests.Response: - raise NotImplementedError("The UnivariateModels does not support create.") - - def update(self, df: pd.DataFrame, **kwargs: Any) -> requests.Response: - """ - Update the records in the database. - - :param df: The DataFrame containing the records to update. - :type df: pd.DataFrame - :param kwargs: Additional fields to include in the payload. - :type kwargs: Any - :return: The response from the POST request. - :rtype: requests.Response - :raises requests.RequestException: If the request fails. - - """ - bulk_update_url = ( - f"{self.url.rstrip('/')}/{self.bulk_update_url_suffix.rstrip('/')}/" - ) - - self.logger.debug("bulk_update_url: %s", bulk_update_url) - - # Include additional fields in the payload if provided - payload = {"data": df.to_dict(orient="records")} - payload.update(kwargs) - - try: - response = requests.post( - bulk_update_url, - headers=self.header, - json=payload, - ) - response.raise_for_status() - return response - except requests.RequestException as e: - self.logger.error(f"Error in POST request: {e}") - raise - - def delete(self, id: str, **kwargs) -> Any: - """ - Delete a UnivariateModels record from the database. - - :param id: The ID of the UnivariateModels record to delete. - :return: A dictionary with a status message indicating success or failure. - - """ - # Include the Authorization header with the token - headers = kwargs.get("headers", {}) - headers["Authorization"] = f"Token {self.token}" - - # Make the DELETE request with the updated headers - response = requests.delete(f"{self.url}/{id}/", headers=headers, **kwargs) - - if response.status_code == 204: - return { - "status": "success", - "message": "UnivariateModels deleted successfully.", - } - - # Raise an error if the response indicates failure - response.raise_for_status() diff --git a/tfbpapi/__init__.py b/tfbpapi/__init__.py index 8c0f3be..e69de29 100644 --- a/tfbpapi/__init__.py +++ b/tfbpapi/__init__.py @@ -1,39 +0,0 @@ -from .BindingAPI import BindingAPI -from .BindingConcatenatedAPI import BindingConcatenatedAPI -from .BindingManualQCAPI import BindingManualQCAPI -from .CallingCardsBackgroundAPI import CallingCardsBackgroundAPI -from .DataSourceAPI import DataSourceAPI -from .DtoAPI import DtoAPI -from .ExpressionAPI import ExpressionAPI -from .ExpressionManualQCAPI import ExpressionManualQCAPI -from .FileFormatAPI import FileFormatAPI -from .GenomicFeatureAPI import GenomicFeatureAPI -from .metric_arrays import metric_arrays -from .PromoterSetAPI import PromoterSetAPI -from .PromoterSetSigAPI import PromoterSetSigAPI -from .rank_transforms import shifted_negative_log_ranks, stable_rank, transform -from .RankResponseAPI import RankResponseAPI -from .RegulatorAPI import RegulatorAPI -from .UnivariateModelsAPI import UnivariateModelsAPI - -__all__ = [ - "BindingAPI", - "BindingConcatenatedAPI", - "BindingManualQCAPI", - "CallingCardsBackgroundAPI", - "DataSourceAPI", - "DtoAPI", - "ExpressionAPI", - "ExpressionManualQCAPI", - "FileFormatAPI", - "GenomicFeatureAPI", - "metric_arrays", - "transform", - "PromoterSetAPI", - "PromoterSetSigAPI", - "RankResponseAPI", - "RegulatorAPI", - "stable_rank", - "shifted_negative_log_ranks", - "UnivariateModelsAPI", -] diff --git a/tfbpapi/datainfo/__init__.py b/tfbpapi/datainfo/__init__.py new file mode 100644 index 0000000..88452eb --- /dev/null +++ b/tfbpapi/datainfo/__init__.py @@ -0,0 +1,23 @@ +from .datacard import DataCard +from .fetchers import HfDataCardFetcher, HfRepoStructureFetcher, HfSizeInfoFetcher +from .models import ( + DatasetCard, + DatasetConfig, + DatasetType, + ExtractedMetadata, + FeatureInfo, + MetadataRelationship, +) + +__all__ = [ + "DataCard", + "HfDataCardFetcher", + "HfRepoStructureFetcher", + "HfSizeInfoFetcher", + "DatasetCard", + "DatasetConfig", + "DatasetType", + "ExtractedMetadata", + "FeatureInfo", + "MetadataRelationship", +] diff --git a/tfbpapi/datainfo/datacard.py b/tfbpapi/datainfo/datacard.py new file mode 100644 index 0000000..e59fc4c --- /dev/null +++ b/tfbpapi/datainfo/datacard.py @@ -0,0 +1,339 @@ +"""DataCard class for easy exploration of HuggingFace dataset metadata.""" + +import logging +from typing import Any, Dict, List, Optional, Set, Union + +from pydantic import ValidationError + +from ..errors import ( + DataCardError, + DataCardValidationError, + HfDataFetchError, +) +from .fetchers import HfDataCardFetcher, HfRepoStructureFetcher, HfSizeInfoFetcher +from .models import ( + DatasetCard, + DatasetConfig, + DatasetType, + ExtractedMetadata, + MetadataRelationship, +) + + +class DataCard: + """Easy-to-use interface for exploring HuggingFace dataset metadata. + + Provides methods to discover and explore dataset contents, configurations, + and metadata without loading the actual genomic data. + """ + + def __init__(self, repo_id: str, token: Optional[str] = None): + """Initialize DataCard for a repository. + + :param repo_id: HuggingFace repository identifier (e.g., "user/dataset") + :param token: Optional HuggingFace token for authentication + """ + self.repo_id = repo_id + self.token = token + self.logger = logging.getLogger(self.__class__.__name__) + + # Initialize fetchers + self._card_fetcher = HfDataCardFetcher(token=token) + self._structure_fetcher = HfRepoStructureFetcher(token=token) + self._size_fetcher = HfSizeInfoFetcher(token=token) + + # Cache for parsed card + self._dataset_card: Optional[DatasetCard] = None + self._metadata_cache: Dict[str, List[ExtractedMetadata]] = {} + + @property + def dataset_card(self) -> DatasetCard: + """Get the validated dataset card.""" + if self._dataset_card is None: + self._load_and_validate_card() + # this is here for type checking purposes. _load_and_validate_card() + # will either set the _dataset_card or raise an error + assert self._dataset_card is not None + return self._dataset_card + + def _load_and_validate_card(self) -> None: + """Load and validate the dataset card from HuggingFace.""" + try: + self.logger.debug(f"Loading dataset card for {self.repo_id}") + card_data = self._card_fetcher.fetch(self.repo_id) + + if not card_data: + raise DataCardValidationError( + f"No dataset card found for {self.repo_id}" + ) + + # Validate using Pydantic model + self._dataset_card = DatasetCard(**card_data) + self.logger.debug(f"Successfully validated dataset card for {self.repo_id}") + + except ValidationError as e: + # Create a more user-friendly error message + error_details = [] + for error in e.errors(): + field_path = " -> ".join(str(x) for x in error['loc']) + error_type = error['type'] + error_msg = error['msg'] + input_value = error.get('input', 'N/A') + + if 'dtype' in field_path and error_type == 'string_type': + error_details.append( + f"Field '{field_path}': Expected a simple data type string (like 'string', 'int64', 'float64') " + f"but got a complex structure. This might be a categorical field with class labels. " + f"Actual value: {input_value}" + ) + else: + error_details.append(f"Field '{field_path}': {error_msg} (got: {input_value})") + + detailed_msg = f"Dataset card validation failed for {self.repo_id}:\n" + "\n".join(f" - {detail}" for detail in error_details) + self.logger.error(detailed_msg) + raise DataCardValidationError(detailed_msg) from e + except HfDataFetchError as e: + raise DataCardError(f"Failed to fetch dataset card: {e}") from e + + @property + def configs(self) -> List[DatasetConfig]: + """Get all dataset configurations.""" + return self.dataset_card.configs + + def get_config(self, config_name: str) -> Optional[DatasetConfig]: + """Get a specific configuration by name.""" + return self.dataset_card.get_config_by_name(config_name) + + def get_configs_by_type( + self, dataset_type: Union[DatasetType, str] + ) -> List[DatasetConfig]: + """Get configurations by dataset type.""" + if isinstance(dataset_type, str): + dataset_type = DatasetType(dataset_type) + return self.dataset_card.get_configs_by_type(dataset_type) + + def get_regulators(self, config_name: Optional[str] = None) -> Set[str]: + """Get all regulators mentioned in the dataset. + + :param config_name: Optional specific config to search, otherwise searches all + :return: Set of regulator identifiers found + """ + raise NotImplementedError("Method not yet implemented") + + def get_experimental_conditions( + self, config_name: Optional[str] = None + ) -> Set[str]: + """Get all experimental conditions mentioned in the dataset. + + :param config_name: Optional specific config to search, otherwise searches all + :return: Set of experimental conditions found + """ + raise NotImplementedError("Method not yet implemented") + + def get_field_values(self, config_name: str, field_name: str) -> Set[str]: + """Get all unique values for a specific field in a configuration. + + :param config_name: Configuration name + :param field_name: Field name to extract values from + :return: Set of unique values + :raises DataCardError: If config or field not found + """ + config = self.get_config(config_name) + if not config: + raise DataCardError(f"Configuration '{config_name}' not found") + + # Check if field exists in the config + field_names = [f.name for f in config.dataset_info.features] + if field_name not in field_names: + raise DataCardError( + f"Field '{field_name}' not found in config '{config_name}'" + ) + + return self._extract_field_values(config, field_name) + + def _extract_field_values(self, config: DatasetConfig, field_name: str) -> Set[str]: + """Extract unique values for a field from various sources.""" + values = set() + + # Check cache first + cache_key = f"{config.config_name}:{field_name}" + if cache_key in self._metadata_cache: + cached_metadata = self._metadata_cache[cache_key] + for meta in cached_metadata: + if meta.field_name == field_name: + values.update(meta.values) + return values + + try: + # For partitioned datasets, extract from file structure + if ( + config.dataset_info.partitioning + and config.dataset_info.partitioning.enabled + ): + partition_values = self._extract_partition_values(config, field_name) + if partition_values: + values.update(partition_values) + + # For embedded metadata fields, we would need to query the actual data + # This is a placeholder - in practice, you might use the HF datasets server API + if config.metadata_fields and field_name in config.metadata_fields: + # Placeholder for actual data extraction + self.logger.debug( + f"Would extract embedded metadata for {field_name} in {config.config_name}" + ) + + except Exception as e: + self.logger.warning(f"Failed to extract values for {field_name}: {e}") + + return values + + def _extract_partition_values( + self, config: DatasetConfig, field_name: str + ) -> Set[str]: + """Extract values from partition structure.""" + if ( + not config.dataset_info.partitioning + or not config.dataset_info.partitioning.enabled + ): + return set() + + partition_columns = config.dataset_info.partitioning.partition_by or [] + if field_name not in partition_columns: + return set() + + try: + # Get partition values from repository structure + partition_values = self._structure_fetcher.get_partition_values( + self.repo_id, field_name + ) + return set(partition_values) + except HfDataFetchError: + self.logger.warning(f"Failed to extract partition values for {field_name}") + return set() + + def get_metadata_relationships(self) -> List[MetadataRelationship]: + """Get relationships between data configs and their metadata.""" + relationships = [] + data_configs = self.dataset_card.get_data_configs() + metadata_configs = self.dataset_card.get_metadata_configs() + + for data_config in data_configs: + # Check for explicit applies_to relationships + for meta_config in metadata_configs: + if ( + meta_config.applies_to + and data_config.config_name in meta_config.applies_to + ): + relationships.append( + MetadataRelationship( + data_config=data_config.config_name, + metadata_config=meta_config.config_name, + relationship_type="explicit", + ) + ) + continue + + + # Check for embedded metadata + if data_config.metadata_fields: + relationships.append( + MetadataRelationship( + data_config=data_config.config_name, + metadata_config=f"{data_config.config_name}_embedded", + relationship_type="embedded", + ) + ) + + return relationships + + + def get_repository_info(self) -> Dict[str, Any]: + """Get general repository information.""" + card = self.dataset_card + + try: + structure = self._structure_fetcher.fetch(self.repo_id) + total_files = structure.get("total_files", 0) + last_modified = structure.get("last_modified") + except HfDataFetchError: + total_files = None + last_modified = None + + return { + "repo_id": self.repo_id, + "pretty_name": card.pretty_name, + "license": card.license, + "tags": card.tags, + "language": card.language, + "size_categories": card.size_categories, + "num_configs": len(card.configs), + "dataset_types": [config.dataset_type.value for config in card.configs], + "total_files": total_files, + "last_modified": last_modified, + "has_default_config": self.dataset_card.get_default_config() is not None, + } + + def explore_config(self, config_name: str) -> Dict[str, Any]: + """Get detailed information about a specific configuration.""" + config = self.get_config(config_name) + if not config: + raise DataCardError(f"Configuration '{config_name}' not found") + + info: Dict[str, Any] = { + "config_name": config.config_name, + "description": config.description, + "dataset_type": config.dataset_type.value, + "is_default": config.default, + "num_features": len(config.dataset_info.features), + "features": [ + {"name": f.name, "dtype": f.dtype, "description": f.description} + for f in config.dataset_info.features + ], + "data_files": [ + {"split": df.split, "path": df.path} for df in config.data_files + ], + } + + # Add partitioning info if present + if config.dataset_info.partitioning: + info["partitioning"] = { + "enabled": config.dataset_info.partitioning.enabled, + "partition_by": config.dataset_info.partitioning.partition_by, + "path_template": config.dataset_info.partitioning.path_template, + } + + # Add metadata-specific fields + if config.applies_to: + info["applies_to"] = config.applies_to + + if config.metadata_fields: + info["metadata_fields"] = config.metadata_fields + + return info + + def summary(self) -> str: + """Get a human-readable summary of the dataset.""" + card = self.dataset_card + info = self.get_repository_info() + + lines = [ + f"Dataset: {card.pretty_name or self.repo_id}", + f"Repository: {self.repo_id}", + f"License: {card.license or 'Not specified'}", + f"Configurations: {len(card.configs)}", + f"Dataset Types: {', '.join(info['dataset_types'])}", + ] + + if card.tags: + lines.append(f"Tags: {', '.join(card.tags)}") + + # Add config summaries + lines.append("\nConfigurations:") + for config in card.configs: + default_mark = " (default)" if config.default else "" + lines.append( + f" - {config.config_name}: {config.dataset_type.value}{default_mark}" + ) + lines.append(f" {config.description}") + + return "\n".join(lines) diff --git a/tfbpapi/datainfo/fetchers.py b/tfbpapi/datainfo/fetchers.py new file mode 100644 index 0000000..76b33fc --- /dev/null +++ b/tfbpapi/datainfo/fetchers.py @@ -0,0 +1,224 @@ +"""Data fetchers for HuggingFace Hub integration.""" + +import logging +import os +import re +from typing import Any, Dict, List, Optional, Set + +import requests +from huggingface_hub import DatasetCard, repo_info +from requests import HTTPError + +from ..errors import HfDataFetchError + + +class HfDataCardFetcher: + """Handles fetching dataset cards from HuggingFace Hub.""" + + def __init__(self, token: Optional[str] = None): + """Initialize the fetcher. + + :param token: HuggingFace token for authentication + """ + self.logger = logging.getLogger(self.__class__.__name__) + self.token = token or os.getenv("HF_TOKEN") + + def fetch(self, repo_id: str, repo_type: str = "dataset") -> Dict[str, Any]: + """Fetch and return dataset card data. + + :param repo_id: Repository identifier (e.g., "user/dataset") + :param repo_type: Type of repository ("dataset", "model", "space") + :return: Dataset card data as dictionary + :raises HfDataFetchError: If fetching fails + """ + try: + self.logger.debug(f"Fetching dataset card for {repo_id}") + card = DatasetCard.load(repo_id, repo_type=repo_type, token=self.token) + + if not card.data: + self.logger.warning(f"Dataset card for {repo_id} has no data section") + return {} + + return card.data.to_dict() + + except Exception as e: + error_msg = f"Failed to fetch dataset card for {repo_id}: {e}" + self.logger.error(error_msg) + raise HfDataFetchError(error_msg) from e + + +class HfSizeInfoFetcher: + """Handles fetching size information from HuggingFace Dataset Server API.""" + + def __init__(self, token: Optional[str] = None): + """Initialize the fetcher. + + :param token: HuggingFace token for authentication + """ + self.logger = logging.getLogger(self.__class__.__name__) + self.token = token or os.getenv("HF_TOKEN") + self.base_url = "https://datasets-server.huggingface.co" + + def _build_headers(self) -> Dict[str, str]: + """Build request headers with authentication if available.""" + headers = {"User-Agent": "TFBP-API/1.0"} + if self.token: + headers["Authorization"] = f"Bearer {self.token}" + return headers + + def fetch(self, repo_id: str) -> Dict[str, Any]: + """Fetch dataset size information. + + :param repo_id: Repository identifier (e.g., "user/dataset") + :return: Size information as dictionary + :raises HfDataFetchError: If fetching fails + """ + url = f"{self.base_url}/size" + params = {"dataset": repo_id} + headers = self._build_headers() + + try: + self.logger.debug(f"Fetching size info for {repo_id}") + response = requests.get(url, params=params, headers=headers, timeout=30) + response.raise_for_status() + + data = response.json() + self.logger.debug(f"Size info fetched successfully for {repo_id}") + return data + + except HTTPError as e: + if e.response.status_code == 404: + error_msg = f"Dataset {repo_id} not found" + elif e.response.status_code == 403: + error_msg = ( + f"Access denied to dataset {repo_id} (check token permissions)" + ) + else: + error_msg = f"HTTP error fetching size for {repo_id}: {e}" + + self.logger.error(error_msg) + raise HfDataFetchError(error_msg) from e + + except requests.RequestException as e: + error_msg = f"Request failed fetching size for {repo_id}: {e}" + self.logger.error(error_msg) + raise HfDataFetchError(error_msg) from e + + except ValueError as e: + error_msg = f"Invalid JSON response fetching size for {repo_id}: {e}" + self.logger.error(error_msg) + raise HfDataFetchError(error_msg) from e + + +class HfRepoStructureFetcher: + """Handles fetching repository structure from HuggingFace Hub.""" + + def __init__(self, token: Optional[str] = None): + """Initialize the fetcher. + + :param token: HuggingFace token for authentication + """ + self.logger = logging.getLogger(self.__class__.__name__) + self.token = token or os.getenv("HF_TOKEN") + self._cached_structure: Dict[str, Dict[str, Any]] = {} + + def fetch(self, repo_id: str, force_refresh: bool = False) -> Dict[str, Any]: + """Fetch repository structure information. + + :param repo_id: Repository identifier (e.g., "user/dataset") + :param force_refresh: If True, bypass cache and fetch fresh data + :return: Repository structure information + :raises HfDataFetchError: If fetching fails + """ + # Check cache first unless force refresh is requested + if not force_refresh and repo_id in self._cached_structure: + self.logger.debug(f"Using cached repo structure for {repo_id}") + return self._cached_structure[repo_id] + + try: + self.logger.debug(f"Fetching repo structure for {repo_id}") + info = repo_info(repo_id=repo_id, repo_type="dataset", token=self.token) + + # Extract file structure + files = [] + partitions: Dict[str, set] = {} + + for sibling in info.siblings or []: + file_info = { + "path": sibling.rfilename, + "size": sibling.size, + "is_lfs": sibling.lfs is not None, + } + files.append(file_info) + + # Extract partition information from file paths + self._extract_partition_info(sibling.rfilename, partitions) + + result = { + "repo_id": repo_id, + "files": files, + "partitions": partitions, + "total_files": len(files), + "last_modified": ( + info.last_modified.isoformat() if info.last_modified else None + ), + } + + # Cache the result + self._cached_structure[repo_id] = result + return result + + except Exception as e: + error_msg = f"Failed to fetch repo structure for {repo_id}: {e}" + self.logger.error(error_msg) + raise HfDataFetchError(error_msg) from e + + def _extract_partition_info( + self, file_path: str, partitions: Dict[str, Set[str]] + ) -> None: + """Extract partition information from file paths. + + :param file_path: Path to analyze for partitions + :param partitions: Dictionary to update with partition info + """ + # Look for partition patterns like "column=value" in path + partition_pattern = r"([^/=]+)=([^/]+)" + matches = re.findall(partition_pattern, file_path) + + for column, value in matches: + if column not in partitions: + partitions[column] = set() + partitions[column].add(value) + + def get_partition_values(self, repo_id: str, partition_column: str, force_refresh: bool = False) -> List[str]: + """Get all values for a specific partition column. + + :param repo_id: Repository identifier + :param partition_column: Name of the partition column + :param force_refresh: If True, bypass cache and fetch fresh data + :return: List of unique partition values + :raises HfDataFetchError: If fetching fails + """ + structure = self.fetch(repo_id, force_refresh=force_refresh) + partition_values = structure.get("partitions", {}).get(partition_column, set()) + return sorted(list(partition_values)) + + def get_dataset_files( + self, repo_id: str, path_pattern: Optional[str] = None, force_refresh: bool = False + ) -> List[Dict[str, Any]]: + """Get dataset files, optionally filtered by path pattern. + + :param repo_id: Repository identifier + :param path_pattern: Optional regex pattern to filter files + :param force_refresh: If True, bypass cache and fetch fresh data + :return: List of matching files + :raises HfDataFetchError: If fetching fails + """ + structure = self.fetch(repo_id, force_refresh=force_refresh) + files = structure["files"] + + if path_pattern: + pattern = re.compile(path_pattern) + files = [f for f in files if pattern.search(f["path"])] + + return files diff --git a/tfbpapi/datainfo/models.py b/tfbpapi/datainfo/models.py new file mode 100644 index 0000000..2ad8383 --- /dev/null +++ b/tfbpapi/datainfo/models.py @@ -0,0 +1,250 @@ +"""Pydantic models for dataset card validation.""" + +from enum import Enum +from typing import Any, Dict, List, Optional, Set, Union + +from pydantic import BaseModel, ConfigDict, Field, field_validator + + +class DatasetType(str, Enum): + """Supported dataset types.""" + + GENOMIC_FEATURES = "genomic_features" + ANNOTATED_FEATURES = "annotated_features" + GENOME_MAP = "genome_map" + METADATA = "metadata" + + +class ClassLabelType(BaseModel): + """Categorical data type with class labels.""" + + names: List[str] = Field(..., description="List of possible class names") + + +class FeatureInfo(BaseModel): + """Information about a dataset feature/column.""" + + name: str = Field(..., description="Column name in the data") + dtype: Union[str, Dict[str, ClassLabelType]] = Field( + ..., + description="Data type (string, int64, float64, etc.) or categorical class labels", + ) + description: str = Field(..., description="Detailed description of the field") + + @field_validator("dtype", mode="before") + @classmethod + def validate_dtype(cls, v): + """Validate and normalize dtype field.""" + if isinstance(v, str): + return v + elif isinstance(v, dict): + # Handle class_label structure + if "class_label" in v: + # Convert to our ClassLabelType structure + class_label_data = v["class_label"] + if isinstance(class_label_data, dict) and "names" in class_label_data: + return {"class_label": ClassLabelType(**class_label_data)} + else: + raise ValueError( + f"Invalid class_label structure: expected dict with 'names' key, got {class_label_data}" + ) + else: + raise ValueError( + f"Unknown dtype structure: expected 'class_label' key in dict, got keys: {list(v.keys())}" + ) + else: + raise ValueError( + f"dtype must be a string or dict with class_label info, got {type(v)}: {v}" + ) + + def get_dtype_summary(self) -> str: + """Get a human-readable summary of the data type.""" + if isinstance(self.dtype, str): + return self.dtype + elif isinstance(self.dtype, dict) and "class_label" in self.dtype: + names = self.dtype["class_label"].names + return f"categorical ({len(names)} classes: {', '.join(names)})" + else: + return str(self.dtype) + + +class PartitioningInfo(BaseModel): + """Partitioning configuration for datasets.""" + + enabled: bool = Field(default=False, description="Whether partitioning is enabled") + partition_by: Optional[List[str]] = Field( + default=None, description="Partition column names" + ) + path_template: Optional[str] = Field( + default=None, description="Path template for partitioned files" + ) + + +class DataFileInfo(BaseModel): + """Information about data files.""" + + split: str = Field(default="train", description="Dataset split name") + path: str = Field(..., description="Path to data file(s)") + + +class DatasetInfo(BaseModel): + """Dataset structure information.""" + + features: List[FeatureInfo] = Field(..., description="Feature definitions") + partitioning: Optional[PartitioningInfo] = Field( + default=None, description="Partitioning configuration" + ) + + +class DatasetConfig(BaseModel): + """Configuration for a dataset within a repository.""" + + config_name: str = Field(..., description="Unique configuration identifier") + description: str = Field(..., description="Human-readable description") + dataset_type: DatasetType = Field(..., description="Type of dataset") + default: bool = Field( + default=False, description="Whether this is the default config" + ) + applies_to: Optional[List[str]] = Field( + default=None, description="Configs this metadata applies to" + ) + metadata_fields: Optional[List[str]] = Field( + default=None, description="Fields for embedded metadata extraction" + ) + data_files: List[DataFileInfo] = Field(..., description="Data file information") + dataset_info: DatasetInfo = Field(..., description="Dataset structure information") + + @field_validator("applies_to") + @classmethod + def applies_to_only_for_metadata(cls, v, info): + """Validate that applies_to is only used for metadata configs.""" + if v is not None: + dataset_type = info.data.get("dataset_type") + if dataset_type != DatasetType.METADATA: + raise ValueError( + "applies_to field is only valid for metadata dataset types" + ) + return v + + @field_validator("metadata_fields") + @classmethod + def metadata_fields_validation(cls, v): + """Validate metadata_fields usage.""" + if v is not None and len(v) == 0: + raise ValueError("metadata_fields cannot be empty list, use None instead") + return v + + +class BasicMetadata(BaseModel): + """Basic dataset metadata.""" + + license: Optional[str] = Field(default=None, description="Dataset license") + language: Optional[List[str]] = Field(default=None, description="Dataset languages") + tags: Optional[List[str]] = Field(default=None, description="Descriptive tags") + pretty_name: Optional[str] = Field( + default=None, description="Human-readable dataset name" + ) + size_categories: Optional[List[str]] = Field( + default=None, description="Dataset size categories" + ) + + +class DatasetCard(BaseModel): + """Complete dataset card model.""" + + configs: List[DatasetConfig] = Field(..., description="Dataset configurations") + license: Optional[str] = Field(default=None, description="Dataset license") + language: Optional[List[str]] = Field(default=None, description="Dataset languages") + tags: Optional[List[str]] = Field(default=None, description="Descriptive tags") + pretty_name: Optional[str] = Field( + default=None, description="Human-readable dataset name" + ) + size_categories: Optional[List[str]] = Field( + default=None, description="Dataset size categories" + ) + + @field_validator("configs") + @classmethod + def configs_not_empty(cls, v): + """Ensure at least one config is present.""" + if not v: + raise ValueError("At least one dataset configuration is required") + return v + + @field_validator("configs") + @classmethod + def unique_config_names(cls, v): + """Ensure config names are unique.""" + names = [config.config_name for config in v] + if len(names) != len(set(names)): + raise ValueError("Configuration names must be unique") + return v + + @field_validator("configs") + @classmethod + def at_most_one_default(cls, v): + """Ensure at most one config is marked as default.""" + defaults = [config for config in v if config.default] + if len(defaults) > 1: + raise ValueError("At most one configuration can be marked as default") + return v + + def get_config_by_name(self, name: str) -> Optional[DatasetConfig]: + """Get a configuration by name.""" + for config in self.configs: + if config.config_name == name: + return config + return None + + def get_configs_by_type(self, dataset_type: DatasetType) -> List[DatasetConfig]: + """Get all configurations of a specific type.""" + return [ + config for config in self.configs if config.dataset_type == dataset_type + ] + + def get_default_config(self) -> Optional[DatasetConfig]: + """Get the default configuration if one exists.""" + defaults = [config for config in self.configs if config.default] + return defaults[0] if defaults else None + + def get_data_configs(self) -> List[DatasetConfig]: + """Get all non-metadata configurations.""" + return [ + config + for config in self.configs + if config.dataset_type != DatasetType.METADATA + ] + + def get_metadata_configs(self) -> List[DatasetConfig]: + """Get all metadata configurations.""" + return [ + config + for config in self.configs + if config.dataset_type == DatasetType.METADATA + ] + + +class ExtractedMetadata(BaseModel): + """Metadata extracted from datasets.""" + + config_name: str = Field(..., description="Source configuration name") + field_name: str = Field( + ..., description="Field name the metadata was extracted from" + ) + values: Set[str] = Field(..., description="Unique values found") + extraction_method: str = Field(..., description="How the metadata was extracted") + + model_config = ConfigDict( + # Allow sets in JSON serialization + json_encoders={set: list} + ) + + +class MetadataRelationship(BaseModel): + """Relationship between a data config and its metadata.""" + + data_config: str = Field(..., description="Data configuration name") + metadata_config: str = Field(..., description="Metadata configuration name") + relationship_type: str = Field( + ..., description="Type of relationship (explicit, embedded)" + ) diff --git a/tfbpapi/errors.py b/tfbpapi/errors.py new file mode 100644 index 0000000..3c0fdfa --- /dev/null +++ b/tfbpapi/errors.py @@ -0,0 +1,214 @@ +"""Custom exception classes for dataset management.""" + +from typing import Any, Dict, Optional + + +class DatasetError(Exception): + """Base exception for all dataset-related errors.""" + + def __init__(self, message: str, details: Optional[Dict[str, Any]] = None): + super().__init__(message) + self.details = details or {} + + def __str__(self) -> str: + base_msg = super().__str__() + if self.details: + detail_str = ", ".join(f"{k}={v}" for k, v in self.details.items()) + return f"{base_msg} (Details: {detail_str})" + return base_msg + + +class RepoTooLargeError(DatasetError): + """Raised when repository exceeds auto-download threshold.""" + + def __init__(self, repo_id: str, size_mb: float, threshold_mb: float): + message = f"Repository '{repo_id}' is too large for auto-download: {size_mb:.2f}MB exceeds {threshold_mb}MB threshold" + super().__init__( + message, + details={ + "repo_id": repo_id, + "actual_size_mb": size_mb, + "threshold_mb": threshold_mb, + }, + ) + self.repo_id = repo_id + self.size_mb = size_mb + self.threshold_mb = threshold_mb + + +class DataCardParsingError(DatasetError): + """Raised when dataset card parsing fails.""" + + def __init__( + self, + message: str, + repo_id: Optional[str] = None, + config_name: Optional[str] = None, + original_error: Optional[Exception] = None, + ): + details = {} + if repo_id: + details["repo_id"] = repo_id + if config_name: + details["config_name"] = config_name + if original_error: + details["original_error"] = str(original_error) + + super().__init__(message, details) + self.repo_id = repo_id + self.config_name = config_name + self.original_error = original_error + + +class HfDataFetchError(DatasetError): + """Raised when HuggingFace API requests fail.""" + + def __init__( + self, + message: str, + repo_id: Optional[str] = None, + status_code: Optional[int] = None, + endpoint: Optional[str] = None, + ): + details = {} + if repo_id: + details["repo_id"] = repo_id + if status_code: + details["status_code"] = status_code + if endpoint: + details["endpoint"] = endpoint + + super().__init__(message, details) + self.repo_id = repo_id + self.status_code = status_code + self.endpoint = endpoint + + +class TableNotFoundError(DatasetError): + """Raised when requested table doesn't exist.""" + + def __init__(self, table_name: str, available_tables: Optional[list] = None): + available_str = ( + f"Available tables: {available_tables}" + if available_tables + else "No tables available" + ) + message = f"Table '{table_name}' not found. {available_str}" + + super().__init__( + message, + details={ + "requested_table": table_name, + "available_tables": available_tables or [], + }, + ) + self.table_name = table_name + self.available_tables = available_tables or [] + + +class MissingDatasetTypeError(DatasetError): + """Raised when dataset_type field is missing from config.""" + + def __init__(self, config_name: str, available_fields: Optional[list] = None): + fields_str = f"Available fields: {available_fields}" if available_fields else "" + message = ( + f"Missing 'dataset_type' field in config '{config_name}'. {fields_str}" + ) + + super().__init__( + message, + details={ + "config_name": config_name, + "available_fields": available_fields or [], + }, + ) + self.config_name = config_name + self.available_fields = available_fields or [] + + +class InvalidDatasetTypeError(DatasetError): + """Raised when dataset_type value is not recognized.""" + + def __init__(self, invalid_type: str, valid_types: Optional[list] = None): + valid_str = f"Valid types: {valid_types}" if valid_types else "" + message = f"Invalid dataset type '{invalid_type}'. {valid_str}" + + super().__init__( + message, + details={"invalid_type": invalid_type, "valid_types": valid_types or []}, + ) + self.invalid_type = invalid_type + self.valid_types = valid_types or [] + + +class ConfigNotFoundError(DatasetError): + """Raised when a requested config doesn't exist.""" + + def __init__( + self, + config_name: str, + repo_id: Optional[str] = None, + available_configs: Optional[list] = None, + ): + repo_str = f" in repository '{repo_id}'" if repo_id else "" + available_str = ( + f"Available configs: {available_configs}" if available_configs else "" + ) + message = f"Config '{config_name}' not found{repo_str}. {available_str}" + + super().__init__( + message, + details={ + "config_name": config_name, + "repo_id": repo_id, + "available_configs": available_configs or [], + }, + ) + self.config_name = config_name + self.repo_id = repo_id + self.available_configs = available_configs or [] + + +class DataCardError(DatasetError): + """Base exception for DataCard operations.""" + pass + + +class DataCardValidationError(DataCardError): + """Exception raised when dataset card validation fails.""" + + def __init__( + self, + message: str, + repo_id: Optional[str] = None, + validation_errors: Optional[list] = None, + ): + details = {} + if repo_id: + details["repo_id"] = repo_id + if validation_errors: + details["validation_errors"] = validation_errors + + super().__init__(message, details) + self.repo_id = repo_id + self.validation_errors = validation_errors or [] + + +class DataCardMetadataError(DataCardError): + """Exception raised when metadata extraction fails.""" + + def __init__( + self, + message: str, + config_name: Optional[str] = None, + field_name: Optional[str] = None, + ): + details = {} + if config_name: + details["config_name"] = config_name + if field_name: + details["field_name"] = field_name + + super().__init__(message, details) + self.config_name = config_name + self.field_name = field_name diff --git a/tfbpapi/metric_arrays.py b/tfbpapi/metric_arrays.py deleted file mode 100644 index 2bfaf14..0000000 --- a/tfbpapi/metric_arrays.py +++ /dev/null @@ -1,162 +0,0 @@ -import logging -from collections.abc import Callable - -import pandas as pd - -logger = logging.getLogger(__name__) - - -def metric_arrays( - res_dict: dict[str, pd.DataFrame | dict[str, pd.DataFrame]], - metrics_dict: dict[str, Callable], - rownames: str = "target_symbol", - colnames: str = "regulator_symbol", - row_dedup_func: Callable | None = None, - drop_incomplete_rows: bool = True, -) -> dict[str, pd.DataFrame]: - """ - Extract specified metrics from an AbstractRecordsAndFilesAPI instance's - read(retrieve_files=True) results object. - - :param res_dict: The output of an AbstractRecordsAndFiles instance. - :param metrics_dict: A dictionary where the keys are metrics and the values are - functions to apply to rows in the event that there are multiple rows with - the same rownames. Set to None to raise error if duplicate rownames are found. - :param rownames: Column name to use for row labels. - :param colnames: Column name to use for column labels. - :param drop_incomplete_rows: When True, drops rows and columns with all NaN values. - - :return: A dictionary where the metric is the key and the value is a DataFrame. - The column values are metric values, and the column names correspond - to `colnames` in the metadata DataFrame. - - :raises AttributeError: If the values in `colnames` or `rownames` are not unique - :raises KeyError: If the res_dict does not have keys 'metadata' and 'data' - :raises KeyError: If the data dictionary does not have the same keys as the 'id' - column - :raises ValueError: If the metadata does not have an 'id' column - :raises ValueError: If either the metadata or the data dictionary values are not - DataFrames - :raises ValueError: If the `colnames` is not in the res_dict metadata - :raises ValueError: If the `rownames` is not in the res_dict data - :raises ValueError: If the metrics are not in the data dictionary - - """ - - # Check required keys - if not all(k in res_dict for k in ["metadata", "data"]): - raise KeyError("res_dict must have keys 'metadata' and 'data'") - - metadata: pd.DataFrame = res_dict["metadata"] - - # Verify 'id' in metadata - if "id" not in metadata.columns: - raise ValueError("metadata must have an 'id' column") - - # Check for missing keys in 'data' - missing_keys = [k for k in metadata["id"] if str(k) not in res_dict["data"]] - if missing_keys: - raise KeyError( - f"Data dictionary must have the same keys as the 'id' " - f"column. Missing keys: {missing_keys}" - ) - - # Ensure all data dictionary values are DataFrames - if not all(isinstance(v, pd.DataFrame) for v in res_dict["data"].values()): - raise ValueError("All values in the data dictionary must be DataFrames") - - # Verify rownames in data and colnames in metadata - if colnames not in metadata.columns: - raise ValueError(f"colnames '{colnames}' not in metadata") - data_with_missing_rownames = [ - id for id, df in res_dict["data"].items() if rownames not in df.columns - ] - if data_with_missing_rownames: - raise ValueError( - f"rownames '{rownames}' not in data for ids: {data_with_missing_rownames}" - ) - - # Factorize unique row and column labels - row_labels = pd.Index( - {item for df in res_dict["data"].values() for item in df[rownames].unique()} - ) - - # Initialize output dictionary with NaN DataFrames for each metric - output_dict = { - m: pd.DataFrame(index=pd.Index(row_labels, name=rownames)) - for m in metrics_dict.keys() - } - - # Populate DataFrames with metric values - info_msgs = set() - for _, row in metadata.iterrows(): - try: - data = res_dict["data"][row["id"]] - except KeyError: - info_msgs.add("casting `id` to str to extract data from res_dict['data']") - data = res_dict["data"][str(row["id"])] - - for metric, row_dedup_func in metrics_dict.items(): - # Filter data to include only the rownames and metric columns - if metric not in data.columns: - raise ValueError( - f"Metric '{metric}' not found in data for id '{row['id']}'" - ) - - metric_data = data[[rownames, metric]] - - # Handle deduplication if row_dedup_func is provided - if row_dedup_func is not None: - metric_data = ( - metric_data.groupby(rownames)[metric] - .apply(row_dedup_func) - .reset_index() - ) - else: - # Ensure no duplicates exist if no deduplication function is provided - if metric_data[rownames].duplicated().any(): - raise ValueError( - f"Duplicate entries found for metric '{metric}' " - f"in id '{row['id']}' without dedup_func" - ) - - # test if row[colnames] is already in output_dict[metric]. If it is, add a - # replicate suffix and try again, Continue doing this until the column name - # is unique - colname = row[colnames] - suffix = 2 - while colname in output_dict[metric].columns: - colname = f"{row[colnames]}_rep{suffix}" - suffix += 1 - if suffix > 2: - info_msgs.add( - f"Column name '{row[colnames]}' already exists in " - f"output DataFrame for metric '{metric}'. " - f"Renaming to '{colname}'" - ) - # Join metric data with output DataFrame for the metric - output_dict[metric] = output_dict[metric].join( - metric_data.set_index(rownames).rename(columns={metric: colname}), - how="left", - ) - logger.info("; ".join(info_msgs)) - - # Drop incomplete rows and columns if drop_incomplete_rows is True - if drop_incomplete_rows: - for metric, df in output_dict.items(): - # Drop rows and columns where all values are NaN - initial_shape = df.shape - output_dict[metric] = df.dropna(axis=0) - final_shape = output_dict[metric].shape - - dropped_rows = initial_shape[0] - final_shape[0] - dropped_columns = initial_shape[1] - final_shape[1] - - if dropped_rows > 0 or dropped_columns > 0: - logger.warning( - f"{dropped_rows} rows and {dropped_columns} " - f"columns with incomplete " - f"records were dropped for metric '{metric}'." - ) - - return output_dict diff --git a/tfbpapi/rank_transforms.py b/tfbpapi/rank_transforms.py deleted file mode 100644 index 9e4c672..0000000 --- a/tfbpapi/rank_transforms.py +++ /dev/null @@ -1,154 +0,0 @@ -import numpy as np -from scipy.stats import rankdata - - -def shifted_negative_log_ranks(ranks: np.ndarray) -> np.ndarray: - """ - Transforms ranks to negative log10 values and shifts such that the lowest value is - 0. - - :param ranks: A vector of ranks - :return np.ndarray: A vector of negative log10 transformed ranks shifted such that - the lowest value is 0 - :raises ValueError: If the ranks are not numeric. - - """ - if not np.issubdtype(ranks.dtype, np.number): - raise ValueError("`ranks` must be a numeric") - max_rank = np.max(ranks) - log_max_rank = np.log10(max_rank) - return -1 * np.log10(ranks) + log_max_rank - - -def stable_rank( - pvalue_vector: np.ndarray, enrichment_vector: np.ndarray, method="average" -) -> np.ndarray: - """ - Ranks data by primary_column, breaking ties based on secondary_column. The expected - primary and secondary columns are 'pvalue' and 'enrichment', respectively. Then the - ranks are transformed to negative log10 values and shifted such that the lowest - value is 0 and the highest value is log10(min_rank). - - :param pvalue_vector: A vector of pvalues - :param enrichment_vector: A vector of enrichment values corresponding to the pvalues - :param method: The method to use for final ranking. Default is "average". - See `rankdata` - - :return np.ndarray: A vector of negative log10 transformed ranks shifted such that - the lowest value is 0 and the highest value is log10(min_rank) - :raises ValueError: If the primary or secondary column is not numeric. - - """ - - # Check if primary and secondary columns are numeric - if not np.issubdtype(pvalue_vector.dtype, np.number): - raise ValueError("`primary_vector` must be a numeric") - if not np.issubdtype(enrichment_vector.dtype, np.number): - raise ValueError("`secondary_vector` must be a numeric") - - # Step 1: Rank by primary_column - # note that this will now always be an integer, unlike average which could return - # decimal values making adding the secondary rank more difficult - primary_rank = rankdata(pvalue_vector, method="min") - - # Step 2: Identify ties in primary_rank - unique_ranks = np.unique(primary_rank) - - # Step 3: Adjust ranks within ties using secondary ranking - adjusted_primary_rank = primary_rank.astype( - float - ) # Convert to float for adjustments - - for unique_rank in unique_ranks: - # Get indices where primary_rank == unique_rank - tie_indices = np.where(primary_rank == unique_rank)[0] - - if len(tie_indices) > 1: # Only adjust if there are ties - # Rank within the tie group by secondary_column - # (descending if higher is better) - tie_secondary_values = enrichment_vector[tie_indices] - secondary_rank_within_ties = rankdata( - -tie_secondary_values, method="average" - ) - - # Calculate dynamic scale factor to ensure adjustments are < 1. Since the - # primary_rank is an integer, adding a number less than 1 will not affect - # rank relative to the other groups. - max_secondary_rank = np.max(secondary_rank_within_ties) - scale_factor = ( - 0.9 / max_secondary_rank - ) # Keep scale factor slightly below 1/max rank - - # multiple the secondary_rank_within_ties values by 0.1 and add this value - # to the adjusted_primary_rank_values. This will rank the tied primary - # values by the secondary values, but not affect the overall primary rank - # outside of the tie group - # think about this scale factor - adjusted_primary_rank[tie_indices] += ( - secondary_rank_within_ties * scale_factor - ) - - # Step 4: Final rank based on the adjusted primary ranks - final_ranks = rankdata(adjusted_primary_rank, method=method) - - return final_ranks - - -def rank_by_pvalue(pvalue_vector: np.ndarray, method="average") -> np.ndarray: - """ - This expects a vector of pvalues, returns a vector of ranks where the lowest pvalue - has the lowest rank. - - :param pvalue_vector: A vector of pvalues - :param enrichment_vector: A vector of enrichment values corresponding to the pvalues - :param method: The method to use for ranking. Default is "average". See `rankdata` - :return np.ndarray: A vector of negative log10 transformed ranks shifted such that - the lowest value is 0 and the highest value is log10(min_rank) - :raises ValueError: If the primary or secondary column is not numeric. - - """ - - # Check if primary and secondary columns are numeric - if not np.issubdtype(pvalue_vector.dtype, np.number): - raise ValueError("`primary_vector` must be a numeric") - - # Step 1: Rank by primary_column - # note that this will now always be an integer, unlike average which could return - # decimal values making adding the secondary rank more difficult - return rankdata(pvalue_vector, method=method) - - -def transform( - pvalue_vector: np.ndarray, - enrichment_vector: np.ndarray, - use_enrichment: bool = True, - negative_log_shift: bool = True, - **kwargs, -) -> np.ndarray: - """ - This calls the rank() function and then transforms the ranks to negative log10 - values and shifts to the right such that the lowest value (largest rank, least - important) is 0. - - :param pvalue_vector: A vector of pvalues - :param enrichment_vector: A vector of enrichment values corresponding to the pvalues - :param use_enrichment: Set to True to use the enrichment vector to break ties. - Default is True. If False, pvalues will be ranked directly with method="average' - :param negative_log_shift: Set to True to shift the ranks to the right such that the - lowest value (largest rank, least important) is 0. Default is True. - :param kwargs: Additional keyword arguments to pass to the rank() function (e.g. - method="min") - :return np.ndarray: A vector of negative log10 transformed ranks shifted such that - the lowest value is 0 and the highest value is log10(min_rank) - :raises ValueError: If the primary or secondary column is not numeric. - - """ - if use_enrichment: - ranks = stable_rank(pvalue_vector, enrichment_vector, **kwargs) - else: - ranks = rank_by_pvalue(pvalue_vector, **kwargs) - - if negative_log_shift: - return shifted_negative_log_ranks(ranks) - else: - return ranks diff --git a/tfbpapi/tests/data/cache_info.pkl b/tfbpapi/tests/data/cache_info.pkl deleted file mode 100644 index 7906d8e30dd63514636537b3083c3ee20c7995e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 123975 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 diff --git a/tfbpapi/tests/datainfo/__init__.py b/tfbpapi/tests/datainfo/__init__.py new file mode 100644 index 0000000..672d1e8 --- /dev/null +++ b/tfbpapi/tests/datainfo/__init__.py @@ -0,0 +1 @@ +"""Tests for the datainfo package.""" \ No newline at end of file diff --git a/tfbpapi/tests/datainfo/conftest.py b/tfbpapi/tests/datainfo/conftest.py new file mode 100644 index 0000000..51afb26 --- /dev/null +++ b/tfbpapi/tests/datainfo/conftest.py @@ -0,0 +1,302 @@ +"""Shared fixtures and test data for datainfo tests.""" + +import pytest +from unittest.mock import Mock + + +@pytest.fixture +def sample_dataset_card_data(): + """Sample dataset card data for testing.""" + return { + "license": "mit", + "language": ["en"], + "tags": ["biology", "genomics", "yeast"], + "pretty_name": "Test Genomics Dataset", + "size_categories": ["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≯!LT?|_H! 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 z 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 YgTM)>F#ch07ho6Il67-!r=i}E%5Ns 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

?W~Gc*^Ru%3*ce|{EVR!)Pvo8K$ekY| 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 z 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 K2%zG7~yq3@~=SbuENeX6~z#xR@7J7ye{`&6)9v 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 diff --git a/tfbpapi/tests/snapshots/snap_test_AbstractAPI.py b/tfbpapi/tests/snapshots/snap_test_AbstractAPI.py deleted file mode 100644 index 8444992..0000000 --- a/tfbpapi/tests/snapshots/snap_test_AbstractAPI.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# snapshottest: v1 - https://goo.gl/zC4yUc -from __future__ import unicode_literals - -from snapshottest import Snapshot - - -snapshots = Snapshot() - -snapshots['test_cache_operations cache_get_after_delete'] = 'None' - -snapshots['test_cache_operations cache_get_after_set'] = 'test_value' - -snapshots['test_cache_operations cache_list'] = "['test_key']" - -snapshots['test_pop_params pop_params_after_all_removed'] = '{}' - -snapshots['test_pop_params pop_params_after_one_removed'] = '{"param2": "value2"}' - -snapshots['test_push_params push_params'] = '{"param1": "value1", "param2": "value2"}' diff --git a/tfbpapi/tests/snapshots/snap_test_AbstractRecordsAndFilesAPI.py b/tfbpapi/tests/snapshots/snap_test_AbstractRecordsAndFilesAPI.py deleted file mode 100644 index 807cb7d..0000000 --- a/tfbpapi/tests/snapshots/snap_test_AbstractRecordsAndFilesAPI.py +++ /dev/null @@ -1,15 +0,0 @@ -# snapshottest: v1 - https://goo.gl/zC4yUc - -from snapshottest import Snapshot - -snapshots = Snapshot() - -snapshots[ - "test_save_response_records_and_files 1" -] = """id,uploader_id,upload_date,modifier_id,modified_date,binding_id,promoter_id,background_id,fileformat_id,file -10690,1,2024-03-26,1,2024-03-26 14:28:43.825628+00:00,4079,4,6,5,promotersetsig/10690.csv.gz -10694,1,2024-03-26,1,2024-03-26 14:28:44.739775+00:00,4083,4,6,5,promotersetsig/10694.csv.gz -10754,1,2024-03-26,1,2024-03-26 14:29:01.837335+00:00,4143,4,6,5,promotersetsig/10754.csv.gz -10929,1,2024-03-26,1,2024-03-26 14:29:45.379790+00:00,4318,4,6,5,promotersetsig/10929.csv.gz -10939,1,2024-03-26,1,2024-03-26 14:29:47.853980+00:00,4327,4,6,5,promotersetsig/10939.csv.gz -""" diff --git a/tfbpapi/tests/snapshots/test_AbstractAPI/test_cache_operations/cache_get_after_delete b/tfbpapi/tests/snapshots/test_AbstractAPI/test_cache_operations/cache_get_after_delete deleted file mode 100644 index 4af1832..0000000 --- a/tfbpapi/tests/snapshots/test_AbstractAPI/test_cache_operations/cache_get_after_delete +++ /dev/null @@ -1 +0,0 @@ -None \ No newline at end of file diff --git a/tfbpapi/tests/snapshots/test_AbstractAPI/test_cache_operations/cache_get_after_set b/tfbpapi/tests/snapshots/test_AbstractAPI/test_cache_operations/cache_get_after_set deleted file mode 100644 index fff1c65..0000000 --- a/tfbpapi/tests/snapshots/test_AbstractAPI/test_cache_operations/cache_get_after_set +++ /dev/null @@ -1 +0,0 @@ -test_value \ No newline at end of file diff --git a/tfbpapi/tests/snapshots/test_AbstractAPI/test_cache_operations/cache_list b/tfbpapi/tests/snapshots/test_AbstractAPI/test_cache_operations/cache_list deleted file mode 100644 index 1950491..0000000 --- a/tfbpapi/tests/snapshots/test_AbstractAPI/test_cache_operations/cache_list +++ /dev/null @@ -1 +0,0 @@ -['test_key'] \ No newline at end of file diff --git a/tfbpapi/tests/snapshots/test_AbstractAPI/test_pop_params/pop_params_after_all_removed b/tfbpapi/tests/snapshots/test_AbstractAPI/test_pop_params/pop_params_after_all_removed deleted file mode 100644 index 9e26dfe..0000000 --- a/tfbpapi/tests/snapshots/test_AbstractAPI/test_pop_params/pop_params_after_all_removed +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/tfbpapi/tests/snapshots/test_AbstractAPI/test_pop_params/pop_params_after_one_removed b/tfbpapi/tests/snapshots/test_AbstractAPI/test_pop_params/pop_params_after_one_removed deleted file mode 100644 index cab5c0c..0000000 --- a/tfbpapi/tests/snapshots/test_AbstractAPI/test_pop_params/pop_params_after_one_removed +++ /dev/null @@ -1 +0,0 @@ -{"param2": "value2"} \ No newline at end of file diff --git a/tfbpapi/tests/snapshots/test_AbstractAPI/test_push_params/push_params b/tfbpapi/tests/snapshots/test_AbstractAPI/test_push_params/push_params deleted file mode 100644 index 21d59b6..0000000 --- a/tfbpapi/tests/snapshots/test_AbstractAPI/test_push_params/push_params +++ /dev/null @@ -1 +0,0 @@ -{"param1": "value1", "param2": "value2"} \ No newline at end of file diff --git a/tfbpapi/tests/test_AbstractAPI.py b/tfbpapi/tests/test_AbstractAPI.py deleted file mode 100644 index 84a643d..0000000 --- a/tfbpapi/tests/test_AbstractAPI.py +++ /dev/null @@ -1,94 +0,0 @@ -import json -from typing import Any - -import pytest -import responses - -from tfbpapi.AbstractAPI import AbstractAPI -from tfbpapi.ParamsDict import ParamsDict - - -class ConcreteAPI(AbstractAPI): - """Concrete implementation of AbstractAPI for testing purposes.""" - - def create(self, data: dict[str, Any], **kwargs) -> Any: - pass # Implement for testing if necessary - - def read(self, **kwargs) -> dict[str, Any]: - return {"id": id} # Mock implementation for testing - - def update(self, df: Any, **kwargs) -> Any: - pass # Implement for testing if necessary - - def delete(self, id: str, **kwargs) -> Any: - pass # Implement for testing if necessary - - def submit(self, post_dict: dict, **kwargs) -> Any: - pass # Implement for testing if necessary - - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Any: - pass # Implement for testing if necessary - - -@pytest.fixture -@responses.activate -def api_client(): - valid_url = "https://valid.url" - responses.add(responses.HEAD, valid_url, status=200) - return ConcreteAPI(url=valid_url, token="token") - - -def test_initialize(snapshot, api_client): - assert api_client.url == "https://valid.url" - assert api_client.token == "token" - assert isinstance(api_client.params, ParamsDict) - - -def test_push_params(snapshot, api_client): - params = {"param1": "value1", "param2": "value2"} - api_client.push_params(params) - # Serialize the dictionary to a JSON string for comparison - params_as_json = json.dumps(api_client.params.as_dict(), sort_keys=True) - snapshot.assert_match(params_as_json, "push_params") - - -def test_pop_params(snapshot, api_client): - params = {"param1": "value1", "param2": "value2"} - api_client.push_params(params) - api_client.pop_params(["param1"]) - params_as_json1 = json.dumps(api_client.params.as_dict(), sort_keys=True) - snapshot.assert_match(params_as_json1, "pop_params_after_one_removed") - api_client.pop_params() - params_as_json2 = json.dumps(api_client.params.as_dict(), sort_keys=True) - snapshot.assert_match(params_as_json2, "pop_params_after_all_removed") - - -@responses.activate -def test_is_valid_url(api_client): - invalid_url = "https://invalid.url" - - responses.add(responses.HEAD, invalid_url, status=404) - - with pytest.raises(ValueError): - api_client.url = invalid_url - - -def test_cache_operations(snapshot, api_client): - key = "test_key" - value = "test_value" - - api_client._cache_set(key, value) - snapshot.assert_match(str(api_client._cache_get(key)), "cache_get_after_set") - - keys = api_client._cache_list() - snapshot.assert_match(str(keys), "cache_list") - - api_client._cache_delete(key) - snapshot.assert_match(str(api_client._cache_get(key)), "cache_get_after_delete") - snapshot.assert_match(str(api_client._cache_get(key)), "cache_get_after_delete") - - -if __name__ == "__main__": - pytest.main() diff --git a/tfbpapi/tests/test_AbstractHfAPI.py b/tfbpapi/tests/test_AbstractHfAPI.py deleted file mode 100644 index 6f28eea..0000000 --- a/tfbpapi/tests/test_AbstractHfAPI.py +++ /dev/null @@ -1,435 +0,0 @@ -import tempfile -from collections.abc import Mapping -from pathlib import Path -from typing import Any -from unittest.mock import Mock, patch - -import pytest -from requests import HTTPError - -from tfbpapi.AbstractHfAPI import AbstractHfAPI, RepoTooLargeError - - -class TestHfAPI(AbstractHfAPI): - """Concrete implementation of AbstractHfAPI for testing.""" - - def parse_datacard(self, *args: Any, **kwargs: Any) -> Mapping[str, Any]: - """Test implementation of parse_datacard.""" - return {"test": "datacard"} - - def query(self, *args: Any, **kwargs: Any) -> Any: - """Test implementation of query.""" - return {"test": "query"} - - -@pytest.fixture -def mock_hf_hub_download(): - """Mock hf_hub_download to return a fake path.""" - with patch("tfbpapi.AbstractHfAPI.hf_hub_download") as mock: - mock.return_value = "/fake/path/to/file.txt" - yield mock - - -@pytest.fixture -def mock_snapshot_download(): - """Mock snapshot_download to return a fake path.""" - with patch("tfbpapi.AbstractHfAPI.snapshot_download") as mock: - mock.return_value = "/fake/path/to/snapshot" - yield mock - - -@pytest.fixture -def mock_repo_info(): - """Mock repo_info to return fake repo information.""" - with patch("tfbpapi.AbstractHfAPI.repo_info") as mock: - # Create a mock with siblings attribute - mock_info = Mock() - mock_info.siblings = [ - Mock(size=1024 * 1024), # 1MB file - Mock(size=512 * 1024), # 512KB file - Mock(size=None), # File with no size - ] - mock.return_value = mock_info - yield mock - - -@pytest.fixture -def mock_requests_get(): - """Mock requests.get for dataset size API calls.""" - with patch("tfbpapi.AbstractHfAPI.requests.get") as mock: - # Create a mock response - mock_response = Mock() - mock_response.json.return_value = { - "size": { - "dataset": { - "num_bytes_original_files": 10 * 1024 * 1024, # 10MB - "size_determination_complete": True, - } - }, - "partial": False, - } - mock_response.raise_for_status.return_value = None - mock.return_value = mock_response - yield mock - - -@pytest.fixture -def mock_dataset_size_call(): - """Mock the _get_dataset_size call to prevent real API calls during init.""" - with patch.object(AbstractHfAPI, "_get_dataset_size") as mock: - yield mock - - -@pytest.fixture -def temp_cache_dir(): - """Create a temporary directory for cache testing.""" - with tempfile.TemporaryDirectory() as temp_dir: - yield Path(temp_dir) - - -class TestAbstractHfAPI: - """Test cases for AbstractHfAPI.""" - - def test_init_basic(self, temp_cache_dir, mock_dataset_size_call): - """Test basic initialization.""" - api = TestHfAPI( - repo_id="test/repo", - repo_type="dataset", - token="test-token", - cache_dir=temp_cache_dir, - ) - - assert api.repo_id == "test/repo" - assert api.repo_type == "dataset" - assert api.token == "test-token" - assert api.cache_dir == temp_cache_dir - - def test_init_with_env_vars( - self, temp_cache_dir, monkeypatch, mock_dataset_size_call - ): - """Test initialization with environment variables.""" - monkeypatch.setenv("HF_TOKEN", "env-token") - monkeypatch.setenv("HF_CACHE_DIR", str(temp_cache_dir)) - - api = TestHfAPI(repo_id="test/repo") - - assert api.token == "env-token" - assert api.cache_dir == temp_cache_dir - - def test_init_user_overrides_env(self, temp_cache_dir, monkeypatch): - """Test that user parameters override environment variables.""" - monkeypatch.setenv("HF_TOKEN", "env-token") - - api = TestHfAPI( - repo_id="test/repo", token="user-token", cache_dir=temp_cache_dir - ) - - assert api.token == "user-token" - assert api.cache_dir == temp_cache_dir - - def test_cache_dir_setter_valid(self, temp_cache_dir): - """Test cache_dir setter with valid directory.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - - new_cache_dir = temp_cache_dir / "new_cache" - new_cache_dir.mkdir() - - api.cache_dir = new_cache_dir - assert api.cache_dir == new_cache_dir - - def test_cache_dir_setter_invalid(self, temp_cache_dir): - """Test cache_dir setter with invalid directory.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - - invalid_dir = temp_cache_dir / "nonexistent" - - with pytest.raises( - FileNotFoundError, match="Cache directory .* does not exist" - ): - api.cache_dir = invalid_dir - - @patch("tfbpapi.AbstractHfAPI.requests.get") - def test_get_dataset_size_success(self, mock_get, temp_cache_dir): - """Test successful dataset size retrieval.""" - mock_response = Mock() - mock_response.json.return_value = { - "size": {"dataset": {"num_bytes": 1024}}, - "partial": False, - } - mock_response.raise_for_status.return_value = None - mock_get.return_value = mock_response - - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - api._get_dataset_size() - - assert api.size is not None - assert not api.size.get("partial", True) - - @patch("tfbpapi.AbstractHfAPI.requests.get") - def test_get_dataset_size_partial(self, mock_get, temp_cache_dir): - """Test dataset size retrieval with partial results.""" - mock_response = Mock() - mock_response.json.return_value = { - "size": {"dataset": {"num_bytes": 1024}}, - "partial": True, - } - mock_response.raise_for_status.return_value = None - mock_get.return_value = mock_response - - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - api._get_dataset_size() - - assert api.size["partial"] is True # type: ignore[index] - assert "size_warning" in api.size["size"]["dataset"] # type: ignore[index] - - def test_repo_id_setter_success(self, mock_requests_get, temp_cache_dir): - """Test successful repo_id setting.""" - api = TestHfAPI(repo_id="initial/repo", cache_dir=temp_cache_dir) - - api.repo_id = "new/repo" - assert api.repo_id == "new/repo" - mock_requests_get.assert_called() - - @patch("tfbpapi.AbstractHfAPI.requests.get") - def test_repo_id_setter_failure(self, mock_get, temp_cache_dir): - """Test repo_id setting with API failure.""" - mock_get.side_effect = HTTPError("Repository not found") - - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - - # Should not raise, but should log error - api.repo_id = "nonexistent/repo" - assert api.repo_id == "nonexistent/repo" - - def test_get_dataset_size_mb(self, temp_cache_dir): - """Test dataset size calculation in MB.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - - # Test with no size data - assert api._get_dataset_size_mb() == float("inf") - - # Test with size data - api.size = { - "size": {"dataset": {"num_bytes_original_files": 2 * 1024 * 1024}} # 2MB - } - assert api._get_dataset_size_mb() == 2.0 - - def test_build_auth_headers(self, temp_cache_dir): - """Test authentication header building.""" - # Without token - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - api.token = None - assert api._build_auth_headers() == {} - - # With token - api.token = "test-token" - headers = api._build_auth_headers() - assert headers == {"Authorization": "Bearer test-token"} - - def test_ensure_str_paths(self, temp_cache_dir): - """Test path string conversion.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - - kwargs = { - "local_dir": Path("/some/path"), - "cache_dir": Path("/cache/path"), - "other_param": "unchanged", - } - - api._ensure_str_paths(kwargs) - - assert kwargs["local_dir"] == "/some/path" - assert kwargs["cache_dir"] == "/cache/path" - assert kwargs["other_param"] == "unchanged" - - def test_normalize_patterns(self, temp_cache_dir): - """Test pattern normalization.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - - kwargs = { - "allow_patterns": "*.txt", - "ignore_patterns": ["*.log", "*.tmp"], - "other_param": "unchanged", - } - - api._normalize_patterns(kwargs) - - assert kwargs["allow_patterns"] == ["*.txt"] - assert kwargs["ignore_patterns"] == ["*.log", "*.tmp"] - assert kwargs["other_param"] == "unchanged" - - def test_download_single_file(self, mock_hf_hub_download, temp_cache_dir): - """Test single file download.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - - result = api._download_single_file("test.txt") - - assert result == Path("/fake/path/to/file.txt") - mock_hf_hub_download.assert_called_once() - - # Check that correct arguments were passed - call_args = mock_hf_hub_download.call_args[1] - assert call_args["repo_id"] == "test/repo" - assert call_args["filename"] == "test.txt" - - def test_download_single_file_dry_run(self, mock_hf_hub_download, temp_cache_dir): - """Test single file download with dry run.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - - result = api._download_single_file("test.txt", dry_run=True) - - assert result == Path("dry_run_path") - mock_hf_hub_download.assert_not_called() - - def test_download_snapshot(self, mock_snapshot_download, temp_cache_dir): - """Test snapshot download.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - - result = api._download_snapshot(allow_patterns=["*.txt"]) - - assert result == Path("/fake/path/to/snapshot") - mock_snapshot_download.assert_called_once() - - def test_download_snapshot_dry_run(self, mock_snapshot_download, temp_cache_dir): - """Test snapshot download with dry run.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - - result = api._download_snapshot(dry_run=True, allow_patterns=["*.txt"]) - - assert result == Path("dry_run_path") - mock_snapshot_download.assert_not_called() - - def test_download_single_file_string(self, temp_cache_dir): - """Test download with single file as string.""" - # Create API instance by bypassing problematic initialization - api = TestHfAPI.__new__(TestHfAPI) # Create without calling __init__ - - # Manually set the required attributes - api._repo_id = "test/repo" - api.repo_type = "dataset" - api.token = None - api._cache_dir = temp_cache_dir - api.size = { - "size": {"dataset": {"num_bytes_original_files": 1024}} - } # Small size - api.logger = Mock() # Mock logger to avoid issues - - with patch("tfbpapi.AbstractHfAPI.hf_hub_download") as mock_hf_download: - # Configure the mock to return a fake path - mock_hf_download.return_value = "/fake/path/to/file.txt" - - result = api.download(files="test.txt", auto_download_threshold_mb=0) - - assert result == Path("/fake/path/to/file.txt") - mock_hf_download.assert_called_once() - - # Verify the call arguments - call_args = mock_hf_download.call_args[1] - assert call_args["repo_id"] == "test/repo" - assert call_args["filename"] == "test.txt" - - def test_download_single_file_list( - self, mock_hf_hub_download, temp_cache_dir, mock_dataset_size_call - ): - """Test download with single file as list.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - api.size = { - "size": {"dataset": {"num_bytes_original_files": 1024}} - } # Small size - - result = api.download(files=["test.txt"], auto_download_threshold_mb=0) - - assert result == Path("/fake/path/to/file.txt") - mock_hf_hub_download.assert_called_once() - - def test_download_multiple_files(self, mock_snapshot_download, temp_cache_dir): - """Test download with multiple files.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - api.size = { - "size": {"dataset": {"num_bytes_original_files": 1024}} - } # Small size - - result = api.download(files=["test1.txt", "test2.txt"]) - - assert result == Path("/fake/path/to/snapshot") - mock_snapshot_download.assert_called_once() - - def test_download_force_full(self, mock_snapshot_download, temp_cache_dir): - """Test download with force_full_download=True.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - api.size = { - "size": {"dataset": {"num_bytes_original_files": 1000 * 1024 * 1024}} - } # Large size - - result = api.download(force_full_download=True) - - assert result == Path("/fake/path/to/snapshot") - mock_snapshot_download.assert_called_once() - - def test_download_repo_too_large(self, temp_cache_dir): - """Test download with repo too large error.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - api.size = { - "size": {"dataset": {"num_bytes_original_files": 1000 * 1024 * 1024}} - } # 1GB - - with pytest.raises(RepoTooLargeError, match="Dataset size .* exceeds"): - api.download(auto_download_threshold_mb=10) - - def test_download_small_repo_auto(self, mock_snapshot_download, temp_cache_dir): - """Test download with small repo under threshold.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - api.size = { - "size": {"dataset": {"num_bytes_original_files": 5 * 1024 * 1024}} - } # 5MB - - result = api.download(auto_download_threshold_mb=10) - - assert result == Path("/fake/path/to/snapshot") - mock_snapshot_download.assert_called_once() - - def test_snapshot_path_property(self, temp_cache_dir): - """Test snapshot_path property getter and setter.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - - # Initially None - assert api.snapshot_path is None - - # Set to path - test_path = "/some/path" - api.snapshot_path = Path(test_path) - assert api.snapshot_path == Path(test_path) - - # Set to None - api.snapshot_path = None - assert api.snapshot_path is None - - def test_size_property(self, temp_cache_dir): - """Test size property getter and setter.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - - # Initially None - assert api.size is None - - # Set size data - size_data = {"size": {"dataset": {"num_bytes": 1024}}} - api.size = size_data - assert api.size == size_data - - def test_abstract_methods_implemented(self, temp_cache_dir): - """Test that abstract methods are properly implemented.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - - # Test parse_datacard - result = api.parse_datacard() - assert result == {"test": "datacard"} - - # Test query - result = api.query() - assert result == {"test": "query"} - - def test_repo_too_large_error(self): - """Test RepoTooLargeError exception.""" - error = RepoTooLargeError("Test error message") - assert str(error) == "Test error message" - assert isinstance(error, ValueError) diff --git a/tfbpapi/tests/test_AbstractRecordsAndFilesAPI.py b/tfbpapi/tests/test_AbstractRecordsAndFilesAPI.py deleted file mode 100644 index 1c64a39..0000000 --- a/tfbpapi/tests/test_AbstractRecordsAndFilesAPI.py +++ /dev/null @@ -1,284 +0,0 @@ -import gzip -from io import BytesIO -from tempfile import NamedTemporaryFile -from typing import Any - -import pandas as pd -import pytest -import responses -from aioresponses import aioresponses - -from tfbpapi.AbstractRecordsAndFilesAPI import ( - AbstractRecordsAndFilesAPI, -) - -# The following test is commented out because it requires a running server -- this is -# how I retrieved the data for the tests below. The data is saved in the snapshot -# directory -# -# @pytest.mark.asyncio -# async def test_save_response_records_and_files(snapshot): -# async with aiohttp.ClientSession() as session: -# url = "http://127.0.0.1:8001/api/promotersetsig/export" -# async with session.get( -# url, -# headers={ -# "Authorization": f"token {os.getenv('TOKEN')}", -# "Content-Type": "application/json", -# }, -# params={ -# "regulator_symbol": "HAP5", -# "workflow": "nf_core_callingcards_dev", -# "data_usable": "pass", -# }, -# ) as response: -# response.raise_for_status() -# response_text = await response.text() -# snapshot.assert_match(response_text) -# assert response.status == 200 - - -# @pytest.mark.asyncio -# async def test_save_response_records_and_files(): -# async with aiohttp.ClientSession() as session: -# url = "http://127.0.0.1:8001/api/promotersetsig/record_table_and_files" -# async with session.get( -# url, -# headers={ -# "Authorization": f"token {os.getenv('TOKEN')}", -# "Content-Type": "application/gzip", -# }, -# params={ -# "regulator_symbol": "HAP5", -# "workflow": "nf_core_callingcards_dev", -# "data_usable": "pass", -# }, -# ) as response: -# response.raise_for_status() -# response_content = await response.read() -# with open("saved_response.tar.gz", "wb") as f: -# f.write(response_content) -# assert response.status == 200 - - -def promotersetsig_csv_gzip() -> bytes: - # Define the data as a dictionary - data = { - "id": [10690, 10694, 10754, 10929, 10939], - "uploader_id": [1, 1, 1, 1, 1], - "upload_date": ["2024-03-26"] * 5, - "modifier_id": [1, 1, 1, 1, 1], - "modified_date": [ - "2024-03-26 14:28:43.825628+00:00", - "2024-03-26 14:28:44.739775+00:00", - "2024-03-26 14:29:01.837335+00:00", - "2024-03-26 14:29:45.379790+00:00", - "2024-03-26 14:29:47.853980+00:00", - ], - "binding_id": [4079, 4083, 4143, 4318, 4327], - "promoter_id": [4, 4, 4, 4, 4], - "background_id": [6, 6, 6, 6, 6], - "fileformat_id": [5, 5, 5, 5, 5], - "file": [ - "promotersetsig/10690.csv.gz", - "promotersetsig/10694.csv.gz", - "promotersetsig/10754.csv.gz", - "promotersetsig/10929.csv.gz", - "promotersetsig/10939.csv.gz", - ], - } - - # Create a DataFrame - df = pd.DataFrame(data) - - # Convert the DataFrame to CSV and compress it using gzip - csv_buffer = BytesIO() - with gzip.GzipFile(fileobj=csv_buffer, mode="w") as gz: - df.to_csv(gz, index=False) - - # Get the gzipped data as bytes - return csv_buffer.getvalue() - - -class ConcreteRecordsAndFilesAPI(AbstractRecordsAndFilesAPI): - """Concrete implementation of AbstractRecordsAndFilesAPI for testing purposes.""" - - def create(self, data: dict[str, Any], **kwargs) -> Any: - pass - - def update(self, df: Any, **kwargs) -> Any: - pass - - def delete(self, id: str, **kwargs) -> Any: - pass - - def submit(self, post_dict: dict, **kwargs) -> Any: - pass # Implement for testing if necessary - - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Any: - pass # Implement for testing if necessary - - -@pytest.fixture -@responses.activate -def api_client(): - valid_url = "http://127.0.0.1:8001/api/promotersetsig" - responses.add(responses.HEAD, valid_url, status=200) - return ConcreteRecordsAndFilesAPI(url=valid_url, token="my_token") - - -@pytest.mark.asyncio -async def test_read_without_files(snapshot, api_client): - with aioresponses() as m: - # Mock the HTTP response with the saved snapshot response - m.get( - "http://127.0.0.1:8001/api/promotersetsig/export", - status=200, - body=promotersetsig_csv_gzip(), - headers={"Content-Type": "application/gzip"}, - ) - - result = await api_client.read() - assert isinstance(result.get("metadata"), pd.DataFrame) - assert result.get("metadata").shape == ( - 5, - 10, - ) - - -# chatGPT and I went through many iterations of trying to mock two endpoints at once. -# no success. the retrieve_files is untested outside of the tutorial notebook as a -# result -# -# @pytest.mark.asyncio -# async def test_read_with_responses(snapshot, api_client): -# with responses.RequestsMock() as rsps: -# # Mock the /export endpoint -# rsps.add( -# responses.GET, -# "http://127.0.0.1:8001/api/promotersetsig/export", -# body=promotersetsig_csv_gzip(), -# status=200, -# content_type="text/csv", -# ) - -# # Path to the tar.gz file -# tar_gz_file_path = os.path.join( -# os.path.dirname(__file__), -# "snapshots", -# "promotersetsig_records_and_files.tar.gz", -# ) - -# # Read the content of the tar.gz file -# with open(tar_gz_file_path, "rb") as tar_gz_file: -# tar_gz_content = tar_gz_file.read() - -# # Mock the /record_table_and_files endpoint -# rsps.add( -# responses.GET, -# "http://127.0.0.1:8001/api/promotersetsig/record_table_and_files", -# body=tar_gz_content, -# status=200, -# content_type="application/gzip", -# ) - -# # Helper function to create a mock ClientResponse -# async def create_mock_response(url, method, body, content_type, status): -# return MockClientResponse( -# method, URL(url), status, {"Content-Type": content_type}, body -# ) - -# # Patch aiohttp.ClientSession.get to use our mocked responses -# async def mock_get(self, url, **kwargs): -# if "export" in url: -# return await create_mock_response( -# url, -# "GET", -# promotersetsig_csv_gzip().encode(), -# "text/csv", -# 200, -# ) -# elif "record_table_and_files" in url: -# return await create_mock_response( -# url, -# "GET", -# tar_gz_content, -# "application/gzip", -# 200, -# ) -# else: -# raise ValueError("Unexpected URL") - -# with patch("aiohttp.ClientSession.get", new=mock_get): -# # Test the read method without retrieving files -# result = await api_client.read() -# assert isinstance(result.get("metadata"), pd.DataFrame) -# assert result.get("metadata").shape == (5, 10) - -# # Test the read method with retrieving files -# result = await api_client.read(retrieve_files=True) -# assert isinstance(result.get("metadata"), pd.DataFrame) -# assert result.get("metadata").shape == (5, 10) -# assert isinstance(result.get("data"), dict) -# assert len(result.get("data")) == 5 -# assert all(isinstance(v, pd.DataFrame) \ -# for v in result.get("data").values()) - -# test the _detect_delimiter method #### - - -def test_detect_delimiter_errors(api_client): - # test that a FileNotFound error is raised if the file does not exist - with pytest.raises(FileNotFoundError): - api_client._detect_delimiter("non_existent_file.csv") - - with NamedTemporaryFile(mode="w", suffix=".csv.gz") as tmpfile: - tmpfile.write("col1,col2,col3\nval1,val2,val3") - tmpfile.flush() - tmpfile_path = tmpfile.name - - with pytest.raises(gzip.BadGzipFile): - api_client._detect_delimiter(tmpfile_path) - - -def test_comma_delimiter(api_client): - with NamedTemporaryFile(mode="w", suffix=".csv") as tmpfile: - tmpfile.write("col1,col2,col3\nval1,val2,val3") - tmpfile.flush() - tmpfile_path = tmpfile.name - - delimiter = api_client._detect_delimiter(tmpfile_path) - assert delimiter == "," - - -def test_tab_delimiter(api_client): - with NamedTemporaryFile(mode="w", suffix=".csv") as tmpfile: - tmpfile.write("col1\tcol2\tcol3\nval1\tval2\tval3") - tmpfile.flush() - tmpfile_path = tmpfile.name - - delimiter = api_client._detect_delimiter(tmpfile_path) - assert delimiter == "\t" - - -def test_space_delimiter(api_client): - with NamedTemporaryFile(mode="w", suffix=".csv") as tmpfile: - tmpfile.write("col1 col2 col3\nval1 val2 val3") - tmpfile.flush() - tmpfile_path = tmpfile.name - - delimiter = api_client._detect_delimiter(tmpfile_path) - assert delimiter == " " - - -def test_gzipped_file(api_client): - with NamedTemporaryFile(suffix=".csv.gz") as tmpfile: - with gzip.open(tmpfile.name, "wt") as gzfile: - gzfile.write("col1,col2,col3\nval1,val2,val3") - gzfile.flush() - tmpfile_path = tmpfile.name - - delimiter = api_client._detect_delimiter(tmpfile_path) - assert delimiter == "," diff --git a/tfbpapi/tests/test_AbstractRecordsOnlyAPI.py b/tfbpapi/tests/test_AbstractRecordsOnlyAPI.py deleted file mode 100644 index 1def39a..0000000 --- a/tfbpapi/tests/test_AbstractRecordsOnlyAPI.py +++ /dev/null @@ -1,71 +0,0 @@ -import gzip -from typing import Any - -import pandas as pd -import pytest -import responses -from aioresponses import aioresponses - -from tfbpapi.AbstractRecordsOnlyAPI import AbstractRecordsOnlyAPI - - -class ConcreteAPI(AbstractRecordsOnlyAPI): - """Concrete implementation of AbstractRecordsOnlyAPI for testing purposes.""" - - def create(self, data: dict[str, Any], **kwargs) -> Any: - pass # Implement for testing if necessary - - def update(self, df: Any, **kwargs) -> Any: - pass # Implement for testing if necessary - - def delete(self, id: str, **kwargs) -> Any: - pass # Implement for testing if necessary - - def submit(self, post_dict: dict, **kwargs) -> Any: - pass # Implement for testing if necessary - - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Any: - pass # Implement for testing if necessary - - -@pytest.fixture -@responses.activate -def api_client(): - valid_url = "https://example.com/api/endpoint" - responses.add(responses.HEAD, valid_url, status=200) - return ConcreteAPI(url=valid_url, token="my_token") - - -@pytest.mark.asyncio -async def test_read(snapshot, api_client): - with aioresponses() as m: - # Mocking the response - mocked_csv = ( - "id,uploader_id,upload_date,modifier_id,modified_date,binding_id,promoter_id,background_id,fileformat_id,file\n" # noqa: E501 - "10690,1,2024-03-26,1,2024-03-26 14:28:43.825628+00:00,4079,4,6,5,promotersetsig/10690.csv.gz\n" # noqa: E501 - "10694,1,2024-03-26,1,2024-03-26 14:28:44.739775+00:00,4083,4,6,5,promotersetsig/10694.csv.gz\n" # noqa: E501 - "10754,1,2024-03-26,1,2024-03-26 14:29:01.837335+00:00,4143,4,6,5,promotersetsig/10754.csv.gz\n" # noqa: E501 - "10929,1,2024-03-26,1,2024-03-26 14:29:45.379790+00:00,4318,4,6,5,promotersetsig/10929.csv.gz\n" # noqa: E501 - "10939,1,2024-03-26,1,2024-03-26 14:29:47.853980+00:00,4327,4,6,5,promotersetsig/10939.csv.gz" # noqa: E501 - ) - - # Convert to bytes and gzip the content - gzipped_csv = gzip.compress(mocked_csv.encode("utf-8")) - - m.get( - "https://example.com/api/endpoint/export", - status=200, - body=gzipped_csv, - headers={"Content-Type": "application/gzip"}, - ) - - result = await api_client.read() - assert isinstance(result, dict) - assert isinstance(result.get("metadata"), pd.DataFrame) - assert result.get("metadata").shape == (5, 10) # type: ignore - - -if __name__ == "__main__": - pytest.main() diff --git a/tfbpapi/tests/test_Cache.py b/tfbpapi/tests/test_Cache.py deleted file mode 100644 index a84eb37..0000000 --- a/tfbpapi/tests/test_Cache.py +++ /dev/null @@ -1,66 +0,0 @@ -import time - -import pytest - -from tfbpapi.Cache import Cache - - -def test_cache_set_and_get(): - cache = Cache() - cache.set("key1", "value1") - assert cache.get("key1") == "value1" - assert cache.get("key2", "default_value") == "default_value" - - -def test_cache_list(): - cache = Cache() - cache.set("key1", "value1") - cache.set("key2", "value2") - keys = cache.list() - assert "key1" in keys - assert "key2" in keys - - -def test_cache_delete(): - cache = Cache() - cache.set("key1", "value1") - cache.set("key2", "value2") - cache.delete("key1") - assert cache.get("key1") is None - assert cache.get("key2") == "value2" - - -def test_cache_ttl(): - cache = Cache(ttl=1) # TTL set to 1 second - cache.set("key1", "value1") - time.sleep(1.5) # Wait for TTL to expire - assert cache.get("key1") is None # Should be None after TTL expiry - - -def test_cache_lru(): - cache = Cache(maxsize=2) - cache.set("key1", "value1") - cache.set("key2", "value2") - cache.set("key3", "value3") # This should evict "key1" if LRU works - assert cache.get("key1") is None - assert cache.get("key2") == "value2" - assert cache.get("key3") == "value3" - - -def test_separate_cache_instances(): - cache1 = Cache() - cache2 = Cache() - - cache1.set("key1", "value1") - cache2.set("key2", "value2") - - # Ensure they don't share state - assert cache1.get("key1") == "value1" - assert cache1.get("key2") is None - - assert cache2.get("key2") == "value2" - assert cache2.get("key1") is None - - -if __name__ == "__main__": - pytest.main() diff --git a/tfbpapi/tests/test_HfQueryAPI.py b/tfbpapi/tests/test_HfQueryAPI.py deleted file mode 100644 index 1aeeaea..0000000 --- a/tfbpapi/tests/test_HfQueryAPI.py +++ /dev/null @@ -1,530 +0,0 @@ -import tempfile -from pathlib import Path -from unittest.mock import Mock, patch - -import pandas as pd -import pytest -from datasets import Dataset, DatasetDict - -from tfbpapi.HfQueryAPI import HfQueryAPI - - -@pytest.fixture -def mock_dataset_card(): - """Mock DatasetCard.load to return fake card data.""" - with patch("tfbpapi.HfQueryAPI.DatasetCard.load") as mock: - mock_card = Mock() - mock_card.data.to_dict.return_value = { - "configs": [ - { - "config_name": "default", - "dataset_info": { - "features": [ - { - "name": "text", - "dtype": "string", - "description": "Input text", - }, - { - "name": "label", - "dtype": "int64", - "description": "Classification label", - }, - ] - }, - "data_files": [ - {"path": "data/train.parquet", "split": "train"}, - {"path": "data/test.parquet", "split": "test"}, - ], - } - ] - } - mock.return_value = mock_card - yield mock - - -@pytest.fixture -def mock_load_dataset(): - """Mock load_dataset to return fake dataset.""" - with patch("tfbpapi.HfQueryAPI.load_dataset") as mock: - # Create mock dataset with sample data - mock_dataset_dict = DatasetDict( - { - "train": Dataset.from_pandas( - pd.DataFrame( - {"text": ["hello", "world", "test"], "label": [0, 1, 0]} - ) - ), - "test": Dataset.from_pandas( - pd.DataFrame({"text": ["sample", "data"], "label": [1, 0]}) - ), - } - ) - mock.return_value = mock_dataset_dict - yield mock - - -@pytest.fixture -def mock_duckdb(): - """Mock DuckDB connection.""" - with patch("tfbpapi.HfQueryAPI.duckdb.connect") as mock_connect: - mock_conn = Mock() - mock_result = Mock() - mock_result.fetchdf.return_value = pd.DataFrame({"count": [3]}) - mock_conn.execute.return_value = mock_result - mock_connect.return_value = mock_conn - yield mock_conn - - -@pytest.fixture -def mock_hf_api_init(): - """Mock AbstractHfAPI initialization to prevent real API calls.""" - # Instead of mocking the __init__, we'll mock the methods that cause issues - with patch( - "tfbpapi.AbstractHfAPI.AbstractHfAPI._get_dataset_size" - ) as mock_get_size: - mock_get_size.return_value = None - yield mock_get_size - - -@pytest.fixture -def temp_cache_dir(): - """Create a temporary directory for cache testing.""" - with tempfile.TemporaryDirectory() as temp_dir: - yield Path(temp_dir) - - -class TestHfQueryAPI: - """Test cases for HfQueryAPI.""" - - def test_init_with_auto_parse( - self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir - ): - """Test initialization with auto_parse_datacard=True.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - # Verify initialization - assert api.auto_download_threshold_mb == 100.0 - assert api._datasets == { - "default": { - "features": { - "text": {"dtype": "string", "description": "Input text"}, - "label": {"dtype": "int64", "description": "Classification label"}, - }, - "data_files": [ - {"path": "data/train.parquet", "split": "train"}, - {"path": "data/test.parquet", "split": "test"}, - ], - "config": { - "config_name": "default", - "dataset_info": { - "features": [ - { - "name": "text", - "dtype": "string", - "description": "Input text", - }, - { - "name": "label", - "dtype": "int64", - "description": "Classification label", - }, - ] - }, - "data_files": [ - {"path": "data/train.parquet", "split": "train"}, - {"path": "data/test.parquet", "split": "test"}, - ], - }, - "loaded": False, - } - } - mock_dataset_card.assert_called_once() - - def test_init_without_auto_parse( - self, mock_hf_api_init, mock_duckdb, temp_cache_dir - ): - """Test initialization with auto_parse_datacard=False.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=False - ) - - assert api._datasets == {} - - def test_init_parse_datacard_failure( - self, mock_hf_api_init, mock_duckdb, temp_cache_dir - ): - """Test initialization when parse_datacard fails.""" - with patch("tfbpapi.HfQueryAPI.DatasetCard.load") as mock_card: - mock_card.side_effect = Exception("Failed to load") - - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - assert api._datasets == {} - # Logger warning should have been called during init - - def test_datasets_property( - self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir - ): - """Test datasets property getter and setter.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - # Test getter - assert "default" in api.datasets - - # Test setter - new_datasets = {"custom": {"features": {}, "data_files": [], "loaded": False}} - api.datasets = new_datasets - assert api.datasets == new_datasets - - def test_available_tables( - self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir - ): - """Test available_tables property.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - assert api.available_tables == ["default"] - - def test_parse_datacard_success( - self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir - ): - """Test successful datacard parsing.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=False - ) - - result = api.parse_datacard() - - assert "default" in result - assert "features" in result["default"] - assert "text" in result["default"]["features"] - assert "label" in result["default"]["features"] - - def test_parse_datacard_failure( - self, mock_hf_api_init, mock_duckdb, temp_cache_dir - ): - """Test datacard parsing failure.""" - with patch("tfbpapi.HfQueryAPI.DatasetCard.load") as mock_card: - mock_card.side_effect = Exception("Load failed") - - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=False - ) - - result = api.parse_datacard() - - assert result == {} - - def test_ensure_dataset_loaded_not_found( - self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir - ): - """Test _ensure_dataset_loaded with non-existent table.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - with pytest.raises(ValueError, match="Table 'nonexistent' not found"): - api._ensure_dataset_loaded("nonexistent") - - def test_ensure_dataset_loaded_already_loaded( - self, - mock_hf_api_init, - mock_dataset_card, - mock_load_dataset, - mock_duckdb, - temp_cache_dir, - ): - """Test _ensure_dataset_loaded when dataset already loaded.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - # Pre-load a dataset - mock_dataset = Mock() - api._loaded_datasets["default"] = mock_dataset - - result = api._ensure_dataset_loaded("default") - assert result == mock_dataset - mock_load_dataset.assert_not_called() - - def test_ensure_dataset_loaded_download_and_load( - self, - mock_hf_api_init, - mock_dataset_card, - mock_load_dataset, - mock_duckdb, - temp_cache_dir, - ): - """Test _ensure_dataset_loaded with download and load.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - # Mock download method - ensure snapshot_path is None initially - api.snapshot_path = None - mock_download = Mock() - mock_download.return_value = Path("/fake/snapshot") - api.download = mock_download # type: ignore - - # Set side effect to simulate download setting snapshot_path - def download_side_effect(**kwargs): - api.snapshot_path = Path("/fake/snapshot") - return Path("/fake/snapshot") - - mock_download.side_effect = download_side_effect - - result = api._ensure_dataset_loaded("default") - - assert result == mock_load_dataset.return_value - mock_download.assert_called_once_with(auto_download_threshold_mb=100.0) - mock_load_dataset.assert_called_once() - assert api._datasets["default"]["loaded"] is True - - def test_query_with_table_name( - self, - mock_hf_api_init, - mock_dataset_card, - mock_load_dataset, - mock_duckdb, - temp_cache_dir, - ): - """Test query with explicit table name.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - # Mock the dataset loading - api.snapshot_path = Path("/fake/snapshot") - - result = api.query("SELECT * FROM default", table_name="default") - - assert isinstance(result, pd.DataFrame) - mock_duckdb.execute.assert_called_once_with("SELECT * FROM default") - - def test_query_infer_table_name( - self, - mock_hf_api_init, - mock_dataset_card, - mock_load_dataset, - mock_duckdb, - temp_cache_dir, - ): - """Test query with table name inference.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - # Mock the dataset loading - api.snapshot_path = Path("/fake/snapshot") - - result = api.query("SELECT * FROM default") - - assert isinstance(result, pd.DataFrame) - mock_duckdb.execute.assert_called_once_with("SELECT * FROM default") - - def test_describe_table( - self, - mock_hf_api_init, - mock_dataset_card, - mock_load_dataset, - mock_duckdb, - temp_cache_dir, - ): - """Test describe_table method.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - # Mock the dataset loading - api.snapshot_path = Path("/fake/snapshot") - - result = api.describe_table("default") - - assert isinstance(result, pd.DataFrame) - mock_duckdb.execute.assert_called_with("DESCRIBE default") - - def test_sample( - self, - mock_hf_api_init, - mock_dataset_card, - mock_load_dataset, - mock_duckdb, - temp_cache_dir, - ): - """Test sample method.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - # Mock the dataset loading - api.snapshot_path = Path("/fake/snapshot") - - result = api.sample("default", n=3) - - assert isinstance(result, pd.DataFrame) - mock_duckdb.execute.assert_called_with("SELECT * FROM default LIMIT 3") - - def test_count( - self, - mock_hf_api_init, - mock_dataset_card, - mock_load_dataset, - mock_duckdb, - temp_cache_dir, - ): - """Test count method.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - # Mock the dataset loading - api.snapshot_path = Path("/fake/snapshot") - - result = api.count("default") - - assert result == 3 - mock_duckdb.execute.assert_called_with("SELECT COUNT(*) as count FROM default") - - def test_get_columns( - self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir - ): - """Test get_columns method.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - result = api.get_columns("default") - assert result == ["text", "label"] - - def test_get_columns_not_found( - self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir - ): - """Test get_columns with non-existent table.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - with pytest.raises(ValueError, match="Table 'nonexistent' not found"): - api.get_columns("nonexistent") - - def test_context_manager( - self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir - ): - """Test context manager functionality.""" - with HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) as api: - assert api is not None - - # Verify close was called - mock_duckdb.close.assert_called_once() - - def test_close( - self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir - ): - """Test close method.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - api.close() - mock_duckdb.close.assert_called_once() - - def test_table_filters_basic( - self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir - ): - """Test basic table filter functionality.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - # Test setting and getting filters - assert api.get_table_filter("default") is None - - api.set_table_filter("default", "text = 'test'") - assert api.get_table_filter("default") == "text = 'test'" - - # Test removing filters - api.remove_table_filter("default") - assert api.get_table_filter("default") is None - - def test_table_filters_query_modification( - self, - mock_hf_api_init, - mock_dataset_card, - mock_load_dataset, - mock_duckdb, - temp_cache_dir, - ): - """Test that table filters modify queries correctly.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - # Mock the dataset loading - api.snapshot_path = Path("/fake/snapshot") - - # Set a filter - api.set_table_filter("default", "label = 1") - - # Execute a query - api.query("SELECT * FROM default", table_name="default") - - # Verify the query was modified to include the filter - expected_sql = "SELECT * FROM (SELECT * FROM default WHERE label = 1)" - mock_duckdb.execute.assert_called_once_with(expected_sql) - - def test_table_filters_no_modification_when_no_filters( - self, - mock_hf_api_init, - mock_dataset_card, - mock_load_dataset, - mock_duckdb, - temp_cache_dir, - ): - """Test that queries are not modified when no filters are set.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - # Mock the dataset loading - api.snapshot_path = Path("/fake/snapshot") - - # Execute a query without any filters - original_sql = "SELECT * FROM default" - api.query(original_sql, table_name="default") - - # Verify the query was not modified - mock_duckdb.execute.assert_called_once_with(original_sql) - - def test_apply_table_filters_method( - self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir - ): - """Test the _apply_table_filters method directly.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - # Test with no filters - sql = "SELECT * FROM default" - assert api._apply_table_filters(sql) == sql - - # Test with filter applied - api.set_table_filter("default", "text LIKE '%test%'") - modified_sql = api._apply_table_filters(sql) - expected = "SELECT * FROM (SELECT * FROM default WHERE text LIKE '%test%')" - assert modified_sql == expected - - # Test that already filtered queries don't get double-wrapped - already_filtered = "SELECT * FROM (SELECT * FROM default WHERE existing = 1)" - result = api._apply_table_filters(already_filtered) - # Should not modify since it already contains a filtered subquery - assert result == already_filtered diff --git a/tfbpapi/tests/test_ParamsDict.py b/tfbpapi/tests/test_ParamsDict.py deleted file mode 100644 index cccaeda..0000000 --- a/tfbpapi/tests/test_ParamsDict.py +++ /dev/null @@ -1,96 +0,0 @@ -import pytest -import requests -import responses - -from tfbpapi.ParamsDict import ParamsDict - - -def test_initialization(): - params = ParamsDict({"b": 2, "a": 1}, valid_keys=["a", "b"]) - assert params == {"a": 1, "b": 2} - - -def test_getitem(): - params = ParamsDict({"a": 1, "b": 2}, valid_keys=["a", "b"]) - assert params["a"] == 1 - assert params[["a", "b"]] == ParamsDict({"a": 1, "b": 2}) - with pytest.raises(KeyError): - _ = params["123"] # Changed from 123 to '123' - - -def test_setitem(): - params = ParamsDict({"a": 1}, valid_keys=["a", "b", "c", "d"]) - params.update({"b": 2}) - assert params == {"a": 1, "b": 2} - - params[["c", "d"]] = [3, 4] - assert params == {"a": 1, "b": 2, "c": 3, "d": 4} - - with pytest.raises(ValueError): - params[["e", "f"]] = [5] - - with pytest.raises(KeyError): - params[123] = 5 # type: ignore - - with pytest.raises(KeyError): - params.update({"d": 4, "e": 5}) - - -def test_delitem(): - params = ParamsDict({"a": 1, "b": 2}, valid_keys=["a", "b"]) - del params["a"] - assert params == {"b": 2} - with pytest.raises(KeyError): - del params["123"] # Changed from 123 to '123' - - -def test_repr(): - params = ParamsDict({"a": 1, "b": 2}, valid_keys=["a", "b"]) - assert repr(params) == "ParamsDict({'a': 1, 'b': 2})" - - -def test_str(): - params = ParamsDict({"a": 1, "b": 2}, valid_keys=["a", "b"]) - assert str(params) == "a: 1, b: 2" - - -def test_len(): - params = ParamsDict({"a": 1, "b": 2}, valid_keys=["a", "b", "c"]) - assert len(params) == 2 - params["c"] = 3 - assert len(params) == 3 - - -def test_keys_values_items(): - params = ParamsDict({"a": 1, "b": 2}, valid_keys=["a", "b"]) - assert set(params.keys()) == {"a", "b"} - assert set(params.values()) == {1, 2} - assert set(params.items()) == {("a", 1), ("b", 2)} - - -def test_clear(): - params = ParamsDict({"a": 1, "b": 2}, valid_keys=["a", "b"]) - params.clear() - assert len(params) == 0 - - -def test_as_dict(): - params = ParamsDict({"a": 1, "b": 2}, valid_keys=["a", "b"]) - assert params.as_dict() == {"a": 1, "b": 2} - - -@responses.activate -def test_requests_integration(): - params = ParamsDict({"a": 1, "b": 2}, valid_keys=["a", "b"]) - - url = "https://httpbin.org/get" - responses.add(responses.GET, url, json={"args": {"a": "1", "b": "2"}}, status=200) - - response = requests.get(url, params=params) - assert response.status_code == 200 - response_json = response.json() - assert response_json["args"] == {"a": "1", "b": "2"} - - -if __name__ == "__main__": - pytest.main() diff --git a/tfbpapi/tests/test_metric_arrays.py b/tfbpapi/tests/test_metric_arrays.py deleted file mode 100644 index 45a8203..0000000 --- a/tfbpapi/tests/test_metric_arrays.py +++ /dev/null @@ -1,194 +0,0 @@ -import logging - -import numpy as np -import pandas as pd -import pytest - -from tfbpapi.metric_arrays import metric_arrays - - -def test_metric_arrays_expected_result(caplog): - res_dict = { - "metadata": pd.DataFrame( - { - "id": ["A", "B"], - "regulator_symbol": ["tf1", "tf2"], - } - ), - "data": { - "A": pd.DataFrame( - { - "target_symbol": ["gene1", "gene2"], - "metric1": [1.0, 2.0], - } - ), - "B": pd.DataFrame( - { - "target_symbol": ["gene2", "gene1"], - "metric1": [3.0, 4.0], - } - ), - }, - } - metrics_dict = {"metric1": np.mean} - - # Run function - with caplog.at_level(logging.WARNING): - output_dict = metric_arrays(res_dict, metrics_dict) - - # Check expected result for metric1 - # order based on the index of output_dict['metrics1'] since the ordering of - # the rows is random due to the set operation - expected_df = pd.DataFrame( - {"tf1": [1.0, 2.0], "tf2": [4.0, 3.0]}, - index=pd.Index(["gene1", "gene2"], name="target_symbol"), - ).reindex(output_dict["metric1"].index) - - pd.testing.assert_frame_equal(output_dict["metric1"], expected_df) - - # Check no warning since there are no incomplete rows or columns - assert "incomplete" not in caplog.text - - -def test_metric_arrays_missing_data(caplog): - res_dict = { - "metadata": pd.DataFrame( - { - "id": ["A", "B"], - "regulator_symbol": ["tf1", "tf2"], - } - ), - "data": { - "A": pd.DataFrame( - { - "target_symbol": ["gene1", "gene2"], - "metric1": [1.0, 2.0], - } - ), - "B": pd.DataFrame( - { - "target_symbol": ["gene1", "gene3"], - "metric1": [5.0, 3.0], - } - ), - }, - } - metrics_dict = {"metric1": np.mean} - - # Run function with incomplete row dropping - with caplog.at_level(logging.WARNING): - output_dict1 = metric_arrays(res_dict, metrics_dict, drop_incomplete_rows=False) - - # Check result for metric1 with "gene2" dropped due to missing data in B - # sort based on output_dict['metric1'] index since - # the ordering of the rows is random - expected_df1 = pd.DataFrame( - {"tf1": [1.0, 2.0, np.nan], "tf2": [5.0, np.nan, 3.0]}, - index=pd.Index(["gene1", "gene2", "gene3"], name="target_symbol"), - ).reindex(output_dict1["metric1"].index) - - pd.testing.assert_frame_equal(output_dict1["metric1"], expected_df1) - - # Run function with incomplete row dropping - with caplog.at_level(logging.WARNING): - output_dict2 = metric_arrays(res_dict, metrics_dict, drop_incomplete_rows=True) - - # Check result for metric1 with "gene2" dropped due to missing data in B - expected_df2 = pd.DataFrame( - {"tf1": [1.0], "tf2": [5.0]}, - index=pd.Index(["gene1"], name="target_symbol"), - ).reindex(output_dict2["metric1"].index) - - pd.testing.assert_frame_equal(output_dict2["metric1"], expected_df2) - - # Check warning for incomplete rows - assert "2 rows and 0 columns with incomplete records were dropped" in caplog.text - - -def test_metric_arrays_missing_keys(): - res_dict = { - "metadata": pd.DataFrame( - {"id": ["A"], "target_symbol": ["gene1"], "regulator_symbol": ["tf1"]} - ), - # Missing data for id "A" - "data": {}, - } - metrics_dict = {"metric1": np.mean} - - # Expect a KeyError for missing data keys - with pytest.raises(KeyError, match="Data dictionary must have the same keys"): - metric_arrays(res_dict, metrics_dict) - - -def test_metric_arrays_non_dataframe_value(): - res_dict = { - "metadata": pd.DataFrame( - {"id": ["A"], "target_symbol": ["gene1"], "regulator_symbol": ["tf1"]} - ), - "data": {"A": [1, 2, 3]}, # Invalid non-DataFrame entry - } - metrics_dict = {"metric1": np.mean} - - # Expect ValueError when data dictionary values are not DataFrames - with pytest.raises( - ValueError, match="All values in the data dictionary must be DataFrames" - ): - metric_arrays(res_dict, metrics_dict) - - -def test_metric_arrays_duplicate_rows_without_dedup_func(): - res_dict = { - "metadata": pd.DataFrame( - { - "id": ["A"], - "target_symbol": ["gene1"], - "regulator_symbol": ["tf1"], - } - ), - "data": { - "A": pd.DataFrame( - { - "target_symbol": ["gene1", "gene1"], - "metric1": [1.0, 2.0], - } - ), - }, - } - metrics_dict = {"metric1": None} # No deduplication function provided - - # Expect a ValueError due to duplicate rows without deduplication function - # - with pytest.raises( - ValueError, match="Duplicate entries found for metric 'metric1'" - ): - metric_arrays(res_dict, metrics_dict) # type: ignore - - -def test_metric_arrays_deduplication_function(): - res_dict = { - "metadata": pd.DataFrame( - { - "id": ["A"], - "target_symbol": ["gene1"], - "regulator_symbol": ["tf1"], - } - ), - "data": { - "A": pd.DataFrame( - { - "target_symbol": ["gene1", "gene1"], - "metric1": [1.0, 2.0], - } - ), - }, - } - metrics_dict = {"metric1": np.mean} # Deduplication function to average duplicates - - # Run function with deduplication - output_dict = metric_arrays(res_dict, metrics_dict) - - # Check that duplicates were averaged correctly - expected_df = pd.DataFrame( - {"tf1": [1.5]}, pd.Index(["gene1"], name="target_symbol") - ) - pd.testing.assert_frame_equal(output_dict["metric1"], expected_df) diff --git a/tfbpapi/tests/test_rank_transforms.py b/tfbpapi/tests/test_rank_transforms.py deleted file mode 100644 index 31dbeaa..0000000 --- a/tfbpapi/tests/test_rank_transforms.py +++ /dev/null @@ -1,80 +0,0 @@ -import numpy as np -from scipy.stats import rankdata - -from tfbpapi.rank_transforms import ( - shifted_negative_log_ranks, - transform, -) - - -def test_shifted_negative_log_ranks_basic(): - ranks = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) - expected_log_ranks = -1 * np.log10(ranks) + np.log10(np.max(ranks)) - - actual_log_ranks = shifted_negative_log_ranks(ranks) - np.testing.assert_array_almost_equal(actual_log_ranks, expected_log_ranks) - - -def test_shifted_negative_log_ranks_with_ties(): - ranks = np.array([1.0, 2.5, 2.5, 3.0, 4.0]) - expected_log_ranks = -1 * np.log10(ranks) + np.log10(np.max(ranks)) - - actual_log_ranks = shifted_negative_log_ranks(ranks) - np.testing.assert_array_almost_equal(actual_log_ranks, expected_log_ranks) - - -def test_negative_log_transform_basic(): - pvalues = np.array([0.01, 0.05, 0.01, 0.02, 0.05]) - enrichment = np.array([5.0, 3.0, 6.0, 4.0, 4.5]) - - # Expected ranks based on pvalue (primary) with enrichment (secondary) tie-breaking - expected_ranks = np.array([2.0, 5.0, 1.0, 3.0, 4.0]) - expected_log_ranks = -1 * np.log10(expected_ranks) + np.log10( - np.max(expected_ranks) - ) - - actual_log_ranks = transform(pvalues, enrichment) - np.testing.assert_array_almost_equal(actual_log_ranks, expected_log_ranks) - - -def test_all_ties_in_primary_column(): - pvalues = np.array([0.01, 0.01, 0.01, 0.01]) - enrichment = np.array([10.0, 20.0, 15.0, 5.0]) - - # With all pvalues tied, the ranking should depend solely - # on enrichment (higher is better) - expected_secondary_ranks = rankdata(-enrichment, method="average") - expected_log_ranks = -1 * np.log10(expected_secondary_ranks) + np.log10( - np.max(expected_secondary_ranks) - ) - - actual_log_ranks = transform(pvalues, enrichment) - np.testing.assert_array_almost_equal(actual_log_ranks, expected_log_ranks) - - -def test_no_ties_in_primary_column(): - pvalues = np.array([0.01, 0.02, 0.03, 0.04]) - enrichment = np.array([5.0, 10.0, 15.0, 20.0]) - - # With no ties in pvalue, the secondary column should have no effect - expected_ranks = rankdata(pvalues, method="average") - expected_log_ranks = -1 * np.log10(expected_ranks) + np.log10( - np.max(expected_ranks) - ) - - actual_log_ranks = transform(pvalues, enrichment) - np.testing.assert_array_almost_equal(actual_log_ranks, expected_log_ranks) - - -def test_tied_in_both_pvalue_and_enrichment(): - pvalues = np.array([0.01, 0.05, 0.01, 0.02, 0.05]) - enrichment = np.array([5.0, 3.0, 5.0, 4.0, 3.0]) - - # With ties in both primary and secondary columns - expected_ranks = np.array([1.5, 4.5, 1.5, 3.0, 4.5]) - expected_log_ranks = -1 * np.log10(expected_ranks) + np.log10( - np.max(expected_ranks) - ) - - actual_log_ranks = transform(pvalues, enrichment) - np.testing.assert_array_almost_equal(actual_log_ranks, expected_log_ranks) From fff878d0fd4fb4fb8fff8364ed1478e2f15a5fc3 Mon Sep 17 00:00:00 2001 From: chasem Date: Wed, 17 Sep 2025 20:23:22 -0500 Subject: [PATCH 06/49] this is getting closer to what im after. datainfo is close to finished. documentation works and is updated --- tfbpapi/HfQueryAPI.py | 22 +- tfbpapi/datainfo/datacard.py | 79 +++--- tfbpapi/datainfo/fetchers.py | 64 +++-- tfbpapi/datainfo/models.py | 52 ++-- tfbpapi/errors.py | 33 +-- tfbpapi/tests/datainfo/__init__.py | 2 +- tfbpapi/tests/datainfo/conftest.py | 110 ++++--- tfbpapi/tests/datainfo/test_datacard.py | 363 +++++++++++++++++------- tfbpapi/tests/datainfo/test_fetchers.py | 71 +++-- tfbpapi/tests/datainfo/test_models.py | 114 +++++--- 10 files changed, 572 insertions(+), 338 deletions(-) diff --git a/tfbpapi/HfQueryAPI.py b/tfbpapi/HfQueryAPI.py index 98c380a..4eee06d 100644 --- a/tfbpapi/HfQueryAPI.py +++ b/tfbpapi/HfQueryAPI.py @@ -855,6 +855,7 @@ def get_cache_info(self) -> dict[str, Any]: Get comprehensive cache information including current repo details. :return: Dictionary with cache stats, repo info, and recommendations + """ if not self._cache_manager: self.logger.debug("Cache management is disabled") @@ -967,6 +968,7 @@ def get_repo_cache_info(self, repo_id: str | None = None) -> dict[str, Any]: :param repo_id: Repository ID (defaults to current repo) :return: Dictionary with detailed repo cache information + """ if not self._cache_manager: self.logger.debug("Cache management is disabled") @@ -1047,6 +1049,7 @@ def check_cached_files(self, table_name: str | None = None) -> dict[str, Any]: :param table_name: Specific table to check (defaults to all tables) :return: Dictionary with file cache status + """ if not self._cache_manager: self.logger.debug("Cache management is disabled") @@ -1157,6 +1160,7 @@ def cleanup_cache( :param keep_current_repo: Whether to preserve current repo from cleanup :param dry_run: If True, show what would be deleted without executing :return: Dictionary with cleanup results and summary + """ if not self._cache_manager: self.logger.warning("Cache management is disabled, cannot perform cleanup") @@ -1315,9 +1319,11 @@ def auto_cleanup_cache_if_needed(self) -> dict[str, Any]: """ Automatically clean cache if configured policies are exceeded. - This method is called automatically during operations if auto_cleanup is enabled. + This method is called automatically during operations if auto_cleanup is + enabled. :return: Dictionary with cleanup results or None if no cleanup was needed + """ if not self._cache_manager or not getattr(self, "_cache_auto_cleanup", False): self.logger.debug("Auto-cleanup is disabled") @@ -1377,6 +1383,7 @@ def suggest_cache_cleanup(self) -> dict[str, Any]: Analyze cache and provide cleanup recommendations without executing. :return: Dictionary with analysis and recommendations + """ if not self._cache_manager: return {"cache_management": "disabled"} @@ -1491,9 +1498,11 @@ def warm_cache( :param repo_ids: List of repository IDs to pre-download :param tables: List of table names from current repo to pre-download - :param include_current_repo: Whether to include current repo if repo_ids specified + :param include_current_repo: Whether to include current repo if repo_ids + specified :param dry_run: If True, show what would be downloaded without executing :return: Dictionary with warming results + """ if not self._cache_manager: return {"cache_management": "disabled"} @@ -1607,6 +1616,7 @@ def verify_cache_integrity(self) -> dict[str, Any]: Verify integrity of cached files and detect corruption. :return: Dictionary with verification results + """ if not self._cache_manager: return {"cache_management": "disabled"} @@ -1695,7 +1705,7 @@ def verify_cache_integrity(self) -> dict[str, Any]: "message": f"File size mismatch in {file_info.file_name}", } ) - except (OSError, IOError) as e: + except OSError as e: repo_issues.append( { "type": "access_error", @@ -1769,6 +1779,7 @@ def migrate_cache(self, new_cache_dir: str | Path) -> dict[str, Any]: :param new_cache_dir: Target directory for cache migration :return: Dictionary with migration results + """ if not self._cache_manager: return {"cache_management": "disabled"} @@ -1888,6 +1899,7 @@ def configure_cache_policies( :param max_size: Maximum total cache size (e.g., "10GB") :param save_to_env: Save configuration to environment variables :return: Dictionary with updated configuration + """ if not self._cache_manager: return {"cache_management": "disabled"} @@ -1948,6 +1960,7 @@ def get_cache_configuration(self) -> dict[str, Any]: Get current cache configuration including environment variables. :return: Dictionary with comprehensive cache configuration + """ import os @@ -2016,6 +2029,7 @@ def reset_cache_configuration( :param remove_env_vars: Also remove related environment variables :return: Dictionary with reset results + """ if not self._cache_manager: return {"cache_management": "disabled"} @@ -2064,6 +2078,7 @@ def apply_cache_policy_from_env(self) -> dict[str, Any]: Apply cache policies from environment variables. :return: Dictionary with applied configuration + """ if not self._cache_manager: return {"cache_management": "disabled"} @@ -2112,6 +2127,7 @@ def export_cache_configuration(self, format: str = "env") -> dict[str, Any]: :param format: Export format - "env", "json", "yaml" :return: Dictionary with exported configuration + """ if not self._cache_manager: return {"cache_management": "disabled"} diff --git a/tfbpapi/datainfo/datacard.py b/tfbpapi/datainfo/datacard.py index e59fc4c..c58cd2d 100644 --- a/tfbpapi/datainfo/datacard.py +++ b/tfbpapi/datainfo/datacard.py @@ -21,17 +21,21 @@ class DataCard: - """Easy-to-use interface for exploring HuggingFace dataset metadata. + """ + Easy-to-use interface for exploring HuggingFace dataset metadata. + + Provides methods to discover and explore dataset contents, configurations, and + metadata without loading the actual genomic data. - Provides methods to discover and explore dataset contents, configurations, - and metadata without loading the actual genomic data. """ - def __init__(self, repo_id: str, token: Optional[str] = None): - """Initialize DataCard for a repository. + def __init__(self, repo_id: str, token: str | None = None): + """ + Initialize DataCard for a repository. :param repo_id: HuggingFace repository identifier (e.g., "user/dataset") :param token: Optional HuggingFace token for authentication + """ self.repo_id = repo_id self.token = token @@ -43,8 +47,8 @@ def __init__(self, repo_id: str, token: Optional[str] = None): self._size_fetcher = HfSizeInfoFetcher(token=token) # Cache for parsed card - self._dataset_card: Optional[DatasetCard] = None - self._metadata_cache: Dict[str, List[ExtractedMetadata]] = {} + self._dataset_card: DatasetCard | None = None + self._metadata_cache: dict[str, list[ExtractedMetadata]] = {} @property def dataset_card(self) -> DatasetCard: @@ -75,68 +79,77 @@ def _load_and_validate_card(self) -> None: # Create a more user-friendly error message error_details = [] for error in e.errors(): - field_path = " -> ".join(str(x) for x in error['loc']) - error_type = error['type'] - error_msg = error['msg'] - input_value = error.get('input', 'N/A') + field_path = " -> ".join(str(x) for x in error["loc"]) + error_type = error["type"] + error_msg = error["msg"] + input_value = error.get("input", "N/A") - if 'dtype' in field_path and error_type == 'string_type': + if "dtype" in field_path and error_type == "string_type": error_details.append( f"Field '{field_path}': Expected a simple data type string (like 'string', 'int64', 'float64') " f"but got a complex structure. This might be a categorical field with class labels. " f"Actual value: {input_value}" ) else: - error_details.append(f"Field '{field_path}': {error_msg} (got: {input_value})") + error_details.append( + f"Field '{field_path}': {error_msg} (got: {input_value})" + ) - detailed_msg = f"Dataset card validation failed for {self.repo_id}:\n" + "\n".join(f" - {detail}" for detail in error_details) + detailed_msg = ( + f"Dataset card validation failed for {self.repo_id}:\n" + + "\n".join(f" - {detail}" for detail in error_details) + ) self.logger.error(detailed_msg) raise DataCardValidationError(detailed_msg) from e except HfDataFetchError as e: raise DataCardError(f"Failed to fetch dataset card: {e}") from e @property - def configs(self) -> List[DatasetConfig]: + def configs(self) -> list[DatasetConfig]: """Get all dataset configurations.""" return self.dataset_card.configs - def get_config(self, config_name: str) -> Optional[DatasetConfig]: + def get_config(self, config_name: str) -> DatasetConfig | None: """Get a specific configuration by name.""" return self.dataset_card.get_config_by_name(config_name) def get_configs_by_type( - self, dataset_type: Union[DatasetType, str] - ) -> List[DatasetConfig]: + self, dataset_type: DatasetType | str + ) -> list[DatasetConfig]: """Get configurations by dataset type.""" if isinstance(dataset_type, str): dataset_type = DatasetType(dataset_type) return self.dataset_card.get_configs_by_type(dataset_type) - def get_regulators(self, config_name: Optional[str] = None) -> Set[str]: - """Get all regulators mentioned in the dataset. + def get_regulators(self, config_name: str | None = None) -> set[str]: + """ + Get all regulators mentioned in the dataset. :param config_name: Optional specific config to search, otherwise searches all :return: Set of regulator identifiers found + """ raise NotImplementedError("Method not yet implemented") - def get_experimental_conditions( - self, config_name: Optional[str] = None - ) -> Set[str]: - """Get all experimental conditions mentioned in the dataset. + def get_experimental_conditions(self, config_name: str | None = None) -> set[str]: + """ + Get all experimental conditions mentioned in the dataset. :param config_name: Optional specific config to search, otherwise searches all :return: Set of experimental conditions found + """ raise NotImplementedError("Method not yet implemented") - def get_field_values(self, config_name: str, field_name: str) -> Set[str]: - """Get all unique values for a specific field in a configuration. + def get_field_values(self, config_name: str, field_name: str) -> set[str]: + """ + Get all unique values for a specific field in a configuration. :param config_name: Configuration name :param field_name: Field name to extract values from :return: Set of unique values :raises DataCardError: If config or field not found + """ config = self.get_config(config_name) if not config: @@ -151,7 +164,7 @@ def get_field_values(self, config_name: str, field_name: str) -> Set[str]: return self._extract_field_values(config, field_name) - def _extract_field_values(self, config: DatasetConfig, field_name: str) -> Set[str]: + def _extract_field_values(self, config: DatasetConfig, field_name: str) -> set[str]: """Extract unique values for a field from various sources.""" values = set() @@ -189,7 +202,7 @@ def _extract_field_values(self, config: DatasetConfig, field_name: str) -> Set[s def _extract_partition_values( self, config: DatasetConfig, field_name: str - ) -> Set[str]: + ) -> set[str]: """Extract values from partition structure.""" if ( not config.dataset_info.partitioning @@ -211,7 +224,7 @@ def _extract_partition_values( self.logger.warning(f"Failed to extract partition values for {field_name}") return set() - def get_metadata_relationships(self) -> List[MetadataRelationship]: + def get_metadata_relationships(self) -> list[MetadataRelationship]: """Get relationships between data configs and their metadata.""" relationships = [] data_configs = self.dataset_card.get_data_configs() @@ -233,7 +246,6 @@ def get_metadata_relationships(self) -> List[MetadataRelationship]: ) continue - # Check for embedded metadata if data_config.metadata_fields: relationships.append( @@ -246,8 +258,7 @@ def get_metadata_relationships(self) -> List[MetadataRelationship]: return relationships - - def get_repository_info(self) -> Dict[str, Any]: + def get_repository_info(self) -> dict[str, Any]: """Get general repository information.""" card = self.dataset_card @@ -273,13 +284,13 @@ def get_repository_info(self) -> Dict[str, Any]: "has_default_config": self.dataset_card.get_default_config() is not None, } - def explore_config(self, config_name: str) -> Dict[str, Any]: + def explore_config(self, config_name: str) -> dict[str, Any]: """Get detailed information about a specific configuration.""" config = self.get_config(config_name) if not config: raise DataCardError(f"Configuration '{config_name}' not found") - info: Dict[str, Any] = { + info: dict[str, Any] = { "config_name": config.config_name, "description": config.description, "dataset_type": config.dataset_type.value, diff --git a/tfbpapi/datainfo/fetchers.py b/tfbpapi/datainfo/fetchers.py index 76b33fc..e33ac05 100644 --- a/tfbpapi/datainfo/fetchers.py +++ b/tfbpapi/datainfo/fetchers.py @@ -15,21 +15,25 @@ class HfDataCardFetcher: """Handles fetching dataset cards from HuggingFace Hub.""" - def __init__(self, token: Optional[str] = None): - """Initialize the fetcher. + def __init__(self, token: str | None = None): + """ + Initialize the fetcher. :param token: HuggingFace token for authentication + """ self.logger = logging.getLogger(self.__class__.__name__) self.token = token or os.getenv("HF_TOKEN") - def fetch(self, repo_id: str, repo_type: str = "dataset") -> Dict[str, Any]: - """Fetch and return dataset card data. + def fetch(self, repo_id: str, repo_type: str = "dataset") -> dict[str, Any]: + """ + Fetch and return dataset card data. :param repo_id: Repository identifier (e.g., "user/dataset") :param repo_type: Type of repository ("dataset", "model", "space") :return: Dataset card data as dictionary :raises HfDataFetchError: If fetching fails + """ try: self.logger.debug(f"Fetching dataset card for {repo_id}") @@ -50,28 +54,32 @@ def fetch(self, repo_id: str, repo_type: str = "dataset") -> Dict[str, Any]: class HfSizeInfoFetcher: """Handles fetching size information from HuggingFace Dataset Server API.""" - def __init__(self, token: Optional[str] = None): - """Initialize the fetcher. + def __init__(self, token: str | None = None): + """ + Initialize the fetcher. :param token: HuggingFace token for authentication + """ self.logger = logging.getLogger(self.__class__.__name__) self.token = token or os.getenv("HF_TOKEN") self.base_url = "https://datasets-server.huggingface.co" - def _build_headers(self) -> Dict[str, str]: + def _build_headers(self) -> dict[str, str]: """Build request headers with authentication if available.""" headers = {"User-Agent": "TFBP-API/1.0"} if self.token: headers["Authorization"] = f"Bearer {self.token}" return headers - def fetch(self, repo_id: str) -> Dict[str, Any]: - """Fetch dataset size information. + def fetch(self, repo_id: str) -> dict[str, Any]: + """ + Fetch dataset size information. :param repo_id: Repository identifier (e.g., "user/dataset") :return: Size information as dictionary :raises HfDataFetchError: If fetching fails + """ url = f"{self.base_url}/size" params = {"dataset": repo_id} @@ -113,22 +121,26 @@ def fetch(self, repo_id: str) -> Dict[str, Any]: class HfRepoStructureFetcher: """Handles fetching repository structure from HuggingFace Hub.""" - def __init__(self, token: Optional[str] = None): - """Initialize the fetcher. + def __init__(self, token: str | None = None): + """ + Initialize the fetcher. :param token: HuggingFace token for authentication + """ self.logger = logging.getLogger(self.__class__.__name__) self.token = token or os.getenv("HF_TOKEN") - self._cached_structure: Dict[str, Dict[str, Any]] = {} + self._cached_structure: dict[str, dict[str, Any]] = {} - def fetch(self, repo_id: str, force_refresh: bool = False) -> Dict[str, Any]: - """Fetch repository structure information. + def fetch(self, repo_id: str, force_refresh: bool = False) -> dict[str, Any]: + """ + Fetch repository structure information. :param repo_id: Repository identifier (e.g., "user/dataset") :param force_refresh: If True, bypass cache and fetch fresh data :return: Repository structure information :raises HfDataFetchError: If fetching fails + """ # Check cache first unless force refresh is requested if not force_refresh and repo_id in self._cached_structure: @@ -141,7 +153,7 @@ def fetch(self, repo_id: str, force_refresh: bool = False) -> Dict[str, Any]: # Extract file structure files = [] - partitions: Dict[str, set] = {} + partitions: dict[str, set] = {} for sibling in info.siblings or []: file_info = { @@ -174,12 +186,14 @@ def fetch(self, repo_id: str, force_refresh: bool = False) -> Dict[str, Any]: raise HfDataFetchError(error_msg) from e def _extract_partition_info( - self, file_path: str, partitions: Dict[str, Set[str]] + self, file_path: str, partitions: dict[str, set[str]] ) -> None: - """Extract partition information from file paths. + """ + Extract partition information from file paths. :param file_path: Path to analyze for partitions :param partitions: Dictionary to update with partition info + """ # Look for partition patterns like "column=value" in path partition_pattern = r"([^/=]+)=([^/]+)" @@ -190,29 +204,35 @@ def _extract_partition_info( partitions[column] = set() partitions[column].add(value) - def get_partition_values(self, repo_id: str, partition_column: str, force_refresh: bool = False) -> List[str]: - """Get all values for a specific partition column. + def get_partition_values( + self, repo_id: str, partition_column: str, force_refresh: bool = False + ) -> list[str]: + """ + Get all values for a specific partition column. :param repo_id: Repository identifier :param partition_column: Name of the partition column :param force_refresh: If True, bypass cache and fetch fresh data :return: List of unique partition values :raises HfDataFetchError: If fetching fails + """ structure = self.fetch(repo_id, force_refresh=force_refresh) partition_values = structure.get("partitions", {}).get(partition_column, set()) return sorted(list(partition_values)) def get_dataset_files( - self, repo_id: str, path_pattern: Optional[str] = None, force_refresh: bool = False - ) -> List[Dict[str, Any]]: - """Get dataset files, optionally filtered by path pattern. + self, repo_id: str, path_pattern: str | None = None, force_refresh: bool = False + ) -> list[dict[str, Any]]: + """ + Get dataset files, optionally filtered by path pattern. :param repo_id: Repository identifier :param path_pattern: Optional regex pattern to filter files :param force_refresh: If True, bypass cache and fetch fresh data :return: List of matching files :raises HfDataFetchError: If fetching fails + """ structure = self.fetch(repo_id, force_refresh=force_refresh) files = structure["files"] diff --git a/tfbpapi/datainfo/models.py b/tfbpapi/datainfo/models.py index 2ad8383..980c9fa 100644 --- a/tfbpapi/datainfo/models.py +++ b/tfbpapi/datainfo/models.py @@ -18,14 +18,14 @@ class DatasetType(str, Enum): class ClassLabelType(BaseModel): """Categorical data type with class labels.""" - names: List[str] = Field(..., description="List of possible class names") + names: list[str] = Field(..., description="List of possible class names") class FeatureInfo(BaseModel): """Information about a dataset feature/column.""" name: str = Field(..., description="Column name in the data") - dtype: Union[str, Dict[str, ClassLabelType]] = Field( + dtype: str | dict[str, ClassLabelType] = Field( ..., description="Data type (string, int64, float64, etc.) or categorical class labels", ) @@ -72,10 +72,10 @@ class PartitioningInfo(BaseModel): """Partitioning configuration for datasets.""" enabled: bool = Field(default=False, description="Whether partitioning is enabled") - partition_by: Optional[List[str]] = Field( + partition_by: list[str] | None = Field( default=None, description="Partition column names" ) - path_template: Optional[str] = Field( + path_template: str | None = Field( default=None, description="Path template for partitioned files" ) @@ -90,8 +90,8 @@ class DataFileInfo(BaseModel): class DatasetInfo(BaseModel): """Dataset structure information.""" - features: List[FeatureInfo] = Field(..., description="Feature definitions") - partitioning: Optional[PartitioningInfo] = Field( + features: list[FeatureInfo] = Field(..., description="Feature definitions") + partitioning: PartitioningInfo | None = Field( default=None, description="Partitioning configuration" ) @@ -105,13 +105,13 @@ class DatasetConfig(BaseModel): default: bool = Field( default=False, description="Whether this is the default config" ) - applies_to: Optional[List[str]] = Field( + applies_to: list[str] | None = Field( default=None, description="Configs this metadata applies to" ) - metadata_fields: Optional[List[str]] = Field( + metadata_fields: list[str] | None = Field( default=None, description="Fields for embedded metadata extraction" ) - data_files: List[DataFileInfo] = Field(..., description="Data file information") + data_files: list[DataFileInfo] = Field(..., description="Data file information") dataset_info: DatasetInfo = Field(..., description="Dataset structure information") @field_validator("applies_to") @@ -138,13 +138,13 @@ def metadata_fields_validation(cls, v): class BasicMetadata(BaseModel): """Basic dataset metadata.""" - license: Optional[str] = Field(default=None, description="Dataset license") - language: Optional[List[str]] = Field(default=None, description="Dataset languages") - tags: Optional[List[str]] = Field(default=None, description="Descriptive tags") - pretty_name: Optional[str] = Field( + license: str | None = Field(default=None, description="Dataset license") + language: list[str] | None = Field(default=None, description="Dataset languages") + tags: list[str] | None = Field(default=None, description="Descriptive tags") + pretty_name: str | None = Field( default=None, description="Human-readable dataset name" ) - size_categories: Optional[List[str]] = Field( + size_categories: list[str] | None = Field( default=None, description="Dataset size categories" ) @@ -152,14 +152,14 @@ class BasicMetadata(BaseModel): class DatasetCard(BaseModel): """Complete dataset card model.""" - configs: List[DatasetConfig] = Field(..., description="Dataset configurations") - license: Optional[str] = Field(default=None, description="Dataset license") - language: Optional[List[str]] = Field(default=None, description="Dataset languages") - tags: Optional[List[str]] = Field(default=None, description="Descriptive tags") - pretty_name: Optional[str] = Field( + configs: list[DatasetConfig] = Field(..., description="Dataset configurations") + license: str | None = Field(default=None, description="Dataset license") + language: list[str] | None = Field(default=None, description="Dataset languages") + tags: list[str] | None = Field(default=None, description="Descriptive tags") + pretty_name: str | None = Field( default=None, description="Human-readable dataset name" ) - size_categories: Optional[List[str]] = Field( + size_categories: list[str] | None = Field( default=None, description="Dataset size categories" ) @@ -189,25 +189,25 @@ def at_most_one_default(cls, v): raise ValueError("At most one configuration can be marked as default") return v - def get_config_by_name(self, name: str) -> Optional[DatasetConfig]: + def get_config_by_name(self, name: str) -> DatasetConfig | None: """Get a configuration by name.""" for config in self.configs: if config.config_name == name: return config return None - def get_configs_by_type(self, dataset_type: DatasetType) -> List[DatasetConfig]: + def get_configs_by_type(self, dataset_type: DatasetType) -> list[DatasetConfig]: """Get all configurations of a specific type.""" return [ config for config in self.configs if config.dataset_type == dataset_type ] - def get_default_config(self) -> Optional[DatasetConfig]: + def get_default_config(self) -> DatasetConfig | None: """Get the default configuration if one exists.""" defaults = [config for config in self.configs if config.default] return defaults[0] if defaults else None - def get_data_configs(self) -> List[DatasetConfig]: + def get_data_configs(self) -> list[DatasetConfig]: """Get all non-metadata configurations.""" return [ config @@ -215,7 +215,7 @@ def get_data_configs(self) -> List[DatasetConfig]: if config.dataset_type != DatasetType.METADATA ] - def get_metadata_configs(self) -> List[DatasetConfig]: + def get_metadata_configs(self) -> list[DatasetConfig]: """Get all metadata configurations.""" return [ config @@ -231,7 +231,7 @@ class ExtractedMetadata(BaseModel): field_name: str = Field( ..., description="Field name the metadata was extracted from" ) - values: Set[str] = Field(..., description="Unique values found") + values: set[str] = Field(..., description="Unique values found") extraction_method: str = Field(..., description="How the metadata was extracted") model_config = ConfigDict( diff --git a/tfbpapi/errors.py b/tfbpapi/errors.py index 3c0fdfa..654d6fc 100644 --- a/tfbpapi/errors.py +++ b/tfbpapi/errors.py @@ -6,7 +6,7 @@ class DatasetError(Exception): """Base exception for all dataset-related errors.""" - def __init__(self, message: str, details: Optional[Dict[str, Any]] = None): + def __init__(self, message: str, details: dict[str, Any] | None = None): super().__init__(message) self.details = details or {} @@ -42,9 +42,9 @@ class DataCardParsingError(DatasetError): def __init__( self, message: str, - repo_id: Optional[str] = None, - config_name: Optional[str] = None, - original_error: Optional[Exception] = None, + repo_id: str | None = None, + config_name: str | None = None, + original_error: Exception | None = None, ): details = {} if repo_id: @@ -66,9 +66,9 @@ class HfDataFetchError(DatasetError): def __init__( self, message: str, - repo_id: Optional[str] = None, - status_code: Optional[int] = None, - endpoint: Optional[str] = None, + repo_id: str | None = None, + status_code: int | None = None, + endpoint: str | None = None, ): details = {} if repo_id: @@ -87,7 +87,7 @@ def __init__( class TableNotFoundError(DatasetError): """Raised when requested table doesn't exist.""" - def __init__(self, table_name: str, available_tables: Optional[list] = None): + def __init__(self, table_name: str, available_tables: list | None = None): available_str = ( f"Available tables: {available_tables}" if available_tables @@ -109,7 +109,7 @@ def __init__(self, table_name: str, available_tables: Optional[list] = None): class MissingDatasetTypeError(DatasetError): """Raised when dataset_type field is missing from config.""" - def __init__(self, config_name: str, available_fields: Optional[list] = None): + def __init__(self, config_name: str, available_fields: list | None = None): fields_str = f"Available fields: {available_fields}" if available_fields else "" message = ( f"Missing 'dataset_type' field in config '{config_name}'. {fields_str}" @@ -129,7 +129,7 @@ def __init__(self, config_name: str, available_fields: Optional[list] = None): class InvalidDatasetTypeError(DatasetError): """Raised when dataset_type value is not recognized.""" - def __init__(self, invalid_type: str, valid_types: Optional[list] = None): + def __init__(self, invalid_type: str, valid_types: list | None = None): valid_str = f"Valid types: {valid_types}" if valid_types else "" message = f"Invalid dataset type '{invalid_type}'. {valid_str}" @@ -147,8 +147,8 @@ class ConfigNotFoundError(DatasetError): def __init__( self, config_name: str, - repo_id: Optional[str] = None, - available_configs: Optional[list] = None, + repo_id: str | None = None, + available_configs: list | None = None, ): repo_str = f" in repository '{repo_id}'" if repo_id else "" available_str = ( @@ -171,6 +171,7 @@ def __init__( class DataCardError(DatasetError): """Base exception for DataCard operations.""" + pass @@ -180,8 +181,8 @@ class DataCardValidationError(DataCardError): def __init__( self, message: str, - repo_id: Optional[str] = None, - validation_errors: Optional[list] = None, + repo_id: str | None = None, + validation_errors: list | None = None, ): details = {} if repo_id: @@ -200,8 +201,8 @@ class DataCardMetadataError(DataCardError): def __init__( self, message: str, - config_name: Optional[str] = None, - field_name: Optional[str] = None, + config_name: str | None = None, + field_name: str | None = None, ): details = {} if config_name: diff --git a/tfbpapi/tests/datainfo/__init__.py b/tfbpapi/tests/datainfo/__init__.py index 672d1e8..e38de55 100644 --- a/tfbpapi/tests/datainfo/__init__.py +++ b/tfbpapi/tests/datainfo/__init__.py @@ -1 +1 @@ -"""Tests for the datainfo package.""" \ No newline at end of file +"""Tests for the datainfo package.""" diff --git a/tfbpapi/tests/datainfo/conftest.py b/tfbpapi/tests/datainfo/conftest.py index 51afb26..09aa58f 100644 --- a/tfbpapi/tests/datainfo/conftest.py +++ b/tfbpapi/tests/datainfo/conftest.py @@ -1,8 +1,9 @@ """Shared fixtures and test data for datainfo tests.""" -import pytest from unittest.mock import Mock +import pytest + @pytest.fixture def sample_dataset_card_data(): @@ -25,30 +26,30 @@ def sample_dataset_card_data(): { "name": "gene_id", "dtype": "string", - "description": "Systematic gene identifier" + "description": "Systematic gene identifier", }, { "name": "gene_symbol", "dtype": "string", - "description": "Standard gene symbol" + "description": "Standard gene symbol", }, { "name": "chromosome", "dtype": "string", - "description": "Chromosome identifier" + "description": "Chromosome identifier", }, { "name": "start", "dtype": "int64", - "description": "Gene start position" + "description": "Gene start position", }, { "name": "end", "dtype": "int64", - "description": "Gene end position" - } + "description": "Gene end position", + }, ] - } + }, }, { "config_name": "binding_data", @@ -61,54 +62,59 @@ def sample_dataset_card_data(): { "name": "regulator_symbol", "dtype": "string", - "description": "Transcription factor name" + "description": "Transcription factor name", }, { "name": "target_gene", "dtype": "string", - "description": "Target gene identifier" + "description": "Target gene identifier", }, { "name": "experimental_condition", "dtype": "string", - "description": "Experimental treatment condition" + "description": "Experimental treatment condition", }, { "name": "binding_score", "dtype": "float64", - "description": "Quantitative binding measurement" - } + "description": "Quantitative binding measurement", + }, ] - } + }, }, { "config_name": "genome_map_data", "description": "Genome-wide signal tracks", "dataset_type": "genome_map", - "data_files": [{"split": "train", "path": "tracks/regulator=*/experiment=*/*.parquet"}], + "data_files": [ + { + "split": "train", + "path": "tracks/regulator=*/experiment=*/*.parquet", + } + ], "dataset_info": { "features": [ { "name": "chr", "dtype": "string", - "description": "Chromosome identifier" + "description": "Chromosome identifier", }, { "name": "pos", "dtype": "int32", - "description": "Genomic position" + "description": "Genomic position", }, { "name": "signal", "dtype": "float32", - "description": "Signal intensity" - } + "description": "Signal intensity", + }, ], "partitioning": { "enabled": True, - "partition_by": ["regulator", "experiment"] - } - } + "partition_by": ["regulator", "experiment"], + }, + }, }, { "config_name": "experiment_metadata", @@ -121,22 +127,22 @@ def sample_dataset_card_data(): { "name": "sample_id", "dtype": "string", - "description": "Unique sample identifier" + "description": "Unique sample identifier", }, { "name": "experimental_condition", "dtype": "string", - "description": "Experimental treatment or condition" + "description": "Experimental treatment or condition", }, { "name": "publication_doi", "dtype": "string", - "description": "DOI of associated publication" - } + "description": "DOI of associated publication", + }, ] - } - } - ] + }, + }, + ], } @@ -155,10 +161,10 @@ def minimal_dataset_card_data(): { "name": "test_field", "dtype": "string", - "description": "Test field" + "description": "Test field", } ] - } + }, } ] } @@ -174,9 +180,7 @@ def invalid_dataset_card_data(): "description": "Invalid configuration", # Missing required dataset_type field "data_files": [{"split": "train", "path": "test.parquet"}], - "dataset_info": { - "features": [] # Empty features list - } + "dataset_info": {"features": []}, # Empty features list } ] } @@ -188,38 +192,27 @@ def sample_repo_structure(): return { "repo_id": "test/dataset", "files": [ - { - "path": "features.parquet", - "size": 2048000, - "is_lfs": True - }, - { - "path": "binding/part1.parquet", - "size": 1024000, - "is_lfs": True - }, + {"path": "features.parquet", "size": 2048000, "is_lfs": True}, + {"path": "binding/part1.parquet", "size": 1024000, "is_lfs": True}, { "path": "tracks/regulator=TF1/experiment=exp1/data.parquet", "size": 5120000, - "is_lfs": True + "is_lfs": True, }, { "path": "tracks/regulator=TF1/experiment=exp2/data.parquet", "size": 4096000, - "is_lfs": True + "is_lfs": True, }, { "path": "tracks/regulator=TF2/experiment=exp1/data.parquet", "size": 3072000, - "is_lfs": True - } + "is_lfs": True, + }, ], - "partitions": { - "regulator": {"TF1", "TF2"}, - "experiment": {"exp1", "exp2"} - }, + "partitions": {"regulator": {"TF1", "TF2"}, "experiment": {"exp1", "exp2"}}, "total_files": 5, - "last_modified": "2023-12-01T10:30:00Z" + "last_modified": "2023-12-01T10:30:00Z", } @@ -231,7 +224,7 @@ def sample_size_info(): "num_bytes": 15360000, "num_rows": 150000, "download_size": 12288000, - "dataset_size": 15360000 + "dataset_size": 15360000, } @@ -279,7 +272,7 @@ def sample_feature_info(): return { "name": "gene_symbol", "dtype": "string", - "description": "Standard gene symbol (e.g., HO, GAL1)" + "description": "Standard gene symbol (e.g., HO, GAL1)", } @@ -289,14 +282,11 @@ def sample_partitioning_info(): return { "enabled": True, "partition_by": ["regulator", "condition"], - "path_template": "data/regulator={regulator}/condition={condition}/*.parquet" + "path_template": "data/regulator={regulator}/condition={condition}/*.parquet", } @pytest.fixture def sample_data_file_info(): """Sample data file information.""" - return { - "split": "train", - "path": "genomic_features.parquet" - } \ No newline at end of file + return {"split": "train", "path": "genomic_features.parquet"} diff --git a/tfbpapi/tests/datainfo/test_datacard.py b/tfbpapi/tests/datainfo/test_datacard.py index 40429bd..59345b9 100644 --- a/tfbpapi/tests/datainfo/test_datacard.py +++ b/tfbpapi/tests/datainfo/test_datacard.py @@ -1,7 +1,8 @@ """Tests for the DataCard class.""" -import pytest from unittest.mock import Mock, patch + +import pytest from pydantic import ValidationError from tfbpapi.datainfo import DataCard @@ -16,10 +17,17 @@ class TestDataCard: """Test suite for DataCard class.""" - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_init(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, test_token): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_init( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + test_token, + ): """Test DataCard initialization.""" datacard = DataCard(test_repo_id, token=test_token) @@ -33,10 +41,12 @@ def test_init(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher mock_structure_fetcher.assert_called_once_with(token=test_token) mock_size_fetcher.assert_called_once_with(token=test_token) - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_init_without_token(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_init_without_token( + self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id + ): """Test DataCard initialization without token.""" datacard = DataCard(test_repo_id) @@ -48,10 +58,17 @@ def test_init_without_token(self, mock_size_fetcher, mock_structure_fetcher, moc mock_structure_fetcher.assert_called_once_with(token=None) mock_size_fetcher.assert_called_once_with(token=None) - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_load_and_validate_card_success(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_load_and_validate_card_success( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): """Test successful card loading and validation.""" # Setup mock mock_fetcher_instance = Mock() @@ -68,10 +85,12 @@ def test_load_and_validate_card_success(self, mock_size_fetcher, mock_structure_ assert card.pretty_name == "Test Genomics Dataset" mock_fetcher_instance.fetch.assert_called_once_with(test_repo_id) - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_load_card_no_data(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_load_card_no_data( + self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id + ): """Test handling when no dataset card is found.""" mock_fetcher_instance = Mock() mock_card_fetcher.return_value = mock_fetcher_instance @@ -82,10 +101,17 @@ def test_load_card_no_data(self, mock_size_fetcher, mock_structure_fetcher, mock with pytest.raises(DataCardValidationError, match="No dataset card found"): _ = datacard.dataset_card - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_load_card_validation_error(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, invalid_dataset_card_data): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_load_card_validation_error( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + invalid_dataset_card_data, + ): """Test handling of validation errors.""" mock_fetcher_instance = Mock() mock_card_fetcher.return_value = mock_fetcher_instance @@ -93,13 +119,17 @@ def test_load_card_validation_error(self, mock_size_fetcher, mock_structure_fetc datacard = DataCard(test_repo_id) - with pytest.raises(DataCardValidationError, match="Dataset card validation failed"): + with pytest.raises( + DataCardValidationError, match="Dataset card validation failed" + ): _ = datacard.dataset_card - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_load_card_fetch_error(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_load_card_fetch_error( + self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id + ): """Test handling of fetch errors.""" mock_fetcher_instance = Mock() mock_card_fetcher.return_value = mock_fetcher_instance @@ -110,10 +140,17 @@ def test_load_card_fetch_error(self, mock_size_fetcher, mock_structure_fetcher, with pytest.raises(DataCardError, match="Failed to fetch dataset card"): _ = datacard.dataset_card - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_configs_property(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_configs_property( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): """Test getting all configurations via property.""" mock_fetcher_instance = Mock() mock_card_fetcher.return_value = mock_fetcher_instance @@ -129,10 +166,17 @@ def test_configs_property(self, mock_size_fetcher, mock_structure_fetcher, mock_ assert "genome_map_data" in config_names assert "experiment_metadata" in config_names - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_get_config_by_name(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_get_config_by_name( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): """Test getting a specific configuration by name.""" mock_fetcher_instance = Mock() mock_card_fetcher.return_value = mock_fetcher_instance @@ -148,10 +192,17 @@ def test_get_config_by_name(self, mock_size_fetcher, mock_structure_fetcher, moc # Test non-existent config assert datacard.get_config("nonexistent") is None - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_get_configs_by_type(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_get_configs_by_type( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): """Test getting configurations by dataset type.""" mock_fetcher_instance = Mock() mock_card_fetcher.return_value = mock_fetcher_instance @@ -174,10 +225,17 @@ def test_get_configs_by_type(self, mock_size_fetcher, mock_structure_fetcher, mo assert len(genome_map_configs) == 1 assert genome_map_configs[0].config_name == "genome_map_data" - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_get_field_values_success(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_get_field_values_success( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): """Test getting field values for a specific config and field.""" mock_fetcher_instance = Mock() mock_card_fetcher.return_value = mock_fetcher_instance @@ -190,10 +248,17 @@ def test_get_field_values_success(self, mock_size_fetcher, mock_structure_fetche # Since _extract_field_values returns empty set by default, we expect empty set assert isinstance(values, set) - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_get_field_values_config_not_found(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_get_field_values_config_not_found( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): """Test error when config not found.""" mock_fetcher_instance = Mock() mock_card_fetcher.return_value = mock_fetcher_instance @@ -201,13 +266,22 @@ def test_get_field_values_config_not_found(self, mock_size_fetcher, mock_structu datacard = DataCard(test_repo_id) - with pytest.raises(DataCardError, match="Configuration 'nonexistent' not found"): + with pytest.raises( + DataCardError, match="Configuration 'nonexistent' not found" + ): datacard.get_field_values("nonexistent", "some_field") - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_get_field_values_field_not_found(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_get_field_values_field_not_found( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): """Test error when field not found.""" mock_fetcher_instance = Mock() mock_card_fetcher.return_value = mock_fetcher_instance @@ -218,10 +292,17 @@ def test_get_field_values_field_not_found(self, mock_size_fetcher, mock_structur with pytest.raises(DataCardError, match="Field 'nonexistent' not found"): datacard.get_field_values("binding_data", "nonexistent") - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_get_metadata_relationships(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_get_metadata_relationships( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): """Test getting metadata relationships.""" mock_fetcher_instance = Mock() mock_card_fetcher.return_value = mock_fetcher_instance @@ -243,10 +324,18 @@ def test_get_metadata_relationships(self, mock_size_fetcher, mock_structure_fetc assert embedded_rels[0].data_config == "binding_data" assert embedded_rels[0].metadata_config == "binding_data_embedded" - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_get_repository_info_success(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data, sample_repo_structure): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_get_repository_info_success( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + sample_repo_structure, + ): """Test getting repository information.""" mock_card_fetcher_instance = Mock() mock_structure_fetcher_instance = Mock() @@ -272,10 +361,17 @@ def test_get_repository_info_success(self, mock_size_fetcher, mock_structure_fet assert info["last_modified"] == "2023-12-01T10:30:00Z" assert info["has_default_config"] is True - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_get_repository_info_fetch_error(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_get_repository_info_fetch_error( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): """Test getting repository info when structure fetch fails.""" mock_card_fetcher_instance = Mock() mock_structure_fetcher_instance = Mock() @@ -283,7 +379,9 @@ def test_get_repository_info_fetch_error(self, mock_size_fetcher, mock_structure mock_structure_fetcher.return_value = mock_structure_fetcher_instance mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data - mock_structure_fetcher_instance.fetch.side_effect = HfDataFetchError("Structure fetch failed") + mock_structure_fetcher_instance.fetch.side_effect = HfDataFetchError( + "Structure fetch failed" + ) datacard = DataCard(test_repo_id) @@ -293,10 +391,17 @@ def test_get_repository_info_fetch_error(self, mock_size_fetcher, mock_structure assert info["total_files"] is None assert info["last_modified"] is None - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_explore_config(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_explore_config( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): """Test exploring a specific configuration.""" mock_fetcher_instance = Mock() mock_card_fetcher.return_value = mock_fetcher_instance @@ -313,22 +418,35 @@ def test_explore_config(self, mock_size_fetcher, mock_structure_fetcher, mock_ca assert config_info["is_default"] is False assert config_info["num_features"] == 4 assert len(config_info["features"]) == 4 - assert config_info["metadata_fields"] == ["regulator_symbol", "experimental_condition"] + assert config_info["metadata_fields"] == [ + "regulator_symbol", + "experimental_condition", + ] # Test config with partitioning partitioned_config_info = datacard.explore_config("genome_map_data") assert "partitioning" in partitioned_config_info assert partitioned_config_info["partitioning"]["enabled"] is True - assert partitioned_config_info["partitioning"]["partition_by"] == ["regulator", "experiment"] + assert partitioned_config_info["partitioning"]["partition_by"] == [ + "regulator", + "experiment", + ] # Test metadata config with applies_to metadata_config_info = datacard.explore_config("experiment_metadata") assert metadata_config_info["applies_to"] == ["binding_data"] - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_explore_config_not_found(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_explore_config_not_found( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): """Test exploring a non-existent configuration.""" mock_fetcher_instance = Mock() mock_card_fetcher.return_value = mock_fetcher_instance @@ -336,13 +454,23 @@ def test_explore_config_not_found(self, mock_size_fetcher, mock_structure_fetche datacard = DataCard(test_repo_id) - with pytest.raises(DataCardError, match="Configuration 'nonexistent' not found"): + with pytest.raises( + DataCardError, match="Configuration 'nonexistent' not found" + ): datacard.explore_config("nonexistent") - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_summary(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data, sample_repo_structure): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_summary( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + sample_repo_structure, + ): """Test getting a summary of the dataset.""" mock_card_fetcher_instance = Mock() mock_structure_fetcher_instance = Mock() @@ -366,10 +494,17 @@ def test_summary(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetc assert "experiment_metadata" in summary assert "(default)" in summary # genomic_features is marked as default - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_extract_partition_values(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_extract_partition_values( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): """Test extracting partition values.""" mock_card_fetcher_instance = Mock() mock_structure_fetcher_instance = Mock() @@ -377,7 +512,11 @@ def test_extract_partition_values(self, mock_size_fetcher, mock_structure_fetche mock_structure_fetcher.return_value = mock_structure_fetcher_instance mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data - mock_structure_fetcher_instance.get_partition_values.return_value = ["TF1", "TF2", "TF3"] + mock_structure_fetcher_instance.get_partition_values.return_value = [ + "TF1", + "TF2", + "TF3", + ] datacard = DataCard(test_repo_id) @@ -388,12 +527,21 @@ def test_extract_partition_values(self, mock_size_fetcher, mock_structure_fetche values = datacard._extract_partition_values(config, "regulator") assert values == {"TF1", "TF2", "TF3"} - mock_structure_fetcher_instance.get_partition_values.assert_called_once_with(test_repo_id, "regulator") - - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_extract_partition_values_no_partitioning(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data): + mock_structure_fetcher_instance.get_partition_values.assert_called_once_with( + test_repo_id, "regulator" + ) + + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_extract_partition_values_no_partitioning( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): """Test extracting partition values when partitioning is disabled.""" mock_card_fetcher_instance = Mock() mock_structure_fetcher_instance = Mock() @@ -413,10 +561,17 @@ def test_extract_partition_values_no_partitioning(self, mock_size_fetcher, mock_ assert values == set() mock_structure_fetcher_instance.get_partition_values.assert_not_called() - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_extract_partition_values_field_not_in_partitions(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_extract_partition_values_field_not_in_partitions( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): """Test extracting partition values when field is not a partition column.""" mock_card_fetcher_instance = Mock() mock_structure_fetcher_instance = Mock() @@ -436,10 +591,17 @@ def test_extract_partition_values_field_not_in_partitions(self, mock_size_fetche assert values == set() mock_structure_fetcher_instance.get_partition_values.assert_not_called() - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_extract_partition_values_fetch_error(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_extract_partition_values_fetch_error( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): """Test extracting partition values when fetch fails.""" mock_card_fetcher_instance = Mock() mock_structure_fetcher_instance = Mock() @@ -447,7 +609,9 @@ def test_extract_partition_values_fetch_error(self, mock_size_fetcher, mock_stru mock_structure_fetcher.return_value = mock_structure_fetcher_instance mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data - mock_structure_fetcher_instance.get_partition_values.side_effect = HfDataFetchError("Fetch failed") + mock_structure_fetcher_instance.get_partition_values.side_effect = ( + HfDataFetchError("Fetch failed") + ) datacard = DataCard(test_repo_id) @@ -456,4 +620,3 @@ def test_extract_partition_values_fetch_error(self, mock_size_fetcher, mock_stru # Should return empty set on error assert values == set() - diff --git a/tfbpapi/tests/datainfo/test_fetchers.py b/tfbpapi/tests/datainfo/test_fetchers.py index 234878a..a0a04fe 100644 --- a/tfbpapi/tests/datainfo/test_fetchers.py +++ b/tfbpapi/tests/datainfo/test_fetchers.py @@ -1,7 +1,8 @@ """Tests for datainfo fetcher classes.""" -import pytest from unittest.mock import Mock, patch + +import pytest import requests from requests import HTTPError @@ -23,18 +24,20 @@ def test_init_with_token(self, test_token): def test_init_without_token(self): """Test initialization without token.""" - with patch.dict('os.environ', {}, clear=True): + with patch.dict("os.environ", {}, clear=True): fetcher = HfDataCardFetcher() assert fetcher.token is None def test_init_with_env_token(self, test_token): """Test initialization with environment token.""" - with patch.dict('os.environ', {'HF_TOKEN': test_token}): + with patch.dict("os.environ", {"HF_TOKEN": test_token}): fetcher = HfDataCardFetcher() assert fetcher.token == test_token - @patch('tfbpapi.datainfo.fetchers.DatasetCard') - def test_fetch_success(self, mock_dataset_card, test_repo_id, sample_dataset_card_data): + @patch("tfbpapi.datainfo.fetchers.DatasetCard") + def test_fetch_success( + self, mock_dataset_card, test_repo_id, sample_dataset_card_data + ): """Test successful dataset card fetch.""" # Setup mock mock_card = Mock() @@ -49,7 +52,7 @@ def test_fetch_success(self, mock_dataset_card, test_repo_id, sample_dataset_car test_repo_id, repo_type="dataset", token="test_token" ) - @patch('tfbpapi.datainfo.fetchers.DatasetCard') + @patch("tfbpapi.datainfo.fetchers.DatasetCard") def test_fetch_no_data_section(self, mock_dataset_card, test_repo_id): """Test fetch when dataset card has no data section.""" # Setup mock with no data @@ -62,7 +65,7 @@ def test_fetch_no_data_section(self, mock_dataset_card, test_repo_id): assert result == {} - @patch('tfbpapi.datainfo.fetchers.DatasetCard') + @patch("tfbpapi.datainfo.fetchers.DatasetCard") def test_fetch_exception(self, mock_dataset_card, test_repo_id): """Test fetch when DatasetCard.load raises exception.""" mock_dataset_card.load.side_effect = Exception("API Error") @@ -74,7 +77,7 @@ def test_fetch_exception(self, mock_dataset_card, test_repo_id): def test_fetch_different_repo_types(self, sample_dataset_card_data): """Test fetch with different repository types.""" - with patch('tfbpapi.datainfo.fetchers.DatasetCard') as mock_dataset_card: + with patch("tfbpapi.datainfo.fetchers.DatasetCard") as mock_dataset_card: mock_card = Mock() mock_card.data.to_dict.return_value = sample_dataset_card_data mock_dataset_card.load.return_value = mock_card @@ -119,7 +122,7 @@ def test_build_headers_without_token(self): assert headers["User-Agent"] == "TFBP-API/1.0" assert "Authorization" not in headers - @patch('tfbpapi.datainfo.fetchers.requests.get') + @patch("tfbpapi.datainfo.fetchers.requests.get") def test_fetch_success(self, mock_get, test_repo_id, sample_size_info): """Test successful size info fetch.""" # Setup mock response @@ -139,7 +142,7 @@ def test_fetch_success(self, mock_get, test_repo_id, sample_size_info): assert call_args[1]["headers"]["Authorization"] == "Bearer test_token" assert call_args[1]["timeout"] == 30 - @patch('tfbpapi.datainfo.fetchers.requests.get') + @patch("tfbpapi.datainfo.fetchers.requests.get") def test_fetch_404_error(self, mock_get, test_repo_id): """Test fetch with 404 error.""" # Setup mock 404 response @@ -153,7 +156,7 @@ def test_fetch_404_error(self, mock_get, test_repo_id): with pytest.raises(HfDataFetchError, match="Dataset .* not found"): fetcher.fetch(test_repo_id) - @patch('tfbpapi.datainfo.fetchers.requests.get') + @patch("tfbpapi.datainfo.fetchers.requests.get") def test_fetch_403_error(self, mock_get, test_repo_id): """Test fetch with 403 error.""" # Setup mock 403 response @@ -164,10 +167,12 @@ def test_fetch_403_error(self, mock_get, test_repo_id): fetcher = HfSizeInfoFetcher() - with pytest.raises(HfDataFetchError, match="Access denied.*check token permissions"): + with pytest.raises( + HfDataFetchError, match="Access denied.*check token permissions" + ): fetcher.fetch(test_repo_id) - @patch('tfbpapi.datainfo.fetchers.requests.get') + @patch("tfbpapi.datainfo.fetchers.requests.get") def test_fetch_other_http_error(self, mock_get, test_repo_id): """Test fetch with other HTTP error.""" # Setup mock 500 response @@ -181,7 +186,7 @@ def test_fetch_other_http_error(self, mock_get, test_repo_id): with pytest.raises(HfDataFetchError, match="HTTP error fetching size"): fetcher.fetch(test_repo_id) - @patch('tfbpapi.datainfo.fetchers.requests.get') + @patch("tfbpapi.datainfo.fetchers.requests.get") def test_fetch_request_exception(self, mock_get, test_repo_id): """Test fetch with request exception.""" mock_get.side_effect = requests.RequestException("Network error") @@ -191,7 +196,7 @@ def test_fetch_request_exception(self, mock_get, test_repo_id): with pytest.raises(HfDataFetchError, match="Request failed fetching size"): fetcher.fetch(test_repo_id) - @patch('tfbpapi.datainfo.fetchers.requests.get') + @patch("tfbpapi.datainfo.fetchers.requests.get") def test_fetch_json_decode_error(self, mock_get, test_repo_id): """Test fetch with JSON decode error.""" # Setup mock response with invalid JSON @@ -214,7 +219,7 @@ def test_init(self, test_token): assert fetcher.token == test_token assert fetcher._cached_structure == {} - @patch('tfbpapi.datainfo.fetchers.repo_info') + @patch("tfbpapi.datainfo.fetchers.repo_info") def test_fetch_success(self, mock_repo_info, test_repo_id, sample_repo_structure): """Test successful repository structure fetch.""" # Setup mock repo info @@ -222,7 +227,11 @@ def test_fetch_success(self, mock_repo_info, test_repo_id, sample_repo_structure mock_info.siblings = [ Mock(rfilename="features.parquet", size=2048000, lfs=Mock()), Mock(rfilename="binding/part1.parquet", size=1024000, lfs=Mock()), - Mock(rfilename="tracks/regulator=TF1/experiment=exp1/data.parquet", size=5120000, lfs=Mock()), + Mock( + rfilename="tracks/regulator=TF1/experiment=exp1/data.parquet", + size=5120000, + lfs=Mock(), + ), ] mock_info.last_modified.isoformat.return_value = "2023-12-01T10:30:00Z" mock_repo_info.return_value = mock_info @@ -240,7 +249,7 @@ def test_fetch_success(self, mock_repo_info, test_repo_id, sample_repo_structure repo_id=test_repo_id, repo_type="dataset", token="test_token" ) - @patch('tfbpapi.datainfo.fetchers.repo_info') + @patch("tfbpapi.datainfo.fetchers.repo_info") def test_fetch_with_caching(self, mock_repo_info, test_repo_id): """Test fetch with caching behavior.""" # Setup mock @@ -264,7 +273,7 @@ def test_fetch_with_caching(self, mock_repo_info, test_repo_id): result3 = fetcher.fetch(test_repo_id, force_refresh=True) assert mock_repo_info.call_count == 2 - @patch('tfbpapi.datainfo.fetchers.repo_info') + @patch("tfbpapi.datainfo.fetchers.repo_info") def test_fetch_siblings_none(self, mock_repo_info, test_repo_id): """Test fetch when siblings is None.""" # Setup mock with None siblings @@ -280,7 +289,7 @@ def test_fetch_siblings_none(self, mock_repo_info, test_repo_id): assert result["files"] == [] assert result["partitions"] == {} - @patch('tfbpapi.datainfo.fetchers.repo_info') + @patch("tfbpapi.datainfo.fetchers.repo_info") def test_fetch_exception(self, mock_repo_info, test_repo_id): """Test fetch when repo_info raises exception.""" mock_repo_info.side_effect = Exception("API Error") @@ -296,14 +305,18 @@ def test_extract_partition_info(self): partitions = {} # Test normal partition pattern - fetcher._extract_partition_info("data/regulator=TF1/condition=control/file.parquet", partitions) + fetcher._extract_partition_info( + "data/regulator=TF1/condition=control/file.parquet", partitions + ) assert "regulator" in partitions assert "TF1" in partitions["regulator"] assert "condition" in partitions assert "control" in partitions["condition"] # Test multiple values for same partition - fetcher._extract_partition_info("data/regulator=TF2/condition=treatment/file.parquet", partitions) + fetcher._extract_partition_info( + "data/regulator=TF2/condition=treatment/file.parquet", partitions + ) assert len(partitions["regulator"]) == 2 assert "TF2" in partitions["regulator"] assert "treatment" in partitions["condition"] @@ -313,7 +326,7 @@ def test_extract_partition_info(self): # partitions dict should remain unchanged assert len(partitions) == 2 - @patch('tfbpapi.datainfo.fetchers.repo_info') + @patch("tfbpapi.datainfo.fetchers.repo_info") def test_get_partition_values_success(self, mock_repo_info, test_repo_id): """Test getting partition values for a specific column.""" # Setup mock with partitioned files @@ -331,7 +344,7 @@ def test_get_partition_values_success(self, mock_repo_info, test_repo_id): assert values == ["TF1", "TF2", "TF3"] # Should be sorted - @patch('tfbpapi.datainfo.fetchers.repo_info') + @patch("tfbpapi.datainfo.fetchers.repo_info") def test_get_partition_values_no_partitions(self, mock_repo_info, test_repo_id): """Test getting partition values when no partitions exist.""" # Setup mock with no partitioned files @@ -347,7 +360,7 @@ def test_get_partition_values_no_partitions(self, mock_repo_info, test_repo_id): assert values == [] - @patch('tfbpapi.datainfo.fetchers.repo_info') + @patch("tfbpapi.datainfo.fetchers.repo_info") def test_get_dataset_files_all(self, mock_repo_info, test_repo_id): """Test getting all dataset files.""" # Setup mock @@ -371,7 +384,7 @@ def test_get_dataset_files_all(self, mock_repo_info, test_repo_id): assert files[1]["size"] == 2000 assert files[1]["is_lfs"] is True - @patch('tfbpapi.datainfo.fetchers.repo_info') + @patch("tfbpapi.datainfo.fetchers.repo_info") def test_get_dataset_files_with_pattern(self, mock_repo_info, test_repo_id): """Test getting dataset files with path pattern filter.""" # Setup mock @@ -394,7 +407,7 @@ def test_get_dataset_files_uses_cache(self): """Test that get_dataset_files uses fetch caching.""" fetcher = HfRepoStructureFetcher() - with patch.object(fetcher, 'fetch') as mock_fetch: + with patch.object(fetcher, "fetch") as mock_fetch: mock_fetch.return_value = {"files": []} # First call @@ -409,7 +422,7 @@ def test_get_partition_values_uses_cache(self): """Test that get_partition_values uses fetch caching.""" fetcher = HfRepoStructureFetcher() - with patch.object(fetcher, 'fetch') as mock_fetch: + with patch.object(fetcher, "fetch") as mock_fetch: mock_fetch.return_value = {"partitions": {"regulator": {"TF1", "TF2"}}} # First call @@ -419,4 +432,4 @@ def test_get_partition_values_uses_cache(self): # Second call with force_refresh fetcher.get_partition_values("test/repo", "regulator", force_refresh=True) - mock_fetch.assert_called_with("test/repo", force_refresh=True) \ No newline at end of file + mock_fetch.assert_called_with("test/repo", force_refresh=True) diff --git a/tfbpapi/tests/datainfo/test_models.py b/tfbpapi/tests/datainfo/test_models.py index 1513e46..618a392 100644 --- a/tfbpapi/tests/datainfo/test_models.py +++ b/tfbpapi/tests/datainfo/test_models.py @@ -4,15 +4,15 @@ from pydantic import ValidationError from tfbpapi.datainfo.models import ( - DatasetType, - FeatureInfo, - PartitioningInfo, DataFileInfo, - DatasetInfo, - DatasetConfig, DatasetCard, + DatasetConfig, + DatasetInfo, + DatasetType, ExtractedMetadata, + FeatureInfo, MetadataRelationship, + PartitioningInfo, ) @@ -74,12 +74,8 @@ def test_feature_info_categorical_dtype(self): """Test FeatureInfo with categorical dtype.""" categorical_feature_data = { "name": "mechanism", - "dtype": { - "class_label": { - "names": ["GEV", "ZEV"] - } - }, - "description": "Induction system" + "dtype": {"class_label": {"names": ["GEV", "ZEV"]}}, + "description": "Induction system", } feature = FeatureInfo(**categorical_feature_data) @@ -92,9 +88,7 @@ def test_feature_info_categorical_dtype(self): def test_feature_info_simple_string_dtype(self): """Test FeatureInfo with simple string dtype.""" feature = FeatureInfo( - name="test_field", - dtype="string", - description="Test field" + name="test_field", dtype="string", description="Test field" ) assert feature.dtype == "string" assert feature.get_dtype_summary() == "string" @@ -105,7 +99,7 @@ def test_feature_info_invalid_categorical_dtype(self): FeatureInfo( name="test_field", dtype={"class_label": "invalid"}, # Should be dict with names - description="Test field" + description="Test field", ) def test_feature_info_unknown_dtype_structure(self): @@ -114,7 +108,7 @@ def test_feature_info_unknown_dtype_structure(self): FeatureInfo( name="test_field", dtype={"unknown_key": "value"}, - description="Test field" + description="Test field", ) @@ -133,7 +127,10 @@ def test_enabled_partitioning_info(self, sample_partitioning_info): partitioning = PartitioningInfo(**sample_partitioning_info) assert partitioning.enabled is True assert partitioning.partition_by == ["regulator", "condition"] - assert partitioning.path_template is not None and "regulator={regulator}" in partitioning.path_template + assert ( + partitioning.path_template is not None + and "regulator={regulator}" in partitioning.path_template + ) def test_partial_partitioning_info(self): """Test partitioning with only some fields set.""" @@ -174,14 +171,19 @@ def test_minimal_dataset_info(self, sample_feature_info): assert len(dataset_info.features) == 1 assert dataset_info.partitioning is None - def test_dataset_info_with_partitioning(self, sample_feature_info, sample_partitioning_info): + def test_dataset_info_with_partitioning( + self, sample_feature_info, sample_partitioning_info + ): """Test DatasetInfo with partitioning.""" features = [FeatureInfo(**sample_feature_info)] partitioning = PartitioningInfo(**sample_partitioning_info) dataset_info = DatasetInfo(features=features, partitioning=partitioning) assert len(dataset_info.features) == 1 - assert dataset_info.partitioning is not None and dataset_info.partitioning.enabled is True + assert ( + dataset_info.partitioning is not None + and dataset_info.partitioning.enabled is True + ) def test_dataset_info_empty_features_error(self): """Test that empty features list is allowed.""" @@ -204,7 +206,7 @@ def test_minimal_dataset_config(self, sample_feature_info, sample_data_file_info description="Test configuration", dataset_type=DatasetType.GENOMIC_FEATURES, data_files=data_files, - dataset_info=dataset_info + dataset_info=dataset_info, ) assert config.config_name == "test_config" @@ -213,7 +215,9 @@ def test_minimal_dataset_config(self, sample_feature_info, sample_data_file_info assert config.applies_to is None assert config.metadata_fields is None - def test_dataset_config_with_applies_to_metadata(self, sample_feature_info, sample_data_file_info): + def test_dataset_config_with_applies_to_metadata( + self, sample_feature_info, sample_data_file_info + ): """Test DatasetConfig with applies_to for metadata types.""" features = [FeatureInfo(**sample_feature_info)] data_files = [DataFileInfo(**sample_data_file_info)] @@ -225,44 +229,55 @@ def test_dataset_config_with_applies_to_metadata(self, sample_feature_info, samp dataset_type=DatasetType.METADATA, applies_to=["data_config1", "data_config2"], data_files=data_files, - dataset_info=dataset_info + dataset_info=dataset_info, ) assert config.applies_to == ["data_config1", "data_config2"] - def test_dataset_config_applies_to_validation_error(self, sample_feature_info, sample_data_file_info): + def test_dataset_config_applies_to_validation_error( + self, sample_feature_info, sample_data_file_info + ): """Test that applies_to is only valid for metadata types.""" features = [FeatureInfo(**sample_feature_info)] data_files = [DataFileInfo(**sample_data_file_info)] dataset_info = DatasetInfo(features=features) - with pytest.raises(ValidationError, match="applies_to field is only valid for metadata dataset types"): + with pytest.raises( + ValidationError, + match="applies_to field is only valid for metadata dataset types", + ): DatasetConfig( config_name="invalid_config", description="Invalid configuration", dataset_type=DatasetType.GENOMIC_FEATURES, # Not a metadata type applies_to=["some_config"], # This should cause validation error data_files=data_files, - dataset_info=dataset_info + dataset_info=dataset_info, ) - def test_dataset_config_empty_metadata_fields_error(self, sample_feature_info, sample_data_file_info): + def test_dataset_config_empty_metadata_fields_error( + self, sample_feature_info, sample_data_file_info + ): """Test that empty metadata_fields list raises error.""" features = [FeatureInfo(**sample_feature_info)] data_files = [DataFileInfo(**sample_data_file_info)] dataset_info = DatasetInfo(features=features) - with pytest.raises(ValidationError, match="metadata_fields cannot be empty list"): + with pytest.raises( + ValidationError, match="metadata_fields cannot be empty list" + ): DatasetConfig( config_name="test_config", description="Test configuration", dataset_type=DatasetType.ANNOTATED_FEATURES, metadata_fields=[], # Empty list should cause error data_files=data_files, - dataset_info=dataset_info + dataset_info=dataset_info, ) - def test_dataset_config_with_metadata_fields(self, sample_feature_info, sample_data_file_info): + def test_dataset_config_with_metadata_fields( + self, sample_feature_info, sample_data_file_info + ): """Test DatasetConfig with valid metadata_fields.""" features = [FeatureInfo(**sample_feature_info)] data_files = [DataFileInfo(**sample_data_file_info)] @@ -274,7 +289,7 @@ def test_dataset_config_with_metadata_fields(self, sample_feature_info, sample_d dataset_type=DatasetType.ANNOTATED_FEATURES, metadata_fields=["field1", "field2"], data_files=data_files, - dataset_info=dataset_info + dataset_info=dataset_info, ) assert config.metadata_fields == ["field1", "field2"] @@ -301,10 +316,14 @@ def test_full_dataset_card(self, sample_dataset_card_data): def test_empty_configs_error(self): """Test that empty configs list raises error.""" - with pytest.raises(ValidationError, match="At least one dataset configuration is required"): + with pytest.raises( + ValidationError, match="At least one dataset configuration is required" + ): DatasetCard(configs=[]) - def test_duplicate_config_names_error(self, sample_feature_info, sample_data_file_info): + def test_duplicate_config_names_error( + self, sample_feature_info, sample_data_file_info + ): """Test that duplicate config names raise error.""" features = [FeatureInfo(**sample_feature_info)] data_files = [DataFileInfo(**sample_data_file_info)] @@ -315,7 +334,7 @@ def test_duplicate_config_names_error(self, sample_feature_info, sample_data_fil description="First config", dataset_type=DatasetType.GENOMIC_FEATURES, data_files=data_files, - dataset_info=dataset_info + dataset_info=dataset_info, ) config2 = DatasetConfig( @@ -323,13 +342,15 @@ def test_duplicate_config_names_error(self, sample_feature_info, sample_data_fil description="Second config", dataset_type=DatasetType.ANNOTATED_FEATURES, data_files=data_files, - dataset_info=dataset_info + dataset_info=dataset_info, ) with pytest.raises(ValidationError, match="Configuration names must be unique"): DatasetCard(configs=[config1, config2]) - def test_multiple_default_configs_error(self, sample_feature_info, sample_data_file_info): + def test_multiple_default_configs_error( + self, sample_feature_info, sample_data_file_info + ): """Test that multiple default configs raise error.""" features = [FeatureInfo(**sample_feature_info)] data_files = [DataFileInfo(**sample_data_file_info)] @@ -341,7 +362,7 @@ def test_multiple_default_configs_error(self, sample_feature_info, sample_data_f dataset_type=DatasetType.GENOMIC_FEATURES, default=True, data_files=data_files, - dataset_info=dataset_info + dataset_info=dataset_info, ) config2 = DatasetConfig( @@ -350,10 +371,12 @@ def test_multiple_default_configs_error(self, sample_feature_info, sample_data_f dataset_type=DatasetType.ANNOTATED_FEATURES, default=True, # Another default data_files=data_files, - dataset_info=dataset_info + dataset_info=dataset_info, ) - with pytest.raises(ValidationError, match="At most one configuration can be marked as default"): + with pytest.raises( + ValidationError, match="At most one configuration can be marked as default" + ): DatasetCard(configs=[config1, config2]) def test_get_config_by_name(self, sample_dataset_card_data): @@ -418,7 +441,7 @@ def test_extracted_metadata_creation(self): config_name="test_config", field_name="regulator_symbol", values={"TF1", "TF2", "TF3"}, - extraction_method="partition_values" + extraction_method="partition_values", ) assert metadata.config_name == "test_config" @@ -432,7 +455,7 @@ def test_extracted_metadata_serialization(self): config_name="test_config", field_name="condition", values={"control", "treatment"}, - extraction_method="embedded" + extraction_method="embedded", ) # Test basic serialization (sets remain as sets in model_dump) @@ -454,7 +477,7 @@ def test_metadata_relationship_creation(self): relationship = MetadataRelationship( data_config="binding_data", metadata_config="experiment_metadata", - relationship_type="explicit" + relationship_type="explicit", ) assert relationship.data_config == "binding_data" @@ -465,17 +488,14 @@ def test_metadata_relationship_types(self): """Test different relationship types.""" # Test explicit relationship explicit = MetadataRelationship( - data_config="data1", - metadata_config="meta1", - relationship_type="explicit" + data_config="data1", metadata_config="meta1", relationship_type="explicit" ) assert explicit.relationship_type == "explicit" - # Test embedded relationship embedded = MetadataRelationship( data_config="data3", metadata_config="data3_embedded", - relationship_type="embedded" + relationship_type="embedded", ) - assert embedded.relationship_type == "embedded" \ No newline at end of file + assert embedded.relationship_type == "embedded" From cad983432ec08aa0c57b26a4354da61249277a99 Mon Sep 17 00:00:00 2001 From: chasem Date: Sat, 20 Sep 2025 16:47:13 -0500 Subject: [PATCH 07/49] working on hfqueryapi --- docs/HfCacheManager.md | 2 +- docs/tutorials/cache_manager_tutorial.ipynb | 1535 +++++++++++++ docs/tutorials/database_interface.ipynb | 2266 ------------------ docs/tutorials/datacard_tutorial.ipynb | 150 +- docs/tutorials/hfqueryapi_tutorial.ipynb | 2214 ++++++++++++++++++ mkdocs.yml | 2 + tfbpapi/HfCacheManager.py | 240 +- tfbpapi/HfQueryAPI.py | 2298 ++----------------- tfbpapi/constants.py | 11 + tfbpapi/datainfo/fetchers.py | 10 +- tfbpapi/errors.py | 13 +- tfbpapi/tests/test_HfCacheManager.py | 538 ++++- 12 files changed, 4829 insertions(+), 4450 deletions(-) create mode 100644 docs/tutorials/cache_manager_tutorial.ipynb delete mode 100644 docs/tutorials/database_interface.ipynb create mode 100644 docs/tutorials/hfqueryapi_tutorial.ipynb create mode 100644 tfbpapi/constants.py diff --git a/docs/HfCacheManager.md b/docs/HfCacheManager.md index 6aa53a3..89d5c77 100644 --- a/docs/HfCacheManager.md +++ b/docs/HfCacheManager.md @@ -1 +1 @@ -::: tfbpapi.HfCacheManager.HFCacheManager +::: tfbpapi.HfCacheManager.HfCacheManager diff --git a/docs/tutorials/cache_manager_tutorial.ipynb b/docs/tutorials/cache_manager_tutorial.ipynb new file mode 100644 index 0000000..a043417 --- /dev/null +++ b/docs/tutorials/cache_manager_tutorial.ipynb @@ -0,0 +1,1535 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# HfCacheManager Tutorial: Intelligent Cache Management for HuggingFace Datasets\n", + "\n", + "The `HfCacheManager` class provides sophisticated cache management capabilities for HuggingFace genomics datasets. It extends DataCard functionality with intelligent caching strategies and automated cache cleanup tools.\n", + "\n", + "This tutorial covers:\n", + "- Setting up HfCacheManager for cache management\n", + "- Understanding the 3-case metadata caching strategy\n", + "- Automated cache cleanup by age, size, and revision\n", + "- Cache monitoring and diagnostics\n", + "- Best practices for efficient cache management\n", + "- Integration with data loading workflows\n", + "\n", + "**Prerequisites**: Basic familiarity with DataCard (see datacard_tutorial.ipynb) and HuggingFace datasets." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1. Setting Up HfCacheManager\n", + "\n", + "The HfCacheManager extends DataCard with cache management capabilities. Unlike DataCard which focuses on dataset exploration, HfCacheManager adds intelligent caching and cleanup features." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "HfCacheManager initialized for: BrentLab/hackett_2020\n", + "DuckDB connection: Active\n", + "Logger configured: Yes\n", + "Current HF cache size: 4.6G\n", + "Cached repositories: 9\n" + ] + } + ], + "source": [ + "import duckdb\n", + "import logging\n", + "from tfbpapi.HfCacheManager import HfCacheManager\n", + "from huggingface_hub import scan_cache_dir\n", + "\n", + "# Set up logging to see cache management activities\n", + "logging.basicConfig(level=logging.INFO)\n", + "logger = logging.getLogger(__name__)\n", + "\n", + "# Create DuckDB connection for metadata caching\n", + "conn = duckdb.connect(':memory:')\n", + "\n", + "# Initialize HfCacheManager\n", + "cache_manager = HfCacheManager(\n", + " repo_id='BrentLab/mahendrawada_2025',\n", + " duckdb_conn=conn,\n", + " logger=logger\n", + ")\n", + "\n", + "print(f\"HfCacheManager initialized for: {cache_manager.repo_id}\")\n", + "print(f\"DuckDB connection: {'Active' if conn else 'None'}\")\n", + "print(f\"Logger configured: {'Yes' if logger else 'No'}\")\n", + "\n", + "# Show current cache status -- NOTE: this is from huggingface_hub,\n", + "# not from HfCacheManager\n", + "cache_info = scan_cache_dir()\n", + "print(f\"Current HF cache size: {cache_info.size_on_disk_str}\")\n", + "print(f\"Cached repositories: {len(cache_info.repos)}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "HFCacheInfo(size_on_disk=4576042428, repos=frozenset({CachedRepoInfo(repo_id='BrentLab/mahendrawada_2025', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025'), size_on_disk=1997004, nb_files=9, revisions=frozenset({CachedRevisionInfo(commit_hash='8bea431be57e21633be4174df291540b1fae212a', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/8bea431be57e21633be4174df291540b1fae212a'), size_on_disk=18603, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/8bea431be57e21633be4174df291540b1fae212a/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/6a2a5a6a8006e386c0e47a2a98151442046d3d41'), size_on_disk=18603, blob_last_accessed=1758400385.1923163, blob_last_modified=1758155849.9076133)}), refs=frozenset({'main'}), last_modified=1758155849.9076133), CachedRevisionInfo(commit_hash='d602c1651f3117dd8d3c7443440eb819b7aa2ec2', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2'), size_on_disk=1963211, files=frozenset({CachedFileInfo(file_name='rnaseq_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2/rnaseq_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/4411789f50aab449658c100f2758ceb410022a0a30f956fd5fe41152915580f9'), size_on_disk=286605, blob_last_accessed=1757439452.1947446, blob_last_modified=1756858618.0563447), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/1bf59ab7ca7d0595586675ff09c4767b04037f29'), size_on_disk=15355, blob_last_accessed=1758055881.95639, blob_last_modified=1757439229.7199314), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756858617.9613454, blob_last_modified=1756858618.033345), CachedFileInfo(file_name='parse_mahendrawada.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2/scripts/parse_mahendrawada.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/c1130d6f8e06f94b1441f73821e3ae727663d0fa'), size_on_disk=6546, blob_last_accessed=1757439229.7259312, blob_last_modified=1757439229.7949307), CachedFileInfo(file_name='chec_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2/chec_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/dd3e967d8c54c6056dc9fd6bdb921c3eed9d10df3a449273bd08faeb1b936765'), size_on_disk=1220601, blob_last_accessed=1757439235.362876, blob_last_modified=1756858618.0613449), CachedFileInfo(file_name='features_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2/features_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/618ea89d880c8a1b4c6d05cc3c13a70cbb05784d10bf2b6c4fc954705203096c'), size_on_disk=431436, blob_last_accessed=1757439459.1206765, blob_last_modified=1756858618.1383443), CachedFileInfo(file_name='checksums.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2/checksums.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/bee0d3fd2c2bd4f07ec16489cdba8d5b4b6a2ae2'), size_on_disk=207, blob_last_accessed=1757439229.7169313, blob_last_modified=1757439229.7729309)}), refs=frozenset(), last_modified=1757439229.7949307), CachedRevisionInfo(commit_hash='66e0cf1fc85e136990b8230d6fa80b64c1091c7c', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/66e0cf1fc85e136990b8230d6fa80b64c1091c7c'), size_on_disk=1956293, files=frozenset({CachedFileInfo(file_name='features_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/66e0cf1fc85e136990b8230d6fa80b64c1091c7c/features_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/618ea89d880c8a1b4c6d05cc3c13a70cbb05784d10bf2b6c4fc954705203096c'), size_on_disk=431436, blob_last_accessed=1757439459.1206765, blob_last_modified=1756858618.1383443), CachedFileInfo(file_name='rnaseq_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/66e0cf1fc85e136990b8230d6fa80b64c1091c7c/rnaseq_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/4411789f50aab449658c100f2758ceb410022a0a30f956fd5fe41152915580f9'), size_on_disk=286605, blob_last_accessed=1757439452.1947446, blob_last_modified=1756858618.0563447), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/66e0cf1fc85e136990b8230d6fa80b64c1091c7c/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756858617.9613454, blob_last_modified=1756858618.033345), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/66e0cf1fc85e136990b8230d6fa80b64c1091c7c/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/2ababbde8810cb0f65bfbceb4f2b5b4b65e491c1'), size_on_disk=15190, blob_last_accessed=1756858550.24473, blob_last_modified=1756858550.24373), CachedFileInfo(file_name='chec_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/66e0cf1fc85e136990b8230d6fa80b64c1091c7c/chec_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/dd3e967d8c54c6056dc9fd6bdb921c3eed9d10df3a449273bd08faeb1b936765'), size_on_disk=1220601, blob_last_accessed=1757439235.362876, blob_last_modified=1756858618.0613449)}), refs=frozenset(), last_modified=1756858618.1383443)}), last_accessed=1758400385.1923163, last_modified=1758155849.9076133), CachedRepoInfo(repo_id='BrentLab/yeast_genome_resources', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources'), size_on_disk=114475, nb_files=7, revisions=frozenset({CachedRevisionInfo(commit_hash='15fdb72f8c31ae58f3e3ce7c90be279383743a2f', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/15fdb72f8c31ae58f3e3ce7c90be279383743a2f'), size_on_disk=88406, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/15fdb72f8c31ae58f3e3ce7c90be279383743a2f/features/chr=chrII/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/d64638adb2290412a962abffe0fbd0546ff139b29064bbf313b0a768449454a5'), size_on_disk=82276, blob_last_accessed=1755812630.656903, blob_last_modified=1755812631.0999012), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/15fdb72f8c31ae58f3e3ce7c90be279383743a2f/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/645da8028fd6abe86ee305f76eab78cdf29be364'), size_on_disk=6130, blob_last_accessed=1755819093.2316637, blob_last_modified=1755819093.2306638)}), refs=frozenset(), last_modified=1755819093.2306638), CachedRevisionInfo(commit_hash='42beb28478e27c5d4c5bb2308b12100e6489ef07', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/42beb28478e27c5d4c5bb2308b12100e6489ef07'), size_on_disk=6125, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/42beb28478e27c5d4c5bb2308b12100e6489ef07/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/ab6cc475062a2767a41b2aaa006faf4d9d2d60d6'), size_on_disk=6125, blob_last_accessed=1758155946.5559895, blob_last_modified=1758155946.5549896)}), refs=frozenset({'main'}), last_modified=1758155946.5549896), CachedRevisionInfo(commit_hash='25b47a191e40728efc2437d906e84317f922b36b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/25b47a191e40728efc2437d906e84317f922b36b'), size_on_disk=5426, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/25b47a191e40728efc2437d906e84317f922b36b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/9cdf7b342b249f25f2c2cbe46a2e0a26ded4f692'), size_on_disk=5426, blob_last_accessed=1755815405.3308983, blob_last_modified=1755815405.3298984)}), refs=frozenset(), last_modified=1755815405.3298984), CachedRevisionInfo(commit_hash='aae6e5ea8139e23940d11b4d7e77684c78bb7f6b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/aae6e5ea8139e23940d11b4d7e77684c78bb7f6b'), size_on_disk=4585, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/aae6e5ea8139e23940d11b4d7e77684c78bb7f6b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/4320f8f18620d988a5fc32872a671ace406fd0a2'), size_on_disk=4585, blob_last_accessed=1755814342.1923234, blob_last_modified=1755814342.1913235)}), refs=frozenset(), last_modified=1755814342.1913235), CachedRevisionInfo(commit_hash='7441b9a8c858332e730e7ddb9d255460ae698626', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/7441b9a8c858332e730e7ddb9d255460ae698626'), size_on_disk=87714, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/7441b9a8c858332e730e7ddb9d255460ae698626/features/chr=chrII/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/d64638adb2290412a962abffe0fbd0546ff139b29064bbf313b0a768449454a5'), size_on_disk=82276, blob_last_accessed=1755812630.656903, blob_last_modified=1755812631.0999012), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/7441b9a8c858332e730e7ddb9d255460ae698626/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/e63c2087b2e56dc15e8ac6cf5693c2391127b732'), size_on_disk=5438, blob_last_accessed=1755816785.70087, blob_last_modified=1755816785.6988702)}), refs=frozenset(), last_modified=1755816785.6988702), CachedRevisionInfo(commit_hash='a5ecb243ba27ca4b4d6ad1b60cc160c033ca2be6', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/a5ecb243ba27ca4b4d6ad1b60cc160c033ca2be6'), size_on_disk=82276, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/a5ecb243ba27ca4b4d6ad1b60cc160c033ca2be6/features/chr=chrII/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/d64638adb2290412a962abffe0fbd0546ff139b29064bbf313b0a768449454a5'), size_on_disk=82276, blob_last_accessed=1755812630.656903, blob_last_modified=1755812631.0999012)}), refs=frozenset(), last_modified=1755812631.0999012), CachedRevisionInfo(commit_hash='6607f7be76546563db0a68a9365c0a858bb5f3a1', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/6607f7be76546563db0a68a9365c0a858bb5f3a1'), size_on_disk=4495, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/6607f7be76546563db0a68a9365c0a858bb5f3a1/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/a31c4647c502767c22b5fe725ec8de58686a02d4'), size_on_disk=4495, blob_last_accessed=1755813454.8008156, blob_last_modified=1755813454.7998157)}), refs=frozenset(), last_modified=1755813454.7998157)}), last_accessed=1758155946.5559895, last_modified=1758155946.5549896), CachedRepoInfo(repo_id='BrentLab/barkai_compendium', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium'), size_on_disk=3579068970, nb_files=504, revisions=frozenset({CachedRevisionInfo(commit_hash='a987ef37c72fcd07b18828d320bc4305480daade', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade'), size_on_disk=3579068970, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417769/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e640018be0b2de04e0ed77c7586c413990876e91348531f43511c20da5fe279a'), size_on_disk=11568261, blob_last_accessed=1756926331.59533, blob_last_modified=1756926333.0913236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417930/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62f17648a07c7675d6a101e3d12efeae247c742e37eaacf96f95e03dceedefde'), size_on_disk=20574235, blob_last_accessed=1756926361.2242014, blob_last_modified=1756926362.5701957), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417693/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/98a5478f5bd4feb033ac69510f2bf785c87777377b7b8b6dce24b8f89dbd6a03'), size_on_disk=18580692, blob_last_accessed=1756926319.7013817, blob_last_modified=1756926321.9783719), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417686/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6a42f24b250baf0d88696bc123f01494a400b3f03f2ff2a1fc828f9295721a16'), size_on_disk=12070748, blob_last_accessed=1756926318.2063882, blob_last_modified=1756926319.627382), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417618/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d2ce91297551bc97b4fca46007f80c8846e5f9e9c78d9c1d7ded02fc31b3ef38'), size_on_disk=9239111, blob_last_accessed=1756926302.3654573, blob_last_modified=1756926304.3714485), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417724/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e5beee12039dfe6b4dae180a31ac0897dea725754554db43e2df9f99da94db58'), size_on_disk=4535402, blob_last_accessed=1756926324.69836, blob_last_modified=1756926325.9703546), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417794/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f15be566c029c52cf46f6258e589529afb3a002f010710a54d04dd6151f8b684'), size_on_disk=10205023, blob_last_accessed=1756926335.9063113, blob_last_modified=1756926337.151306), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417788/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fd6f44471f1b86568bc059f25e5ccec7bda2b85b9f8be536aeaf2305fe015f5d'), size_on_disk=9112879, blob_last_accessed=1756926334.546317, blob_last_modified=1756926335.971311), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4331f53e52f628f99a9695fa70339b08eb69a011cc8a3cbdb08dfa8e6c5d3ddd'), size_on_disk=10779086, blob_last_accessed=1756926376.8381338, blob_last_modified=1756926377.9141293), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417777/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bd953fd8a3ee2a5c90382c1c9adc8091fbca801aa87f07a409b6dd5a9704421f'), size_on_disk=1267333, blob_last_accessed=1756926332.9623241, blob_last_modified=1756926333.6353211), CachedFileInfo(file_name='genome_map.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b8f0ed936cb43c4649126e9b7596cef0ab626a2d'), size_on_disk=142786, blob_last_accessed=1756926300.410466, blob_last_modified=1756926300.4954655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380950/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f508a5da82832b920577592b853da2c2d76ebf0adca587e10b63507f46dc5066'), size_on_disk=3452513, blob_last_accessed=1756926368.7781687, blob_last_modified=1756926369.7081647), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417846/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a3dc440aac6a69d75ec73242a30e59d8183023a2677f1576b5e73d037d6e6654'), size_on_disk=2680468, blob_last_accessed=1756926346.0562673, blob_last_modified=1756926346.7392642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417662/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/90008c75533e5a395bf92b2a4382cfb8d6c27af4921615cac22bc1965375efb5'), size_on_disk=12701661, blob_last_accessed=1756926313.0204108, blob_last_modified=1756926314.158406), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417781/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9dd7989f90f8762246868ccd7d11c9ce876b2b33099efc3a4c5bf1bc7cbcca80'), size_on_disk=6923944, blob_last_accessed=1756926333.4783218, blob_last_modified=1756926335.3743136), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417689/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e27d1d153e5b135a09ec5db80bb05ace721fd60ac90f0693ac34ca3160fdfbdd'), size_on_disk=5015020, blob_last_accessed=1756926319.2973835, blob_last_modified=1756926321.220375), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417909/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c5de46a74c16aee32dfb2be89a50dceaa5704c0c550076dcec483d1c21d983af'), size_on_disk=5969725, blob_last_accessed=1756926357.3362184, blob_last_modified=1756926358.5162132), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417856/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3dc4e868f31759c9ac3d43f40097f5ed2f0efb6307e3bc1ccbed70c50a16ee4e'), size_on_disk=6126482, blob_last_accessed=1756926347.3832614, blob_last_modified=1756926348.3162575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417734/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/10d4c6d9acc5b56370fc32062262ae3871008e20e960c6b352ac27a4e229c905'), size_on_disk=1495194, blob_last_accessed=1756926325.9453547, blob_last_modified=1756926326.745351), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417669/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8eaf08a45dbd72787c5250403f3d3079db9902e77527c7ca438abc740fdfb753'), size_on_disk=7376929, blob_last_accessed=1756926314.6724036, blob_last_modified=1756926316.0383978), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381044/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/89a79f2c5f6258380ff4776af49db6119ac0c04342686705cfde94feebd7c9ca'), size_on_disk=3576881, blob_last_accessed=1756926776.065759, blob_last_modified=1756926778.8987432), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380932/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f214422d02adb2b0a1037eb424d8574123cf27d8a5cd1418feb5733b0249f60c'), size_on_disk=6251591, blob_last_accessed=1756926365.6181824, blob_last_modified=1756926366.5551784), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417937/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e432985b09ba72eab54fc008b87b20a718dbae96d920a61b47de7e16d214227c'), size_on_disk=6445266, blob_last_accessed=1756926362.6341953, blob_last_modified=1756926363.6641908), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417754/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0d840353f1148a08928dfb3177a0abfe635c406fc77d6d9958743585ade6b872'), size_on_disk=1101891, blob_last_accessed=1756926329.758338, blob_last_modified=1756926330.215336), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417757/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6c9c5dbd0513759ace8599b2542a1f99fe0c30feb318665ebdb55bfb111c8c67'), size_on_disk=5834465, blob_last_accessed=1756926330.011337, blob_last_modified=1756926330.7623336), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380964/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3a63df07de544662fb04d5388f34b0c8068efbc19f464b673d8a860a57b5b422'), size_on_disk=956844, blob_last_accessed=1756926370.333162, blob_last_modified=1756926370.8741596), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380926/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fd708dfb8084d767fde32c52acb58e785083ce7becbd2e160277f31460719346'), size_on_disk=6579161, blob_last_accessed=1756926364.8451858, blob_last_modified=1756926365.5441828), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417874/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6674c316eab3b03bbbe3502565efff7650445f5e1453933336d74938e42a14bf'), size_on_disk=6201897, blob_last_accessed=1756926350.5772476, blob_last_modified=1756926351.5142436), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417610/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a93c708ade83c1c4521589a51835a1dcc98972ab900e0eefdb8ce34e64394bf4'), size_on_disk=2566580, blob_last_accessed=1756926301.328462, blob_last_modified=1756926302.4814568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417793/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8a51bd4d953e697022a85f9b527afd45126988ba4bbb796d71c9e5f6c8fffc44'), size_on_disk=10797388, blob_last_accessed=1756926335.4883132, blob_last_modified=1756926336.7013078), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381050/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f3c3bac37a1d4ec633edf53ba305c970eb3de412fd54dfbaf0cdf89f39574334'), size_on_disk=1518412, blob_last_accessed=1756926777.1687527, blob_last_modified=1756926778.028748), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381016/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0e597280417f8f8b5b476bc9d66033afc9412759f5e2f9bd9f3367932cc6f03f'), size_on_disk=820782, blob_last_accessed=1756926377.9791288, blob_last_modified=1756926378.4041271), CachedFileInfo(file_name='GSE178430_metadata.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE178430_metadata.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9673758f13ec096ec4a89642bbe34d60975f5f05'), size_on_disk=61, blob_last_accessed=1756926300.2374666, blob_last_modified=1756926300.2994664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417670/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5e36b63ef1fbb55f821ed2c3d61f0d0bf86cf00846e86ff9dbe6f4d9597f9dc8'), size_on_disk=10881106, blob_last_accessed=1756926314.7704034, blob_last_modified=1756926315.980398), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380944/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7331bfc92dc902a721083ac4ea027481c90ce204d7dca7c493fec8f284db0155'), size_on_disk=8432479, blob_last_accessed=1756926367.299175, blob_last_modified=1756926368.6141694), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380928/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/251ffcb8e06f2ac0db0791065248327a9d1eeef33a07f5c6b25946e04b6c46d8'), size_on_disk=2289693, blob_last_accessed=1756926365.037185, blob_last_modified=1756926366.1961799), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380992/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dbc6b1de202e7fb25701c02607facc3e8c332550907ca0b70ba177533a311d6d'), size_on_disk=2551903, blob_last_accessed=1756926374.2281451, blob_last_modified=1756926375.0341415), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381068/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/16e4412527449a3cb9d2da129c3309a71b2ec9b33a7a39cb9bb31d41ce5d2e04'), size_on_disk=14909067, blob_last_accessed=1756926780.5687337, blob_last_modified=1756926782.3187242), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380961/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fc4356aaa2a30217145b4aecc4f3fdd2575fcd696a2603fea3d8112998f770c7'), size_on_disk=798599, blob_last_accessed=1756926370.1791627, blob_last_modified=1756926370.589161), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417612/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ee40f7af39e729ee79ef18d6777bbd632b7cc7811041a32d54f4a02b42638464'), size_on_disk=3509045, blob_last_accessed=1756926301.5874608, blob_last_modified=1756926302.6274562), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380933/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/07c22e11c56ce5ecb6c22c910c3d3a0d9b54fd341bef0513b4bf42a9c53ec6a0'), size_on_disk=10138164, blob_last_accessed=1756926365.8761814, blob_last_modified=1756926366.7471776), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380955/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/369e0f1de51ea197e928618e562613eeed2ed59ce5c1389ce07ff6dd96d391b4'), size_on_disk=3596907, blob_last_accessed=1756926369.4561658, blob_last_modified=1756926370.2661622), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380934/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d264f2a91c87696f6a07f573f1d921c91e1d53e3afca59610ba4703b3683b617'), size_on_disk=4601537, blob_last_accessed=1756926366.1091802, blob_last_modified=1756926366.9191768), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417755/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f4ffef985aed69a80417b5179ef9923bd73378a93a97f2dff128b1f7716b4599'), size_on_disk=8437424, blob_last_accessed=1756926329.777338, blob_last_modified=1756926331.2423315), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380967/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3abcaaa4db306d580dc27882172ced53d591b8aa69a1805bd2a9eea0448ff941'), size_on_disk=1245882, blob_last_accessed=1756926370.6731606, blob_last_modified=1756926371.1021585), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417903/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3ddfaef9dc57d0f63b8613408aba3af803dabd5ee87a1584edd9391b2b41bec3'), size_on_disk=11218813, blob_last_accessed=1756926355.9432244, blob_last_modified=1756926357.2232187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417888/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/961a35161b04a0e892f46a0a90b6d4776d7fe07ef82fc7016c95b1a885c4274f'), size_on_disk=9651505, blob_last_accessed=1756926353.298236, blob_last_modified=1756926354.65023), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417666/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/38008595447ab722c1889d0e19babf74299822ac7733504a4f09e1af1e4ad1ac'), size_on_disk=7712422, blob_last_accessed=1756926313.945407, blob_last_modified=1756926315.073402), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417837/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/04d099809ca78bd87dcb9921398681ac64e827439049a37e97a71ba0828debfb'), size_on_disk=8348990, blob_last_accessed=1756926344.4012744, blob_last_modified=1756926345.6152692), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380939/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dc6382d9dba7855504a6c7fa2a4adc44f564e25ff5c54eedd082c3cbe0a520a0'), size_on_disk=9235430, blob_last_accessed=1756926366.630178, blob_last_modified=1756926367.5861738), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417667/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef18f0ec136ef4b0facd217b32604556a7c9f514d75e26d2be17eb8da9b74a14'), size_on_disk=7979245, blob_last_accessed=1756926314.2284057, blob_last_modified=1756926315.734399), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417814/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9f70b1469a6d0ad8758aa7a44e754e07ccc401906f8af403a72075d763919535'), size_on_disk=7618977, blob_last_accessed=1756926339.5142956, blob_last_modified=1756926341.0872889), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381058/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9dc97b5892fcc8af593c03fad2040396b9b10626a655319ad40b38acd75e1d7b'), size_on_disk=11534270, blob_last_accessed=1756926778.543745, blob_last_modified=1756926779.7317386), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417730/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f2554716083cfa1bb54c2098315b92e1b3537eb265da884c00ba7e7d4c050da'), size_on_disk=14980864, blob_last_accessed=1756926325.5143564, blob_last_modified=1756926326.803351), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bbfeb8571ee8915d793698c2116cef9f2ea61573c66b4ae7066078271c6172d6'), size_on_disk=5132301, blob_last_accessed=1756926362.6441953, blob_last_modified=1756926363.7271905), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417830/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/21e2dbb49f07cb7c2d7277f5bf1a442e216a2991677c3c4ece5c51316795ea77'), size_on_disk=12760016, blob_last_accessed=1756926342.9432807, blob_last_modified=1756926344.1602755), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417614/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dac699f09872b0c58b742c692b341f0b00b7d83284cec5ef838c276a6f48908c'), size_on_disk=1759475, blob_last_accessed=1756926301.7274601, blob_last_modified=1756926302.4774568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381026/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e41f2d7a1b084a404e7f5e9e003b27f88665a983ec73859f7eb42cd8bfa2bdd3'), size_on_disk=2857045, blob_last_accessed=1756926378.898125, blob_last_modified=1756926379.7281213), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417721/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5457a24eab864fb369ae0e6fbdb302876e2a6c65ae83444f18bc01ad08ecfc0c'), size_on_disk=2073045, blob_last_accessed=1756926324.3243616, blob_last_modified=1756926324.9983587), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417843/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6569e23f0eb660e9f7111b9cdd69e7ae797ba265647ee44e4cbf2d8babdeca9e'), size_on_disk=2706587, blob_last_accessed=1756926345.3402703, blob_last_modified=1756926346.125267), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417752/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/efc9f1ecf4a7b06b23da46fb8c7d1879b67a9d6603ed753d09e2028396ec05c5'), size_on_disk=3420764, blob_last_accessed=1756926329.26534, blob_last_modified=1756926330.0613368), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380988/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/27b6e71986cf2b7da20b64dbda0ba47e08753f7184ddd57ed288f11df88d21e1'), size_on_disk=16063760, blob_last_accessed=1756926373.7421472, blob_last_modified=1756926374.932142), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381054/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1a9f9c650973937d852d2de54c0d73336d1516184d29e203563fd9bf8d7fefa5'), size_on_disk=3346485, blob_last_accessed=1756926778.1077476, blob_last_modified=1756926778.8637433), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417665/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7c9b3d448e53fcac9334df092cc003f438cbc462e6785ac3d61202bd65a7125f'), size_on_disk=7481605, blob_last_accessed=1756926313.490409, blob_last_modified=1756926314.7744033), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417945/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6689ba375c993d00d1f0e716d3bc39604d68df654a67b2518c56f4d54c7f4ca6'), size_on_disk=11289854, blob_last_accessed=1756926364.0191894, blob_last_modified=1756926364.9011855), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380981/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/198a0b08846a11e72de25e0e9c44f495ef88c9260de2e036d240715004c04953'), size_on_disk=8535317, blob_last_accessed=1756926372.5771523, blob_last_modified=1756926373.8291469), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381040/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a7c622a8ae1f5083eef26ecbcc2444055a9265b674ae80002e5468c94c0eb070'), size_on_disk=9682671, blob_last_accessed=1756926774.782766, blob_last_modified=1756926776.7967548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381019/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3023e878b223b00b77d3411a6d20195b85aa7e61a8445fe725ff694c77816053'), size_on_disk=1022843, blob_last_accessed=1756926378.3191273, blob_last_modified=1756926378.864125), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381032/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/335a75b123ae38273f1b1a10f16e9547eec356f68a1d3078926f011d3332e2b5'), size_on_disk=656385, blob_last_accessed=1756926379.6781216, blob_last_modified=1756926775.4077625), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380979/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef743a2a6a8fe44b62963b981ef0429676e7854243ab07d450d9c3ab569c7e8e'), size_on_disk=3735979, blob_last_accessed=1756926372.2201538, blob_last_modified=1756926373.6791475), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417867/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f4783b00d4674df92d84f9a304fb9d143c7b9e4754e3efcb9c072a49a9b9298b'), size_on_disk=8536709, blob_last_accessed=1756926349.3882527, blob_last_modified=1756926350.2882488), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417644/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0c788a7ba70689a796af823df5cc14504dfdc854617952459367364c4498cdeb'), size_on_disk=949200, blob_last_accessed=1756926309.758425, blob_last_modified=1756926310.6444213), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417899/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b4349f94cb0679468e9c28a9439c1a71c88d7288adc3bdad0ca31a2590f1d4e0'), size_on_disk=7910137, blob_last_accessed=1756926355.3832269, blob_last_modified=1756926356.3022227), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380957/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5a2362d35ac90fb1926051e6313886a649074b0022b128741a22a5ee24bb095d'), size_on_disk=5388789, blob_last_accessed=1756926369.5311654, blob_last_modified=1756926370.8571596), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417876/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0713329434bfbd55f1998400d9d44c1d8c7fec6cb1b646c0fdb926f1046f9b47'), size_on_disk=11246388, blob_last_accessed=1756926350.971246, blob_last_modified=1756926352.0472412), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417636/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4a3b2c7076107dcc47a87179cecab9e2051350c94e951a7aadc2ff0cf6fdbaca'), size_on_disk=43479381, blob_last_accessed=1756926307.6494343, blob_last_modified=1756926311.4484177), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380968/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/549ef4e39a437adb99efecdc9b05fee3ca4b31c878b99f7a4cf1398f405ca049'), size_on_disk=3575926, blob_last_accessed=1756926370.7441602, blob_last_modified=1756926371.5941565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380925/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdf190d30101829153acfd62edd4d689ac65a7d737f0e80978c7d460819b5caa'), size_on_disk=9220100, blob_last_accessed=1756926364.6101868, blob_last_modified=1756926366.1331801), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381046/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a96c1819b1010c40fc4d92418e407cc5b5f48f9e24883d4b4cdf3e02d23b688a'), size_on_disk=3071378, blob_last_accessed=1756926776.6277559, blob_last_modified=1756926777.855749), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380974/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ea9eb19e91fedadd9e46d5d7dc9169995065af5b6b8e823f1667117be9d737d0'), size_on_disk=8889742, blob_last_accessed=1756926371.5481567, blob_last_modified=1756926372.2801535), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380931/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/be287270314fed3dded81e0c24a66d8bd991b219fd6a157f1aeaa1690262e563'), size_on_disk=4324179, blob_last_accessed=1756926365.1611843, blob_last_modified=1756926366.1241803), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380940/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4f59a7f9287c427bed27910f08a041e460eff2490c21e2fbdaa5cc9348f5921e'), size_on_disk=6062382, blob_last_accessed=1756926366.8141773, blob_last_modified=1756926368.3051708), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417635/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d2d1c241467bd5c55cf059f793b4236c99d48e59bec853d1052ff6c644e50d1b'), size_on_disk=3044373, blob_last_accessed=1756926307.2014363, blob_last_modified=1756926308.391431), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417855/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ac2218ccb9f80664ce377351ea1dc84bf3f70b36006a86cffdb761a40ddab791'), size_on_disk=1981158, blob_last_accessed=1756926347.3992615, blob_last_modified=1756926348.3062575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417653/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/59233342d3c3b71fce95d5295cf7c1e5ef3f4254dbc120229cb83b6c8115c759'), size_on_disk=12208357, blob_last_accessed=1756926311.136419, blob_last_modified=1756926312.3904135), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380977/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/74237b16fd38090785d203d96bae72e1f5ce919d423f793cfb9d3b86623358e6'), size_on_disk=11114601, blob_last_accessed=1756926371.6651561, blob_last_modified=1756926373.12615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417894/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/12f262a12783317d135ffc5e1d281a3d43b746a82ed8053c15de824b94bd5ef8'), size_on_disk=6449687, blob_last_accessed=1756926354.4772308, blob_last_modified=1756926355.4742265), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417771/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8fe5513ae86b28ecca3d4c70bdb73097f578dd31b4d08a072960b61550f582fa'), size_on_disk=3970195, blob_last_accessed=1756926331.9313285, blob_last_modified=1756926332.8643246), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381070/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/32c8f4c4efdbe6e137add61b0e31c27ae6a9eb5e8b463e9e4410a27cc51a57d3'), size_on_disk=29864184, blob_last_accessed=1756926780.7487328, blob_last_modified=1756926783.3167186), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417764/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8a30e54d96d5b83981e37a0d8f6cae9e6584819a76a200f2730d9de32e282d02'), size_on_disk=2641764, blob_last_accessed=1756926331.099332, blob_last_modified=1756926332.088328), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417749/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8460c24102627d611c8918a1620078ee1bdf9929c44cdca863cd0dbb23f9cf1f'), size_on_disk=4938551, blob_last_accessed=1756926328.814342, blob_last_modified=1756926329.7143383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417924/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f2053fcaa7f21b1703e3491e167b457c29759b2fe9535f0cb2bfa257a1b86cea'), size_on_disk=6988370, blob_last_accessed=1756926359.940207, blob_last_modified=1756926360.8152032), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417732/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f99cdb10c12a75e1d020125d68b5c5302263eaef050b5c6b0032e27a0ac9a23c'), size_on_disk=685742, blob_last_accessed=1756926325.7463555, blob_last_modified=1756926326.326353), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417631/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b4b8c985528935ec46c6c7e78482af1ed26238137fc63b2196c6631e2d3796ab'), size_on_disk=27651513, blob_last_accessed=1756926306.2814403, blob_last_modified=1756926309.1994276), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380929/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aeddf7cf3aee11ee08c90d305e66fc682e675959c171ecfaa591bc1d9015ebbc'), size_on_disk=6656759, blob_last_accessed=1756926365.1011846, blob_last_modified=1756926366.0181806), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380975/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ea018f4882a60f5fb07a4bbae51bd494e44e6df9b215d9da5e405687a567dcbf'), size_on_disk=8369150, blob_last_accessed=1756926371.6051564, blob_last_modified=1756926372.7471516), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417727/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/414c702fdf3dd97c536f3237d2c38e0ef5ad8d08bb7c991ee252e16d57b201b6'), size_on_disk=2636905, blob_last_accessed=1756926325.0683584, blob_last_modified=1756926325.8093553), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417673/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/535d074f70f4553e24f3171fe5cfbd1821635ce7ca6ef93f8665876de620d6c9'), size_on_disk=6559693, blob_last_accessed=1756926315.1844015, blob_last_modified=1756926317.0733933), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417659/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/20f21bb1ba86fdbee1918776793b1ff2275be49933c72eaba90b8be8582f7692'), size_on_disk=15311232, blob_last_accessed=1756926312.2364142, blob_last_modified=1756926313.8474073), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417826/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3532f474125bbdc0445e46fe8be83801ca9c079afb1dedd7cd95e25994c50d17'), size_on_disk=11492099, blob_last_accessed=1756926341.9132853, blob_last_modified=1756926343.2502794), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417609/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e3d8451242fe9ecf9f82e2ac1a406144840a6182178228e6e1fa67e6a04a318a'), size_on_disk=3176651, blob_last_accessed=1756926300.651465, blob_last_modified=1756926302.0864584), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380942/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a958d997c1aff07ec07304485ee818280bc7714dd9c485ea73ef7fda81f2fe63'), size_on_disk=11407010, blob_last_accessed=1756926367.109176, blob_last_modified=1756926368.6211693), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380982/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d1e44b0b82ea22fb6c513ce02b49facf5f38b64b2baa91b24942fc6858125e37'), size_on_disk=6977161, blob_last_accessed=1756926372.8241513, blob_last_modified=1756926373.4951482), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417792/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/196cd0cf279f6bc85ca0dbeb1f089721f63eb167644d999b45d8cfe9a1f52acf'), size_on_disk=11982413, blob_last_accessed=1756926335.4593132, blob_last_modified=1756926336.651308), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417681/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6e4ea424e9eab4dc3ea304621992e1db158565a8ce30374980b1084a653a0132'), size_on_disk=7689435, blob_last_accessed=1756926316.934394, blob_last_modified=1756926318.231388), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417859/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e4c5915df6cb5bb450e1948d8c43bbe031d09ec345688ab51f2d8ea60f3be09f'), size_on_disk=12709203, blob_last_accessed=1756926347.8872592, blob_last_modified=1756926348.9972544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417692/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/82405dc0a21bd225cc6a9e813e62d4dc0522719abf1e677c954a6ec38763b306'), size_on_disk=4873514, blob_last_accessed=1756926319.6253822, blob_last_modified=1756926320.6443777), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417875/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8dc3634dae23cad7f562d15903341a67d3048929bd2198e53f71529b00a3508a'), size_on_disk=11490812, blob_last_accessed=1756926350.6762471, blob_last_modified=1756926351.7832425), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381042/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/80a0c18e8d146043e9c95c6a86552d28e28267a489b3e5c6f2a11319cf47b877'), size_on_disk=4477936, blob_last_accessed=1756926775.660761, blob_last_modified=1756926776.9177542), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381053/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6ca1dc55841bbb6d8d8a83d1a87d00570c1eea7cf1963db8cb04ddd427711fb6'), size_on_disk=10277654, blob_last_accessed=1756926777.9197485, blob_last_modified=1756926779.9187374), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417746/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/432442dbdd5578acedcbf6eddc14cfe0d460facc7b0dea857225fbd4e6a4242c'), size_on_disk=2726090, blob_last_accessed=1756926328.6353428, blob_last_modified=1756926329.1983404), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417628/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/291f0e4d2d0fa1656de1583bd9e131fd4d2fc1934d0bb70a6774f7e7f90e62ae'), size_on_disk=15444530, blob_last_accessed=1756926305.1314452, blob_last_modified=1756926307.1334364), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417944/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/01e0e606e4e8b2203e6497ffc8c231bce8672a43d83098ba838c8edfdb052c27'), size_on_disk=4563261, blob_last_accessed=1756926363.89319, blob_last_modified=1756926364.6951864), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417883/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8e23cc97e00adfd244a989f83702f4ce7a998ba7998dd95908a1b4ec256bc054'), size_on_disk=10709227, blob_last_accessed=1756926352.4542394, blob_last_modified=1756926353.675234), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380980/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e36f58da696f0827c72fed8647c2a45a0499fdc5d1859386dbf025388f07e16d'), size_on_disk=2288305, blob_last_accessed=1756926372.371153, blob_last_modified=1756926373.2051497), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381035/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/410edf5a6097a77bcec6880ec2618b4aec4bc10c44607163fe2b27aca0a05117'), size_on_disk=1451911, blob_last_accessed=1756926379.8691208, blob_last_modified=1756926380.5191178), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417706/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4f1ea5592a855f3bf5739b0caae1c69c7606276d125c3dfe8e98379d12bedf1b'), size_on_disk=1154142, blob_last_accessed=1756926322.9053679, blob_last_modified=1756926323.5173652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417655/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/25d98ba17be9b354b0ec7a0e513a6ff9d4593dd1cadf269f28bcea1ca9c71565'), size_on_disk=5461224, blob_last_accessed=1756926311.5504172, blob_last_modified=1756926312.9474113), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417790/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f88bed27521de8285bbb92cf95242820116e6fa2c23ab578e6608a6a25e0c04e'), size_on_disk=10836858, blob_last_accessed=1756926335.078315, blob_last_modified=1756926336.1843102), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417679/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bc48687cfd5c03c1a71787981180295dd3a27cf4d2c46bd26bb68cdf8b35925a'), size_on_disk=17346576, blob_last_accessed=1756926316.1203973, blob_last_modified=1756926317.84439), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380954/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/338886924ec42501a99a4b842e18fdbed28200d52daaf43e00ee601a147e0188'), size_on_disk=4903852, blob_last_accessed=1756926369.2841666, blob_last_modified=1756926370.101163), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417696/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2e1bce702520ca07d9037bd4de4d21bb30ef6b7fdf7de9d75ea232acc036a72e'), size_on_disk=31303382, blob_last_accessed=1756926320.7203774, blob_last_modified=1756926777.0657532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417627/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4027ab6ecd21364ca365bbe7192797ed924c817e6edc841d750111b2e3f6f45a'), size_on_disk=21089243, blob_last_accessed=1756926305.0144458, blob_last_modified=1756926309.6264257), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417763/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/91d8a58ead35fc52f7c2649ce9509a92236a20ca8c26f5f2ef4ba1a38e64afb1'), size_on_disk=230495, blob_last_accessed=1756926330.8943331, blob_last_modified=1756926331.4653306), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417902/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2d72fc510cd348af7611ca170b7804b228b74bd38e7ff5e437375c1cc926fa95'), size_on_disk=8952511, blob_last_accessed=1756926355.9322243, blob_last_modified=1756926357.2422187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417939/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a0fc7804b026f95809f33087deb75f00beeb9635475b62864d3e58e948361bfa'), size_on_disk=9099632, blob_last_accessed=1756926362.9701939, blob_last_modified=1756926363.9411898), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381038/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/415f62b8333a02df8a87ba85e1d0de30a6cc9fdb07f7c8dc02d30f81ed4c14ef'), size_on_disk=2424198, blob_last_accessed=1756926379.8751206, blob_last_modified=1756926380.4921181), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417783/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9603c95097c451e616c54d52d3474b866453409905ea16365d019e77c6ca31b3'), size_on_disk=4176778, blob_last_accessed=1756926333.6983209, blob_last_modified=1756926334.4773176), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417947/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/50bb9cc74904c9d3b6cc3ec8bc12c7ad1fb89204be6f89a863e703a94d224512'), size_on_disk=4023877, blob_last_accessed=1756926364.0161893, blob_last_modified=1756926364.9561853), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417823/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1887333f71d11fe7c43748662f58d9ba3fb12f3f6ca30a61d7e1c7a0c95f7064'), size_on_disk=9444476, blob_last_accessed=1756926341.6372864, blob_last_modified=1756926342.8442812), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417678/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/825d9bcd10c008b68cc84e92f20608c0d52770ac028bbc4a9219f1cd210ab8c4'), size_on_disk=29320273, blob_last_accessed=1756926316.0503976, blob_last_modified=1756926318.3993874), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417860/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/72e751d246e9b938ce9ab4380e2a1114ecd3c36d5829e3298f391c1edfefae24'), size_on_disk=3996315, blob_last_accessed=1756926347.9312592, blob_last_modified=1756926348.819255), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417932/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f040d4f594fe69143a7c3f04f0498be2a30fb9b5e78d42ec464fee65efcc42dd'), size_on_disk=9231914, blob_last_accessed=1756926361.4262006, blob_last_modified=1756926362.5771956), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417731/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/39ec5969bc8f3acceb3aa30b72f0c0101785a36da340f3983ad00d8cc80160a4'), size_on_disk=1557745, blob_last_accessed=1756926325.658356, blob_last_modified=1756926326.2043536), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417759/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b2dad4cf4b03c846adf9d5229aeab340e58034c751bc68b37bb0c8402a7c071b'), size_on_disk=1372312, blob_last_accessed=1756926330.1243365, blob_last_modified=1756926331.1623318), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417791/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8d2eb1bdd1a5cae023abb271e5858cf111879953cb0d6267f4ebf21a3d30c4ad'), size_on_disk=5232904, blob_last_accessed=1756926335.2033143, blob_last_modified=1756926336.417309), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417919/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f5504428a85399cb212c74235abf9cec068dba03136a87ea79eae6d63a3cf430'), size_on_disk=7011211, blob_last_accessed=1756926358.6892123, blob_last_modified=1756926359.8312075), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417911/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3ca8b1b14564fbc90b44995a61a34d5a67482eb5fd867ce4f9f2196de2b73539'), size_on_disk=3051177, blob_last_accessed=1756926357.404218, blob_last_modified=1756926358.3022141), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381027/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ba9aba2cc5b9bd9e83599ccba5920f028ac9c6936436dac50f45c606f3abd841'), size_on_disk=6537548, blob_last_accessed=1756926379.0151243, blob_last_modified=1756926775.5467618), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417779/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/918376b5cb0b63e3e531ac990854e5a7b461e7251f5ed562c5a92cbf6a5a0141'), size_on_disk=9459105, blob_last_accessed=1756926333.1593232, blob_last_modified=1756926334.2033186), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417926/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/303d44a38fbb646cf211ce1bd03fe2d24bb004f385c63d05ab8e707c5b283f0c'), size_on_disk=10766909, blob_last_accessed=1756926360.6072042, blob_last_modified=1756926361.6751995), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380993/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d568d29486ad3ec6f67f87b78cacb8b7102c591f979ec2bdb17ff75bde789670'), size_on_disk=3031778, blob_last_accessed=1756926374.496144, blob_last_modified=1756926375.2661407), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417857/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/64f710e0b8a62bb6eddf3508f133b6b82af8bd21dd07f2319af6d07981085b1e'), size_on_disk=11109477, blob_last_accessed=1756926347.471261, blob_last_modified=1756926349.6202517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417929/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/85b194132215311994e48908799e31357d1094663a232e8170a4edcfa5a11eff'), size_on_disk=9155231, blob_last_accessed=1756926360.9862025, blob_last_modified=1756926361.9231985), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417923/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a32971eb62fe5c4c08298b1bdd97a916d8ef8f5898aee42aaf3fbaa8069e92d1'), size_on_disk=10004280, blob_last_accessed=1756926359.919207, blob_last_modified=1756926362.0851977), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417890/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3eca80a3220fd0294fdfca83740ba70a20af764d28833494948705c4d542ded3'), size_on_disk=9018889, blob_last_accessed=1756926353.7652338, blob_last_modified=1756926354.7252295), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417739/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1c9e3fcff1dc467673efe70177cb4d678d7eff7b5b2bb3ded247d7a458b466dc'), size_on_disk=21974051, blob_last_accessed=1756926326.8623507, blob_last_modified=1756926328.7393425), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bd149efcdaa899981b965dc424d16fb66668a7666a059c6bc5043572331394c2'), size_on_disk=4513644, blob_last_accessed=1756926377.1751323, blob_last_modified=1756926378.1161282), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417865/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/348bfc1685f79e9ec7f559e96b61b8c43628d611378b6c4ead8bffea44415f1d'), size_on_disk=9233047, blob_last_accessed=1756926348.8982549, blob_last_modified=1756926349.9182506), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380995/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e0cae334f1cb71be0be1e8ceb5fc6ad23d22e4db20cb64b1314c3d080f2bb5f7'), size_on_disk=6315860, blob_last_accessed=1756926374.931142, blob_last_modified=1756926375.7271385), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417941/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/30188c510d2d7041e1525e304edc8f25b569a1771cf00844cb27300a052441e8'), size_on_disk=7032333, blob_last_accessed=1756926363.2551925, blob_last_modified=1756926364.2891881), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417824/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/65d997db1339fc5a8380a61fcf1ab1a6d28fb9142fc7d78c89236dc218b91ea3'), size_on_disk=8218856, blob_last_accessed=1756926341.7612858, blob_last_modified=1756926343.0512803), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417626/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1981a2a1e321acc5c35bbeb9b61a12636c27f28d6e216de20093afdbee9f8689'), size_on_disk=12046611, blob_last_accessed=1756926304.4464483, blob_last_modified=1756926306.2084405), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417832/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/268a3b7a91ccc3f3909042a711454c336b1887705de99442bda022bcbc26a01e'), size_on_disk=7655304, blob_last_accessed=1756926343.2332795, blob_last_modified=1756926344.3042748), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380946/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62fe0084bf7a8f9f484e76366f203e1c68862a211d5d501c08e19b8f28678d6b'), size_on_disk=8090103, blob_last_accessed=1756926367.6871734, blob_last_modified=1756926368.7681687), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417722/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f29055ed874a3a40735526808a6739884efecd8d7c5e7e9eb1fcaa8e495ade3'), size_on_disk=345818, blob_last_accessed=1756926324.5753605, blob_last_modified=1756926325.2483575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417633/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0b38824aeb2fac57eca256e3652539deb4bc221eeb5afc3701a997885f9ad9d0'), size_on_disk=19311587, blob_last_accessed=1756926306.8324378, blob_last_modified=1756926308.847429), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417821/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c29db68a3430cf8cb01716d90a92105387777ed1361992eb9501271c35359cfb'), size_on_disk=6597050, blob_last_accessed=1756926341.1692884, blob_last_modified=1756926342.2702837), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417690/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/283af9b17d776b75f6ff6ad1f426d6d0e0e5aa1f2089a20910edcb6763191b69'), size_on_disk=12265448, blob_last_accessed=1756926319.171384, blob_last_modified=1756926321.4153743), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381069/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aa8f6186814bf973f8b4448a0c4b692c31594e03905c75d5691b982d6ccde97d'), size_on_disk=13705106, blob_last_accessed=1756926780.5807338, blob_last_modified=1756926782.319724), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381011/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/839fea471c67e08e0b68ae6344432c76da559e67c505c3f6120b20db2f1f753e'), size_on_disk=4378212, blob_last_accessed=1756926377.2971318, blob_last_modified=1756926378.0891285), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380952/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3811d6e6194d8b88c84fc0931cba844da23677e76e1e9a25e2890df968a7e529'), size_on_disk=1003172, blob_last_accessed=1756926368.8351684, blob_last_modified=1756926369.4641657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417660/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c394d7b0cfca3f3782dca86380e1a55903111d32efde98173af8a0b726ef5e7d'), size_on_disk=6441490, blob_last_accessed=1756926312.4744132, blob_last_modified=1756926313.4024093), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417811/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/da92b1c1579a2295da085050305367bd02a47fd52a74d1ec2a9f227b5e808b11'), size_on_disk=6127159, blob_last_accessed=1756926338.8782983, blob_last_modified=1756926340.1172931), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417829/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/63a9af789279180a07d8e8afe07bb0e9b8aa32c4340e01347a6f7ee809198308'), size_on_disk=5677879, blob_last_accessed=1756926342.904281, blob_last_modified=1756926343.7332773), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417716/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cd98cf97346b62e4f1efcace598c9c273d9ad9b8d8692e35f50b11d46f7ed8a6'), size_on_disk=5100666, blob_last_accessed=1756926323.8513637, blob_last_modified=1756926324.8793592), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381039/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5f95b6de8d0d73589c612313eaed6ce1e6adf734e795719f23df6f54cbbedc69'), size_on_disk=1656495, blob_last_accessed=1756926379.8751206, blob_last_modified=1756926380.283119), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417872/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7d71ca12594ab85797fca8c1b88947cb0f3ece93596f1a4c5adc04c11c0abf68'), size_on_disk=8522283, blob_last_accessed=1756926350.1742494, blob_last_modified=1756926351.180245), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417695/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d6ae8d39c2949235dd4d82e7a0e56d9f20c20ba5f1b7a445fbba1f0df29c88b7'), size_on_disk=7710772, blob_last_accessed=1756926320.4913783, blob_last_modified=1756926321.959372), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417887/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/58707813ec63af2eb1a72b0c8317b4701b4777df8f828f2e08a0cbb1ccfa534f'), size_on_disk=9080847, blob_last_accessed=1756926353.0712368, blob_last_modified=1756926354.412231), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417709/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/395b1dd171ab9759e06a22580672bef65aac2acd7d548e38098d207c80071d89'), size_on_disk=717131, blob_last_accessed=1756926322.9983675, blob_last_modified=1756926323.6373646), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381023/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/22e3e0f4cd1f2c755ec1ac13b99d2d6c98d0158319478f2deca4e89aefc5e88e'), size_on_disk=3860195, blob_last_accessed=1756926378.5651264, blob_last_modified=1756926379.6101217), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417949/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6123bc7bd312e6e37fabadee561c7dacad32d26bada92c9785c783f94fc50a37'), size_on_disk=1320707, blob_last_accessed=1756926364.4151876, blob_last_modified=1756926365.0891848), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417625/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/96b04f28e74e25bd1502a4ea9aca8047bfa8462e61cced0d726af4a336e1a585'), size_on_disk=17384720, blob_last_accessed=1756926304.4014485, blob_last_modified=1756926306.2074406), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417853/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5995033a04f017892b30c025a1527f9b6420bf1e7636c466cdd8d1462cf228c8'), size_on_disk=2186899, blob_last_accessed=1756926346.9012635, blob_last_modified=1756926347.8172596), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417713/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c5d75f4a32cadceee844da0c5ce703483f575fb927990a1615ee37205137d986'), size_on_disk=524444, blob_last_accessed=1756926323.533365, blob_last_modified=1756926324.045363), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417921/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/05fb4ff0001b88b711eee9f29ae30642d940667f7166121c9782f464057880f5'), size_on_disk=7891276, blob_last_accessed=1756926359.3782094, blob_last_modified=1756926361.2442014), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417611/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c82d882f6538d65316dd01e26a82af13e41717952c79e20dae6c3f1d9724ab60'), size_on_disk=3720891, blob_last_accessed=1756926301.4614613, blob_last_modified=1756926302.2704577), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417684/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eee25a9a91c54d0404db884532d1b85555f2e1de0c664abcbda4343f9d729842'), size_on_disk=10310520, blob_last_accessed=1756926317.7173905, blob_last_modified=1756926319.3473833), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417772/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a21b204a2da7654dd5bf4e7965a3c1e19159c16052232030787175a830ff0233'), size_on_disk=8204405, blob_last_accessed=1756926332.1323278, blob_last_modified=1756926333.453322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417725/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4195b9ed6506437a735fd8061cd1b79211c99169c4319c4f72b801a501f2b0f9'), size_on_disk=2233032, blob_last_accessed=1756926324.958359, blob_last_modified=1756926325.594356), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417820/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9d36f1c17151e3203186142abff734c0182377d5287b3f27d00917608501abb3'), size_on_disk=8116588, blob_last_accessed=1756926340.9562893, blob_last_modified=1756926342.0902843), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417869/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8dd08ab28007f90a664a6fbde23298ae2bb0ded917d5d75b1275c41e3eb5f7dd'), size_on_disk=16604892, blob_last_accessed=1756926349.6872516, blob_last_modified=1756926352.7202382), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/059cc8a8b1f7918ab1b6499d3d9843100b3b37fe6dcd57dfe9ae512c6030cad6'), size_on_disk=2109635, blob_last_accessed=1756926376.7021344, blob_last_modified=1756926377.469131), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380996/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/09e663ce2a60df5635f4e4ea77d6350e7752a9bb5e81fbdfd818869ac53ca805'), size_on_disk=13309208, blob_last_accessed=1756926375.0511415, blob_last_modified=1756926376.0221374), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417654/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/67d658902a2b91d0d36345be06dc00c85af1b88ba40c37deaa091705d97b3ffc'), size_on_disk=6323092, blob_last_accessed=1756926311.166419, blob_last_modified=1756926314.4344046), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417885/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6d5faeda491268a8ec88393883d743aac6cd14d4837698164ca32ca1052da36f'), size_on_disk=7668243, blob_last_accessed=1756926352.806238, blob_last_modified=1756926354.1422322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417707/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/89192f5007f730a90c823219ea107c799935f5e824963090abad813603160fa8'), size_on_disk=2615872, blob_last_accessed=1756926322.9183679, blob_last_modified=1756926323.9473634), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380987/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2eb045d9f88b952eed8f8354e77c682bc3003bd2e9eacb0eb0c2df5ca44a34cc'), size_on_disk=4463796, blob_last_accessed=1756926373.559148, blob_last_modified=1756926374.4241443), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417617/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9ad746ec09ed51ed497d87f3dd64b11916b70e6833ce66141bc8feb6ba73d1d3'), size_on_disk=23082279, blob_last_accessed=1756926302.1754582, blob_last_modified=1756926303.6944516), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8168980e59c50149bfb9866a1e8855c235f2882b988df99331c9247f65e9813e'), size_on_disk=2289686, blob_last_accessed=1756926376.4721353, blob_last_modified=1756926377.2151322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417917/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2118491b5ba6b4af1c0d394eec3139b0258cf3a4e42b4d1a02ab177247852081'), size_on_disk=14019020, blob_last_accessed=1756926358.4622135, blob_last_modified=1756926359.8632073), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381036/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ff76dabe83d2948240dd846fcc2a969380b7bd9f8d2eff2c71c299f0b14134cd'), size_on_disk=2593393, blob_last_accessed=1756926379.8661208, blob_last_modified=1756926380.5611176), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380984/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a15ab5e5a0ead07dc2a530a1e4abb714027a95ce090c7a0eda5ec2bb714d903f'), size_on_disk=8070704, blob_last_accessed=1756926373.2251494, blob_last_modified=1756926374.1601455), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417726/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/739e474fc54ffee0c7e6300d3abb1f83d33d48c78d137ed848f837fa186b9308'), size_on_disk=2430088, blob_last_accessed=1756926325.0663583, blob_last_modified=1756926325.6803558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417712/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/59cbfa83b9abe13f60d6a80b35afe857e3ffd761366133f60505312a2b5a72e2'), size_on_disk=641243, blob_last_accessed=1756926323.2163665, blob_last_modified=1756926323.7173643), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417934/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5816750a355a277e532d46ffa5159f8a2ec1ce66686fb12f07ccab4b4f7b3c98'), size_on_disk=7324082, blob_last_accessed=1756926361.995198, blob_last_modified=1756926363.89619), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417774/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ad00f794fb29d2a609375d63b09170f396f055ae392cd05a31d5f8fb5ede7a33'), size_on_disk=2914273, blob_last_accessed=1756926332.1763275, blob_last_modified=1756926333.217323), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380997/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/add73ab9723ef88a47192fa169b89aaf17c16a0841fc9e53c1c5d73cfdc2ad50'), size_on_disk=15536494, blob_last_accessed=1756926375.1291413, blob_last_modified=1756926377.6141305), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417661/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c6effb515a20066646b7863cb86e9dc012e14187e432ba7458cd6af18662bfd6'), size_on_disk=22122748, blob_last_accessed=1756926312.9054115, blob_last_modified=1756926315.6813993), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381037/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5cef560daffa3a5b1e841a312937eca595ced0bbdb3e6c641b13359a0340fea1'), size_on_disk=2090047, blob_last_accessed=1756926379.8821206, blob_last_modified=1756926380.4171183), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380976/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d27d3efdccd104ca208d69fe9d94fe9011cb6212b34739679b1c8f550c5e9df2'), size_on_disk=12889551, blob_last_accessed=1756926371.6141565, blob_last_modified=1756926372.8021512), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381066/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ae017c72cf3b91ea3fdd40f63a126f640e7049340baca502ff3228c6ee40a7b4'), size_on_disk=13139272, blob_last_accessed=1756926780.000737, blob_last_modified=1756926782.6217225), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417668/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b426513aab4e071a24b7d27100a4da4da8c71c8e9b3e82163808b0ec6a42b8d0'), size_on_disk=6011682, blob_last_accessed=1756926314.5374043, blob_last_modified=1756926315.4354005), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381022/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/980ed07a51dfad581fc2f7bce44031c92445822ba7e994cbfc9b5508cdf97c85'), size_on_disk=2103246, blob_last_accessed=1756926378.5151265, blob_last_modified=1756926378.9441247), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417862/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cea0cfbd611f8c6482ec33f8ea32f304a481d1c1225d3dacea432dd359fc976b'), size_on_disk=5244392, blob_last_accessed=1756926348.381257, blob_last_modified=1756926349.6162517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417868/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/52e3f224a0147264d4ce477d26f4e11ff0745c328eb3e2ac44209dba1c8c55eb'), size_on_disk=5039768, blob_last_accessed=1756926349.6852515, blob_last_modified=1756926350.6082475), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417845/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ca2b89837de406b10d72533a3e3d5457b3512b704f7cea395fb0e1b88828393e'), size_on_disk=4648586, blob_last_accessed=1756926345.6932688, blob_last_modified=1756926346.5862648), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417895/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2daca9b311da1afa40f578c6df7dc4b0039dd4857663c98cd87c665a547b7144'), size_on_disk=11915424, blob_last_accessed=1756926354.7232296, blob_last_modified=1756926355.8262248), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417908/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/128e54326b088977000f8caaba7d09e324281ed7fe0e3e5f2779783f0a82f293'), size_on_disk=3614805, blob_last_accessed=1756926357.1272192, blob_last_modified=1756926358.2542143), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417744/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/faf407f0b251cf5f58c8ac7becee4b995327fd9a7d6599c3045fde8a78e1e811'), size_on_disk=2548979, blob_last_accessed=1756926327.7843466, blob_last_modified=1756926328.7443423), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417736/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/671a67d7275810840cafc9db32e43385c4ba61b007d702e7eb5cb244a0e4afa9'), size_on_disk=13855124, blob_last_accessed=1756926326.1693537, blob_last_modified=1756926327.261349), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381030/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e5baa8386faa327b26d27d6c8e6b265dddac1d55565e81d724a1980b93d871a3'), size_on_disk=1867415, blob_last_accessed=1756926379.2021236, blob_last_modified=1756926379.6681216), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417841/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cb829be078dd8b533a90c2c548dfb2ce0455026be54c36572337e0a1ddeeb2cf'), size_on_disk=5791736, blob_last_accessed=1756926345.138271, blob_last_modified=1756926345.9902675), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380989/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3c8b646955209386a1d97882163b7c9816463d2dea68fbed2cc960817ffeb332'), size_on_disk=12022144, blob_last_accessed=1756926373.7761471, blob_last_modified=1756926375.1511412), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417634/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/529d984782684991b2eb7be2d345b5a4e8a6e08d3dbe30ad34495d8380464e6e'), size_on_disk=16306189, blob_last_accessed=1756926306.810438, blob_last_modified=1756926310.239423), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417789/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6bd6339b995a5730b6b65ec65f680b57195469ebee9f2e15de6e604b069d4b0e'), size_on_disk=9612750, blob_last_accessed=1756926334.6493168, blob_last_modified=1756926335.8183117), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417604/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e6dd3abdf75462f1f5d16f19383b98a8a4c817dc3b13c7cea2b4bea611c40c62'), size_on_disk=961320, blob_last_accessed=1756926300.390466, blob_last_modified=1756926301.3534617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417621/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/354c654ace9bc123bcd9009c56af518b98f1a547a7ccd79f348d0e533607a41e'), size_on_disk=20457307, blob_last_accessed=1756926302.5744565, blob_last_modified=1756926307.7874336), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/225bf60f7c9796682210dc48d07a30d3dabab8ebc13abb7a84834279fa131020'), size_on_disk=5307353, blob_last_accessed=1756926376.2401364, blob_last_modified=1756926377.3751316), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417622/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/28eed3b67a6206b453d8adb775edf2bea32cfd54ba5b37d51787f67280e0317f'), size_on_disk=20411775, blob_last_accessed=1756926302.674456, blob_last_modified=1756926304.8564465), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417646/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2b3d78fc052de8647b1ba6e83245b5f375b4e6c10952feff795855160458a1d5'), size_on_disk=396843, blob_last_accessed=1756926310.1544235, blob_last_modified=1756926311.0694194), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417786/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdd4770c9b3e18e86f9a46ff924e3a562e37b632d6f0e8ad11627956a3416e09'), size_on_disk=11354740, blob_last_accessed=1756926334.3243182, blob_last_modified=1756926335.4083135), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417704/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/477a83e4a5eace957a036c5ec65ef4539442c078058c1a44a877763cb850c86d'), size_on_disk=459034, blob_last_accessed=1756926322.1393712, blob_last_modified=1756926322.8993678), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417718/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3ba23a0e7c5b3a455e95fbf6a22b51687d5330c19207241bdfd189e3776f5ba3'), size_on_disk=3304954, blob_last_accessed=1756926324.0233629, blob_last_modified=1756926324.986359), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417683/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6131abcd72bd4c6a8627685c4df8a295af50e824f52a5cde156adae7cd694bba'), size_on_disk=15545733, blob_last_accessed=1756926317.382392, blob_last_modified=1756926319.0483847), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417918/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9f1cf3832969364b6cee1c817d3583a554b765bdbc1ff3f5b29692b7ea98ee7e'), size_on_disk=6936136, blob_last_accessed=1756926358.5802128, blob_last_modified=1756926360.9132028), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417922/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fba17c65fa210922e57c533f5146b4237677149a5022fb836c1cd52dc89855b2'), size_on_disk=10194446, blob_last_accessed=1756926359.5812085, blob_last_modified=1756926360.610204), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380951/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b9b68679dd2da94e4aa4852ae3de3ee5b823c1fb1483f4ece3eee5d947014785'), size_on_disk=1154119, blob_last_accessed=1756926368.8281684, blob_last_modified=1756926369.410166), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417801/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4bbb1b3faac1a4c15eaac8c81260f4f18493d2a082915cbe4c480ce76a94140e'), size_on_disk=4600965, blob_last_accessed=1756926337.090306, blob_last_modified=1756926337.9153025), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380947/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3d6062f97d18f4945c36590355a312df2a16206c26626392cfb44d9613aea49d'), size_on_disk=9201129, blob_last_accessed=1756926368.5321698, blob_last_modified=1756926369.383166), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417737/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2e1d5b2306dd24257d4ba261baa42b324c9f4f8ca3a1d30e0f920c4f4a5f99ba'), size_on_disk=1342511, blob_last_accessed=1756926326.2713532, blob_last_modified=1756926326.9043505), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381057/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4e6c617d22a655d32847df8b5334f4fb7236a15b8bf6e30aaabcbb0a4c55782f'), size_on_disk=17494867, blob_last_accessed=1756926778.5417452, blob_last_modified=1756926780.4997342), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380936/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5a931bc1181001314c3822051c493ba7a8b87b5bb26b8866783612f2bcf49c62'), size_on_disk=3467608, blob_last_accessed=1756926366.19118, blob_last_modified=1756926367.2261755), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381017/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1d9b2dbb5079eca871182c2dce1d681b943f1362072c18e255a4bd6d1068b840'), size_on_disk=2436549, blob_last_accessed=1756926378.161128, blob_last_modified=1756926378.7931254), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380971/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0147090dd5a74f558b67151503ff635423a8af661b56d1faf06bb50a59adfb3a'), size_on_disk=1276600, blob_last_accessed=1756926370.9411592, blob_last_modified=1756926371.462157), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381071/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cf15be48b47b6c369c6a4290b783b9b22b01d6cb89d04c738c275241548b811b'), size_on_disk=2543373, blob_last_accessed=1756926781.2297301, blob_last_modified=1756926782.309724), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417676/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/53c35af59283469a22148b2841cb8574faf80d59162a33d269560283c9228af2'), size_on_disk=12006227, blob_last_accessed=1756926315.8083987, blob_last_modified=1756926316.8563943), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417800/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8e563a7d64bf06e7fe26c2817271f626273eed4c7dd19db0b9fd8e14c25f73b3'), size_on_disk=9904697, blob_last_accessed=1756926336.7953074, blob_last_modified=1756926338.958298), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417893/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f3b9f2c711cb806350320b93dba528efd6433d0ca10c39e5e2dd6aa89a408bd'), size_on_disk=9896637, blob_last_accessed=1756926354.2372317, blob_last_modified=1756926355.535226), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417805/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/661f37ed294c22f6c3b02d5c1ea25794f5e33090c246185e891ee82153e2e5f2'), size_on_disk=3071166, blob_last_accessed=1756926337.8643029, blob_last_modified=1756926339.3292964), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417884/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ff7d3efa4a231f5130be299d8d0ff67fe4973856e4db18e8a6c53d47ba6b28a7'), size_on_disk=11684836, blob_last_accessed=1756926352.791238, blob_last_modified=1756926354.0932324), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417642/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/591f688d0d9f98350a1d636e6f93ed37563f3ea9765ca6b633490533b0cf32d4'), size_on_disk=11498542, blob_last_accessed=1756926309.2804272, blob_last_modified=1756926310.8714201), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417671/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bced5a56092fea7de5754bebc33caaab643f27236f8d9020e7bc6b30b9045d6e'), size_on_disk=5347180, blob_last_accessed=1756926314.7704034, blob_last_modified=1756926315.8293986), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417879/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6714b3f5155dd540962a7541625f45e14e6a93b4c01e913790b70818e50c821b'), size_on_disk=8601872, blob_last_accessed=1756926351.4612439, blob_last_modified=1756926352.8182378), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fb5958ec4e10f5c7e19ebc55b24f26012e6d8ccf'), size_on_disk=8004, blob_last_accessed=1756926300.2594666, blob_last_modified=1756926300.3214662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417815/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ee8a37cd03dfe154008718f2a0f7fd95b3fdf468f99b1fc73ca515433fa45930'), size_on_disk=6563669, blob_last_accessed=1756926339.6062953, blob_last_modified=1756926341.8062856), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417912/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4451877e599fa65e1f00f67b5fcd41fe74719d8bcb19ddacac419fb347c77cf5'), size_on_disk=4943909, blob_last_accessed=1756926357.7152166, blob_last_modified=1756926358.6302128), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380973/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/343eb57057b5af866386fb4c02d0c41b888e206f5473ed7b4041200cfb93dbd5'), size_on_disk=1511721, blob_last_accessed=1756926371.3611574, blob_last_modified=1756926372.4931526), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417700/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1240fbb2497074bb7ff98b1bc6399b8a8ad5d320246285abfb6738e27e2bbdeb'), size_on_disk=19312447, blob_last_accessed=1756926321.6763732, blob_last_modified=1756926322.8383682), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417877/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b170370a220db5677b9c6193707d32d1d14b9082ca574b9562c4ac8ea4c72c26'), size_on_disk=10449263, blob_last_accessed=1756926351.0392456, blob_last_modified=1756926352.32624), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381060/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1bb05aa71fe55db65fc3868b131854c1e6666dca8cc370874f517aa0756d21ac'), size_on_disk=9137992, blob_last_accessed=1756926778.9637427, blob_last_modified=1756926780.2307358), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417785/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/209799965b03585e1143e881d87c5cea80d37e06637d2fc3f7e1ad2eaadfd3bc'), size_on_disk=11557515, blob_last_accessed=1756926334.1813188, blob_last_modified=1756926335.1353147), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417762/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c8b34d14be6dd44af03432d758f705cf81e86a46831c6deb4b0944248709630a'), size_on_disk=3024339, blob_last_accessed=1756926330.943333, blob_last_modified=1756926331.59933), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417699/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0c64ecbe6b245299fe04c04239d792fbae0f1b288562fe0c7912a24de56f0ad5'), size_on_disk=5707915, blob_last_accessed=1756926321.6203735, blob_last_modified=1756926322.8483682), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417838/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0dcd4dc268902c8e0845a1ee81938a81a00412014d5b198a41a523cc3450dfc4'), size_on_disk=10178872, blob_last_accessed=1756926344.5452738, blob_last_modified=1756926345.3902702), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417702/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/41123195fa339a4ffabf6d686a284c343d0519089a18b828818c92ec43a26eae'), size_on_disk=6213776, blob_last_accessed=1756926322.0263717, blob_last_modified=1756926323.0323672), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4c4a265f9e3e8d373550ea58ca1cde6094c823f090336974fde3e7cee117fd99'), size_on_disk=1770007, blob_last_accessed=1756926376.6771345, blob_last_modified=1756926377.1011326), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417608/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d225f561536fca4d073d2418a32efc3df68e9666a3403c7d8b1ee0a520861322'), size_on_disk=873305, blob_last_accessed=1756926300.603465, blob_last_modified=1756926301.521461), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417778/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/54e8ea03e214967eb4a1c7b50a086c4d7083d2c9c95dd0419e545fb0e8f18422'), size_on_disk=9345257, blob_last_accessed=1756926333.1513233, blob_last_modified=1756926334.0903192), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417624/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/28c1de2a4bd6b421cd05c3bb211ffde83e4b830127f106ec7be42ad288e26ef6'), size_on_disk=16758887, blob_last_accessed=1756926303.7884512, blob_last_modified=1756926305.6184433), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417641/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/355d8669b1d4c5576d8aa900582800fdf1819360f0db91d5e0299342b49884fb'), size_on_disk=6322689, blob_last_accessed=1756926309.3414268, blob_last_modified=1756926310.7974205), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380949/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/65090ef93158269c98bc2e747e505cea9d460f0959d0b2957e58543cd1944163'), size_on_disk=2110872, blob_last_accessed=1756926368.705169, blob_last_modified=1756926369.142167), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417658/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b6cf4c024743be14fcd5730f0ddbb960dbcd83d07adf8668639f9a1cdf195368'), size_on_disk=8754108, blob_last_accessed=1756926311.5764172, blob_last_modified=1756926313.1134105), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381063/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1e4361a7d20f8e92e8efc238365e0ed06eb021243864321c93d4638fa21634c4'), size_on_disk=10916323, blob_last_accessed=1756926779.3497405, blob_last_modified=1756926780.685733), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417697/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/198a3cd31740f347f0308db2914df4fc0fc303b4a9f040fbaf79fb5ca0170e7f'), size_on_disk=5521438, blob_last_accessed=1756926321.4153743, blob_last_modified=1756926322.4743698), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417809/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/486100d34df42ce97476d9753f8d57edb5f53596eaeca2d0cf81f5f95a4f4e12'), size_on_disk=7376702, blob_last_accessed=1756926338.5872996, blob_last_modified=1756926339.9172938), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417711/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e30dbc09cd2dfb4722d60d0833123542e9c5096f70b97867cd15d466aa7b927a'), size_on_disk=96859, blob_last_accessed=1756926323.1863666, blob_last_modified=1756926323.7823641), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417925/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8cdb783613764a35fd3a73db89463db6876951d443f5651a9a46877a53917f3f'), size_on_disk=912848, blob_last_accessed=1756926360.409205, blob_last_modified=1756926361.1532018), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417640/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/411d9146359847b5805450f67cbde4f1cbd51e3c2fae4bbcd6295db7426cb2ca'), size_on_disk=7000528, blob_last_accessed=1756926308.9124289, blob_last_modified=1756926310.0344238), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380953/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5b3410430d33f3123b935982be90037f2d0a8e657f6b0b70a7042846d5ddd1b0'), size_on_disk=1192961, blob_last_accessed=1756926368.8401685, blob_last_modified=1756926369.627165), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417657/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62398c518588ad11a939f95736cf0ed01a39890567cd4a8f05e9c9ba1155f9c1'), size_on_disk=10144285, blob_last_accessed=1756926311.5634172, blob_last_modified=1756926313.1174104), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381014/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5c5106dfb24bcfb1436aeb87b8218df873417ca2809aed71ca58e9b48ffacd29'), size_on_disk=1425306, blob_last_accessed=1756926377.5811305, blob_last_modified=1756926378.2571278), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380994/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7c5f237d9c32233a005e39386360f02d9e1932f9ac267ac3458f96aaf59e01ef'), size_on_disk=2496053, blob_last_accessed=1756926374.8051426, blob_last_modified=1756926375.3471403), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417742/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1a87c492aab479dd0c013fbe80e006f18ba80de751da600b95babcb3f6e88dad'), size_on_disk=2054520, blob_last_accessed=1756926327.3293486, blob_last_modified=1756926328.4213438), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380999/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/acb2ee4711dab5800afb7ccbea72c8c80268b0a848723e7bd8ecf197decb982d'), size_on_disk=2605563, blob_last_accessed=1756926375.2281408, blob_last_modified=1756926376.1321368), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417729/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8383a0e86c2bfe15c46045980d6768eb4e7e3c4c157ff1ae7ce20144e245ca80'), size_on_disk=3058876, blob_last_accessed=1756926325.3113573, blob_last_modified=1756926326.103354), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381048/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/53cd017f084781da16e69ae01b406ac0d8824a4d991e39b22c428e664a3808f2'), size_on_disk=2181901, blob_last_accessed=1756926776.9907537, blob_last_modified=1756926777.7067497), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417740/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e91586589c967e7bcbd521989874b21c024da79365d0d9916ffc66b774043e01'), size_on_disk=11875677, blob_last_accessed=1756926326.8903506, blob_last_modified=1756926327.6843472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417835/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6245fc8d220aad556115d4b831f72ae6a9beac48ed6e2aba811284aa0ccab4f7'), size_on_disk=7888215, blob_last_accessed=1756926343.795277, blob_last_modified=1756926344.942272), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417705/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/92b4f5af6793c98b854bcca83c86ac0cb41ec4e40ec23d3ba9c4ff287852c63c'), size_on_disk=332184, blob_last_accessed=1756926322.5923693, blob_last_modified=1756926323.1503668), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381059/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/59daa71f55c9c0da6f8d56fc2497491477312fbdbcb9e92d4951874f2ed72686'), size_on_disk=13124154, blob_last_accessed=1756926778.926743, blob_last_modified=1756926781.8157268), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417852/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/43431a444b2761ed9b6fa4c7e6edfc8da31b64ff933408c517bc771e9f3e94dc'), size_on_disk=6967416, blob_last_accessed=1756926346.8272638, blob_last_modified=1756926347.8462594), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417748/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/db4c67c001a1f688f70918f0e25d3ddd996392b33b9f1144a14551bc0026d036'), size_on_disk=3246481, blob_last_accessed=1756926328.8123422, blob_last_modified=1756926329.6943383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417892/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7ac6b85018422c74c894467b7b53b1c2dc7c5298fc31654bdea5ca70493e0a87'), size_on_disk=8194725, blob_last_accessed=1756926354.176232, blob_last_modified=1756926354.9712286), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417906/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/34d3381dd45e579dcc298e91cc95174c4219cf548401b8f2392cf746fd508c5b'), size_on_disk=13795318, blob_last_accessed=1756926356.3642225, blob_last_modified=1756926357.640217), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380927/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/38e67ea2fef57610ff63c0c298a6db65ae1c0a159f2b1e61940f2e65306b8fa9'), size_on_disk=7297320, blob_last_accessed=1756926364.969185, blob_last_modified=1756926365.7781818), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417753/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a8969c75211310735ad33d5af57327f2babc86276adc7cefcda254cc9fc9baa5'), size_on_disk=6433095, blob_last_accessed=1756926329.577339, blob_last_modified=1756926330.4023352), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417817/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ccf81aa26021a009a212ef8f8bee71c12f5c8d0a2b97ffdb2c4d86d13c8b8133'), size_on_disk=7521086, blob_last_accessed=1756926340.0802932, blob_last_modified=1756926341.3222878), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417816/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/86256a1b98c6d44750fd21ac9ea099262f1f0ec03d182b65741d669fb80f289e'), size_on_disk=10618044, blob_last_accessed=1756926339.7852945, blob_last_modified=1756926341.8372855), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381041/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a53caa1961d9245d82db10989b8a8d601760ccf26f67666d5558937830c51e31'), size_on_disk=6621865, blob_last_accessed=1756926774.784766, blob_last_modified=1756926775.9747593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417878/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d3d105cc45280a5ea88c2bba754c0c5849d0cec7112a0926e9ed0c946e7f5cfd'), size_on_disk=8838404, blob_last_accessed=1756926351.2492447, blob_last_modified=1756926352.7352383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381043/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f6ded8e6496dcfdd499edc382d19da2071f25daebbb250b375b9a2706e9d8211'), size_on_disk=9139336, blob_last_accessed=1756926775.6057615, blob_last_modified=1756926777.1057532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417645/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/761eba472193b23cbf7735946e08aa1c4db5b1abd5125f1aad22ddabbf872f04'), size_on_disk=2080044, blob_last_accessed=1756926309.765425, blob_last_modified=1756926311.5084174), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417623/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/562a43ff381a2ca177fb1cc3dbe47fc2cf7cade495bad511ca7ed75aefca7a98'), size_on_disk=19049552, blob_last_accessed=1756926302.696456, blob_last_modified=1756926305.4934437), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417897/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/309aab637d037638082524649ef7b0828885a98e786c64af01072d68968b7d66'), size_on_disk=7857749, blob_last_accessed=1756926354.8022292, blob_last_modified=1756926355.9902241), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417873/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d282d59c3c191bd7b7fa7251108224d0ac3929bc98f96a2c43f283f8a5cd046a'), size_on_disk=7609540, blob_last_accessed=1756926350.3852484, blob_last_modified=1756926351.373244), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417825/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/efc4971b63b21d6aee96e17d9e9b2cc2e3fd34608cc3722e36032c594fc03e48'), size_on_disk=8550531, blob_last_accessed=1756926341.8732853, blob_last_modified=1756926345.0782714), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417927/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8e639025209cff8d34b7e35a5fa371282bb725173225097a39f8f2101a49deed'), size_on_disk=7241057, blob_last_accessed=1756926360.6762037, blob_last_modified=1756926362.1171975), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417913/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/35c1a160b767f9f5870216ed33a1dc24804690772a5baee58f623c6dc0632127'), size_on_disk=9135350, blob_last_accessed=1756926357.9462156, blob_last_modified=1756926359.3142097), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381064/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8569620c4d2efb8dd163aa69d8edf8f855b96eba637e754a126a88e6f0c73192'), size_on_disk=20598404, blob_last_accessed=1756926779.800738, blob_last_modified=1756926781.1587305), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381061/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d44d57e9c03bb6e6e750809eea88657c2960ed251b8acaa1352a07f4cb346a82'), size_on_disk=3789431, blob_last_accessed=1756926779.0527422, blob_last_modified=1756926779.8977375), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380958/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ef3849004b6c72205ea1b298cf6a586f1114061ca94925229eda191d4666885'), size_on_disk=3288900, blob_last_accessed=1756926369.617165, blob_last_modified=1756926370.303162), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381049/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c571bcc5f87a79c7cd38b9614ee83b0d5cc742094e17a7990c829394fca193be'), size_on_disk=3753119, blob_last_accessed=1756926777.145753, blob_last_modified=1756926778.2097468), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417915/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9fbfc50c3e1f756169d25442880ba33e5cf237e3c83a0d674b64ace47e8d3322'), size_on_disk=13801295, blob_last_accessed=1756926358.318214, blob_last_modified=1756926359.4592092), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381024/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5182750a0c8af8253cc98ecad8715519463d5c18adbe067488dfef91198c0d80'), size_on_disk=603678, blob_last_accessed=1756926378.7151258, blob_last_modified=1756926379.128124), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381052/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/652334151cb539b888d9cfcb60d9711d80bd74ec65cfa3799de10b2970ab9c09'), size_on_disk=5788579, blob_last_accessed=1756926777.7767494, blob_last_modified=1756926779.283741), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381065/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/99cc73bc4c0908da04c01c7c59eff995a478ffc023c31fb970c7bb9789b2d70b'), size_on_disk=14673323, blob_last_accessed=1756926780.0087368, blob_last_modified=1756926781.5847282), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417848/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eec4ffff5d145c0ee0850d5e68787ab90e6510cffa5803579db44e3a575822dc'), size_on_disk=13556826, blob_last_accessed=1756926346.1882668, blob_last_modified=1756926347.2852619), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380943/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/987771cb978dbc949eff5044071149a00223c6771efcc870850e3c8cbe0648c3'), size_on_disk=13397144, blob_last_accessed=1756926367.295175, blob_last_modified=1756926368.7601688), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417680/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aa9e972e7427b1db5556008756651933c8792891339607dee2ee231733072d1c'), size_on_disk=18443288, blob_last_accessed=1756926316.5853953, blob_last_modified=1756926321.4263742), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417750/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aedd94fe4b42547108a807242052397b6dfbfda0eb4e56a9f2e267b312251147'), size_on_disk=1531584, blob_last_accessed=1756926329.1973405, blob_last_modified=1756926329.9403372), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417813/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f7d5239d0d39e171aad1e84964f8cdc3c7a325fe8c38b6829e114cb3997e658'), size_on_disk=10238322, blob_last_accessed=1756926339.4142962, blob_last_modified=1756926340.8722897), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417933/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4fe347d88754646d39d93dd8a78e857d04d1903f7163a21495038b087d5f6b8a'), size_on_disk=12389872, blob_last_accessed=1756926361.7481992, blob_last_modified=1756926362.9061942), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417741/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/734d5947cb26e877b6ae9e9adaf84575974a4fe2fb464ba11c4320906f17a42a'), size_on_disk=4566889, blob_last_accessed=1756926326.96635, blob_last_modified=1756926328.5653431), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380930/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/734e9bc8119c7a507e9cf84f9926b1f9d06ccc8b0062a9b4dc36631f6f1bc6d1'), size_on_disk=4604638, blob_last_accessed=1756926365.1301844, blob_last_modified=1756926365.9251812), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380956/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/15047f69259f2cbc2cb08949d07ac6fde505bade6a0e3bb3912b183a48a4b288'), size_on_disk=2282680, blob_last_accessed=1756926369.4771657, blob_last_modified=1756926370.1431627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381045/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdc8f9e6447280a7fc713c2a8f2157eb9076a5c01a7eb8437176e9ee30d6d4ab'), size_on_disk=4939421, blob_last_accessed=1756926776.3437574, blob_last_modified=1756926777.2507522), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417807/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ebc732f5d06d745bb63af3c7f5966722972fa722ef9d0194d71cf6d9485fda83'), size_on_disk=14049230, blob_last_accessed=1756926338.268301, blob_last_modified=1756926339.5272956), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417898/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ced2c80d4f9d2aa28260b885a20b1c90b41d8eb54ee92f418ed592b97b954a9e'), size_on_disk=6422129, blob_last_accessed=1756926355.1262279, blob_last_modified=1756926356.0962236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417910/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/694d86a1e469836a53e1ce5dba0d64b77b8f68590a2034e2a1ef5310457dc7fb'), size_on_disk=4494010, blob_last_accessed=1756926357.3112185, blob_last_modified=1756926358.3942137), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417701/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a0dd9587c7525a610a3794cf41f6db24a6ba22a12ed852dc27381a68db3e8fca'), size_on_disk=8781514, blob_last_accessed=1756926321.6273735, blob_last_modified=1756926323.1263669), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417871/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e44cb883893b5e30baa752dfbd773b31795f2072a7ec4f473371f435989cad42'), size_on_disk=6264896, blob_last_accessed=1756926349.9852502, blob_last_modified=1756926350.937246), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417847/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4dd39704bc9c1d60a8271f16206887171ee92abde023c53501c1560533a688c1'), size_on_disk=4755120, blob_last_accessed=1756926346.125267, blob_last_modified=1756926346.8372638), CachedFileInfo(file_name='GSE209631_metadata.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE209631_metadata.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fe31b19357a717d3d51d1d201620d37fca79b1c8'), size_on_disk=61, blob_last_accessed=1756926300.2214668, blob_last_modified=1756926300.2814665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417728/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2be09708238c50a31b4e9b334c6202c26eaa6a950ebf3de4287651999d45097e'), size_on_disk=3385901, blob_last_accessed=1756926325.0603585, blob_last_modified=1756926325.869355), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417703/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e82b21d919b84770e2d141eaea09aff82ef57c3ff3dbd15c588c1b8cff212c9e'), size_on_disk=1462841, blob_last_accessed=1756926322.0533717, blob_last_modified=1756926322.898368), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417840/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7e32bef3c0f7f7520382325aba12f5cf9405c22ebce8554b1d877fe24d2da52b'), size_on_disk=9679209, blob_last_accessed=1756926345.0092719, blob_last_modified=1756926346.0542672), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417720/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/961772a081bde1795af5caea9d7562e2b1f889981bd3c3f1c792ad9093a758d7'), size_on_disk=659095, blob_last_accessed=1756926324.1233625, blob_last_modified=1756926324.6243603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417643/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/66dfa3feea4410ba34f5d5d37c7b01e1fa2cdd4ff38f9dfb10d20d55e40e449f'), size_on_disk=1012185, blob_last_accessed=1756926309.326427, blob_last_modified=1756926310.0784237), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417839/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a04fc093d2a4fe873de135165bd62a38a1cb9b29860881ed973022ff128dc6d8'), size_on_disk=6789775, blob_last_accessed=1756926344.5502737, blob_last_modified=1756926346.4242656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417606/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f76c81d98337c9e127bf5516af0dd135458fd622c01f2b5c9fd85ce6edfa4c7f'), size_on_disk=9502783, blob_last_accessed=1756926300.5794652, blob_last_modified=1756926301.6394606), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417882/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ac2bd9710196abc4469d4d78ccbde6ddc67e9ef39ade7d50c32b0d951866e653'), size_on_disk=10076041, blob_last_accessed=1756926352.116241, blob_last_modified=1756926353.223236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417733/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/542261ed8bf189205b611485f1a878f28cae5c90e452ba9cad687eacfdfc2814'), size_on_disk=2645461, blob_last_accessed=1756926325.8883548, blob_last_modified=1756926327.3873484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417776/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/01c4a97f92cd5e2919759d52d083eb824408a2fd7ade9413a060c6b20d233963'), size_on_disk=2759569, blob_last_accessed=1756926332.8593245, blob_last_modified=1756926333.629321), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380963/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c0a30240a0a99347e114928a15a24268495fd37480fde6822f503438bdf3ed48'), size_on_disk=579777, blob_last_accessed=1756926370.2681623, blob_last_modified=1756926370.81716), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417858/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2d7539fcb6297592fe3755ccc88e122441925fbaf9f5600d4daa88639630fd8b'), size_on_disk=1021831, blob_last_accessed=1756926347.8042595, blob_last_modified=1756926348.5072565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417866/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b0424cd7567723443472cb266e090a30e2b64ee6b09bb1b94d8dcfdb9f0b0c43'), size_on_disk=9685580, blob_last_accessed=1756926349.109254, blob_last_modified=1756926350.493248), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756926300.2174668, blob_last_modified=1756926300.2804666), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ad0e80a28e4cd2027893adee5afceab851576ed2637c1a8e94d0af2adae6ace1'), size_on_disk=5088900, blob_last_accessed=1756926375.41114, blob_last_modified=1756926376.3821359), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417842/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c763016dcbf1b53df840e3ea3c1b45234e2d9e797114000efaf692e2c394d487'), size_on_disk=9967694, blob_last_accessed=1756926345.1472712, blob_last_modified=1756926347.1812623), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417656/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/03f8707a323ef7563c8ec2d6352d24c6454d043c4bbbca417680df5c7db208f8'), size_on_disk=10029839, blob_last_accessed=1756926311.588417, blob_last_modified=1756926312.8104117), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417738/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c3d82a351044aa8cd5d43979369088ccf2f563022d4f7aa3683ef20923202770'), size_on_disk=19772535, blob_last_accessed=1756926326.3913527, blob_last_modified=1756926328.6913426), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417844/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ae034bb207abc0c649fb60aae78e798d46647f53b4e0352c6510fb7c8339a234'), size_on_disk=5408445, blob_last_accessed=1756926345.5212696, blob_last_modified=1756926346.4342656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380972/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/91a8106ca7ac2babcabd1549072b2b730909e1386f827e732428bff2bb5251cb'), size_on_disk=6712599, blob_last_accessed=1756926371.1721582, blob_last_modified=1756926372.0711544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417942/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f3a5b1eb0f954ddf66a8dfa1a38465726d2251730d3898865535269a43c7fa4a'), size_on_disk=6911573, blob_last_accessed=1756926363.415192, blob_last_modified=1756926364.3521879), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380978/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a1d811dc3a4bfd44683ddf8bd0401a71d4941497c5cac8d8040a597fc5d26587'), size_on_disk=5902739, blob_last_accessed=1756926372.160154, blob_last_modified=1756926373.09715), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417602/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/26ae91a914e9ad095615c88ac3c62c99e28ae2687ccd66007f13ac8fbbffa3fe'), size_on_disk=3717181, blob_last_accessed=1756926448.0367467, blob_last_modified=1756926301.5714607), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380966/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f27a802d18bf78781f3d168370e0c0e241a130d04a5871d7416442be3ce3e40f'), size_on_disk=4241927, blob_last_accessed=1756926370.3881617, blob_last_modified=1756926371.272158), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417831/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/53d810292e5a513a8ab543fe18783f8d493cedbadc7c97400dc103a4675954df'), size_on_disk=6767934, blob_last_accessed=1756926343.12528, blob_last_modified=1756926344.4612741), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417891/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cabef83ee373e37d528b127d813f1535faede4b98c5524d83e9906df83558b07'), size_on_disk=12596609, blob_last_accessed=1756926353.9052331, blob_last_modified=1756926355.2992272), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417946/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef43c84affe53bbdefcd70cb97a4a4d1e483824b8b6f91e3360a11094609f815'), size_on_disk=4853893, blob_last_accessed=1756926364.0141892, blob_last_modified=1756926365.0591848), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417798/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0ac62fa7573f4b3c756fd9c280be4b97ec0c7c756e8c76307d4fab56d29e1c08'), size_on_disk=10509245, blob_last_accessed=1756926336.5193086, blob_last_modified=1756926337.800303), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417664/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/91fc5dce8497d1fc5466fb5ac31e44a20b295d8f97825500f256f6830ea2f983'), size_on_disk=8203399, blob_last_accessed=1756926313.1894102, blob_last_modified=1756926314.6624038), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417796/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/52d8262071c9c9de8cd8fb52be9e67acf125b8ce67033749fb92989f0d1f9345'), size_on_disk=10184069, blob_last_accessed=1756926336.2803097, blob_last_modified=1756926337.4333048), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417851/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2d8a571fae50598143744a1f13d40cec2ea8c2198e87aae17f96d7c8a00460d0'), size_on_disk=11051527, blob_last_accessed=1756926346.6472647, blob_last_modified=1756926347.6582603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417745/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ab84f2e8e76311cfe45a4b525b239186e835cab37f1e5222fdc35c286064e5e'), size_on_disk=1270105, blob_last_accessed=1756926328.596343, blob_last_modified=1756926329.1233408), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380959/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4693c1876e880fee1ec277fd5ae9127aafa9f40d41a3d5ed482dfcfa8cef55f1'), size_on_disk=873902, blob_last_accessed=1756926369.7061646, blob_last_modified=1756926370.1731627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417896/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/71e393229e9071bad31abe5dc3d41bd5d719a5dcb68077827861b4b4e9a49a85'), size_on_disk=6055777, blob_last_accessed=1756926354.7882292, blob_last_modified=1756926355.8442247), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417652/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d38c028233981eb275c44995eafa73172388a9e0e704fe278b0cedbab49d429f'), size_on_disk=12947754, blob_last_accessed=1756926311.0344195, blob_last_modified=1756926312.1414146), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417827/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8bfd1221f930e86ffd6150c82aae85f00a23fff33c672e40f47d671f5a2e4d17'), size_on_disk=7291599, blob_last_accessed=1756926342.156284, blob_last_modified=1756926343.1502798), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417818/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3b30217b127ff88f165762c5054c392d9489b9007ba7ca8b86f18b44f9988e4d'), size_on_disk=8388230, blob_last_accessed=1756926340.2372925, blob_last_modified=1756926341.5632868), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417663/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cc7010c3ed70c3083d49d88c0f95dbe8f6eff873d4b5848eeae06a22df71a479'), size_on_disk=8938116, blob_last_accessed=1756926313.18441, blob_last_modified=1756926314.5674043), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417715/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ac5effc2ec07a8fb49bac4d77b7d4129a18ae2180807874ea02d1c252a3b68bd'), size_on_disk=520286, blob_last_accessed=1756926323.8693635, blob_last_modified=1756926324.477361), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381028/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8d40caa48e7b9d32d2a0548732ef91c01ede9d0f4f6d74f045817e818c52c808'), size_on_disk=18141950, blob_last_accessed=1756926378.9691246, blob_last_modified=1756926778.2687466), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417694/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/058b507f75cb01bb5912e14e7bbb4e25a1de945e578323bce9f920f31a9ced82'), size_on_disk=11980395, blob_last_accessed=1756926320.09838, blob_last_modified=1756926321.5623736), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417685/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/32edfbf9226b7ef1060ebe3e6986a1803d60408ccda969528cf4a59c5785c722'), size_on_disk=16967633, blob_last_accessed=1756926317.9163895, blob_last_modified=1756926319.5263824), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417828/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eb4987a8ef94e1691c74c1d828d7d9313da4691b577118a73524def2a90c6d0b'), size_on_disk=4827634, blob_last_accessed=1756926342.3472834, blob_last_modified=1756926343.2782793), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380937/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e31f54581c4a4a47033065033f3f9266913edad654543dd07b20d0ffe54847dd'), size_on_disk=2691574, blob_last_accessed=1756926366.2071798, blob_last_modified=1756926367.1951756), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417916/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2abad8555b89fbb5009e8f2a95e553a5ce579d1841ae4e71eec7b137f3282ef1'), size_on_disk=9236466, blob_last_accessed=1756926358.371214, blob_last_modified=1756926359.0582108), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417768/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8bb8e088eb5123049e418c868ab42d94fb48946f934a8313f4cf283e3bfa09ba'), size_on_disk=4124375, blob_last_accessed=1756926331.3153312, blob_last_modified=1756926332.2313273), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417770/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0e7437c90c616b78ccc0963e0388385028b70d66fbe73e0ceb4e30e2d0239816'), size_on_disk=8252889, blob_last_accessed=1756926331.6563299, blob_last_modified=1756926333.0863235), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fc6783c3340c869bc3838a9a9d5abb8bca67361feeef12c42b6cf478d73bfd66'), size_on_disk=9020794, blob_last_accessed=1756926366.2571797, blob_last_modified=1756926367.2561753), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417850/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c08fca103aa3dd1944dcf5c73b67397263842dc687b4101c8ab2c1a63d50205a'), size_on_disk=1182282, blob_last_accessed=1756926346.5122652, blob_last_modified=1756926347.3082619), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417863/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4aabb9d1a4b105c7669deb0f13cd635ab7433ed1ae02f5785aad014d604f1504'), size_on_disk=6900487, blob_last_accessed=1756926348.4532568, blob_last_modified=1756926349.2742534), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380991/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dd86db1779581d5853d4d6a0fa2aeacdb00f92c832683ad788743f17db625db4'), size_on_disk=2640507, blob_last_accessed=1756926374.030146, blob_last_modified=1756926374.8661423), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381018/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/878bf028aca9daeebe7ed0f8ee522580d512771ec4a5e35bfc62ac675176ad15'), size_on_disk=701951, blob_last_accessed=1756926378.2281277, blob_last_modified=1756926378.643126), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417773/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d7980069a740f581dbcfd9416372452203d636d8ad4c9352c3b2d6483cd27379'), size_on_disk=5178664, blob_last_accessed=1756926332.1683276, blob_last_modified=1756926333.411322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417904/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cf9194917da2f4c6c3f0411256b2604e8c14959373d7431047de2e23cffbb9bf'), size_on_disk=7269877, blob_last_accessed=1756926356.062224, blob_last_modified=1756926358.1762147), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417787/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/17f33e06b6bd77c0bbd3141b53fc2d9550b0c54e99b18fe79ddad931e3a283a9'), size_on_disk=12355842, blob_last_accessed=1756926334.5003173, blob_last_modified=1756926336.2953095), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417881/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bf380975f31e362e8964000688bff364809bbe2e8e3efa9ceaa71253ca6beefb'), size_on_disk=8717095, blob_last_accessed=1756926351.848242, blob_last_modified=1756926352.9732373), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417650/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c4d103888f768ff3cb0c68f4b747cb8d8aad8058b75174160cfc42b2504407f1'), size_on_disk=1337049, blob_last_accessed=1756926310.89842, blob_last_modified=1756926311.4924176), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/43f12fb4826be4c7a3dd64800fc72cbcab2a9ce5bed7f7b5b9aee974deb7c06d'), size_on_disk=1116877, blob_last_accessed=1756926376.094137, blob_last_modified=1756926376.6491346), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380945/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/51efeee4439e81d920c4d9e860de321d90c72703600044e52215c774e1540650'), size_on_disk=6902367, blob_last_accessed=1756926367.356175, blob_last_modified=1756926368.6011696), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417795/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b995a48f28ae3baa145438a39c9fed73fa8689eb31e07ff02a3bc6bc827eb76e'), size_on_disk=8032882, blob_last_accessed=1756926336.1053104, blob_last_modified=1756926336.9993064), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381025/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aea8c4d9f3783191d3e89f4566811f01089f3b0b76b736c5167bda54693b0bc4'), size_on_disk=339954, blob_last_accessed=1756926378.7781255, blob_last_modified=1756926379.125124), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417649/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6baf4bcd82d9fe55b8dcdc7602c0d39d20954675ef4c5484331ff25dac8ad3f4'), size_on_disk=2672976, blob_last_accessed=1756926310.8234205, blob_last_modified=1756926311.4704177), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417784/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fd3dd53cf34f48c235e4f771378f42a5605446bbc62d108a5637a042513fd75d'), size_on_disk=4826642, blob_last_accessed=1756926333.7233207, blob_last_modified=1756926334.576317), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c103eef2a9834296fb1328b7012b5b9184c5a3f131817dfca257843e863d34b2'), size_on_disk=9829417, blob_last_accessed=1756926362.1531975, blob_last_modified=1756926363.3531923), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381020/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c85f953439e9f6918c7f7c8194bcbbebe0ef8a96070119813025a42bde8c08fe'), size_on_disk=897644, blob_last_accessed=1756926378.3451273, blob_last_modified=1756926378.7061257), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417861/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef65c45e0e86b7d04e274c39408b332cc94879c0804f16acf7c537b14bb6498c'), size_on_disk=10436399, blob_last_accessed=1756926348.3832572, blob_last_modified=1756926349.6192517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417651/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b95b6be3a7a6a5b4af3bbf15c5a4a6b3893f3030ef6d1dcfe79e6c6554e3d3cb'), size_on_disk=13551797, blob_last_accessed=1756926310.9544199, blob_last_modified=1756926314.6814036), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380970/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bdec4a51127eaf09abb5d25d7f3a732afc3b561105fd18ba1df44d04e6dd90a2'), size_on_disk=3545790, blob_last_accessed=1756926370.9301593, blob_last_modified=1756926371.5451567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417775/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2ca9ba5e1394191206945f41168d555519fe3963d4c39f251c1ead57f2a323f4'), size_on_disk=1059891, blob_last_accessed=1756926332.3723266, blob_last_modified=1756926332.773325), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381029/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6431cec7b7404a0773d8715761e2b161f01ebd8471950e71e17dad8976d83f3c'), size_on_disk=7229691, blob_last_accessed=1756926379.0071244, blob_last_modified=1756926776.565756), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380960/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/60771297311bf96c3213c2a96321062a06067c91d2bba2fce69a7a6d7a0c980e'), size_on_disk=560607, blob_last_accessed=1756926369.7811644, blob_last_modified=1756926370.3121622), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417907/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b24926bd36cf33ea9107043b9a47db8239ad6208745c56002513225e8cb80a6e'), size_on_disk=7594380, blob_last_accessed=1756926356.7332208, blob_last_modified=1756926357.882216), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417717/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/689842dffacf7efa4ba6cd9b4314d195e89f9105eb71675f27019a9e25be63db'), size_on_disk=102777, blob_last_accessed=1756926323.9603631, blob_last_modified=1756926324.5793605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417889/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/295f9efb0d78e3a116ce4022a7830297570446e2017a3fcd3b3d423c0c20664b'), size_on_disk=9617979, blob_last_accessed=1756926353.3102357, blob_last_modified=1756926354.7322295), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417803/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1fd8b542fb1b0e18bc5346c50ad85da440ff6190a987467ec6876ed3ec51a4e8'), size_on_disk=4928668, blob_last_accessed=1756926337.5433042, blob_last_modified=1756926338.7182992), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417638/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c8cface311cd55cd1542ed94d609da72cd2b3087422c28ad26c60e1d81a3e6bd'), size_on_disk=993699, blob_last_accessed=1756926308.4724307, blob_last_modified=1756926309.2634273), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381067/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/da9c5ab3c0f970f9c2af4c0f5f0e9a95d47769e8096ff6b9d76092becbc8517e'), size_on_disk=9837433, blob_last_accessed=1756926780.2977352, blob_last_modified=1756926781.5777283), CachedFileInfo(file_name='GSE178430_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE178430_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/be2c4af55a502d7fa22abb28afe6db347950327bde1bc7e97dbb68fdb0c59f3d'), size_on_disk=9398, blob_last_accessed=1756926861.9192877, blob_last_modified=1756926300.5344653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380998/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6808b8e1fa74b807321826b2c54182a3b6e257f0a223be0a6a94caf9463959e9'), size_on_disk=16847111, blob_last_accessed=1756926375.2231407, blob_last_modified=1756926376.572135), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417928/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/97e74e01fd641bfef68b6206a3c62ab41b5c28d8ea8369ce4198df1b3387b266'), size_on_disk=2449472, blob_last_accessed=1756926360.9072027, blob_last_modified=1756926361.3542008), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381056/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/382194ca9758ddc18f059a649b13c5a21ead3a7f1fa5e012065315047a037a58'), size_on_disk=2351268, blob_last_accessed=1756926778.362746, blob_last_modified=1756926778.9827425), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380985/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2ec986412ef2a9e6e59f4ea3dc278b27ab3975b5025d00bd1cd38fe1867b0fea'), size_on_disk=7208882, blob_last_accessed=1756926373.2251494, blob_last_modified=1756926373.9591463), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417940/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/270559ca1db4605de241e8642461b4d7304d124e4fc624f7d8a8a59867301a4c'), size_on_disk=2328150, blob_last_accessed=1756926363.182193, blob_last_modified=1756926363.9021897), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417639/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/43c5fb0a4f049fa5895f8167928d047cc4954aa37db1fc8a9892fe9f99c401bf'), size_on_disk=782319, blob_last_accessed=1756926308.7694294, blob_last_modified=1756926309.6284256), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417674/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5970db11c3efe2bba4eb50d07480cb0b46ed74dfc3ddf12d611ecd61f1d5fb68'), size_on_disk=4619941, blob_last_accessed=1756926315.5114, blob_last_modified=1756926317.630391), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417691/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/48de5a2968610e5ab855522d27f535d40e1973c1835ec32fcfa5bcd91a0795dc'), size_on_disk=14481663, blob_last_accessed=1756926319.447383, blob_last_modified=1756926321.2093751), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417708/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b58038866c334daaf757a086f99c3a5c5962b8563769ee0a3205bcdcbb0144ec'), size_on_disk=250863, blob_last_accessed=1756926323.0003674, blob_last_modified=1756926323.366366), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381051/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ba017d941936d755013757711f5de4661acd187ef79524662d85485ba6588f34'), size_on_disk=8199472, blob_last_accessed=1756926777.322752, blob_last_modified=1756926778.4757454), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417808/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0251ae27bd961d6c962253c555963b42148092cdaa2616a78f6872cf1a4e5e4d'), size_on_disk=12900784, blob_last_accessed=1756926338.3973005, blob_last_modified=1756926339.439296), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380948/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6ac219aa7f5be77c21bde60c80ccad59edf825e2a009d6a4b7fc8bd4d8be83d4'), size_on_disk=6046323, blob_last_accessed=1756926368.6691692, blob_last_modified=1756926369.5471654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417901/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/69432386b347f5b364c02d270b38891358fa2068465354d1ada84d5450ea5bf0'), size_on_disk=9484337, blob_last_accessed=1756926355.6292257, blob_last_modified=1756926357.0582194), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417804/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/16ee23ae4dbe8a4d968a614f5e6de65028b6edce012231a50eb776d65f949d34'), size_on_disk=2107502, blob_last_accessed=1756926337.7883031, blob_last_modified=1756926338.5083), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380969/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/10410488c74f35a493cef2ffc4a64b28b336be1fb32a2b6dac692e0d7fbd4271'), size_on_disk=1973405, blob_last_accessed=1756926371.041159, blob_last_modified=1756926372.1191542), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380941/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9a23f03fee8d60d752bd0bc1e59e9205abea4f3a78e3272b9a03447f076383d5'), size_on_disk=3514837, blob_last_accessed=1756926366.9851766, blob_last_modified=1756926368.7591689), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381031/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b4705ac4577c8e7b116d1a1b677967f69412b0e4716e0de7fa7ee97bc9be4d74'), size_on_disk=4551217, blob_last_accessed=1756926379.2931232, blob_last_modified=1756926776.2707577), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417943/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0e4191a403f5070636bcb281bd80d39e11c70fb502fa2b18150032d58f2e0740'), size_on_disk=5396199, blob_last_accessed=1756926363.7271905, blob_last_modified=1756926364.542187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417747/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7011e7ff4b64ca9c57bdab93cfcfc52938e7b0574b48417feb4f6185f6c118b9'), size_on_disk=4791771, blob_last_accessed=1756926328.7593424, blob_last_modified=1756926329.9543371), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417948/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d43ea802a452aa9d57004381e1688222e61bd313b107077c1c663555547d1d44'), size_on_disk=1748124, blob_last_accessed=1756926364.3661878, blob_last_modified=1756926365.038185), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417620/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b973f1a83f111c4e114a40d3731ff24779cafcb7b71523fe8571cf2ed849b316'), size_on_disk=20780203, blob_last_accessed=1756926302.5494566, blob_last_modified=1756926306.7334383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417687/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fa4f79173f5a5ed278c7ef80d9fb220aa9c615de97bae7963969e27ecf0e587c'), size_on_disk=13352339, blob_last_accessed=1756926318.3003879, blob_last_modified=1756926320.3913789), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381062/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/76151e17f242c5108010debea961b0b13f3821be2fcb77fd923a36cc0e672695'), size_on_disk=4880117, blob_last_accessed=1756926779.3357406, blob_last_modified=1756926780.5167341), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417743/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d4eb93049648498d2fdc1f125574429a4dfdec65e626ad9fa16bdf0d0cf599d8'), size_on_disk=2280534, blob_last_accessed=1756926327.462348, blob_last_modified=1756926329.1343408), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417880/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b08db0a2399fc4e5b6f59c19b7af05b1294d0fac10b2c336b627234fa828f1be'), size_on_disk=9818886, blob_last_accessed=1756926351.5782433, blob_last_modified=1756926353.246236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381055/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0aad0d3724742845da7f0afae0c18ab5c992bec76c02e0ffda057f00d0894d35'), size_on_disk=3355008, blob_last_accessed=1756926778.3377461, blob_last_modified=1756926779.263741), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417819/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a1ba6808d672dbc9c58c26364c5cd9ed0b8a3041e6714844846648d3cd1aa050'), size_on_disk=3244517, blob_last_accessed=1756926340.7022905, blob_last_modified=1756926341.6952863), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417886/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62fe87e7519370c4ac46e6d9d0abf314a8f92d9a5d6804ebddae0ed095641219'), size_on_disk=7968977, blob_last_accessed=1756926352.9472373, blob_last_modified=1756926353.7992337), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417637/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ca1a954c7b2aca56529daf9a89249ad4e6ec2373e29ac7c19d2af1533afc4263'), size_on_disk=2776397, blob_last_accessed=1756926307.8804333, blob_last_modified=1756926309.1794276), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380990/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4e04122d2830db35f2b82b29efaca846fe10d7638c88c3e4d3cf78bf10cf245a'), size_on_disk=2093813, blob_last_accessed=1756926373.9261465, blob_last_modified=1756926375.1611412), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417714/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d6abeb724a8c8b23217f3465284564da5baffee59ee4e3af582e0a3705ae32c6'), size_on_disk=262443, blob_last_accessed=1756926323.756364, blob_last_modified=1756926324.256362), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417799/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cba2fb579c7706a7ff38fad3c7c649000560c1052e681adea986c5cc02c02cfb'), size_on_disk=5688906, blob_last_accessed=1756926336.7133079, blob_last_modified=1756926337.7073035), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417782/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d6624acaa7c4ec21ae02d6d1322b8669a148308095e714b41bff868483fe2fa9'), size_on_disk=11742567, blob_last_accessed=1756926333.5273216, blob_last_modified=1756926334.824316), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381033/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5fff567b96a4649d787e93ef8cdca2d8ce977b7f2eb25d6f78e50bc30c798bce'), size_on_disk=1712156, blob_last_accessed=1756926379.7391212, blob_last_modified=1756926380.3661187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417648/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fb706eb700e8fc003822b897accd3501360d737fd1a9ffcf26c3e9d9b25a5e35'), size_on_disk=1285054, blob_last_accessed=1756926310.3104227, blob_last_modified=1756926311.0174196), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417854/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d52c8b14e197b179a9b43fbc89bb3205d09d458161ee49ac15a62b7c1f7a39fe'), size_on_disk=6203801, blob_last_accessed=1756926347.311262, blob_last_modified=1756926348.387257), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/64a119c8028083521d3872e2daca1545f2f8753eb8acfc751bf0c6d25803a3f8'), size_on_disk=2003211, blob_last_accessed=1756926376.794134, blob_last_modified=1756926377.2761319), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417836/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4fb964c54f4a37387c7844902d6171011a9a81efff244f365efcb156f630158f'), size_on_disk=3312893, blob_last_accessed=1756926344.2532752, blob_last_modified=1756926345.0702715), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417677/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cfcdac65879c0c26771c1cd1c4a656d4db20e6adb2ed3ad833cf23cd35a9d848'), size_on_disk=13135194, blob_last_accessed=1756926315.9083984, blob_last_modified=1756926318.1283886), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417616/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0b0f3c9481fd0e254abd544ba803de0c9da9b3a686451975f981d02fc481148d'), size_on_disk=15058562, blob_last_accessed=1756926301.8254597, blob_last_modified=1756926305.0234458), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417723/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f397cef2d9c491fb034826727c63b3c224f6ed791c9b4dcf884c63c55c9961c'), size_on_disk=2800893, blob_last_accessed=1756926324.70836, blob_last_modified=1756926325.4453568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417812/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9e8445f413d26051897f0fe3e7566b116a9a55062b78201990de334f17aecb31'), size_on_disk=19905089, blob_last_accessed=1756926339.0272977, blob_last_modified=1756926340.6232908), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380962/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/72be01aa42db736ffac8236c23c16edea00bdf5da88c3114fc94d9eb3daa4ee9'), size_on_disk=1460258, blob_last_accessed=1756926370.2361624, blob_last_modified=1756926370.6811604), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380965/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6b384b53be751cec9056e74c2c8ce9fe2e532cfbf6a38e43bb09ce74ec817f3a'), size_on_disk=12974487, blob_last_accessed=1756926370.3761618, blob_last_modified=1756926371.4361572), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417629/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/29c9c86be94792d797005b106ab00d066634bea44fbf17e81738a00cbffa264b'), size_on_disk=22750780, blob_last_accessed=1756926305.5594435, blob_last_modified=1756926307.5804346), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417822/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62cbbb0172158a2c0776000136424032a794757ca9e9f971e5f445deb20e8579'), size_on_disk=6040431, blob_last_accessed=1756926341.3882875, blob_last_modified=1756926342.8302813), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4c942b91ac018330aeb4da512a3e47ae4531a434b6e9933cf3e2bb476a77f9ca'), size_on_disk=6312758, blob_last_accessed=1756926366.1041803, blob_last_modified=1756926367.0141764), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417615/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3b8b3c4c686238c7c642151bbdcb2ba7796de56c1922e558a1e7f08507dc5abb'), size_on_disk=2866472, blob_last_accessed=1756926301.8314598, blob_last_modified=1756926302.5744565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417756/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ede027f2c13a1e6669becf6194ace85af5800d5fe77db16ec47bfb3abaa735d3'), size_on_disk=8620421, blob_last_accessed=1756926329.968337, blob_last_modified=1756926331.0303326), CachedFileInfo(file_name='GSE209631_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE209631_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/be49eef90682fbd55a5e60ada8b4c1c554e1b5a2ec1209996269db020d3bb074'), size_on_disk=4258, blob_last_accessed=1756926300.173467, blob_last_modified=1756926300.594465), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417905/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b9d291469b7a414bab157f45bfd3a1f7ffaaf931de25fec9eb18012a18b70939'), size_on_disk=4418829, blob_last_accessed=1756926356.1612234, blob_last_modified=1756926357.2652185), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417936/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8ba89583b8c822b64968df9eca8273d12270429f0f820d6027f13c66504c383d'), size_on_disk=4552381, blob_last_accessed=1756926362.205197, blob_last_modified=1756926363.171193), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381021/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3e80794742d10b6d120e70c4e4f1d7f77123ddf8aa18499f686b4abc8eadd325'), size_on_disk=1524611, blob_last_accessed=1756926378.410127, blob_last_modified=1756926378.897125), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417675/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/79072181ebce12769f2f0a1971977d6691dde1841b53d7b54cf9d4f47f53b3dc'), size_on_disk=6478433, blob_last_accessed=1756926315.763399, blob_last_modified=1756926317.2643924), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417630/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/26fdbadf7c1252a8fd73ac85ea7430d942d2d11ddc43ea1b2590aaa008e08337'), size_on_disk=3015575, blob_last_accessed=1756926305.7104428, blob_last_modified=1756926306.7214384), CachedFileInfo(file_name='GSE222268_metadata.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE222268_metadata.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4459325dc2deeb9f00d803b89cd8fdc823ed383f'), size_on_disk=61, blob_last_accessed=1756926300.2714665, blob_last_modified=1756926300.3444662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417719/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cc0a365d1d79f0469571216394f0cb211b9096f2a567e0a347a18f0c496bac06'), size_on_disk=2076369, blob_last_accessed=1756926324.010363, blob_last_modified=1756926324.951359), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417849/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/23b6606ffcf116cf066b103bf1b6b4fb33c40e544e20e4565fa44c5bbcea4bdd'), size_on_disk=5689527, blob_last_accessed=1756926346.584265, blob_last_modified=1756926347.3892615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381034/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/112a8e705f7536fa06c630165d3c409f87a5784ad626b891cc2b3919e24bdd14'), size_on_disk=250813, blob_last_accessed=1756926379.795121, blob_last_modified=1756926380.1791193), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417710/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9837ecd3769eaaf407d939ff09d6ae74d832630b27353251942bd262cd70fc35'), size_on_disk=330457, blob_last_accessed=1756926323.098367, blob_last_modified=1756926323.797364), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417607/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/623d085888c300fba1d05a9018ec3d4c31912f2b91a872ef34feda3322889b70'), size_on_disk=6809356, blob_last_accessed=1756926300.611465, blob_last_modified=1756926301.75846), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417766/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/39da4f16b82cc0b5019d8526f2e3eed49de9760e319f8e839db621bf4c1ab021'), size_on_disk=6482957, blob_last_accessed=1756926331.2123318, blob_last_modified=1756926332.1133277), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417920/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/00a869703f03e3df3f0f51a7d93d52dd7eac5174ed2a1b752207460e71c7b41a'), size_on_disk=14880236, blob_last_accessed=1756926359.1242106, blob_last_modified=1756926360.409205), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417698/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d240b652ebca64784e037cc8ffbda424e2c92f368c5bb9c730930f48f29facc2'), size_on_disk=1855826, blob_last_accessed=1756926321.2993748, blob_last_modified=1756926322.0703714), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417672/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f0741c46f1460c30d4d9b88ca1f7c0ca77a040cf25a34d82fab4c777a921ac26'), size_on_disk=18268758, blob_last_accessed=1756926314.9764023, blob_last_modified=1756926316.5143957), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417864/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/15d259c52e80a2d2d9c20274ef940237c90001d32f2aecb80779335a708222ff'), size_on_disk=3706383, blob_last_accessed=1756926348.5722563, blob_last_modified=1756926350.0962496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3fe2828766efd936f02efae2da3e068ef9b28ea374e594e09850e3e8ff0d8164'), size_on_disk=4421540, blob_last_accessed=1756926375.7961383, blob_last_modified=1756926376.7581341), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417780/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4f1b7c3ded6441c8e906c3494ae63750d25303ee6f8d8e05120dc11ce8d79080'), size_on_disk=9025914, blob_last_accessed=1756926333.2923226, blob_last_modified=1756926334.387318), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417931/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7e70415ed0dfb1eb42e46789c905ccf2b593dc192d907d939d87acd59cd963e1'), size_on_disk=4991063, blob_last_accessed=1756926361.306201, blob_last_modified=1756926363.1161933), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417751/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9a5733130de69115a731511dac8bf577fc5609a7f9649106ae9a12ffae7011ba'), size_on_disk=1660869, blob_last_accessed=1756926329.2023404, blob_last_modified=1756926329.9043374), CachedFileInfo(file_name='GSE222268_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE222268_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e0f1d5b82fd97798d0c02d992c49c0e83d8dcf82e07e001284e78d9656355481'), size_on_disk=4411, blob_last_accessed=1756926300.1784668, blob_last_modified=1756926300.5344653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381013/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c831ca7cc9cfd2cc2ccd92a0168b2e14dedf2bde9e29780dfaf6d281b31e9462'), size_on_disk=4648281, blob_last_accessed=1756926377.4421313, blob_last_modified=1756926378.3451273), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417900/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/45d5a0bbf39e542f22aa4e36186673d1246533dc513f006312a26286496f6ead'), size_on_disk=11235417, blob_last_accessed=1756926355.535226, blob_last_modified=1756926356.6632211), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417758/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/54f8e8d84e5b8b9457aaa143829fcfd5a4715d0fb184478ce659cd6c6efe7be1'), size_on_disk=4176901, blob_last_accessed=1756926330.0203369, blob_last_modified=1756926331.1383321), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380983/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eec33f1158aa017b073fcb16d6400cd800eb79e8c0b78b5e2f831d7413e4a736'), size_on_disk=4478939, blob_last_accessed=1756926372.869151, blob_last_modified=1756926373.6721475), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381047/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dda6f6ca669a0e5c9d34b548305cbeb6b82f1b70086e06d3604e7e096fd7fb25'), size_on_disk=6799004, blob_last_accessed=1756926776.8777544, blob_last_modified=1756926778.4717455), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417682/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/83b3100cfd1dc6918855c4a711ff607b36f40971655733855a7df353b4d5fca5'), size_on_disk=14037100, blob_last_accessed=1756926317.1663928, blob_last_modified=1756926319.1013844), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417605/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f689b77eccd8d95d70fb616f4e2aa03a41201aec2a8c3c86849b7b145cb0f796'), size_on_disk=1787545, blob_last_accessed=1756926300.5184655, blob_last_modified=1756926301.74346), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417806/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2befcad73b2aa8170ceaf8c249ceba45e87037771e86c9bb4bd7addc5815bbec'), size_on_disk=5276603, blob_last_accessed=1756926337.9863024, blob_last_modified=1756926338.8112986), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417802/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ea68aebb727f77d846a145d43ec16fa2496e9c05d49a8791aa131b8e3e536452'), size_on_disk=4881005, blob_last_accessed=1756926337.2223055, blob_last_modified=1756926338.1273017), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417632/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/23c561fde2e6c2973ec1913373c343a5c49d6cc0dbb44ff21eb5aff87cd77d3a'), size_on_disk=18481828, blob_last_accessed=1756926306.2764404, blob_last_modified=1756926308.5744302), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417765/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bd2b5a1b1593db002ea9c159d4c40d1d8addddbf124207a7b32b0bb43e2e4d72'), size_on_disk=2696437, blob_last_accessed=1756926331.2103317, blob_last_modified=1756926332.046328), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f00bc431128183affc8964f9610f3e9486a043bcea5b31b3ebbcbd9b0070a448'), size_on_disk=6096918, blob_last_accessed=1756926377.3431315, blob_last_modified=1756926378.2571278), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417688/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f21ccaca0f6782b6ce2b70b50c9dc93487b6aaec03db6c30d9cb0c9fc01dda42'), size_on_disk=16023102, blob_last_accessed=1756926318.5463867, blob_last_modified=1756926320.0283804), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417647/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8c01c928abe00b5afa6a2292820695797391b3671cc110b9026f5fc5cb8564ae'), size_on_disk=2891716, blob_last_accessed=1756926310.1614234, blob_last_modified=1756926310.93942), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417613/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/695d3095619f3a313d25c506b9d0493f171896f1439bcf3bd1cd932d1f6fed5d'), size_on_disk=2812565, blob_last_accessed=1756926301.6434605, blob_last_modified=1756926302.4814568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417810/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b807197acb33acdd1b70eddedd59dc4d831e271d231d6c9bb6f1281fb4d5813f'), size_on_disk=9808564, blob_last_accessed=1756926338.8082986, blob_last_modified=1756926339.7202947), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417834/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a69c65a4d32399355863fa14d4a1e92ecff2c92498e122e8e77796de16d42948'), size_on_disk=8537233, blob_last_accessed=1756926343.3672788, blob_last_modified=1756926345.2462707), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417870/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ca1bc9825b47ee9ac59e53bc6ada3b1a9e8aba1b57c57c57947afae7926054b'), size_on_disk=10710319, blob_last_accessed=1756926349.7052515, blob_last_modified=1756926350.7552469), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417833/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/48792db399d7f0aab6765109e8c01abd61a2da93e5d0bdf7c325bcffe01fe113'), size_on_disk=8139822, blob_last_accessed=1756926343.3262792, blob_last_modified=1756926344.480274), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381015/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/345c83c5ab13d230542683014d3d6073ba5a348e3063fea325843981c6c6e7c1'), size_on_disk=3807314, blob_last_accessed=1756926377.67913, blob_last_modified=1756926378.4971266), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417767/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8d0afed33cd85298c709656da4b210e3fe87aa4d6822208d744200816d59b95b'), size_on_disk=3739401, blob_last_accessed=1756926331.2333317, blob_last_modified=1756926331.8623288), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417760/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0500e5835116a61477f2d98554d0dd97e8d932a6b564f709c40c4884f2b49052'), size_on_disk=2234621, blob_last_accessed=1756926330.2873356, blob_last_modified=1756926330.8273335), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417735/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/634551506800bc76183ce179c8c8f324cb6f0f577f6cf0b629b74ce4f1e995d5'), size_on_disk=19171195, blob_last_accessed=1756926326.0393543, blob_last_modified=1756926329.4873393), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417914/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d062dbf660e1ef240ff8ef99ff2d486d9caaabda14575e6365ce6f725fa91ccf'), size_on_disk=8599250, blob_last_accessed=1756926358.2422144, blob_last_modified=1756926360.3412054), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417797/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/12ee278356fa75b0ad555fe06376c78b71630324b7b89aa28abe960c187d256f'), size_on_disk=14391201, blob_last_accessed=1756926336.3743093, blob_last_modified=1756926338.322301), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417619/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0a4d4159941caa34a5e079821d49047d1434077926b2933c27d384e04ed2e5b5'), size_on_disk=9949802, blob_last_accessed=1756926302.5744565, blob_last_modified=1756926304.05045), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417761/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ba86b2041414034d38b95ccbf3c65957642c56b56914240999986ebc76b9e193'), size_on_disk=1635118, blob_last_accessed=1756926330.478335, blob_last_modified=1756926331.0933323), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381000/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/503f854145f3a220aa936072c3e66410d34914fef948992399a4addbfc977186'), size_on_disk=3904727, blob_last_accessed=1756926375.3401403, blob_last_modified=1756926376.5601351), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417603/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdfb1fd9b21341104e6423c684b0ae8a52ed1701eb3e232babb98340f953ea5b'), size_on_disk=4460026, blob_last_accessed=1756926300.366466, blob_last_modified=1756926301.2574623), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380986/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f23d3060df5cb96778fbdf10fff0155c931e1a1e8cc077c7f1582542d54827f5'), size_on_disk=5858046, blob_last_accessed=1756926373.285149, blob_last_modified=1756926374.5971434)}), refs=frozenset({'main'}), last_modified=1756926783.3167186)}), last_accessed=1756926861.9192877, last_modified=1756926783.3167186), CachedRepoInfo(repo_id='BrentLab/rossi_2021', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021'), size_on_disk=213725645, nb_files=47, revisions=frozenset({CachedRevisionInfo(commit_hash='6b29baae54286d5fbdd1c70f499c03319badad51', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51'), size_on_disk=213707016, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466110/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/d5283e08e898b4f59837f7ff2064f79c8a828994f868c637894f4e07df36daa5'), size_on_disk=13123795, blob_last_accessed=1757598690.8318212, blob_last_modified=1757104407.6787057), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466116/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/587a7b86dffe3d7ade9adeeb94029ea1de54e61fb2c2445dc527979b7f1c24ac'), size_on_disk=5596311, blob_last_accessed=1757598690.988821, blob_last_modified=1757104407.3507075), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/04bacc0f8c03ee000ba3b69320522d0b1be909686474b6e8a126173a0ceae8c4'), size_on_disk=15547870, blob_last_accessed=1757598690.8528214, blob_last_modified=1756944357.1972551), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466142/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/3dc044b5cc1b7bc8070d8de4b7b870524b296f26b4f43b22dce2dcbf161d4c74'), size_on_disk=1739520, blob_last_accessed=1757598691.2588208, blob_last_modified=1757104415.5896657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466112/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8f3441964a046d3c148b41f335948a085d26884e1a890e515711a2d1b48a40be'), size_on_disk=4883601, blob_last_accessed=1757598690.9128213, blob_last_modified=1757104408.5027015), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466125/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/0bba21490ea0a6297031b0ff3219fc0158d9bbcf15099906c86a770b1a1312fc'), size_on_disk=2624759, blob_last_accessed=1757598691.081821, blob_last_modified=1757104409.4426968), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466139/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/25c7d3ffbacca76fd998edffe0c762e220f448140019aa2d389facff5117ce48'), size_on_disk=176811, blob_last_accessed=1757598691.2478209, blob_last_modified=1757104412.6266806), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466108/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/56875f81a3cc7c1d40b3042b2c443058eb0f9a2b7e7ebc2e1704c098be612628'), size_on_disk=1765819, blob_last_accessed=1757598690.8238213, blob_last_modified=1757104407.1947083), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466117/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/49065d4cee74c20f34303405e34e284e29f19082b76fa883a38aeab24cadf674'), size_on_disk=1742958, blob_last_accessed=1757598690.995821, blob_last_modified=1757104411.8276846), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466150/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/80cd2d325e5d7a50c6ecbf79a052f45c54a93b9423e90735bf8989aada9bb9eb'), size_on_disk=3234303, blob_last_accessed=1757598691.3798206, blob_last_modified=1757104417.837654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466111/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/ba431fc9a05f40581e450429d467226a8e3c49195de355437f9044bb0fdfe524'), size_on_disk=5749460, blob_last_accessed=1757598690.9028213, blob_last_modified=1757104404.9707196), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466128/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/f631797d2bc13a580f570637d94a0b9ea37deb2fd7ef92e32c66fd9014260525'), size_on_disk=1059587, blob_last_accessed=1757598691.118821, blob_last_modified=1757104410.6996903), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466140/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/25f98a891820b5a91c11629ed5615d40ffe1fe4a6cba1b2c6642067d76b5be87'), size_on_disk=68174, blob_last_accessed=1757598691.2588208, blob_last_modified=1757104415.6456654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466141/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/e9efb807ce35fc3e43b1ff0d27b80646c7d137c587abd9d6247393e44d4f30a0'), size_on_disk=986450, blob_last_accessed=1757598691.2628207, blob_last_modified=1757104413.129678), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/09f6dce90ec0e64d8f18614091dd18ad2e86213a'), size_on_disk=4403, blob_last_accessed=1757598690.987821, blob_last_modified=1757104409.9196944), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466149/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/ae4d5237fde214a92d3267fbe11f6f1f88569cc7f3f7ccb40fed200871bf33b1'), size_on_disk=10933582, blob_last_accessed=1757598691.3518207, blob_last_modified=1757104415.1316679), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466114/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/2c07f671cca471d6ad3e10e071cd1b976a89a45ff32920913d8fc36dbeaa1678'), size_on_disk=10992797, blob_last_accessed=1757598690.9498212, blob_last_modified=1757104406.3287127), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466136/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/f5e7fd644d30ae0c8e2c72bc341155bfe5df2ce102b68b08ff4d243470026fcc'), size_on_disk=343033, blob_last_accessed=1757598691.1958208, blob_last_modified=1757104411.7116852), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466144/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/57b3d2c7917bfa29312cc51c921beff914858c61762e28a658eb29d8eaf3348b'), size_on_disk=918271, blob_last_accessed=1757598691.2758207, blob_last_modified=1757104413.554676), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466124/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/39ef32412cae257b724c3b797ea482eeffb5b29afef4246d997ea525a2124054'), size_on_disk=7162800, blob_last_accessed=1757598691.075821, blob_last_modified=1757104409.5966961), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466113/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/acec3b041a74ab45dbeb03e7f183c2b3d16479aadd64a81f80a74aa42d56368d'), size_on_disk=5866774, blob_last_accessed=1757598690.921821, blob_last_modified=1757104409.0226989), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466129/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/b290d4c9436756b444d0da1af1229b0a2a6e877fd968c5d01b73efa96e62cff8'), size_on_disk=6955953, blob_last_accessed=1757598691.196821, blob_last_modified=1757104410.78769), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466122/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/6362da9e07f02d4c9dc05f15f638ca1b1bf0de5989da4ee8c6f0768f2453f98f'), size_on_disk=1249990, blob_last_accessed=1757598691.0398211, blob_last_modified=1757104411.9016843), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1757598690.9228213, blob_last_modified=1757104403.6017265), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466118/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/192057e6004dd5cd303e8b1abf54cb72005e211c315665e28eec7b617a84d95a'), size_on_disk=728586, blob_last_accessed=1757598691.0068212, blob_last_modified=1757104407.0917087), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466130/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/73db821736e3e47c09da250e369fa06812e02e5abe7a4a3ceab5d1020422b66c'), size_on_disk=1920920, blob_last_accessed=1757598691.1328208, blob_last_modified=1757104410.9306893), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466107/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/2f924bc58d82a21b621b051addd1913c77369afaa738171ef0962548fc1a1021'), size_on_disk=5670198, blob_last_accessed=1757598690.8608212, blob_last_modified=1757104405.0417192), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466126/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/34292b90d99b5a5be542c7dd10b11336605330639d92e93b092e567a56fe7d93'), size_on_disk=2850164, blob_last_accessed=1757598691.086821, blob_last_modified=1757104410.0976934), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466143/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/54bd4af6ed8caf52a5810598d96c9b235266542586a692112cb247a209c2649f'), size_on_disk=10801408, blob_last_accessed=1757598691.2688208, blob_last_modified=1757104413.3726768), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466135/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/d44d381db0b8c0cb4960796e48d3d16e3d766aff87cbc686789a0c5799ba0a98'), size_on_disk=5204256, blob_last_accessed=1757598691.180821, blob_last_modified=1757104412.4376817), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466115/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/c08ce1194dd98cb028a6038944317e70ac1545e32b21dd323eafa57b16611e6e'), size_on_disk=3503431, blob_last_accessed=1757598690.9468212, blob_last_modified=1757104405.9427147), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466121/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/78d0f377f8a756b28f2f5296932368c9c6b764d2972dc93aa6dd354591d29860'), size_on_disk=7053621, blob_last_accessed=1757598691.0328212, blob_last_modified=1757104408.5097015), CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1757101035.6211069, blob_last_modified=1756944310.9395182), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466127/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/1565c5175e05c322f160529cb4bb74d5e8468458f150f228d6aadf2b670c3b4a'), size_on_disk=11670434, blob_last_accessed=1757598691.092821, blob_last_modified=1757104411.0776885), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466123/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/57b7ca5d419676590538573f295b99db05a952e20cb4c6b2a1618974611d5051'), size_on_disk=3672899, blob_last_accessed=1757598691.073821, blob_last_modified=1757104409.590696), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466134/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/0b23434181eb1fc864e858cfca82d2d8fed1524a8f7dbea3bf576f6f452cf08b'), size_on_disk=12398132, blob_last_accessed=1757598691.169821, blob_last_modified=1757104412.552681), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466133/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/b6d6913e14b52d5d51dab2f254815da89e03cebe75e80356b27cf18e31eb048f'), size_on_disk=5090722, blob_last_accessed=1757598691.169821, blob_last_modified=1757104412.180683), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466131/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/bdce0842b5568e406a799dbe36b4b9645a30fa58eb0a707f24ed3bb3900fbf67'), size_on_disk=11759994, blob_last_accessed=1757598691.157821, blob_last_modified=1757104411.1146884), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466132/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/cc41509e63473d565341ed36fd6881fecfd537e59c003f4779d4c4ec7b49cb54'), size_on_disk=1906184, blob_last_accessed=1757598691.161821, blob_last_modified=1757104413.4366765), CachedFileInfo(file_name='genome_map.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/51978dc2125afb40a41a29672f8ae750308d9a63'), size_on_disk=5311747, blob_last_accessed=1757598690.9468212, blob_last_modified=1757104406.477712), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466109/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/6588704c47423e671054df9cb2318c045138b0018f2eefff61ef91a25848eb58'), size_on_disk=5259413, blob_last_accessed=1757598690.8298213, blob_last_modified=1757104404.9987195), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466119/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/0157d9136c2c2bb5640866449adf4bab8c62b3d7f7fb4262981908c77986f89e'), size_on_disk=12516152, blob_last_accessed=1757598691.0068212, blob_last_modified=1757104412.3166823), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466120/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/4220a31e6a7be79341828d41da77ef77fb5a8026980d4445fec18c57b5229c1f'), size_on_disk=3644577, blob_last_accessed=1757598691.034821, blob_last_modified=1757104408.3467023)}), refs=frozenset(), last_modified=1757104417.837654), CachedRevisionInfo(commit_hash='6aac54b4b3fbb414561a8b6cc7acf87880c3c3c2', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6aac54b4b3fbb414561a8b6cc7acf87880c3c3c2'), size_on_disk=4665, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6aac54b4b3fbb414561a8b6cc7acf87880c3c3c2/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/14e1e02ad3804c0a4146a52509460e9cbb759d93'), size_on_disk=4665, blob_last_accessed=1758306843.4906857, blob_last_modified=1758156479.1516545)}), refs=frozenset(), last_modified=1758156479.1516545), CachedRevisionInfo(commit_hash='1e07e46fd0b5348243487ac4a573b570a2a3946b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1e07e46fd0b5348243487ac4a573b570a2a3946b'), size_on_disk=4683, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1e07e46fd0b5348243487ac4a573b570a2a3946b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/bb690d71edd44885609ed660d926f3cf2c4c473e'), size_on_disk=4683, blob_last_accessed=1758306349.7238934, blob_last_modified=1758156991.1235294)}), refs=frozenset({'main'}), last_modified=1758156991.1235294), CachedRevisionInfo(commit_hash='22ebbfcf62a7ed665fb8eceaea53b58b45a1709e', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/22ebbfcf62a7ed665fb8eceaea53b58b45a1709e'), size_on_disk=4602, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/22ebbfcf62a7ed665fb8eceaea53b58b45a1709e/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/e03b8d10ba59c549907a309ad8343133b7b902fc'), size_on_disk=4602, blob_last_accessed=1757613581.062093, blob_last_modified=1757613581.0610929)}), refs=frozenset(), last_modified=1757613581.0610929), CachedRevisionInfo(commit_hash='1acaa5629922414e8efe23a5d023bd44965824c8', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1acaa5629922414e8efe23a5d023bd44965824c8'), size_on_disk=4683, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1acaa5629922414e8efe23a5d023bd44965824c8/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/bb690d71edd44885609ed660d926f3cf2c4c473e'), size_on_disk=4683, blob_last_accessed=1758306349.7238934, blob_last_modified=1758156991.1235294)}), refs=frozenset(), last_modified=1758156991.1235294), CachedRevisionInfo(commit_hash='664d95cdd482d753bc139d26119061c2fa6eaa76', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/664d95cdd482d753bc139d26119061c2fa6eaa76'), size_on_disk=4679, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/664d95cdd482d753bc139d26119061c2fa6eaa76/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/ff6f75db42c0690f0e0285e56c1aa95bc5cf1300'), size_on_disk=4679, blob_last_accessed=1758157110.1705706, blob_last_modified=1758157110.1695707)}), refs=frozenset(), last_modified=1758157110.1695707), CachedRevisionInfo(commit_hash='3ce77eb2070d7bb43049ba26bb462fded0ff7863', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/3ce77eb2070d7bb43049ba26bb462fded0ff7863'), size_on_disk=15562566, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/3ce77eb2070d7bb43049ba26bb462fded0ff7863/genome_map/accession=SRR11466106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/04bacc0f8c03ee000ba3b69320522d0b1be909686474b6e8a126173a0ceae8c4'), size_on_disk=15547870, blob_last_accessed=1757598690.8528214, blob_last_modified=1756944357.1972551), CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/3ce77eb2070d7bb43049ba26bb462fded0ff7863/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1757101035.6211069, blob_last_modified=1756944310.9395182)}), refs=frozenset(), last_modified=1756944357.1972551)}), last_accessed=1758306843.4906857, last_modified=1758157110.1695707), CachedRepoInfo(repo_id='BrentLab/harbison_2004', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004'), size_on_disk=12305, nb_files=2, revisions=frozenset({CachedRevisionInfo(commit_hash='2916ad316cc0d8a1ce2e7f35d3707b831b203e87', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/2916ad316cc0d8a1ce2e7f35d3707b831b203e87'), size_on_disk=7160, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/2916ad316cc0d8a1ce2e7f35d3707b831b203e87/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/7e3ec66f5479346ca9d082545ff0b6a960fb60d5'), size_on_disk=7160, blob_last_accessed=1758155946.7689884, blob_last_modified=1758155946.7669883)}), refs=frozenset({'main'}), last_modified=1758155946.7669883), CachedRevisionInfo(commit_hash='073cdeb1b541c28b1a97df9dbc6be6af1d9c23b6', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/073cdeb1b541c28b1a97df9dbc6be6af1d9c23b6'), size_on_disk=5145, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/073cdeb1b541c28b1a97df9dbc6be6af1d9c23b6/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/f7a6cf3dd4477f08e508f38665350e8d11589963'), size_on_disk=5145, blob_last_accessed=1755876214.0380862, blob_last_modified=1755876188.1642818)}), refs=frozenset(), last_modified=1755876188.1642818)}), last_accessed=1758155946.7689884, last_modified=1758155946.7669883), CachedRepoInfo(repo_id='BrentLab/hughes_2006', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006'), size_on_disk=11943331, nb_files=8, revisions=frozenset({CachedRevisionInfo(commit_hash='cc20720c4fe7de9151a051aafbd41e6de2b4cd84', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84'), size_on_disk=11937630, files=frozenset({CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756856028.69735, blob_last_modified=1756856028.7793496), CachedFileInfo(file_name='overexpression.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/overexpression.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/9b8cc1feede780efe0e8537555ed1cbbf067e7fa9aa7a7edc74e5a343c64a443'), size_on_disk=6337781, blob_last_accessed=1756856028.536351, blob_last_modified=1756856029.230347), CachedFileInfo(file_name='metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/e27a67137876123c68c229705cfbb8e06ed3ee76a3a868b66d80d624f8d03671'), size_on_disk=7942, blob_last_accessed=1756856028.533351, blob_last_modified=1756856028.9083488), CachedFileInfo(file_name='checksum.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/checksum.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/973b686313e32c96b6e0ef1dbc84020857c073ef'), size_on_disk=159, blob_last_accessed=1756856028.5883508, blob_last_modified=1756856028.6513503), CachedFileInfo(file_name='parse_hughes_2006.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/scripts/parse_hughes_2006.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/0c2645bdc0ab7f6be38a428a0a92752e9d3f88d5'), size_on_disk=9255, blob_last_accessed=1756856028.5803509, blob_last_modified=1756856028.6393504), CachedFileInfo(file_name='knockout.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/knockout.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/5e000e2e9401011c9ecc81655c94d55b7f9469ed856216fa21cef2883a904c93'), size_on_disk=5571518, blob_last_accessed=1756856029.2493467, blob_last_modified=1756856029.2143471), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/842b551fb3d1ba1e6f1daa35157a0c7c616f7655'), size_on_disk=8514, blob_last_accessed=1756855276.4871445, blob_last_modified=1756855276.4861445)}), refs=frozenset({'main'}), last_modified=1756856029.230347), CachedRevisionInfo(commit_hash='51ae7addf429c955a97934a15b242ff8a2ccd653', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/51ae7addf429c955a97934a15b242ff8a2ccd653'), size_on_disk=5701, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/51ae7addf429c955a97934a15b242ff8a2ccd653/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/f799807a7bf81229b0d063e6c939339c1f5c90f4'), size_on_disk=5701, blob_last_accessed=1756855057.8437808, blob_last_modified=1756855057.8417807)}), refs=frozenset(), last_modified=1756855057.8417807)}), last_accessed=1756856029.2493467, last_modified=1756856029.230347), CachedRepoInfo(repo_id='BrentLab/hackett_2020', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020'), size_on_disk=407271569, nb_files=17, revisions=frozenset({CachedRevisionInfo(commit_hash='60b3ecf6a06e709f0397b327ece5c31016303b5c', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/60b3ecf6a06e709f0397b327ece5c31016303b5c'), size_on_disk=9543, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/60b3ecf6a06e709f0397b327ece5c31016303b5c/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/6f54100c2bb84a6bc059c17e0954dad0ea451666'), size_on_disk=9543, blob_last_accessed=1758400469.288775, blob_last_modified=1758155372.1328666)}), refs=frozenset({'main'}), last_modified=1758155372.1328666), CachedRevisionInfo(commit_hash='b9d39535e175295d9cba23489a49fafebbac5ca0', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0'), size_on_disk=404565563, files=frozenset({CachedFileInfo(file_name='hackett_2020.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/hackett_2020.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4f47951c65c799b5c74ee3782b49c16cbb038d50'), size_on_disk=55, blob_last_accessed=1757105553.3228161, blob_last_modified=1756843395.5719483), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756843395.5059488, blob_last_modified=1756843395.5679483), CachedFileInfo(file_name='parse_mcisaac_data.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/scripts/parse_mcisaac_data.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0'), size_on_disk=4691, blob_last_accessed=1756843395.7579472, blob_last_modified=1756843395.820947), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/03e579e00c632cdb393c0ffafd9d15b069390e5c'), size_on_disk=9066, blob_last_accessed=1757439136.0598524, blob_last_modified=1757105945.6476595), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1757541699.4460278, blob_last_modified=1756843405.225893)}), refs=frozenset(), last_modified=1757105945.6476595), CachedRevisionInfo(commit_hash='2c196866d4f057cb5cf0adf1fe35f2c712c61025', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025'), size_on_disk=404565376, files=frozenset({CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756843395.5059488, blob_last_modified=1756843395.5679483), CachedFileInfo(file_name='hackett_2020.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/hackett_2020.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4f47951c65c799b5c74ee3782b49c16cbb038d50'), size_on_disk=55, blob_last_accessed=1757105553.3228161, blob_last_modified=1756843395.5719483), CachedFileInfo(file_name='parse_mcisaac_data.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/scripts/parse_mcisaac_data.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0'), size_on_disk=4691, blob_last_accessed=1756843395.7579472, blob_last_modified=1756843395.820947), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1757541699.4460278, blob_last_modified=1756843405.225893), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c3e72ccb1b8deba4bbfd18abe6081de7ec3914d9'), size_on_disk=8879, blob_last_accessed=1757105552.290822, blob_last_modified=1757104206.2667632)}), refs=frozenset(), last_modified=1757104206.2667632), CachedRevisionInfo(commit_hash='1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43'), size_on_disk=2678425, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=20/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/97adc0bb2b436c325555d82bb7f0ffb0e250d73092fcbf03ac0c00fa2b762e51'), size_on_disk=351379, blob_last_accessed=1756145253.7895415, blob_last_modified=1756145254.6265287), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=10/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4a2eb1051cad96c5b39a014f19b092f042b03700cc1b1a40c2de9e7edd73edf9'), size_on_disk=352201, blob_last_accessed=1756145253.7915416, blob_last_modified=1756145254.6375287), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=0/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/409962e1914ac9e9d631e4ae73e04b167822d0915d8d8d4a87703d4c7281ffb9'), size_on_disk=209695, blob_last_accessed=1756145254.7305272, blob_last_modified=1756145254.608529), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=15/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/51dfb68ed2518f0dd8ab4d4070d4896b6eca912ecb7e4aa773d857e3096020b5'), size_on_disk=351273, blob_last_accessed=1756145217.972108, blob_last_modified=1756142799.7054222), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=90/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/ebd06af83cc386d9094ce6b8d795b155c76c47c96da1a458743348264f1638ae'), size_on_disk=349917, blob_last_accessed=1756145253.7845416, blob_last_modified=1756145254.7235274), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/45cb0687c3ea72e7b32ad17dd799bc38ad02f80b'), size_on_disk=16034, blob_last_accessed=1756138543.6766906, blob_last_modified=1756138543.6746907), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=5/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/ed331d9d2b27a0958b12368c7b5d2d3383b111d3d1c39ed0e77b78561dc78b6a'), size_on_disk=348626, blob_last_accessed=1756145253.7845416, blob_last_modified=1756145254.6395288), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=45/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/db8595cd20dd7440c890c46025cf0643da8ec6be3e15bc18a8759b3fecdc02af'), size_on_disk=349256, blob_last_accessed=1756145253.7795417, blob_last_modified=1756145254.6225288), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=30/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/a85bd6b418d9644d9adaa1269c27f97469a4aaee51af63cf1aa041f62cd8ba2c'), size_on_disk=350044, blob_last_accessed=1756145253.7915416, blob_last_modified=1756145254.6385286)}), refs=frozenset(), last_modified=1756145254.7235274), CachedRevisionInfo(commit_hash='7192b0e2275ba5aa3ffb3752b87f1c3595f2331a', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a'), size_on_disk=404565656, files=frozenset({CachedFileInfo(file_name='hackett_2020.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/hackett_2020.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4f47951c65c799b5c74ee3782b49c16cbb038d50'), size_on_disk=55, blob_last_accessed=1757105553.3228161, blob_last_modified=1756843395.5719483), CachedFileInfo(file_name='parse_mcisaac_data.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/scripts/parse_mcisaac_data.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0'), size_on_disk=4691, blob_last_accessed=1756843395.7579472, blob_last_modified=1756843395.820947), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756843395.5059488, blob_last_modified=1756843395.5679483), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/f3899cd97da03a3543db10d76c15ee0442bb136e'), size_on_disk=9159, blob_last_accessed=1758145252.5929751, blob_last_modified=1757541416.40237), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1757541699.4460278, blob_last_modified=1756843405.225893)}), refs=frozenset(), last_modified=1757541416.40237)}), last_accessed=1758400469.288775, last_modified=1758155372.1328666), CachedRepoInfo(repo_id='BrentLab/hu_2007_reimand_2010', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010'), size_on_disk=42682732, nb_files=4, revisions=frozenset({CachedRevisionInfo(commit_hash='31ccd35daf785420014a1346a7db064dd306d4f8', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8'), size_on_disk=42682732, files=frozenset({CachedFileInfo(file_name='hu_2007_reimand_2010.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/hu_2007_reimand_2010.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/eb9be5a1f01907a81eb2ab4fbbee36ff8c50186f'), size_on_disk=63, blob_last_accessed=1756844741.7997973, blob_last_modified=1756844741.8557973), CachedFileInfo(file_name='hu_2007_reimand_2010.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/hu_2007_reimand_2010.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/47a15f3e23f8cb50af3217d00c6439d29a7422b5190116e4453a5f78cb22540d'), size_on_disk=42677516, blob_last_accessed=1756844742.1787965, blob_last_modified=1756844742.1477966), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756844741.8097973, blob_last_modified=1756844741.8767972), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/886f4614dfd1ef33709d462908ae18608c13e8ba'), size_on_disk=2692, blob_last_accessed=1756844742.0937967, blob_last_modified=1756844742.1537964)}), refs=frozenset({'main'}), last_modified=1756844742.1537964)}), last_accessed=1756844742.1787965, last_modified=1756844742.1537964), CachedRepoInfo(repo_id='BrentLab/kemmeren_2014', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014'), size_on_disk=319226397, nb_files=3, revisions=frozenset({CachedRevisionInfo(commit_hash='a6c9a954a1def1774b68582f117ad1abc5864a07', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07'), size_on_disk=319226397, files=frozenset({CachedFileInfo(file_name='kemmeren_2014.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07/kemmeren_2014.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/3c463017444bd7690959c0d6fff0d0ebe2faa7b916dd9c44e305156f6c98b863'), size_on_disk=319218929, blob_last_accessed=1756832531.471449, blob_last_modified=1756832530.9644527), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756832529.5094638, blob_last_modified=1756832529.5434635), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/a29a64128c10ef9691ee773131972c61f720ba07'), size_on_disk=5007, blob_last_accessed=1756832529.5094638, blob_last_modified=1756832529.5404634)}), refs=frozenset({'main'}), last_modified=1756832530.9644527)}), last_accessed=1756832531.471449, last_modified=1756832530.9644527)}), warnings=[CorruptedCacheException(\"Snapshots dir doesn't exist in cached repo: /home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots\")])" + ] + }, + "execution_count": 76, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "cache_info" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2. Understanding the 3-Case Metadata Caching Strategy\n", + "\n", + "HfCacheManager implements an intelligent 3-case strategy for metadata access that minimizes downloads and maximizes performance:\n", + "\n", + "1. **DuckDB Check**: First check if metadata already exists in the DuckDB database\n", + "2. **Cache Load**: If not in DuckDB, try to load from local HuggingFace cache \n", + "3. **Download**: If not cached, download from HuggingFace Hub\n", + "\n", + "This strategy is implemented in the internal `_get_metadata_for_config()` method and automatically used when loading data with HfQueryAPI." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Demonstrating the 3-Case Strategy\n", + "\n", + "Let's see how the caching strategy works by examining what metadata tables would be created and checking the DuckDB state." + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DuckDB Metadata Tables (Case 1):\n", + "===================================\n", + " No metadata tables found in DuckDB\n", + " → Would proceed to Case 2 (check HF cache) or Case 3 (download)\n", + "\n", + "The 3-case strategy ensures:\n", + "• Fast access: DuckDB queries are nearly instantaneous\n", + "• Minimal downloads: Reuse locally cached files when possible\n", + "• Automatic fallback: Download only when necessary\n", + "• Transparent operation: Works automatically with HfQueryAPI\n" + ] + } + ], + "source": [ + "# Check current DuckDB state (Case 1 check)\n", + "tables = conn.execute(\n", + " \"SELECT table_name FROM information_schema.tables WHERE table_name LIKE 'metadata_%'\"\n", + ").fetchall()\n", + "\n", + "print(\"DuckDB Metadata Tables (Case 1):\")\n", + "print(\"=\" * 35)\n", + "if tables:\n", + " for table in tables:\n", + " count = conn.execute(f\"SELECT COUNT(*) FROM {table[0]}\").fetchone()[0]\n", + " print(f\" • {table[0]}: {count} rows\")\n", + "else:\n", + " print(\" No metadata tables found in DuckDB\")\n", + " print(\" → Would proceed to Case 2 (check HF cache) or Case 3 (download)\")\n", + "\n", + "print(f\"\\nThe 3-case strategy ensures:\")\n", + "print(\"• Fast access: DuckDB queries are nearly instantaneous\")\n", + "print(\"• Minimal downloads: Reuse locally cached files when possible\") \n", + "print(\"• Automatic fallback: Download only when necessary\")\n", + "print(\"• Transparent operation: Works automatically with HfQueryAPI\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Checking HuggingFace Cache Status (Case 2)\n", + "\n", + "The second case checks if files are already cached locally by HuggingFace. Let's examine the cache state for our target repository." + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "HuggingFace Cache Status (Case 2):\n", + "========================================\n", + "✓ Repository BrentLab/hackett_2020 found in cache\n", + " Size: 407.3M\n", + " Revisions: 5\n", + " Files: 17\n", + " Latest revision: 60b3ecf6\n", + " Last accessed: 1758155372.1328666\n", + "\n", + " → Case 2 would succeed: Load from local cache\n", + "\n", + "Cache efficiency: Using local files avoids re-downloading 407.3M\n" + ] + } + ], + "source": [ + "# Check if target repository is in HuggingFace cache\n", + "cache_info = scan_cache_dir()\n", + "target_repo = None\n", + "\n", + "for repo in cache_info.repos:\n", + " if repo.repo_id == cache_manager.repo_id:\n", + " target_repo = repo\n", + " break\n", + "\n", + "print(\"HuggingFace Cache Status (Case 2):\")\n", + "print(\"=\" * 40)\n", + "\n", + "if target_repo:\n", + " print(f\"✓ Repository {cache_manager.repo_id} found in cache\")\n", + " print(f\" Size: {target_repo.size_on_disk_str}\")\n", + " print(f\" Revisions: {len(target_repo.revisions)}\")\n", + " print(f\" Files: {target_repo.nb_files}\")\n", + " \n", + " # Show latest revision info\n", + " if target_repo.revisions:\n", + " latest_rev = max(target_repo.revisions, key=lambda r: r.last_modified)\n", + " print(f\" Latest revision: {latest_rev.commit_hash[:8]}\")\n", + " print(f\" Last accessed: {latest_rev.last_modified}\")\n", + " \n", + " print(\"\\n → Case 2 would succeed: Load from local cache\")\n", + "else:\n", + " print(f\"✗ Repository {cache_manager.repo_id} not found in cache\")\n", + " print(\" → Would proceed to Case 3: Download from HuggingFace Hub\")\n", + "\n", + "print(f\"\\nCache efficiency: Using local files avoids re-downloading {target_repo.size_on_disk_str if target_repo else 'unknown size'}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3. Cache Management and Cleanup\n", + "\n", + "HfCacheManager's primary value is in providing sophisticated cache management. Let's explore the different cleanup strategies available." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Cache Overview and Current Status\n", + "\n", + "Before cleaning, let's understand what we're working with." + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Current HuggingFace Cache Overview:\n", + "========================================\n", + "Total cache size: 4.6G\n", + "Number of repositories: 9\n", + "\n", + "Largest repositories (top 5):\n", + " • BrentLab/barkai_compendium: 3.6G (1 revisions)\n", + " • BrentLab/hackett_2020: 407.3M (5 revisions)\n", + " • BrentLab/kemmeren_2014: 319.2M (1 revisions)\n", + " • BrentLab/rossi_2021: 213.7M (7 revisions)\n", + " • BrentLab/hu_2007_reimand_2010: 42.7M (1 revisions)\n", + " ... and 4 more repositories\n", + "\n", + "Total revisions across all repos: 29\n", + "Revisions older than 30 days: 0\n", + "Recent revisions (≤30 days): 29\n" + ] + } + ], + "source": [ + "# Get comprehensive cache overview\n", + "cache_info = scan_cache_dir()\n", + "\n", + "print(\"Current HuggingFace Cache Overview:\")\n", + "print(\"=\" * 40)\n", + "print(f\"Total cache size: {cache_info.size_on_disk_str}\")\n", + "print(f\"Number of repositories: {len(cache_info.repos)}\")\n", + "\n", + "# Analyze cache by repository size\n", + "repo_sizes = []\n", + "for repo in cache_info.repos:\n", + " repo_sizes.append((repo.repo_id, repo.size_on_disk, repo.size_on_disk_str, len(repo.revisions)))\n", + "\n", + "# Sort by size (largest first)\n", + "repo_sizes.sort(key=lambda x: x[1], reverse=True)\n", + "\n", + "print(f\"\\nLargest repositories (top 5):\")\n", + "for repo_id, size_bytes, size_str, revisions in repo_sizes[:5]:\n", + " print(f\" • {repo_id}: {size_str} ({revisions} revisions)\")\n", + "\n", + "if len(repo_sizes) > 5:\n", + " print(f\" ... and {len(repo_sizes) - 5} more repositories\")\n", + "\n", + "# Calculate total revisions\n", + "total_revisions = sum(len(repo.revisions) for repo in cache_info.repos)\n", + "print(f\"\\nTotal revisions across all repos: {total_revisions}\")\n", + "\n", + "# Show age distribution\n", + "from datetime import datetime\n", + "now = datetime.now().timestamp()\n", + "old_revisions = 0\n", + "for repo in cache_info.repos:\n", + " for rev in repo.revisions:\n", + " age_days = (now - rev.last_modified) / (24 * 3600)\n", + " if age_days > 30:\n", + " old_revisions += 1\n", + "\n", + "print(f\"Revisions older than 30 days: {old_revisions}\")\n", + "print(f\"Recent revisions (≤30 days): {total_revisions - old_revisions}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Querying Loaded Metadata\n", + "\n", + "Once metadata is loaded into DuckDB, we can query it using SQL." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Internal Cache Management Methods\n", + "\n", + "HfCacheManager provides several internal methods that work behind the scenes. Let's explore what these methods do and how they integrate with the caching strategy." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4. Working with Specific Metadata Configurations\n", + "\n", + "You can also retrieve metadata for specific configurations rather than all at once." + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "HfCacheManager Internal Methods:\n", + "===================================\n", + "\n", + "1. _get_metadata_for_config(config)\n", + " → Implements the 3-case strategy for a specific configuration\n", + " → Returns detailed result with strategy used and success status\n", + "\n", + "2. _check_metadata_exists_in_duckdb(table_name)\n", + " → Case 1: Checks if metadata table already exists in DuckDB\n", + " → Fast check using information_schema.tables\n", + "\n", + "3. _load_metadata_from_cache(config, table_name)\n", + " → Case 2: Attempts to load from local HuggingFace cache\n", + " → Uses try_to_load_from_cache() to find cached files\n", + "\n", + "4. _download_and_load_metadata(config, table_name)\n", + " → Case 3: Downloads from HuggingFace Hub if not cached\n", + " → Uses snapshot_download() for efficient file retrieval\n", + "\n", + "5. _create_duckdb_table_from_files(file_paths, table_name)\n", + " → Creates DuckDB views from parquet files\n", + " → Handles both single files and multiple files efficiently\n", + "\n", + "6. _extract_embedded_metadata_field(data_table, field, metadata_table)\n", + " → Extracts metadata fields from data tables\n", + " → Creates separate queryable metadata views\n", + "\n", + "These methods work together to provide:\n", + "• Transparent caching that 'just works'\n", + "• Minimal network usage through intelligent fallbacks\n", + "• Fast metadata access via DuckDB views\n", + "• Automatic handling of different file structures\n" + ] + } + ], + "source": [ + "# Demonstrate understanding of internal cache methods\n", + "print(\"HfCacheManager Internal Methods:\")\n", + "print(\"=\" * 35)\n", + "\n", + "print(\"\\n1. _get_metadata_for_config(config)\")\n", + "print(\" → Implements the 3-case strategy for a specific configuration\")\n", + "print(\" → Returns detailed result with strategy used and success status\")\n", + "\n", + "print(\"\\n2. _check_metadata_exists_in_duckdb(table_name)\")\n", + "print(\" → Case 1: Checks if metadata table already exists in DuckDB\")\n", + "print(\" → Fast check using information_schema.tables\")\n", + "\n", + "print(\"\\n3. _load_metadata_from_cache(config, table_name)\")\n", + "print(\" → Case 2: Attempts to load from local HuggingFace cache\")\n", + "print(\" → Uses try_to_load_from_cache() to find cached files\")\n", + "\n", + "print(\"\\n4. _download_and_load_metadata(config, table_name)\")\n", + "print(\" → Case 3: Downloads from HuggingFace Hub if not cached\")\n", + "print(\" → Uses snapshot_download() for efficient file retrieval\")\n", + "\n", + "print(\"\\n5. _create_duckdb_table_from_files(file_paths, table_name)\")\n", + "print(\" → Creates DuckDB views from parquet files\")\n", + "print(\" → Handles both single files and multiple files efficiently\")\n", + "\n", + "print(\"\\n6. _extract_embedded_metadata_field(data_table, field, metadata_table)\")\n", + "print(\" → Extracts metadata fields from data tables\")\n", + "print(\" → Creates separate queryable metadata views\")\n", + "\n", + "print(\"\\nThese methods work together to provide:\")\n", + "print(\"• Transparent caching that 'just works'\")\n", + "print(\"• Minimal network usage through intelligent fallbacks\")\n", + "print(\"• Fast metadata access via DuckDB views\")\n", + "print(\"• Automatic handling of different file structures\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 5. Extracting Embedded Metadata\n", + "\n", + "Some datasets have metadata embedded within their data files. The HfCacheManager can extract this embedded metadata into separate, queryable tables." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4. Embedded Metadata Extraction\n", + "\n", + "One unique feature of HfCacheManager is the ability to extract embedded metadata fields from data tables into separate, queryable metadata tables." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Demonstrate embedded metadata extraction concept\n", + "print(\"Embedded Metadata Extraction:\")\n", + "print(\"=\" * 35)\n", + "\n", + "print(\"\\nScenario: You have a data table with embedded metadata fields\")\n", + "print(\"Example: genomics data with 'experimental_condition' field\")\n", + "\n", + "# Create sample data to demonstrate the concept\n", + "conn.execute(\"\"\"\n", + " CREATE TABLE sample_genomics_data AS \n", + " SELECT \n", + " 'gene_' || (row_number() OVER()) as gene_id,\n", + " random() * 1000 as expression_value,\n", + " CASE \n", + " WHEN (row_number() OVER()) % 4 = 0 THEN 'control'\n", + " WHEN (row_number() OVER()) % 4 = 1 THEN 'treatment_A'\n", + " WHEN (row_number() OVER()) % 4 = 2 THEN 'treatment_B'\n", + " ELSE 'stress_condition'\n", + " END as experimental_condition,\n", + " CASE \n", + " WHEN (row_number() OVER()) % 3 = 0 THEN 'timepoint_0h'\n", + " WHEN (row_number() OVER()) % 3 = 1 THEN 'timepoint_6h'\n", + " ELSE 'timepoint_24h'\n", + " END as timepoint\n", + " FROM range(100)\n", + "\"\"\")\n", + "\n", + "print(\"✓ Created sample genomics data with embedded metadata fields\")\n", + "\n", + "# Show the data structure\n", + "sample_data = conn.execute(\n", + " \"SELECT * FROM sample_genomics_data LIMIT 5\"\n", + ").fetchall()\n", + "\n", + "print(f\"\\nSample data structure:\")\n", + "print(\"gene_id | expression_value | experimental_condition | timepoint\")\n", + "print(\"-\" * 65)\n", + "for row in sample_data:\n", + " print(f\"{row[0]:8} | {row[1]:15.1f} | {row[2]:20} | {row[3]}\")\n", + "\n", + "print(f\"\\nEmbedded metadata fields identified:\")\n", + "print(\"• experimental_condition: Contains treatment/control information\")\n", + "print(\"• timepoint: Contains temporal sampling information\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Use HfCacheManager to extract embedded metadata\n", + "print(\"Using HfCacheManager for Metadata Extraction:\")\n", + "print(\"=\" * 50)\n", + "\n", + "# Extract experimental_condition metadata\n", + "success1 = cache_manager._extract_embedded_metadata_field(\n", + " 'sample_genomics_data', \n", + " 'experimental_condition', \n", + " 'metadata_experimental_conditions'\n", + ")\n", + "\n", + "# Extract timepoint metadata \n", + "success2 = cache_manager._extract_embedded_metadata_field(\n", + " 'sample_genomics_data',\n", + " 'timepoint', \n", + " 'metadata_timepoints'\n", + ")\n", + "\n", + "print(f\"Experimental condition extraction: {'✓ Success' if success1 else '✗ Failed'}\")\n", + "print(f\"Timepoint extraction: {'✓ Success' if success2 else '✗ Failed'}\")\n", + "\n", + "# Show extracted metadata tables\n", + "if success1:\n", + " print(f\"\\nExtracted experimental conditions:\")\n", + " conditions = conn.execute(\n", + " \"SELECT value, count FROM metadata_experimental_conditions ORDER BY count DESC\"\n", + " ).fetchall()\n", + " \n", + " for condition, count in conditions:\n", + " print(f\" • {condition}: {count} samples\")\n", + "\n", + "if success2:\n", + " print(f\"\\nExtracted timepoints:\")\n", + " timepoints = conn.execute(\n", + " \"SELECT value, count FROM metadata_timepoints ORDER BY count DESC\"\n", + " ).fetchall()\n", + " \n", + " for timepoint, count in timepoints:\n", + " print(f\" • {timepoint}: {count} samples\")\n", + "\n", + "print(f\"\\nBenefits of extraction:\")\n", + "print(\"• Separate queryable metadata tables\")\n", + "print(\"• Fast metadata-based filtering and analysis\") \n", + "print(\"• Clear separation of data and metadata concerns\")\n", + "print(\"• Reusable metadata across different analyses\")" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Current HuggingFace Cache Status:\n", + "===================================\n", + "Total size: 4.6G\n", + "Number of repositories: 9\n", + "\n", + "Repository breakdown:\n", + " • BrentLab/mahendrawada_2025: 2.0M (3 revisions)\n", + " • BrentLab/yeast_genome_resources: 114.5K (7 revisions)\n", + " • BrentLab/barkai_compendium: 3.6G (1 revisions)\n", + " • BrentLab/rossi_2021: 213.7M (7 revisions)\n", + " • BrentLab/harbison_2004: 12.3K (2 revisions)\n", + " ... and 4 more repositories\n", + "\n", + "Target repository (BrentLab/hackett_2020) cache info:\n", + " Size: 407.3M\n", + " Revisions: 5\n", + " Latest revision: 60b3ecf6\n", + " Last modified: 1758155372.1328666\n" + ] + } + ], + "source": [ + "from huggingface_hub import scan_cache_dir\n", + "\n", + "# Get current cache information \n", + "cache_info = scan_cache_dir()\n", + "\n", + "print(\"Current HuggingFace Cache Status:\")\n", + "print(\"=\" * 35)\n", + "print(f\"Total size: {cache_info.size_on_disk_str}\")\n", + "print(f\"Number of repositories: {len(cache_info.repos)}\")\n", + "\n", + "print(\"\\nRepository breakdown:\")\n", + "for repo in list(cache_info.repos)[:5]: # Show first 5 repos\n", + " print(f\" • {repo.repo_id}: {repo.size_on_disk_str} ({len(repo.revisions)} revisions)\")\n", + "\n", + "if len(cache_info.repos) > 5:\n", + " print(f\" ... and {len(cache_info.repos) - 5} more repositories\")\n", + "\n", + "# Show target repository if it exists in cache\n", + "target_repo = None\n", + "for repo in cache_info.repos:\n", + " if repo.repo_id == cache_manager.repo_id:\n", + " target_repo = repo\n", + " break\n", + "\n", + "if target_repo:\n", + " print(f\"\\nTarget repository ({cache_manager.repo_id}) cache info:\")\n", + " print(f\" Size: {target_repo.size_on_disk_str}\")\n", + " print(f\" Revisions: {len(target_repo.revisions)}\")\n", + " if target_repo.revisions:\n", + " latest_rev = max(target_repo.revisions, key=lambda r: r.last_modified)\n", + " print(f\" Latest revision: {latest_rev.commit_hash[:8]}\")\n", + " print(f\" Last modified: {latest_rev.last_modified}\")\n", + "else:\n", + " print(f\"\\nTarget repository ({cache_manager.repo_id}) not found in cache.\")\n", + " print(\"It may need to be downloaded first.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Cache Cleanup by Age" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Cleaning cache by age (30+ days old):\n", + "========================================\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:__main__:No old revisions found to delete\n", + "INFO:__main__:Found 0 old revisions. Will free 0.0\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Cleanup strategy created:\n", + "Expected space freed: 0.0\n", + "Items to delete: 0\n", + "No old files found for cleanup.\n" + ] + } + ], + "source": [ + "# Clean cache entries older than 30 days (dry run)\n", + "print(\"Cleaning cache by age (30+ days old):\")\n", + "print(\"=\" * 40)\n", + "\n", + "age_cleanup = cache_manager.clean_cache_by_age(\n", + " max_age_days=30,\n", + " dry_run=True # Set to False to actually execute\n", + ")\n", + "\n", + "print(f\"\\nCleanup strategy created:\")\n", + "print(f\"Expected space freed: {age_cleanup.expected_freed_size_str}\")\n", + "\n", + "# Count total items to delete across all categories\n", + "total_items = len(age_cleanup.blobs) + len(age_cleanup.refs) + len(age_cleanup.repos) + len(age_cleanup.snapshots)\n", + "print(f\"Items to delete: {total_items}\")\n", + "\n", + "# Show breakdown of what would be deleted\n", + "if total_items > 0:\n", + " print(f\"\\nBreakdown of items to delete:\")\n", + " print(f\" • Blob files: {len(age_cleanup.blobs)}\")\n", + " print(f\" • Reference files: {len(age_cleanup.refs)}\")\n", + " print(f\" • Repository directories: {len(age_cleanup.repos)}\")\n", + " print(f\" • Snapshot directories: {len(age_cleanup.snapshots)}\")\n", + " \n", + " # Show some example items\n", + " if age_cleanup.blobs:\n", + " print(f\"\\nSample blob files to delete:\")\n", + " for item in list(age_cleanup.blobs)[:3]:\n", + " print(f\" • {item}\")\n", + " if len(age_cleanup.blobs) > 3:\n", + " print(f\" ... and {len(age_cleanup.blobs) - 3} more blob files\")\n", + "else:\n", + " print(\"No old files found for cleanup.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Cache Cleanup by Size" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Cleaning cache to target size: 1GB\n", + "========================================\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:__main__:Selected 14 revisions for deletion. Will free 4.0G\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Size-based cleanup strategy:\n", + "Expected space freed: 4.0G\n", + "Items to delete: 30\n", + "\n", + "Comparing cleanup strategies for 1GB:\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:__main__:Selected 14 revisions for deletion. Will free 4.0G\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " • oldest_first : 4.0G (30 items)\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:__main__:Selected 1 revisions for deletion. Will free 3.6G\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " • largest_first : 3.6G (1 items)\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:__main__:Selected 14 revisions for deletion. Will free 4.0G\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " • least_used : 4.0G (30 items)\n" + ] + } + ], + "source": [ + "# Clean cache to target size (dry run)\n", + "target_size = \"1GB\"\n", + "print(f\"Cleaning cache to target size: {target_size}\")\n", + "print(\"=\" * 40)\n", + "\n", + "size_cleanup = cache_manager.clean_cache_by_size(\n", + " target_size=target_size,\n", + " strategy=\"oldest_first\", # Can be: oldest_first, largest_first, least_used\n", + " dry_run=True\n", + ")\n", + "\n", + "print(f\"\\nSize-based cleanup strategy:\")\n", + "print(f\"Expected space freed: {size_cleanup.expected_freed_size_str}\")\n", + "\n", + "# Count total items to delete across all categories\n", + "total_items = len(size_cleanup.blobs) + len(size_cleanup.refs) + len(size_cleanup.repos) + len(size_cleanup.snapshots)\n", + "print(f\"Items to delete: {total_items}\")\n", + "\n", + "# Compare different strategies\n", + "strategies = [\"oldest_first\", \"largest_first\", \"least_used\"]\n", + "print(f\"\\nComparing cleanup strategies for {target_size}:\")\n", + "\n", + "for strategy in strategies:\n", + " try:\n", + " strategy_result = cache_manager.clean_cache_by_size(\n", + " target_size=target_size,\n", + " strategy=strategy,\n", + " dry_run=True\n", + " )\n", + " strategy_total = (len(strategy_result.blobs) + len(strategy_result.refs) + \n", + " len(strategy_result.repos) + len(strategy_result.snapshots))\n", + " print(f\" • {strategy:15}: {strategy_result.expected_freed_size_str:>8} \"\n", + " f\"({strategy_total} items)\")\n", + " except Exception as e:\n", + " print(f\" • {strategy:15}: Error - {e}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Cleaning Unused Revisions" + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Cleaning unused revisions (keep latest 2 per repo):\n", + "==================================================\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:__main__:Found 14 unused revisions. Will free 216.4M\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Revision cleanup strategy:\n", + "Expected space freed: 216.4M\n", + "Items to delete: 75\n", + "\n", + "Breakdown of cleanup:\n", + " • Blob files: 61\n", + " • Reference files: 0\n", + " • Repository directories: 0\n", + " • Snapshot directories: 14\n", + "\n", + "Per-repository revision analysis:\n", + "\n", + " • BrentLab/mahendrawada_2025:\n", + " Total revisions: 3\n", + " Would keep: 2\n", + " Would delete: 1\n", + " Keep: 8bea431b (modified: 1758155849.9076133)\n", + " Keep: d602c165 (modified: 1757439229.7949307)\n", + " Delete: 66e0cf1f (modified: 1756858618.1383443)\n", + "\n", + " • BrentLab/yeast_genome_resources:\n", + " Total revisions: 7\n", + " Would keep: 2\n", + " Would delete: 5\n", + " Keep: 42beb284 (modified: 1758155946.5549896)\n", + " Keep: 15fdb72f (modified: 1755819093.2306638)\n", + " Delete: 7441b9a8 (modified: 1755816785.6988702)\n", + "\n", + " • BrentLab/barkai_compendium:\n", + " Total revisions: 1\n", + " Would keep: 1\n", + " Would delete: 0\n", + " Keep: a987ef37 (modified: 1756926783.3167186)\n" + ] + } + ], + "source": [ + "# Clean unused revisions, keeping only the latest 2 per repository\n", + "print(\"Cleaning unused revisions (keep latest 2 per repo):\")\n", + "print(\"=\" * 50)\n", + "\n", + "revision_cleanup = cache_manager.clean_unused_revisions(\n", + " keep_latest=2,\n", + " dry_run=True\n", + ")\n", + "\n", + "print(f\"\\nRevision cleanup strategy:\")\n", + "print(f\"Expected space freed: {revision_cleanup.expected_freed_size_str}\")\n", + "\n", + "# Count total items to delete across all categories\n", + "total_items = len(revision_cleanup.blobs) + len(revision_cleanup.refs) + len(revision_cleanup.repos) + len(revision_cleanup.snapshots)\n", + "print(f\"Items to delete: {total_items}\")\n", + "\n", + "# Show breakdown\n", + "if total_items > 0:\n", + " print(f\"\\nBreakdown of cleanup:\")\n", + " print(f\" • Blob files: {len(revision_cleanup.blobs)}\")\n", + " print(f\" • Reference files: {len(revision_cleanup.refs)}\") \n", + " print(f\" • Repository directories: {len(revision_cleanup.repos)}\")\n", + " print(f\" • Snapshot directories: {len(revision_cleanup.snapshots)}\")\n", + "\n", + "# Show repository-specific breakdown\n", + "cache_info = scan_cache_dir()\n", + "if cache_info.repos:\n", + " print(\"\\nPer-repository revision analysis:\")\n", + " for repo in list(cache_info.repos)[:3]:\n", + " print(f\"\\n • {repo.repo_id}:\")\n", + " print(f\" Total revisions: {len(repo.revisions)}\")\n", + " print(f\" Would keep: {min(2, len(repo.revisions))}\")\n", + " print(f\" Would delete: {max(0, len(repo.revisions) - 2)}\")\n", + " \n", + " # Show revision details\n", + " sorted_revisions = sorted(repo.revisions, key=lambda r: r.last_modified, reverse=True)\n", + " for i, rev in enumerate(sorted_revisions[:2]):\n", + " print(f\" Keep: {rev.commit_hash[:8]} (modified: {rev.last_modified})\")\n", + " \n", + " for rev in sorted_revisions[2:3]: # Show one that would be deleted\n", + " print(f\" Delete: {rev.commit_hash[:8]} (modified: {rev.last_modified})\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Automated Cache Management" + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:__main__:Starting automated cache cleanup...\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Automated cache cleanup (comprehensive):\n", + "========================================\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:__main__:No old revisions found to delete\n", + "INFO:__main__:Found 0 old revisions. Will free 0.0\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n", + "INFO:__main__:Found 14 unused revisions. Will free 216.4M\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n", + "INFO:__main__:Automated cleanup complete. Total freed: 206.4MB\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Automated cleanup executed 2 strategies:\n", + " 1. Strategy freed: 0.0\n", + " 2. Strategy freed: 216.4M\n", + "\n", + "Total space that would be freed: 206.4MB\n", + "Cache size after cleanup: 4.1GB\n" + ] + } + ], + "source": [ + "# Automated cache cleanup with multiple strategies\n", + "print(\"Automated cache cleanup (comprehensive):\")\n", + "print(\"=\" * 40)\n", + "\n", + "auto_cleanup = cache_manager.auto_clean_cache(\n", + " max_age_days=30, # Remove anything older than 30 days\n", + " max_total_size=\"5GB\", # Target maximum cache size\n", + " keep_latest_per_repo=2, # Keep 2 latest revisions per repo\n", + " dry_run=True # Dry run for safety\n", + ")\n", + "\n", + "print(f\"\\nAutomated cleanup executed {len(auto_cleanup)} strategies:\")\n", + "\n", + "total_freed = 0\n", + "for i, strategy in enumerate(auto_cleanup, 1):\n", + " print(f\" {i}. Strategy freed: {strategy.expected_freed_size_str}\")\n", + " total_freed += strategy.expected_freed_size\n", + "\n", + "print(f\"\\nTotal space that would be freed: {cache_manager._format_bytes(total_freed)}\")\n", + "\n", + "# Calculate final cache size\n", + "current_cache = scan_cache_dir()\n", + "final_size = current_cache.size_on_disk - total_freed\n", + "print(f\"Cache size after cleanup: {cache_manager._format_bytes(max(0, final_size))}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 7. Best Practices and Performance Tips\n", + "\n", + "Here are some best practices for using HfCacheManager effectively:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Performance Best Practices" + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Performance Demonstration: Cache Management Benefits\n", + "=======================================================\n", + "\n", + "Demonstrating cache cleanup performance...\n", + "\n", + "1. Cache scanning performance:\n", + " Time to scan cache: 0.111 seconds\n", + " Repositories found: 9\n", + " Total cache size: 4.6G\n", + "\n", + "2. Cleanup strategy creation performance:\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:__main__:No old revisions found to delete\n", + "INFO:__main__:Found 0 old revisions. Will free 0.0\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Age cleanup strategy: 0.109 seconds\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:__main__:Selected 14 revisions for deletion. Will free 4.0G\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Size cleanup strategy: 0.105 seconds\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:__main__:Found 14 unused revisions. Will free 216.4M\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Revision cleanup strategy: 0.111 seconds\n", + "\n", + "Performance insights:\n", + "• Cache scanning is fast: 0.111s for 9 repos\n", + "• Cleanup strategy creation is efficient\n", + "• Dry runs allow safe preview of cleanup operations\n", + "• Multiple strategies can be compared quickly\n" + ] + } + ], + "source": [ + "import time\n", + "\n", + "print(\"Performance Demonstration: Cache Management Benefits\")\n", + "print(\"=\" * 55)\n", + "\n", + "print(\"\\nDemonstrating cache cleanup performance...\")\n", + "\n", + "# Show performance of cache scanning and cleanup strategy creation\n", + "print(\"\\n1. Cache scanning performance:\")\n", + "start_time = time.time()\n", + "cache_info = scan_cache_dir()\n", + "scan_time = time.time() - start_time\n", + "print(f\" Time to scan cache: {scan_time:.3f} seconds\")\n", + "print(f\" Repositories found: {len(cache_info.repos)}\")\n", + "print(f\" Total cache size: {cache_info.size_on_disk_str}\")\n", + "\n", + "# Show performance of cleanup strategy creation\n", + "print(\"\\n2. Cleanup strategy creation performance:\")\n", + "\n", + "start_time = time.time()\n", + "age_strategy = cache_manager.clean_cache_by_age(max_age_days=30, dry_run=True)\n", + "age_time = time.time() - start_time\n", + "print(f\" Age cleanup strategy: {age_time:.3f} seconds\")\n", + "\n", + "start_time = time.time()\n", + "size_strategy = cache_manager.clean_cache_by_size(target_size=\"1GB\", dry_run=True)\n", + "size_time = time.time() - start_time\n", + "print(f\" Size cleanup strategy: {size_time:.3f} seconds\")\n", + "\n", + "start_time = time.time()\n", + "revision_strategy = cache_manager.clean_unused_revisions(keep_latest=2, dry_run=True)\n", + "revision_time = time.time() - start_time\n", + "print(f\" Revision cleanup strategy: {revision_time:.3f} seconds\")\n", + "\n", + "print(f\"\\nPerformance insights:\")\n", + "print(f\"• Cache scanning is fast: {scan_time:.3f}s for {len(cache_info.repos)} repos\")\n", + "print(f\"• Cleanup strategy creation is efficient\")\n", + "print(f\"• Dry runs allow safe preview of cleanup operations\")\n", + "print(f\"• Multiple strategies can be compared quickly\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Memory and Storage Optimization" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Memory and Storage Optimization Tips:\n", + "========================================\n", + "\n", + "1. DuckDB Views vs Tables:\n", + " • HfCacheManager creates VIEWS by default (not tables)\n", + " • Views reference original parquet files without duplication\n", + " • This saves storage space while enabling fast SQL queries\n", + "\n", + "2. Metadata-First Workflow:\n", + " • Load metadata first to understand data structure\n", + " • Use metadata to filter and select specific data subsets\n", + " • Avoid loading entire datasets when only portions are needed\n", + "\n", + "3. Cache Management Strategy:\n", + " • Run automated cleanup regularly\n", + " • Keep cache size reasonable for your system\n", + " • Prioritize keeping recent and frequently-used datasets\n" + ] + } + ], + "source": [ + "print(\"Memory and Storage Optimization Tips:\")\n", + "print(\"=\" * 40)\n", + "\n", + "print(\"\\n1. DuckDB Views vs Tables:\")\n", + "print(\" • HfCacheManager creates VIEWS by default (not tables)\")\n", + "print(\" • Views reference original parquet files without duplication\")\n", + "print(\" • This saves storage space while enabling fast SQL queries\")\n", + "\n", + "print(\"\\n2. Metadata-First Workflow:\")\n", + "print(\" • Load metadata first to understand data structure\")\n", + "print(\" • Use metadata to filter and select specific data subsets\")\n", + "print(\" • Avoid loading entire datasets when only portions are needed\")\n", + "\n", + "print(\"\\n3. Cache Management Strategy:\")\n", + "print(\" • Run automated cleanup regularly\")\n", + "print(\" • Keep cache size reasonable for your system\")\n", + "print(\" • Prioritize keeping recent and frequently-used datasets\")\n", + "\n", + "# Demonstrate DuckDB view benefits\n", + "tables_info = conn.execute(\n", + " \"SELECT table_name, table_type FROM information_schema.tables WHERE table_name LIKE 'metadata_%'\"\n", + ").fetchall()\n", + "\n", + "if tables_info:\n", + " print(f\"\\nCurrent DuckDB objects ({len(tables_info)} total):\")\n", + " for table_name, table_type in tables_info:\n", + " print(f\" • {table_name}: {table_type}\")\n", + " \n", + " view_count = sum(1 for _, table_type in tables_info if table_type == 'VIEW')\n", + " print(f\"\\n {view_count} views created (space-efficient!)\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 8. Integration with Other Components\n", + "\n", + "The HfCacheManager works seamlessly with other components in the tfbpapi ecosystem." + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "HfCacheManager Integration Workflow:\n", + "========================================\n", + "\n", + "1. Cache Management Setup:\n", + " from tfbpapi.HfCacheManager import HfCacheManager\n", + " cache_mgr = HfCacheManager(repo_id, duckdb_conn)\n", + " # Inherits all DataCard functionality + cache management\n", + "\n", + "2. Proactive Cache Cleanup:\n", + " # Clean before large operations\n", + " cache_mgr.auto_clean_cache(max_total_size='5GB', dry_run=False)\n", + " # Or use specific strategies\n", + " cache_mgr.clean_cache_by_age(max_age_days=30)\n", + "\n", + "3. Data Loading with Cache Awareness:\n", + " # The 3-case strategy works automatically with HfQueryAPI\n", + " from tfbpapi import HfQueryAPI\n", + " query_api = HfQueryAPI(repo_id, duckdb_conn)\n", + " # Metadata loading uses cache manager's strategy\n", + " data_df = query_api.get_pandas('config_name')\n", + "\n", + "4. Embedded Metadata Extraction:\n", + " # Extract metadata fields after data loading\n", + " cache_mgr._extract_embedded_metadata_field(\n", + " 'data_table_name', 'metadata_field', 'metadata_table_name')\n", + "\n", + "5. Regular Cache Maintenance:\n", + " # Schedule regular cleanup\n", + " cache_mgr.clean_unused_revisions(keep_latest=2)\n", + " cache_mgr.clean_cache_by_size('10GB', strategy='oldest_first')\n", + "\n", + "Current Session State:\n", + "Repository: BrentLab/hackett_2020\n", + "DuckDB tables: 0\n", + "HF cache size: 4.6G\n", + "Cache repositories: 9\n" + ] + } + ], + "source": [ + "print(\"HfCacheManager Integration Workflow:\")\n", + "print(\"=\" * 40)\n", + "\n", + "print(\"\\n1. Cache Management Setup:\")\n", + "print(\" from tfbpapi.HfCacheManager import HfCacheManager\")\n", + "print(\" cache_mgr = HfCacheManager(repo_id, duckdb_conn)\")\n", + "print(\" # Inherits all DataCard functionality + cache management\")\n", + "\n", + "print(\"\\n2. Proactive Cache Cleanup:\")\n", + "print(\" # Clean before large operations\")\n", + "print(\" cache_mgr.auto_clean_cache(max_total_size='5GB', dry_run=False)\")\n", + "print(\" # Or use specific strategies\")\n", + "print(\" cache_mgr.clean_cache_by_age(max_age_days=30)\")\n", + "\n", + "print(\"\\n3. Data Loading with Cache Awareness:\")\n", + "print(\" # The 3-case strategy works automatically with HfQueryAPI\")\n", + "print(\" from tfbpapi import HfQueryAPI\")\n", + "print(\" query_api = HfQueryAPI(repo_id, duckdb_conn)\")\n", + "print(\" # Metadata loading uses cache manager's strategy\")\n", + "print(\" data_df = query_api.get_pandas('config_name')\")\n", + "\n", + "print(\"\\n4. Embedded Metadata Extraction:\")\n", + "print(\" # Extract metadata fields after data loading\")\n", + "print(\" cache_mgr._extract_embedded_metadata_field(\")\n", + "print(\" 'data_table_name', 'metadata_field', 'metadata_table_name')\")\n", + "\n", + "print(\"\\n5. Regular Cache Maintenance:\")\n", + "print(\" # Schedule regular cleanup\")\n", + "print(\" cache_mgr.clean_unused_revisions(keep_latest=2)\")\n", + "print(\" cache_mgr.clean_cache_by_size('10GB', strategy='oldest_first')\")\n", + "\n", + "# Show current state\n", + "print(f\"\\nCurrent Session State:\")\n", + "print(f\"Repository: {cache_manager.repo_id}\")\n", + "print(f\"DuckDB tables: {len(conn.execute('SELECT table_name FROM information_schema.tables').fetchall())}\")\n", + "\n", + "cache_info = scan_cache_dir()\n", + "print(f\"HF cache size: {cache_info.size_on_disk_str}\")\n", + "print(f\"Cache repositories: {len(cache_info.repos)}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 9. Troubleshooting and Error Handling\n", + "\n", + "The HfCacheManager includes comprehensive error handling and diagnostic capabilities." + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Cache Management Troubleshooting:\n", + "===================================\n", + "\n", + "1. Import and Setup Issues:\n", + " • Ensure correct import: from tfbpapi.HfCacheManager import HfCacheManager\n", + " • Verify DuckDB connection: conn = duckdb.connect(':memory:')\n", + " • Check repository access permissions\n", + "\n", + "2. Cache Space and Performance Issues:\n", + " Current cache size: 4.6G\n", + " • Use auto_clean_cache() for automated management\n", + " • Monitor cache growth with scan_cache_dir()\n", + " • Set appropriate size limits for your system\n", + "\n", + "3. Cache Cleanup Issues:\n", + " • Use dry_run=True first to preview changes\n", + " • Check disk permissions for cache directory\n", + " • Verify no active processes are using cached files\n", + "\n", + "4. DuckDB Integration Issues:\n", + " • Ensure DuckDB connection is active\n", + " • Check memory limits for in-memory databases\n", + " • Verify table names don't conflict\n", + "\n", + "Cache Health Check:\n", + "✓ DuckDB connection: DuckDB OK\n", + "✓ Cache access: 9 repositories found\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:__main__:No old revisions found to delete\n", + "INFO:__main__:Found 0 old revisions. Will free 0.0\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "✓ Cache cleanup methods: Working\n", + "\n", + "Current Status:\n", + "Repository: BrentLab/hackett_2020\n", + "Logger configured: True\n", + "Cache management ready: ✓\n" + ] + } + ], + "source": [ + "print(\"Cache Management Troubleshooting:\")\n", + "print(\"=\" * 35)\n", + "\n", + "print(\"\\n1. Import and Setup Issues:\")\n", + "print(\" • Ensure correct import: from tfbpapi.HfCacheManager import HfCacheManager\")\n", + "print(\" • Verify DuckDB connection: conn = duckdb.connect(':memory:')\")\n", + "print(\" • Check repository access permissions\")\n", + "\n", + "print(\"\\n2. Cache Space and Performance Issues:\")\n", + "try:\n", + " cache_info = scan_cache_dir()\n", + " print(f\" Current cache size: {cache_info.size_on_disk_str}\")\n", + " print(\" • Use auto_clean_cache() for automated management\")\n", + " print(\" • Monitor cache growth with scan_cache_dir()\")\n", + " print(\" • Set appropriate size limits for your system\")\n", + " \n", + " # Show if cache is getting large\n", + " total_gb = cache_info.size_on_disk / (1024**3)\n", + " if total_gb > 10:\n", + " print(f\" ⚠️ Large cache detected ({total_gb:.1f}GB) - consider cleanup\")\n", + " \n", + "except Exception as e:\n", + " print(f\" Cache scan error: {e}\")\n", + "\n", + "print(\"\\n3. Cache Cleanup Issues:\")\n", + "print(\" • Use dry_run=True first to preview changes\")\n", + "print(\" • Check disk permissions for cache directory\")\n", + "print(\" • Verify no active processes are using cached files\")\n", + "\n", + "print(\"\\n4. DuckDB Integration Issues:\")\n", + "print(\" • Ensure DuckDB connection is active\")\n", + "print(\" • Check memory limits for in-memory databases\")\n", + "print(\" • Verify table names don't conflict\")\n", + "\n", + "# Perform health checks\n", + "print(f\"\\nCache Health Check:\")\n", + "\n", + "# Test DuckDB\n", + "try:\n", + " test_result = conn.execute(\"SELECT 'DuckDB OK' as status\").fetchone()\n", + " print(f\"✓ DuckDB connection: {test_result[0]}\")\n", + "except Exception as e:\n", + " print(f\"✗ DuckDB connection: {e}\")\n", + "\n", + "# Test cache access\n", + "try:\n", + " cache_info = scan_cache_dir()\n", + " print(f\"✓ Cache access: {len(cache_info.repos)} repositories found\")\n", + "except Exception as e:\n", + " print(f\"✗ Cache access: {e}\")\n", + "\n", + "# Test cache manager methods\n", + "try:\n", + " test_cleanup = cache_manager.clean_cache_by_age(max_age_days=999, dry_run=True)\n", + " print(f\"✓ Cache cleanup methods: Working\")\n", + "except Exception as e:\n", + " print(f\"✗ Cache cleanup methods: {e}\")\n", + "\n", + "print(f\"\\nCurrent Status:\")\n", + "print(f\"Repository: {cache_manager.repo_id}\")\n", + "print(f\"Logger configured: {cache_manager.logger is not None}\")\n", + "print(f\"Cache management ready: ✓\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 10. Summary and Best Practices\n", + "\n", + "The HfCacheManager provides a sophisticated and efficient way to manage metadata and cache for HuggingFace genomics datasets." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Key HfCacheManager Features:\n", + "\n", + "1. **3-Case Metadata Strategy**: Intelligent caching that checks DuckDB → HF cache → download\n", + "2. **Automated Cache Cleanup**: Multiple strategies (age, size, revision-based) for cache management\n", + "3. **Cache Monitoring**: Real-time cache analysis and diagnostics\n", + "4. **DuckDB Integration**: Efficient metadata storage and querying without data duplication\n", + "5. **Embedded Metadata Extraction**: Extract metadata fields from data tables into queryable views\n", + "\n", + "### Cache Management Best Practices:\n", + "\n", + "1. **Monitor Cache Size**: Regular `scan_cache_dir()` checks prevent disk space issues\n", + "2. **Proactive Cleanup**: Use `auto_clean_cache()` before large data operations\n", + "3. **Strategic Retention**: Keep recent revisions, clean old ones with `clean_unused_revisions()`\n", + "4. **Size Management**: Set cache size limits appropriate for your system resources\n", + "5. **Integration with DataCard**: Use DataCard for exploration, HfCacheManager for cache management\n", + "\n", + "### Recommended Cache Management Workflow:\n", + "\n", + "```python\n", + "# 1. Initialize cache manager\n", + "from tfbpapi.HfCacheManager import HfCacheManager\n", + "import duckdb\n", + "\n", + "conn = duckdb.connect(':memory:')\n", + "cache_mgr = HfCacheManager(repo_id, conn)\n", + "\n", + "# 2. Check current cache status\n", + "from huggingface_hub import scan_cache_dir\n", + "cache_info = scan_cache_dir()\n", + "print(f\"Current cache: {cache_info.size_on_disk_str}\")\n", + "\n", + "# 3. Automated cleanup before heavy operations\n", + "cache_mgr.auto_clean_cache(\n", + " max_total_size=\"10GB\",\n", + " max_age_days=30,\n", + " keep_latest_per_repo=2,\n", + " dry_run=False\n", + ")\n", + "\n", + "# 4. Load data (uses 3-case strategy automatically)\n", + "from tfbpapi import HfQueryAPI\n", + "query_api = HfQueryAPI(repo_id, conn)\n", + "data = query_api.get_pandas('config_name')\n", + "\n", + "# 5. Extract embedded metadata if needed\n", + "cache_mgr._extract_embedded_metadata_field(\n", + " 'data_table', 'metadata_field', 'metadata_table'\n", + ")\n", + "\n", + "# 6. Regular maintenance\n", + "cache_mgr.clean_cache_by_age(max_age_days=30, dry_run=False)\n", + "```\n", + "\n", + "This approach ensures optimal cache performance while maintaining efficient access to genomics datasets." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "tfbpapi-py3.11", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/tutorials/database_interface.ipynb b/docs/tutorials/database_interface.ipynb deleted file mode 100644 index 79e41f7..0000000 --- a/docs/tutorials/database_interface.ipynb +++ /dev/null @@ -1,2266 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# The Database Interface Classes\n", - "\n", - "For each API endpoint exposed in the Django app, there is a corresponding class that\n", - "provide methods to execute CRUD operations asynchronously.\n", - "\n", - "There are two types of API endpoints -- those that contain only records data, and \n", - "those that store both records and pointers to files.\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Connecting to the Database\n", - "\n", - "The database currently runs on HTCF service partition. This is a single\n", - "node with 8 CPU and 30 GB that is meant for long running low resource jobs.\n", - "The components that need to run are a postgres database, a redis instance and the\n", - "django app. As long as these components are running on the service partition, \n", - "you can connect via an ssh tunnel with:\n", - "\n", - "```bash\n", - "ssh username@login.htcf.wustl.edu -N -L 8001:n240:8000\n", - "```\n", - "\n", - "where the `8001:n240:8000` takes the form of `local_port:cluster_node:app_port`. The\n", - "django app will always be served on port `8000`, and `n240` is the only service\n", - "partition node. You may choose a different local port.\n", - "\n", - "If you do this and cannot connect, let me know and I'll check the status of the jobs \n", - "on the cluster.\n", - "\n", - "### Database username and password\n", - "\n", - "Once you have a tunnel, you can access the database frontend at `127.0.0.1:8001`\n", - "(or a different local port, if you changed that number). If you haven't already\n", - "signed up, you'll need to click the 'sign up' button and follow\n", - "the instructions. The e-mail server is not hooked up at the moment, so when it says\n", - "\"see the e-mail\", send a slack message and let me know. I'll give you a link to\n", - "complete the sign up process. After that, you can just use the \"sign in\" button.\n", - "\n", - "For computational tasks, including generating rank response data, celery workers must\n", - "be launched on the HTCF general partition. There is currently a script that is meant to\n", - "monitor the redis queue and launch/kill these workers automatically, but this\n", - "functionality is new and largely untested. You can monitor the workers/tasks if you\n", - "create another tunnel with:\n", - "\n", - "```bash\n", - "ssh username@login.htcf.wustl.edu -N -L 8002:n240:5555\n", - "```\n", - "You'd access this dashboard at `127.0.0.1:5555`\n", - "\n", - "The login is currently:\n", - "\n", - "```raw\n", - "username: \"flower\"\n", - "password: \"daisy\"\n", - "```\n", - "(yes, really -- the security comes from the fact that you need to login with HTCF)\n", - "\n", - "### Configuring the Database Interface Classes\n", - "\n", - "The database classes expect the following environmental variables to be set. \n", - "\n", - "```raw\n", - "BASE_URL='http://127.0.0.1:8001'\n", - "TOKEN=''\n", - "BINDING_URL='http://127.0.0.1:8001/api/binding'\n", - "BINDINGMANUALQC_URL='http://127.0.0.1:8001/api/bindingmanualqc'\n", - "CALLINGCARDSBACKGROUND_URL='http://127.0.0.1:8001/api/callingcardsbackground'\n", - "DATASOURCE_URL='http://127.0.0.1:8001/api/datasource'\n", - "EXPRESSION_URL='http://127.0.0.1:8001/api/expression'\n", - "EXPRESSIONMANUALQC_URL='http://127.0.0.1:8001/api/expressionmanualqc'\n", - "FILEFORMAT_URL='http://127.0.0.1:8001/api/fileformat'\n", - "GENOMICFEATURE_URL='http://127.0.0.1:8001/api/genomicfeature'\n", - "PROMOTERSET_URL='http://127.0.0.1:8001/api/promoterset'\n", - "PROMOTERSETSIG_URL='http://127.0.0.1:8001/api/promotersetsig'\n", - "REGULATOR_URL='http://127.0.0.1:8001/api/regulator'\n", - "```\n", - "\n", - "This can be achieved in the package during development with a `.env` file at the\n", - "top most level of the package. The `.env` file is loaded in the package `__init__.py`.\n", - "\n", - "If you are importing `yeastdnnexplorer` into a different environment, then you'll \n", - "need to add the package `dotenv` and execute `load_dotenv(dotenv_path=env_path)`. If\n", - "the `.env` file is in the same `PWD` in which you execute that command, there is no\n", - "need to specify a path.\n", - "\n", - "### Token Authentication\n", - "\n", - "Once you have a username and password to the database, you can retrieve your token. \n", - "Make sure that you put this token, at least, in a `.env` file, and make sure that \n", - "`.env` file is in your `.gitignore`.\n", - "\n", - "Alternatively, you could retrieve and store in memory the token at the beginning of \n", - "each session -- this is more secure if you are not using a `.env` file. \n", - "\n", - "The `.env` file is already in the `yeastddnexplorer` `.gitignore`\n", - "\n", - "```bash\n", - "curl -X 'POST' \\\n", - " 'http://127.0.0.1:8001/auth-token/' \\\n", - " -H 'accept: application/json' \\\n", - " -H 'Content-Type: application/json' \\\n", - " -d '{\n", - " \"username\": \"username\",\n", - " \"password\": \"password\"\n", - "}'\n", - "```\n", - "\n", - "Or with python:\n", - "\n", - "```python\n", - "import requests\n", - "\n", - "url = \"http://127.0.0.1:8001/auth-token/\"\n", - "headers = {\n", - " \"accept\": \"application/json\",\n", - " \"Content-Type\": \"application/json\",\n", - "}\n", - "data = {\n", - " \"username\": \"username\",\n", - " \"password\": \"password\",\n", - "}\n", - "\n", - "response = requests.post(url, json=data, headers=headers)\n", - "print(response.text)\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Using the Interface Classes" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "from yeastdnnexplorer.interface import *\n", - "import matplotlib.pyplot as plt" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Records Only Endpoints\n", - "\n", - "The records only endpoints are:\n", - "\n", - "- BindingManualQC\n", - "\n", - "- DataSource\n", - "\n", - "- ExpressionManualQC\n", - "\n", - "- FileFormat\n", - "\n", - "- GenomicFeature\n", - "\n", - "- PromoterSetSig\n", - "\n", - "- Regulator\n", - "\n", - "When the `read()` method is called on the corresponding API classes, a dataframe will\n", - "be returned in the response.\n", - "\n", - "All of the `read()` methods, for both types of API endpoints, return the result of\n", - "a callable. By default, the callable returns a dictionary with two keys: `metadata` and\n", - "`data`. For response only tables, the `metadata` value will be the records from the\n", - "database as a pandas dataframe and the `data` will be `None.\n", - "\n", - "### Example -- RegulatorAPI" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "

\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
iduploadermodifierregulator_locus_tagregulator_symbolupload_datemodified_dateunder_developmentnotesgenomicfeature
01chasemchasemYAL051WOAF12024-07-012024-07-01T12:47:18.619129-05:00Falsenone24
12chasemchasemYBL103CRTG32024-07-012024-07-01T12:47:19.667722-05:00Falsenone140
23chasemchasemYBL066CSEF12024-07-012024-07-01T12:47:20.523161-05:00Falsenone186
34chasemchasemYBL054WTOD62024-07-012024-07-01T12:47:21.309606-05:00Falsenone199
45chasemchasemYBL052CSAS32024-07-012024-07-01T12:47:22.161007-05:00Falsenone201
.................................
18091810chasemchasemYOR262WGPN22024-07-012024-07-01T14:14:36.164403-05:00Falsenone6387
18101811chasemchasemYPR190CRPC822024-07-012024-07-01T14:14:38.921261-05:00Falsenone7070
18111812chasemchasemYPL228WCET12024-07-012024-07-01T14:15:51.518999-05:00Falsenone6603
18121813chasemchasemYKL049CCSE42024-07-012024-07-01T14:15:56.555122-05:00Falsenone4083
18131814chasemchasemYMR168CCEP32024-07-012024-07-01T14:22:14.060524-05:00Falsenone5258
\n", - "

1814 rows × 10 columns

\n", - "
" - ], - "text/plain": [ - " id uploader modifier regulator_locus_tag regulator_symbol upload_date \\\n", - "0 1 chasem chasem YAL051W OAF1 2024-07-01 \n", - "1 2 chasem chasem YBL103C RTG3 2024-07-01 \n", - "2 3 chasem chasem YBL066C SEF1 2024-07-01 \n", - "3 4 chasem chasem YBL054W TOD6 2024-07-01 \n", - "4 5 chasem chasem YBL052C SAS3 2024-07-01 \n", - "... ... ... ... ... ... ... \n", - "1809 1810 chasem chasem YOR262W GPN2 2024-07-01 \n", - "1810 1811 chasem chasem YPR190C RPC82 2024-07-01 \n", - "1811 1812 chasem chasem YPL228W CET1 2024-07-01 \n", - "1812 1813 chasem chasem YKL049C CSE4 2024-07-01 \n", - "1813 1814 chasem chasem YMR168C CEP3 2024-07-01 \n", - "\n", - " modified_date under_development notes \\\n", - "0 2024-07-01T12:47:18.619129-05:00 False none \n", - "1 2024-07-01T12:47:19.667722-05:00 False none \n", - "2 2024-07-01T12:47:20.523161-05:00 False none \n", - "3 2024-07-01T12:47:21.309606-05:00 False none \n", - "4 2024-07-01T12:47:22.161007-05:00 False none \n", - "... ... ... ... \n", - "1809 2024-07-01T14:14:36.164403-05:00 False none \n", - "1810 2024-07-01T14:14:38.921261-05:00 False none \n", - "1811 2024-07-01T14:15:51.518999-05:00 False none \n", - "1812 2024-07-01T14:15:56.555122-05:00 False none \n", - "1813 2024-07-01T14:22:14.060524-05:00 False none \n", - "\n", - " genomicfeature \n", - "0 24 \n", - "1 140 \n", - "2 186 \n", - "3 199 \n", - "4 201 \n", - "... ... \n", - "1809 6387 \n", - "1810 7070 \n", - "1811 6603 \n", - "1812 4083 \n", - "1813 5258 \n", - "\n", - "[1814 rows x 10 columns]" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "regulator = RegulatorAPI()\n", - "\n", - "result = await regulator.read()\n", - "result.get(\"metadata\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Record and File Endpoints\n", - "\n", - "The record and file endpoints are the following:\n", - "\n", - "- CallingCardsBackground\n", - "\n", - "- Expression\n", - "\n", - "- PromoterSet\n", - "\n", - "- PromoterSetSig\n", - "\n", - "- RankResponse *\n", - "\n", - "The default `read()` method is the same as the Records only Endpoint API classes.\n", - "However, there is an additional argument, `retrieve_files` which if set to `True`\n", - "will retrieve the file for which each record provides metadata. The return value of\n", - "`read()` is again a callable, and by default the `data` key will store a dictionary\n", - "where the keys correspond to the `id` column in the `metadata`.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
iduploadermodifiersingle_bindingcomposite_bindingfileformatbackgroundupload_datemodified_datefilepromotersourceregulator_symbolregulator_locus_tagconditionrank_recalldata_usablebackground_name
01chasemchasem1.0NaN1NaN2024-07-012024-07-01T13:51:48.611781-05:00https://yeastregulatorydb-htcf-data.s3.amazona...NaNharbison_chipOAF1YAL051WYPDunreviewedunreviewedNaN
12chasemchasem2.0NaN1NaN2024-07-012024-07-01T13:51:49.643452-05:00https://yeastregulatorydb-htcf-data.s3.amazona...NaNharbison_chipPDR3YBL005WYPDunreviewedunreviewedNaN
23chasemchasem3.0NaN1NaN2024-07-012024-07-01T13:51:50.744384-05:00https://yeastregulatorydb-htcf-data.s3.amazona...NaNharbison_chipHIR1YBL008WYPDunreviewedunreviewedNaN
34chasemchasem4.0NaN1NaN2024-07-012024-07-01T13:51:51.507918-05:00https://yeastregulatorydb-htcf-data.s3.amazona...NaNharbison_chipHAP3YBL021CYPDunreviewedunreviewedNaN
45chasemchasem5.0NaN1NaN2024-07-012024-07-01T13:51:52.277595-05:00https://yeastregulatorydb-htcf-data.s3.amazona...NaNharbison_chipTOD6YBL054WYPDunreviewedunreviewedNaN
.........................................................
22377011adminadminNaN145.051.02024-07-302024-07-30T16:39:36.457965-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4.0mitra_ccCBF1YJR060WNaNunreviewedpassad1
22387012adminadminNaN146.051.02024-07-302024-07-30T16:39:36.848168-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4.0mitra_ccGCN4YEL009CNaNunreviewedpassad1
22397013adminadminNaN147.051.02024-07-302024-07-30T16:39:37.234144-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4.0mitra_ccOAF1YAL051WNaNunreviewedpassad1
22407014adminadminNaN148.051.02024-07-302024-07-30T16:39:38.547155-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4.0mitra_ccYOX1YML027WNaNunreviewedpassad1
22417015adminadminNaN149.051.02024-07-302024-07-30T16:39:39.713590-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4.0mitra_ccLEU3YLR451WNaNunreviewedpassad1
\n", - "

2242 rows × 18 columns

\n", - "
" - ], - "text/plain": [ - " id uploader modifier single_binding composite_binding fileformat \\\n", - "0 1 chasem chasem 1.0 NaN 1 \n", - "1 2 chasem chasem 2.0 NaN 1 \n", - "2 3 chasem chasem 3.0 NaN 1 \n", - "3 4 chasem chasem 4.0 NaN 1 \n", - "4 5 chasem chasem 5.0 NaN 1 \n", - "... ... ... ... ... ... ... \n", - "2237 7011 admin admin NaN 145.0 5 \n", - "2238 7012 admin admin NaN 146.0 5 \n", - "2239 7013 admin admin NaN 147.0 5 \n", - "2240 7014 admin admin NaN 148.0 5 \n", - "2241 7015 admin admin NaN 149.0 5 \n", - "\n", - " background upload_date modified_date \\\n", - "0 NaN 2024-07-01 2024-07-01T13:51:48.611781-05:00 \n", - "1 NaN 2024-07-01 2024-07-01T13:51:49.643452-05:00 \n", - "2 NaN 2024-07-01 2024-07-01T13:51:50.744384-05:00 \n", - "3 NaN 2024-07-01 2024-07-01T13:51:51.507918-05:00 \n", - "4 NaN 2024-07-01 2024-07-01T13:51:52.277595-05:00 \n", - "... ... ... ... \n", - "2237 1.0 2024-07-30 2024-07-30T16:39:36.457965-05:00 \n", - "2238 1.0 2024-07-30 2024-07-30T16:39:36.848168-05:00 \n", - "2239 1.0 2024-07-30 2024-07-30T16:39:37.234144-05:00 \n", - "2240 1.0 2024-07-30 2024-07-30T16:39:38.547155-05:00 \n", - "2241 1.0 2024-07-30 2024-07-30T16:39:39.713590-05:00 \n", - "\n", - " file promoter \\\n", - "0 https://yeastregulatorydb-htcf-data.s3.amazona... NaN \n", - "1 https://yeastregulatorydb-htcf-data.s3.amazona... NaN \n", - "2 https://yeastregulatorydb-htcf-data.s3.amazona... NaN \n", - "3 https://yeastregulatorydb-htcf-data.s3.amazona... NaN \n", - "4 https://yeastregulatorydb-htcf-data.s3.amazona... NaN \n", - "... ... ... \n", - "2237 https://yeastregulatorydb-htcf-data.s3.amazona... 4.0 \n", - "2238 https://yeastregulatorydb-htcf-data.s3.amazona... 4.0 \n", - "2239 https://yeastregulatorydb-htcf-data.s3.amazona... 4.0 \n", - "2240 https://yeastregulatorydb-htcf-data.s3.amazona... 4.0 \n", - "2241 https://yeastregulatorydb-htcf-data.s3.amazona... 4.0 \n", - "\n", - " source regulator_symbol regulator_locus_tag condition \\\n", - "0 harbison_chip OAF1 YAL051W YPD \n", - "1 harbison_chip PDR3 YBL005W YPD \n", - "2 harbison_chip HIR1 YBL008W YPD \n", - "3 harbison_chip HAP3 YBL021C YPD \n", - "4 harbison_chip TOD6 YBL054W YPD \n", - "... ... ... ... ... \n", - "2237 mitra_cc CBF1 YJR060W NaN \n", - "2238 mitra_cc GCN4 YEL009C NaN \n", - "2239 mitra_cc OAF1 YAL051W NaN \n", - "2240 mitra_cc YOX1 YML027W NaN \n", - "2241 mitra_cc LEU3 YLR451W NaN \n", - "\n", - " rank_recall data_usable background_name \n", - "0 unreviewed unreviewed NaN \n", - "1 unreviewed unreviewed NaN \n", - "2 unreviewed unreviewed NaN \n", - "3 unreviewed unreviewed NaN \n", - "4 unreviewed unreviewed NaN \n", - "... ... ... ... \n", - "2237 unreviewed pass ad1 \n", - "2238 unreviewed pass ad1 \n", - "2239 unreviewed pass ad1 \n", - "2240 unreviewed pass ad1 \n", - "2241 unreviewed pass ad1 \n", - "\n", - "[2242 rows x 18 columns]" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# First, retrieve only the records -- you'll want to filter these results down before\n", - "# retrieving the files most likely\n", - "pss_api = PromoterSetSigAPI()\n", - "result = await pss_api.read()\n", - "result.get(\"metadata\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Filtering\n", - "\n", - "All API classes have a `params` attribute which stores the filtering parameters\n", - "which will be applied to the HTTP requests." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "pss_api.push_params({\"regulator_symbol\": \"GZF3\",\n", - " \"workflow\": \"nf_core_callingcards_1_0_0\",\n", - " \"data_usable\": \"pass\"})" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "ParamsDict({'regulator_symbol': 'GZF3', 'workflow': 'nf_core_callingcards_1_0_0', 'data_usable': 'pass'})" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "pss_api.params" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Retrieving files from a Records and Files Object\n", - "\n", - "To retrieve files from a Records and Files endpoint object, do the following:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
iduploadermodifiersingle_bindingcomposite_bindingfileformatbackgroundupload_datemodified_datefilepromotersourceregulator_symbolregulator_locus_tagconditionrank_recalldata_usablebackground_name
06577adminadmin1837NaN512024-07-012024-07-01T16:43:50.145871-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccGZF3YJL110Cunknownpasspassad1
16580adminadmin1841NaN512024-07-012024-07-01T16:43:50.968078-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccGZF3YJL110Cunknownpasspassad1
26642adminadmin1902NaN512024-07-012024-07-01T16:43:54.969507-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccGZF3YJL110Cunknownpasspassad1
36651adminadmin1911NaN512024-07-012024-07-01T16:43:55.326651-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccGZF3YJL110Cunknownpasspassad1
46717adminadmin1960NaN512024-07-012024-07-01T16:44:00.500038-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccGZF3YJL110Cunknownpasspassad1
\n", - "
" - ], - "text/plain": [ - " id uploader modifier single_binding composite_binding fileformat \\\n", - "0 6577 admin admin 1837 NaN 5 \n", - "1 6580 admin admin 1841 NaN 5 \n", - "2 6642 admin admin 1902 NaN 5 \n", - "3 6651 admin admin 1911 NaN 5 \n", - "4 6717 admin admin 1960 NaN 5 \n", - "\n", - " background upload_date modified_date \\\n", - "0 1 2024-07-01 2024-07-01T16:43:50.145871-05:00 \n", - "1 1 2024-07-01 2024-07-01T16:43:50.968078-05:00 \n", - "2 1 2024-07-01 2024-07-01T16:43:54.969507-05:00 \n", - "3 1 2024-07-01 2024-07-01T16:43:55.326651-05:00 \n", - "4 1 2024-07-01 2024-07-01T16:44:00.500038-05:00 \n", - "\n", - " file promoter source \\\n", - "0 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - "1 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - "2 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - "3 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - "4 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - "\n", - " regulator_symbol regulator_locus_tag condition rank_recall data_usable \\\n", - "0 GZF3 YJL110C unknown pass pass \n", - "1 GZF3 YJL110C unknown pass pass \n", - "2 GZF3 YJL110C unknown pass pass \n", - "3 GZF3 YJL110C unknown pass pass \n", - "4 GZF3 YJL110C unknown pass pass \n", - "\n", - " background_name \n", - "0 ad1 \n", - "1 ad1 \n", - "2 ad1 \n", - "3 ad1 \n", - "4 ad1 " - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# note that retrieve_files is set to True\n", - "result = await pss_api.read(retrieve_files = True)\n", - "\n", - "# the metadata slot is the same as before\n", - "result.get(\"metadata\")" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "# but now the data slot is a dictionary where the `id` are keys and the values\n", - "# are the files parsed into pandas dataframes\n", - "result.get(\"data\").get(\"6568\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Filtering on multiple items\n", - "\n", - "Some filters, and eventually all, will accept multiple arguments as a comma\n", - "separated string without spaces. For example:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "regulator_symbol: GZF3,RTG3, workflow: nf_core_callingcards_1_0_0, data_usable: pass\n" - ] - } - ], - "source": [ - "pss_api.push_params({\"regulator_symbol\": \"GZF3,RTG3\"})\n", - "\n", - "print(pss_api.params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Parameters can be removed one by one" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "regulator_symbol: GZF3,RTG3, workflow: nf_core_callingcards_1_0_0, data_usable: pass\n" - ] - } - ], - "source": [ - "print(pss_api.params)" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "regulator_symbol: GZF3,RTG3, workflow: nf_core_callingcards_1_0_0\n" - ] - } - ], - "source": [ - "pss_api.pop_params('data_usable')\n", - "\n", - "print(pss_api.params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "or cleared entirely" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], - "source": [ - "pss_api.pop_params(None)\n", - "\n", - "print(pss_api.params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Another example with Expression\n", - "\n", - "This is used to get an expression_id to use in the RankResponseAPI() below" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
iduploadermodifierregulator_idregulator_locus_tagregulator_symbolsource_nameassayupload_datemodified_date...replicatecontrolmechanismrestrictiontimefilenotesregulatorsourcepromotersetsig_processing
02516chasemchasem3578YJL110CGZF3mcisaac_oeoverexpression2024-07-012024-07-01T13:36:13.814201-05:00...1undefinedgevN15.0https://yeastregulatorydb-htcf-data.s3.amazona...strain_id:SMY156n ; date:201501011357False
12510chasemchasem3578YJL110CGZF3mcisaac_oeoverexpression2024-07-012024-07-01T13:36:08.810276-05:00...1undefinedgevP15.0https://yeastregulatorydb-htcf-data.s3.amazona...strain_id:SMY156 ; date:201501011357False
\n", - "

2 rows × 22 columns

\n", - "
" - ], - "text/plain": [ - " id uploader modifier regulator_id regulator_locus_tag regulator_symbol \\\n", - "0 2516 chasem chasem 3578 YJL110C GZF3 \n", - "1 2510 chasem chasem 3578 YJL110C GZF3 \n", - "\n", - " source_name assay upload_date modified_date \\\n", - "0 mcisaac_oe overexpression 2024-07-01 2024-07-01T13:36:13.814201-05:00 \n", - "1 mcisaac_oe overexpression 2024-07-01 2024-07-01T13:36:08.810276-05:00 \n", - "\n", - " ... replicate control mechanism restriction time \\\n", - "0 ... 1 undefined gev N 15.0 \n", - "1 ... 1 undefined gev P 15.0 \n", - "\n", - " file \\\n", - "0 https://yeastregulatorydb-htcf-data.s3.amazona... \n", - "1 https://yeastregulatorydb-htcf-data.s3.amazona... \n", - "\n", - " notes regulator source \\\n", - "0 strain_id:SMY156n ; date:20150101 135 7 \n", - "1 strain_id:SMY156 ; date:20150101 135 7 \n", - "\n", - " promotersetsig_processing \n", - "0 False \n", - "1 False \n", - "\n", - "[2 rows x 22 columns]" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "expression = ExpressionAPI()\n", - "\n", - "expression.push_params({\"regulator_symbol\": \"GZF3\",\n", - " \"lab\": \"mcisaac\",\n", - " \"time\": \"15\"})\n", - "\n", - "expression_res = await expression.read()\n", - "\n", - "expression_res.get(\"metadata\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Rank Response\n", - "\n", - "The rank response endpoint is slightly different than the others. It is implemented\n", - "asynchronously on the database side, and will run many tasks simultaneously. As such,\n", - "it uses `submit()` and `retrieve()` methods.\n", - "\n", - "Additionally, it is a POST request and all parameters are passed in a json list.\n", - "If you include, for a given dictionary, multiple items to promoetersetsig_ids and/or\n", - "expression_ids, those datasets will be aggregated prior to calculating the rank\n", - "response. The current parameters that may be passed for each are:\n", - "\n", - "- promotersetsig_ids: a list of promotersetsig_ids. If more than 1, then the data\n", - "will be aggregated prior to calcuating rank response\n", - "- expression_ids: a list of expression_ids. If more than 1, then the data\n", - "will be aggregated prior to calcuating rank response\n", - "- expression_effect_colname: name of the column to use for the rank response effect\n", - "- expression_effect_threshold: The threshold to use on abs(effect) to label responsive/\n", - "unresponsive genes\n", - "- expression_pvalue_threshold: the threshold to use below which to label responsive/\n", - "unresponsive genes\n", - "- rank_bin_size: the size of the bins by which rank response is summarized.\n", - "- rank_by_binding_effect: if this is \"true\", then rank by the binding effect first\n", - "rather than pvalue. This is used for harbison_chip and mcisaac_oe\n", - "- summarize_by_rank_bin: if this is set to false, the unsummarized rank response\n", - "(merged binding/response) is returned" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "rr_api = RankResponseAPI()\n", - "\n", - "data = [\n", - " {\n", - " \"promotersetsig_ids\": [\"5555\"],\n", - " \"expression_ids\": [\"2510\"],\n", - " \"rank_by_binding_effect\": \"true\",\n", - " }\n", - "]\n", - "\n", - "group_id = await rr_api.submit(post_dict=data)\n", - "\n", - "result = await rr_api.retrieve(group_id)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
regulator_symbolpromotersetsig_idsexpression_idsn_responsivetotal_expression_genesid
0GZF3555525108106175.0a46a15db-8318-476e-80aa-af5588909eef
\n", - "
" - ], - "text/plain": [ - " regulator_symbol promotersetsig_ids expression_ids n_responsive \\\n", - "0 GZF3 5555 2510 810 \n", - "\n", - " total_expression_genes id \n", - "0 6175.0 a46a15db-8318-476e-80aa-af5588909eef " - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "result.get(\"metadata\")" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
rank_binn_responsive_in_rankrandomn_successesresponse_ratiopvalueci_lowerci_upper
0520.13117420.4000000.1312110.0527450.853367
11030.13117450.5000000.0055100.1870860.812914
21540.13117490.6000000.0000270.3228700.836636
32000.13117490.4500000.0004900.2305780.684722
42520.131174110.4400000.0001490.2440240.650718
53010.131174120.4000000.0002240.2265580.593965
63510.131174130.3714290.0002960.2147320.550769
74000.131174130.3250000.0012780.1857290.491295
84510.131174140.3111110.0013630.1816590.466491
95000.131174140.2800000.0049460.1623110.424905
105510.131174150.2727270.0043780.1613800.409619
116000.131174150.2500000.0115260.1471860.378596
\n", - "
" - ], - "text/plain": [ - " rank_bin n_responsive_in_rank random n_successes response_ratio \\\n", - "0 5 2 0.131174 2 0.400000 \n", - "1 10 3 0.131174 5 0.500000 \n", - "2 15 4 0.131174 9 0.600000 \n", - "3 20 0 0.131174 9 0.450000 \n", - "4 25 2 0.131174 11 0.440000 \n", - "5 30 1 0.131174 12 0.400000 \n", - "6 35 1 0.131174 13 0.371429 \n", - "7 40 0 0.131174 13 0.325000 \n", - "8 45 1 0.131174 14 0.311111 \n", - "9 50 0 0.131174 14 0.280000 \n", - "10 55 1 0.131174 15 0.272727 \n", - "11 60 0 0.131174 15 0.250000 \n", - "\n", - " pvalue ci_lower ci_upper \n", - "0 0.131211 0.052745 0.853367 \n", - "1 0.005510 0.187086 0.812914 \n", - "2 0.000027 0.322870 0.836636 \n", - "3 0.000490 0.230578 0.684722 \n", - "4 0.000149 0.244024 0.650718 \n", - "5 0.000224 0.226558 0.593965 \n", - "6 0.000296 0.214732 0.550769 \n", - "7 0.001278 0.185729 0.491295 \n", - "8 0.001363 0.181659 0.466491 \n", - "9 0.004946 0.162311 0.424905 \n", - "10 0.004378 0.161380 0.409619 \n", - "11 0.011526 0.147186 0.378596 " - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "result.get(\"data\").get(result.get(\"metadata\").id[0])" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "df = result.get(\"data\").get(result.get(\"metadata\").id[0]).iloc[1:50,:]\n", - "\n", - "# Plot\n", - "plt.figure(figsize=(10, 6))\n", - "plt.plot(df['rank_bin'], df['response_ratio'], marker='o')\n", - "plt.title('GZF3 - promotersetsig_id 6577. McIsaac 15 2510')\n", - "plt.xlabel('Rank Bin')\n", - "plt.ylabel('Response Ratio')\n", - "plt.title('Response Ratio vs Rank Bin')\n", - "plt.grid(True)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# CallingCards Aggregated Data\n", - "\n", - "For regulators which have data generated in the Brentlab and processed through the\n", - "nf-core/callingcards:1.0.0 workflow, if that data has been manually (or eventually \n", - "automatically) QC reviewed,\n", - "*and if there are at least 2 samples which are labeled as data_usable*, then there will\n", - "exist a BindingConcatenated record to which both a BindingManualQC record *and* a\n", - "PromoterSetSig record are foreign keyed.\n", - "\n", - "To view the set of samples for which there is aggregated data, you may do the following:" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "pss_api.pop_params(None)\n", - "\n", - "pss_api.push_params({\"source_name\": \"brent_nf_cc\", \"aggregated\": \"true\"})\n", - "\n", - "callingcards_aggregated_meta_res = await pss_api.read()\n", - "\n", - "callingcards_aggregated_meta_df = callingcards_aggregated_meta_res.get(\"metadata\")" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
iduploadermodifiersingle_bindingcomposite_bindingfileformatbackgroundupload_datemodified_datefilepromotersourceregulator_symbolregulator_locus_tagrank_recalldata_usablebackground_name
06867adminadminNaN6512024-07-062024-07-06T11:08:33.125644-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccWTM1YOR230Wpasspassad1
16868adminadminNaN3512024-07-062024-07-06T11:08:33.125640-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccMIG2YGL209Wpasspassad1
26869adminadminNaN1512024-07-062024-07-06T11:08:33.119818-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccCAT8YMR280Cpasspassad1
36870adminadminNaN5512024-07-062024-07-06T11:08:33.125605-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccPDR1YGL013Cpasspassad1
46871adminadminNaN4512024-07-062024-07-06T11:08:33.129564-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccPHO4YFR034Cpasspassad1
......................................................
666933adminadminNaN67512024-07-062024-07-06T11:08:45.567814-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccABF2YMR072Wfailpassad1
676934adminadminNaN68512024-07-062024-07-06T11:08:45.921894-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccUSV1YPL230Wpasspassad1
686935adminadminNaN69512024-07-062024-07-06T11:08:46.166036-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccMGA1YGR249Wpasspassad1
696936adminadminNaN70512024-07-062024-07-06T11:08:46.246482-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccCIN5YOR028Cpasspassad1
706937adminadminNaN71512024-07-062024-07-06T11:08:47.987665-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccROX1YPR065Wpasspassad1
\n", - "

71 rows × 17 columns

\n", - "
" - ], - "text/plain": [ - " id uploader modifier single_binding composite_binding fileformat \\\n", - "0 6867 admin admin NaN 6 5 \n", - "1 6868 admin admin NaN 3 5 \n", - "2 6869 admin admin NaN 1 5 \n", - "3 6870 admin admin NaN 5 5 \n", - "4 6871 admin admin NaN 4 5 \n", - ".. ... ... ... ... ... ... \n", - "66 6933 admin admin NaN 67 5 \n", - "67 6934 admin admin NaN 68 5 \n", - "68 6935 admin admin NaN 69 5 \n", - "69 6936 admin admin NaN 70 5 \n", - "70 6937 admin admin NaN 71 5 \n", - "\n", - " background upload_date modified_date \\\n", - "0 1 2024-07-06 2024-07-06T11:08:33.125644-05:00 \n", - "1 1 2024-07-06 2024-07-06T11:08:33.125640-05:00 \n", - "2 1 2024-07-06 2024-07-06T11:08:33.119818-05:00 \n", - "3 1 2024-07-06 2024-07-06T11:08:33.125605-05:00 \n", - "4 1 2024-07-06 2024-07-06T11:08:33.129564-05:00 \n", - ".. ... ... ... \n", - "66 1 2024-07-06 2024-07-06T11:08:45.567814-05:00 \n", - "67 1 2024-07-06 2024-07-06T11:08:45.921894-05:00 \n", - "68 1 2024-07-06 2024-07-06T11:08:46.166036-05:00 \n", - "69 1 2024-07-06 2024-07-06T11:08:46.246482-05:00 \n", - "70 1 2024-07-06 2024-07-06T11:08:47.987665-05:00 \n", - "\n", - " file promoter source \\\n", - "0 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - "1 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - "2 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - "3 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - "4 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - ".. ... ... ... \n", - "66 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - "67 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - "68 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - "69 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - "70 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - "\n", - " regulator_symbol regulator_locus_tag rank_recall data_usable \\\n", - "0 WTM1 YOR230W pass pass \n", - "1 MIG2 YGL209W pass pass \n", - "2 CAT8 YMR280C pass pass \n", - "3 PDR1 YGL013C pass pass \n", - "4 PHO4 YFR034C pass pass \n", - ".. ... ... ... ... \n", - "66 ABF2 YMR072W fail pass \n", - "67 USV1 YPL230W pass pass \n", - "68 MGA1 YGR249W pass pass \n", - "69 CIN5 YOR028C pass pass \n", - "70 ROX1 YPR065W pass pass \n", - "\n", - " background_name \n", - "0 ad1 \n", - "1 ad1 \n", - "2 ad1 \n", - "3 ad1 \n", - "4 ad1 \n", - ".. ... \n", - "66 ad1 \n", - "67 ad1 \n", - "68 ad1 \n", - "69 ad1 \n", - "70 ad1 \n", - "\n", - "[71 rows x 17 columns]" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "callingcards_aggregated_meta_df" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
iduploadermodifiersingle_bindingcomposite_bindingfileformatbackgroundupload_datemodified_datefilepromotersourceregulator_symbolregulator_locus_tagrank_recalldata_usablebackground_name
06873adminadminNaN7512024-07-062024-07-06T11:08:33.117009-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccGZF3YJL110Cpasspassad1
\n", - "
" - ], - "text/plain": [ - " id uploader modifier single_binding composite_binding fileformat \\\n", - "0 6873 admin admin NaN 7 5 \n", - "\n", - " background upload_date modified_date \\\n", - "0 1 2024-07-06 2024-07-06T11:08:33.117009-05:00 \n", - "\n", - " file promoter source \\\n", - "0 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - "\n", - " regulator_symbol regulator_locus_tag rank_recall data_usable background_name \n", - "0 GZF3 YJL110C pass pass ad1 " - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "pss_api.push_params({\"regulator_symbol\": \"GZF3\"})\n", - "\n", - "callingcards_res = await pss_api.read()\n", - "\n", - "gzf3_callingcards_res = callingcards_res.get(\"metadata\")\n", - "\n", - "gzf3_callingcards_res\n", - "\n", - "#gzf3_callingcards_res[~gzf3_callingcards_res.composite_binding_id.isna()]" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "data = [\n", - " {\n", - " \"promotersetsig_ids\": [\"6873\"],\n", - " \"expression_ids\": [\"2510\"],\n", - " }\n", - "]\n", - "\n", - "group_id = await rr_api.submit(post_dict=data)\n", - "\n", - "agg_result = await rr_api.retrieve(group_id)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA04AAAIjCAYAAAA0vUuxAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAB/eUlEQVR4nO3deVxU9f7H8ffMsIzIJiKCS4pbhrikhlGZlmuZZd3Ka1lm2820utpqG9mv275velutvJXV9Wq2mHtloqhoibuIO4iCAi5sM+f3hzGJLDPgwAzwej4ePHLO+Z5zPjN8Q95+v+d7TIZhGAIAAAAAVMjs6QIAAAAAwNsRnAAAAADACYITAAAAADhBcAIAAAAAJwhOAAAAAOAEwQkAAAAAnCA4AQAAAIATBCcAAAAAcILgBAAAAABOEJwAAA3S0qVLZTKZtHTpUk+XUifccsstCgwMrPHr7Ny5UyaTSdOnT6/xawFAVRCcAKAapk+fLpPJ5Pjy8fFRy5Ytdcstt2jfvn2eLq9OKvmFueTLbDYrLCxMl112mRITE6t93nfffbde/BJel/vcU089VeZ7GxUVpSuuuEIrVqzwdHkA4BIfTxcAAHXZ008/rejoaOXn52vFihWaPn26li1bppSUFFmtVk+XVyeNGjVKl19+uWw2m7Zu3ap3331Xl1xyiVatWqWuXbtW+XzvvvuuwsPDdcstt5TafvHFF+vEiRPy8/NzU+W1oy73ualTpyowMFB2u1179uzR+++/r4svvlhJSUnq0aOHJKlNmzY6ceKEfH19PVssAJyG4AQAZ+Cyyy5T7969JUm33367wsPD9cILL+jbb7/V9ddf7+Hq6qaePXtq9OjRjtd9+/bVZZddpqlTp+rdd99123XMZrPXB43y1OU+d+211yo8PNzxesSIEYqNjdXXX3/tCE4mk6lOfl8A1H9M1QMAN+rbt68kKTU1tdT2zZs369prr1VYWJisVqt69+6tb7/9tlSboqIiTZkyRR07dpTValXTpk110UUXacGCBY42JfeZ7NixQ0OGDFHjxo3VokULPf300zIMo9T5jh07pvvvv1+tW7eWv7+/zj77bL388stl2plMJk2YMEGzZ89WbGys/P391aVLF82bN69Uu7y8PP3zn/9U27Zt5e/vr4iICA0aNEjJycml2q1cuVJDhw5VSEiIAgIC1K9fP/3222/V+0BV8Wf68ccf69JLL1VERIT8/f0VExOjqVOnlmrTtm1bbdiwQT///LNjmlj//v0lVXyP09dff61evXqpUaNGCg8P1+jRo51OhVu9erVMJpM++eSTMvt++uknmUwmfffdd5Jc/xxdVd7nU1hYqCeffFK9evVSSEiIGjdurL59+2rJkiWlji2ZHvnyyy/rvffeU/v27eXv76/zzjtPq1atcnrtdevWqVmzZurfv7+OHj1a5dojIyMlST4+f/07bnn3OJX0+3379mnEiBEKDAxUs2bN9MADD8hms1X5ugBQHYw4AYAb7dy5U5LUpEkTx7YNGzbowgsvVMuWLfXII4+ocePG+uqrrzRixAj997//1dVXXy3p5H0gzz33nG6//XbFxcUpNzdXq1evVnJysgYNGuQ4n81m09ChQ3X++efrxRdf1Lx585SQkKDi4mI9/fTTkiTDMHTllVdqyZIluu2229SjRw/99NNPevDBB7Vv3z699tprpepetmyZZs2apbvvvltBQUF688039be//U27d+9W06ZNJUl33XWXvvnmG02YMEExMTHKysrSsmXLtGnTJvXs2VOStHjxYl122WXq1auXEhISZDabHQHn119/VVxcnFs+U+nktK8uXbroyiuvlI+Pj+bOnau7775bdrtd48ePlyS9/vrruueeexQYGKjHHntMktS8efMKrzV9+nSNHTtW5513np577jkdOHBAb7zxhn777TetXbtWoaGh5R7Xu3dvtWvXTl999ZXGjBlTat/MmTPVpEkTDRkyxOXP8Uw/n9zcXH3wwQcaNWqU7rjjDuXl5enDDz/UkCFDSk2LK/H5558rLy9P//jHP2QymfTiiy/qmmuu0Y4dOyqcMrdq1SoNGTJEvXv31pw5c9SoUSOntWZnZ0uS7Ha79u3bp//7v/+T1Wp1aaTMZrNpyJAh6tOnj15++WUtXLhQr7zyitq3b69x48Y5PR4AzpgBAKiyjz/+2JBkLFy40Dh48KCxZ88e45tvvjGaNWtm+Pv7G3v27HG0HTBggNG1a1cjPz/fsc1utxsXXHCB0bFjR8e27t27G8OGDav0umPGjDEkGffcc0+pcw0bNszw8/MzDh48aBiGYcyePduQZDzzzDOljr/22msNk8lkbN++3bFNkuHn51dq2++//25IMt566y3HtpCQEGP8+PEV1ma3242OHTsaQ4YMMex2u2P78ePHjejoaGPQoEGVvre0tDRDkjFlyhTj4MGDRkZGhvHrr78a5513niHJ+Prrr0u1P378eJlzDBkyxGjXrl2pbV26dDH69etXpu2SJUsMScaSJUsMwzCMwsJCIyIiwoiNjTVOnDjhaPfdd98Zkownn3yy0vonT55s+Pr6GtnZ2Y5tBQUFRmhoqHHrrbc6tjn7HCtSlT5XXFxsFBQUlDr+8OHDRvPmzUvVUvKZN23atFTdc+bMMSQZc+fOdWwbM2aM0bhxY8MwDGPZsmVGcHCwMWzYsFL9uiIJCQmGpDJfoaGhxrx580q1Lanp448/LnVtScbTTz9dqu25555r9OrVy+n1AcAdmKoHAGdg4MCBatasmVq3bq1rr71WjRs31rfffqtWrVpJOvkv7IsXL9b111+vvLw8HTp0SIcOHVJWVpaGDBmibdu2OaaBhYaGasOGDdq2bZvT606YMMHx55KpdoWFhVq4cKEk6YcffpDFYtG9995b6rj7779fhmHoxx9/LPM+2rdv73jdrVs3BQcHa8eOHY5toaGhWrlypfbv319uTevWrdO2bdt0ww03KCsry/Fejx07pgEDBuiXX36R3W53+t4SEhLUrFkzRUZGqm/fvtq0aZNeeeUVXXvttaXanTrCkZOTo0OHDqlfv37asWOHcnJynF7ndKtXr1ZmZqbuvvvuUvfYDBs2TJ07d9b3339f6fEjR45UUVGRZs2a5dg2f/58HTlyRCNHjnRsc/Y5OuOsz0mSxWJxLHpht9uVnZ2t4uJi9e7du9wpgSNHjiw1YlUy/e/U73+JJUuWaMiQIRowYIBmzZolf39/l2v/73//qwULFmj+/Pn6+OOP1alTJ/3tb3/T8uXLXTr+rrvuKvW6b9++5dYIADWBqXoAcAbeeecdderUSTk5Ofroo4/0yy+/lPpFcvv27TIMQ0888YSeeOKJcs+RmZmpli1b6umnn9ZVV12lTp06KTY2VkOHDtVNN92kbt26lWpvNpvVrl27Uts6deok6a9pW7t27VKLFi0UFBRUqt0555zj2H+qs846q0xdTZo00eHDhx2vX3zxRY0ZM0atW7dWr169dPnll+vmm2921FIS+E6fqnaqnJycMlPuTnfnnXfquuuuU35+vhYvXqw333yz3PtYfvvtNyUkJCgxMVHHjx8vc52QkJBKr3O6ks/k7LPPLrOvc+fOWrZsWaXHd+/eXZ07d9bMmTN12223STo5TS88PFyXXnqpo52zz9EZZ32uxCeffKJXXnlFmzdvVlFRkWN7dHR0mbanf/9Lvkenfv8lKT8/X8OGDVOvXr301Vdflbo3yRUXX3xxqcUhrr32WnXs2FH33HOP1qxZU+mxVqtVzZo1K1Pn6TUCQE0hOAHAGYiLi3OscDZixAhddNFFuuGGG7RlyxbHssuS9MADDzjucTldhw4dJJ38pTI1NVVz5szR/Pnz9cEHH+i1117TtGnTdPvtt9fo+7BYLOVuN05ZSOL6669X37599b///U/z58/XSy+9pBdeeEGzZs3SZZdd5nivL730Upl7aEq48gDVjh07auDAgZKkK664QhaLRY888oguueQSx2edmpqqAQMGqHPnznr11VfVunVr+fn56YcfftBrr73m0shWTRg5cqT+9a9/6dChQwoKCtK3336rUaNGlQoYzj5HZ5z1OUmaMWOGbrnlFo0YMUIPPvigIiIiZLFY9Nxzz5VZZENy7fsvSf7+/rr88ss1Z84czZs3T1dccYXLn015AgMD1adPH82ZM0fHjh1T48aNK2xbUY0AUFuYqgcAblLyi+n+/fv19ttvS5JjFMHX11cDBw4s9+vUUaGwsDCNHTtWX3zxhfbs2aNu3brpqaeeKnUdu91eZnrS1q1bJZ1cRU46+Syc/fv3Ky8vr1S7zZs3O/ZXR1RUlO6++27Nnj1baWlpatq0qf71r39JkmOqX3BwcIXvtTrP5nnssccUFBSkxx9/3LFt7ty5Kigo0Lfffqt//OMfuvzyyzVw4MByFygwmUwuXafkM9myZUuZfVu2bHHpMxs5cqSKi4v13//+Vz/++KNyc3P197//vUy7yj7Hqiivz0nSN998o3bt2mnWrFm66aabNGTIEA0cOFD5+flVvsapTCaT/vOf/2jAgAG67rrryqxIWB3FxcWSVK1V+QCgNhGcAMCN+vfvr7i4OL3++uvKz89XRESE+vfvr3//+99KT08v0/7gwYOOP2dlZZXaFxgYqA4dOqigoKDMcaf+kmwYht5++235+vpqwIABkuR4gOyp7STptddek8lkcmlk41Q2m63MfUMRERFq0aKFo75evXqpffv2evnll8v9JfjU91oVoaGh+sc//qGffvpJ69atk/TX6MOpIyI5OTn6+OOPyxzfuHFjHTlyxOl1evfurYiICE2bNq3UZ/7jjz9q06ZNGjZsmNNznHPOOeratatmzpypmTNnKioqShdffLFjvyufY1Wd3uek8j+flStXKjExsVrXOJWfn59mzZql8847T8OHD1dSUlK1z5Wdna3ly5crMjJSERERZ1wbANQkpuoBgJs9+OCDuu666zR9+nTdddddeuedd3TRRRepa9euuuOOO9SuXTsdOHBAiYmJ2rt3r37//XdJUkxMjPr3769evXopLCxMq1evdixbfSqr1ap58+ZpzJgx6tOnj3788Ud9//33evTRRx33gAwfPlyXXHKJHnvsMe3cuVPdu3fX/PnzNWfOHP3zn/8stRCEK/Ly8tSqVStde+216t69uwIDA7Vw4UKtWrVKr7zyiqST91598MEHuuyyy9SlSxeNHTtWLVu21L59+7RkyRIFBwdr7ty51fpM77vvPr3++ut6/vnn9eWXX2rw4MHy8/PT8OHD9Y9//ENHjx7V+++/r4iIiDIBtVevXpo6daqeeeYZdejQQREREaXuOSrh6+urF154QWPHjlW/fv00atQox3Lkbdu21cSJE12qdeTIkXryySdltVp12223yWz+698oXfkcq+P0PnfFFVdo1qxZuvrqqzVs2DClpaVp2rRpiomJccvITqNGjfTdd9/p0ksv1WWXXaaff/5ZsbGxTo/75ptvFBgYKMMwtH//fn344Yc6fPiwpk2b5vLIIAB4jOcW9AOAuqtkaehVq1aV2Wez2Yz27dsb7du3N4qLiw3DMIzU1FTj5ptvNiIjIw1fX1+jZcuWxhVXXGF88803juOeeeYZIy4uzggNDTUaNWpkdO7c2fjXv/5lFBYWOtqULAmdmppqDB482AgICDCaN29uJCQkGDabrVQdeXl5xsSJE40WLVoYvr6+RseOHY2XXnqp1FLhhnFyOfLylsdu06aNMWbMGMMwTi6r/eCDDxrdu3c3goKCjMaNGxvdu3c33n333TLHrV271rjmmmuMpk2bGv7+/kabNm2M66+/3li0aFGln2nJMtQvvfRSuftvueUWw2KxOJZN//bbb41u3boZVqvVaNu2rfHCCy8YH330kSHJSEtLcxyXkZFhDBs2zAgKCjIkOZYmP3058hIzZ840zj33XMPf398ICwszbrzxRmPv3r2V1n6qbdu2OZbbXrZsWal9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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "agg_df = agg_result.get(\"data\").get(agg_result.get(\"metadata\").id[0]).iloc[1:50,:]\n", - "\n", - "# Plot\n", - "plt.figure(figsize=(10, 6))\n", - "plt.plot(agg_df['rank_bin'], agg_df['response_ratio'], marker='o')\n", - "plt.title('Aggregated GZF3. McIsaac 15 2510')\n", - "plt.xlabel('Rank Bin')\n", - "plt.ylabel('Response Ratio')\n", - "plt.title('Response Ratio vs Rank Bin')\n", - "plt.grid(True)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Caveats\n", - "\n", - "1. I have written the scripts to automatically check the redis queue for work and to \n", - " both launch celery worker nodes, and kill them when they are finished. But, though\n", - " they work if I run them manually, they have not worked when scheduled through a\n", - " cronjob. I'll work with Brian and Eric next week to figure out why.\n", - "\n", - "1. I haven't tested each of the endpoint APIs individually. Help is welcome." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.9" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/tutorials/datacard_tutorial.ipynb b/docs/tutorials/datacard_tutorial.ipynb index 071397b..47df9b6 100644 --- a/docs/tutorials/datacard_tutorial.ipynb +++ b/docs/tutorials/datacard_tutorial.ipynb @@ -27,14 +27,22 @@ }, { "cell_type": "code", - "execution_count": 132, + "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Repository: BrentLab/rossi_2021\n" + "Repository: BrentLab/mahendrawada_2025\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chase/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" ] } ], @@ -43,7 +51,7 @@ "\n", "# Initialize DataCard with the Rossi 2021 dataset\n", "# try this with mahendrawada_2025, which is more complex\n", - "card = DataCard('BrentLab/rossi_2021')\n", + "card = DataCard('BrentLab/mahendrawada_2025')\n", "\n", "print(f\"Repository: {card.repo_id}\")" ] @@ -59,7 +67,7 @@ }, { "cell_type": "code", - "execution_count": 133, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -68,16 +76,16 @@ "text": [ "Repository Information:\n", "========================================\n", - "repo_id : BrentLab/rossi_2021\n", - "pretty_name : Rossi ChIP-exo 2021\n", + "repo_id : BrentLab/mahendrawada_2025\n", + "pretty_name : Mahendrawada 2025 ChEC-seq and Nascent RNA-seq data\n", "license : mit\n", - "tags : ['transcription-factor', 'binding', 'chipexo', 'genomics', 'biology']\n", + "tags : ['biology', 'genomics', 'yeast', 'transcription-factors', 'gene-expression', 'binding', 'chec', 'perturbation', 'rnaseq', 'nascent rnaseq']\n", "language : ['en']\n", - "size_categories : None\n", - "num_configs : 2\n", - "dataset_types : ['metadata', 'genome_map']\n", - "total_files : 1237\n", - "last_modified : 2025-09-18T00:59:17+00:00\n", + "size_categories : ['100K \u001b[39m\u001b[32m2\u001b[39m metadata_info = \u001b[43mcard\u001b[49m\u001b[43m.\u001b[49m\u001b[43mexplore_config\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43mmetadata\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[32m 4\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33mMetadata Configuration Details:\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 5\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33m=\u001b[39m\u001b[33m\"\u001b[39m * \u001b[32m40\u001b[39m)\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/code/tfbp/tfbpapi/tfbpapi/datainfo/datacard.py:291\u001b[39m, in \u001b[36mDataCard.explore_config\u001b[39m\u001b[34m(self, config_name)\u001b[39m\n\u001b[32m 289\u001b[39m config = \u001b[38;5;28mself\u001b[39m.get_config(config_name)\n\u001b[32m 290\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m config:\n\u001b[32m--> \u001b[39m\u001b[32m291\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m DataCardError(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mConfiguration \u001b[39m\u001b[33m'\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mconfig_name\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m'\u001b[39m\u001b[33m not found\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 293\u001b[39m info: \u001b[38;5;28mdict\u001b[39m[\u001b[38;5;28mstr\u001b[39m, Any] = {\n\u001b[32m 294\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mconfig_name\u001b[39m\u001b[33m\"\u001b[39m: config.config_name,\n\u001b[32m 295\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mdescription\u001b[39m\u001b[33m\"\u001b[39m: config.description,\n\u001b[32m (...)\u001b[39m\u001b[32m 305\u001b[39m ],\n\u001b[32m 306\u001b[39m }\n\u001b[32m 308\u001b[39m \u001b[38;5;66;03m# Add partitioning info if present\u001b[39;00m\n", + "\u001b[31mDataCardError\u001b[39m: Configuration 'metadata' not found" ] } ], @@ -230,7 +264,7 @@ }, { "cell_type": "code", - "execution_count": 137, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -289,7 +323,7 @@ }, { "cell_type": "code", - "execution_count": 138, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -333,7 +367,7 @@ }, { "cell_type": "code", - "execution_count": 139, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -376,7 +410,7 @@ }, { "cell_type": "code", - "execution_count": 140, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -413,7 +447,7 @@ }, { "cell_type": "code", - "execution_count": 141, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -465,7 +499,7 @@ }, { "cell_type": "code", - "execution_count": 142, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -524,7 +558,7 @@ }, { "cell_type": "code", - "execution_count": 143, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -556,7 +590,7 @@ }, { "cell_type": "code", - "execution_count": 144, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -615,7 +649,7 @@ }, { "cell_type": "code", - "execution_count": 145, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -663,7 +697,7 @@ }, { "cell_type": "code", - "execution_count": 146, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -737,7 +771,7 @@ ], "metadata": { "kernelspec": { - "display_name": "tfbpapi-py3.11 (3.11.9)", + "display_name": "tfbpapi-py3.11", "language": "python", "name": "python3" }, diff --git a/docs/tutorials/hfqueryapi_tutorial.ipynb b/docs/tutorials/hfqueryapi_tutorial.ipynb new file mode 100644 index 0000000..70c3ebb --- /dev/null +++ b/docs/tutorials/hfqueryapi_tutorial.ipynb @@ -0,0 +1,2214 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# HfQueryAPI Tutorial: Metadata-Driven Data Exploration\n", + "\n", + "This tutorial demonstrates how to use the HfQueryAPI for efficient metadata-driven exploration and querying of Hugging Face datasets, using the Hackett 2020 transcription factor overexpression dataset as an example.\n", + "\n", + "## Overview\n", + "\n", + "The HfQueryAPI provides a streamlined workflow for:\n", + "1. **Exploring metadata** to understand dataset structure\n", + "2. **Setting filters** based on metadata values\n", + "3. **Querying data** with automatic filter application\n", + "4. **Efficient SQL-based filtering** on large datasets\n", + "\n", + "## Dataset: Hackett 2020\n", + "\n", + "The `BrentLab/hackett_2020` dataset contains transcription factor overexpression data with embedded metadata fields including:\n", + "- `regulator_locus_tag` & `regulator_symbol`: Transcription factor identifiers\n", + "- `time`: Time point (0, 15, 30, 90 minutes)\n", + "- `mechanism`: Experimental mechanism (ZEV)\n", + "- `restriction`: Restriction enzyme treatment (P, NP)\n", + "- `date`, `strain`: Experimental metadata\n", + "\n", + "Let's start exploring!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Initialize HfQueryAPI and Getting Metadata\n", + "\n", + "First, we'll initialize the API client for the Hackett 2020 dataset:" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Initialized HfQueryAPI for BrentLab/hackett_2020\n", + "Repository type: dataset\n", + "Available configurations: ['hackett_2020']\n" + ] + } + ], + "source": [ + "from tfbpapi.HfQueryAPI import HfQueryAPI\n", + "import pandas as pd\n", + "\n", + "# Initialize the API client\n", + "api = HfQueryAPI(repo_id=\"BrentLab/hackett_2020\", repo_type=\"dataset\")\n", + "\n", + "print(f\"Initialized HfQueryAPI for {api.repo_id}\")\n", + "print(f\"Repository type: {api.repo_type}\")\n", + "print(f\"Available configurations: {[c.config_name for c in api.configs]}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [], + "source": [ + "metadata = api.get_metadata(\"hackett_2020\")" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Experimental Conditions Summary\n", + "==================================================\n", + "\n", + "⏱️ Time Points:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Time Point (min)", + "rawType": "float64", + "type": "float" + }, + { + "name": "Frequency", + "rawType": "int64", + "type": "integer" + }, + { + "name": "% of Records", + "rawType": "float64", + "type": "float" + } + ], + "ref": "537d2492-acbe-4e3d-a2cc-3c1129ec2ed1", + "rows": [ + [ + "0", + "0.0", + "217", + "12.8" + ], + [ + "1", + "2.5", + "8", + "0.5" + ], + [ + "2", + "5.0", + "212", + "12.5" + ], + [ + "3", + "7.5", + "2", + "0.1" + ], + [ + "4", + "8.0", + "2", + "0.1" + ], + [ + "5", + "10.0", + "187", + "11.0" + ], + [ + "6", + "12.5", + "2", + "0.1" + ], + [ + "7", + "15.0", + "212", + "12.5" + ], + [ + "8", + "18.0", + "1", + "0.1" + ], + [ + "9", + "20.0", + "184", + "10.9" + ], + [ + "10", + "30.0", + "216", + "12.8" + ], + [ + "11", + "45.0", + "213", + "12.6" + ], + [ + "12", + "60.0", + "20", + "1.2" + ], + [ + "13", + "90.0", + "212", + "12.5" + ], + [ + "14", + "100.0", + "2", + "0.1" + ], + [ + "15", + "120.0", + "1", + "0.1" + ], + [ + "16", + "180.0", + "1", + "0.1" + ], + [ + "17", + "290.0", + "1", + "0.1" + ] + ], + "shape": { + "columns": 3, + "rows": 18 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Time Point (min)Frequency% of Records
00.021712.8
12.580.5
25.021212.5
37.520.1
48.020.1
510.018711.0
612.520.1
715.021212.5
818.010.1
920.018410.9
1030.021612.8
1145.021312.6
1260.0201.2
1390.021212.5
14100.020.1
15120.010.1
16180.010.1
17290.010.1
\n", + "
" + ], + "text/plain": [ + " Time Point (min) Frequency % of Records\n", + "0 0.0 217 12.8\n", + "1 2.5 8 0.5\n", + "2 5.0 212 12.5\n", + "3 7.5 2 0.1\n", + "4 8.0 2 0.1\n", + "5 10.0 187 11.0\n", + "6 12.5 2 0.1\n", + "7 15.0 212 12.5\n", + "8 18.0 1 0.1\n", + "9 20.0 184 10.9\n", + "10 30.0 216 12.8\n", + "11 45.0 213 12.6\n", + "12 60.0 20 1.2\n", + "13 90.0 212 12.5\n", + "14 100.0 2 0.1\n", + "15 120.0 1 0.1\n", + "16 180.0 1 0.1\n", + "17 290.0 1 0.1" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Experimental Design Overview:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Condition Type", + "rawType": "object", + "type": "string" + }, + { + "name": "Unique Values", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Most Common", + "rawType": "object", + "type": "string" + } + ], + "ref": "fc6ebeb5-026f-49dc-ab9a-89995ff14cb9", + "rows": [ + [ + "0", + "Mechanisms", + "2", + "ZEV" + ], + [ + "1", + "Restriction Treatments", + "3", + "P" + ], + [ + "2", + "Strains", + "215", + "SMY2207" + ], + [ + "3", + "Experimental Dates", + "23", + "20150101 to 20161117" + ] + ], + "shape": { + "columns": 3, + "rows": 4 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Condition TypeUnique ValuesMost Common
0Mechanisms2ZEV
1Restriction Treatments3P
2Strains215SMY2207
3Experimental Dates2320150101 to 20161117
\n", + "
" + ], + "text/plain": [ + " Condition Type Unique Values Most Common\n", + "0 Mechanisms 2 ZEV\n", + "1 Restriction Treatments 3 P\n", + "2 Strains 215 SMY2207\n", + "3 Experimental Dates 23 20150101 to 20161117" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Create summary tables for experimental conditions\n", + "print(\"Experimental Conditions Summary\")\n", + "print(\"=\" * 50)\n", + "\n", + "# Time points summary\n", + "time_summary = pd.DataFrame({\n", + " 'Time Point (min)': metadata['time'].value_counts().sort_index().index,\n", + " 'Frequency': metadata['time'].value_counts().sort_index().values,\n", + "})\n", + "time_summary['% of Records'] = (time_summary['Frequency'] / time_summary['Frequency'].sum() * 100).round(1)\n", + "\n", + "print(\"\\n⏱️ Time Points:\")\n", + "display(time_summary)\n", + "\n", + "# Experimental conditions table\n", + "conditions_summary = pd.DataFrame({\n", + " 'Condition Type': ['Mechanisms', 'Restriction Treatments', 'Strains', 'Experimental Dates'],\n", + " 'Unique Values': [\n", + " metadata['mechanism'].nunique(),\n", + " metadata['restriction'].nunique(), \n", + " metadata['strain'].nunique(),\n", + " metadata['date'].nunique()\n", + " ],\n", + " 'Most Common': [\n", + " metadata['mechanism'].mode().iloc[0],\n", + " metadata['restriction'].mode().iloc[0],\n", + " metadata['strain'].mode().iloc[0],\n", + " f\"{metadata['date'].min()} to {metadata['date'].max()}\"\n", + " ]\n", + "})\n", + "\n", + "print(\"\\nExperimental Design Overview:\")\n", + "display(conditions_summary)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Transcription Factor Analysis\n", + "==================================================\n", + "\n", + "Dataset Overview:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Metric", + "rawType": "object", + "type": "string" + }, + { + "name": "Value", + "rawType": "object", + "type": "string" + } + ], + "ref": "bd31a290-891e-4027-8987-337b87e04e47", + "rows": [ + [ + "0", + "Total Unique Transcription Factors", + "203" + ] + ], + "shape": { + "columns": 2, + "rows": 1 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
MetricValue
0Total Unique Transcription Factors203
\n", + "
" + ], + "text/plain": [ + " Metric Value\n", + "0 Total Unique Transcription Factors 203" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Create comprehensive transcription factor analysis tables\n", + "print(\"Transcription Factor Analysis\")\n", + "print(\"=\" * 50)\n", + "\n", + "print(f\"\\nDataset Overview:\")\n", + "tf_overview = pd.DataFrame({\n", + " 'Metric': [\n", + " 'Total Unique Transcription Factors',\n", + " ],\n", + " 'Value': [\n", + " f\"{metadata['regulator_locus_tag'].nunique():,}\",\n", + " ]\n", + "})\n", + "display(tf_overview)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setting Simple Filters" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Filter Analysis\n", + "========================================\n", + "\n", + "Applied Filters:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Filter Component", + "rawType": "object", + "type": "string" + }, + { + "name": "Value", + "rawType": "object", + "type": "string" + } + ], + "ref": "6c3fe59a-d2b0-49aa-b067-c472c07db1e0", + "rows": [ + [ + "0", + "Time Point", + "15 minutes" + ], + [ + "1", + "Mechanism", + "ZEV" + ], + [ + "2", + "Restriction", + "P (Restriction)" + ], + [ + "3", + "SQL Filter", + "time = 15 AND mechanism = 'ZEV' AND restriction = 'P'" + ] + ], + "shape": { + "columns": 2, + "rows": 4 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Filter ComponentValue
0Time Point15 minutes
1MechanismZEV
2RestrictionP (Restriction)
3SQL Filtertime = 15 AND mechanism = 'ZEV' AND restrictio...
\n", + "
" + ], + "text/plain": [ + " Filter Component Value\n", + "0 Time Point 15 minutes\n", + "1 Mechanism ZEV\n", + "2 Restriction P (Restriction)\n", + "3 SQL Filter time = 15 AND mechanism = 'ZEV' AND restrictio..." + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Top Transcription Factors in Filtered Dataset:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Locus Tag", + "rawType": "object", + "type": "string" + }, + { + "name": "Symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "Records in Subset", + "rawType": "int64", + "type": "integer" + } + ], + "ref": "183800f8-03b3-4fcb-846a-7585ea8533b5", + "rows": [ + [ + "0", + "YPL016W", + "SWI1", + "18525" + ], + [ + "1", + "YEL009C", + "GCN4", + "12350" + ], + [ + "2", + "YPL133C", + "RDS2", + "12350" + ], + [ + "3", + "Z3EV", + "Z3EV", + "12350" + ], + [ + "4", + "YBL008W", + "HIR1", + "6175" + ], + [ + "5", + "YBL021C", + "HAP3", + "6175" + ], + [ + "6", + "YBL025W", + "RRN10", + "6175" + ], + [ + "7", + "YBR239C", + "ERT1", + "6175" + ] + ], + "shape": { + "columns": 3, + "rows": 8 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Locus TagSymbolRecords in Subset
0YPL016WSWI118525
1YEL009CGCN412350
2YPL133CRDS212350
3Z3EVZ3EV12350
4YBL008WHIR16175
5YBL021CHAP36175
6YBL025WRRN106175
7YBR239CERT16175
\n", + "
" + ], + "text/plain": [ + " Locus Tag Symbol Records in Subset\n", + "0 YPL016W SWI1 18525\n", + "1 YEL009C GCN4 12350\n", + "2 YPL133C RDS2 12350\n", + "3 Z3EV Z3EV 12350\n", + "4 YBL008W HIR1 6175\n", + "5 YBL021C HAP3 6175\n", + "6 YBL025W RRN10 6175\n", + "7 YBR239C ERT1 6175" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Set filters for 15-minute timepoint with ZEV mechanism and P restriction\n", + "api.set_filter(\"hackett_2020\", time=15, mechanism=\"ZEV\", restriction=\"P\")\n", + "\n", + "# Create comprehensive filter analysis\n", + "print(\"Filter Analysis\")\n", + "print(\"=\" * 40)\n", + "\n", + "# Show current filter\n", + "current_filter = api.get_current_filter(\"hackett_2020\")\n", + "filter_info = pd.DataFrame({\n", + " 'Filter Component': ['Time Point', 'Mechanism', 'Restriction', 'SQL Filter'],\n", + " 'Value': ['15 minutes', 'ZEV', 'P (Restriction)', current_filter]\n", + "})\n", + "\n", + "print(\"\\nApplied Filters:\")\n", + "display(filter_info)\n", + "\n", + "# Analyze filter impact\n", + "filtered_metadata = metadata[\n", + " (metadata['time'] == 15) & \n", + " (metadata['mechanism'] == 'ZEV') & \n", + " (metadata['restriction'] == 'P')\n", + "]\n", + "\n", + "# Show top TFs in filtered data\n", + "filtered_tf_summary = filtered_metadata.groupby(['regulator_locus_tag', 'regulator_symbol'])['count'].sum().sort_values(ascending=False).head(8)\n", + "tf_filtered_df = pd.DataFrame({\n", + " 'Locus Tag': [idx[0] for idx in filtered_tf_summary.index],\n", + " 'Symbol': [idx[1] for idx in filtered_tf_summary.index],\n", + " 'Records in Subset': filtered_tf_summary.values,\n", + "})\n", + "\n", + "print(\"\\nTop Transcription Factors in Filtered Dataset:\")\n", + "display(tf_filtered_df)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Query Data with Automatic Filter Application\n", + "\n", + "Now when we query the data, our filters will be automatically applied:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setting Complex SQL Filters\n", + "\n", + "For more sophisticated filtering, we can use the `set_sql_filter()` method with full SQL expressions:" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Complex SQL Filter Analysis\n", + "==================================================\n", + "\n", + "Applied Complex Filter:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Filter Component", + "rawType": "object", + "type": "string" + }, + { + "name": "Value", + "rawType": "object", + "type": "string" + } + ], + "ref": "1063532f-04e7-453f-9fac-2261490f4701", + "rows": [ + [ + "0", + "Time Points", + "15, 30 minutes" + ], + [ + "1", + "Mechanism", + "ZEV (overexpression)" + ], + [ + "2", + "Restriction", + "P (restriction enzyme)" + ], + [ + "3", + "Selected TFs", + "YER040W (GLN3), YER028C (RSF2), YPL016W (SWI1)" + ], + [ + "4", + "Complete SQL Filter", + "time IN (15, 30) AND \n mechanism = 'ZEV' AND \n restriction = 'P' AND \n regulator_locus_tag IN ('YER040W', 'YER028C', 'YPL016W')" + ] + ], + "shape": { + "columns": 2, + "rows": 5 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Filter ComponentValue
0Time Points15, 30 minutes
1MechanismZEV (overexpression)
2RestrictionP (restriction enzyme)
3Selected TFsYER040W (GLN3), YER028C (RSF2), YPL016W (SWI1)
4Complete SQL Filtertime IN (15, 30) AND \\n mechanism = 'ZEV' A...
\n", + "
" + ], + "text/plain": [ + " Filter Component Value\n", + "0 Time Points 15, 30 minutes\n", + "1 Mechanism ZEV (overexpression)\n", + "2 Restriction P (restriction enzyme)\n", + "3 Selected TFs YER040W (GLN3), YER028C (RSF2), YPL016W (SWI1)\n", + "4 Complete SQL Filter time IN (15, 30) AND \\n mechanism = 'ZEV' A..." + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Set a complex filter for multiple time points and specific transcription factors\n", + "api.set_sql_filter(\"hackett_2020\", \"\"\"\n", + " time IN (15, 30) AND \n", + " mechanism = 'ZEV' AND \n", + " restriction = 'P' AND \n", + " regulator_locus_tag IN ('YER040W', 'YER028C', 'YPL016W')\n", + "\"\"\")\n", + "\n", + "print(\"Complex SQL Filter Analysis\")\n", + "print(\"=\" * 50)\n", + "\n", + "# Show filter details\n", + "filter_details = pd.DataFrame({\n", + " 'Filter Component': [\n", + " 'Time Points',\n", + " 'Mechanism', \n", + " 'Restriction',\n", + " 'Selected TFs',\n", + " 'Complete SQL Filter'\n", + " ],\n", + " 'Value': [\n", + " '15, 30 minutes',\n", + " 'ZEV (overexpression)',\n", + " 'P (restriction enzyme)',\n", + " 'YER040W (GLN3), YER028C (RSF2), YPL016W (SWI1)',\n", + " api.get_current_filter('hackett_2020')\n", + " ]\n", + "})\n", + "\n", + "print(\"\\nApplied Complex Filter:\")\n", + "display(filter_details)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [], + "source": [ + "## Query with the complex filter" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Time Course Comparison for Selected TFs:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Locus Tag", + "rawType": "object", + "type": "string" + }, + { + "name": "Symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "Time (min)", + "rawType": "float64", + "type": "float" + }, + { + "name": "Target Count", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Avg Response", + "rawType": "float64", + "type": "float" + }, + { + "name": "Strong Responders", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Max |Response|", + "rawType": "float64", + "type": "float" + }, + { + "name": "% Strong", + "rawType": "float64", + "type": "float" + } + ], + "ref": "fbfefef3-454e-4318-99ae-3d43d79d3800", + "rows": [ + [ + "0", + "YER028C", + "MIG3", + "15.0", + "6175", + "-0.01", + "99", + "5.894", + "1.6" + ], + [ + "1", + "YER028C", + "MIG3", + "30.0", + "6175", + "-0.028", + "246", + "5.516", + "4.0" + ], + [ + "2", + "YER040W", + "GLN3", + "15.0", + "6175", + "0.018", + "81", + "7.923", + "1.3" + ], + [ + "3", + "YER040W", + "GLN3", + "30.0", + "6175", + "0.042", + "631", + "10.459", + "10.2" + ], + [ + "4", + "YPL016W", + "SWI1", + "15.0", + "18525", + "0.001", + "431", + "6.216", + "2.3" + ], + [ + "5", + "YPL016W", + "SWI1", + "30.0", + "18525", + "0.033", + "762", + "6.753", + "4.1" + ] + ], + "shape": { + "columns": 8, + "rows": 6 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Locus TagSymbolTime (min)Target CountAvg ResponseStrong RespondersMax |Response|% Strong
0YER028CMIG315.06175-0.010995.8941.6
1YER028CMIG330.06175-0.0282465.5164.0
2YER040WGLN315.061750.018817.9231.3
3YER040WGLN330.061750.04263110.45910.2
4YPL016WSWI115.0185250.0014316.2162.3
5YPL016WSWI130.0185250.0337626.7534.1
\n", + "
" + ], + "text/plain": [ + " Locus Tag Symbol Time (min) Target Count Avg Response Strong Responders \\\n", + "0 YER028C MIG3 15.0 6175 -0.010 99 \n", + "1 YER028C MIG3 30.0 6175 -0.028 246 \n", + "2 YER040W GLN3 15.0 6175 0.018 81 \n", + "3 YER040W GLN3 30.0 6175 0.042 631 \n", + "4 YPL016W SWI1 15.0 18525 0.001 431 \n", + "5 YPL016W SWI1 30.0 18525 0.033 762 \n", + "\n", + " Max |Response| % Strong \n", + "0 5.894 1.6 \n", + "1 5.516 4.0 \n", + "2 7.923 1.3 \n", + "3 10.459 10.2 \n", + "4 6.216 2.3 \n", + "5 6.753 4.1 " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Overall TF Performance Summary:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Locus Tag", + "rawType": "object", + "type": "string" + }, + { + "name": "Symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "Total Targets", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Total Strong", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Avg Response", + "rawType": "float64", + "type": "float" + }, + { + "name": "% Strong Overall", + "rawType": "float64", + "type": "float" + } + ], + "ref": "ca80d3c1-4fec-4613-9f73-12ba5359ed44", + "rows": [ + [ + "0", + "YER028C", + "MIG3", + "12350", + "345", + "-0.019", + "2.8" + ], + [ + "1", + "YER040W", + "GLN3", + "12350", + "712", + "0.03", + "5.8" + ], + [ + "2", + "YPL016W", + "SWI1", + "37050", + "1193", + "0.017", + "3.2" + ] + ], + "shape": { + "columns": 6, + "rows": 3 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Locus TagSymbolTotal TargetsTotal StrongAvg Response% Strong Overall
0YER028CMIG312350345-0.0192.8
1YER040WGLN3123507120.0305.8
2YPL016WSWI13705011930.0173.2
\n", + "
" + ], + "text/plain": [ + " Locus Tag Symbol Total Targets Total Strong Avg Response \\\n", + "0 YER028C MIG3 12350 345 -0.019 \n", + "1 YER040W GLN3 12350 712 0.030 \n", + "2 YPL016W SWI1 37050 1193 0.017 \n", + "\n", + " % Strong Overall \n", + "0 2.8 \n", + "1 5.8 \n", + "2 3.2 " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "\n", + "time_comparison = api.query(\"\"\"\n", + " SELECT \n", + " regulator_locus_tag,\n", + " regulator_symbol,\n", + " time,\n", + " COUNT(*) as target_count,\n", + " ROUND(AVG(log2_shrunken_timecourses), 3) as avg_response,\n", + " COUNT(CASE WHEN ABS(log2_shrunken_timecourses) > 0.5 THEN 1 END) as strong_responders,\n", + " ROUND(MAX(ABS(log2_shrunken_timecourses)), 3) as max_abs_response\n", + " FROM hackett_2020 \n", + " GROUP BY regulator_locus_tag, regulator_symbol, time\n", + " ORDER BY regulator_locus_tag, time\n", + "\"\"\", \"hackett_2020\")\n", + "\n", + "# Format display\n", + "time_comparison_display = time_comparison.copy()\n", + "time_comparison_display.columns = ['Locus Tag', 'Symbol', 'Time (min)', 'Target Count', 'Avg Response', 'Strong Responders', 'Max |Response|']\n", + "time_comparison_display['% Strong'] = (time_comparison_display['Strong Responders'] / time_comparison_display['Target Count'] * 100).round(1)\n", + "\n", + "print(\"\\nTime Course Comparison for Selected TFs:\")\n", + "display(time_comparison_display)\n", + "\n", + "# Summary analysis\n", + "tf_summary = time_comparison.groupby(['regulator_locus_tag', 'regulator_symbol']).agg({\n", + " 'target_count': 'sum',\n", + " 'strong_responders': 'sum',\n", + " 'avg_response': 'mean'\n", + "}).reset_index()\n", + "\n", + "tf_summary['total_%_strong'] = (tf_summary['strong_responders'] / tf_summary['target_count'] * 100).round(1)\n", + "tf_summary_display = tf_summary.copy()\n", + "tf_summary_display.columns = ['Locus Tag', 'Symbol', 'Total Targets', 'Total Strong', 'Avg Response', '% Strong Overall']\n", + "\n", + "print(\"\\nOverall TF Performance Summary:\")\n", + "display(tf_summary_display)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Filter Management\n", + "\n", + "The filtering system provides full control over active filters:" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "🔧 Filter Management Demonstration\n", + "==================================================\n", + "\n", + "Current filter:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Status", + "rawType": "object", + "type": "string" + }, + { + "name": "SQL WHERE clause", + "rawType": "object", + "type": "string" + } + ], + "ref": "e93f9433-b31e-49db-b98d-5c5b4116e1d6", + "rows": [ + [ + "0", + "Active Filter", + "time IN (15, 30) AND \n mechanism = 'ZEV' AND \n restriction = 'P' AND \n regulator_locus_tag IN ('YER040W', 'YER028C', 'YPL016W')" + ] + ], + "shape": { + "columns": 2, + "rows": 1 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
StatusSQL WHERE clause
0Active Filtertime IN (15, 30) AND \\n mechanism = 'ZEV' A...
\n", + "
" + ], + "text/plain": [ + " Status SQL WHERE clause\n", + "0 Active Filter time IN (15, 30) AND \\n mechanism = 'ZEV' A..." + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "After clearing filters:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Status", + "rawType": "object", + "type": "string" + }, + { + "name": "SQL WHERE clause", + "rawType": "object", + "type": "string" + } + ], + "ref": "7ca71950-6a31-404e-baa4-d096e56c4991", + "rows": [ + [ + "0", + "After Clearing", + "None" + ] + ], + "shape": { + "columns": 2, + "rows": 1 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
StatusSQL WHERE clause
0After ClearingNone
\n", + "
" + ], + "text/plain": [ + " Status SQL WHERE clause\n", + "0 After Clearing None" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "📊 Dataset Size Comparison:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Dataset State", + "rawType": "object", + "type": "string" + }, + { + "name": "Total Records", + "rawType": "object", + "type": "string" + }, + { + "name": "Percentage", + "rawType": "object", + "type": "string" + } + ], + "ref": "d30810e1-4013-4929-a14a-522ee08c48fe", + "rows": [ + [ + "0", + "Unfiltered (Full Dataset)", + "10,454,275", + "100.0%" + ], + [ + "1", + "Filtered (time=15, ZEV, P)", + "1,012,700", + "9.7%" + ], + [ + "2", + "Data Reduction", + "9,441,575", + "90.3%" + ] + ], + "shape": { + "columns": 3, + "rows": 3 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Dataset StateTotal RecordsPercentage
0Unfiltered (Full Dataset)10,454,275100.0%
1Filtered (time=15, ZEV, P)1,012,7009.7%
2Data Reduction9,441,57590.3%
\n", + "
" + ], + "text/plain": [ + " Dataset State Total Records Percentage\n", + "0 Unfiltered (Full Dataset) 10,454,275 100.0%\n", + "1 Filtered (time=15, ZEV, P) 1,012,700 9.7%\n", + "2 Data Reduction 9,441,575 90.3%" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Complete HfQueryAPI Workflow:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Step", + "rawType": "object", + "type": "string" + }, + { + "name": "Method", + "rawType": "object", + "type": "string" + }, + { + "name": "Purpose", + "rawType": "object", + "type": "string" + } + ], + "ref": "f8e9e25a-c9d1-479d-b664-f70bac590d68", + "rows": [ + [ + "0", + "1. Explore Metadata", + "api.get_metadata()", + "Understand dataset structure" + ], + [ + "1", + "2. Set Simple Filters", + "api.set_filter(config, **kwargs)", + "Filter by metadata values" + ], + [ + "2", + "3. Set Complex SQL Filters", + "api.set_sql_filter(config, sql)", + "Complex multi-condition filtering" + ], + [ + "3", + "4. Query with Auto-Apply", + "api.query(sql, config)", + "Analyze with automatic filtering" + ], + [ + "4", + "5. Clear/Manage Filters", + "api.clear_filter() / get_current_filter()", + "Reset or inspect current state" + ] + ], + "shape": { + "columns": 3, + "rows": 5 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
StepMethodPurpose
01. Explore Metadataapi.get_metadata()Understand dataset structure
12. Set Simple Filtersapi.set_filter(config, **kwargs)Filter by metadata values
23. Set Complex SQL Filtersapi.set_sql_filter(config, sql)Complex multi-condition filtering
34. Query with Auto-Applyapi.query(sql, config)Analyze with automatic filtering
45. Clear/Manage Filtersapi.clear_filter() / get_current_filter()Reset or inspect current state
\n", + "
" + ], + "text/plain": [ + " Step Method \\\n", + "0 1. Explore Metadata api.get_metadata() \n", + "1 2. Set Simple Filters api.set_filter(config, **kwargs) \n", + "2 3. Set Complex SQL Filters api.set_sql_filter(config, sql) \n", + "3 4. Query with Auto-Apply api.query(sql, config) \n", + "4 5. Clear/Manage Filters api.clear_filter() / get_current_filter() \n", + "\n", + " Purpose \n", + "0 Understand dataset structure \n", + "1 Filter by metadata values \n", + "2 Complex multi-condition filtering \n", + "3 Analyze with automatic filtering \n", + "4 Reset or inspect current state " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Demonstrate filter management capabilities\n", + "print(\"🔧 Filter Management Demonstration\")\n", + "print(\"=\" * 50)\n", + "\n", + "# Show current filter\n", + "current_filter = api.get_current_filter('hackett_2020')\n", + "print(f\"\\nCurrent filter:\")\n", + "current_filter_df = pd.DataFrame({\n", + " 'Status': ['Active Filter'],\n", + " 'SQL WHERE clause': [current_filter if current_filter else 'None']\n", + "})\n", + "display(current_filter_df)\n", + "\n", + "# Clear all filters and show impact\n", + "api.clear_filter(\"hackett_2020\")\n", + "print(f\"\\nAfter clearing filters:\")\n", + "cleared_filter_df = pd.DataFrame({\n", + " 'Status': ['After Clearing'],\n", + " 'SQL WHERE clause': [api.get_current_filter('hackett_2020') or 'None']\n", + "})\n", + "display(cleared_filter_df)\n", + "\n", + "# Query unfiltered vs filtered data comparison\n", + "total_records = api.query(\"SELECT COUNT(*) as total FROM hackett_2020\", \"hackett_2020\")\n", + "\n", + "# Set filters again for comparison\n", + "api.set_filter(\"hackett_2020\", time=15, mechanism=\"ZEV\", restriction=\"P\")\n", + "filtered_records = api.query(\"SELECT COUNT(*) as total FROM hackett_2020\", \"hackett_2020\")\n", + "\n", + "# Create comprehensive comparison table\n", + "comparison_results = pd.DataFrame({\n", + " 'Dataset State': [\n", + " 'Unfiltered (Full Dataset)',\n", + " 'Filtered (time=15, ZEV, P)',\n", + " 'Data Reduction'\n", + " ],\n", + " 'Total Records': [\n", + " f\"{total_records.iloc[0]['total']:,}\",\n", + " f\"{filtered_records.iloc[0]['total']:,}\",\n", + " f\"{total_records.iloc[0]['total'] - filtered_records.iloc[0]['total']:,}\"\n", + " ],\n", + " 'Percentage': [\n", + " '100.0%',\n", + " f\"{(filtered_records.iloc[0]['total'] / total_records.iloc[0]['total'] * 100):.1f}%\",\n", + " f\"{((total_records.iloc[0]['total'] - filtered_records.iloc[0]['total']) / total_records.iloc[0]['total'] * 100):.1f}%\"\n", + " ]\n", + "})\n", + "\n", + "print(\"\\n📊 Dataset Size Comparison:\")\n", + "display(comparison_results)\n", + "\n", + "# Show filter workflow summary\n", + "workflow_summary = pd.DataFrame({\n", + " 'Step': [\n", + " '1. Explore Metadata',\n", + " '2. Set Simple Filters', \n", + " '3. Set Complex SQL Filters',\n", + " '4. Query with Auto-Apply',\n", + " '5. Clear/Manage Filters'\n", + " ],\n", + " 'Method': [\n", + " 'api.get_metadata()',\n", + " 'api.set_filter(config, **kwargs)',\n", + " 'api.set_sql_filter(config, sql)',\n", + " 'api.query(sql, config)',\n", + " 'api.clear_filter() / get_current_filter()'\n", + " ],\n", + " 'Purpose': [\n", + " 'Understand dataset structure',\n", + " 'Filter by metadata values',\n", + " 'Complex multi-condition filtering',\n", + " 'Analyze with automatic filtering',\n", + " 'Reset or inspect current state'\n", + " ]\n", + "})\n", + "\n", + "print(\"\\nComplete HfQueryAPI Workflow:\")\n", + "display(workflow_summary)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "tfbpapi-py3.11", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/mkdocs.yml b/mkdocs.yml index 85960a2..e6fd78e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -150,6 +150,8 @@ nav: - Home: index.md - Tutorials: - "DataCard: Exploring Datasets": tutorials/datacard_tutorial.ipynb + - "Cache Management": tutorials/cache_manager_tutorial.ipynb + - "Querying the Datasets": tutorials/hfqueryapi_tutorial.ipynb - API Reference: - Core Components: - HfQueryAPI: HfQueryAPI.md diff --git a/tfbpapi/HfCacheManager.py b/tfbpapi/HfCacheManager.py index 95a0e02..d8b9ad9 100644 --- a/tfbpapi/HfCacheManager.py +++ b/tfbpapi/HfCacheManager.py @@ -1,17 +1,247 @@ import logging from datetime import datetime, timedelta -from typing import Literal +from pathlib import Path +from typing import Any, Literal -from huggingface_hub import scan_cache_dir +import duckdb +from huggingface_hub import scan_cache_dir, try_to_load_from_cache from huggingface_hub.utils import DeleteCacheStrategy +from .datainfo.datacard import DataCard -class HFCacheManager: - """Cache memory management for Hugging Face Hub cache.""" - def __init__(self, logger: logging.Logger | None = None): +class HfCacheManager(DataCard): + """Enhanced cache management for Hugging Face Hub with metadata-focused + retrieval.""" + + def __init__( + self, + repo_id: str, + duckdb_conn: duckdb.DuckDBPyConnection, + token: str | None = None, + logger: logging.Logger | None = None, + ): + super().__init__(repo_id, token) + self.duckdb_conn = duckdb_conn self.logger = logger or logging.getLogger(__name__) + def _get_metadata_for_config(self, config) -> dict[str, Any]: + """Get metadata for a specific configuration using 3-case strategy.""" + config_result = { + "config_name": config.config_name, + "strategy": None, + "table_name": None, + "success": False, + "message": "", + } + + table_name = f"metadata_{config.config_name}" + + try: + # Case 1: Check if metadata already exists in DuckDB + if self._check_metadata_exists_in_duckdb(table_name): + config_result.update( + { + "strategy": "duckdb_exists", + "table_name": table_name, + "success": True, + "message": f"Metadata table {table_name} " + "already exists in DuckDB", + } + ) + return config_result + + # Case 2: Check if HF data is in cache, create DuckDB representation + if self._load_metadata_from_cache(config, table_name): + config_result.update( + { + "strategy": "cache_loaded", + "table_name": table_name, + "success": True, + "message": "Loaded metadata from cache " + f"into table {table_name}", + } + ) + return config_result + + # Case 3: Download from HF (explicit vs embedded) + if self._download_and_load_metadata(config, table_name): + config_result.update( + { + "strategy": "downloaded", + "table_name": table_name, + "success": True, + "message": "Downloaded and loaded metadata " + f"into table {table_name}", + } + ) + return config_result + + config_result["message"] = ( + f"Failed to retrieve metadata for {config.config_name}" + ) + + except Exception as e: + config_result["message"] = f"Error processing {config.config_name}: {e}" + self.logger.error(f"Error in metadata config {config.config_name}: {e}") + + return config_result + + def _check_metadata_exists_in_duckdb(self, table_name: str) -> bool: + """Case 1: Check if metadata table already exists in DuckDB database.""" + try: + # Query information schema to check if table exists + result = self.duckdb_conn.execute( + "SELECT table_name FROM information_schema.tables WHERE table_name = ?", + [table_name], + ).fetchone() + + exists = result is not None + if exists: + self.logger.debug(f"Table {table_name} already exists in DuckDB") + return exists + + except Exception as e: + self.logger.debug(f"Error checking DuckDB table existence: {e}") + return False + + def _load_metadata_from_cache(self, config, table_name: str) -> bool: + """Case 2: HF data in cache, create DuckDB representation.""" + try: + # Check if metadata files are cached locally + cached_files = [] + for data_file in config.data_files: + cached_path = try_to_load_from_cache( + repo_id=self.repo_id, + filename=data_file.path, + repo_type="dataset", + ) + + if isinstance(cached_path, str) and Path(cached_path).exists(): + cached_files.append(cached_path) + + if not cached_files: + self.logger.debug(f"No cached files found for {config.config_name}") + return False + + # Load cached parquet files into DuckDB + self._create_duckdb_table_from_files(cached_files, table_name) + self.logger.info( + f"Loaded {len(cached_files)} cached files into {table_name}" + ) + return True + + except Exception as e: + self.logger.debug(f"Error loading from cache for {config.config_name}: {e}") + return False + + def _download_and_load_metadata(self, config, table_name: str) -> bool: + """Case 3: Download from HF (explicit vs embedded).""" + try: + from huggingface_hub import snapshot_download + + # Download specific files for this metadata config + file_patterns = [data_file.path for data_file in config.data_files] + + downloaded_path = snapshot_download( + repo_id=self.repo_id, + repo_type="dataset", + allow_patterns=file_patterns, + token=self.token, + ) + + # Find downloaded parquet files + downloaded_files = [] + for pattern in file_patterns: + file_path = Path(downloaded_path) / pattern + if file_path.exists() and file_path.suffix == ".parquet": + downloaded_files.append(str(file_path)) + else: + # Handle wildcard patterns + parent_dir = Path(downloaded_path) / Path(pattern).parent + if parent_dir.exists(): + downloaded_files.extend( + [str(f) for f in parent_dir.glob("*.parquet")] + ) + + if not downloaded_files: + self.logger.warning( + f"No parquet files found after download for {config.config_name}" + ) + return False + + # Load downloaded files into DuckDB + self._create_duckdb_table_from_files(downloaded_files, table_name) + self.logger.info( + f"Downloaded and loaded {len(downloaded_files)} files into {table_name}" + ) + return True + + except Exception as e: + self.logger.error( + f"Error downloading metadata for {config.config_name}: {e}" + ) + return False + + def _create_duckdb_table_from_files( + self, file_paths: list[str], table_name: str + ) -> None: + """Create DuckDB table/view from parquet files.""" + if len(file_paths) == 1: + # Single file + create_sql = f""" + CREATE OR REPLACE VIEW {table_name} AS + SELECT * FROM read_parquet('{file_paths[0]}') + """ + else: + # Multiple files + files_str = "', '".join(file_paths) + create_sql = f""" + CREATE OR REPLACE VIEW {table_name} AS + SELECT * FROM read_parquet(['{files_str}']) + """ + + self.duckdb_conn.execute(create_sql) + self.logger.debug( + f"Created DuckDB view {table_name} from {len(file_paths)} files" + ) + + def _extract_embedded_metadata_field( + self, data_table_name: str, field_name: str, metadata_table_name: str + ) -> bool: + """Extract a specific metadata field from a data table.""" + try: + # Create a metadata view with unique values from the specified field + extract_sql = f""" + CREATE OR REPLACE VIEW {metadata_table_name} AS + SELECT DISTINCT {field_name} as value, COUNT(*) as count + FROM {data_table_name} + WHERE {field_name} IS NOT NULL + GROUP BY {field_name} + ORDER BY count DESC + """ + + self.duckdb_conn.execute(extract_sql) + + # Verify the table was created and has data + count_result = self.duckdb_conn.execute( + f"SELECT COUNT(*) FROM {metadata_table_name}" + ).fetchone() + + if count_result and count_result[0] > 0: + self.logger.info( + f"Extracted {count_result[0]} unique values for {field_name} " + f"into {metadata_table_name}" + ) + return True + else: + self.logger.warning(f"No data found for field {field_name}") + return False + + except Exception as e: + self.logger.error(f"Error extracting field {field_name}: {e}") + return False + def clean_cache_by_age( self, max_age_days: int = 30, diff --git a/tfbpapi/HfQueryAPI.py b/tfbpapi/HfQueryAPI.py index 4eee06d..ddca995 100644 --- a/tfbpapi/HfQueryAPI.py +++ b/tfbpapi/HfQueryAPI.py @@ -1,21 +1,15 @@ import logging -import os -import re from pathlib import Path -from typing import Any, Literal +from typing import Literal -import duckdb import pandas as pd -from datasets import Dataset, DatasetDict, load_dataset -from huggingface_hub import hf_hub_download, snapshot_download -from huggingface_hub.constants import HF_HUB_CACHE -from .errors import RepoTooLargeError -from .HfCacheManager import HFCacheManager +from .constants import CACHE_DIR +from .HfCacheManager import HfCacheManager -class HfQueryAPI: - """Hugging Face API client with intelligent downloading and SQL querying.""" +class HfQueryAPI(HfCacheManager): + """Minimal Hugging Face API client focused on metadata retrieval.""" def __init__( self, @@ -23,136 +17,35 @@ def __init__( repo_type: Literal["model", "dataset", "space"] = "dataset", token: str | None = None, cache_dir: str | Path | None = None, - auto_download_threshold_mb: float = 100.0, - auto_parse_datacard: bool = True, - enable_cache_management: bool = True, - cache_auto_cleanup: bool = False, - cache_max_age_days: int = 30, - cache_max_size: str = "10GB", ): """ - Initialize the HF Query API client. + Initialize the minimal HF Query API client. :param repo_id: Repository identifier (e.g., "user/dataset") :param repo_type: Type of repository ("dataset", "model", "space") :param token: HuggingFace token for authentication :param cache_dir: HF cache_dir for downloads - :param auto_download_threshold_mb: Threshold in MB for auto full download - :param auto_parse_datacard: Whether to auto-parse the datacard on init - :param enable_cache_management: Enable integrated cache management features - :param cache_auto_cleanup: Enable automatic cache cleanup - :param cache_max_age_days: Maximum age in days for cache entries - :param cache_max_size: Maximum total cache size (e.g., "10GB") """ - self.logger = logging.getLogger(self.__class__.__name__) + # No DuckDB connection needed for metadata-only functionality + import duckdb - # Initialize data info manager with new architecture - self.data_info = HfDataInfoManager( - repo_id=repo_id, repo_type=repo_type, token=token - ) - - self.cache_dir = Path( - cache_dir if cache_dir else os.getenv("HF_CACHE_DIR", HF_HUB_CACHE) - ) - self.auto_download_threshold_mb = auto_download_threshold_mb - self._loaded_datasets: dict[str, Dataset | DatasetDict] = {} self._duckdb_conn = duckdb.connect(":memory:") - self._table_filters: dict[str, str] = {} - self._partition_cache: dict[str, set[str]] = {} # Track downloaded partitions - - # Initialize cache management - self._cache_manager = None - if enable_cache_management: - self._cache_manager = HFCacheManager(logger=self.logger) - self._cache_auto_cleanup = cache_auto_cleanup - self._cache_max_age_days = cache_max_age_days - self._cache_max_size = cache_max_size - - if auto_parse_datacard: - try: - self.data_info.parse_datacard() - except Exception as e: - self.logger.warning(f"Failed to auto-parse datacard: {e}") - self.data_info.clear() - - @property - def repo_id(self) -> str: - return self.data_info.repo_id - - @property - def repo_type(self) -> str: - return self.data_info.repo_type - - @property - def token(self) -> str | None: - return self.data_info.token - @property - def datasets(self) -> dict[str, Any]: - """Parsed dataset configurations from the datacard.""" - # Convert TableConfig objects to legacy dictionary format for backward compatibility - result = {} - for name, table_config in self.data_info.datasets.items(): - if table_config: - # Convert FeatureInfo objects to legacy format - features = {} - for feat_name, feat_info in table_config.features.items(): - features[feat_name] = { - "dtype": feat_info.dtype, - "description": feat_info.description, - } - - # Convert DataFileInfo objects to legacy format - data_files = [] - for file_info in table_config.data_files: - data_files.append( - {"path": file_info.path, "split": file_info.split} - ) - - result[name] = { - "features": features, - "data_files": data_files, - "config": table_config.config, - "loaded": table_config.downloaded, # Use "loaded" for backward compatibility - "is_partitioned": table_config.is_partitioned, - } - return result - - @datasets.setter - def datasets(self, value: dict[str, Any]) -> None: - """Set dataset configurations (for backward compatibility).""" - # Clear existing data and register new tables - self.data_info.clear() - # Convert legacy format to TableConfig objects if needed - from .datainfo.models import TableConfig - - table_configs = {} - for name, config in value.items(): - if isinstance(config, TableConfig): - table_configs[name] = config - else: - # Convert from legacy dict format - table_configs[name] = TableConfig( - name=name, - features=config.get("features", {}), - data_files=config.get("data_files", []), - config=config, - downloaded=config.get("loaded", False), # "loaded" was the old name - is_partitioned=config.get("is_partitioned", False), - partition_info=None, - ) - self.data_info._registry.register_tables(table_configs) + # Initialize parent with minimal setup + super().__init__( + repo_id=repo_id, + duckdb_conn=self._duckdb_conn, + token=token, + logger=logging.getLogger(self.__class__.__name__), + ) - @property - def available_tables(self) -> list[str]: - """List of available table names for querying.""" - return self.data_info.available_tables + # Store basic configuration + self.repo_type = repo_type + self.cache_dir = Path(cache_dir) if cache_dir is not None else CACHE_DIR - @property - def cache_manager(self) -> HFCacheManager | None: - """Access to the integrated cache manager for advanced cache operations.""" - return self._cache_manager + # Filter storage system + self._table_filters: dict[str, str] = {} # config_name -> SQL WHERE clause @property def cache_dir(self) -> Path: @@ -166,2025 +59,286 @@ def cache_dir(self, value: str | Path) -> None: raise FileNotFoundError(f"Cache directory {path} does not exist") self._cache_dir = path - @property - def size(self) -> dict[str, Any] | None: - """Size information from the HF Dataset Server API.""" - size_info = self.data_info.get_dataset_size() - if size_info: - return { - "total": size_info.total_bytes, - "total_mb": size_info.total_mb, - "configs": size_info.config_sizes, - } - return None - - @size.setter - def size(self, value: dict[str, Any]) -> None: - """Set size information (for backward compatibility).""" - # Convert legacy format to DatasetSize object - from .datainfo.models import DatasetSize - - dataset_size = DatasetSize.from_hf_size_response(value) - self.data_info._dataset_size = dataset_size - - @property - def snapshot_path(self) -> Path | None: - """Path to the last downloaded snapshot (if any).""" - return getattr(self, "_snapshot_path", None) - - @snapshot_path.setter - def snapshot_path(self, value: str | Path | None) -> None: - self._snapshot_path = None if value is None else Path(value) - - def _build_auth_headers(self) -> dict[str, str]: - """Build authentication headers if token is available.""" - return ( - {"Authorization": f"Bearer {self.data_info.token}"} - if self.data_info.token - else {} - ) - - def _normalize_patterns(self, kwargs: dict[str, Any]) -> None: - """Convert string patterns to lists.""" - for pattern_key in ["allow_patterns", "ignore_patterns"]: - if pattern_key in kwargs and kwargs[pattern_key] is not None: - patterns = kwargs[pattern_key] - if isinstance(patterns, str): - kwargs[pattern_key] = [patterns] - - def download_partitions( - self, table_name: str, partition_values: set[str] | None = None - ) -> Path: - """ - Download specific partitions using the path_template from partitioning metadata. - - :param table_name: Name of the dataset table - :param partition_values: Specific partition values to download (None for all) - :return: Path to downloaded data - + def get_metadata(self, config_name: str | None = None) -> pd.DataFrame: """ - table_config = self.data_info.get_table_or_raise(table_name) - - if not table_config.is_partitioned: - raise ValueError(f"Table {table_name} is not configured as partitioned") - - partition_info = self.data_info.get_partition_info(table_name) - if not partition_info: - raise ValueError(f"Table {table_name} missing partition information") - - path_template = partition_info.get("path_template") - if not path_template: - raise ValueError( - f"Table {table_name} missing required path_template in partitioning config" - ) - - partition_columns = partition_info.get("partition_by", []) - - if partition_values and partition_columns: - # Download specific partitions using path template - patterns = [] - for partition_value in partition_values: - # For single-column partitioning, substitute the first column - if len(partition_columns) == 1: - column = partition_columns[0] - pattern = path_template.replace(f"{{{column}}}", partition_value) - patterns.append(pattern) - else: - # For multi-column partitioning, we'd need more sophisticated logic - # For now, create a wildcard pattern for the specific value - # This is a simplified approach - real implementation would need - # to handle multi-dimensional partition filtering - pattern = path_template - for column in partition_columns: - if f"{{{column}}}" in pattern: - pattern = pattern.replace(f"{{{column}}}", partition_value) - break - patterns.append(pattern) - - self.logger.info( - f"Downloading partitions for {table_name}: {partition_values}" - ) - self.logger.debug(f"Using patterns: {patterns}") - return self.download(allow_patterns=patterns) - else: - # Download all partitions - use the original data files paths - patterns = table_config.get_file_paths() + Retrieve metadata as a DataFrame with actual metadata values. - self.logger.info(f"Downloading all partitions for {table_name}") - return self.download(allow_patterns=patterns) + For explicit metadata (dataset_type == METADATA): Returns all rows from metadata + table. For embedded metadata (has metadata_fields): Returns distinct + combinations of metadata fields. - def download( - self, - files: list[str] | str | None = None, - force_full_download: bool = False, - auto_download_threshold_mb: float = 100.0, - dry_run: bool = False, - **kwargs, - ) -> Path: - """Download dataset with intelligent partitioning support.""" - dataset_size_mb = self.data_info.get_dataset_size_mb() - - if dataset_size_mb <= auto_download_threshold_mb or force_full_download: - self.logger.info( - f"Dataset size ({dataset_size_mb:.2f} MB) is below threshold. " - "Downloading entire repo." - ) - files = None - kwargs.pop("allow_patterns", None) - kwargs.pop("ignore_patterns", None) - elif ( - not files - and not kwargs.get("allow_patterns") - and not kwargs.get("ignore_patterns") - ): - excess_size_mb = dataset_size_mb - auto_download_threshold_mb - raise RepoTooLargeError( - f"Dataset size ({dataset_size_mb:.2f} MB) exceeds threshold by " - f"{excess_size_mb:.2f} MB. Specify files, patterns, or set " - "force_full_download=True." - ) - - # Handle specific file downloads - if files is not None: - if isinstance(files, str) or (isinstance(files, list) and len(files) == 1): - filename = files if isinstance(files, str) else files[0] - return self._download_single_file(filename, dry_run=dry_run, **kwargs) - elif isinstance(files, list) and len(files) > 1: - if kwargs.get("allow_patterns") is not None: - self.logger.warning( - "Both 'files' and 'allow_patterns' provided. Using 'files'." - ) - kwargs["allow_patterns"] = files - - return self._download_snapshot(dry_run=dry_run, **kwargs) - - def _download_single_file( - self, filename: str, dry_run: bool = False, **kwargs - ) -> Path: - """Download a single file using hf_hub_download.""" - self.logger.info(f"Downloading single file: {filename}") - - if dry_run: - self.logger.info(f"[DRY RUN] Would download {filename} from {self.repo_id}") - return Path("dry_run_path") - - hf_kwargs = { - "repo_id": self.repo_id, - "repo_type": self.repo_type, - "filename": filename, - "token": self.token, - **kwargs, - } - - if "local_dir" not in hf_kwargs and self.cache_dir is not None: - hf_kwargs["cache_dir"] = str(self.cache_dir) - - for key in ["local_dir", "cache_dir"]: - if key in hf_kwargs and hf_kwargs[key] is not None: - hf_kwargs[key] = str(hf_kwargs[key]) - file_path = hf_hub_download(**hf_kwargs) - self.snapshot_path = Path(file_path).parent - return Path(file_path) - - def _download_snapshot(self, dry_run: bool = False, **kwargs) -> Path: - """Download repository snapshot using snapshot_download.""" - if dry_run: - self.logger.info(f"[DRY RUN] Would download from {self.repo_id}:") - self.logger.info(f" - allow_patterns: {kwargs.get('allow_patterns')}") - self.logger.info(f" - ignore_patterns: {kwargs.get('ignore_patterns')}") - return Path("dry_run_path") - - self.logger.info( - f"Downloading repo snapshot - " - f"allow: {kwargs.get('allow_patterns')}, " - f"ignore: {kwargs.get('ignore_patterns')}" - ) + :param config_name: Optional specific config name, otherwise returns all + metadata + :return: DataFrame with metadata values + :raises ValueError: If config_name is specified but not found - snapshot_kwargs = { - "repo_id": self.repo_id, - "repo_type": self.repo_type, - "token": self.token, - **kwargs, - } - - if ( - "local_dir" not in snapshot_kwargs - and "cache_dir" not in snapshot_kwargs - and self.cache_dir is not None - ): - snapshot_kwargs["cache_dir"] = str(self.cache_dir) - - self._normalize_patterns(snapshot_kwargs) - snapshot_path = snapshot_download(**snapshot_kwargs) - self.snapshot_path = Path(snapshot_path) - return self.snapshot_path - - def _update_available_tables(self) -> None: - """Update the logger with information about available tables.""" - if self.data_info.available_tables: - self.logger.info(f"Available tables: {', '.join(self.available_tables)}") - - def _extract_table_references(self, sql: str) -> set[str]: """ - Extract all table references from a SQL query. + # Get explicit metadata configurations + explicit_metadata_configs = self.dataset_card.get_metadata_configs() - Handles FROM clauses, JOINs, subqueries, and direct parquet file references. + # Get data configurations that have embedded metadata + # (metadata_fields specified) + embedded_metadata_configs = [ + config + for config in self.dataset_card.get_data_configs() + if config.metadata_fields + ] - :param sql: SQL query string - :return: Set of table names/file references found in the query + # Combine both types + all_metadata_sources = explicit_metadata_configs + embedded_metadata_configs - """ - table_refs = set() - - # Remove comments and normalize whitespace - sql_clean = re.sub(r"--.*?\n", " ", sql, flags=re.DOTALL) - sql_clean = re.sub(r"/\*.*?\*/", " ", sql_clean, flags=re.DOTALL) - sql_clean = re.sub(r"\s+", " ", sql_clean.strip()) - - # Pattern to match table references in FROM and JOIN clauses - # This handles: FROM table, FROM read_parquet('file.parquet'), JOIN table ON... - from_pattern = r""" - (?:FROM|JOIN)\s+ # FROM or JOIN keyword - (?: - read_parquet\s*\(\s*['"]([^'"]+)['"][\s)]* # read_parquet('file.parquet') - | - ([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*) # table.name or tablename - ) - (?:\s+(?:AS\s+)?[a-zA-Z_][a-zA-Z0-9_]*)? # Optional alias - (?:\s+ON\s+.*?(?=\s+(?:FROM|JOIN|WHERE|GROUP|ORDER|LIMIT|$)))? # Optional ON clause for JOINs - """ + if not all_metadata_sources: + # Return empty DataFrame + return pd.DataFrame() - for match in re.finditer(from_pattern, sql_clean, re.IGNORECASE | re.VERBOSE): - parquet_file = match.group(1) - table_name = match.group(2) - - if parquet_file: - # Extract just the filename without extension for parquet files - file_ref = Path(parquet_file).stem - table_refs.add(file_ref) - elif table_name: - # Clean table name (remove schema prefix if present) - clean_name = table_name.split(".")[-1] - table_refs.add(clean_name) - - # Also check for simple table name patterns in case the regex missed something - simple_pattern = r"\b(?:FROM|JOIN)\s+([a-zA-Z_][a-zA-Z0-9_]*)\b" - for match in re.finditer(simple_pattern, sql_clean, re.IGNORECASE): - table_name = match.group(1).lower() - # Filter out SQL keywords and function names - if table_name not in { - "select", - "where", - "group", - "order", - "having", - "limit", - "offset", - "union", - "intersect", - "except", - "read_parquet", - "read_csv", - "read_json", - }: - table_refs.add(table_name) - - return table_refs - - def _resolve_table_to_files(self, table_ref: str) -> list[str]: - """ - Resolve a table reference to specific files that need to be downloaded. + # Filter by config_name if specified + if config_name: + matching_configs = [ + c for c in all_metadata_sources if c.config_name == config_name + ] + if not matching_configs: + available_names = [c.config_name for c in all_metadata_sources] + raise ValueError( + f"Config '{config_name}' not found. " + f"Available metadata configs: {available_names}" + ) + configs_to_process = matching_configs + else: + configs_to_process = all_metadata_sources - :param table_ref: Table name or file reference from SQL - :return: List of file paths/patterns needed for this table + # Process each configuration and collect DataFrames + dataframes = [] + for config in configs_to_process: + # Ensure the data/metadata is loaded + config_result = self._get_metadata_for_config(config) - """ - return self.data_info.resolve_table_to_files(table_ref) - - def _ensure_tables_available(self, sql: str) -> set[str]: - """ - Ensure all tables referenced in SQL are available, downloading if necessary. + if not config_result.get("success", False): + self.logger.warning( + f"Failed to load data for config {config.config_name}" + ) + continue - :param sql: SQL query string - :return: Set of table names that were processed + table_name = config_result.get("table_name") + if not table_name: + self.logger.warning(f"No table name for config {config.config_name}") + continue - """ - table_refs = self._extract_table_references(sql) - processed_tables = set() - - for table_ref in table_refs: - if self.data_info.has_table(table_ref): - # Table is already known from dataset card - if not self.data_info.is_table_downloaded(table_ref): - self._ensure_dataset_loaded(table_ref, sql) - processed_tables.add(table_ref) - else: - # Try to discover as standalone file - if self._try_discover_standalone_table(table_ref): - processed_tables.add(table_ref) + try: + if config in explicit_metadata_configs: + # Explicit metadata: return all rows from metadata table + sql = f"SELECT * FROM {table_name}" else: - # File doesn't exist locally, try to download it - files_needed = self._resolve_table_to_files(table_ref) - if self._try_download_specific_files(files_needed, table_ref): - processed_tables.add(table_ref) - else: - self.logger.warning( - f"Could not locate or download table: {table_ref}" + # Embedded metadata: return distinct combinations of metadata fields + if config.metadata_fields is None: + raise ValueError( + f"Config {config.config_name} has no metadata fields" ) - - return processed_tables - - def _try_download_specific_files(self, files: list[str], table_name: str) -> bool: - """ - Attempt to download specific files for a table. - - :param files: List of file paths/patterns to download - :param table_name: Name of the table these files represent - :return: True if download was successful - - """ - try: - # Try downloading specific files - for file_path in files: - try: - # First check if file exists in repo - downloaded_file_path = self._download_single_file( - file_path, dry_run=False + fields = ", ".join(config.metadata_fields) + where_clauses = " AND ".join( + [f"{field} IS NOT NULL" for field in config.metadata_fields] ) - if downloaded_file_path and downloaded_file_path.exists(): - # Register the file as a table if it's a parquet file - if file_path.endswith(".parquet"): - self._register_parquet_as_table( - downloaded_file_path, table_name - ) - return True - except Exception as e: - self.logger.debug(f"Failed to download {file_path}: {e}") - continue - - # If individual file downloads failed, try pattern-based download - if files: - try: - self.download(allow_patterns=files, force_full_download=False) - # After download, try to discover the table again - return self._try_discover_standalone_table(table_name) - except RepoTooLargeError: - self.logger.error( - f"Repository too large to download files for table: {table_name}" - ) - return False - except Exception as e: - self.logger.error( - f"Failed to download files for table {table_name}: {e}" - ) - return False - - return False - except Exception as e: - self.logger.error(f"Error downloading files for table {table_name}: {e}") - return False - - def _register_parquet_as_table(self, parquet_path: Path, table_name: str) -> None: - """Register a parquet file directly as a DuckDB table.""" - create_view_sql = f""" - CREATE OR REPLACE VIEW {table_name} AS - SELECT * FROM read_parquet('{parquet_path}') - """ - self._duckdb_conn.execute(create_view_sql) + sql = f""" + SELECT DISTINCT {fields}, COUNT(*) as count + FROM {table_name} + WHERE {where_clauses} + GROUP BY {fields} + ORDER BY count DESC + """ - # Add to datasets registry via DataInfo - self.data_info.add_standalone_table(table_name, parquet_path, downloaded=True) + df = self.duckdb_conn.execute(sql).fetchdf() - def _try_discover_standalone_table(self, table_name: str) -> bool: - """ - Try to discover a standalone parquet file as a table. - - :param table_name: The name of the table to discover - :return: True if the table was discovered and registered - - """ - if not self.snapshot_path: - return False - - # Look for parquet file with matching name - parquet_file = self.snapshot_path / f"{table_name}.parquet" - if parquet_file.exists(): - # Register the standalone parquet file with DataInfo - self.data_info.add_standalone_table( - table_name, parquet_file, downloaded=True - ) - - # Register directly with DuckDB - create_view_sql = f""" - CREATE OR REPLACE VIEW {table_name} AS - SELECT * FROM read_parquet('{parquet_file}') - """ - self._duckdb_conn.execute(create_view_sql) - self.logger.debug( - f"Registered standalone parquet file as table: {table_name}" - ) - return True - - return False - - def _ensure_dataset_loaded( - self, table_name: str, sql: str | None = None - ) -> Dataset | DatasetDict | None: - """ - Ensure a dataset is loaded, with intelligent partition downloading. + # Add config source column if multiple configs + if len(configs_to_process) > 1: + df["config_name"] = config.config_name - :param table_name: Name of the dataset configuration - :param sql: Optional SQL query to determine required partitions - :return: The loaded dataset (None for partitioned datasets that are loaded - directly into DuckDB) + dataframes.append(df) - """ - if not self.data_info.has_table(table_name): - # Try to discover the table as a standalone parquet file - if self._try_discover_standalone_table(table_name): - self.logger.info(f"Discovered standalone table: {table_name}") - else: - raise ValueError( - f"Table '{table_name}' not found. " - f"Available tables: {self.available_tables}" + except Exception as e: + self.logger.error( + f"Error querying metadata for {config.config_name}: {e}" ) + continue - dataset_info = self.data_info.get_table_info(table_name) - - # Check if we need to download partitions - if dataset_info and dataset_info.is_partitioned and sql: - required_partitions = self._get_required_partitions(sql, table_name) - - if required_partitions: - # Check if we have these partitions cached - cached_partitions = self._partition_cache.get(table_name, set()) - missing_partitions = required_partitions - cached_partitions - - if missing_partitions: - self.logger.info( - f"Downloading missing partitions: {missing_partitions}" - ) - self._download_partitions(table_name, missing_partitions) - self._partition_cache.setdefault(table_name, set()).update( - missing_partitions - ) + # Combine all DataFrames + if not dataframes: + return pd.DataFrame() + elif len(dataframes) == 1: + return dataframes[0] + else: + return pd.concat(dataframes, ignore_index=True) - # Check if dataset is already loaded - if table_name in self._loaded_datasets: - return self._loaded_datasets[table_name] + def set_filter(self, config_name: str, **kwargs) -> None: + """ + Set simple filters using keyword arguments. - # Check if standalone table is already registered - if dataset_info and dataset_info.downloaded: - return None # Standalone tables are registered directly with DuckDB + Converts keyword arguments to SQL WHERE clause and stores + for automatic application. - # Download if needed - if not self.snapshot_path: - if dataset_info and dataset_info.is_partitioned and sql: - required_partitions = self._get_required_partitions(sql, table_name) - self._download_partitions(table_name, required_partitions) - else: - self.logger.info(f"Downloading dataset for table '{table_name}'...") - self.download( - auto_download_threshold_mb=self.auto_download_threshold_mb - ) + :param config_name: Configuration name to apply filters to + :param kwargs: Filter conditions as keyword arguments + (e.g., time=15, mechanism="ZEV") - # Load the dataset - try: - self.logger.info(f"Loading dataset configuration '{table_name}'...") - - if dataset_info and dataset_info.is_partitioned: - # For partitioned datasets, load directly into DuckDB - self._load_partitioned_dataset(table_name) - # Mark as downloaded but don't store in _loaded_datasets since it's in DuckDB - self.data_info.mark_table_downloaded(table_name, True) - return None - else: - # Standard dataset loading - dataset = load_dataset( - str(self.snapshot_path), name=table_name, keep_in_memory=False - ) - self._loaded_datasets[table_name] = dataset - self._register_dataset_with_duckdb(table_name, dataset) - return dataset - except Exception as e: - self.logger.error(f"Failed to load dataset '{table_name}': {e}") - raise + Example: + api.set_filter("hackett_2020", time=15, mechanism="ZEV", restriction="P") + # Equivalent to: WHERE time = 15 AND mechanism = 'ZEV' AND restriction = 'P' - def _load_partitioned_dataset(self, table_name: str) -> None: """ - Load a partitioned dataset directly into DuckDB without using Hugging Face - datasets. - - This is more efficient for partitioned datasets like genome_map. + if not kwargs: + # If no kwargs provided, clear the filter + self.clear_filter(config_name) + return - """ - # Find parquet files in the snapshot - if not self.snapshot_path: - raise ValueError("No snapshot path available") - - # Look for parquet files matching the dataset pattern - parquet_files: list[Path] = [] - for file_info in self.data_info.get_table_data_files(table_name): - pattern = file_info["path"] - if "*" in pattern: - # Convert pattern to actual file search - search_pattern = pattern.replace("*", "**") - parquet_files.extend(self.snapshot_path.glob(search_pattern)) - - if not parquet_files: - # Fallback: find all parquet files - parquet_files = list(self.snapshot_path.rglob("*.parquet")) - - if parquet_files: - # Register parquet files directly with DuckDB - file_paths = [str(f) for f in parquet_files] - self.logger.info( - f"Registering {len(file_paths)} parquet files for {table_name}" - ) - - # Create a view that reads from all parquet files - files_str = "', '".join(file_paths) - create_view_sql = f""" - CREATE OR REPLACE VIEW {table_name} AS - SELECT * FROM read_parquet(['{files_str}']) - """ - self._duckdb_conn.execute(create_view_sql) - self.logger.debug( - f"Created view '{table_name}' from {len(file_paths)} parquet files" - ) - else: - raise ValueError( - f"No parquet files found for partitioned dataset {table_name}" - ) - - def _register_dataset_with_duckdb( - self, table_name: str, dataset: Dataset | DatasetDict - ) -> None: - """Register a standard dataset with DuckDB for SQL querying.""" - try: - if isinstance(dataset, DatasetDict): - for split_name, split_dataset in dataset.items(): - view_name = ( - f"{table_name}_{split_name}" - if split_name != "train" - else table_name - ) - df = split_dataset.to_pandas() - self._duckdb_conn.register(view_name, df) - self.logger.debug( - f"Registered view '{view_name}' with {len(df)} rows" - ) + # Convert kwargs to SQL WHERE clause + conditions = [] + for key, value in kwargs.items(): + if isinstance(value, str): + # String values need quotes + conditions.append(f"{key} = '{value}'") + elif value is None: + # Handle NULL values + conditions.append(f"{key} IS NULL") else: - df = dataset.to_pandas() - self._duckdb_conn.register(table_name, df) - self.logger.debug( - f"Registered table '{table_name}' with {len(df)} rows" - ) - except Exception as e: - self.logger.error( - f"Failed to register dataset '{table_name}' with DuckDB: {e}" - ) - raise + # Numeric/boolean values + conditions.append(f"{key} = {value}") - def query(self, sql: str, table_name: str | None = None) -> pd.DataFrame: - """ - Execute a SQL query with intelligent table discovery and loading. - - :param sql: SQL query string - :param table_name: Optional table name to ensure is loaded (legacy parameter) - :return: Query results as a pandas DataFrame + where_clause = " AND ".join(conditions) + self._table_filters[config_name] = where_clause + self.logger.info(f"Set filter for {config_name}: {where_clause}") + def set_sql_filter(self, config_name: str, sql_where: str) -> None: """ - # Use intelligent table discovery to ensure all referenced tables are available - processed_tables = self._ensure_tables_available(sql) - - # Legacy support: if table_name is provided, ensure it's also available - if table_name and table_name not in processed_tables: - try: - self._ensure_dataset_loaded(table_name, sql) - except ValueError: - # Try to discover as standalone table - if not self._try_discover_standalone_table(table_name): - self.logger.warning(f"Could not load specified table: {table_name}") - - # Apply table filters - modified_sql = self._apply_table_filters(sql) - - try: - result = self._duckdb_conn.execute(modified_sql).fetchdf() - self.logger.debug(f"Query returned {len(result)} rows") - return result - except Exception as e: - self.logger.error(f"Query failed: {e}") - if modified_sql != sql: - self.logger.debug(f"Original query: {sql}") - self.logger.debug(f"Modified query: {modified_sql}") - - # If query failed, provide helpful information about available tables - if self.data_info.available_tables: - self.logger.info( - f"Available tables: {', '.join(self.data_info.available_tables)}" - ) - - raise - - def _apply_table_filters(self, sql: str) -> str: - """Apply table filters to a SQL query.""" - if not self._table_filters: - return sql - - modified_sql = sql - for table_name, filter_condition in self._table_filters.items(): - pattern = rf"\b{re.escape(table_name)}\b" - replacement = f"(SELECT * FROM {table_name} WHERE {filter_condition})" - - if re.search(pattern, modified_sql, re.IGNORECASE): - if not re.search( - rf"\(SELECT.*FROM\s+{re.escape(table_name)}\s+WHERE", - modified_sql, - re.IGNORECASE, - ): - modified_sql = re.sub( - pattern, replacement, modified_sql, flags=re.IGNORECASE - ) - - return modified_sql - - def get_table_filter(self, table_name: str) -> str | None: - """Get the current filter for a table.""" - return self._table_filters.get(table_name) - - def set_table_filter(self, table_name: str, filter_condition: str) -> None: - """Set a filter condition for a table.""" - self._table_filters[table_name] = filter_condition - self.logger.debug(f"Set filter for table '{table_name}': {filter_condition}") - - def remove_table_filter(self, table_name: str) -> None: - """Remove any filter condition for a table.""" - removed_filter = self._table_filters.pop(table_name, None) - if removed_filter: - self.logger.debug( - f"Removed filter for table '{table_name}': {removed_filter}" - ) + Set complex filters using SQL WHERE clause. - # Standard methods - def describe_table(self, table_name: str) -> pd.DataFrame: - """Get information about a table's structure.""" - self._ensure_dataset_loaded(table_name) - return self.query(f"DESCRIBE {table_name}") + Stores raw SQL WHERE clause for automatic application to queries. - def sample(self, table_name: str, n: int = 5) -> pd.DataFrame: - """Get a sample of rows from a table.""" - return self.query(f"SELECT * FROM {table_name} LIMIT {n}", table_name) + :param config_name: Configuration name to apply filters to + :param sql_where: SQL WHERE clause (without the 'WHERE' keyword) - def count(self, table_name: str) -> int: - """Get the number of rows in a table.""" - result = self.query(f"SELECT COUNT(*) as count FROM {table_name}", table_name) - return result.iloc[0]["count"] + Example: + api.set_sql_filter("hackett_2020", "time IN (15, 30) AND mechanism = 'ZEV'") - def get_columns(self, table_name: str) -> list[str]: - """Get column names for a table.""" - if not self.data_info.has_table(table_name): - raise ValueError(f"Table '{table_name}' not found") - return list(self.data_info.get_table_features(table_name).keys()) - - # ============== Cache Management Methods ============== - - def get_cache_info(self) -> dict[str, Any]: """ - Get comprehensive cache information including current repo details. + if not sql_where.strip(): + self.clear_filter(config_name) + return - :return: Dictionary with cache stats, repo info, and recommendations + self._table_filters[config_name] = sql_where.strip() + self.logger.info(f"Set SQL filter for {config_name}: {sql_where}") + def clear_filter(self, config_name: str) -> None: """ - if not self._cache_manager: - self.logger.debug("Cache management is disabled") - return {"cache_management": "disabled"} - - self.logger.info("Retrieving comprehensive cache information") + Remove all filters for the specified configuration. - from huggingface_hub import scan_cache_dir - - try: - cache_info = scan_cache_dir() - self.logger.debug( - f"Scanned cache directory: {cache_info.size_on_disk_str} in {len(cache_info.repos)} repos" - ) - except Exception as e: - self.logger.error(f"Failed to scan cache directory: {e}") - return {"error": f"Failed to scan cache: {e}"} - - # Find current repo in cache - current_repo_info = None - for repo in cache_info.repos: - if ( - repo.repo_id == self.repo_id - and repo.repo_type.lower() == self.repo_type - ): - current_repo_info = repo - break - - result = { - "cache_management": "enabled", - "cache_directory": str(self.cache_dir), - "total_cache_size": cache_info.size_on_disk_str, - "total_cache_size_bytes": cache_info.size_on_disk, - "total_repos_cached": len(cache_info.repos), - "current_repo": { - "repo_id": self.repo_id, - "repo_type": self.repo_type, - "cached": current_repo_info is not None, - "cache_info": None, - }, - "cache_policies": { - "auto_cleanup": getattr(self, "_cache_auto_cleanup", False), - "max_age_days": getattr(self, "_cache_max_age_days", 30), - "max_size": getattr(self, "_cache_max_size", "10GB"), - }, - "recommendations": [], - } - - if current_repo_info: - result["current_repo"]["cache_info"] = { - "size_on_disk": current_repo_info.size_on_disk_str, - "size_bytes": current_repo_info.size_on_disk, - "nb_files": current_repo_info.nb_files, - "last_accessed": current_repo_info.last_accessed, - "last_modified": current_repo_info.last_modified, - "revisions_count": len(current_repo_info.revisions), - "revisions": [ - { - "commit_hash": rev.commit_hash[:8], - "size_on_disk": rev.size_on_disk_str, - "last_modified": rev.last_modified, - "files_count": len(rev.files), - } - for rev in sorted( - current_repo_info.revisions, - key=lambda r: r.last_modified, - reverse=True, - ) - ], - } + :param config_name: Configuration name to clear filters for - # Add recommendations with logging - max_size_bytes = self._cache_manager._parse_size_string( - getattr(self, "_cache_max_size", "10GB") - ) - if cache_info.size_on_disk > max_size_bytes: - recommendation = ( - f"Cache size ({cache_info.size_on_disk_str}) exceeds configured limit " - f"({getattr(self, '_cache_max_size', '10GB')}). Consider running cache cleanup." - ) - result["recommendations"].append(recommendation) - self.logger.warning(f"Cache size warning: {recommendation}") - - if len(cache_info.repos) > 50: # Arbitrary threshold - recommendation = ( - f"Large number of cached repos ({len(cache_info.repos)}). " - "Consider cleaning unused repositories." - ) - result["recommendations"].append(recommendation) - self.logger.info(f"Cache optimization suggestion: {recommendation}") - - if current_repo_info: - self.logger.info( - f"Current repo {self.repo_id} found in cache: " - f"{current_repo_info.size_on_disk_str}, {len(current_repo_info.revisions)} revisions" - ) - else: - self.logger.debug(f"Current repo {self.repo_id} not found in cache") - - self.logger.info( - f"Cache info summary: {cache_info.size_on_disk_str} total, " - f"{len(cache_info.repos)} repos, {len(result['recommendations'])} recommendations" - ) - - return result - - def get_repo_cache_info(self, repo_id: str | None = None) -> dict[str, Any]: """ - Get detailed cache information for a specific repository. - - :param repo_id: Repository ID (defaults to current repo) - :return: Dictionary with detailed repo cache information + if config_name in self._table_filters: + del self._table_filters[config_name] + self.logger.info(f"Cleared filter for {config_name}") + def get_current_filter(self, config_name: str) -> str | None: """ - if not self._cache_manager: - self.logger.debug("Cache management is disabled") - return {"cache_management": "disabled"} + Get the current filter for the specified configuration. - target_repo_id = repo_id or self.repo_id - self.logger.info( - f"Retrieving detailed cache information for repo: {target_repo_id}" - ) - - from huggingface_hub import scan_cache_dir - - try: - cache_info = scan_cache_dir() - except Exception as e: - self.logger.error(f"Failed to scan cache directory: {e}") - return {"error": f"Failed to scan cache: {e}"} - - # Find the specified repo - target_repo = None - for repo in cache_info.repos: - if repo.repo_id == target_repo_id: - target_repo = repo - break - - if not target_repo: - self.logger.info(f"Repository {target_repo_id} not found in cache") - return { - "repo_id": target_repo_id, - "cached": False, - "message": "Repository not found in cache", - } - - return { - "repo_id": target_repo_id, - "repo_type": target_repo.repo_type, - "cached": True, - "size_on_disk": target_repo.size_on_disk_str, - "size_bytes": target_repo.size_on_disk, - "files_count": target_repo.nb_files, - "last_accessed": target_repo.last_accessed, - "last_modified": target_repo.last_modified, - "revisions_count": len(target_repo.revisions), - "revisions": [ - { - "commit_hash": rev.commit_hash, - "short_hash": rev.commit_hash[:8], - "size_on_disk": rev.size_on_disk_str, - "size_bytes": rev.size_on_disk, - "last_modified": rev.last_modified, - "files_count": len(rev.files), - "files": [ - { - "name": file.file_name, - "size": file.size_on_disk, - "blob_last_accessed": file.blob_last_accessed, - "blob_last_modified": file.blob_last_modified, - } - for file in sorted(rev.files, key=lambda f: f.file_name) - ], - } - for rev in sorted( - target_repo.revisions, key=lambda r: r.last_modified, reverse=True - ) - ], - } - - self.logger.info( - f"Found repo {target_repo_id} in cache: {target_repo.size_on_disk_str}, " - f"{target_repo.nb_files} files, {len(target_repo.revisions)} revisions" - ) + :param config_name: Configuration name to get filter for + :return: Current SQL WHERE clause or None if no filter set - return result - - def check_cached_files(self, table_name: str | None = None) -> dict[str, Any]: """ - Check which files for current dataset/table are cached locally. - - :param table_name: Specific table to check (defaults to all tables) - :return: Dictionary with file cache status + return self._table_filters.get(config_name) + def query(self, sql: str, config_name: str) -> pd.DataFrame: """ - if not self._cache_manager: - self.logger.debug("Cache management is disabled") - return {"cache_management": "disabled"} + Execute SQL query with automatic filter application. - if table_name: - self.logger.info(f"Checking cache status for table: {table_name}") - else: - self.logger.info(f"Checking cache status for all tables in {self.repo_id}") - - from huggingface_hub import _CACHED_NO_EXIST, try_to_load_from_cache - - result = { - "repo_id": self.repo_id, - "cache_directory": str(self.cache_dir), - "tables": {}, - } - - # Check specific table or all tables - tables_to_check = [table_name] if table_name else self.available_tables - self.logger.debug(f"Checking {len(tables_to_check)} tables: {tables_to_check}") - - for table in tables_to_check: - if not self.data_info.has_table(table): - self.logger.warning(f"Table {table} not found in dataset configuration") - result["tables"][table] = { - "exists": False, - "message": "Table not found in dataset configuration", - } - continue - - table_config = self.data_info.get_table_info(table) - if not table_config: - continue - - file_status = {} - for file_info in table_config.data_files: - file_path = file_info.path + Loads the specified configuration, applies any stored filters, + and executes the query. - # Check if file is cached - cached_path = try_to_load_from_cache( - repo_id=self.repo_id, - filename=file_path, - repo_type=self.repo_type, - ) + :param sql: SQL query to execute + :param config_name: Configuration name to query (table will be loaded if needed) + :return: DataFrame with query results + :raises ValueError: If config_name not found or query fails - if isinstance(cached_path, str): - # File is cached - file_status[file_path] = { - "cached": True, - "local_path": cached_path, - "split": file_info.split, - } - elif cached_path is _CACHED_NO_EXIST: - # Non-existence is cached (file doesn't exist on Hub) - file_status[file_path] = { - "cached": False, - "exists_on_hub": False, - "split": file_info.split, - } - else: - # File is not cached - file_status[file_path] = { - "cached": False, - "exists_on_hub": True, # Assumed - "split": file_info.split, - } - - cached_count = sum( - 1 for f in file_status.values() if f.get("cached", False) - ) - total_files = len(table_config.data_files) - - result["tables"][table] = { - "exists": True, - "files": file_status, - "total_files": total_files, - "cached_files": cached_count, - "is_partitioned": table_config.is_partitioned, - } - - self.logger.info( - f"Table {table}: {cached_count}/{total_files} files cached " - f"({cached_count/total_files*100:.1f}%)" - ) - - total_tables = len( - [t for t in result["tables"].values() if t.get("exists", False)] - ) - self.logger.info(f"Cache check complete: processed {total_tables} tables") - - return result - - def cleanup_cache( - self, - strategy: str = "auto", - max_age_days: int | None = None, - target_size: str | None = None, - keep_current_repo: bool = True, - dry_run: bool = True, - ) -> dict[str, Any]: - """ - Clean up the HuggingFace Hub cache using various strategies. - - :param strategy: Cleanup strategy - "auto", "age", "size", "unused" - :param max_age_days: Maximum age for cache entries (for "age" strategy) - :param target_size: Target cache size (for "size" strategy, e.g., "5GB") - :param keep_current_repo: Whether to preserve current repo from cleanup - :param dry_run: If True, show what would be deleted without executing - :return: Dictionary with cleanup results and summary + Example: + api.set_filter("hackett_2020", time=15, mechanism="ZEV") + df = api.query("SELECT regulator_locus_tag, target_locus_tag + FROM hackett_2020", "hackett_2020") + # Automatically applies: WHERE time = 15 AND mechanism = 'ZEV' """ - if not self._cache_manager: - self.logger.warning("Cache management is disabled, cannot perform cleanup") - return { - "cache_management": "disabled", - "message": "Cache management is not enabled", - } - - # Use instance defaults if not specified - max_age_days = max_age_days or getattr(self, "_cache_max_age_days", 30) - target_size = target_size or getattr(self, "_cache_max_size", "10GB") - - self.logger.info( - f"Starting cache cleanup: strategy={strategy}, max_age={max_age_days}d, " - f"target_size={target_size}, dry_run={dry_run}, keep_current_repo={keep_current_repo}" - ) - - result = { - "strategy": strategy, - "dry_run": dry_run, - "keep_current_repo": keep_current_repo, - "strategies_executed": [], - "total_freed_bytes": 0, - "total_freed_str": "0B", - } - - try: - if strategy == "auto": - # Multi-strategy automated cleanup - self.logger.info("Executing automated multi-strategy cleanup") - strategies = self._cache_manager.auto_clean_cache( - max_age_days=max_age_days, - max_total_size=target_size, - keep_latest_per_repo=2, - dry_run=dry_run, - ) - total_freed = sum(s.expected_freed_size for s in strategies) - result["strategies_executed"] = [ - { - "type": "auto_cleanup", - "freed_bytes": total_freed, - "freed_str": self._cache_manager._format_bytes(total_freed), - "details": f"Executed {len(strategies)} cleanup strategies", - } - ] - result["total_freed_bytes"] = total_freed - self.logger.info( - f"Auto cleanup {'would free' if dry_run else 'freed'} " - f"{self._cache_manager._format_bytes(total_freed)} using {len(strategies)} strategies" - ) - - elif strategy == "age": - # Age-based cleanup - self.logger.info( - f"Executing age-based cleanup (older than {max_age_days} days)" - ) - delete_strategy = self._cache_manager.clean_cache_by_age( - max_age_days=max_age_days, dry_run=dry_run - ) - result["strategies_executed"] = [ - { - "type": "age_based", - "freed_bytes": delete_strategy.expected_freed_size, - "freed_str": delete_strategy.expected_freed_size_str, - "max_age_days": max_age_days, - } - ] - result["total_freed_bytes"] = delete_strategy.expected_freed_size - self.logger.info( - f"Age-based cleanup {'would free' if dry_run else 'freed'} " - f"{delete_strategy.expected_freed_size_str}" - ) - - elif strategy == "size": - # Size-based cleanup - self.logger.info( - f"Executing size-based cleanup (target: {target_size})" - ) - delete_strategy = self._cache_manager.clean_cache_by_size( - target_size=target_size, strategy="oldest_first", dry_run=dry_run - ) - result["strategies_executed"] = [ - { - "type": "size_based", - "freed_bytes": delete_strategy.expected_freed_size, - "freed_str": delete_strategy.expected_freed_size_str, - "target_size": target_size, - } - ] - result["total_freed_bytes"] = delete_strategy.expected_freed_size - self.logger.info( - f"Size-based cleanup {'would free' if dry_run else 'freed'} " - f"{delete_strategy.expected_freed_size_str} to reach target {target_size}" - ) - - elif strategy == "unused": - # Clean unused revisions - self.logger.info( - "Executing unused revisions cleanup (keeping 2 latest per repo)" - ) - delete_strategy = self._cache_manager.clean_unused_revisions( - keep_latest=2, dry_run=dry_run - ) - result["strategies_executed"] = [ - { - "type": "unused_revisions", - "freed_bytes": delete_strategy.expected_freed_size, - "freed_str": delete_strategy.expected_freed_size_str, - "keep_latest": 2, - } - ] - result["total_freed_bytes"] = delete_strategy.expected_freed_size - self.logger.info( - f"Unused revisions cleanup {'would free' if dry_run else 'freed'} " - f"{delete_strategy.expected_freed_size_str}" - ) - - else: - self.logger.error(f"Unknown cleanup strategy: {strategy}") - return { - "error": f"Unknown cleanup strategy: {strategy}", - "available_strategies": ["auto", "age", "size", "unused"], - } - - result["total_freed_str"] = self._cache_manager._format_bytes( - result["total_freed_bytes"] - ) - - # Add current repo protection info - if keep_current_repo: - result["current_repo_protected"] = { - "repo_id": self.repo_id, - "message": "Current repository was protected from cleanup", - } - self.logger.debug( - f"Current repository {self.repo_id} protected from cleanup" - ) - - # Final summary logging - self.logger.info( - f"Cache cleanup completed: {strategy} strategy, " - f"{'would free' if dry_run else 'freed'} {result['total_freed_str']}" + # Validate config exists + if config_name not in [c.config_name for c in self.dataset_card.configs]: + available_configs = [c.config_name for c in self.dataset_card.configs] + raise ValueError( + f"Config '{config_name}' not found. " + f"Available configs: {available_configs}" ) - return result + # Load the configuration data + config = self.dataset_card.get_config_by_name(config_name) + if not config: + raise ValueError(f"Could not retrieve config '{config_name}'") - except Exception as e: - self.logger.error(f"Cache cleanup failed: {e}") - return { - "error": f"Cache cleanup failed: {str(e)}", - "strategy": strategy, - "dry_run": dry_run, - } - - def auto_cleanup_cache_if_needed(self) -> dict[str, Any]: - """ - Automatically clean cache if configured policies are exceeded. - - This method is called automatically during operations if auto_cleanup is - enabled. - - :return: Dictionary with cleanup results or None if no cleanup was needed - - """ - if not self._cache_manager or not getattr(self, "_cache_auto_cleanup", False): - self.logger.debug("Auto-cleanup is disabled") - return {"auto_cleanup": "disabled"} - - self.logger.debug("Checking if auto-cleanup is needed") - - from huggingface_hub import scan_cache_dir - - try: - cache_info = scan_cache_dir() - except Exception as e: - self.logger.error(f"Failed to scan cache for auto-cleanup: {e}") - return {"auto_cleanup": "error", "error": str(e)} - max_size_bytes = self._cache_manager._parse_size_string( - getattr(self, "_cache_max_size", "10GB") - ) - - cleanup_needed = cache_info.size_on_disk > max_size_bytes - - if not cleanup_needed: - self.logger.debug( - f"Auto-cleanup not needed: {cache_info.size_on_disk_str} " - f"< {getattr(self, '_cache_max_size', '10GB')}" + config_result = self._get_metadata_for_config(config) + if not config_result.get("success", False): + raise ValueError( + f"Failed to load data for config '{config_name}': " + f"{config_result.get('message', 'Unknown error')}" ) - return { - "auto_cleanup": "enabled", - "cleanup_needed": False, - "current_size": cache_info.size_on_disk_str, - "max_size": getattr(self, "_cache_max_size", "10GB"), - } - - self.logger.info( - f"Auto-cleanup triggered: cache size ({cache_info.size_on_disk_str}) " - f"exceeds limit ({getattr(self, '_cache_max_size', '10GB')})" - ) - cleanup_result = self.cleanup_cache( - strategy="auto", - dry_run=False, # Execute cleanup - keep_current_repo=True, - ) - - cleanup_result.update( - { - "auto_cleanup": "enabled", - "triggered": True, - "reason": "cache_size_exceeded", - "previous_size": cache_info.size_on_disk_str, - } - ) + table_name = config_result.get("table_name") + if not table_name: + raise ValueError(f"No table available for config '{config_name}'") - return cleanup_result - - def suggest_cache_cleanup(self) -> dict[str, Any]: - """ - Analyze cache and provide cleanup recommendations without executing. + # Replace config name with actual table name in SQL for user convenience + sql_with_table = sql.replace(config_name, table_name) - :return: Dictionary with analysis and recommendations + # Apply stored filters + final_sql = self._apply_filter_to_sql(sql_with_table, config_name) - """ - if not self._cache_manager: - return {"cache_management": "disabled"} - - from datetime import datetime, timedelta - - from huggingface_hub import scan_cache_dir - - cache_info = scan_cache_dir() - suggestions = { - "cache_analysis": { - "total_size": cache_info.size_on_disk_str, - "total_repos": len(cache_info.repos), - "recommendations": [], - }, - "cleanup_strategies": {}, - } - - # Analyze size - max_size_bytes = self._cache_manager._parse_size_string( - getattr(self, "_cache_max_size", "10GB") - ) - if cache_info.size_on_disk > max_size_bytes: - suggestions["cache_analysis"]["recommendations"].append( - { - "type": "size_exceeded", - "message": f"Cache size ({cache_info.size_on_disk_str}) exceeds " - f"configured limit ({getattr(self, '_cache_max_size', '10GB')})", - "suggested_action": "Run cleanup_cache(strategy='size')", - } - ) - - # Analyze age - cutoff_date = datetime.now() - timedelta( - days=getattr(self, "_cache_max_age_days", 30) - ) - old_repos = [] - for repo in cache_info.repos: - for revision in repo.revisions: - if datetime.fromtimestamp(revision.last_modified) < cutoff_date: - old_repos.append((repo.repo_id, revision.commit_hash[:8])) - - if old_repos: - suggestions["cache_analysis"]["recommendations"].append( - { - "type": "old_revisions", - "message": f"Found {len(old_repos)} old revisions " - f"(older than {getattr(self, '_cache_max_age_days', 30)} days)", - "suggested_action": "Run cleanup_cache(strategy='age')", - } - ) - - # Analyze unused revisions - repos_with_multiple_revisions = [ - repo for repo in cache_info.repos if len(repo.revisions) > 2 - ] - if repos_with_multiple_revisions: - suggestions["cache_analysis"]["recommendations"].append( - { - "type": "multiple_revisions", - "message": f"Found {len(repos_with_multiple_revisions)} repos " - "with multiple cached revisions", - "suggested_action": "Run cleanup_cache(strategy='unused')", - } - ) - - # Dry run different strategies to show potential savings try: - age_cleanup = self._cache_manager.clean_cache_by_age( - max_age_days=getattr(self, "_cache_max_age_days", 30), dry_run=True - ) - suggestions["cleanup_strategies"]["age_based"] = { - "description": f"Remove revisions older than {getattr(self, '_cache_max_age_days', 30)} days", - "potential_savings": age_cleanup.expected_freed_size_str, - "potential_savings_bytes": age_cleanup.expected_freed_size, - } - - size_cleanup = self._cache_manager.clean_cache_by_size( - target_size=getattr(self, "_cache_max_size", "10GB"), - strategy="oldest_first", - dry_run=True, - ) - suggestions["cleanup_strategies"]["size_based"] = { - "description": f"Reduce cache to {getattr(self, '_cache_max_size', '10GB')}", - "potential_savings": size_cleanup.expected_freed_size_str, - "potential_savings_bytes": size_cleanup.expected_freed_size, - } - - unused_cleanup = self._cache_manager.clean_unused_revisions( - keep_latest=2, dry_run=True - ) - suggestions["cleanup_strategies"]["unused_revisions"] = { - "description": "Remove unused revisions (keep 2 latest per repo)", - "potential_savings": unused_cleanup.expected_freed_size_str, - "potential_savings_bytes": unused_cleanup.expected_freed_size, - } - + return self.duckdb_conn.execute(final_sql).fetchdf() except Exception as e: - suggestions["error"] = f"Failed to analyze cleanup strategies: {e}" - - return suggestions + self.logger.error(f"Query execution failed: {e}") + self.logger.error(f"Final SQL: {final_sql}") + raise ValueError(f"Query execution failed: {e}") from e - def warm_cache( - self, - repo_ids: list[str] | None = None, - tables: list[str] | None = None, - include_current_repo: bool = True, - dry_run: bool = False, - ) -> dict[str, Any]: + def _apply_filter_to_sql(self, sql: str, config_name: str) -> str: """ - Pre-download (warm) cache with specified repositories or tables. + Apply stored filters to SQL query. - :param repo_ids: List of repository IDs to pre-download - :param tables: List of table names from current repo to pre-download - :param include_current_repo: Whether to include current repo if repo_ids - specified - :param dry_run: If True, show what would be downloaded without executing - :return: Dictionary with warming results + Modifies the SQL query to include stored WHERE clause filters. - """ - if not self._cache_manager: - return {"cache_management": "disabled"} - - result = { - "cache_warming": "enabled", - "dry_run": dry_run, - "operations": [], - "total_downloaded": 0, - "errors": [], - } - - # Handle table-specific warming for current repo - if tables: - repo_result = { - "repo_id": self.repo_id, - "type": "table_specific", - "tables": {}, - "success": True, - } - - for table_name in tables: - try: - if not self.data_info.has_table(table_name): - repo_result["tables"][table_name] = { - "status": "error", - "message": "Table not found in dataset configuration", - } - continue - - if not dry_run: - # Download files for this table - self._ensure_dataset_loaded(table_name) - repo_result["tables"][table_name] = { - "status": "downloaded", - "message": "Table files cached successfully", - } - result["total_downloaded"] += 1 - else: - repo_result["tables"][table_name] = { - "status": "would_download", - "message": "Would download table files", - } - - except Exception as e: - error_msg = f"Failed to warm cache for table {table_name}: {e}" - repo_result["tables"][table_name] = { - "status": "error", - "message": str(e), - } - result["errors"].append(error_msg) - repo_result["success"] = False - - result["operations"].append(repo_result) - - # Handle repository-specific warming - if repo_ids or (not tables and include_current_repo): - target_repos = repo_ids or [] - if include_current_repo and self.repo_id not in target_repos: - target_repos.append(self.repo_id) - - for repo_id in target_repos: - repo_result = { - "repo_id": repo_id, - "type": "full_repo", - "success": True, - "message": "", - } - - try: - if not dry_run: - if repo_id == self.repo_id: - # Use current API instance for current repo - downloaded_path = self.download() - repo_result["message"] = ( - f"Repository cached at {downloaded_path}" - ) - else: - # Create temporary API instance for other repos - temp_api = HfQueryAPI( - repo_id=repo_id, - repo_type=self.repo_type, - token=self.token, - cache_dir=self.cache_dir, - enable_cache_management=False, # Avoid recursive cache management - ) - downloaded_path = temp_api.download() - repo_result["message"] = ( - f"Repository cached at {downloaded_path}" - ) - - result["total_downloaded"] += 1 - else: - repo_result["message"] = f"Would download repository {repo_id}" - - except Exception as e: - error_msg = f"Failed to warm cache for repo {repo_id}: {e}" - repo_result["success"] = False - repo_result["message"] = str(e) - result["errors"].append(error_msg) - - result["operations"].append(repo_result) - - if not repo_ids and not tables and not include_current_repo: - result["message"] = "No repositories or tables specified for cache warming" - - return result - - def verify_cache_integrity(self) -> dict[str, Any]: - """ - Verify integrity of cached files and detect corruption. - - :return: Dictionary with verification results + :param sql: Original SQL query + :param config_name: Configuration name to get filters for + :return: Modified SQL query with filters applied """ - if not self._cache_manager: - return {"cache_management": "disabled"} - - import os - - from huggingface_hub import scan_cache_dir - - cache_info = scan_cache_dir() - result = { - "cache_verification": "enabled", - "total_repos_scanned": len(cache_info.repos), - "issues_found": [], - "healthy_repos": [], - "summary": { - "healthy": 0, - "corrupted": 0, - "missing_files": 0, - "symlink_issues": 0, - }, - } - - for repo in cache_info.repos: - repo_issues = [] - - # Check if snapshots directory exists - if not repo.repo_path.exists(): - repo_issues.append( - { - "type": "missing_repo_directory", - "message": f"Repository directory does not exist: {repo.repo_path}", - } - ) - result["summary"]["corrupted"] += 1 - continue + if config_name not in self._table_filters: + return sql - # Check each revision - for revision in repo.revisions: - if not revision.snapshot_path.exists(): - repo_issues.append( - { - "type": "missing_snapshot", - "revision": revision.commit_hash[:8], - "message": f"Snapshot directory missing: {revision.snapshot_path}", - } - ) - continue - - # Check each file in the revision - for file_info in revision.files: - if not file_info.file_path.exists(): - repo_issues.append( - { - "type": "missing_file", - "revision": revision.commit_hash[:8], - "file": file_info.file_name, - "message": f"File missing: {file_info.file_path}", - } - ) - continue - - # Check if it's a symlink and target exists - if file_info.file_path.is_symlink(): - if not file_info.blob_path.exists(): - repo_issues.append( - { - "type": "broken_symlink", - "revision": revision.commit_hash[:8], - "file": file_info.file_name, - "message": f"Symlink target missing: {file_info.blob_path}", - } - ) - continue - - # Basic file size check - try: - actual_size = os.path.getsize(file_info.file_path) - if actual_size != file_info.size_on_disk: - repo_issues.append( - { - "type": "size_mismatch", - "revision": revision.commit_hash[:8], - "file": file_info.file_name, - "expected_size": file_info.size_on_disk, - "actual_size": actual_size, - "message": f"File size mismatch in {file_info.file_name}", - } - ) - except OSError as e: - repo_issues.append( - { - "type": "access_error", - "revision": revision.commit_hash[:8], - "file": file_info.file_name, - "message": f"Cannot access file: {e}", - } - ) + filter_clause = self._table_filters[config_name] + sql_upper = sql.upper() - # Categorize the repo - if repo_issues: - result["issues_found"].append( - { - "repo_id": repo.repo_id, - "repo_type": repo.repo_type, - "issues": repo_issues, - "issues_count": len(repo_issues), - } - ) - - # Update summary - if any( - issue["type"] in ["missing_repo_directory", "missing_snapshot"] - for issue in repo_issues - ): - result["summary"]["corrupted"] += 1 - elif any(issue["type"] == "missing_file" for issue in repo_issues): - result["summary"]["missing_files"] += 1 - elif any(issue["type"] == "broken_symlink" for issue in repo_issues): - result["summary"]["symlink_issues"] += 1 - else: - result["summary"]["corrupted"] += 1 - else: - result["healthy_repos"].append( - { - "repo_id": repo.repo_id, - "repo_type": repo.repo_type, - "size": repo.size_on_disk_str, - } - ) - result["summary"]["healthy"] += 1 - - # Overall health assessment - total_repos = len(cache_info.repos) - if total_repos > 0: - health_percentage = (result["summary"]["healthy"] / total_repos) * 100 - result["overall_health"] = { - "percentage": round(health_percentage, 1), - "status": ( - "healthy" - if health_percentage > 95 - else "warning" if health_percentage > 80 else "critical" - ), - "recommendation": self._get_health_recommendation(health_percentage), - } - - return result - - def _get_health_recommendation(self, health_percentage: float) -> str: - """Get recommendation based on cache health percentage.""" - if health_percentage > 95: - return "Cache is in excellent condition" - elif health_percentage > 80: - return "Cache has minor issues. Consider running cleanup to remove problematic entries" + if "WHERE" in sql_upper: + # SQL already has WHERE clause, append with AND + return f"{sql} AND ({filter_clause})" else: - return "Cache has significant issues. Run cleanup_cache() or consider clearing the cache entirely" - - def migrate_cache(self, new_cache_dir: str | Path) -> dict[str, Any]: - """ - Migrate cache to a new directory location. - - :param new_cache_dir: Target directory for cache migration - :return: Dictionary with migration results - - """ - if not self._cache_manager: - return {"cache_management": "disabled"} - - import shutil - from pathlib import Path - - new_cache_path = Path(new_cache_dir) - current_cache_path = self.cache_dir - - result = { - "cache_migration": "enabled", - "source": str(current_cache_path), - "destination": str(new_cache_path), - "success": False, - "files_migrated": 0, - "errors": [], - } - - try: - # Validate target directory - if new_cache_path.exists() and list(new_cache_path.iterdir()): - return { - **result, - "error": f"Target directory {new_cache_path} is not empty", - "suggestion": "Choose an empty directory or clear the target directory", - } - - # Create target directory if needed - new_cache_path.mkdir(parents=True, exist_ok=True) - - # Get current cache info - from huggingface_hub import scan_cache_dir - - cache_info = scan_cache_dir() - - if not cache_info.repos: - result.update( - { - "success": True, - "message": "No cached repositories to migrate", - } - ) - # Update cache directory - self.cache_dir = new_cache_path - return result - - # Migrate each repository - migrated_repos = [] - for repo in cache_info.repos: - try: - # Create repo structure in new location - repo_name = repo.repo_path.name - new_repo_path = new_cache_path / repo_name - - # Copy entire repository directory - shutil.copytree(repo.repo_path, new_repo_path, symlinks=True) - migrated_repos.append(repo.repo_id) - result["files_migrated"] += repo.nb_files - - except Exception as e: - error_msg = f"Failed to migrate repo {repo.repo_id}: {e}" - result["errors"].append(error_msg) - - if migrated_repos and not result["errors"]: - # Migration successful, update cache directory - self.cache_dir = new_cache_path - result.update( - { - "success": True, - "migrated_repos": migrated_repos, - "repos_count": len(migrated_repos), - "message": f"Successfully migrated {len(migrated_repos)} repositories", - } - ) - elif migrated_repos: - result.update( - { - "success": True, # Partial success - "migrated_repos": migrated_repos, - "repos_count": len(migrated_repos), - "message": f"Partially migrated {len(migrated_repos)} repositories with {len(result['errors'])} errors", - } - ) + # Add WHERE clause + # Find the position to insert WHERE (before ORDER BY, GROUP BY, LIMIT, etc.) + insert_keywords = ["ORDER BY", "GROUP BY", "HAVING", "LIMIT", "OFFSET"] + insert_position = len(sql) + + for keyword in insert_keywords: + pos = sql_upper.find(keyword) + if pos != -1 and pos < insert_position: + insert_position = pos + + if insert_position == len(sql): + # No special clauses, append WHERE at the end + return f"{sql} WHERE {filter_clause}" else: - result.update( - { - "success": False, - "message": "Migration failed completely", - } - ) - - except Exception as e: - result.update( - { - "success": False, - "error": f"Migration failed: {str(e)}", - } - ) - - return result - - # ============== Cache Configuration Management ============== - - def configure_cache_policies( - self, - auto_cleanup: bool | None = None, - max_age_days: int | None = None, - max_size: str | None = None, - save_to_env: bool = False, - ) -> dict[str, Any]: - """ - Configure cache management policies for this instance. - - :param auto_cleanup: Enable/disable automatic cache cleanup - :param max_age_days: Maximum age in days for cache entries - :param max_size: Maximum total cache size (e.g., "10GB") - :param save_to_env: Save configuration to environment variables - :return: Dictionary with updated configuration - - """ - if not self._cache_manager: - return {"cache_management": "disabled"} - - # Update instance configuration - if auto_cleanup is not None: - self._cache_auto_cleanup = auto_cleanup - if max_age_days is not None: - self._cache_max_age_days = max_age_days - if max_size is not None: - self._cache_max_size = max_size - - config = { - "cache_management": "enabled", - "configuration_updated": True, - "policies": { - "auto_cleanup": getattr(self, "_cache_auto_cleanup", False), - "max_age_days": getattr(self, "_cache_max_age_days", 30), - "max_size": getattr(self, "_cache_max_size", "10GB"), - }, - "env_variables_updated": False, - } - - # Save to environment variables if requested - if save_to_env: - import os - - env_vars = {} - - if auto_cleanup is not None: - env_var = "TFBPAPI_CACHE_AUTO_CLEANUP" - os.environ[env_var] = str(auto_cleanup).lower() - env_vars[env_var] = str(auto_cleanup).lower() - - if max_age_days is not None: - env_var = "TFBPAPI_CACHE_MAX_AGE_DAYS" - os.environ[env_var] = str(max_age_days) - env_vars[env_var] = str(max_age_days) - - if max_size is not None: - env_var = "TFBPAPI_CACHE_MAX_SIZE" - os.environ[env_var] = max_size - env_vars[env_var] = max_size - - config.update( - { - "env_variables_updated": True, - "env_variables": env_vars, - "note": "Environment variables set for current session. " - "Add them to your shell profile for persistence.", - } - ) - - return config - - def get_cache_configuration(self) -> dict[str, Any]: - """ - Get current cache configuration including environment variables. - - :return: Dictionary with comprehensive cache configuration - - """ - import os - - config = { - "cache_management": "enabled" if self._cache_manager else "disabled", - "cache_directory": str(self.cache_dir), - "instance_config": {}, - "environment_config": {}, - "effective_config": {}, - } - - if not self._cache_manager: - return config - - # Instance configuration - config["instance_config"] = { - "auto_cleanup": getattr(self, "_cache_auto_cleanup", False), - "max_age_days": getattr(self, "_cache_max_age_days", 30), - "max_size": getattr(self, "_cache_max_size", "10GB"), - } - - # Environment configuration - env_config = {} - env_vars = { - "TFBPAPI_CACHE_AUTO_CLEANUP": "auto_cleanup", - "TFBPAPI_CACHE_MAX_AGE_DAYS": "max_age_days", - "TFBPAPI_CACHE_MAX_SIZE": "max_size", - "HF_CACHE_DIR": "cache_directory", - "HF_HUB_CACHE": "cache_directory_fallback", - } - - for env_var, config_key in env_vars.items(): - value = os.getenv(env_var) - if value: - env_config[config_key] = value - - config["environment_config"] = env_config - - # Effective configuration (environment overrides instance) - effective = config["instance_config"].copy() - - # Apply environment overrides - if "auto_cleanup" in env_config: - effective["auto_cleanup"] = env_config["auto_cleanup"].lower() in ( - "true", - "1", - "yes", - ) - if "max_age_days" in env_config: - try: - effective["max_age_days"] = int(env_config["max_age_days"]) - except ValueError: - pass - if "max_size" in env_config: - effective["max_size"] = env_config["max_size"] - - config["effective_config"] = effective - - return config - - def reset_cache_configuration( - self, remove_env_vars: bool = False - ) -> dict[str, Any]: - """ - Reset cache configuration to defaults. - - :param remove_env_vars: Also remove related environment variables - :return: Dictionary with reset results - - """ - if not self._cache_manager: - return {"cache_management": "disabled"} - - # Reset instance configuration to defaults - self._cache_auto_cleanup = False - self._cache_max_age_days = 30 - self._cache_max_size = "10GB" - - result = { - "cache_management": "enabled", - "configuration_reset": True, - "new_config": { - "auto_cleanup": False, - "max_age_days": 30, - "max_size": "10GB", - }, - "env_variables_removed": [], - } - - # Remove environment variables if requested - if remove_env_vars: - import os - - env_vars_to_remove = [ - "TFBPAPI_CACHE_AUTO_CLEANUP", - "TFBPAPI_CACHE_MAX_AGE_DAYS", - "TFBPAPI_CACHE_MAX_SIZE", - ] - - for env_var in env_vars_to_remove: - if env_var in os.environ: - del os.environ[env_var] - result["env_variables_removed"].append(env_var) - - if result["env_variables_removed"]: - result["note"] = ( - "Environment variables removed from current session. " - "Remove them from your shell profile for permanent effect." + # Insert WHERE before the special clause + return ( + f"{sql[:insert_position].rstrip()} " + f"WHERE {filter_clause} {sql[insert_position:]}" ) - - return result - - def apply_cache_policy_from_env(self) -> dict[str, Any]: - """ - Apply cache policies from environment variables. - - :return: Dictionary with applied configuration - - """ - if not self._cache_manager: - return {"cache_management": "disabled"} - - import os - - applied_config = [] - - # Auto cleanup - auto_cleanup_env = os.getenv("TFBPAPI_CACHE_AUTO_CLEANUP") - if auto_cleanup_env: - self._cache_auto_cleanup = auto_cleanup_env.lower() in ("true", "1", "yes") - applied_config.append(f"auto_cleanup: {self._cache_auto_cleanup}") - - # Max age days - max_age_env = os.getenv("TFBPAPI_CACHE_MAX_AGE_DAYS") - if max_age_env: - try: - self._cache_max_age_days = int(max_age_env) - applied_config.append(f"max_age_days: {self._cache_max_age_days}") - except ValueError: - applied_config.append( - f"max_age_days: invalid value '{max_age_env}' (keeping default)" - ) - - # Max size - max_size_env = os.getenv("TFBPAPI_CACHE_MAX_SIZE") - if max_size_env: - self._cache_max_size = max_size_env - applied_config.append(f"max_size: {self._cache_max_size}") - - return { - "cache_management": "enabled", - "environment_config_applied": True, - "applied_settings": applied_config, - "current_config": { - "auto_cleanup": getattr(self, "_cache_auto_cleanup", False), - "max_age_days": getattr(self, "_cache_max_age_days", 30), - "max_size": getattr(self, "_cache_max_size", "10GB"), - }, - } - - def export_cache_configuration(self, format: str = "env") -> dict[str, Any]: - """ - Export current cache configuration in various formats. - - :param format: Export format - "env", "json", "yaml" - :return: Dictionary with exported configuration - - """ - if not self._cache_manager: - return {"cache_management": "disabled"} - - current_config = { - "auto_cleanup": getattr(self, "_cache_auto_cleanup", False), - "max_age_days": getattr(self, "_cache_max_age_days", 30), - "max_size": getattr(self, "_cache_max_size", "10GB"), - "cache_directory": str(self.cache_dir), - } - - result = { - "cache_management": "enabled", - "format": format, - "configuration": current_config, - } - - if format == "env": - env_lines = [ - f"export TFBPAPI_CACHE_AUTO_CLEANUP={str(current_config['auto_cleanup']).lower()}", - f"export TFBPAPI_CACHE_MAX_AGE_DAYS={current_config['max_age_days']}", - f"export TFBPAPI_CACHE_MAX_SIZE={current_config['max_size']}", - f"export HF_CACHE_DIR={current_config['cache_directory']}", - ] - result["exported_config"] = "\n".join(env_lines) - result["usage"] = "Add these lines to your ~/.bashrc or ~/.zshrc file" - - elif format == "json": - import json - - result["exported_config"] = json.dumps(current_config, indent=2) - - elif format == "yaml": - yaml_lines = [ - "cache_configuration:", - f" auto_cleanup: {str(current_config['auto_cleanup']).lower()}", - f" max_age_days: {current_config['max_age_days']}", - f" max_size: \"{current_config['max_size']}\"", - f" cache_directory: \"{current_config['cache_directory']}\"", - ] - result["exported_config"] = "\n".join(yaml_lines) - - else: - result["error"] = f"Unsupported format: {format}" - result["supported_formats"] = ["env", "json", "yaml"] - - return result - - def close(self) -> None: - """Close the DuckDB connection.""" - if self._duckdb_conn: - self._duckdb_conn.close() - - def __enter__(self): - """Context manager entry.""" - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - """Context manager exit.""" - self.close() diff --git a/tfbpapi/constants.py b/tfbpapi/constants.py new file mode 100644 index 0000000..749678f --- /dev/null +++ b/tfbpapi/constants.py @@ -0,0 +1,11 @@ +import os +from pathlib import Path + +from huggingface_hub.constants import HF_HUB_CACHE + +CACHE_DIR = Path(os.getenv("HF_CACHE_DIR", HF_HUB_CACHE)) + + +def get_hf_token() -> str | None: + """Get HuggingFace token from environment variable.""" + return os.getenv("HF_TOKEN") diff --git a/tfbpapi/datainfo/fetchers.py b/tfbpapi/datainfo/fetchers.py index e33ac05..30cc72a 100644 --- a/tfbpapi/datainfo/fetchers.py +++ b/tfbpapi/datainfo/fetchers.py @@ -1,14 +1,14 @@ """Data fetchers for HuggingFace Hub integration.""" import logging -import os import re -from typing import Any, Dict, List, Optional, Set +from typing import Any import requests from huggingface_hub import DatasetCard, repo_info from requests import HTTPError +from ..constants import get_hf_token from ..errors import HfDataFetchError @@ -23,7 +23,7 @@ def __init__(self, token: str | None = None): """ self.logger = logging.getLogger(self.__class__.__name__) - self.token = token or os.getenv("HF_TOKEN") + self.token = token or get_hf_token() def fetch(self, repo_id: str, repo_type: str = "dataset") -> dict[str, Any]: """ @@ -62,7 +62,7 @@ def __init__(self, token: str | None = None): """ self.logger = logging.getLogger(self.__class__.__name__) - self.token = token or os.getenv("HF_TOKEN") + self.token = token or get_hf_token() self.base_url = "https://datasets-server.huggingface.co" def _build_headers(self) -> dict[str, str]: @@ -129,7 +129,7 @@ def __init__(self, token: str | None = None): """ self.logger = logging.getLogger(self.__class__.__name__) - self.token = token or os.getenv("HF_TOKEN") + self.token = token or get_hf_token() self._cached_structure: dict[str, dict[str, Any]] = {} def fetch(self, repo_id: str, force_refresh: bool = False) -> dict[str, Any]: diff --git a/tfbpapi/errors.py b/tfbpapi/errors.py index 654d6fc..6952b79 100644 --- a/tfbpapi/errors.py +++ b/tfbpapi/errors.py @@ -1,6 +1,6 @@ """Custom exception classes for dataset management.""" -from typing import Any, Dict, Optional +from typing import Any class DatasetError(Exception): @@ -22,7 +22,8 @@ class RepoTooLargeError(DatasetError): """Raised when repository exceeds auto-download threshold.""" def __init__(self, repo_id: str, size_mb: float, threshold_mb: float): - message = f"Repository '{repo_id}' is too large for auto-download: {size_mb:.2f}MB exceeds {threshold_mb}MB threshold" + message = f"Repository '{repo_id}' is too large for " + f"auto-download: {size_mb:.2f}MB exceeds {threshold_mb}MB threshold" super().__init__( message, details={ @@ -46,7 +47,7 @@ def __init__( config_name: str | None = None, original_error: Exception | None = None, ): - details = {} + details: dict[str, Any] = {} if repo_id: details["repo_id"] = repo_id if config_name: @@ -70,7 +71,7 @@ def __init__( status_code: int | None = None, endpoint: str | None = None, ): - details = {} + details: dict[str, Any] = {} if repo_id: details["repo_id"] = repo_id if status_code: @@ -184,7 +185,7 @@ def __init__( repo_id: str | None = None, validation_errors: list | None = None, ): - details = {} + details: dict[str, Any] = {} if repo_id: details["repo_id"] = repo_id if validation_errors: @@ -204,7 +205,7 @@ def __init__( config_name: str | None = None, field_name: str | None = None, ): - details = {} + details: dict[str, Any] = {} if config_name: details["config_name"] = config_name if field_name: diff --git a/tfbpapi/tests/test_HfCacheManager.py b/tfbpapi/tests/test_HfCacheManager.py index 0a4e472..e044150 100644 --- a/tfbpapi/tests/test_HfCacheManager.py +++ b/tfbpapi/tests/test_HfCacheManager.py @@ -1,55 +1,519 @@ -from tfbpapi.HfCacheManager import HFCacheManager +"""Comprehensive tests for HfCacheManager class.""" +import logging +from datetime import datetime, timedelta +from unittest.mock import Mock, patch -def test_parse_size_string(): - """Test size string parsing.""" - cache_manager = HFCacheManager() - assert cache_manager._parse_size_string("10KB") == 10 * 1024 - assert cache_manager._parse_size_string("5MB") == 5 * 1024**2 - assert cache_manager._parse_size_string("2GB") == 2 * 1024**3 - assert cache_manager._parse_size_string("1TB") == 1 * 1024**4 - assert cache_manager._parse_size_string("500") == 500 +import duckdb +import pytest +from tfbpapi.datainfo.models import DatasetType +from tfbpapi.HfCacheManager import HfCacheManager -def test_clean_cache_by_age(mock_scan_cache_dir): - """Test age-based cache cleaning.""" - cache_manager = HFCacheManager() +class TestHfCacheManagerInit: + """Test HfCacheManager initialization.""" - # This will use the mocked scan_cache_dir - strategy = cache_manager.clean_cache_by_age(max_age_days=1, dry_run=True) + def test_init_basic(self): + """Test basic initialization.""" + conn = duckdb.connect(":memory:") + repo_id = "test/repo" - # Test your logic - assert strategy is not None or len(mock_scan_cache_dir.repos) == 0 - if strategy: - assert len(strategy.repos) >= 0 - assert strategy.expected_freed_size >= 0 + with patch( + "tfbpapi.HfCacheManager.DataCard.__init__", return_value=None + ) as mock_datacard_init: + cache_manager = HfCacheManager(repo_id, conn) + # Manually set the properties that would normally + # be set by DataCard.__init__ + cache_manager.repo_id = repo_id + cache_manager.token = None + assert cache_manager.repo_id == repo_id + assert cache_manager.duckdb_conn == conn + assert cache_manager.token is None + assert cache_manager.logger is not None + # DataCard should be initialized as parent + mock_datacard_init.assert_called_once_with(repo_id, None) -def test_clean_cache_by_size(mock_scan_cache_dir): - """Test size-based cache cleaning.""" + def test_init_with_token_and_logger(self): + """Test initialization with token and custom logger.""" + conn = duckdb.connect(":memory:") + repo_id = "test/repo" + token = "test_token" + logger = logging.getLogger("test_logger") - cache_manager = HFCacheManager() + with patch( + "tfbpapi.HfCacheManager.DataCard.__init__", return_value=None + ) as mock_datacard_init: + cache_manager = HfCacheManager(repo_id, conn, token=token, logger=logger) + # Manually set the properties that would + # normally be set by DataCard.__init__ + cache_manager.repo_id = repo_id + cache_manager.token = token - # Test with a very small target to force cleanup - strategy = cache_manager.clean_cache_by_size(target_size="1GB", dry_run=True) + assert cache_manager.repo_id == repo_id + assert cache_manager.duckdb_conn == conn + assert cache_manager.token == token + assert cache_manager.logger == logger + # DataCard should be initialized as parent with token + mock_datacard_init.assert_called_once_with(repo_id, token) - if mock_scan_cache_dir.size_on_disk > 1024: # If cache has data - assert strategy is not None - assert len(strategy.repos) > 0 +class TestHfCacheManagerDatacard: + """Test DataCard integration since HfCacheManager now inherits from DataCard.""" -def test_auto_clean_cache(mock_scan_cache_dir): - """Test automated cache cleaning.""" + def test_datacard_inheritance(self): + """Test that HfCacheManager properly inherits from DataCard.""" + conn = duckdb.connect(":memory:") + repo_id = "test/repo" + token = "test_token" - cache_manager = HFCacheManager() + with patch( + "tfbpapi.HfCacheManager.DataCard.__init__", return_value=None + ) as mock_datacard_init: + cache_manager = HfCacheManager(repo_id, conn, token=token) - strategies = cache_manager.auto_clean_cache( - max_age_days=1, - max_total_size="1KB", - keep_latest_per_repo=1, - dry_run=True, + # DataCard should be initialized during construction + mock_datacard_init.assert_called_once_with(repo_id, token) + + # Should have DataCard methods available (they exist on the class) + assert hasattr(cache_manager, "get_config") + assert hasattr(cache_manager, "get_configs_by_type") + + +class TestHfCacheManagerDuckDBOperations: + """Test DuckDB operations that are still part of HfCacheManager.""" + + @patch("tfbpapi.HfCacheManager.DataCard.__init__", return_value=None) + def test_create_duckdb_table_from_files_single_file( + self, mock_datacard_init, tmpdir + ): + """Test creating DuckDB table from single parquet file.""" + # Create a mock parquet file + parquet_file = tmpdir.join("test.parquet") + parquet_file.write("dummy_content") + + # Use a separate cache manager with mock connection for this test + mock_conn = Mock() + test_cache_manager = HfCacheManager("test/repo", mock_conn) + + test_cache_manager._create_duckdb_table_from_files( + [str(parquet_file)], "test_table" + ) + + mock_conn.execute.assert_called_once() + sql_call = mock_conn.execute.call_args[0][0] + assert "CREATE OR REPLACE VIEW test_table" in sql_call + assert str(parquet_file) in sql_call + + @patch("tfbpapi.HfCacheManager.DataCard.__init__", return_value=None) + def test_create_duckdb_table_from_files_multiple_files( + self, mock_datacard_init, tmpdir + ): + """Test creating DuckDB table from multiple parquet files.""" + # Create mock parquet files + file1 = tmpdir.join("test1.parquet") + file1.write("dummy_content1") + file2 = tmpdir.join("test2.parquet") + file2.write("dummy_content2") + + files = [str(file1), str(file2)] + + # Use a separate cache manager with mock connection for this test + mock_conn = Mock() + test_cache_manager = HfCacheManager("test/repo", mock_conn) + + test_cache_manager._create_duckdb_table_from_files(files, "test_table") + + mock_conn.execute.assert_called_once() + sql_call = mock_conn.execute.call_args[0][0] + assert "CREATE OR REPLACE VIEW test_table" in sql_call + assert str(file1) in sql_call + assert str(file2) in sql_call + + +class TestHfCacheManagerCacheManagement: + """Test cache management functionality.""" + + def setup_method(self): + """Set up test fixtures.""" + with patch("tfbpapi.HfCacheManager.DataCard.__init__", return_value=None): + self.conn = duckdb.connect(":memory:") + self.repo_id = "test/repo" + self.cache_manager = HfCacheManager(self.repo_id, self.conn) + + def test_parse_size_string(self): + """Test size string parsing.""" + assert self.cache_manager._parse_size_string("10KB") == 10 * 1024 + assert self.cache_manager._parse_size_string("5MB") == 5 * 1024**2 + assert self.cache_manager._parse_size_string("2GB") == 2 * 1024**3 + assert self.cache_manager._parse_size_string("1TB") == 1 * 1024**4 + assert self.cache_manager._parse_size_string("500") == 500 + assert self.cache_manager._parse_size_string("10.5GB") == int(10.5 * 1024**3) + + def test_format_bytes(self): + """Test byte formatting.""" + assert self.cache_manager._format_bytes(0) == "0B" + assert self.cache_manager._format_bytes(1023) == "1023.0B" + assert self.cache_manager._format_bytes(1024) == "1.0KB" + assert self.cache_manager._format_bytes(1024**2) == "1.0MB" + assert self.cache_manager._format_bytes(1024**3) == "1.0GB" + assert self.cache_manager._format_bytes(1024**4) == "1.0TB" + + @patch("tfbpapi.HfCacheManager.scan_cache_dir") + def test_clean_cache_by_age(self, mock_scan_cache_dir): + """Test age-based cache cleaning.""" + # Setup mock cache info + mock_cache_info = Mock() + mock_revision = Mock() + mock_revision.commit_hash = "abc123" + mock_revision.last_modified = (datetime.now() - timedelta(days=35)).timestamp() + + mock_repo = Mock() + mock_repo.revisions = [mock_revision] + + mock_cache_info.repos = [mock_repo] + mock_delete_strategy = Mock() + mock_delete_strategy.expected_freed_size_str = "100MB" + mock_cache_info.delete_revisions.return_value = mock_delete_strategy + + mock_scan_cache_dir.return_value = mock_cache_info + + result = self.cache_manager.clean_cache_by_age(max_age_days=30, dry_run=True) + + assert result == mock_delete_strategy + mock_cache_info.delete_revisions.assert_called_once_with("abc123") + + @patch("tfbpapi.HfCacheManager.scan_cache_dir") + def test_clean_cache_by_age_no_old_revisions(self, mock_scan_cache_dir): + """Test age-based cleaning when no old revisions exist.""" + mock_cache_info = Mock() + mock_revision = Mock() + mock_revision.commit_hash = "abc123" + mock_revision.last_modified = datetime.now().timestamp() # Recent + + mock_repo = Mock() + mock_repo.revisions = [mock_revision] + + mock_cache_info.repos = [mock_repo] + mock_delete_strategy = Mock() + mock_delete_strategy.expected_freed_size_str = "0B" + mock_cache_info.delete_revisions.return_value = mock_delete_strategy + + mock_scan_cache_dir.return_value = mock_cache_info + + result = self.cache_manager.clean_cache_by_age(max_age_days=30, dry_run=True) + + # Should still return a strategy, but with empty revisions + assert result == mock_delete_strategy + mock_cache_info.delete_revisions.assert_called_once_with() + + @patch("tfbpapi.HfCacheManager.scan_cache_dir") + def test_clean_cache_by_size(self, mock_scan_cache_dir): + """Test size-based cache cleaning.""" + # Setup mock cache info + mock_cache_info = Mock() + mock_cache_info.size_on_disk = 5 * 1024**3 # 5GB + mock_cache_info.size_on_disk_str = "5.0GB" + + mock_revision = Mock() + mock_revision.commit_hash = "abc123" + mock_revision.last_modified = datetime.now().timestamp() + mock_revision.size_on_disk = 2 * 1024**3 # 2GB + + mock_repo = Mock() + mock_repo.revisions = [mock_revision] + + mock_cache_info.repos = [mock_repo] + mock_delete_strategy = Mock() + mock_delete_strategy.expected_freed_size_str = "2GB" + mock_cache_info.delete_revisions.return_value = mock_delete_strategy + + mock_scan_cache_dir.return_value = mock_cache_info + + result = self.cache_manager.clean_cache_by_size( + target_size="3GB", strategy="oldest_first", dry_run=True + ) + + assert result == mock_delete_strategy + mock_cache_info.delete_revisions.assert_called_once() + + @patch("tfbpapi.HfCacheManager.scan_cache_dir") + def test_clean_cache_by_size_already_under_target(self, mock_scan_cache_dir): + """Test size-based cleaning when already under target.""" + mock_cache_info = Mock() + mock_cache_info.size_on_disk = 1 * 1024**3 # 1GB + mock_cache_info.size_on_disk_str = "1.0GB" + mock_cache_info.repos = [] + + mock_delete_strategy = Mock() + mock_delete_strategy.expected_freed_size_str = "0B" + mock_cache_info.delete_revisions.return_value = mock_delete_strategy + + mock_scan_cache_dir.return_value = mock_cache_info + + result = self.cache_manager.clean_cache_by_size( + target_size="2GB", strategy="oldest_first", dry_run=True + ) + + assert result == mock_delete_strategy + + @patch("tfbpapi.HfCacheManager.scan_cache_dir") + def test_clean_unused_revisions(self, mock_scan_cache_dir): + """Test cleaning unused revisions.""" + # Setup mock with multiple revisions + mock_cache_info = Mock() + + mock_revision1 = Mock() + mock_revision1.commit_hash = "abc123" + mock_revision1.last_modified = (datetime.now() - timedelta(days=1)).timestamp() + + mock_revision2 = Mock() + mock_revision2.commit_hash = "def456" + mock_revision2.last_modified = (datetime.now() - timedelta(days=10)).timestamp() + + mock_revision3 = Mock() + mock_revision3.commit_hash = "ghi789" + mock_revision3.last_modified = (datetime.now() - timedelta(days=20)).timestamp() + + mock_repo = Mock() + mock_repo.revisions = [mock_revision1, mock_revision2, mock_revision3] + + mock_cache_info.repos = [mock_repo] + mock_delete_strategy = Mock() + mock_delete_strategy.expected_freed_size_str = "1GB" + mock_cache_info.delete_revisions.return_value = mock_delete_strategy + + mock_scan_cache_dir.return_value = mock_cache_info + + result = self.cache_manager.clean_unused_revisions(keep_latest=2, dry_run=True) + + assert result == mock_delete_strategy + # Should delete oldest revision (ghi789) + mock_cache_info.delete_revisions.assert_called_once_with("ghi789") + + @patch("tfbpapi.HfCacheManager.scan_cache_dir") + def test_auto_clean_cache(self, mock_scan_cache_dir): + """Test automated cache cleaning.""" + mock_cache_info = Mock() + mock_cache_info.size_on_disk = 10 * 1024**3 # 10GB + mock_cache_info.repos = [] + + mock_delete_strategy = Mock() + mock_delete_strategy.expected_freed_size = 1 * 1024**3 # 1GB + mock_delete_strategy.expected_freed_size_str = "1GB" + + mock_scan_cache_dir.return_value = mock_cache_info + + with patch.object( + self.cache_manager, "clean_cache_by_age", return_value=mock_delete_strategy + ): + with patch.object( + self.cache_manager, + "clean_unused_revisions", + return_value=mock_delete_strategy, + ): + with patch.object( + self.cache_manager, + "clean_cache_by_size", + return_value=mock_delete_strategy, + ): + result = self.cache_manager.auto_clean_cache( + max_age_days=30, + max_total_size="5GB", + keep_latest_per_repo=2, + dry_run=True, + ) + + assert ( + len(result) == 3 + ) # All three cleanup strategies should be executed + assert all(strategy == mock_delete_strategy for strategy in result) + + +class TestHfCacheManagerErrorHandling: + """Test error handling and edge cases.""" + + def setup_method(self): + """Set up test fixtures.""" + with patch("tfbpapi.HfCacheManager.DataCard.__init__", return_value=None): + self.conn = duckdb.connect(":memory:") + self.repo_id = "test/repo" + self.cache_manager = HfCacheManager(self.repo_id, self.conn) + + def test_parse_size_string_invalid_input(self): + """Test error handling for invalid size strings.""" + with pytest.raises(ValueError): + self.cache_manager._parse_size_string("invalid") + + @patch("tfbpapi.HfCacheManager.scan_cache_dir") + def test_clean_cache_invalid_strategy(self, mock_scan_cache_dir): + """Test error handling for invalid cleanup strategy.""" + mock_cache_info = Mock() + mock_cache_info.size_on_disk = 5 * 1024**3 + mock_cache_info.repos = [] + mock_scan_cache_dir.return_value = mock_cache_info + + with pytest.raises(ValueError, match="Unknown strategy"): + self.cache_manager.clean_cache_by_size( + target_size="1GB", + strategy="invalid_strategy", # type: ignore[arg-type] + dry_run=True, + ) + + +class TestHfCacheManagerIntegration: + """Integration tests with real DuckDB operations.""" + + def setup_method(self): + """Set up test fixtures.""" + with patch("tfbpapi.HfCacheManager.DataCard.__init__", return_value=None): + self.conn = duckdb.connect(":memory:") + self.repo_id = "test/repo" + self.cache_manager = HfCacheManager(self.repo_id, self.conn) + + def test_metadata_workflow_integration(self, tmpdir): + """Test complete metadata workflow with real files.""" + # Create temporary parquet file content + metadata_file = tmpdir.join("metadata.parquet") + metadata_file.write("dummy_parquet_content") + + # Test the core table creation functionality + mock_conn = Mock() + test_cache_manager = HfCacheManager("test/repo", mock_conn) + + # Test _create_duckdb_table_from_files directly + test_cache_manager._create_duckdb_table_from_files( + [str(metadata_file)], "metadata_test_metadata" + ) + + # Verify the SQL was generated correctly + mock_conn.execute.assert_called_once() + sql_call = mock_conn.execute.call_args[0][0] + assert "CREATE OR REPLACE VIEW metadata_test_metadata" in sql_call + assert str(metadata_file) in sql_call + + def test_embedded_metadata_workflow_integration(self): + """Test complete embedded metadata workflow with real DuckDB operations.""" + # Create real test data in DuckDB + self.conn.execute( + """ + CREATE TABLE test_data AS + SELECT + 'gene_' || (row_number() OVER()) as gene_id, + CASE + WHEN (row_number() OVER()) % 3 = 0 THEN 'treatment_A' + WHEN (row_number() OVER()) % 3 = 1 THEN 'treatment_B' + ELSE 'control' + END as experimental_condition, + random() * 1000 as expression_value + FROM range(30) + """ + ) + + # Extract embedded metadata + result = self.cache_manager._extract_embedded_metadata_field( + "test_data", "experimental_condition", "metadata_test_condition" + ) + + assert result is True + + # Verify the metadata table was created correctly + metadata_results = self.conn.execute( + "SELECT value, count FROM metadata_test_condition ORDER BY count DESC" + ).fetchall() + + assert len(metadata_results) == 3 # Three unique conditions + + # Check that the counts make sense (should be 10 each for 30 total rows) + total_count = sum(row[1] for row in metadata_results) + assert total_count == 30 + + # Check that conditions are as expected + conditions = {row[0] for row in metadata_results} + assert conditions == {"treatment_A", "treatment_B", "control"} + + def test_table_existence_checking_integration(self): + """Test table existence checking with real DuckDB operations.""" + # Test non-existent table + assert ( + self.cache_manager._check_metadata_exists_in_duckdb("nonexistent_table") + is False + ) + + # Create a real table + self.conn.execute("CREATE TABLE test_table (id INTEGER, name TEXT)") + + # Test existing table + assert self.cache_manager._check_metadata_exists_in_duckdb("test_table") is True + + # Test with view + self.conn.execute("CREATE VIEW test_view AS SELECT * FROM test_table") + assert self.cache_manager._check_metadata_exists_in_duckdb("test_view") is True + + +# Fixtures for common test data +@pytest.fixture +def sample_metadata_config(): + """Sample metadata configuration for testing.""" + return Mock( + config_name="test_metadata", + description="Test metadata configuration", + data_files=[Mock(path="metadata.parquet")], + applies_to=["data_config"], + ) + + +@pytest.fixture +def sample_data_config(): + """Sample data configuration for testing.""" + return Mock( + config_name="test_data", + metadata_fields=["condition", "replicate"], + dataset_type=DatasetType.ANNOTATED_FEATURES, ) - # Should return a list of strategies - assert isinstance(strategies, list) + +@pytest.fixture +def mock_cache_revision(): + """Mock cache revision for testing.""" + revision = Mock() + revision.commit_hash = "abc123def456" + revision.last_modified = datetime.now().timestamp() + revision.size_on_disk = 1024 * 1024 * 100 # 100MB + return revision + + +@pytest.fixture +def mock_cache_repo(mock_cache_revision): + """Mock cache repository for testing.""" + repo = Mock() + repo.repo_id = "test/repository" + repo.revisions = [mock_cache_revision] + repo.size_on_disk = 1024 * 1024 * 100 # 100MB + repo.size_on_disk_str = "100.0MB" + return repo + + +@pytest.fixture +def mock_cache_info(mock_cache_repo): + """Mock cache info for testing.""" + cache_info = Mock() + cache_info.cache_dir = "/tmp/cache" + cache_info.repos = [mock_cache_repo] + cache_info.size_on_disk = 1024 * 1024 * 100 # 100MB + cache_info.size_on_disk_str = "100.0MB" + + # Mock delete_revisions method + def mock_delete_revisions(*revision_hashes): + strategy = Mock() + strategy.expected_freed_size = ( + len(revision_hashes) * 1024 * 1024 * 50 + ) # 50MB per revision + strategy.expected_freed_size_str = f"{len(revision_hashes) * 50}.0MB" + strategy.delete_content = list(revision_hashes) + strategy.execute = Mock() + return strategy + + cache_info.delete_revisions = mock_delete_revisions + return cache_info From 1dd92d4b81e5b834a0373fcbb52359e09593b49d Mon Sep 17 00:00:00 2001 From: chasem Date: Wed, 24 Sep 2025 22:55:35 -0500 Subject: [PATCH 08/49] adding rank response tutorial and role to features --- docs/huggingface_datacard.md | 19 + docs/tutorials/passing_callingcards.csv | 82 ++ docs/tutorials/rank_response_tutorial.ipynb | 968 ++++++++++++++++++++ mkdocs.yml | 1 + pyproject.toml | 2 + tfbpapi/HfCacheManager.py | 81 +- tfbpapi/HfQueryAPI.py | 601 +++++++++--- tfbpapi/HfRankResponse.py | 2 +- tfbpapi/RankResponseAnalysis.py | 277 ++++++ tfbpapi/constants.py | 44 + tfbpapi/datainfo/datacard.py | 24 +- tfbpapi/datainfo/models.py | 4 + tfbpapi/errors.py | 35 + tfbpapi/tests/datainfo/test_datacard.py | 6 +- tfbpapi/tests/test_HfQueryAPI.py | 859 +++++++++++++++++ 15 files changed, 2856 insertions(+), 149 deletions(-) create mode 100644 docs/tutorials/passing_callingcards.csv create mode 100644 docs/tutorials/rank_response_tutorial.ipynb create mode 100644 tfbpapi/RankResponseAnalysis.py create mode 100644 tfbpapi/tests/test_HfQueryAPI.py diff --git a/docs/huggingface_datacard.md b/docs/huggingface_datacard.md index 916d6d4..c41b062 100644 --- a/docs/huggingface_datacard.md +++ b/docs/huggingface_datacard.md @@ -78,6 +78,7 @@ dataset_info: - name: field_name # Column name in the data dtype: string # Data type (string, int64, float64, etc.) description: "Detailed description of what this field contains" + role: "target_identifier" # Optional: semantic role of the feature ``` ### Common Data Types @@ -103,6 +104,17 @@ dataset_info: - `pos`: Single position - `strand`: Strand information (+ or -) +### Feature Roles + +The optional `role` field provides semantic meaning to features, especially useful for `annotated_features` datasets: + +**Standard Roles:** +- `target_identifier`: Identifies target genes/features (e.g., target_locus_tag, target_symbol) +- `regulator_identifier`: Identifies regulatory factors (e.g., regulator_locus_tag, regulator_symbol) +- `quantitative_measure`: Quantitative measurements (e.g., binding_score, expression_level, p_value) +- `experimental_condition`: Experimental conditions or metadata +- `genomic_coordinate`: Positional information (chr, start, end, pos) + ## Partitioned Datasets For large datasets (eg most genome_map datasets), use partitioning: @@ -270,12 +282,19 @@ configs: - name: regulator_symbol dtype: string description: Transcription factor name + role: regulator_identifier + - name: target_locus_tag + dtype: string + description: Target gene systematic identifier + role: target_identifier - name: target_symbol dtype: string description: Target gene name + role: target_identifier - name: binding_score dtype: float64 description: Quantitative binding measurement + role: quantitative_measure - config_name: experiment_metadata description: Experimental conditions and sample information diff --git a/docs/tutorials/passing_callingcards.csv b/docs/tutorials/passing_callingcards.csv new file mode 100644 index 0000000..acaea06 --- /dev/null +++ b/docs/tutorials/passing_callingcards.csv @@ -0,0 +1,82 @@ +id,uploader,modifier,source_name,regulator_symbol,regulator_locus_tag,condition,data_usable,preferred_replicate,background_name,promoterset,batch,lab,assay,source_orig_id,upload_date,modified_date,file,single_binding,composite_binding,promoter,background,fileformat +8573,admin,admin,brent_nf_cc,SOK2,YMR016C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.335275-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8573.csv.gz,NA,1328,4,1,5 +8574,admin,admin,brent_nf_cc,RDR1,YOR380W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.373096-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8574.csv.gz,NA,1329,4,1,5 +8575,admin,admin,brent_nf_cc,PGD1,YGL025C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.674607-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8575.csv.gz,NA,1330,4,1,5 +8576,admin,admin,brent_nf_cc,TOG1,YER184C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.701098-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8576.csv.gz,NA,1331,4,1,5 +8577,admin,admin,brent_nf_cc,MET4,YNL103W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.787380-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8577.csv.gz,NA,1332,4,1,5 +8578,admin,admin,brent_nf_cc,CST6,YIL036W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.794787-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8578.csv.gz,NA,1333,4,1,5 +8579,admin,admin,brent_nf_cc,YOX1,YML027W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.813536-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8579.csv.gz,NA,1334,4,1,5 +8580,admin,admin,brent_nf_cc,RPD3,YNL330C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.866654-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8580.csv.gz,NA,1336,4,1,5 +8581,admin,admin,brent_nf_cc,MSN1,YOL116W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.844507-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8581.csv.gz,NA,1335,4,1,5 +8582,admin,admin,brent_nf_cc,HAP3,YBL021C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.918717-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8582.csv.gz,NA,1338,4,1,5 +8583,admin,admin,brent_nf_cc,SKN7,YHR206W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.924313-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8583.csv.gz,NA,1337,4,1,5 +8584,admin,admin,brent_nf_cc,SMP1,YBR182C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.984147-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8584.csv.gz,NA,1339,4,1,5 +8585,admin,admin,brent_nf_cc,MBP1,YDL056W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.068717-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8585.csv.gz,NA,1340,4,1,5 +8586,admin,admin,brent_nf_cc,RTG3,YBL103C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.078145-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8586.csv.gz,NA,1341,4,1,5 +8587,admin,admin,brent_nf_cc,DAT1,YML113W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.146444-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8587.csv.gz,NA,1342,4,1,5 +8588,admin,admin,brent_nf_cc,GZF3,YJL110C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.294324-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8588.csv.gz,NA,1344,4,1,5 +8589,admin,admin,brent_nf_cc,RDS2,YPL133C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.280370-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8589.csv.gz,NA,1343,4,1,5 +8590,admin,admin,brent_nf_cc,PDR3,YBL005W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.343977-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8590.csv.gz,NA,1346,4,1,5 +8591,admin,admin,brent_nf_cc,RFX1,YLR176C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.304403-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8591.csv.gz,NA,1345,4,1,5 +8592,admin,admin,brent_nf_cc,MET31,YPL038W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.521624-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8592.csv.gz,NA,1347,4,1,5 +8593,admin,admin,brent_nf_cc,INO4,YOL108C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:44.341119-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8593.csv.gz,NA,1349,4,1,5 +8594,admin,admin,brent_nf_cc,RGT1,YKL038W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:44.301633-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8594.csv.gz,NA,1348,4,1,5 +8595,admin,admin,brent_nf_cc,STP2,YHR006W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:44.653232-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8595.csv.gz,NA,1350,4,1,5 +8596,admin,admin,brent_nf_cc,AZF1,YOR113W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.090708-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8596.csv.gz,NA,1352,4,1,5 +8597,admin,admin,brent_nf_cc,ZAP1,YJL056C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.085289-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8597.csv.gz,NA,1351,4,1,5 +8598,admin,admin,brent_nf_cc,MOT3,YMR070W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.171772-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8598.csv.gz,NA,1353,4,1,5 +8599,admin,admin,brent_nf_cc,VHR1,YIL056W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.215135-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8599.csv.gz,NA,1354,4,1,5 +8600,admin,admin,brent_nf_cc,URC2,YDR520C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.286065-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8600.csv.gz,NA,1355,4,1,5 +8601,admin,admin,brent_nf_cc,INO2,YDR123C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.339489-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8601.csv.gz,NA,1356,4,1,5 +8602,admin,admin,brent_nf_cc,FKH2,YNL068C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.395206-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8602.csv.gz,NA,1357,4,1,5 +8603,admin,admin,brent_nf_cc,CHA4,YLR098C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.445223-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8603.csv.gz,NA,1360,4,1,5 +8604,admin,admin,brent_nf_cc,TYE7,YOR344C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.446764-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8604.csv.gz,NA,1359,4,1,5 +8605,admin,admin,brent_nf_cc,OAF1,YAL051W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.447196-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8605.csv.gz,NA,1358,4,1,5 +8606,admin,admin,brent_nf_cc,MET28,YIR017C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.551152-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8606.csv.gz,NA,1361,4,1,5 +8607,admin,admin,brent_nf_cc,AFT1,YGL071W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.825510-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8607.csv.gz,NA,1362,4,1,5 +8608,admin,admin,brent_nf_cc,GAT2,YMR136W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.897217-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8608.csv.gz,NA,1363,4,1,5 +8609,admin,admin,brent_nf_cc,PHO2,YDL106C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:46.029628-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8609.csv.gz,NA,1366,4,1,5 +8610,admin,admin,brent_nf_cc,DAL80,YKR034W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.927940-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8610.csv.gz,NA,1364,4,1,5 +8611,admin,admin,brent_nf_cc,UME1,YPL139C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.951004-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8611.csv.gz,NA,1365,4,1,5 +8612,admin,admin,brent_nf_cc,RLM1,YPL089C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:46.219541-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8612.csv.gz,NA,1367,4,1,5 +8613,admin,admin,brent_nf_cc,HAP5,YOR358W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:46.366453-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8613.csv.gz,NA,1368,4,1,5 +8614,admin,admin,brent_nf_cc,TUP1,YCR084C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:46.441695-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8614.csv.gz,NA,1369,4,1,5 +8615,admin,admin,brent_nf_cc,IXR1,YKL032C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:46.605683-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8615.csv.gz,NA,1370,4,1,5 +8616,admin,admin,brent_nf_cc,CYC8,YBR112C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.115289-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8616.csv.gz,NA,1371,4,1,5 +8617,admin,admin,brent_nf_cc,UPC2,YDR213W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.185506-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8617.csv.gz,NA,1372,4,1,5 +8618,admin,admin,brent_nf_cc,ARO80,YDR421W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.300436-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8618.csv.gz,NA,1373,4,1,5 +8619,admin,admin,brent_nf_cc,YHP1,YDR451C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.432083-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8619.csv.gz,NA,1374,4,1,5 +8620,admin,admin,brent_nf_cc,GAL11,YOL051W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.457453-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8620.csv.gz,NA,1375,4,1,5 +8621,admin,admin,brent_nf_cc,MED2,YDL005C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.457928-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8621.csv.gz,NA,1376,4,1,5 +8622,admin,admin,brent_nf_cc,GIS1,YDR096W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.495815-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8622.csv.gz,NA,1377,4,1,5 +8623,admin,admin,brent_nf_cc,STP1,YDR463W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.533662-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8623.csv.gz,NA,1379,4,1,5 +8624,admin,admin,brent_nf_cc,RTG1,YOL067C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.529096-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8624.csv.gz,NA,1378,4,1,5 +8625,admin,admin,brent_nf_cc,HAP2,YGL237C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.546475-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8625.csv.gz,NA,1380,4,1,5 +8626,admin,admin,brent_nf_cc,MSN2,YMR037C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.876943-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8626.csv.gz,NA,1381,4,1,5 +8627,admin,admin,brent_nf_cc,LEU3,YLR451W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:48.130584-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8627.csv.gz,NA,1382,4,1,5 +8628,admin,admin,brent_nf_cc,GAL4,YPL248C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:48.383735-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8628.csv.gz,NA,1383,4,1,5 +8629,admin,admin,brent_nf_cc,GCN4,YEL009C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:48.661669-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8629.csv.gz,NA,1384,4,1,5 +8630,admin,admin,brent_nf_cc,CRZ1,YNL027W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:48.724198-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8630.csv.gz,NA,1385,4,1,5 +8631,admin,admin,brent_nf_cc,STE12,YHR084W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:48.827903-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8631.csv.gz,NA,1386,4,1,5 +8632,admin,admin,brent_nf_cc,STP3,YLR375W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.012518-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8632.csv.gz,NA,1387,4,1,5 +8633,admin,admin,brent_nf_cc,PHD1,YKL043W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.172299-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8633.csv.gz,NA,1388,4,1,5 +8634,admin,admin,brent_nf_cc,RPN4,YDL020C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.219434-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8634.csv.gz,NA,1389,4,1,5 +8635,admin,admin,brent_nf_cc,ASH1,YKL185W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.252413-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8635.csv.gz,NA,1390,4,1,5 +8636,admin,admin,brent_nf_cc,TEA1,YOR337W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.600060-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8636.csv.gz,NA,1391,4,1,5 +8637,admin,admin,brent_nf_cc,SUM1,YDR310C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.893152-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8637.csv.gz,NA,1392,4,1,5 +8638,admin,admin,brent_nf_cc,NRG1,YDR043C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.991998-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8638.csv.gz,NA,1394,4,1,5 +8639,admin,admin,brent_nf_cc,TEC1,YBR083W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.962020-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8639.csv.gz,NA,1393,4,1,5 +8640,admin,admin,brent_nf_cc,CSE2,YNR010W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:50.509234-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8640.csv.gz,NA,1395,4,1,5 +8641,admin,admin,brent_nf_cc,ROX1,YPR065W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:50.797488-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8641.csv.gz,NA,1397,4,1,5 +8642,admin,admin,brent_nf_cc,FKH1,YIL131C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:50.782743-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8642.csv.gz,NA,1396,4,1,5 +8643,admin,admin,brent_nf_cc,GAL80,YML051W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:50.868439-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8643.csv.gz,NA,1398,4,1,5 +8644,admin,admin,brent_nf_cc,SKO1,YNL167C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:51.214395-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8644.csv.gz,NA,1399,4,1,5 +8645,admin,admin,brent_nf_cc,PDR1,YGL013C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:51.527347-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8645.csv.gz,NA,1400,4,1,5 +8646,admin,admin,brent_nf_cc,CIN5,YOR028C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:51.544390-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8646.csv.gz,NA,1401,4,1,5 +8647,admin,admin,brent_nf_cc,PPR1,YLR014C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:51.974579-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8647.csv.gz,NA,1402,4,1,5 +8648,admin,admin,brent_nf_cc,HAL9,YOL089C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:52.067382-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8648.csv.gz,NA,1403,4,1,5 +8649,admin,admin,brent_nf_cc,CBF1,YJR060W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:52.302853-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8649.csv.gz,NA,1404,4,1,5 +8650,admin,admin,brent_nf_cc,USV1,YPL230W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:52.353098-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8650.csv.gz,NA,1405,4,1,5 +8651,admin,admin,brent_nf_cc,RPH1,YER169W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:53.152828-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8651.csv.gz,NA,1406,4,1,5 +8652,admin,admin,brent_nf_cc,ERT1,YBR239C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:53.548696-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8652.csv.gz,NA,1407,4,1,5 +8653,admin,admin,brent_nf_cc,MET32,YDR253C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:54.027532-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8653.csv.gz,NA,1408,4,1,5 diff --git a/docs/tutorials/rank_response_tutorial.ipynb b/docs/tutorials/rank_response_tutorial.ipynb new file mode 100644 index 0000000..2e85f20 --- /dev/null +++ b/docs/tutorials/rank_response_tutorial.ipynb @@ -0,0 +1,968 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Rank-Response Analysis Tutorial\n", + "\n", + "This tutorial demonstrates how to conduct comprehensive rank-response analysis using tfbpapi to compare binding data with perturbation responses across multiple datasets.\n", + "\n", + "## Overview\n", + "\n", + "Rank-response analysis evaluates whether targets with stronger binding evidence are more likely to show transcriptional responses upon regulator perturbation. This approach helps validate functional relevance of binding data.\n", + "\n", + "### Datasets Used\n", + "- **Binding data**: Calling Cards, ChEC-seq, ChIP-chip\n", + "- **Perturbation data**: McIsaac (ZEV system), Kemmeren (overexpression), Mahendrawada (RNA-seq)\n", + "\n", + "### Analysis Strategy\n", + "1. Load and filter datasets to common regulators/targets\n", + "2. Rank targets by binding strength for each regulator\n", + "3. Label targets as responsive/non-responsive based on perturbation data\n", + "4. Perform binned analysis to assess rank-response relationship\n", + "5. Compare results across different data combinations" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "from tfbpapi.HfQueryAPI import HfQueryAPI\n", + "from tfbpapi.RankResponseAnalysis import RankResponseAnalyzer\n", + "from tfbpapi.datainfo.datacard import DataCard\n", + "from tfbpapi.errors import DataCardError, HfDataFetchError\n", + "import matplotlib.pyplot as plt\n", + "import seaborn as sns\n", + "\n", + "# Configure plotting\n", + "plt.style.use('default')\n", + "sns.set_palette(\"husl\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Dataset Exploration with DataCard\n", + "\n", + "Before loading data, let's explore dataset structure and metadata using the DataCard interface." + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Exploring dataset structures...\n", + "\n", + "==================================================\n", + "Exploring BrentLab/callingcards\n", + "Dataset types in configs: {'genome_map', 'annotated_features', 'metadata'}\n", + "Configurations: 4\n", + "\n", + "Config: annotated_features\n", + " Type: annotated_features\n", + " Features: 13\n", + " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", + " Target fields: ['target_locus_tag', 'target_symbol']\n", + "\n", + "Config: genome_map\n", + " Type: genome_map\n", + " Features: 7\n", + "\n", + "Config: annotated_features_meta\n", + " Type: metadata\n", + " Features: 9\n", + " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", + "\n", + "Config: genome_map_meta\n", + " Type: metadata\n", + " Features: 7\n", + " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", + "\n", + "==================================================\n", + "Exploring BrentLab/mahendrawada_2025\n", + "Dataset types in configs: {'genome_map', 'annotated_features', 'metadata', 'genomic_features'}\n", + "Configurations: 6\n", + "\n", + "Config: genomic_features\n", + " Type: genomic_features\n", + " Features: 24\n", + "\n", + "Config: mahendrawada_2025_metadata\n", + " Type: metadata\n", + " Features: 7\n", + " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", + "\n", + "Config: chec_seq_genome_map\n", + " Type: genome_map\n", + " Features: 3\n", + "\n", + "Config: mahendrawada_chec_seq\n", + " Type: annotated_features\n", + " Features: 6\n", + " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", + " Target fields: ['target_locus_tag', 'target_symbol']\n", + " Metadata fields: ['regulator_locus_tag', 'regulator_symbol']\n", + "\n", + "Config: reprocessed_chec_seq\n", + " Type: annotated_features\n", + " Features: 6\n", + " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", + " Target fields: ['target_locus_tag', 'target_symbol']\n", + "\n", + "Config: rna_seq\n", + " Type: annotated_features\n", + " Features: 5\n", + " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", + " Target fields: ['target_locus_tag', 'target_symbol']\n", + " Metadata fields: ['regulator_locus_tag', 'regulator_symbol']\n", + "\n", + "==================================================\n", + "Exploring BrentLab/harbison_2004\n", + "Dataset types in configs: {'annotated_features'}\n", + "Configurations: 1\n", + "\n", + "Config: harbison_2004\n", + " Type: annotated_features\n", + " Features: 7\n", + " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", + " Target fields: ['target_locus_tag', 'target_symbol']\n", + " Metadata fields: ['regulator_locus_tag', 'regulator_symbol', 'condition']\n", + "\n", + "==================================================\n", + "Exploring BrentLab/hackett_2020\n", + "Dataset types in configs: {'annotated_features'}\n", + "Configurations: 1\n", + "\n", + "Config: hackett_2020\n", + " Type: annotated_features\n", + " Features: 17\n", + " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", + " Target fields: ['target_locus_tag', 'target_symbol']\n", + " Metadata fields: ['regulator_locus_tag', 'regulator_symbol', 'time', 'mechanism', 'restriction', 'date', 'strain']\n", + "\n", + "==================================================\n", + "Exploring BrentLab/kemmeren_2014\n", + "Dataset types in configs: {'annotated_features'}\n", + "Configurations: 1\n", + "\n", + "Config: kemmeren_2014\n", + " Type: annotated_features\n", + " Features: 11\n", + " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", + " Target fields: ['target_locus_tag', 'target_symbol']\n", + " Metadata fields: ['regulator_locus_tag', 'regulator_symbol']\n", + "\n", + "==================================================\n", + "Successfully explored 5 datasets\n" + ] + } + ], + "source": [ + "# Explore dataset structure using DataCard\n", + "print(\"Exploring dataset structures...\")\n", + "\n", + "datasets_to_explore = [\n", + " \"BrentLab/callingcards\",\n", + " \"BrentLab/mahendrawada_2025\", \n", + " \"BrentLab/harbison_2004\",\n", + " \"BrentLab/hackett_2020\",\n", + " \"BrentLab/kemmeren_2014\"\n", + "]\n", + "\n", + "dataset_info = {}\n", + "\n", + "for repo_id in datasets_to_explore:\n", + " try:\n", + " print(f\"\\n{'='*50}\")\n", + " print(f\"Exploring {repo_id}\")\n", + "\n", + " # Create DataCard instance\n", + " datacard = DataCard(repo_id)\n", + " card = datacard.dataset_card\n", + "\n", + " # DatasetCard doesn't have dataset_type directly - it's on individual configs\n", + " dataset_types = [config.dataset_type.value for config in card.configs]\n", + " print(f\"Dataset types in configs: {set(dataset_types)}\")\n", + " print(f\"Configurations: {len(card.configs)}\")\n", + "\n", + " # Store dataset info for later use\n", + " dataset_info[repo_id] = {\n", + " 'datacard': datacard,\n", + " 'card': card,\n", + " 'configs': {config.config_name: config for config in card.configs}\n", + " }\n", + "\n", + " # Display configuration details\n", + " for config in card.configs:\n", + " print(f\"\\nConfig: {config.config_name}\")\n", + " print(f\" Type: {config.dataset_type.value}\")\n", + " print(f\" Features: {len(config.dataset_info.features) if config.dataset_info.features else 0}\")\n", + "\n", + " if config.dataset_info.features:\n", + " # Show regulator and target fields if available\n", + " feature_names = [f.name for f in config.dataset_info.features]\n", + " regulator_fields = [f for f in feature_names if 'regulator' in f.lower()]\n", + " target_fields = [f for f in feature_names if 'target' in f.lower()]\n", + "\n", + " if regulator_fields:\n", + " print(f\" Regulator fields: {regulator_fields}\")\n", + " if target_fields:\n", + " print(f\" Target fields: {target_fields}\")\n", + "\n", + " if config.metadata_fields:\n", + " print(f\" Metadata fields: {config.metadata_fields}\")\n", + "\n", + " except (DataCardError, HfDataFetchError) as e:\n", + " print(f\"Error exploring {repo_id}: {e}\")\n", + " continue\n", + " except Exception as e:\n", + " print(f\"Unexpected error exploring {repo_id}: {e}\")\n", + " continue\n", + "\n", + "print(f\"\\n{'='*50}\")\n", + "print(f\"Successfully explored {len(dataset_info)} datasets\")" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": {}, + "outputs": [], + "source": [ + "## Initialize dataset connections and load data\n" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Initializing HfQueryAPI connections...\n", + "All API connections initialized\n" + ] + } + ], + "source": [ + "\n", + "print(\"Initializing HfQueryAPI connections...\")\n", + "\n", + "mahendrawada_2025 = HfQueryAPI(repo_id=\"BrentLab/mahendrawada_2025\")\n", + "hackett_2020 = HfQueryAPI(repo_id=\"BrentLab/hackett_2020\")\n", + "callingcards = HfQueryAPI(repo_id=\"BrentLab/callingcards\")\n", + "harbison_2004 = HfQueryAPI(repo_id=\"BrentLab/harbison_2004\") \n", + "kemmeren_2014 = HfQueryAPI(repo_id=\"BrentLab/kemmeren_2014\")\n", + "\n", + "print(\"All API connections initialized\")" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Getting metadata from datasets...\n", + "All metadata loaded\n", + "Found 81 common regulators across all datasets\n" + ] + } + ], + "source": [ + "# Get metadata from each dataset to find common regulators\n", + "print(\"Getting metadata from datasets...\")\n", + "\n", + "harbison_meta = harbison_2004.get_metadata(\"harbison_2004\")\n", + "kemmeren_meta = kemmeren_2014.get_metadata(\"kemmeren_2014\") \n", + "callingcards_meta = callingcards.get_metadata(\"annotated_features\")\n", + "mahendrawada_checseq_meta = mahendrawada_2025.get_metadata(\"mahendrawada_chec_seq\")\n", + "mahendrawada_rnaseq_meta = mahendrawada_2025.get_metadata(\"rna_seq\")\n", + "hackett_2020_meta = hackett_2020.get_metadata(\"hackett_2020\")\n", + "\n", + "print(\"All metadata loaded\")\n", + "\n", + "# Get the intersection of common regulators\n", + "common_regulators = (set(mahendrawada_rnaseq_meta.regulator_locus_tag.unique())\n", + " & set(mahendrawada_checseq_meta.regulator_locus_tag.unique())\n", + " & set(hackett_2020_meta.regulator_locus_tag.unique())\n", + " & set(callingcards_meta.regulator_locus_tag.unique())\n", + " & set(harbison_meta.regulator_locus_tag.unique())\n", + " & set(kemmeren_meta.regulator_locus_tag.unique()))\n", + "\n", + "print(f\"Found {len(common_regulators)} common regulators across all datasets\")\n", + "\n", + "# Create proper SQL IN clause\n", + "if common_regulators:\n", + " regulator_clause = \"(\" + \", \".join(f\"'{reg}'\" for reg in common_regulators) + \")\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Filter the data" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Loading calling cards quality filter...\n", + "Found 81 passing calling cards experiments\n", + "Applying dataset-specific filters...\n", + "All filters applied\n" + ] + } + ], + "source": [ + "# Load and filter calling cards data with passing experiments\n", + "print(\"Loading calling cards quality filter...\")\n", + "\n", + "passing_cc = pd.read_csv(\"./passing_callingcards.csv\")\n", + "db_ids_as_str = [str(id) for id in passing_cc.id.unique()]\n", + "\n", + "# filter the callingcards_data on the db_id column using passing_cc.id\n", + "db_ids = callingcards_meta[callingcards_meta.db_id.isin(db_ids_as_str)].id\n", + "cc_ids_clause = \"(\" + \", \".join(f\"'{db_id}'\" for db_id in db_ids) + \")\"\n", + "\n", + "print(f\"Found {len(db_ids)} passing calling cards experiments\")\n", + "\n", + "# Apply dataset-specific filters\n", + "print(\"Applying dataset-specific filters...\")\n", + "\n", + "hackett_2020.set_sql_filter(\n", + " \"hackett_2020\",\n", + " f\"\"\"\n", + " time = 15 \n", + " AND mechanism = 'ZEV' \n", + " AND restriction = 'P' \n", + " AND regulator_locus_tag IN {regulator_clause}\n", + " \"\"\")\n", + "\n", + "mahendrawada_2025.set_sql_filter(\n", + " \"mahendrawada_chec_seq\",\n", + " f\"\"\"\n", + " regulator_locus_tag IN {regulator_clause}\n", + " \"\"\")\n", + "\n", + "mahendrawada_2025.set_sql_filter(\n", + " \"rna_seq\",\n", + " f\"\"\"\n", + " regulator_locus_tag IN {regulator_clause}\n", + " \"\"\")\n", + "\n", + "callingcards.set_sql_filter(\n", + " \"annotated_features\",\n", + " f\"\"\"\n", + " regulator_locus_tag IN {regulator_clause} \n", + " AND id IN {cc_ids_clause}\n", + " \"\"\")\n", + "\n", + "harbison_2004.set_sql_filter(\n", + " \"harbison_2004\",\n", + " f\"\"\"\n", + " regulator_locus_tag IN {regulator_clause} \n", + " AND condition = 'YPD'\n", + " \"\"\")\n", + "\n", + "print(\"All filters applied\")" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Loading initial datasets...\n", + "Initial datasets loaded\n", + "mcisaac: 61 regulators\n", + "mahendrawada_chec: 81 regulators\n", + "callingcards: 46 regulators\n", + "harbison: 81 regulators\n", + "Final intersection: 33 regulators\n" + ] + } + ], + "source": [ + "# Load initial datasets to determine final common regulators and targets\n", + "print(\"Loading initial datasets...\")\n", + "\n", + "mcisaac = hackett_2020.query(\"SELECT * FROM hackett_2020\", \"hackett_2020\")\n", + "mahendrawada_chec = mahendrawada_2025.query(\"SELECT * FROM mahendrawada_chec_seq\", \"mahendrawada_chec_seq\") \n", + "mahendrawada_perturb = mahendrawada_2025.query(\"SELECT * FROM rna_seq\", \"rna_seq\")\n", + "callingcards_data = callingcards.query(\"SELECT * FROM annotated_features\", \"annotated_features\")\n", + "harbison_data = harbison_2004.query(\"SELECT * FROM harbison_2004\", \"harbison_2004\")\n", + "mahendrawada_features = mahendrawada_2025.query(\"SELECT * FROM genomic_features\", \"genomic_features\")\n", + "\n", + "print(\"Initial datasets loaded\")\n", + "\n", + "# After running your current queries, check what you actually got\n", + "actual_regulators = {\n", + " 'mcisaac': set(mcisaac.regulator_locus_tag.unique()),\n", + " 'mahendrawada_chec': set(mahendrawada_chec.regulator_locus_tag.unique()),\n", + " 'callingcards': set(callingcards_data.regulator_locus_tag.unique()),\n", + " 'harbison': set(harbison_data.regulator_locus_tag.unique())\n", + "}\n", + "\n", + "for name, regulators in actual_regulators.items():\n", + " print(f\"{name}: {len(regulators)} regulators\")\n", + "\n", + "final_common = set.intersection(*actual_regulators.values())\n", + "print(f\"Final intersection: {len(final_common)} regulators\")\n", + "\n", + "final_common_clause = \"(\" + \", \".join(f\"'{reg}'\" for reg in final_common) + \")\"" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Finding common targets...\n", + "Found 5525 common targets\n", + "Loading final datasets with complete filtering...\n", + "Final datasets loaded with complete filtering\n", + "McIsaac: 187,884 rows, 33 regulators\n", + "Kemmeren: 178,332 rows, 33 regulators\n", + "Mahendrawada RNA-seq: 4,411 rows, 33 regulators\n", + "Calling Cards: 182,325 rows, 33 regulators\n", + "Mahendrawada ChEC: 22,478 rows, 33 regulators\n", + "Harbison: 182,952 rows, 33 regulators\n" + ] + } + ], + "source": [ + "# Find common targets across all datasets\n", + "print(\"Finding common targets...\")\n", + "\n", + "common_targets = (set(mcisaac.target_locus_tag.unique())\n", + " & set(mahendrawada_features.locus_tag.unique())\n", + " & set(callingcards_data.target_locus_tag.unique())\n", + " & set(harbison_data.target_locus_tag.unique()))\n", + "\n", + "print(f\"Found {len(common_targets)} common targets\")\n", + "\n", + "if common_targets:\n", + " target_clause = \"(\" + \", \".join(f\"'{tgt}'\" for tgt in common_targets) + \")\"\n", + "\n", + "# Load final datasets with both regulator and target filtering \n", + "print(\"Loading final datasets with complete filtering...\")\n", + "\n", + "mcisaac = hackett_2020.query(f\"\"\"\n", + " SELECT *\n", + " FROM hackett_2020\n", + " WHERE regulator_locus_tag in {final_common_clause} \n", + " AND target_locus_tag IN {target_clause}\n", + " \"\"\", \"hackett_2020\")\n", + "\n", + "mahendrawada_perturb = mahendrawada_2025.query(f\"\"\"\n", + " SELECT *\n", + " FROM rna_seq\n", + " WHERE regulator_locus_tag IN {final_common_clause} \n", + " AND target_locus_tag IN {target_clause}\n", + " \"\"\", \"rna_seq\")\n", + "\n", + "kemmeren = kemmeren_2014.query(f\"\"\"\n", + " SELECT * FROM kemmeren_2014 WHERE regulator_locus_tag IN {final_common_clause} \n", + " AND target_locus_tag IN {target_clause} \n", + " AND variable_in_wt = 0\n", + " \"\"\",\n", + " \"kemmeren_2014\")\n", + "\n", + "mahendrawada_chec = mahendrawada_2025.query(f\"SELECT * FROM mahendrawada_chec_seq WHERE regulator_locus_tag IN {final_common_clause} AND target_locus_tag IN {target_clause}\", \"mahendrawada_chec_seq\")\n", + "\n", + "callingcards_data = callingcards.query(f\"SELECT * FROM annotated_features WHERE regulator_locus_tag IN {final_common_clause} AND target_locus_tag IN {target_clause}\",\n", + " \"annotated_features\")\n", + "\n", + "harbison = harbison_2004.query(f\"SELECT * FROM harbison_2004 WHERE regulator_locus_tag IN {final_common_clause} AND target_locus_tag IN {target_clause}\",\n", + " \"harbison_2004\")\n", + "\n", + "print(\"Final datasets loaded with complete filtering\")\n", + "\n", + "# Print final dataset sizes\n", + "datasets_info = [\n", + " ('McIsaac', mcisaac), ('Kemmeren', kemmeren), ('Mahendrawada RNA-seq', mahendrawada_perturb),\n", + " ('Calling Cards', callingcards_data), ('Mahendrawada ChEC', mahendrawada_chec), ('Harbison', harbison)\n", + "]\n", + "\n", + "for name, data in datasets_info:\n", + " if len(data) > 0:\n", + " regulators = data['regulator_locus_tag'].nunique() if 'regulator_locus_tag' in data.columns else 0\n", + " print(f\"{name}: {len(data):,} rows, {regulators} regulators\")\n", + " else:\n", + " print(f\"{name}: No data loaded\")" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": {}, + "outputs": [], + "source": [ + "## 2. Rank Binding Data by Strength" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Binding datasets ranked by strength\n" + ] + } + ], + "source": [ + "\n", + "# Rank binding datasets by strength (stronger binding = lower rank)\n", + "callingcards_ranked = (callingcards_data\n", + " .sort_values(['regulator_locus_tag', 'poisson_pval', 'callingcards_enrichment'],\n", + " ascending=[True, True, False], # Lower p-value, higher enrichment = stronger\n", + " kind='stable')\n", + " .reset_index(drop=True)\n", + " )\n", + "\n", + "mahendrawada_chec_ranked = (mahendrawada_chec\n", + " .sort_values(['regulator_locus_tag', 'peak_score'],\n", + " ascending=[True, False], # Higher peak score = stronger\n", + " kind='stable')\n", + " .reset_index(drop=True)\n", + " )\n", + "\n", + "harbison_ranked = (harbison\n", + " .sort_values(['regulator_locus_tag', 'pvalue', 'effect'],\n", + " ascending=[True, True, False], # Lower p-value, higher effect = stronger\n", + " kind='stable')\n", + " .reset_index(drop=True)\n", + " )\n", + "\n", + "print(\"Binding datasets ranked by strength\")" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Labeling functions defined\n" + ] + } + ], + "source": [ + "\n", + "def label_by_mcisaac(df):\n", + " \"\"\"Label targets as responsive based on McIsaac perturbation data.\"\"\"\n", + " out = df.merge(\n", + " mcisaac[['regulator_locus_tag', 'target_locus_tag', 'log2_shrunken_timecourses']],\n", + " on=['regulator_locus_tag', 'target_locus_tag'],\n", + " how='left',\n", + " )\n", + "\n", + " out['is_responsive'] = (\n", + " out.get('log2_shrunken_timecourses', pd.Series(0)).fillna(0).abs() > 0)\n", + "\n", + " return out\n", + "\n", + "def label_by_kemmeren(df):\n", + " \"\"\"Label targets as responsive based on Kemmeren perturbation data.\"\"\" \n", + " out = df.merge(\n", + " kemmeren[['regulator_locus_tag', 'target_locus_tag', 'Madj', 'pval']],\n", + " on=['regulator_locus_tag', 'target_locus_tag'],\n", + " how='left',\n", + " )\n", + "\n", + " out['is_responsive'] = (\n", + " out.get('pval', pd.Series(1)).fillna(1) <= 0.05)\n", + "\n", + " return out\n", + "\n", + "def label_by_mahendrawada_rnaseq(df):\n", + " \"\"\"Label targets as responsive based on Mahendrawada RNA-seq data.\"\"\"\n", + " out = df.merge(\n", + " mahendrawada_perturb[['regulator_locus_tag', 'target_locus_tag', 'log2fc']],\n", + " on=['regulator_locus_tag', 'target_locus_tag'],\n", + " how='left',\n", + " )\n", + "\n", + " out['is_responsive'] = ~out.log2fc.isna()\n", + "\n", + " return out\n", + "\n", + "print(\"Labeling functions defined\")" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Analysis functions defined\n" + ] + } + ], + "source": [ + "\n", + "def get_rr_results(df):\n", + " \"\"\"\n", + " Perform rank-response analysis for each regulator in the dataset.\n", + "\n", + " :param df: DataFrame with ranked binding data and responsive labels\n", + "\n", + " :returns: DataFrame with bin-level results for all regulators\n", + " \"\"\"\n", + " res_dict = {}\n", + "\n", + " # For each regulator separately\n", + " for regulator in df['regulator_locus_tag'].unique():\n", + " regulator_data = df[\n", + " df['regulator_locus_tag'] == regulator\n", + " ]\n", + "\n", + " try:\n", + " analyzer = RankResponseAnalyzer(\n", + " data=regulator_data[['target_locus_tag', 'is_responsive']],\n", + " target_col='target_locus_tag',\n", + " responsive_col='is_responsive',\n", + " bin_size=5 # Small bin size for detailed analysis\n", + " )\n", + "\n", + " results = analyzer.get_bin_summary()\n", + " res_dict[regulator] = results\n", + "\n", + " except Exception as e:\n", + " print(f\"Warning: Failed to analyze {regulator}: {e}\")\n", + " continue\n", + "\n", + " if not res_dict:\n", + " return pd.DataFrame()\n", + "\n", + " return (pd.concat(res_dict, names=['regulator_locus_tag', 'original_index'])\n", + " .reset_index(level=0)\n", + " .reset_index(drop=True))\n", + "\n", + "def combine_rr_results(ranked_df, binding_source):\n", + " \"\"\"\n", + " Generate rank-response results for all perturbation sources and combine filtered results.\n", + "\n", + " :param ranked_df: DataFrame with ranked binding data\n", + " :param binding_source: String identifier for the binding data source\n", + "\n", + " :returns: Combined DataFrame with bin==5 results from all perturbation sources\n", + " \"\"\"\n", + " print(f\"Analyzing {binding_source} binding data...\")\n", + "\n", + " # Generate results for each perturbation source\n", + " cc_mcisaac_rr = get_rr_results(label_by_mcisaac(ranked_df))\n", + " cc_kem_rr = get_rr_results(label_by_kemmeren(ranked_df))\n", + " cc_mahendrawada_rr = get_rr_results(label_by_mahendrawada_rnaseq(ranked_df))\n", + "\n", + " # Add source columns to each result\n", + " cc_mcisaac_rr['binding_source'] = binding_source\n", + " cc_mcisaac_rr['perturbation_source'] = 'mcisaac'\n", + "\n", + " cc_kem_rr['binding_source'] = binding_source\n", + " cc_kem_rr['perturbation_source'] = 'kemmeren'\n", + "\n", + " cc_mahendrawada_rr['binding_source'] = binding_source\n", + " cc_mahendrawada_rr['perturbation_source'] = 'mahendrawada_rnaseq'\n", + "\n", + " # Filter to bin==5 (strongest binding bin) and combine\n", + " bin5_results = [cc_mcisaac_rr[cc_mcisaac_rr.bin == 5],\n", + " cc_kem_rr[cc_kem_rr.bin == 5],\n", + " cc_mahendrawada_rr[cc_mahendrawada_rr.bin == 5]]\n", + "\n", + " combined = pd.concat(bin5_results, ignore_index=True)\n", + "\n", + " # Find regulators that have observations from all 3 perturbation sources\n", + " regulator_counts = combined.groupby('regulator_locus_tag')['perturbation_source'].nunique()\n", + " complete_regulators = regulator_counts[regulator_counts == 3].index\n", + "\n", + " # Filter to only keep rows for regulators with all 3 perturbation sources\n", + " final_result = combined[combined.regulator_locus_tag.isin(complete_regulators)]\n", + "\n", + " print(f\"Analysis complete: {len(complete_regulators)} regulators with complete data\")\n", + "\n", + " return final_result\n", + "\n", + "print(\"Analysis functions defined\")" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Binding data ranked by strength\n" + ] + } + ], + "source": [ + "def rank_by_binding_strength(df, binding_cols, ascending_order):\n", + " \"\"\"\n", + " Rank targets by binding strength within each regulator group.\n", + "\n", + " :param df: DataFrame with binding data\n", + " :param binding_cols: List of columns to sort by\n", + " :param ascending_order: List of boolean values for sort order\n", + " \"\"\"\n", + " return (df\n", + " .sort_values(['regulator_locus_tag'] + binding_cols, \n", + " ascending=[True] + ascending_order, \n", + " kind='stable')\n", + " .reset_index(drop=True))\n", + "\n", + "# Rank binding datasets\n", + "callingcards_ranked = rank_by_binding_strength(\n", + " callingcards_data,\n", + " ['poisson_pval', 'callingcards_enrichment'],\n", + " [True, False] # Lower p-value and higher enrichment = stronger binding\n", + ")\n", + "\n", + "mahendrawada_chec_ranked = rank_by_binding_strength(\n", + " mahendrawada_chec,\n", + " ['peak_score'],\n", + " [False] # Higher peak score = stronger binding\n", + ")\n", + "\n", + "harbison_ranked = rank_by_binding_strength(\n", + " harbison_data,\n", + " ['pvalue', 'effect'],\n", + " [True, False] # Lower p-value and higher effect = stronger binding\n", + ")\n", + "\n", + "print(\"Binding data ranked by strength\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Rank by binding and label each target as responsive or non-responsive" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Executing comprehensive rank-response analysis...\n", + "This will analyze each binding dataset against all perturbation sources...\n", + "Analyzing callingcards binding data...\n", + "Analysis complete: 33 regulators with complete data\n", + "Analyzing mahendrawada_chec binding data...\n", + "Analysis complete: 33 regulators with complete data\n", + "Analyzing harbison binding data...\n", + "Analysis complete: 81 regulators with complete data\n", + "\n", + "==================================================\n", + "Individual dataset results:\n", + "Calling Cards: 99 result rows\n", + "ChEC-seq: 99 result rows\n", + "Harbison: 243 result rows\n" + ] + } + ], + "source": [ + "# Run analysis for all available binding datasets\n", + "print(\"Executing comprehensive rank-response analysis...\")\n", + "print(\"This will analyze each binding dataset against all perturbation sources...\")\n", + "\n", + "# Execute analysis for each binding dataset\n", + "cc_rr_res = combine_rr_results(callingcards_ranked, \"callingcards\")\n", + "chec_rr_res = combine_rr_results(mahendrawada_chec_ranked, \"mahendrawada_chec\") \n", + "harb_rr_res = combine_rr_results(harbison_ranked, \"harbison\")\n", + "\n", + "print(f\"\\n{'='*50}\")\n", + "print(\"Individual dataset results:\")\n", + "print(f\"Calling Cards: {len(cc_rr_res)} result rows\")\n", + "print(f\"ChEC-seq: {len(chec_rr_res)} result rows\") \n", + "print(f\"Harbison: {len(harb_rr_res)} result rows\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Visualize rank-response relationships" + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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7O3tBSMjI6Fhw4ZCbGysYGVlJfTr1++LxyOn/flUVsxPnz6Vm67s99LkyZMFR0dHQUdHR+4zosqxPH/+vODr6yuYmpoKxsbGQoMGDYSTJ08qLHv58mXB29tbMDQ0FBwdHYXJkycLK1euVPhsElH+Yk7PnJ45PXP63GRdIz79Z2hoKHh4eAhLlizJ9nP4ad6ZU26SXT7+4cMHYfDgwYKVlZVgYmIi+Pn5Cffv31dYpyq5fHZ554ULFwQvLy/BwMBAKF68uDB9+nRhwYIFAgDh8ePHuR6P//77T2jTpo1gYWEhmJubCx06dBAePnxYIPudnS995j7f7y9d03KKWxA+5rKDBg0SbGxsBIlEIpcnP336VGjXrp1gbGwsFC1aVOjbt69w9epVhc9FTp9VQfi/z+u///4r1K5dWzA0NBScnZ2FRYsWyZWTyWTCtGnTBGdnZ7Fe9vfffwv+/v6Cs7OzXNmTJ0+KOf6nxzO7PD89PV2YOHGi4OrqKujp6QlOTk7C6NGjhZSUFLlyqpxr2WFOT0TaSCIIHFknrwICArB582a19MtJRMp59uwZihUrhvHjx+f6+CsRqebVq1coWrQopkyZgjFjxhR2OEREBYY5PVHBY06fs+DgYCxbtgzJyck5DpBMRET0vfqmx8wgom9PVFQUMjMz0a1bt8IOhUhrffjwQWFaVr+8Pj4+BRsMERERfXeY03/0eU72/PlzrFmzBvXq1WNDBhERUTa++TEziOjbcOjQIVy/fh1Tp05F69at4eLiUtghEWmtjRs3IioqCs2bN4epqSmOHz+O9evX48cff0TdunULOzwiIiL6RjGnl1e7dm34+PigfPnyePLkCVauXIk3b97waRUiIqIcsDGDiLTCpEmTcPLkSdStWxcLFy4s7HCItFqVKlWgq6uLWbNm4c2bN+Kg4FOmTCns0IiIiOgbxpxeXvPmzbF582ZERERAIpGgWrVqWLlyJerXr1/YoREREWkkjplBREREREREREREREQajWNmEBERERERERERERGRRmNjBhERERERERERERERabTvbswMmUyGhw8fokiRIpBIJIUdDhERERUgQRDw9u1bODg4QEeH93QQaSvm9ERERN8v5vRE36/vrjHj4cOHcHJyKuwwiIiIqBDdv38fxYsXL+wwiCiPmNMTERERc3qi789315hRpEgRAB8veGZmZoUcDRERERWkN2/ewMnJScwHiEg7MacnIiL6fjGnJ/p+fXeNGVmPoZuZmbHiQ0RE9J1itzRE2o05PRERETGnJ/r+sGM5IiIiIiIiIiIiIiLSaGzMICIiIiIiIiIiIiIijcbGDCIiIiIiIiIiIiIi0mjf3ZgZRERERJogMzMT6enphR0GaQF9fX3o6PAeJCIiIiJNw5ye6OupUt9hYwYRERFRARIEAY8fP8arV68KOxTSEjo6OnB1dYW+vn5hh0JEREREYE5PpE6q1HfYmEFERERUgLIqPba2tjA2NoZEIinskEiDyWQyPHz4EI8ePUKJEiV4vhARERFpAOb0ROqhan2HjRlEREREBSQzM1Os9FhZWRV2OKQlbGxs8PDhQ2RkZEBPT6+wwyEiIiL6rjGnJ1IvVeo77HyXiIiIqIBk9adrbGxcyJGQNsl63DozM7OQIyEiIiIi5vRE6qVKfYeNGUREREQFjI+hkyp4vhARERFpHuZoROqhymeJjRlERERERERERERERKTR2JhBRERE9I1KSEiARCLBxYsXv2o9EyZMgIeHh/g6ICAArVu3/qp1ahIfHx8EBwerfb2fHzciIiIiou9ZftYjvqU6irrqcdlxcXFBWFiY2tdbUAq1MePo0aPw8/ODg4MDJBIJtm/f/sVloqOjUa1aNRgYGKB06dKIiorK9ziJiIiINE1AQAAkEon4z8rKCk2bNsXly5fFMk5OTnj06BEqVaqk1m3Pnz+/QHKwrH3s16+fwrwBAwZAIpEgICBA6fVFR0dDIpHg1atX6guSmNMTERERFbJP6wb6+vooXbo0Jk2ahIyMjK9er6Y1EOT0Q39B1VGyjvPp06flpqempsLKygoSiQTR0dFKr08Tj7EmK9TGjHfv3sHd3R2LFy9Wqnx8fDxatGiBBg0a4OLFiwgODkbv3r2xb9++fI6UiIiISPM0bdoUjx49wqNHj3Dw4EHo6uqiZcuW4nypVAp7e3vo6uqqdbvm5uawsLBQ6zpz4uTkhA0bNuDDhw/itJSUFPzxxx8oUaJEgcRAuWNOT0RERFT4suoGt2/fxvDhwzFhwgTMnj07T+vKzMyETCZTW2zqXl92CrqOEhkZKTdt27ZtMDU1LZDtf88KtTGjWbNmmDJlCtq0aaNU+aVLl8LV1RVz585F+fLlMXDgQLRv3x6//fZbPkdKREREpHkMDAxgb28Pe3t7eHh4YNSoUbh//z6ePn0KQPGupawnEw4ePIjq1avD2NgYderUwc2bN+XWO2PGDNjZ2aFIkSLo1asXUlJS5OZ/fveQj48PBg8ejJEjR8LS0hL29vaYMGGC3DI3btxAvXr1YGhoiAoVKuDAgQNK3cVfrVo1ODk5YevWreK0rVu3okSJEqhatapcWZlMhunTp8PV1RVGRkZwd3fH5s2bxWPRoEEDAEDRokUVnuqQyWS5xp+YmIhWrVrB1NQUZmZm6NixI548eaLScftWMacnIiIiKnxZdQNnZ2f0798fjRs3xo4dOwB8fGogJCQEjo6OMDExQa1ateSeHoiKioKFhQV27NiBChUqwMDAAD179sTq1avx119/iU8jREdHZ/u088WLFyGRSJCQkJDj+hITE8XyEydOhI2NDczMzNCvXz+kpaWJ8/bu3Yt69erBwsICVlZWaNmyJe7cuSPOd3V1BQBUrVoVEokEPj4+ABTrKKmpqRg8eDBsbW1haGiIevXq4ezZs+J8ZetG2fH391e44WrVqlXw9/dXKHv//n107NgRFhYWsLS0RKtWrcTjNGHChGyPcZa7d++iQYMGMDY2hru7O06dOiW37i1btqBixYowMDCAi4sL5s6dKzc/KSkJfn5+MDIygqurK9atW/fFfdN06r1NL5+dOnUKjRs3lpvm6+ubax/HqampSE1NFV+/efMmv8LLUUpKCu7fv5+v23BycoKhoWG+boOIiIg0V3JyMtauXYvSpUvDysoq17JjxozB3LlzYWNjg379+qFnz544ceIEAODPP//EhAkTsHjxYtSrVw9r1qzBggULULJkyVzXuXr1agwbNgwxMTE4deoUAgICULduXTRp0gSZmZlo3bo1SpQogZiYGLx9+xbDhw9Xet969uyJyMhIdO3aFcDHikKPHj0UHt+ePn061q5di6VLl8LNzQ1Hjx7FL7/8AhsbG9SrVw9btmxBu3btcPPmTZiZmcHIyEip+GUymdiQceTIEWRkZGDAgAH4+eefxRjyety+R8zps8d8noiIiNTJyMgIz58/BwAMHDgQ169fx4YNG+Dg4IBt27ahadOmuHLlCtzc3AAA79+/x8yZM7FixQpYWVmhWLFi+PDhA968eSM+hWBpaYmTJ08qtf3P12drawsAOHjwIAwNDREdHY2EhAT06NEDVlZWmDp1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I7LMmDED7dq1Q7du3VCtWjXExcVh3759KFq0qNKxfolEIoG1tbX4tMjnjI2NcfToUZQoUQJt27ZF+fLlxW6psuo12R1jZVSrVg1//vknNmzYgEqVKmH8+PGYNGkSAgICxDKRkZFwcHCAt7c32rZti8DAQNja2n71fhcmifBpB2ffgTdv3sDc3ByvX7/OtTJcWG7fvo2goCCEh4ezf10iIiI1K+w8ICUlBfHx8XB1dVXpR/xv0YkTJ1CvXj3ExcWhVKlShR2ORuN5o6iwP8tfwpyeiIgo/xR2HsDcjEi9VPlMadUA4ERERESknbZt2wZTU1O4ubkhLi4OQ4YMQd26ddmQQUREREREREphYwYRERER5bu3b9/if//7HxITE2FtbY3GjRur1LcuERERERERfd/YmEFERERE+a579+5yg0ATERERERERqaJQBwAnIiIiIiIiIiIiIiL6EjZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFG0y3sAIiIiIgISEpKwuvXrwtse+bm5rC1tS2w7WWJiopCcHAwXr16BQCYMGECtm/fjosXLwIAAgIC8OrVK2zfvr3AY8tvLi4uCA4ORnBwcGGHQkRERET54HvJ6bN8y7k7aSY2ZhAREREVsqSkJPTs0QOpaWkFtk0DfX2sioxUqfLz+PFjTJ06Fbt27cKDBw9ga2sLDw8PBAcHo1GjRmqJa/78+RAEQS3r+pKC2B8iIiIi+j5oS06vTgWZuxMBbMwgIiIiKnSvX79Galoa+pWtBgfjIvm+vYfv32LpzfN4/fq10hWfhIQE1K1bFxYWFpg9ezYqV66M9PR07Nu3DwMGDMCNGzfUEpu5ubla1vMl+bU/mZmZkEgk0NFhb65ERERE3xNtyOnVraByd6IsrGURERERaQgH4yJwMbXI9395qVwFBQVBIpHgzJkzaNeuHcqUKYOKFSti2LBhOH36tFhu3rx5qFy5MkxMTODk5ISgoCAkJycrvZ2AgAC0bt1afO3j44PBgwdj5MiRsLS0hL29PSZMmCC3zI0bN1CvXj0YGhqiQoUKOHDgACQSSa6Pu6trf6KiomBhYYEdO3agQoUKMDAwQGJiIpKSkuDn5wcjIyO4urpi3bp1ctsXBAET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zHD06FGEhYXhzZs3cHZ2xty5c9GsWTMAwMqVKxEYGIhq1arByckJ06ZNQ0hIiLi8Mrl3mTJl8M8//+DXX39FzZo1YWRkhFq1aqFz5875fixIM0gEVUZA/Aa8efMG5ubmeP36NczMzAo7HAW3b99GUFAQwsPD4ebmVtjhEBERfVMKOw9ISUlBfHw8XF1d5X5oT0pKQs8ePZD6FeNKqMpAXx+rIiNha2tbYNssKCdOnEC9evUQFxeHUqVKFXY4Xy2n8+Z7Vtif5S9hTk9ERJR/CjsPYE5PpF6q1Hf4ZAYRERFRIbO1tcWqyEi8fv26wLZpbm7+zVR6tm3bBlNTU7i5uSEuLg5DhgxB3bp1v4mGDCIiIiLSDszpifIfGzOIiIiINICtrS0rInn09u1b/O9//0NiYiKsra3RuHFjzJ07t7DDIiIiIqLvDHN6ovzFxgwiIiIi0mrdu3fP13E5iIiIiIiIqPDpFHYAREREREREREREREREuWFjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNN3CDoCIiIiIgKSkJLx+/brAtmdubg5bW1u1r1cikWDbtm1o3bq12tdNRERERKTJND2n9/HxgYeHB8LCwvIvKKJ8xMYMIiIiokKWlJSEnj0CkJqWXmDbNNDXw6rIKJUbNB4/foypU6di165dePDgAWxtbeHh4YHg4GA0atToi8snJCTA1dU123mnTp3CDz/8AABIS0tDWFgY1q1bh9u3b8PY2Bhly5ZF79698csvv0BPT0+luImIiIiI8pM25fRE2oqNGURERESF7PXr10hNS0cDDwFFTfN/ey+TgcMX0/H69WuVKj4JCQmoW7cuLCwsMHv2bFSuXBnp6enYt28fBgwYgBs3bii9rgMHDqBixYpy06ysrAB8bMjw9fXFpUuXMHnyZNStWxdmZmY4ffo05syZg6pVq8LDw0PpbRERERER5Tdtyem/Venp6bzh6TvAMTOIiIiINERRU8DaPP//5bVyFRQUBIlEgjNnzqBdu3YoU6YMKlasiGHDhuH06dNiuWfPnqFNmzYwNjaGm5sbduzYobAuKysr2Nvby/3LqnyEhYXh6NGjOHjwIAYMGAAPDw+ULFkSXbp0QUxMDNzc3HKM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b/VcZX6Xd9p67FiPIyLKX4q3PRWgYcOGYfny5Vi9ejViY2PRv39/vHv3Dj169AAAdO/eXW4wQT8/PyxZsgQbNmxAfHw89u/fj3HjxsHPz0+jBnwizSWVShEYGIiYmBiEhobi+vXreP/+Pa5fv47Q0FDExMQgMDCQ5xMVOn19fbRt2xYvX75Ely5dsGvXLjx79gy7du1Cly5d8PLlS7Rt25aDxn1DeH36Pujp6QH4+GMukbLS0tIAQGM//8zpiYiyx/wu77T12LEe932RyWSFHQLRN0GVm7cKdcyMn3/+GU+fPsX48ePx+PFjeHh4YO/eveIAgomJiXKt72PHjoVEIsHYsWPx4MED2NjYwM/PD1OnTi2sXSAt5OXlhXHjxiEiIgJDhgwRp9vb22PcuHHw8vIqxOiI/k9gYCAAYOvWrQgLCxOnS6VSdOjQQZxP3w5en759UqkUFhYWSEpKAgAYGxtDIpEUclSkyWQyGZ4+fQpjY2OFgUQ1BXN6IqKcMb/LO209dqzHffv09fWho6ODhw8fwsbGBvr6+szpifJIEAQ8ffoUEolEvPkvN4VeIxo4cGCOj6BHR0fLvdbV1UVoaChCQ0MLIDL6lnl5eaFOnTq4evUqnj9/DisrK1SqVEnj7uogCgwMREBAAHbu3ImHDx/CwcEBfn5+vJPnG8br07cvaxyBrAYNoi/R0dFBiRIlNLqSzJyeiChnzO/yTluPHetx3zYdHR24urri0aNHHMydSA0kEgmKFy+u1LW90BsziAqLVCqFu7t7YYdB9EX6+vpo165dYYdBBYjXp2+bRCJBsWLFYGtri/T09MIOh7RA1t1/RESkvZjf5Z22HjvW475t+vr6KFGiBDIyMpCZmVnY4RBpNT09PaUbqdmYQURERFQIpFKpxt9VSERERERE2cvqFkeZrnGISD14ixcREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUb7qsaMlJQUdcVBRERERESFgDk9ERERERFpA5UbM2QyGSZPngxHR0eYmpri7t27AIBx48Zh5cqVag+QiIiIiIjUizk9ERERERFpG5UbM6ZMmYKoqCjMmjUL+vr64vRKlSphxYoVag2OiIiIiIjUjzk9ERERERFpG5UbM37//XdERESga9eukEql4nR3d3fcuHFDrcEREREREZH6MacnIiIiIiJto3JjxoMHD1C6dGmF6TKZDOnp6WoJioiIiIiI8g9zeiIiIiIi0jYqN2ZUqFABx44dU5i+efNmVK1aVS1BERERERFR/mFOT0RERERE2kZX1QXGjx8Pf39/PHjwADKZDFu3bsXNmzfx+++/4++//86PGImIiIiISI2Y0xMRERERkbZR+cmMVq1aYefOnThw4ABMTEwwfvx4xMbGYufOnWjSpEl+xEhERERERGrEnJ6IiIiIiLSNyk9mAICXlxf279+v7liI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kxP27duHoUOHokqVKnB0dMSQIUPwv//9T9XdoO9cYGAgAgICsHPnTjx8+BAODg7w8/PjXQakcXiufh0vLy/UqVMHV69exfPnz2FlZYVKlSrxbs18wHOVSLswpydtx+94ou+DtuaYjJuIKH+o3Jgxbdo0vH37FgAwdepUdO/eHf3794ebmxtWrlypcgADBw7EwIEDs50XHR2tMK127do4ffq0ytsh+py+vj7atWtX2GEQfRHP1a8jlUrh7u5e2GF8F3iuEmkP5vT0LeB3PNH3QVtzTMZNRKR+KjdmVK9eXfzb1tYWe/fuVWtARERERESUv5jTExERERGRtlF5APCcnD9/Hi1btlTX6oiIiIiIqIAxpyciIiIiIk2lUmPGvn37EBISgl9//RV3794FANy4cQOtW7dGjRo1IJPJ8iVIIiIiIiJSD+b0RERERESkjZTuZmrlypXo06cPLC0t8fLlS6xYsQLz5s3DoEGD8PPPP+Pq1asoX758fsZKRERERERfgTk9ERERERFpK6WfzJg/fz5mzpyJZ8+e4c8//8SzZ88QHh6OK1euYOnSpaz0EBERERFpOOb0RERERESkrZRuzLhz5w46dOgAAGjbti10dXUxe/ZsFC9ePN+CIyIiIiIi9WFOT0RERERE2krpxowPHz7A2NgYACCRSGBgYIBixYrlW2BERERERKRezOmJiIiIiEhbKT1mBgCsWLECpqamAICMjAxERUXB2tparszgwYPVFx0REREREakVc3oiIiIiItJGSjdmlChRAsuXLxdf29vbY82aNXJlJBIJKz5ERERERBqKOT0REREREWkrpRszEhIS8jEMIiIiIiLKb8zpiYiIiIhIWyk9ZgYREREREREREREREVFhYGMGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUbLU2PGnTt3MHbsWHTu3BlJSUkAgD179uDatWtqDY6IiIiIiPIHc3oiIiIiItImKjdmHDlyBJUrV0ZMTAy2bt2K5ORkAMClS5cQGhqq9gCJiIiIiEi9mNMTEREREZG2UbkxY9SoUZgyZQr2798PfX19cXrDhg1x+vRptQZHRERERETqx5yeiIiIiIi0jcqNGVeuXEGbNm0Uptva2uLZs2dqCYqIiIiIiPIPc3oiIiIiItI2KjdmWFhY4NGjRwrTL1y4AEdHR7UERURERERE+Yc5PRERERERaRuVGzM6deqE//3vf3j8+DEkEglkMhlOnDiBkJAQdO/ePT9iJCIiIiIiNWJOT0RERERE2kblxoxp06ahXLlycHJyQnJyMipUqID69eujTp06GDt2bH7ESEREREREasScnoiIiIiItI2uqgvo6+tj+fLlGD9+PK5cuYLk5GRUrVoVbm5u+REfERERERGpGXN6IiIiIiLSNio3ZmRxcnKCk5MTMjMzceXKFbx8+RJFixZVZ2xERERERJSPmNMTEREREZG2ULmbqeDgYKxcuRIAkJmZCW9vb1SrVg1OTk6Ijo5Wd3xERERERKRmzOmJiIiIiEjbqNyYsXnzZri7uwMAdu7cibt37+LGjRsYOnQoxowZo/YAiYiIiIhIvZjTExERERGRtlG5MePZs2ewt7cHAOzevRsdO3ZEmTJl0LNnT1y5ckXtARIRERERkXoxpyciIiIiIm2jcmOGnZ0drl+/jszMTOzduxdNmjQBALx//x5SqVTtARIRERERkXoxpyciIiIiIm2j8gDgPXr0QMeOHVGsWDFIJBI0btwYABATE4Ny5cqpPUAiIiIiIlIv5vRERERERKRtVG7MmDBhAipVqoT79++jQ4cOMDAwAABIpVKMGjVK7QESEREREZF6MacnIiIiIiJto3JjBgC0b99eYZq/v/9XB0NERERERAWDOT0REREREWmTPDVmHDx4EAcPHkRSUhJkMpncvFWrVqklMCIiIiIiyj/M6YmIiIiISJuo3JgxceJETJo0CdWrVxf72CUiIiIiIu3BnJ6IiIiIiLSNyo0ZS5cuRVRUFLp165Yf8RARERERUT5jTk9ERERERNpGR9UF0tLSUKdOnfyIhYiIiIiICgBzeiIiIiIi0jYqN2b07t0bf/zxR37EQkREREREBYA5PRERERERaRuVu5lKSUlBREQEDhw4gCpVqkBPT09u/rx589QWHBERERERqR9zeiIiIiIi0jYqN2ZcvnwZHh4eAICrV6/KzePAgUREREREmo85PRERERERaRuVGzMOHz6cH3EQEREREVEBYU5PRERERETaRuUxMz7133//4b///lNXLEREREREVMCY0xMRERERkTZQuTFDJpNh0qRJMDc3h7OzM5ydnWFhYYHJkydDJpPlR4xERERERKRGzOmJiIiIiEjbqNzN1JgxY7By5UrMmDEDdevWBQAcP34cEyZMQEpKCqZOnar2IImIiIiISH2Y0xMRERERkbZRuTFj9erVWLFiBX766SdxWpUqVeDo6IigoCBWfIiIiIiINBxzeiIiIiIi0jYqdzP14sULlCtXTmF6uXLl8OLFC7UERURERERE+Yc5PRERERERaRuVGzPc3d2xaNEihemLFi2Cu7u7WoIiIiIiIqL8w5yeiIiIiIi0jcrdTM2aNQstWrTAgQMHULt2bQDAqVOncP/+fezevVvtARIRERERkXoxpyciIiIiIm2j8pMZ3t7euHXrFtq0aYNXr17h1atXaNu2LW7evAkvL6/8iJGIiIiIiNSIOT0REREREWkblZ/MAAAHBwcOCkhEREREpMWY0xMRERERkTbJU2PGy5cvsXLlSsTGxgIAKlSogB49esDS0lKtwRERERERUf5gTk9ERERERNpE5W6mjh49ChcXFyxYsAAvX77Ey5cvsWDBAri6uuLo0aP5ESMREREREakRc3oiIiIiItI2Kj+ZMWDAAPz8889YsmQJpFIpACAzMxNBQUEYMGAArly5ovYgiYiIiIhIfZjTExERERGRtlH5yYy4uDgMHz5crPQAgFQqxbBhwxAXF6fW4IiIiIiISP2Y0xMRERERkbZRuTGjWrVqYr+6n4qNjYW7u7tagiIiIiIiovzDnJ6IiIiIiLSNyt1MDR48GEOGDEFcXBx++OEHAMDp06exePFizJgxA5cvXxbLVqlSRX2REhERERGRWjCnJyIiIiIibaNyY0bnzp0BACNHjsx2nkQigSAIkEgkyMzM/PoIiYiIiIhIrZjTExERERGRtlG5MSM+Pj4/4iAiIiIiogLCnJ6IiIiIiLSNyo0Zzs7O+REHEREREREVEOb0RERERESkbVQeAHz16tXYtWuX+HrkyJGwsLBAnTp1cO/ePbUGR0RERERE6secnoiIiIiItI3KjRnTpk2DkZERAODUqVNYtGgRZs2aBWtrawwdOlTtARIRERERkXoxpyciIiIiIm2jcjdT9+/fR+nSpQEA27dvR/v27REYGIi6devCx8dH3fEREREREZGaMacnIiIiIiJto/KTGaampnj+/DkA4J9//kGTJk0AAIaGhvjw4UOegli8eDFcXFxgaGiIWrVq4cyZM0ott2HDBkgkErRu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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Summary Statistics by Method Combination:\n", + "============================================================\n", + " Count Mean Median Std Min Max\n", + "binding_source perturbation_source \n", + "Calling Cards kemmeren 33 0.309 0.4 0.265 0.0 0.8\n", + " mahendrawada_rnaseq 33 0.103 0.0 0.167 0.0 0.6\n", + " mcisaac 33 0.297 0.2 0.292 0.0 1.0\n", + "ChEC-seq kemmeren 33 0.261 0.2 0.247 0.0 0.8\n", + " mahendrawada_rnaseq 33 0.091 0.0 0.181 0.0 0.8\n", + " mcisaac 33 0.255 0.2 0.284 0.0 0.8\n", + "Harbison kemmeren 81 0.074 0.0 0.177 0.0 0.8\n", + " mahendrawada_rnaseq 81 0.020 0.0 0.098 0.0 0.6\n", + " mcisaac 81 0.064 0.0 0.158 0.0 0.8\n", + "\n", + "Overall Performance by Binding Method:\n", + "========================================\n", + " Count Mean Median Std\n", + "binding_source \n", + "Calling Cards 99 0.236 0.2 0.262\n", + "ChEC-seq 99 0.202 0.2 0.252\n", + "Harbison 243 0.053 0.0 0.149\n" + ] + } + ], + "source": [ + "\n", + "# Combine all rank-response results for comparison\n", + "combined_results = []\n", + "\n", + "if len(cc_rr_res) > 0:\n", + " cc_rr_res['binding_source'] = 'Calling Cards'\n", + " combined_results.append(cc_rr_res)\n", + "\n", + "if len(chec_rr_res) > 0:\n", + " chec_rr_res['binding_source'] = 'ChEC-seq' \n", + " combined_results.append(chec_rr_res)\n", + "\n", + "if len(harb_rr_res) > 0:\n", + " harb_rr_res['binding_source'] = 'Harbison'\n", + " combined_results.append(harb_rr_res)\n", + "\n", + "if combined_results:\n", + " all_rr_data = pd.concat(combined_results, ignore_index=True)\n", + "\n", + " # Create comparative boxplot visualizations\n", + " fig, axes = plt.subplots(1, 2, figsize=(16, 6))\n", + "\n", + " # 1. Combined boxplot - binding methods grouped by perturbation source\n", + " sns.boxplot(data=all_rr_data, x='perturbation_source', y='response_rate', \n", + " hue='binding_source', ax=axes[0])\n", + " axes[0].set_title('Response Rate Performance by Perturbation and Binding Method')\n", + " axes[0].set_xlabel('Perturbation Source')\n", + " axes[0].set_ylabel('Response Rate')\n", + " axes[0].legend(title='Binding Method', bbox_to_anchor=(1.05, 1), loc='upper left')\n", + " axes[0].tick_params(axis='x', rotation=45)\n", + "\n", + " # 2. Faceted view - binding methods grouped by binding source \n", + " sns.boxplot(data=all_rr_data, x='binding_source', y='response_rate',\n", + " hue='perturbation_source', ax=axes[1])\n", + " axes[1].set_title('Response Rate Performance by Binding and Perturbation Method')\n", + " axes[1].set_xlabel('Binding Source')\n", + " axes[1].set_ylabel('Response Rate')\n", + " axes[1].legend(title='Perturbation Method', bbox_to_anchor=(1.05, 1), loc='upper left')\n", + " axes[1].tick_params(axis='x', rotation=45)\n", + "\n", + " plt.tight_layout()\n", + " plt.show()\n", + "\n", + " # Summary statistics table\n", + " print(\"Summary Statistics by Method Combination:\")\n", + " print(\"=\"*60)\n", + " summary_stats = all_rr_data.groupby(['binding_source', 'perturbation_source']).agg({\n", + " 'response_rate': ['count', 'mean', 'median', 'std', 'min', 'max']\n", + " }).round(3)\n", + " summary_stats.columns = ['Count', 'Mean', 'Median', 'Std', 'Min', 'Max']\n", + " print(summary_stats)\n", + "\n", + " # Overall performance by binding method\n", + " print(f\"\\nOverall Performance by Binding Method:\")\n", + " print(\"=\"*40)\n", + " binding_performance = all_rr_data.groupby('binding_source').agg({\n", + " 'response_rate': ['count', 'mean', 'median', 'std']\n", + " }).round(3)\n", + " binding_performance.columns = ['Count', 'Mean', 'Median', 'Std']\n", + " print(binding_performance)\n", + "\n", + "else:\n", + " print(\"No results available for comparison visualization\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "tfbpapi-py3.11", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/mkdocs.yml b/mkdocs.yml index e6fd78e..a751e9f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -152,6 +152,7 @@ nav: - "DataCard: Exploring Datasets": tutorials/datacard_tutorial.ipynb - "Cache Management": tutorials/cache_manager_tutorial.ipynb - "Querying the Datasets": tutorials/hfqueryapi_tutorial.ipynb + - "Rank Response Analysis": tutorials/rank_response_tutorial.ipynb - API Reference: - Core Components: - HfQueryAPI: HfQueryAPI.md diff --git a/pyproject.toml b/pyproject.toml index 72a4b01..ee98517 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,6 +35,8 @@ mkdocs-autorefs = "^1.4.3" mkdocs-section-index = "^0.3.10" mkdocs-jupyter = "^0.25.1" mkdocstrings = {extras = ["python"], version = "^0.30.0"} +matplotlib = "^3.10.6" +seaborn = "^0.13.2" [tool.pytest.ini_options] diff --git a/tfbpapi/HfCacheManager.py b/tfbpapi/HfCacheManager.py index d8b9ad9..4d2e38e 100644 --- a/tfbpapi/HfCacheManager.py +++ b/tfbpapi/HfCacheManager.py @@ -25,8 +25,16 @@ def __init__( self.duckdb_conn = duckdb_conn self.logger = logger or logging.getLogger(__name__) - def _get_metadata_for_config(self, config) -> dict[str, Any]: - """Get metadata for a specific configuration using 3-case strategy.""" + def _get_metadata_for_config( + self, config, force_refresh: bool = False + ) -> dict[str, Any]: + """ + Get metadata for a specific configuration using 3-case strategy. + + :param config: Configuration object to process + :param force_refresh: If True, skip cache checks and download fresh from remote + + """ config_result = { "config_name": config.config_name, "strategy": None, @@ -38,31 +46,33 @@ def _get_metadata_for_config(self, config) -> dict[str, Any]: table_name = f"metadata_{config.config_name}" try: - # Case 1: Check if metadata already exists in DuckDB - if self._check_metadata_exists_in_duckdb(table_name): - config_result.update( - { - "strategy": "duckdb_exists", - "table_name": table_name, - "success": True, - "message": f"Metadata table {table_name} " - "already exists in DuckDB", - } - ) - return config_result - - # Case 2: Check if HF data is in cache, create DuckDB representation - if self._load_metadata_from_cache(config, table_name): - config_result.update( - { - "strategy": "cache_loaded", - "table_name": table_name, - "success": True, - "message": "Loaded metadata from cache " - f"into table {table_name}", - } - ) - return config_result + # Skip cache checks if force_refresh is True + if not force_refresh: + # Case 1: Check if metadata already exists in DuckDB + if self._check_metadata_exists_in_duckdb(table_name): + config_result.update( + { + "strategy": "duckdb_exists", + "table_name": table_name, + "success": True, + "message": f"Metadata table {table_name} " + "already exists in DuckDB", + } + ) + return config_result + + # Case 2: Check if HF data is in cache, create DuckDB representation + if self._load_metadata_from_cache(config, table_name): + config_result.update( + { + "strategy": "cache_loaded", + "table_name": table_name, + "success": True, + "message": "Loaded metadata from cache " + f"into table {table_name}", + } + ) + return config_result # Case 3: Download from HF (explicit vs embedded) if self._download_and_load_metadata(config, table_name): @@ -157,12 +167,21 @@ def _download_and_load_metadata(self, config, table_name: str) -> bool: if file_path.exists() and file_path.suffix == ".parquet": downloaded_files.append(str(file_path)) else: - # Handle wildcard patterns - parent_dir = Path(downloaded_path) / Path(pattern).parent - if parent_dir.exists(): + # Handle wildcard patterns, including nested wildcards + if "*" in pattern: + # Use glob on the full pattern relative to downloaded_path + base_path = Path(downloaded_path) + matching_files = list(base_path.glob(pattern)) downloaded_files.extend( - [str(f) for f in parent_dir.glob("*.parquet")] + [str(f) for f in matching_files if f.suffix == ".parquet"] ) + else: + # Handle non-wildcard patterns that might be directories + parent_dir = Path(downloaded_path) / Path(pattern).parent + if parent_dir.exists(): + downloaded_files.extend( + [str(f) for f in parent_dir.glob("*.parquet")] + ) if not downloaded_files: self.logger.warning( diff --git a/tfbpapi/HfQueryAPI.py b/tfbpapi/HfQueryAPI.py index ddca995..02ae684 100644 --- a/tfbpapi/HfQueryAPI.py +++ b/tfbpapi/HfQueryAPI.py @@ -1,10 +1,13 @@ import logging +import re from pathlib import Path from typing import Literal +import duckdb import pandas as pd -from .constants import CACHE_DIR +from .constants import CACHE_DIR, SQL_FILTER_KEYWORDS +from .errors import InvalidFilterFieldError from .HfCacheManager import HfCacheManager @@ -17,6 +20,7 @@ def __init__( repo_type: Literal["model", "dataset", "space"] = "dataset", token: str | None = None, cache_dir: str | Path | None = None, + duckdb_conn: duckdb.DuckDBPyConnection = duckdb.connect(":memory:"), ): """ Initialize the minimal HF Query API client. @@ -27,10 +31,7 @@ def __init__( :param cache_dir: HF cache_dir for downloads """ - # No DuckDB connection needed for metadata-only functionality - import duckdb - - self._duckdb_conn = duckdb.connect(":memory:") + self._duckdb_conn = duckdb_conn # Initialize parent with minimal setup super().__init__( @@ -45,7 +46,9 @@ def __init__( self.cache_dir = Path(cache_dir) if cache_dir is not None else CACHE_DIR # Filter storage system - self._table_filters: dict[str, str] = {} # config_name -> SQL WHERE clause + # dict structure: + # {config_name: "SQL WHERE clause", ...} + self._table_filters: dict[str, str] = {} @property def cache_dir(self) -> Path: @@ -59,124 +62,494 @@ def cache_dir(self, value: str | Path) -> None: raise FileNotFoundError(f"Cache directory {path} does not exist") self._cache_dir = path - def get_metadata(self, config_name: str | None = None) -> pd.DataFrame: + def _get_explicit_metadata(self, config, table_name: str) -> pd.DataFrame: + """Helper function to handle explicit metadata configurations.""" + sql = f"SELECT * FROM {table_name}" + return self.duckdb_conn.execute(sql).fetchdf() + + def _get_embedded_metadata(self, config, table_name: str) -> pd.DataFrame: + """Helper function to handle embedded metadata configurations.""" + if config.metadata_fields is None: + raise ValueError(f"Config {config.config_name} has no metadata fields") + fields = ", ".join(config.metadata_fields) + where_clauses = " AND ".join( + [f"{field} IS NOT NULL" for field in config.metadata_fields] + ) + sql = f""" + SELECT DISTINCT {fields}, COUNT(*) as count + FROM {table_name} + WHERE {where_clauses} + GROUP BY {fields} + ORDER BY count DESC """ - Retrieve metadata as a DataFrame with actual metadata values. + return self.duckdb_conn.execute(sql).fetchdf() - For explicit metadata (dataset_type == METADATA): Returns all rows from metadata - table. For embedded metadata (has metadata_fields): Returns distinct - combinations of metadata fields. + def _validate_metadata_fields( + self, config_name: str, field_names: list[str] + ) -> None: + """ + Validate that field names exist in the config's metadata columns. - :param config_name: Optional specific config name, otherwise returns all - metadata - :return: DataFrame with metadata values - :raises ValueError: If config_name is specified but not found + :param config_name: Configuration name to validate against + :param field_names: List of field names to validate + :raises InvalidFilterFieldError: If any fields don't exist in metadata """ - # Get explicit metadata configurations - explicit_metadata_configs = self.dataset_card.get_metadata_configs() - - # Get data configurations that have embedded metadata - # (metadata_fields specified) - embedded_metadata_configs = [ - config - for config in self.dataset_card.get_data_configs() - if config.metadata_fields - ] - - # Combine both types - all_metadata_sources = explicit_metadata_configs + embedded_metadata_configs - - if not all_metadata_sources: - # Return empty DataFrame - return pd.DataFrame() - - # Filter by config_name if specified - if config_name: - matching_configs = [ - c for c in all_metadata_sources if c.config_name == config_name - ] - if not matching_configs: - available_names = [c.config_name for c in all_metadata_sources] - raise ValueError( - f"Config '{config_name}' not found. " - f"Available metadata configs: {available_names}" + if not field_names: + return + + try: + metadata_df = self.get_metadata(config_name) + if metadata_df.empty: + raise InvalidFilterFieldError( + config_name=config_name, + invalid_fields=field_names, + available_fields=[], ) - configs_to_process = matching_configs - else: - configs_to_process = all_metadata_sources - # Process each configuration and collect DataFrames - dataframes = [] - for config in configs_to_process: - # Ensure the data/metadata is loaded - config_result = self._get_metadata_for_config(config) + available_fields = list(metadata_df.columns) + invalid_fields = [ + field for field in field_names if field not in available_fields + ] - if not config_result.get("success", False): - self.logger.warning( - f"Failed to load data for config {config.config_name}" + if invalid_fields: + raise InvalidFilterFieldError( + config_name=config_name, + invalid_fields=invalid_fields, + available_fields=available_fields, ) + except Exception as e: + if isinstance(e, InvalidFilterFieldError): + raise + # If metadata retrieval fails for other reasons, log warning but allow + self.logger.warning( + f"Could not validate filter fields for {config_name}: {e}" + ) + + def _extract_fields_from_sql(self, sql_where: str) -> list[str]: + """ + Extract potential field names from SQL WHERE clause. + + Uses a more robust approach to identify column references while avoiding string + literals used as values. + + :param sql_where: SQL WHERE clause (without 'WHERE' keyword) + :return: List of potential field names found in the SQL + + """ + if not sql_where.strip(): + return [] + + field_names = set() + + # Tokenize the SQL to better understand context + # This regex splits on key tokens while preserving them + tokens = re.findall( + r""" + \bIN\s*\([^)]+\)| # IN clauses with content + \bBETWEEN\s+\S+\s+AND\s+\S+| # BETWEEN clauses + (?:'[^']*')|(?:"[^"]*")| # Quoted strings + \b(?:AND|OR|NOT|IS|NULL|LIKE|BETWEEN|IN)\b| # SQL keywords + [=!<>]+| # Comparison operators + [(),]| # Delimiters + \b[a-zA-Z_][a-zA-Z0-9_]*\b| # Identifiers + \S+ # Other tokens + """, + sql_where, + re.VERBOSE | re.IGNORECASE, + ) + + # Track the context to determine if an identifier is a field name or value + i = 0 + while i < len(tokens): + token = tokens[i].strip() + if not token: + i += 1 + continue + + # Skip IN clauses entirely - they contain values, not field names + if re.match(r"\bIN\s*\(", token, re.IGNORECASE): + i += 1 continue - table_name = config_result.get("table_name") - if not table_name: - self.logger.warning(f"No table name for config {config.config_name}") + # Skip BETWEEN clauses entirely - they contain values, not field names + if re.match(r"\bBETWEEN\b", token, re.IGNORECASE): + i += 1 continue - try: - if config in explicit_metadata_configs: - # Explicit metadata: return all rows from metadata table - sql = f"SELECT * FROM {table_name}" - else: - # Embedded metadata: return distinct combinations of metadata fields - if config.metadata_fields is None: - raise ValueError( - f"Config {config.config_name} has no metadata fields" + # Handle quoted strings - could be identifiers or values depending on context + if token.startswith(("'", '"')): + # Extract the content inside quotes + quoted_content = token[1:-1] + + # Find next significant token to determine context + next_significant_token = None + for j in range(i + 1, len(tokens)): + next_token = tokens[j].strip() + if next_token and next_token not in [" ", "\n", "\t"]: + next_significant_token = next_token + break + + # Check if this quoted string is a field name based on context + is_quoted_field = False + + # Check what comes after this quoted string + if next_significant_token: + # If followed by comparison operators or SQL keywords, it's a field name + if ( + next_significant_token + in ["=", "!=", "<>", "<", ">", "<=", ">="] + or next_significant_token.upper() in ["IS", "LIKE", "NOT"] + or re.match( + r"\bBETWEEN\b", next_significant_token, re.IGNORECASE ) - fields = ", ".join(config.metadata_fields) - where_clauses = " AND ".join( - [f"{field} IS NOT NULL" for field in config.metadata_fields] - ) - sql = f""" - SELECT DISTINCT {fields}, COUNT(*) as count - FROM {table_name} - WHERE {where_clauses} - GROUP BY {fields} - ORDER BY count DESC - """ - - df = self.duckdb_conn.execute(sql).fetchdf() - - # Add config source column if multiple configs - if len(configs_to_process) > 1: - df["config_name"] = config.config_name - - dataframes.append(df) - - except Exception as e: - self.logger.error( - f"Error querying metadata for {config.config_name}: {e}" - ) + or re.match(r"\bIN\s*\(", next_significant_token, re.IGNORECASE) + ): + is_quoted_field = True + + # Also check what comes before this quoted string + if not is_quoted_field and i > 0: + # Find the previous significant token + prev_significant_token = None + for j in range(i - 1, -1, -1): + prev_token = tokens[j].strip() + if prev_token and prev_token not in [" ", "\n", "\t"]: + prev_significant_token = prev_token + break + + # If preceded by a comparison operator, could be a field name + # But we need to be very careful not to treat string literals as field names + if prev_significant_token and prev_significant_token in [ + "=", + "!=", + "<>", + "<", + ">", + "<=", + ">=", + ]: + # Only treat as field name if it looks like a database identifier + # AND doesn't look like a typical string value + if self._looks_like_identifier( + quoted_content + ) and self._looks_like_database_identifier(quoted_content): + is_quoted_field = True + + if is_quoted_field: + field_names.add(quoted_content) + + i += 1 + continue + + # Skip SQL keywords and operators + if token.upper() in SQL_FILTER_KEYWORDS or token in [ + "=", + "!=", + "<>", + "<", + ">", + "<=", + ">=", + "(", + ")", + ",", + ]: + i += 1 + continue + + # Skip numeric literals + if re.match(r"^-?\d+(\.\d+)?$", token): + i += 1 continue - # Combine all DataFrames - if not dataframes: - return pd.DataFrame() - elif len(dataframes) == 1: - return dataframes[0] + # Check if this looks like an identifier (field name) + if re.match(r"^[a-zA-Z_][a-zA-Z0-9_]*$", token): + # Check the context - if the next non-whitespace token is a comparison operator, + # then this is likely a field name + next_significant_token = None + for j in range(i + 1, len(tokens)): + next_token = tokens[j].strip() + if next_token and next_token not in [" ", "\n", "\t"]: + next_significant_token = next_token + break + + # Check if followed by a comparison operator or SQL keyword that indicates a field + is_field = False + + if next_significant_token: + # Direct comparison operators + if next_significant_token in [ + "=", + "!=", + "<>", + "<", + ">", + "<=", + ">=", + ]: + is_field = True + # SQL keywords that follow field names + elif next_significant_token.upper() in ["IS", "LIKE", "NOT"]: + is_field = True + # BETWEEN clause (could be just 'BETWEEN' or 'BETWEEN ... AND ...') + elif next_significant_token.upper() == "BETWEEN" or re.match( + r"\bBETWEEN\b", next_significant_token, re.IGNORECASE + ): + is_field = True + # IN clause (could be just 'IN' or 'IN (...)') + elif next_significant_token.upper() == "IN" or re.match( + r"\bIN\s*\(", next_significant_token, re.IGNORECASE + ): + is_field = True + + # If not a field yet, check other contexts + if not is_field and i > 0: + # Find the previous significant token + prev_significant_token = None + for j in range(i - 1, -1, -1): + prev_token = tokens[j].strip() + if prev_token and prev_token not in [" ", "\n", "\t"]: + prev_significant_token = prev_token + break + + # Case 1: After AND/OR and before an operator (original logic) + if ( + prev_significant_token + and prev_significant_token.upper() in ["AND", "OR"] + and next_significant_token + ): + # Same checks as above + if next_significant_token in [ + "=", + "!=", + "<>", + "<", + ">", + "<=", + ">=", + ]: + is_field = True + elif next_significant_token.upper() in ["IS", "LIKE", "NOT"]: + is_field = True + elif next_significant_token.upper() == "BETWEEN" or re.match( + r"\bBETWEEN\b", next_significant_token, re.IGNORECASE + ): + is_field = True + elif next_significant_token.upper() == "IN" or re.match( + r"\bIN\s*\(", next_significant_token, re.IGNORECASE + ): + is_field = True + + # Case 2: After a comparison operator (second operand) + elif prev_significant_token and prev_significant_token in [ + "=", + "!=", + "<>", + "<", + ">", + "<=", + ">=", + ]: + # But exclude function names (identifiers followed by '(') + if next_significant_token != "(": + is_field = True + + # Case 3: After opening parenthesis (function parameter) + elif prev_significant_token == "(": + is_field = True + + if is_field: + field_names.add(token) + + i += 1 + + return list(field_names) + + def _looks_like_identifier(self, content: str) -> bool: + """ + Determine if quoted content looks like an identifier rather than a string + literal. + + :param content: The content inside quotes + :return: True if it looks like an identifier, False if it looks like a string + literal + + """ + if not content: + return False + + # Basic identifier pattern: starts with letter/underscore, contains only alphanumeric/underscore + if re.match(r"^[a-zA-Z_][a-zA-Z0-9_]*$", content): + return True + + # Extended identifier pattern: could contain spaces if it's a column name like "quoted field" + # but not if it contains many special characters or looks like natural language + if " " in content: + # If it contains spaces, it should still look identifier-like + # Allow simple cases like "quoted field" but not "this is a long string value" + words = content.split() + if len(words) <= 3 and all( + re.match(r"^[a-zA-Z_][a-zA-Z0-9_]*$", word) for word in words + ): + return True + return False + + return False + + def _looks_like_database_identifier(self, content: str) -> bool: + """ + Determine if content looks like a database identifier (field/table name). + + This is more strict than _looks_like_identifier and helps distinguish between + quoted identifiers like "field_name" and string values like "value1". + + :param content: The content to check + :return: True if it looks like a database identifier + + """ + if not content: + return False + + # Database identifiers typically: + # 1. Don't start with numbers (field names rarely start with numbers) + # 2. Often contain underscores or descriptive words + # 3. Don't look like simple values + + # Reject if starts with a number (like "value1", "123abc") + if content[0].isdigit(): + return False + + # Handle short simple values that could be literals or field names + if len(content) <= 6 and re.match(r"^[a-z]+\d*$", content.lower()): + # Allow common field name prefixes + field_prefixes = ["field", "col", "column", "attr", "prop"] + if any(content.lower().startswith(prefix) for prefix in field_prefixes): + return True # It's a valid field name like "field1", "col2" + else: + return False # It's likely a simple value like "value", "test" + + # Accept if it contains underscore (common in field names) + if "_" in content: + return True + + # Accept if it has multiple words (like "quoted field") + if " " in content: + return True + + # Accept if it's a longer descriptive name + if len(content) > 8: + return True + + # Reject otherwise (likely a simple value) + return False + + def get_metadata( + self, config_name: str, refresh_cache: bool = False + ) -> pd.DataFrame: + """ + Retrieve metadata as a DataFrame with actual metadata values for a specific + config. + + Supports three types of metadata retrieval: + 1. Direct metadata configs: config_name is itself a metadata config + 2. Embedded metadata: config_name has metadata_fields defined + 3. Applied metadata: config_name appears in another metadata config's applies_to list + + For explicit metadata configs (types 1 & 3), returns all rows from metadata table. + For embedded metadata (type 2), returns distinct combinations of metadata fields. + + :param config_name: Specific config name to retrieve metadata for + :param refresh_cache: If True, force refresh from remote instead of using cache + :return: DataFrame with metadata values for the specified config + :raises ValueError: If config_name has no associated metadata + :raises RuntimeError: If data loading fails for the config + + """ + # Get metadata relationships for this config + relationships = self.get_metadata_relationships(refresh_cache=refresh_cache) + + relevant_relationships = None + + # First priority: data_config matches (config_name is a data config with metadata) + data_config_matches = [r for r in relationships if r.data_config == config_name] + + if data_config_matches: + relevant_relationships = data_config_matches else: - return pd.concat(dataframes, ignore_index=True) + # Second priority: metadata_config matches (config_name is itself a metadata config) + metadata_config_matches = [ + r for r in relationships if r.metadata_config == config_name + ] + relevant_relationships = metadata_config_matches + + if not relevant_relationships: + # Check what configs are available for helpful error message + all_data_configs = {r.data_config for r in relationships} + all_metadata_configs = {r.metadata_config for r in relationships} + all_available = sorted(all_data_configs | all_metadata_configs) + + if not all_available: + return pd.DataFrame() + + raise ValueError( + f"Config '{config_name}' not found. " + f"Available configs with metadata: {all_available}" + ) + + # Get the config object to process + # For explicit relationships, use the metadata config + # For embedded relationships, use the data config + relationship = relevant_relationships[0] # Use first relationship found + + if relationship.relationship_type == "explicit": + # Find the metadata config + if relationship.metadata_config == config_name: + # config_name is itself a metadata config + config = self.get_config(config_name) + else: + # config_name is a data config with metadata applied to it + config = self.get_config(relationship.metadata_config) + else: # embedded + # config_name is a data config with embedded metadata + config = self.get_config(config_name) + + if not config: + raise ValueError(f"Could not find config object for '{config_name}'") + + # Process the single configuration + config_result = self._get_metadata_for_config( + config, force_refresh=refresh_cache + ) + + if not config_result.get("success", False): + raise RuntimeError(f"Failed to load data for config {config.config_name}") + + table_name = config_result.get("table_name") + if not table_name: + raise RuntimeError(f"No table name for config {config.config_name}") + + try: + if relationship.relationship_type == "explicit": + return self._get_explicit_metadata(config, table_name) + else: # embedded + return self._get_embedded_metadata(config, table_name) + except Exception as e: + self.logger.error(f"Error querying metadata for {config.config_name}: {e}") + raise def set_filter(self, config_name: str, **kwargs) -> None: """ Set simple filters using keyword arguments. Converts keyword arguments to SQL WHERE clause and stores - for automatic application. + for automatic application. Validates that all filter fields + exist in the config's metadata columns. :param config_name: Configuration name to apply filters to :param kwargs: Filter conditions as keyword arguments (e.g., time=15, mechanism="ZEV") + :raises InvalidFilterFieldError: If any filter field doesn't exist + in the metadata columns Example: api.set_filter("hackett_2020", time=15, mechanism="ZEV", restriction="P") @@ -188,6 +561,9 @@ def set_filter(self, config_name: str, **kwargs) -> None: self.clear_filter(config_name) return + # Validate that all filter fields exist in metadata columns + self._validate_metadata_fields(config_name, list(kwargs.keys())) + # Convert kwargs to SQL WHERE clause conditions = [] for key, value in kwargs.items(): @@ -205,23 +581,37 @@ def set_filter(self, config_name: str, **kwargs) -> None: self._table_filters[config_name] = where_clause self.logger.info(f"Set filter for {config_name}: {where_clause}") - def set_sql_filter(self, config_name: str, sql_where: str) -> None: + def set_sql_filter( + self, config_name: str, sql_where: str, validate_fields: bool = True + ) -> None: """ Set complex filters using SQL WHERE clause. Stores raw SQL WHERE clause for automatic application to queries. + Validates that field references in the SQL exist in metadata columns + unless validation is disabled. :param config_name: Configuration name to apply filters to :param sql_where: SQL WHERE clause (without the 'WHERE' keyword) + :param validate_fields: Whether to validate field names (default: True) + :raises InvalidFilterFieldError: If any field references don't exist + in the metadata columns (when validate_fields=True) Example: api.set_sql_filter("hackett_2020", "time IN (15, 30) AND mechanism = 'ZEV'") + # To skip validation for complex SQL: + api.set_sql_filter("hackett_2020", "complex_expression(...)", validate_fields=False) """ if not sql_where.strip(): self.clear_filter(config_name) return + # Validate fields if requested + if validate_fields: + extracted_fields = self._extract_fields_from_sql(sql_where) + self._validate_metadata_fields(config_name, extracted_fields) + self._table_filters[config_name] = sql_where.strip() self.logger.info(f"Set SQL filter for {config_name}: {sql_where}") @@ -246,7 +636,9 @@ def get_current_filter(self, config_name: str) -> str | None: """ return self._table_filters.get(config_name) - def query(self, sql: str, config_name: str) -> pd.DataFrame: + def query( + self, sql: str, config_name: str, refresh_cache: bool = False + ) -> pd.DataFrame: """ Execute SQL query with automatic filter application. @@ -255,6 +647,7 @@ def query(self, sql: str, config_name: str) -> pd.DataFrame: :param sql: SQL query to execute :param config_name: Configuration name to query (table will be loaded if needed) + :param refresh_cache: If True, force refresh from remote instead of using cache :return: DataFrame with query results :raises ValueError: If config_name not found or query fails @@ -266,19 +659,21 @@ def query(self, sql: str, config_name: str) -> pd.DataFrame: """ # Validate config exists - if config_name not in [c.config_name for c in self.dataset_card.configs]: - available_configs = [c.config_name for c in self.dataset_card.configs] + if config_name not in [c.config_name for c in self.configs]: + available_configs = [c.config_name for c in self.configs] raise ValueError( f"Config '{config_name}' not found. " f"Available configs: {available_configs}" ) # Load the configuration data - config = self.dataset_card.get_config_by_name(config_name) + config = self.get_config(config_name) if not config: raise ValueError(f"Could not retrieve config '{config_name}'") - config_result = self._get_metadata_for_config(config) + config_result = self._get_metadata_for_config( + config, force_refresh=refresh_cache + ) if not config_result.get("success", False): raise ValueError( f"Failed to load data for config '{config_name}': " diff --git a/tfbpapi/HfRankResponse.py b/tfbpapi/HfRankResponse.py index b52f6f9..67a9c82 100644 --- a/tfbpapi/HfRankResponse.py +++ b/tfbpapi/HfRankResponse.py @@ -24,7 +24,7 @@ def __init__(self, db: IncrementalAnalysisDB): """ self.db = db - self.logger = logging.getLogger(__name__) + self.logger = logging.getLogger(self.__class__.__name__) def compute( self, diff --git a/tfbpapi/RankResponseAnalysis.py b/tfbpapi/RankResponseAnalysis.py new file mode 100644 index 0000000..036e08a --- /dev/null +++ b/tfbpapi/RankResponseAnalysis.py @@ -0,0 +1,277 @@ +""" +Simplified rank-response analysis for pre-arranged binding and perturbation data. + +This module provides a streamlined approach to rank-response analysis where: +1. Binding data is already ranked by strength (rank 1 = strongest binding) +2. Perturbation data provides a simple responsive TRUE/FALSE column +3. Analysis focuses on binning and statistical calculations + +""" + +import logging +from typing import Any, Dict, Optional, Tuple + +import numpy as np +import pandas as pd + + +class RankResponseAnalyzer: + """ + Simplified rank-response analyzer for pre-arranged data. + + Takes a DataFrame with target identifiers (pre-sorted by binding strength) and + responsive boolean values, then performs binning analysis. + + """ + + def __init__( + self, + data: pd.DataFrame, + target_col: str, + responsive_col: str, + bin_size: int = 100, + ): + """ + Initialize the rank-response analyzer. + + :param data: DataFrame with target identifiers and responsive booleans + :param target_col: Name of column containing target identifiers + :param responsive_col: Name of column containing TRUE/FALSE responsive values + :param bin_size: Number of targets per bin for analysis + :raises ValueError: If data validation fails + + """ + self.logger = logging.getLogger(self.__class__.__name__) + + # Store parameters + self.target_col = target_col + self.responsive_col = responsive_col + self.bin_size = bin_size + + # Validate and store data + self.data = self._validate_data(data) + self.n_targets = len(self.data) + self.n_bins = (self.n_targets + bin_size - 1) // bin_size # Ceiling division + + # Calculate overall statistics + self.total_responsive = self.data[responsive_col].sum() + self.overall_response_rate = self.total_responsive / self.n_targets + + self.logger.info( + f"Initialized RankResponseAnalyzer: {self.n_targets} targets, " + f"{self.total_responsive} responsive ({self.overall_response_rate:.1%}), " + f"{self.n_bins} bins of size {bin_size}" + ) + + def _validate_data(self, data: pd.DataFrame) -> pd.DataFrame: + """Validate input data and return cleaned version.""" + if not isinstance(data, pd.DataFrame): + raise ValueError("Data must be a pandas DataFrame") + + if len(data) == 0: + raise ValueError("Data cannot be empty") + + # Check required columns exist + if self.target_col not in data.columns: + raise ValueError(f"Target column '{self.target_col}' not found in data") + if self.responsive_col not in data.columns: + raise ValueError( + f"Responsive column '{self.responsive_col}' not found in data" + ) + + # Extract just the required columns + clean_data = data[[self.target_col, self.responsive_col]].copy() + + # Check for missing values + if clean_data[self.target_col].isna().any(): + raise ValueError( + f"Target column '{self.target_col}' contains missing values" + ) + if clean_data[self.responsive_col].isna().any(): + raise ValueError( + f"Responsive column '{self.responsive_col}' contains missing values" + ) + + # Validate responsive column is boolean-like + unique_values = set(clean_data[self.responsive_col].unique()) + valid_boolean_sets = [ + {True, False}, + {1, 0}, + {1.0, 0.0}, + {"TRUE", "FALSE"}, + {"True", "False"}, + {"true", "false"}, + {"T", "F"}, + ] + + # Allow subsets (e.g., only True values, only False values) + is_valid_boolean = any( + unique_values.issubset(valid_set) for valid_set in valid_boolean_sets + ) + + if not is_valid_boolean: + raise ValueError( + f"Responsive column '{self.responsive_col}' must contain boolean-like values. " + f"Found: {unique_values}" + ) + + # Convert to standard boolean + clean_data[self.responsive_col] = clean_data[self.responsive_col].astype(bool) + + # Reset index to ensure proper ranking (rank 1 = index 0) + clean_data = clean_data.reset_index(drop=True) + + self.logger.debug( + f"Validated data: {len(clean_data)} rows, {unique_values} -> boolean" + ) + + return clean_data + + def create_bins(self) -> pd.DataFrame: + """ + Create bins from the ranked data. + + :return: DataFrame with bin assignments for each target + + """ + bins_data = self.data.copy() + bins_data["rank"] = range(1, len(bins_data) + 1) + bins_data["bin"] = ((bins_data["rank"] - 1) // self.bin_size) + 1 + + return bins_data + + def calculate_bin_stats(self) -> pd.DataFrame: + """ + Calculate statistics for each bin. + + :return: DataFrame with bin-level statistics + + """ + bins_data = self.create_bins() + + bin_stats = [] + for bin_num in range(1, self.n_bins + 1): + bin_data = bins_data[bins_data["bin"] == bin_num] + + n_targets_in_bin = len(bin_data) + n_responsive_in_bin = bin_data[self.responsive_col].sum() + response_rate = ( + n_responsive_in_bin / n_targets_in_bin if n_targets_in_bin > 0 else 0 + ) + + # Calculate rank range for this bin + min_rank = bin_data["rank"].min() + max_rank = bin_data["rank"].max() + + bin_stats.append( + { + "bin": bin_num, + "min_rank": min_rank, + "max_rank": max_rank, + "n_targets": n_targets_in_bin, + "n_responsive": n_responsive_in_bin, + "response_rate": response_rate, + "enrichment_vs_overall": ( + response_rate / self.overall_response_rate + if self.overall_response_rate > 0 + else np.nan + ), + } + ) + + return pd.DataFrame(bin_stats) + + def get_bin_summary(self) -> pd.DataFrame: + """Get comprehensive bin-level summary statistics.""" + return self.calculate_bin_stats() + + def calculate_enrichment(self, reference_rate: float | None = None) -> pd.DataFrame: + """ + Calculate enrichment scores for each bin. + + :param reference_rate: Reference response rate for enrichment calculation. If + None, uses overall response rate. + :return: DataFrame with enrichment calculations + + """ + if reference_rate is None: + reference_rate = self.overall_response_rate + + if reference_rate <= 0: + raise ValueError( + "Reference rate must be greater than 0 for enrichment calculation" + ) + + bin_stats = self.calculate_bin_stats() + bin_stats["enrichment_vs_reference"] = ( + bin_stats["response_rate"] / reference_rate + ) + bin_stats["reference_rate"] = reference_rate + + return bin_stats + + def get_rank_response_curve(self, window_size: int | None = None) -> pd.DataFrame: + """ + Get data for plotting rank vs response rate curve with sliding window. + + :param window_size: Size of sliding window for smoothing. If None, uses + bin_size. + :return: DataFrame with rank positions and smoothed response rates + + """ + if window_size is None: + window_size = self.bin_size + + bins_data = self.create_bins() + curve_data = [] + + for i in range(len(bins_data)): + # Define window around current position + start_idx = max(0, i - window_size // 2) + end_idx = min(len(bins_data), i + window_size // 2 + 1) + + window_data = bins_data.iloc[start_idx:end_idx] + window_response_rate = window_data[self.responsive_col].mean() + + curve_data.append( + { + "rank": bins_data.iloc[i]["rank"], + "target": bins_data.iloc[i][self.target_col], + "responsive": bins_data.iloc[i][self.responsive_col], + "smoothed_response_rate": window_response_rate, + "window_size": len(window_data), + } + ) + + return pd.DataFrame(curve_data) + + def get_summary_stats(self) -> dict[str, Any]: + """Get overall summary statistics.""" + return { + "n_targets": self.n_targets, + "n_responsive": self.total_responsive, + "overall_response_rate": self.overall_response_rate, + "n_bins": self.n_bins, + "bin_size": self.bin_size, + "targets_per_bin_avg": self.n_targets / self.n_bins, + } + + def to_dataframe(self) -> pd.DataFrame: + """Export full results as a comprehensive DataFrame.""" + bins_data = self.create_bins() + bin_stats = self.calculate_bin_stats() + + # Merge bin statistics back to individual target data + result = bins_data.merge(bin_stats, on="bin", suffixes=("", "_bin")) + + return result + + def __repr__(self) -> str: + """String representation of the analyzer.""" + return ( + f"RankResponseAnalyzer(" + f"targets={self.n_targets}, " + f"responsive={self.total_responsive}, " + f"rate={self.overall_response_rate:.1%}, " + f"bins={self.n_bins})" + ) diff --git a/tfbpapi/constants.py b/tfbpapi/constants.py index 749678f..62a4227 100644 --- a/tfbpapi/constants.py +++ b/tfbpapi/constants.py @@ -9,3 +9,47 @@ def get_hf_token() -> str | None: """Get HuggingFace token from environment variable.""" return os.getenv("HF_TOKEN") + + +SQL_FILTER_KEYWORDS = sql_keywords = { + "AND", + "OR", + "NOT", + "IN", + "IS", + "NULL", + "TRUE", + "FALSE", + "LIKE", + "BETWEEN", + "EXISTS", + "ALL", + "ANY", + "SOME", + "CASE", + "WHEN", + "THEN", + "ELSE", + "END", + "CAST", + "AS", + "SELECT", + "FROM", + "WHERE", + "GROUP", + "ORDER", + "BY", + "HAVING", + "LIMIT", + "OFFSET", + "DISTINCT", + "COUNT", + "SUM", + "AVG", + "MIN", + "MAX", + "UPPER", + "LOWER", + "SUBSTR", + "LENGTH", +} diff --git a/tfbpapi/datainfo/datacard.py b/tfbpapi/datainfo/datacard.py index c58cd2d..5bcbd42 100644 --- a/tfbpapi/datainfo/datacard.py +++ b/tfbpapi/datainfo/datacard.py @@ -5,11 +5,7 @@ from pydantic import ValidationError -from ..errors import ( - DataCardError, - DataCardValidationError, - HfDataFetchError, -) +from ..errors import DataCardError, DataCardValidationError, HfDataFetchError from .fetchers import HfDataCardFetcher, HfRepoStructureFetcher, HfSizeInfoFetcher from .models import ( DatasetCard, @@ -224,8 +220,19 @@ def _extract_partition_values( self.logger.warning(f"Failed to extract partition values for {field_name}") return set() - def get_metadata_relationships(self) -> list[MetadataRelationship]: - """Get relationships between data configs and their metadata.""" + def get_metadata_relationships( + self, refresh_cache: bool = False + ) -> list[MetadataRelationship]: + """ + Get relationships between data configs and their metadata. + + :param refresh_cache: If True, force refresh dataset card from remote + + """ + # Clear cached dataset card if refresh requested + if refresh_cache: + self._dataset_card = None + relationships = [] data_configs = self.dataset_card.get_data_configs() metadata_configs = self.dataset_card.get_metadata_configs() @@ -244,9 +251,8 @@ def get_metadata_relationships(self) -> list[MetadataRelationship]: relationship_type="explicit", ) ) - continue - # Check for embedded metadata + # Check for embedded metadata (always runs regardless of explicit relationships) if data_config.metadata_fields: relationships.append( MetadataRelationship( diff --git a/tfbpapi/datainfo/models.py b/tfbpapi/datainfo/models.py index 980c9fa..b152248 100644 --- a/tfbpapi/datainfo/models.py +++ b/tfbpapi/datainfo/models.py @@ -30,6 +30,10 @@ class FeatureInfo(BaseModel): description="Data type (string, int64, float64, etc.) or categorical class labels", ) description: str = Field(..., description="Detailed description of the field") + role: str | None = Field( + default=None, + description="Semantic role of the feature (e.g., 'target_identifier', 'regulator_identifier', 'quantitative_measure')", + ) @field_validator("dtype", mode="before") @classmethod diff --git a/tfbpapi/errors.py b/tfbpapi/errors.py index 6952b79..4fc751e 100644 --- a/tfbpapi/errors.py +++ b/tfbpapi/errors.py @@ -214,3 +214,38 @@ def __init__( super().__init__(message, details) self.config_name = config_name self.field_name = field_name + + +class InvalidFilterFieldError(DatasetError): + """Raised when filter fields don't exist in metadata columns.""" + + def __init__( + self, + config_name: str, + invalid_fields: list[str], + available_fields: list[str] | None = None, + ): + invalid_str = ", ".join(f"'{field}'" for field in invalid_fields) + available_str = ( + f"Available fields: {sorted(available_fields)}" + if available_fields + else "No fields available" + ) + message = ( + f"Invalid filter field(s) {invalid_str} for config '{config_name}'. " + f"{available_str}" + ) + + super().__init__( + message, + details={ + "config_name": config_name, + "invalid_fields": invalid_fields, + "available_fields": ( + sorted(available_fields) if available_fields else [] + ), + }, + ) + self.config_name = config_name + self.invalid_fields = invalid_fields + self.available_fields = sorted(available_fields) if available_fields else [] diff --git a/tfbpapi/tests/datainfo/test_datacard.py b/tfbpapi/tests/datainfo/test_datacard.py index 59345b9..ce22578 100644 --- a/tfbpapi/tests/datainfo/test_datacard.py +++ b/tfbpapi/tests/datainfo/test_datacard.py @@ -7,11 +7,7 @@ from tfbpapi.datainfo import DataCard from tfbpapi.datainfo.models import DatasetType -from tfbpapi.errors import ( - DataCardError, - DataCardValidationError, - HfDataFetchError, -) +from tfbpapi.errors import DataCardError, DataCardValidationError, HfDataFetchError class TestDataCard: diff --git a/tfbpapi/tests/test_HfQueryAPI.py b/tfbpapi/tests/test_HfQueryAPI.py new file mode 100644 index 0000000..02ad783 --- /dev/null +++ b/tfbpapi/tests/test_HfQueryAPI.py @@ -0,0 +1,859 @@ +"""Comprehensive tests for HfQueryAPI class.""" + +import logging +from unittest.mock import MagicMock, Mock, patch + +import duckdb +import pandas as pd +import pytest + +from tfbpapi.datainfo.models import MetadataRelationship +from tfbpapi.errors import InvalidFilterFieldError +from tfbpapi.HfQueryAPI import HfQueryAPI + + +class TestHfQueryAPIInit: + """Test HfQueryAPI initialization.""" + + def test_init_basic(self): + """Test basic initialization.""" + conn = duckdb.connect(":memory:") + repo_id = "test/repo" + + with patch("tfbpapi.HfQueryAPI.HfCacheManager.__init__", return_value=None): + api = HfQueryAPI(repo_id, duckdb_conn=conn) + # Manually set properties that would be set by parent + api.repo_id = repo_id + api.duckdb_conn = conn + api._duckdb_conn = conn + api.logger = logging.getLogger("HfQueryAPI") + + assert api.repo_id == repo_id + assert api.duckdb_conn == conn + assert api._duckdb_conn == conn + + def test_init_with_all_params(self): + """Test initialization with all parameters.""" + import tempfile + + conn = duckdb.connect(":memory:") + repo_id = "test/repo" + token = "test_token" + + with tempfile.TemporaryDirectory() as cache_dir: + with patch("tfbpapi.HfQueryAPI.HfCacheManager.__init__", return_value=None): + api = HfQueryAPI( + repo_id=repo_id, + repo_type="model", + token=token, + cache_dir=cache_dir, + duckdb_conn=conn, + ) + # Manually set properties that would be set by parent + api.repo_id = repo_id + api.duckdb_conn = conn + api._duckdb_conn = conn + + assert api.repo_id == repo_id + assert api.duckdb_conn == conn + assert api._duckdb_conn == conn + assert api.repo_type == "model" + + +class TestHfQueryAPIHelpers: + """Test HfQueryAPI helper methods.""" + + @pytest.fixture + def mock_api(self): + """Create a mock HfQueryAPI instance.""" + conn = duckdb.connect(":memory:") + with patch("tfbpapi.HfQueryAPI.HfCacheManager.__init__", return_value=None): + api = HfQueryAPI("test/repo", duckdb_conn=conn) + api.duckdb_conn = conn + api._duckdb_conn = conn + api.logger = logging.getLogger("test") + return api + + def test_get_explicit_metadata(self, mock_api): + """Test _get_explicit_metadata helper.""" + # Mock config and return value + mock_config = Mock() + mock_config.config_name = "test_config" + + # Create mock DataFrame + expected_df = pd.DataFrame({"field1": [1, 2], "field2": ["a", "b"]}) + + # Replace the duckdb_conn with a mock + mock_conn = Mock() + mock_result = Mock() + mock_result.fetchdf.return_value = expected_df + mock_conn.execute.return_value = mock_result + mock_api.duckdb_conn = mock_conn + + result = mock_api._get_explicit_metadata(mock_config, "test_table") + + # Verify SQL was executed correctly + mock_conn.execute.assert_called_once_with("SELECT * FROM test_table") + pd.testing.assert_frame_equal(result, expected_df) + + def test_get_embedded_metadata(self, mock_api): + """Test _get_embedded_metadata helper.""" + # Mock config with metadata fields + mock_config = Mock() + mock_config.config_name = "test_config" + mock_config.metadata_fields = ["time", "mechanism"] + + expected_df = pd.DataFrame( + {"time": [15, 30], "mechanism": ["ZEV", "ZREV"], "count": [100, 50]} + ) + + # Replace the duckdb_conn with a mock + mock_conn = Mock() + mock_result = Mock() + mock_result.fetchdf.return_value = expected_df + mock_conn.execute.return_value = mock_result + mock_api.duckdb_conn = mock_conn + + result = mock_api._get_embedded_metadata(mock_config, "test_table") + + # Verify correct SQL was generated + expected_sql = """ + SELECT DISTINCT time, mechanism, COUNT(*) as count + FROM test_table + WHERE time IS NOT NULL AND mechanism IS NOT NULL + GROUP BY time, mechanism + ORDER BY count DESC + """ + mock_conn.execute.assert_called_once() + actual_sql = mock_conn.execute.call_args[0][0] + # Normalize whitespace for comparison + assert " ".join(actual_sql.split()) == " ".join(expected_sql.split()) + pd.testing.assert_frame_equal(result, expected_df) + + def test_get_embedded_metadata_no_fields(self, mock_api): + """Test _get_embedded_metadata with no metadata fields.""" + mock_config = Mock() + mock_config.config_name = "test_config" + mock_config.metadata_fields = None + + with pytest.raises(ValueError, match="has no metadata fields"): + mock_api._get_embedded_metadata(mock_config, "test_table") + + def test_extract_fields_from_sql_simple(self, mock_api): + """Test _extract_fields_from_sql with simple SQL.""" + sql = "time = 15 AND mechanism = 'ZEV'" + fields = mock_api._extract_fields_from_sql(sql) + + assert "time" in fields + assert "mechanism" in fields + # Note: The current regex may pick up quoted strings as identifiers + # This is a limitation we accept for simplicity + assert "15" not in fields # Should not include numeric literals + + def test_extract_fields_from_sql_complex(self, mock_api): + """Test _extract_fields_from_sql with complex SQL.""" + sql = "field1 IN (1, 2, 3) AND field2 IS NOT NULL AND field3 LIKE '%test%'" + fields = mock_api._extract_fields_from_sql(sql) + + assert "field1" in fields + assert "field2" in fields + assert "field3" in fields + assert "NULL" not in fields # SQL keyword should be excluded + assert "LIKE" not in fields # SQL keyword should be excluded + + def test_extract_fields_from_sql_quoted(self, mock_api): + """Test _extract_fields_from_sql with quoted identifiers.""" + sql = "\"quoted_field\" = 1 AND 'another_field' > 5" + fields = mock_api._extract_fields_from_sql(sql) + + assert "quoted_field" in fields + assert "another_field" in fields + + def test_validate_metadata_fields_success(self, mock_api): + """Test _validate_metadata_fields with valid fields.""" + # Mock get_metadata to return DataFrame with expected columns + metadata_df = pd.DataFrame( + {"time": [15, 30], "mechanism": ["ZEV", "ZREV"], "restriction": ["P", "A"]} + ) + + with patch.object(mock_api, "get_metadata", return_value=metadata_df): + # Should not raise any exception + mock_api._validate_metadata_fields("test_config", ["time", "mechanism"]) + + def test_validate_metadata_fields_invalid(self, mock_api): + """Test _validate_metadata_fields with invalid fields.""" + metadata_df = pd.DataFrame({"time": [15, 30], "mechanism": ["ZEV", "ZREV"]}) + + with patch.object(mock_api, "get_metadata", return_value=metadata_df): + with pytest.raises(InvalidFilterFieldError) as exc_info: + mock_api._validate_metadata_fields( + "test_config", ["invalid_field", "time"] + ) + + error = exc_info.value + assert "invalid_field" in error.invalid_fields + assert "time" not in error.invalid_fields + assert "time" in error.available_fields + assert error.config_name == "test_config" + + def test_validate_metadata_fields_empty_metadata(self, mock_api): + """Test _validate_metadata_fields with empty metadata.""" + empty_df = pd.DataFrame() + + with patch.object(mock_api, "get_metadata", return_value=empty_df): + with pytest.raises(InvalidFilterFieldError) as exc_info: + mock_api._validate_metadata_fields("test_config", ["any_field"]) + + error = exc_info.value + assert error.invalid_fields == ["any_field"] + assert error.available_fields == [] + + def test_validate_metadata_fields_empty_list(self, mock_api): + """Test _validate_metadata_fields with empty field list.""" + # Should not call get_metadata or raise any exception + with patch.object(mock_api, "get_metadata") as mock_get_metadata: + mock_api._validate_metadata_fields("test_config", []) + mock_get_metadata.assert_not_called() + + +class TestHfQueryAPIMainMethods: + """Test HfQueryAPI main methods.""" + + @pytest.fixture + def mock_api(self): + """Create a mock HfQueryAPI instance.""" + conn = duckdb.connect(":memory:") + with patch("tfbpapi.HfQueryAPI.HfCacheManager.__init__", return_value=None): + api = HfQueryAPI("test/repo", duckdb_conn=conn) + # Set up all necessary attributes + api.repo_id = "test/repo" + api.duckdb_conn = conn + api._duckdb_conn = conn + api.logger = logging.getLogger("test") + api._table_filters = {} + + # Set up the internal dataset card attribute + api._dataset_card = Mock() + return api + + def test_get_metadata_explicit_config(self, mock_api): + """Test get_metadata with explicit metadata config.""" + # Setup mock configurations + explicit_config = Mock() + explicit_config.config_name = "metadata_config" + explicit_config.applies_to = None # This config doesn't apply to others + + # Mock the metadata relationship + relationship = MetadataRelationship( + data_config="some_data", + metadata_config="metadata_config", + relationship_type="explicit", + ) + + # Mock the config loading and table setup + mock_config_result = {"success": True, "table_name": "test_metadata_table"} + + expected_df = pd.DataFrame( + {"sample_id": ["sample1", "sample2"], "condition": ["ctrl", "treatment"]} + ) + + with ( + patch.object( + mock_api, "get_metadata_relationships", return_value=[relationship] + ), + patch.object(mock_api, "get_config", return_value=explicit_config), + patch.object( + mock_api, "_get_metadata_for_config", return_value=mock_config_result + ), + patch.object(mock_api, "_get_explicit_metadata", return_value=expected_df), + ): + + result = mock_api.get_metadata("metadata_config") + + mock_api._get_explicit_metadata.assert_called_once_with( + explicit_config, "test_metadata_table" + ) + pd.testing.assert_frame_equal(result, expected_df) + + def test_get_metadata_embedded_config(self, mock_api): + """Test get_metadata with embedded metadata config.""" + # Setup mock configurations + embedded_config = Mock() + embedded_config.config_name = "data_config" + embedded_config.metadata_fields = ["time", "mechanism"] + + # Mock the metadata relationship + relationship = MetadataRelationship( + data_config="data_config", + metadata_config="data_config_embedded", + relationship_type="embedded", + ) + mock_config_result = {"success": True, "table_name": "test_data_table"} + + expected_df = pd.DataFrame( + {"time": [15, 30], "mechanism": ["ZEV", "ZREV"], "count": [100, 50]} + ) + + with ( + patch.object( + mock_api, "get_metadata_relationships", return_value=[relationship] + ), + patch.object(mock_api, "get_config", return_value=embedded_config), + patch.object( + mock_api, "_get_metadata_for_config", return_value=mock_config_result + ), + patch.object(mock_api, "_get_embedded_metadata", return_value=expected_df), + ): + + result = mock_api.get_metadata("data_config") + + mock_api._get_embedded_metadata.assert_called_once_with( + embedded_config, "test_data_table" + ) + pd.testing.assert_frame_equal(result, expected_df) + + def test_get_metadata_applied_config(self, mock_api): + """Test get_metadata with config that has metadata applied to it.""" + # Setup a metadata config that applies to another config + metadata_config = Mock() + metadata_config.config_name = "experiment_metadata" + metadata_config.applies_to = ["data_config", "other_data_config"] + + # Mock the metadata relationship + relationship = MetadataRelationship( + data_config="data_config", + metadata_config="experiment_metadata", + relationship_type="explicit", + ) + mock_config_result = {"success": True, "table_name": "test_metadata_table"} + + expected_df = pd.DataFrame( + {"experiment_id": ["exp1", "exp2"], "condition": ["ctrl", "treatment"]} + ) + + with ( + patch.object( + mock_api, "get_metadata_relationships", return_value=[relationship] + ), + patch.object(mock_api, "get_config", return_value=metadata_config), + patch.object( + mock_api, "_get_metadata_for_config", return_value=mock_config_result + ), + patch.object(mock_api, "_get_explicit_metadata", return_value=expected_df), + ): + + # Request metadata for a config that appears in applies_to + result = mock_api.get_metadata("data_config") + + # Should return the metadata from the config that applies to it + mock_api._get_explicit_metadata.assert_called_once_with( + metadata_config, "test_metadata_table" + ) + pd.testing.assert_frame_equal(result, expected_df) + + def test_get_metadata_config_not_found(self, mock_api): + """Test get_metadata with non-existent config when other configs exist.""" + # Setup a relationship for a different config + relationship = MetadataRelationship( + data_config="other_data", + metadata_config="other_config", + relationship_type="explicit", + ) + with patch.object( + mock_api, "get_metadata_relationships", return_value=[relationship] + ): + with pytest.raises(ValueError, match="Config 'nonexistent' not found"): + mock_api.get_metadata("nonexistent") + + def test_get_metadata_no_metadata_sources(self, mock_api): + """Test get_metadata when no metadata sources are available.""" + with patch.object(mock_api, "get_metadata_relationships", return_value=[]): + result = mock_api.get_metadata("any_config") + assert result.empty + + def test_get_metadata_load_failure(self, mock_api): + """Test get_metadata when config loading fails.""" + config = Mock() + config.config_name = "test_config" + config.applies_to = None + + # Mock the metadata relationship + relationship = MetadataRelationship( + data_config="some_data", + metadata_config="test_config", + relationship_type="explicit", + ) + mock_config_result = {"success": False} + + with ( + patch.object( + mock_api, "get_metadata_relationships", return_value=[relationship] + ), + patch.object(mock_api, "get_config", return_value=config), + patch.object( + mock_api, "_get_metadata_for_config", return_value=mock_config_result + ), + ): + with pytest.raises(RuntimeError, match="Failed to load data for config"): + mock_api.get_metadata("test_config") + + def test_set_filter_valid_fields(self, mock_api): + """Test set_filter with valid field names.""" + with patch.object(mock_api, "_validate_metadata_fields") as mock_validate: + mock_api.set_filter("test_config", time=15, mechanism="ZEV") + + mock_validate.assert_called_once_with("test_config", ["time", "mechanism"]) + assert ( + mock_api._table_filters["test_config"] + == "time = 15 AND mechanism = 'ZEV'" + ) + + def test_set_filter_clear_on_empty(self, mock_api): + """Test set_filter clears filter when no kwargs provided.""" + # Set an initial filter + mock_api._table_filters["test_config"] = "existing_filter" + + with patch.object(mock_api, "clear_filter") as mock_clear: + mock_api.set_filter("test_config") + mock_clear.assert_called_once_with("test_config") + + def test_set_filter_various_types(self, mock_api): + """Test set_filter with different value types.""" + with patch.object(mock_api, "_validate_metadata_fields"): + mock_api.set_filter( + "test_config", + string_field="text", + numeric_field=42, + null_field=None, + bool_field=True, + ) + + expected = ( + "string_field = 'text' AND numeric_field = 42 AND " + "null_field IS NULL AND bool_field = True" + ) + assert mock_api._table_filters["test_config"] == expected + + def test_set_sql_filter_with_validation(self, mock_api): + """Test set_sql_filter with field validation enabled.""" + sql_where = "time IN (15, 30) AND mechanism = 'ZEV'" + + with ( + patch.object( + mock_api, "_extract_fields_from_sql", return_value=["time", "mechanism"] + ) as mock_extract, + patch.object(mock_api, "_validate_metadata_fields") as mock_validate, + ): + + mock_api.set_sql_filter("test_config", sql_where) + + mock_extract.assert_called_once_with(sql_where) + mock_validate.assert_called_once_with("test_config", ["time", "mechanism"]) + assert mock_api._table_filters["test_config"] == sql_where + + def test_set_sql_filter_without_validation(self, mock_api): + """Test set_sql_filter with field validation disabled.""" + sql_where = "complex_function(field1, field2) > 0" + + with ( + patch.object(mock_api, "_extract_fields_from_sql") as mock_extract, + patch.object(mock_api, "_validate_metadata_fields") as mock_validate, + ): + + mock_api.set_sql_filter("test_config", sql_where, validate_fields=False) + + mock_extract.assert_not_called() + mock_validate.assert_not_called() + assert mock_api._table_filters["test_config"] == sql_where + + def test_set_sql_filter_clear_on_empty(self, mock_api): + """Test set_sql_filter clears filter when empty SQL provided.""" + mock_api._table_filters["test_config"] = "existing_filter" + + with patch.object(mock_api, "clear_filter") as mock_clear: + mock_api.set_sql_filter("test_config", "") + mock_clear.assert_called_once_with("test_config") + + def test_clear_filter(self, mock_api): + """Test clear_filter removes stored filter.""" + mock_api._table_filters["test_config"] = "some_filter" + + mock_api.clear_filter("test_config") + + assert "test_config" not in mock_api._table_filters + + def test_clear_filter_nonexistent(self, mock_api): + """Test clear_filter with non-existent config.""" + # Should not raise an error + mock_api.clear_filter("nonexistent_config") + + def test_get_current_filter_exists(self, mock_api): + """Test get_current_filter returns existing filter.""" + expected_filter = "time = 15" + mock_api._table_filters["test_config"] = expected_filter + + result = mock_api.get_current_filter("test_config") + assert result == expected_filter + + def test_get_current_filter_not_exists(self, mock_api): + """Test get_current_filter returns None for non-existent filter.""" + result = mock_api.get_current_filter("nonexistent_config") + assert result is None + + +class TestHfQueryAPIErrorHandling: + """Test HfQueryAPI error handling and edge cases.""" + + @pytest.fixture + def mock_api(self): + """Create a mock HfQueryAPI instance.""" + conn = duckdb.connect(":memory:") + with patch("tfbpapi.HfQueryAPI.HfCacheManager.__init__", return_value=None): + api = HfQueryAPI("test/repo", duckdb_conn=conn) + # Set up all necessary attributes + api.repo_id = "test/repo" + api.duckdb_conn = conn + api._duckdb_conn = conn + api.logger = logging.getLogger("test") + api._table_filters = {} + + # Set up the internal dataset card attribute + api._dataset_card = Mock() + return api + + def test_set_filter_validation_error_propagates(self, mock_api): + """Test that InvalidFilterFieldError from validation propagates.""" + error = InvalidFilterFieldError( + config_name="test_config", + invalid_fields=["invalid_field"], + available_fields=["valid_field"], + ) + + with patch.object(mock_api, "_validate_metadata_fields", side_effect=error): + with pytest.raises(InvalidFilterFieldError) as exc_info: + mock_api.set_filter("test_config", invalid_field="value") + + assert exc_info.value.config_name == "test_config" + assert "invalid_field" in exc_info.value.invalid_fields + + def test_set_sql_filter_validation_error_propagates(self, mock_api): + """Test that InvalidFilterFieldError from SQL validation propagates.""" + error = InvalidFilterFieldError( + config_name="test_config", + invalid_fields=["nonexistent"], + available_fields=["time", "mechanism"], + ) + + with ( + patch.object( + mock_api, "_extract_fields_from_sql", return_value=["nonexistent"] + ), + patch.object(mock_api, "_validate_metadata_fields", side_effect=error), + ): + + with pytest.raises(InvalidFilterFieldError) as exc_info: + mock_api.set_sql_filter("test_config", "nonexistent = 1") + + assert exc_info.value.config_name == "test_config" + + def test_get_metadata_query_error_propagates(self, mock_api): + """Test that query errors in get_metadata propagate.""" + config = Mock() + config.config_name = "test_config" + config.applies_to = None + + # Mock the metadata relationship + relationship = MetadataRelationship( + data_config="some_data", + metadata_config="test_config", + relationship_type="explicit", + ) + + mock_config_result = {"success": True, "table_name": "test_table"} + + with ( + patch.object( + mock_api, "get_metadata_relationships", return_value=[relationship] + ), + patch.object(mock_api, "get_config", return_value=config), + patch.object( + mock_api, "_get_metadata_for_config", return_value=mock_config_result + ), + patch.object( + mock_api, + "_get_explicit_metadata", + side_effect=Exception("Query failed"), + ), + ): + + with pytest.raises(Exception, match="Query failed"): + mock_api.get_metadata("test_config") + + def test_validate_metadata_fields_get_metadata_error_logged(self, mock_api): + """Test that non-InvalidFilterFieldError exceptions in validation are logged.""" + with ( + patch.object( + mock_api, "get_metadata", side_effect=Exception("Network error") + ), + patch.object(mock_api.logger, "warning") as mock_warning, + ): + + # Should not raise, but should log warning + mock_api._validate_metadata_fields("test_config", ["field1"]) + + mock_warning.assert_called_once() + assert "Could not validate filter fields" in mock_warning.call_args[0][0] + + def test_extract_fields_edge_cases(self, mock_api): + """Test _extract_fields_from_sql with various edge cases.""" + # Empty string + assert mock_api._extract_fields_from_sql("") == [] + + # Only SQL keywords + fields = mock_api._extract_fields_from_sql("AND OR NOT NULL") + assert len(fields) == 0 + + # Mixed quotes and operators + fields = mock_api._extract_fields_from_sql( + '"field1" >= "field2" AND field3 <= field4' + ) + assert "field1" in fields + assert "field2" in fields + assert "field3" in fields + assert "field4" in fields + + # Function calls should be excluded + fields = mock_api._extract_fields_from_sql( + "UPPER(field1) = 'VALUE' AND field2 > MAX(field3)" + ) + assert "field1" in fields + assert "field2" in fields + assert "field3" in fields + assert "UPPER" not in fields + assert "MAX" not in fields + + # Numeric literals should be excluded + fields = mock_api._extract_fields_from_sql("field1 = 123.45 AND field2 = -67") + assert "field1" in fields + assert "field2" in fields + assert "123" not in fields + assert "45" not in fields + assert "67" not in fields + + def test_extract_fields_complex_sql(self, mock_api): + """Test _extract_fields_from_sql with complex SQL patterns.""" + complex_sql = """ + (field1 IN (1, 2, 3) OR field2 IS NOT NULL) + AND field3 LIKE '%pattern%' + AND "quoted field" BETWEEN 'start' AND 'end' + AND field4 > COALESCE(field5, 0) + """ + + fields = mock_api._extract_fields_from_sql(complex_sql) + + expected_fields = [ + "field1", + "field2", + "field3", + "quoted field", + "field4", + "field5", + ] + for field in expected_fields: + assert field in fields, f"Field '{field}' should be extracted from SQL" + + # These should not be extracted (SQL keywords and function names) + unwanted = ["COALESCE", "LIKE", "BETWEEN", "NULL"] + for unwanted_item in unwanted: + assert ( + unwanted_item not in fields + ), f"'{unwanted_item}' should not be extracted" + + # String literals should not be extracted + string_literals = ["start", "end", "pattern"] + for literal in string_literals: + assert ( + literal not in fields + ), f"String literal '{literal}' should not be extracted" + + def test_extract_fields_in_clause_with_quoted_values(self, mock_api): + """Test _extract_fields_from_sql with IN clause containing quoted values.""" + # This is the exact pattern from the user's error case + gene_ids = ["YNL199C", "YDL106C", "YLR098C", "YNR009W", "YLR176C"] + regulator_clause = "(" + ", ".join(f"'{gene_id}'" for gene_id in gene_ids) + ")" + + sql = f""" + time = 15 + AND mechanism = 'ZEV' + AND restriction = 'P' + AND regulator_locus_tag IN {regulator_clause} + """ + + fields = mock_api._extract_fields_from_sql(sql) + + # Should extract field names + expected_fields = ["time", "mechanism", "restriction", "regulator_locus_tag"] + for field in expected_fields: + assert field in fields, f"Field '{field}' should be extracted from SQL" + + # Should NOT extract string literals or gene IDs + unwanted_values = ["ZEV", "P"] + gene_ids + for value in unwanted_values: + assert ( + value not in fields + ), f"String literal '{value}' should not be extracted as field" + + # Should NOT extract numeric literals + assert "15" not in fields + + def test_extract_fields_various_comparison_operators(self, mock_api): + """Test _extract_fields_from_sql with various comparison operators and string + values.""" + sql = """ + field1 = 'value1' AND field2 != 'value2' + AND field3 <> 'value3' AND field4 > 'value4' + AND field5 <= 'value5' AND field6 >= 'value6' + AND field7 LIKE 'pattern%' AND field8 NOT LIKE '%other%' + """ + + fields = mock_api._extract_fields_from_sql(sql) + + # Should extract field names + expected_fields = [ + "field1", + "field2", + "field3", + "field4", + "field5", + "field6", + "field7", + "field8", + ] + for field in expected_fields: + assert field in fields, f"Field '{field}' should be extracted from SQL" + + # Should NOT extract string values + unwanted_values = [ + "value1", + "value2", + "value3", + "value4", + "value5", + "value6", + "pattern", + "other", + ] + for value in unwanted_values: + assert ( + value not in fields + ), f"String literal '{value}' should not be extracted as field" + + def test_extract_fields_between_clause(self, mock_api): + """Test _extract_fields_from_sql with BETWEEN clause containing string + values.""" + sql = ( + "field1 BETWEEN 'start_value' AND 'end_value' AND field2 BETWEEN 10 AND 20" + ) + + fields = mock_api._extract_fields_from_sql(sql) + + # Should extract field names + assert "field1" in fields + assert "field2" in fields + + # Should NOT extract BETWEEN values + assert "start_value" not in fields + assert "end_value" not in fields + assert "10" not in fields + assert "20" not in fields + + def test_get_metadata_table_name_missing(self, mock_api): + """Test get_metadata when table_name is missing from config result.""" + config = Mock() + config.config_name = "test_config" + config.applies_to = None + + # Mock the metadata relationship + relationship = MetadataRelationship( + data_config="some_data", + metadata_config="test_config", + relationship_type="explicit", + ) + # Success but no table name + mock_config_result = {"success": True, "table_name": None} + + with ( + patch.object( + mock_api, "get_metadata_relationships", return_value=[relationship] + ), + patch.object(mock_api, "get_config", return_value=config), + patch.object( + mock_api, "_get_metadata_for_config", return_value=mock_config_result + ), + ): + with pytest.raises(RuntimeError, match="No table name for config"): + mock_api.get_metadata("test_config") + + def test_filter_methods_whitespace_handling(self, mock_api): + """Test that filter methods handle whitespace correctly.""" + # set_sql_filter should strip whitespace + with ( + patch.object(mock_api, "_extract_fields_from_sql", return_value=[]), + patch.object(mock_api, "_validate_metadata_fields"), + ): + + mock_api.set_sql_filter("test_config", " field = 1 ") + assert mock_api._table_filters["test_config"] == "field = 1" + + # Empty/whitespace-only SQL should clear filter + with patch.object(mock_api, "clear_filter") as mock_clear: + mock_api.set_sql_filter("test_config", " ") + mock_clear.assert_called_once_with("test_config") + + +class TestInvalidFilterFieldError: + """Test the InvalidFilterFieldError exception.""" + + def test_error_message_formatting(self): + """Test that error message is formatted correctly.""" + error = InvalidFilterFieldError( + config_name="test_config", + invalid_fields=["field1", "field2"], + available_fields=["valid1", "valid2", "valid3"], + ) + + message = str(error) + assert "test_config" in message + assert "'field1'" in message + assert "'field2'" in message + assert "Available fields:" in message + assert "valid1" in message + + def test_error_with_no_available_fields(self): + """Test error message when no fields are available.""" + error = InvalidFilterFieldError( + config_name="empty_config", + invalid_fields=["any_field"], + available_fields=[], + ) + + message = str(error) + assert "No fields available" in message + + def test_error_attributes(self): + """Test that error attributes are set correctly.""" + invalid_fields = ["bad1", "bad2"] + available_fields = ["good1", "good2"] + + error = InvalidFilterFieldError( + config_name="test_config", + invalid_fields=invalid_fields, + available_fields=available_fields, + ) + + assert error.config_name == "test_config" + assert error.invalid_fields == invalid_fields + assert error.available_fields == sorted(available_fields) + assert error.details["config_name"] == "test_config" + assert error.details["invalid_fields"] == invalid_fields + assert error.details["available_fields"] == sorted(available_fields) From db7dd014faffafb4b3dd30cbec371e2093472d6d Mon Sep 17 00:00:00 2001 From: fei1cell Date: Fri, 10 Oct 2025 11:56:12 -0500 Subject: [PATCH 09/49] correlation analysis tutorial draft --- docs/tutorials/correlation_tutorial.ipynb | 1607 +++++++++++++++++++++ 1 file changed, 1607 insertions(+) create mode 100644 docs/tutorials/correlation_tutorial.ipynb diff --git a/docs/tutorials/correlation_tutorial.ipynb b/docs/tutorials/correlation_tutorial.ipynb new file mode 100644 index 0000000..8ca7fe5 --- /dev/null +++ b/docs/tutorials/correlation_tutorial.ipynb @@ -0,0 +1,1607 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "fcdcb13a", + "metadata": {}, + "source": [ + "# Hackett 2020 vs Kemmeren Correlation Analysis Tutorial\n", + "\n", + "This tutorial demonstrates how to conduct comprehensive correlation analysis between Hackett 2020 (mcisaac) and Kemmeren datasets using tfbpapi, focusing on regulator-target relationships and temporal dynamics.\n", + "\n", + "## Overview\n", + "\n", + "Correlation analysis evalute how well the effects of the same regulators correlate between these two experimental approaches, and whether timing affects the correlation strength.\n", + "\n", + "### Datasets Used\n", + "- **Perturbation data**: McIsaac (ZEV system), Kemmeren (overexpression)\n", + "\n", + "### Analysis Strategy\n", + "1. Load and filter Hackett 2020 data (time=15, mechanism=\"zev\", restriction=\"p\") and Kemmeren 2014 data\n", + "2. Identify shared regulators between Hackett and Kemmeren\n", + "3. For each shared regulator, calculate Spearman correlations:\n", + " - Hackett effect vs Kemmeren effect\n", + " - Hackett effect vs Kemmeren p-value\n", + "4. Generate scatter plots for specific individual regulator\n", + "5. Create distribution plots (boxplots) across all shared regulators\n", + "6. Analyze temporal dynamics by comparing different timepoints in Hackett data" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "30abaca0", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "from tfbpapi.HfQueryAPI import HfQueryAPI\n", + "from tfbpapi.datainfo.datacard import DataCard\n", + "from tfbpapi.errors import DataCardError, HfDataFetchError\n", + "from scipy.stats import spearmanr\n", + "import matplotlib.pyplot as plt\n", + "import seaborn as sns\n", + "\n", + "# Configure plotting\n", + "plt.style.use('default')\n", + "sns.set_palette(\"husl\")" + ] + }, + { + "cell_type": "markdown", + "id": "12941f4d", + "metadata": {}, + "source": [ + "## Dataset Exploration with DataCard\n", + "\n", + "Before loading data, let's explore dataset structure and metadata using the DataCard interface." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "c9b9afdf", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Exploring dataset structures...\n", + "\n", + "==================================================\n", + "Exploring BrentLab/hackett_2020\n", + "Dataset types in configs: {'annotated_features'}\n", + "Configurations: 1\n", + "\n", + "Config: hackett_2020\n", + " Type: annotated_features\n", + " Features: 17\n", + " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", + " Target fields: ['target_locus_tag', 'target_symbol']\n", + " Metadata fields: ['regulator_locus_tag', 'regulator_symbol', 'time', 'mechanism', 'restriction', 'date', 'strain']\n", + "\n", + "==================================================\n", + "Exploring BrentLab/kemmeren_2014\n", + "Dataset types in configs: {'annotated_features'}\n", + "Configurations: 1\n", + "\n", + "Config: kemmeren_2014\n", + " Type: annotated_features\n", + " Features: 11\n", + " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", + " Target fields: ['target_locus_tag', 'target_symbol']\n", + " Metadata fields: ['regulator_locus_tag', 'regulator_symbol']\n", + "\n", + "==================================================\n", + "Successfully explored 2 datasets\n" + ] + } + ], + "source": [ + "# Explore dataset structure using DataCard\n", + "print(\"Exploring dataset structures...\")\n", + "\n", + "datasets_to_explore = [\n", + " \"BrentLab/hackett_2020\",\n", + " \"BrentLab/kemmeren_2014\"\n", + "]\n", + "\n", + "dataset_info = {}\n", + "\n", + "for repo_id in datasets_to_explore:\n", + " try:\n", + " print(f\"\\n{'='*50}\")\n", + " print(f\"Exploring {repo_id}\")\n", + "\n", + " # Create DataCard instance\n", + " datacard = DataCard(repo_id)\n", + " card = datacard.dataset_card\n", + "\n", + " # DatasetCard doesn't have dataset_type directly - it's on individual configs\n", + " dataset_types = [config.dataset_type.value for config in card.configs]\n", + " print(f\"Dataset types in configs: {set(dataset_types)}\")\n", + " print(f\"Configurations: {len(card.configs)}\")\n", + "\n", + " # Store dataset info for later use\n", + " dataset_info[repo_id] = {\n", + " 'datacard': datacard,\n", + " 'card': card,\n", + " 'configs': {config.config_name: config for config in card.configs}\n", + " }\n", + "\n", + " # Display configuration details\n", + " for config in card.configs:\n", + " print(f\"\\nConfig: {config.config_name}\")\n", + " print(f\" Type: {config.dataset_type.value}\")\n", + " print(f\" Features: {len(config.dataset_info.features) if config.dataset_info.features else 0}\")\n", + "\n", + " if config.dataset_info.features:\n", + " # Show regulator and target fields if available\n", + " feature_names = [f.name for f in config.dataset_info.features]\n", + " regulator_fields = [f for f in feature_names if 'regulator' in f.lower()]\n", + " target_fields = [f for f in feature_names if 'target' in f.lower()]\n", + "\n", + " if regulator_fields:\n", + " print(f\" Regulator fields: {regulator_fields}\")\n", + " if target_fields:\n", + " print(f\" Target fields: {target_fields}\")\n", + "\n", + " if config.metadata_fields:\n", + " print(f\" Metadata fields: {config.metadata_fields}\")\n", + "\n", + " except (DataCardError, HfDataFetchError) as e:\n", + " print(f\"Error exploring {repo_id}: {e}\")\n", + " continue\n", + " except Exception as e:\n", + " print(f\"Unexpected error exploring {repo_id}: {e}\")\n", + " continue\n", + "\n", + "print(f\"\\n{'='*50}\")\n", + "print(f\"Successfully explored {len(dataset_info)} datasets\")" + ] + }, + { + "cell_type": "markdown", + "id": "cf0265f0", + "metadata": {}, + "source": [ + "## Initialize dataset connections and load data" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "a6b0424f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Initializing HfQueryAPI connections...\n", + "All API connections initialized\n" + ] + } + ], + "source": [ + "# Initialize dataset connections and load data\n", + "print(\"Initializing HfQueryAPI connections...\")\n", + "\n", + "hackett_2020 = HfQueryAPI(repo_id=\"BrentLab/hackett_2020\")\n", + "kemmeren_2014 = HfQueryAPI(repo_id=\"BrentLab/kemmeren_2014\")\n", + "\n", + "print(\"All API connections initialized\")" + ] + }, + { + "cell_type": "markdown", + "id": "6e1b7269", + "metadata": {}, + "source": [ + "## Get metadata from each dataset to find common regulators" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "baafb468", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Getting metadata from datasets...\n", + "All metadata loaded\n", + "Found 185 common regulators across all datasets\n" + ] + } + ], + "source": [ + "# Get metadata from each dataset to find common regulators\n", + "print(\"Getting metadata from datasets...\")\n", + "\n", + "kemmeren_meta = kemmeren_2014.get_metadata(\"kemmeren_2014\") \n", + "hackett_2020_meta = hackett_2020.get_metadata(\"hackett_2020\")\n", + "\n", + "print(\"All metadata loaded\")\n", + "\n", + "# Get the intersection of common regulators\n", + "common_regulators = (set(hackett_2020_meta.regulator_symbol.unique())\n", + " & set(kemmeren_meta.regulator_symbol.unique()))\n", + "\n", + "print(f\"Found {len(common_regulators)} common regulators across all datasets\")\n", + "\n", + "# Create proper SQL IN clause\n", + "if common_regulators:\n", + " regulator_clause = \"(\" + \", \".join(f\"'{reg}'\" for reg in common_regulators) + \")\"" + ] + }, + { + "cell_type": "markdown", + "id": "2d89d745", + "metadata": {}, + "source": [ + "## Filter the data" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "c888c477", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Applying dataset-specific filters...\n", + "All filters applied\n" + ] + } + ], + "source": [ + "# Apply dataset-specific filters\n", + "print(\"Applying dataset-specific filters...\")\n", + "\n", + "hackett_2020.set_sql_filter(\n", + " \"hackett_2020\",\n", + " f\"\"\"\n", + " time = 15 \n", + " AND mechanism = 'ZEV' \n", + " AND restriction = 'P' \n", + " AND regulator_symbol IN {regulator_clause}\n", + " \"\"\")\n", + "\n", + "print(\"All filters applied\")" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "050e3f4c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Loading both perturbation datasets...\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "e7178fddad6c4f6780c08e8f7f6e8ddc", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, layout=Layout(width='auto'), style=ProgressStyle(bar_color='black'))" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "2b31f3e685a449828ae056a9796a9244", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, layout=Layout(width='auto'), style=ProgressStyle(bar_color='black'))" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Both datasets loaded\n", + "hackett: 145 regulators\n", + "kemmeren: 1487 regulators\n", + "Final intersection: 145 regulators\n", + "Common regulators: ['ACA1', 'ADA2', 'AFT2', 'ARO80', 'ARR1', 'ASH1', 'AZF1', 'BDF2', 'BMH1', 'CAD1']...\n" + ] + } + ], + "source": [ + "# Load both perturbation datasets to find common regulators\n", + "print(\"Loading both perturbation datasets...\")\n", + "\n", + "# Load both datasets\n", + "hackett_data = hackett_2020.query(\"SELECT * FROM hackett_2020\", \"hackett_2020\")\n", + "kemmeren_data = kemmeren_2014.query(\"SELECT * FROM kemmeren_2014\", \"kemmeren_2014\")\n", + "\n", + "print(\"Both datasets loaded\")\n", + "\n", + "# Check what regulators we actually got from each dataset\n", + "actual_regulators = {\n", + " 'hackett': set(hackett_data.regulator_symbol.unique()),\n", + " 'kemmeren': set(kemmeren_data.regulator_symbol.unique()),\n", + "}\n", + "\n", + "for name, regulators in actual_regulators.items():\n", + " print(f\"{name}: {len(regulators)} regulators\")\n", + "\n", + "# Find the intersection - regulators present in BOTH datasets\n", + "final_common = set.intersection(*actual_regulators.values())\n", + "print(f\"Final intersection: {len(final_common)} regulators\")\n", + "\n", + "if final_common:\n", + " final_common_clause = \"(\" + \", \".join(f\"'{reg}'\" for reg in final_common) + \")\"\n", + " print(f\"Common regulators: {sorted(list(final_common))[:10]}...\") # Show first 10\n", + "else:\n", + " print(\"WARNING: No common regulators found!\")\n", + " final_common_clause = \"()\"\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "06293141", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Finding common targets...\n", + "Found 6063 common targets\n", + "Loading final datasets with complete filtering...\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "b7276d52902547829f90a98ba85c6a5c", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, layout=Layout(width='auto'), style=ProgressStyle(bar_color='black'))" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Final datasets loaded with complete filtering\n", + "McIsaac: 891,408 rows, 145 regulators\n", + "Kemmeren: 880,730 rows, 145 regulators\n" + ] + } + ], + "source": [ + "# Find common targets across all datasets\n", + "print(\"Finding common targets...\")\n", + "\n", + "common_targets = (set(hackett_data.target_symbol.unique())\n", + " & set(kemmeren_data.target_symbol.unique()))\n", + "\n", + "print(f\"Found {len(common_targets)} common targets\")\n", + "\n", + "if common_targets:\n", + " target_clause = \"(\" + \", \".join(f\"'{tgt}'\" for tgt in common_targets) + \")\"\n", + "\n", + "# Load final datasets with both regulator and target filtering \n", + "print(\"Loading final datasets with complete filtering...\")\n", + "\n", + "\n", + "# Load final datasets with both regulator and target filtering \n", + "hackett_final = hackett_2020.query(f\"\"\"\n", + " SELECT * FROM hackett_2020\n", + " WHERE regulator_symbol IN {final_common_clause} \n", + " AND target_symbol IN {target_clause}\n", + "\"\"\", \"hackett_2020\")\n", + "\n", + "kemmeren_final = kemmeren_2014.query(f\"\"\"\n", + " SELECT * FROM kemmeren_2014\n", + " WHERE regulator_symbol IN {final_common_clause} \n", + " AND target_symbol IN {target_clause}\n", + "\"\"\", \"kemmeren_2014\")\n", + "\n", + "print(\"Final datasets loaded with complete filtering\")\n", + "\n", + "# Print final dataset sizes\n", + "datasets_info = [\n", + " ('McIsaac', hackett_final), ('Kemmeren', kemmeren_final)\n", + "]\n", + "\n", + "for name, data in datasets_info:\n", + " if len(data) > 0:\n", + " regulators = data['regulator_symbol'].nunique() if 'regulator_symbol' in data.columns else 0\n", + " print(f\"{name}: {len(data):,} rows, {regulators} regulators\")\n", + " else:\n", + " print(f\"{name}: No data loaded\")" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "549e73ae", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
regulator_locus_tagregulator_symbolreporterIdtarget_locus_tagtarget_symbolMMadjApvalvariable_in_wtmultiple_probes
0YBL008WHIR1SCAB000002Q0017Q0017-0.039026-0.0397215.6666425.913350e-01FalseFalse
1YBL008WHIR1SCAB000004Q0045COX1-0.102708-0.1044047.1872182.787440e-01FalseFalse
2YBL008WHIR1SCAB000005Q0050AI1-0.092154-0.1013127.9818137.899890e-01TrueFalse
3YBL008WHIR1SCAB000006Q0055AI2-0.113634-0.1232089.3025736.952350e-01TrueFalse
4YBL008WHIR1SCAB000007Q0060AI3-0.009983-0.0186768.3151419.751770e-01FalseFalse
....................................
880725YOR290CSNF2SCAB007301YML099W-AYML099W-A-0.484219-0.4990607.2140078.946420e-20FalseFalse
880726YOR290CSNF2SCAB007304YNL067W-AYNL067W-A-0.081846-0.0721316.3394411.827010e-01FalseFalse
880727YOR290CSNF2SCAB007305YNL162W-AYNL162W-A-0.356642-0.3227098.6822382.132620e-07FalseFalse
880728YOR290CSNF2SCAB007306YNR001W-AYNR001W-A-0.049399-0.0449076.3529374.300570e-01FalseFalse
880729YOR290CSNF2SCAB007307YNR034W-AEGO4-0.279391-0.4239507.3049851.691570e-02FalseFalse
\n", + "

880730 rows × 11 columns

\n", + "
" + ], + "text/plain": [ + " regulator_locus_tag regulator_symbol reporterId target_locus_tag \\\n", + "0 YBL008W HIR1 SCAB000002 Q0017 \n", + "1 YBL008W HIR1 SCAB000004 Q0045 \n", + "2 YBL008W HIR1 SCAB000005 Q0050 \n", + "3 YBL008W HIR1 SCAB000006 Q0055 \n", + "4 YBL008W HIR1 SCAB000007 Q0060 \n", + "... ... ... ... ... \n", + "880725 YOR290C SNF2 SCAB007301 YML099W-A \n", + "880726 YOR290C SNF2 SCAB007304 YNL067W-A \n", + "880727 YOR290C SNF2 SCAB007305 YNL162W-A \n", + "880728 YOR290C SNF2 SCAB007306 YNR001W-A \n", + "880729 YOR290C SNF2 SCAB007307 YNR034W-A \n", + "\n", + " target_symbol M Madj A pval \\\n", + "0 Q0017 -0.039026 -0.039721 5.666642 5.913350e-01 \n", + "1 COX1 -0.102708 -0.104404 7.187218 2.787440e-01 \n", + "2 AI1 -0.092154 -0.101312 7.981813 7.899890e-01 \n", + "3 AI2 -0.113634 -0.123208 9.302573 6.952350e-01 \n", + "4 AI3 -0.009983 -0.018676 8.315141 9.751770e-01 \n", + "... ... ... ... ... ... \n", + "880725 YML099W-A -0.484219 -0.499060 7.214007 8.946420e-20 \n", + "880726 YNL067W-A -0.081846 -0.072131 6.339441 1.827010e-01 \n", + "880727 YNL162W-A -0.356642 -0.322709 8.682238 2.132620e-07 \n", + "880728 YNR001W-A -0.049399 -0.044907 6.352937 4.300570e-01 \n", + "880729 EGO4 -0.279391 -0.423950 7.304985 1.691570e-02 \n", + "\n", + " variable_in_wt multiple_probes \n", + "0 False False \n", + "1 False False \n", + "2 True False \n", + "3 True False \n", + "4 False False \n", + "... ... ... \n", + "880725 False False \n", + "880726 False False \n", + "880727 False False \n", + "880728 False False \n", + "880729 False False \n", + "\n", + "[880730 rows x 11 columns]" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "kemmeren_final" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "d48a21fc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
regulator_locus_tagregulator_symboltarget_locus_tagtarget_symboltimemechanismrestrictiondatestraingreen_medianred_medianlog2_ratiolog2_cleaned_ratiolog2_noise_modellog2_cleaned_ratio_zth2dlog2_selected_timecourseslog2_shrunken_timecourses
0YER045CACA1YMR056CAAC115.0ZEVP20160229SMY20494775.527504853.350500.1798960.1381880.1286930.0000000.0000000.0
1YER045CACA1YBR085WAAC315.0ZEVP20160229SMY2049173.43045188.95840-0.101896-0.0698970.145825-0.0000000.0000000.0
2YER045CACA1YJR155WAAD1015.0ZEVP20160229SMY20495211.055003284.87500-0.062144-0.0687190.157750-0.068719-0.068719-0.0
3YER045CACA1YNL331CAAD1415.0ZEVP20160229SMY20491139.303001264.24750-0.070802-0.0846150.102746-0.0000000.0000000.0
4YER045CACA1YOL165CAAD1515.0ZEVP20160229SMY204983.9543197.65045-0.149533-0.0985050.157490-0.0000000.0000000.0
......................................................
891403YOR172WYRM1YLR197WNOP5615.0ZEVP20161117SMY22446635.243506217.72150-0.3954030.0169510.1434490.0000000.0000000.0
891404YFL052WZNF1YDL021WGPM215.0ZEVP20161117SMY22453167.437503109.543500.3480970.0027250.1760620.0000000.0000000.0
891405YFL052WZNF1YOL056WGPM315.0ZEVP20161117SMY2245381.35860514.43620-0.112726-0.1099240.129729-0.0000000.0000000.0
891406YFL052WZNF1YOR262WGPN215.0ZEVP20161117SMY2245756.13315898.64070-0.167127-0.0297530.105260-0.0000000.0000000.0
891407YFL052WZNF1YLR243WGPN315.0ZEVP20161117SMY2245914.41855743.23300-0.277554-0.1070390.134676-0.0000000.0000000.0
\n", + "

891408 rows × 17 columns

\n", + "
" + ], + "text/plain": [ + " regulator_locus_tag regulator_symbol target_locus_tag target_symbol \\\n", + "0 YER045C ACA1 YMR056C AAC1 \n", + "1 YER045C ACA1 YBR085W AAC3 \n", + "2 YER045C ACA1 YJR155W AAD10 \n", + "3 YER045C ACA1 YNL331C AAD14 \n", + "4 YER045C ACA1 YOL165C AAD15 \n", + "... ... ... ... ... \n", + "891403 YOR172W YRM1 YLR197W NOP56 \n", + "891404 YFL052W ZNF1 YDL021W GPM2 \n", + "891405 YFL052W ZNF1 YOL056W GPM3 \n", + "891406 YFL052W ZNF1 YOR262W GPN2 \n", + "891407 YFL052W ZNF1 YLR243W GPN3 \n", + "\n", + " time mechanism restriction date strain green_median \\\n", + "0 15.0 ZEV P 20160229 SMY2049 4775.52750 \n", + "1 15.0 ZEV P 20160229 SMY2049 173.43045 \n", + "2 15.0 ZEV P 20160229 SMY2049 5211.05500 \n", + "3 15.0 ZEV P 20160229 SMY2049 1139.30300 \n", + "4 15.0 ZEV P 20160229 SMY2049 83.95431 \n", + "... ... ... ... ... ... ... \n", + "891403 15.0 ZEV P 20161117 SMY2244 6635.24350 \n", + "891404 15.0 ZEV P 20161117 SMY2245 3167.43750 \n", + "891405 15.0 ZEV P 20161117 SMY2245 381.35860 \n", + "891406 15.0 ZEV P 20161117 SMY2245 756.13315 \n", + "891407 15.0 ZEV P 20161117 SMY2245 914.41855 \n", + "\n", + " red_median log2_ratio log2_cleaned_ratio log2_noise_model \\\n", + "0 4853.35050 0.179896 0.138188 0.128693 \n", + "1 188.95840 -0.101896 -0.069897 0.145825 \n", + "2 3284.87500 -0.062144 -0.068719 0.157750 \n", + "3 1264.24750 -0.070802 -0.084615 0.102746 \n", + "4 97.65045 -0.149533 -0.098505 0.157490 \n", + "... ... ... ... ... \n", + "891403 6217.72150 -0.395403 0.016951 0.143449 \n", + "891404 3109.54350 0.348097 0.002725 0.176062 \n", + "891405 514.43620 -0.112726 -0.109924 0.129729 \n", + "891406 898.64070 -0.167127 -0.029753 0.105260 \n", + "891407 743.23300 -0.277554 -0.107039 0.134676 \n", + "\n", + " log2_cleaned_ratio_zth2d log2_selected_timecourses \\\n", + "0 0.000000 0.000000 \n", + "1 -0.000000 0.000000 \n", + "2 -0.068719 -0.068719 \n", + "3 -0.000000 0.000000 \n", + "4 -0.000000 0.000000 \n", + "... ... ... \n", + "891403 0.000000 0.000000 \n", + "891404 0.000000 0.000000 \n", + "891405 -0.000000 0.000000 \n", + "891406 -0.000000 0.000000 \n", + "891407 -0.000000 0.000000 \n", + "\n", + " log2_shrunken_timecourses \n", + "0 0.0 \n", + "1 0.0 \n", + "2 -0.0 \n", + "3 0.0 \n", + "4 0.0 \n", + "... ... \n", + "891403 0.0 \n", + "891404 0.0 \n", + "891405 0.0 \n", + "891406 0.0 \n", + "891407 0.0 \n", + "\n", + "[891408 rows x 17 columns]" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hackett_final" + ] + }, + { + "cell_type": "markdown", + "id": "81a946c6", + "metadata": {}, + "source": [ + "## Individual regulator correlation analysis and scatter plots\n", + "\n", + "Now we'll analyze correlations for each regulator individually, focusing on the relationship between Hackett effects and Kemmeren effects/p-values.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "75cc1bb9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'regulator_symbol': 'ACA1', 'n_targets': 6075, 'eff_eff_corr': np.float64(0.01608402414610723), 'eff_eff_pval': np.float64(0.21004245167836072), 'eff_pval_corr': np.float64(-0.017860635301765383), 'eff_pval_pval': np.float64(0.1639456246498038)}\n", + "Correlation analysis function finished!\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "from scipy.stats import spearmanr\n", + "\n", + "# Set up plotting parameters\n", + "plt.rcParams['figure.figsize'] = (12, 5)\n", + "plt.rcParams['font.size'] = 10\n", + "\n", + "# Example regulator symbol\n", + "chosen_regulator_symbol = 'ACA1'\n", + " \n", + "# Filter data for the specific regulator\n", + "hackett_reg = hackett_final[hackett_final['regulator_symbol'] == chosen_regulator_symbol].copy()\n", + "kemmeren_reg = kemmeren_final[kemmeren_final['regulator_symbol'] == chosen_regulator_symbol].copy()\n", + " \n", + "# Check if data was found\n", + "if len(hackett_reg) == 0 or len(kemmeren_reg) == 0:\n", + " print(f\"No data found for regulator {chosen_regulator_symbol}\")\n", + " \n", + "# Merge datasets on target_symbol\n", + "merged = pd.merge(\n", + " hackett_reg[['target_symbol', 'log2_shrunken_timecourses']].rename(columns={'log2_shrunken_timecourses': 'effect_hackett'}),\n", + " kemmeren_reg[['target_symbol', 'Madj', 'pval']].rename(columns={'Madj': 'effect_kemmeren', 'pval': 'pvalue_kemmeren'}),\n", + " on='target_symbol',\n", + " how='inner'\n", + ")\n", + " \n", + "if len(merged) < 3: # Need at least 3 points for correlation\n", + " print(f\"Insufficient data points ({len(merged)}) for regulator {chosen_regulator_symbol}\")\n", + "\n", + " \n", + "# Calculate correlations\n", + "# Remove any NaN values\n", + "clean_data = merged.dropna(subset=['effect_hackett', 'effect_kemmeren', 'pvalue_kemmeren'])\n", + " \n", + "if len(clean_data) < 3:\n", + " print(f\"Insufficient clean data points ({len(clean_data)}) for regulator {chosen_regulator_symbol}\")\n", + " \n", + "# Spearman correlations\n", + "eff_eff_corr, eff_eff_pval = spearmanr(clean_data['effect_hackett'], clean_data['effect_kemmeren'])\n", + "eff_pval_corr, eff_pval_pval = spearmanr(clean_data['effect_hackett'], clean_data['pvalue_kemmeren'])\n", + " \n", + "correlation_results = {\n", + " 'regulator_symbol': chosen_regulator_symbol,\n", + " 'n_targets': len(clean_data),\n", + " 'eff_eff_corr': eff_eff_corr,\n", + " 'eff_eff_pval': eff_eff_pval,\n", + " 'eff_pval_corr': eff_pval_corr,\n", + " 'eff_pval_pval': eff_pval_pval\n", + "}\n", + " \n", + "print(correlation_results)\n", + "print(\"Correlation analysis function finished!\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "b9324fd2", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABW0AAAJOCAYAAADMCCWlAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3Xl4E9X+BvB3kq50L5TSUigtAkX2HVzYV1GRXQVZFVRE9ILrFVlU4KJwQRDBqxQFvCiLcBUUAYEigoJsIjstW4FCgbbQPcn5/TG/pE2TtsnpkqR9P8/TBzozmZzzZlIO356cUYQQAkRERERERERERETkFDSObgARERERERERERER5WHRloiIiIiIiIiIiMiJsGhLRERERERERERE5ERYtCUiIiIiIiIiIiJyIizaEhERERERERERETkRFm2JiIiIiIiIiIiInAiLtkREREREREREREROhEVbIiIiIiIiIiIiIifCoi0RERERERERERGRE2HRloionNWpUwd16tRxdDOIiIiIiIrFsSsRkWOwaEtELuPChQtQFMXsy93dHTVr1sSQIUNw8OBBRzex3IwaNQqKouDChQuObkqxhBC47777oCgK+vbta9PxGzZswIABAxAREQFPT0/4+fmhWbNmePXVV3HixIlCHxsXF2e6NtauXVvocRkZGZg3bx6efvppxMTEQKPRuEyeRERE5Bo4ds3Dsat1HLsSUVHcHN0AIiJ71a1bF8OHDwcApKen488//8TatWuxceNGbN++HR07dnRwCym/Xbt24fz581AUBVu3bsXVq1cRHh5u9djbt29j8ODB+OWXXxAYGIgePXogOjoaOTk5+Pvvv7FkyRJ8/PHH2LFjBzp37mzx+C+++AIAoCgKli9fjsGDB1t9nhs3bmDKlCkAgMjISAQFBeH27dul02EiIiKifDh2dS0cuxKRs2DRlohczn333Yfp06ebbZszZw7eeustTJ06Fbt373ZMw8gq42B08uTJ+Oijj7BixQq8/fbbFsfpdDr0798fcXFxGD58OD755BP4+/ubHXPt2jX885//RGpqqsXj09LSsG7dOjRt2hShoaH4+eefcfnyZdSqVcvi2GrVquHnn39Gq1atEBwcjN69e2Pr1q2l1GMiIiKiPBy7uhaOXYnIWXB5BCKqEMaOHQsA+PPPPy325eTkYP78+WjZsiV8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qPD09BRNmzYVq1evFjt37rR54JuTkyM++OADUa9ePVP7Jk+eLO7evWv1eCGEmDt3rqhXr55wd3e3OnCNi4sTffr0EYGBgcLDw0PUr19fTJ061aK/QpTewFev14uIiIhiB6NCCLFt2zYBQPTo0cNsu8FgEOvWrRNPPPGECA8PFx4eHqJKlSqicePG4uWXXxYnTpwQQgixdevWYtttZJyFsnr1atO2yMhI0wwUa1+xsbHFnpeIiIioMBy7muPYlWNXIrKPIoQQxS+iQEREDz30EPbt24fU1FTTXX6JiIiIiJwRx65ERK5N4+gGEBE5m2vXrllsW7VqFfbu3Yvu3btz0EtEREREToNjVyKiiokzbYmICqhatSpatGiB+++/H1qtFkeOHMGuXbvg5+eHvXv3okmTJo5uIhERERERAI5diYgqKhZtiYgK+Oc//4nvv/8ely5dQnp6OkJCQtClSxdMnToVMTExjm4eEREREZEJx65ERBUTi7ZEREREREREREREToRr2hIRERERERERERE5ERZtiYiIiIiIiIiIiJwIi7ZEVGn997//RcuWLeHn5wdFUfDKK6/YtI/sk5ubi+nTp6NevXrw9PSEoijYuHFjsfuIiIiIKhOOTSumCxcuQFEUjBo1ytFNISIXw6ItEbk840CoqK86deqYPWbfvn0YNmwY0tLS8MILL2DatGno3bt3sfvKyq5du6AoCqZPn16mz1NSK1asKDbrggPSefPmYcaMGQgPD8eUKVMwbdo0000xitpXVqZPnw5FUbBr164yfR4iIiKqnDg2LT/Wxqbe3t6IiYnBP/7xDyQnJzu6iURE0twc3QAiotJSt25dDB8+3Oq+wMBAs+83b94MIQS++uorPPDAAzbvI1W3bt3w0EMPWd3XvHlzs+9/+OEH+Pr6Ytu2bfDw8LB5HxEREZEr49i0/OQfm968eRNbt27Fv//9b2zYsAF//vknqlat6uAWEhHZj0VbIqow7rvvPptnA1y9ehUAEB4ebtc+UnXv3h1vvvmmTcdevXoVVatWtVqULWofERERkSvj2LT8FByb5ubmolevXti5cycWLVrk9DOGiYis4fIIRFSpGD/qFRsbCwCIiooyfZTK+PEqa/suXLhgOkdCQgKeffZZ1K5dG56enggLC8OoUaNw8eJFq88ZHx+PcePGISoqCp6enqhevTo6d+6M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jyFDhiA+Ph5TpkzBf/7zn1APKSKsXbsWP/zhD9G/f39omoZ333031EOKCI8//jgmTZqE3r17Iy0tDTNnzsT27dtDPayI8OKLL2Ls2LFITExEYmIiTj/9dHz44YehHlZE+tWvfgVN03DrrbeGeihhb8mSJdA0rcnbyJEjQz0sCjM8b+0YnrcGhueugeN5qxzPXeV47hocPG8NDM9dA8OirdC2bdtgmiZeeuklfPPNN/jNb36D3/3ud7jvvvtCPbSIUFdXhyuuuAI33XRTqIcS1t566y3cfvvtWLx4MQoLCzFu3DhceOGFOHz4cKiHFvaqqqowbtw4PP/886EeSkRZs2YNbr75Zqxbtw4rV65EfX09LrjgAlRVVYV6aGEvMzMTv/rVr/D111+joKAA06dPx2WXXYZvvvkm1EOLKOvXr8dLL72EsWPHhnooEePUU09FcXFxw9sXX3wR6iFRmOF5a8fwvNU+nrvK8LxVjueucjx37Tiet8rw3DUAFgXN0qVLraysrFAPI6Lk5+dbSUlJoR5G2Jo8ebJ18803N3xsGIbVv39/6/HHHw/hqCIPAOudd94J9TAi0uHDhy0A1po1a0I9lIjUp08f6w9/+EOohxExKioqrGHDhlkrV660zjnnHOuWW24J9ZDC3uLFi61x48aFehgUgXjeGjiet7aP564dx/PWjuG5a8fw3NU+nrfK8Nw1MNxpG0RutxspKSmhHgZFibq6Onz99dc4//zzG47puo7zzz8f//73v0M4MupO3G43APB7W4AMw8Cbb76JqqoqnH766aEeTsS4+eabcckllzT5vkft27lzJ/r374/s7Gz87//+L/bu3RvqIVEE4HkrBRvPXSkc8NxVhueugeN5qxzPXe1zhnoA0WLXrl149tln8dRTT4V6KBQlysrKYBgG0tPTmxxPT0/Htm3bQjQq6k5M08Stt96KM888E2PGjAn1cCLCf//7X5x++umoqalBr1698M4772D06NGhHlZEePPNN1FYWIj169eHeigRZcqUKVi2bBlGjBiB4uJiPPzwwzjrrLOwZcsW9O7dO9TDozDF81bqDDx3pVDjuWvgeO4qw/NWOZ67BoY7bZu55557WjRFbv7W/KTjwIEDmDFjBq644gpcf/31IRp56EmyI6LwdfPNN2PLli148803Qz2UiDFixAhs3LgRX331FW666SbMmTMH3377baiHFfb27duHW265BX/6058QHx8f6uFElIsuughXXHEFxo4diwsvvBAffPABjh8/jr/85S+hHhp1AZ63yvG8lSj68Nw1cDx3DRzPWzuG566B4U7bZu644w7MnTu3zdtkZ2c3/PvgwYM499xzccYZZ+Dll1/u5NGFt0Czo7b17dsXDocDJSUlTY6XlJSgX79+IRoVdRcLFizA+++/j7Vr1yIzMzPUw4kYsbGxGDp0KABgwoQJWL9+PZ555hm89NJLIR5ZePv6669x+PBh5OXlNRwzDANr167Fc889h9raWjgcjhCOMHIkJydj+PDh2LVrV6iHQl2A561yPG8NPp67Uijx3FWG566B43lrcPHctW0s2jaTmpqK1NRUW7c9cOAAzj33XEyYMAH5+fnQ9e69cTmQ7Kh9sbGxmDBhAj799FPMnDkTgPqTn08//RQLFiwI7eAoalmWhYULF+Kdd97B6tWrkZWVFeohRTTTNFFbWxvqYYS98847D//973+bHJs3bx5GjhyJu+++mye+AaisrMTu3btx7bXXhnoo1AV43irH89bg47krhQLPXYOL567t43lrcPHctW0s2godOHAA06ZNw+DBg/HUU0+htLS04XP8n+T27d27F0ePHsXevXthGAY2btwIABg6dCh69eoV2sGFkdtvvx1z5szBxIkTMXnyZDz99NOoqqrCvHnzQj20sFdZWdnkf+tcLhc2btyIlJQUDBo0KIQjC28333wz/vznP+O9995D7969cejQIQBAUlISEhISQjy68HbvvffioosuwqBBg1BRUYE///nPWL16Nf75z3+Gemhhr3fv3i16z/Xs2ROnnHIKe9K1484778QPf/hDDB48GAcPHsTixYvhcDhw9dVXh3poFEZ43toxPG+1j+euMjxvleO5qxzPXWV43toxPHcNkEUi+fn5FgC/b9S+OXPm+M1u1apVoR5a2Hn22WetQYMGWbGxsdbkyZOtdevWhXpIEWHVqlV+19icOXNCPbSw1tr3tfz8/FAPLezNnz/fGjx4sBUbG2ulpqZa5513nvXxxx+HelgR65xzzrFuueWWUA8j7F155ZVWRkaGFRsbaw0YMMC68sorrV27doV6WBRmeN7aMTxvDQzPXQPH81Y5nrvK8dw1eHjeah/PXQOjWZZldU45mIiIiIiIiIiIiIgC1b2bWRERERERERERERGFGRZtiYiIiIiIiIiIiMIIi7ZEREREREREREREYYRFWyIiIiIiIiIiIqIwwqItERERERERERERURhh0ZaIiIiIiIiIiIgojLBoS0RERERERERERBRGWLQlIiIiIiIiIiIiCiMs2hIRBWDatGkYM2ZMqIfRIa+//jpGjhyJmJgYJCcnNxx/8sknkZ2dDYfDgfHjx4dsfIFavXo1NE3D6tWrQz0UIiIiorDCc9fww3NXIrKLRVsiiijLli2DpmkoKCjw+/lIOjH99ttvsWTJEnz33XctPvfCCy9g2bJlth9L07RW32688caG223btg1z585FTk4Ofv/73+Pll18GAHz88cdYtGgRzjzzTOTn5+Oxxx7r6PRa+OCDD7BkyRLbtzdNE6+99hqmTJmClJQU9O7dG8OHD8d1112HdevWBX18RERERMHGc1f/eO5KRNQ+Z6gHQETUXX377bd4+OGHMW3aNAwZMqTJ51544QX07dsXc+fOtf143//+93Hddde1OD58+PCGf69evRqmaeKZZ57B0KFDG45/9tln0HUdr7zyCmJjYwOeix0ffPABnn/+edsnv7/4xS/w/PPP47LLLsP//u//wul0Yvv27fjwww+RnZ2NqVOnAgDOPvtsVFdXd9q4iYiIiIjnru3huSsRBRuLtkREUWL48OG45ppr2rzN4cOHAaDJn5Z5jyckJITNyWNJSQleeOEFXH/99Q07KryefvpplJaWNnys6zri4+O7eohERERE1AE8dyUiahvbIxBR1MvPz8f06dORlpaGuLg4jB49Gi+++KLf23744Yc455xz0Lt3byQmJmLSpEn485//3Objf/zxx+jRoweuvvpqeDweAOpPuS6//HKkpKQgPj4eEydOxN///veG+yxbtgxXXHEFAODcc89t+HOw1atXY8iQIfjmm2+wZs2ahuPTpk3rcA5DhgzB4sWLAQCpqanQNA1LlizB/9/e/cdUXf1xHH/BFZIuF6Psyg3L+LXJbjAWRgsZOBBppaRNjbmG1fDHvClt8YdajNV0za0FyhgSjVvmxpYRmzKNsbxj6VzWxpiZ/ZChy8IkyxuFpHLP9w/Hndd7BTEz9Pt8bHe79/y6n/f7H96c+zn3hoWFye1266+//vK/35XH23bu3KnMzExFRUXp3nvvVUlJiX788ceg9b/44gs99dRTio2NldVqVXp6urZu3SpJeuGFF1RXVycp8DjctfT29soYo9mzZwf1hYWFyW63+19f/b1gI8cQQz2uzuP1xgYAAHCrULteRu1K7Qr8v+NOWwC3Ja/Xq19//TWo/eLFi0Ft9fX1cjqdKi4u1qRJk7Rnzx6tWbNGPp9PLpfLP+7999/XSy+9JKfTqQ0bNuiee+5RV1eXPv30Uy1btizkdbS1tWnx4sV67rnn1NTUJIvFoqNHj2r27NmKj4/X+vXrZbVa9dFHH2nhwoVqaWnRokWLlJubq3Xr1mnbtm3auHGjUlNTJUmpqamqqanR2rVrFR0drddee02SNG3atDFzMjQ0FDInMTExioyMVE1NjXbs2KHW1lbV19crOjpa6enpSk5O1rvvvqvDhw/rvffekyRlZ2dLkjZv3qzKykotXbpUZWVl6u/vV21trXJzc9XV1eW/66Gjo0Pz58+Xw+FQeXm54uLidOzYMbW1tam8vFyrVq3Szz//rI6ODn344YdjxjJjxgxJ0q5du7RkyRLdfffdY84ZkZubG/QeJ0+e1Ouvvx5QMF9vbAAAAP8UtWswatfLqF0BXJMBgNuI2+02kkZ9OJ3OgDmDg4NB6xQVFZnExET/63PnzhmbzWYef/xxc/78+YCxPp/P/zwvL8+/fktLi4mIiDArVqwww8PD/jEFBQUmLS3NDA0NBayRnZ1tUlJS/G27du0ykozH4wm6PqfTafLy8q4vKcaMmo/m5mb/uKqqKiPJ9Pf3B8xfvny5sVqtAW0nTpwwFovFbN68OaD9yJEjZtKkSf72S5cumYSEBDNjxgzz+++/B4y9Mncul8uM589OaWmpkWRiY2PNokWLzNtvv22OHTsWNM7j8Vwzj8YYc/78eZOZmWkeeOAB09fXN67YAAAA/glq19CoXaldAYyNO20B3Jbq6uoCfqRgxKuvvqrh4eGAtqioKP9zr9erixcvKi8vT+3t7fJ6vZoyZYo6Ojo0MDCg9evXB33HVKijUM3NzSotLdXq1au1bds2/5jffvtN+/fv15tvvqmBgQENDAz45xQVFamqqko//fST4uPj/1H8oTzzzDN6+eWXg9rT0tJuaL1PPvlEPp9PS5cuDbgLIi4uTikpKfJ4PNq4caO6urrU29ur6urqoE/4RztGNha3262srCw1NTWptbVVra2tqqioUH5+vnbs2HHdOVyzZo2OHDmizs5OxcXFjSs2AACAm4HaNRi1a2jUrgBGsGkL4LaUlZWlWbNmBbXHxsYGHbM6ePCgqqqqdOjQIQ0ODgb0jRS+PT09kqRHHnlkzPfu7e3V888/ryVLlqi2tjag7/jx4zLGqLKyUpWVlSHnnzlz5l8pfKdPn665c+fetPV++OEHGWOUkpISsj8iIkKSxpW78QgPD5fL5ZLL5dLZs2d18OBBbd++Xfv27VNJSYk+//zzMddoaGiQ2+1WQ0OD/xd7peuPDQAA4Gagdg1G7RqM2hXAldi0BXBH6+npUUFBgWbOnKl33nlHDz74oCIjI7V3715VV1fL5/ONe02HwyGHw6G9e/fqq6++CijAR9arqKhQUVFRyPnJyck3Fswt5vP5FBYWpn379slisQT1R0dH37Jrue+++1RcXKzi4mLNmTNHnZ2dOnnypP/7w0I5fPiwysvLVVZWppUrVwb0TaTYAAAARlC73riJVN9RuwK4Gdi0BXBH27Nnj/7++2/t3r1bDz30kL/d4/EEjEtKSpIkff3112MWppMnT1ZbW5vy8/P15JNPqrOzU06nU5KUmJgo6fKn3WPdOTDa8at/cjTrZklKSpIxRgkJCSGP8105Trqcu9FivlkxzZo1S52dnerr67tm4dvf36/FixcrIyPD/8u/V1/z9cQGAABwK1G73jhqVwB3mvD/+gIA4N808km0Mcbf5vV65Xa7A8bNmzdPNptNb731loaGhgL6rpw7YsqUKWpvb5fdbldhYaH/mJXdbtecOXPU0NCgvr6+oHn9/f3+51arVZJ07ty5oHFWqzVk+6307LPPymKx6I033gjKgTFGZ8+elSQ9+uijSkhIUE1NTdA1XzlvtHivdvr0aX3zzTdB7RcuXNBnn32m8PDwa/6DMjw8rJKSEl24cEEtLS2KjIy84dgAAABuJWrXG0ftCuBOw522AO5o8+bNU2RkpBYsWKBVq1bpzz//VGNjo+x2e0BhGhMTo+rqapWVlemxxx7TsmXLFBsbq+7ubg0ODuqDDz4IWnvq1Knq6OhQTk6O5s6dqwMHDig+Pl51dXXKyclRWlqaVqxYocTERP3yyy86dOiQTp06pe7ubklSRkaGLBaLtmzZIq/Xq7vuukv5+fmy2+3KzMxUfX29Nm3apOTkZNntduXn548a6/fff6+dO3cGtU+bNk2FhYXjzl1SUpI2bdqkDRs26MSJE1q4cKFsNpt6e3vV2tqqlStXqqKiQuHh4aqvr9eCBQuUkZGhF198UQ6HQ99++62OHj2q9vZ2SVJmZqYkad26dSoqKpLFYlFJSUnI9z516pSysrKUn5+vgoICxcXF6cyZM2publZ3d7deeeUVTZ06NeTc7du3a//+/Vq9enXQXSkjubje2AAAAG4laldq11C5oHYF/k8ZALiNuN1uI8l8+eWXIfvz8vKM0+kMaNu9e7dJT083kydPNg8//LDZsmWLaWpqMpJMb29v0Njs7GwTFRVlYmJiTFZWlmlubh51/ePHjxuHw2FSU1NNf3+/McaYnp4eU1paauLi4kxERISJj4838+fPNx9//HHA3MbGRpOYmGgsFouRZDwejzHGmNOnT5unn37a2Gw2I8nk5eWNmhdJ13xcObeqqspI8l/niOXLlxur1Rpy7ZaWFpOTk2OsVquxWq1m5syZxuVyme+++y5g3IEDB0xhYaGx2WzGarWa9PR0U1tb6++/dOmSWbt2rbn//vtNWFiYGe1P0B9//GG2bt1qioqKzPTp001ERISx2WzmiSeeMI2Njcbn8/nHejyegNyNxDhWLsYTGwAAwI2gdg2N2pXaFcDYwowJcXYCAAAAAAAAAPCf4DttAQAAAAAAAGACYdMWAAAAAAAAACYQNm0BAAAAAAAAYAJh0xYAAAAAAAAAJhA2bQEAAAAAAABgAmHTFgAAAAAAAAAmEDZtAQAAAAAAAGACYdMWAAAAAAAAACYQNm0BAAAAAAAAYAJh0xYAAAAAAAAAJhA2bQEAAAAAAABgAmHTFgAAAAAAAAAmEDZtAQAAAAAAAGAC+R99VH1zOlJ5KgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# create scatter plots\n", + "fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6))\n", + "\n", + "# scatter plot1: Hackett effect vs Kemmeren effect\n", + "scatter1 = ax1.scatter(clean_data['effect_hackett'], clean_data['effect_kemmeren'], \n", + " alpha=0.6, s=10, c='blue')\n", + "ax1.set_xlabel('Hackett Effect Size', fontsize=12)\n", + "ax1.set_ylabel('Kemmeren Effect Size', fontsize=12)\n", + "ax1.set_title(f'Regulator: {chosen_regulator_symbol}\\nEffect vs Effect', fontsize=14)\n", + "# Add correlation coefficient text\n", + "ax1.text(0.05, 0.95, f'Spearman ρ = {eff_eff_corr:.3f}\\np = {eff_eff_pval:.3e}', \n", + " transform=ax1.transAxes, fontsize=12, verticalalignment='top',\n", + " bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))\n", + "ax1.grid(True, linestyle='--', alpha=0.7)\n", + "\n", + "# scatter plot2: Hackett effect vs Kemmeren pvalue\n", + "# Set a minimum p-value to ensure that -log10(p) does not exceed the range of the plot\n", + "min_pvalue = 10**(-5) \n", + "\n", + "# copy pvalue column and replace 0 values\n", + "pvalue_for_plot = clean_data['pvalue_kemmeren'].copy()\n", + "pvalue_for_plot[pvalue_for_plot == 0] = min_pvalue\n", + "\n", + "# calculate log_pvalue\n", + "log_pvalue = -np.log10(pvalue_for_plot)\n", + "\n", + "scatter2 = ax2.scatter(clean_data['effect_hackett'], log_pvalue, \n", + " alpha=0.6, s=10, c='red')\n", + "ax2.set_xlabel('Hackett Effect Size', fontsize=12)\n", + "ax2.set_ylabel('-log10(Kemmeren p-value)', fontsize=12)\n", + "ax2.set_title(f'Regulator: {chosen_regulator_symbol}\\nEffect vs P-value', fontsize=14)\n", + "# Add correlation coefficient text\n", + "ax2.text(0.05, 0.95, f'Spearman ρ = {eff_pval_corr:.3f}\\np = {eff_pval_pval:.3e}', \n", + " transform=ax2.transAxes, fontsize=12, verticalalignment='top',\n", + " bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))\n", + "ax2.grid(True, linestyle='--', alpha=0.7)\n", + "\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "da5d4268", + "metadata": {}, + "source": [ + "## Make distributions over the shared regulators" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "7db5d124", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import Dict, Any, Optional\n", + "\n", + "# Functional encapsulation\n", + "# Calculate the correlation between the two datasets for a given regulator\n", + "def calculate_regulator_correlation(\n", + " chosen_regulator_symbol: str, \n", + " hackett_final: pd.DataFrame, \n", + " kemmeren_final: pd.DataFrame\n", + ") -> Optional[Dict[str, Any]]:\n", + " \"\"\"\n", + " Parameters:\n", + " chosen_regulator_symbol (str): The regulator symbol to be analyzed\n", + " hackett_final (DataFrame): Filtered Hackett dataset\n", + " kemmeren_final (DataFrame): Kemmeren dataset\n", + "\n", + " Returns:\n", + " dict: Dictionary containing correlation coefficients and other \n", + " statistical information, returns None if insufficient data\n", + " \"\"\"\n", + " # Filter data for the specific regulator\n", + " hackett_reg = hackett_final[hackett_final['regulator_symbol'] == chosen_regulator_symbol].copy()\n", + " kemmeren_reg = kemmeren_final[kemmeren_final['regulator_symbol'] == chosen_regulator_symbol].copy()\n", + " \n", + " # Check if data was found\n", + " if len(hackett_reg) == 0 or len(kemmeren_reg) == 0:\n", + " print(f\"No data found for regulator {chosen_regulator_symbol}\")\n", + " return None\n", + " \n", + " # Merge datasets on target_symbol\n", + " merged = pd.merge(\n", + " hackett_reg[['target_symbol', 'log2_shrunken_timecourses']].rename(\n", + " columns={'log2_shrunken_timecourses': 'effect_hackett'}),\n", + " kemmeren_reg[['target_symbol', 'Madj', 'pval']].rename(\n", + " columns={'Madj': 'effect_kemmeren', 'pval': 'pvalue_kemmeren'}),\n", + " on='target_symbol',\n", + " how='inner'\n", + " )\n", + " \n", + " if len(merged) < 3: # Need at least 3 points for correlation\n", + " print(f\"Insufficient data points ({len(merged)}) for regulator {chosen_regulator_symbol}\")\n", + " return None\n", + " \n", + " # Calculate correlations\n", + " # Remove any NaN values\n", + " clean_data = merged.dropna(subset=['effect_hackett', 'effect_kemmeren', 'pvalue_kemmeren'])\n", + " \n", + " if len(clean_data) < 3:\n", + " print(f\"Insufficient clean data points ({len(clean_data)}) for regulator {chosen_regulator_symbol}\")\n", + " return None\n", + " \n", + " # Spearman correlations\n", + " eff_eff_corr, eff_eff_pval = spearmanr(clean_data['effect_hackett'], clean_data['effect_kemmeren'])\n", + " eff_pval_corr, eff_pval_pval = spearmanr(clean_data['effect_hackett'], clean_data['pvalue_kemmeren'])\n", + " \n", + " correlation_results = {\n", + " 'regulator_symbol': chosen_regulator_symbol,\n", + " 'n_targets': len(clean_data),\n", + " 'eff_eff_corr': eff_eff_corr,\n", + " 'eff_eff_pval': eff_eff_pval,\n", + " 'eff_pval_corr': eff_pval_corr,\n", + " 'eff_pval_pval': eff_pval_pval\n", + " }\n", + " \n", + " return correlation_results" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "f116659e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'regulator_symbol': 'ACA1',\n", + " 'n_targets': 6075,\n", + " 'eff_eff_corr': np.float64(0.01608402414610723),\n", + " 'eff_eff_pval': np.float64(0.21004245167836072),\n", + " 'eff_pval_corr': np.float64(-0.017860635301765383),\n", + " 'eff_pval_pval': np.float64(0.1639456246498038)}" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# test the function\n", + "calculate_regulator_correlation('ACA1', hackett_final, kemmeren_final)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "89b6ad09", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Successfully calculated correlations for 145 regulators\n", + " regulator_symbol n_targets eff_eff_corr eff_eff_pval eff_pval_corr \\\n", + "0 ACA1 6075 0.016084 2.100425e-01 -0.017861 \n", + "1 ADA2 6075 -0.100878 3.243911e-15 -0.031116 \n", + "2 AFT2 6075 -0.004887 7.033263e-01 0.034297 \n", + "3 ARO80 6075 -0.037804 3.209219e-03 -0.027915 \n", + "4 ARR1 6075 -0.010794 4.002741e-01 -0.002523 \n", + "\n", + " eff_pval_pval \n", + "0 0.163946 \n", + "1 0.015293 \n", + "2 0.007507 \n", + "3 0.029576 \n", + "4 0.844150 \n" + ] + } + ], + "source": [ + "\n", + "# Extract unique regulator symbols from the filtered Hackett dataset\n", + "regulator_symbols = hackett_final['regulator_symbol'].unique().tolist()\n", + "\n", + "# Initialize an empty list to store all correlation results\n", + "all_correlation_results = []\n", + "\n", + "# Loop through each regulator symbol and calculate correlations\n", + "for regulator_symbol in regulator_symbols:\n", + " # Calculate correlations for this regulator\n", + " correlation_result = calculate_regulator_correlation(\n", + " regulator_symbol, \n", + " hackett_final, \n", + " kemmeren_final\n", + " )\n", + " \n", + " # Only add to results if we got a valid result (not None)\n", + " if correlation_result is not None:\n", + " all_correlation_results.append(correlation_result)\n", + "\n", + "# Convert the list of dictionaries to a DataFrame for easier analysis\n", + "results_df = pd.DataFrame(all_correlation_results)\n", + "\n", + "print(f\"Successfully calculated correlations for {len(results_df)} regulators\")\n", + "print(results_df.head()) # Display first few rows" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "7df98119", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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n2jYsGGWsbGxsRo3blye6wgDwPWE3NVx5K7O4e65q72TC6pXr6569eppyZIlOnXqlEqVKqWVK1fq4MGD6tevn1Ux+d9//1WDBg2UmpqqZs2aqX379goICJCHh4dWrlypVatWFThnutqpU6dkGIYOHTpk87yzZD3Pubn55pu1Y8cOHTp0SFWrVrXr+IWV25YpUybbByTSf79X06ZNyzWutLS0XAu0AwcO1LvvvquIiAh16NBBYWFhlnV1R48efV3ktm+99ZZuueUWxcXFacyYMRozZox8fX3VtWtXTZo0Kdfzh/ugQAvk08qVKyVJ9evXz7NvdHS04uPjdfHiRW3atEnff/+93n77bT300EMKDw9X48aN7T6urf+U7HH06NEc200mk+XTew8PD0nSpUuXsvXNSpIKKus/mJxiOnLkiFU/ZwoICJDZbNbx48etFp2XLt+EwDCMQjluYciK8/nnn7drkf+sG0QcOnSoQMeNjIy0minpKlnn/8033+i+++7Ls7+zzj83jRs3VmJiopYvX253gbYgr4fc3g9y2pa1n9TU1Dxn7eTk999/1zfffKNWrVrpu+++k6enp2XbunXrLDcrKYhr8T5RvHhxS/K6d+9erVixQu+9957eeustpaen6/3333d43wDgbshdHUfu6hxFKXft3r27nnvuOS1YsEBPPvmk5s+fb2m/0pQpU3Tq1CnNnz9fjz32mNW2p556SqtWrbLreB4eHrpw4YLNbVf/nmc9z/Xq1dPGjRvt2n9OGjdurJUrV2r58uVq3ry5XWNcldv+9ddfio6OtivGqx07dkzTpk1TrVq1tHbtWqsP6Y8cOZJrodte1+J9pFixYho8eLAGDx6sw4cPa9WqVYqLi9O8efN05MgR/fjjjw7vG9eOh6sDAK4nu3bt0oIFC+Tj46P777/f7nFeXl664447NHr0aL399tsyDEPffvutZXtWkcOeT9Pz69dff83Wtm/fPh04cEA1atSwXCKWdXdLW4lQ1iVWV/P09MxXzFl3t/zll1+yJUuGYeiXX36x6udMt912m6T//ki5UlZbYRy3MNSvX18mk0lr1661q//tt98uDw8PrVixwnJpU248PDwK5XfRWRo2bChJdp9/1iVTP/30U559HX0tZt1ZedKkSUpPT8+1b9an8Nf69ZD1vGVdDuaIrMvH2rVrZ1WclWy/10ju/z5xyy23qHfv3lq1apVuuukmLV682Cn7BQB3QO5qzd3/T7oSuat75q4PP/ywihUrpo8++kjp6elauHChKleurDvuuMOqX1bO1LFjR6t2wzBynUV8tVKlSunYsWPZPohIS0uzLA2Rxd/fX9WrV9c///xjdfm7I3r16iVPT0/NnDlTx48fz7VvVm5brVo1+fr66vfff9e5c+ey9XP2721+/yawZc+ePTIMQy1atMh2BVVuua1k//vftX5ewsPD9fDDD+uHH35Q5cqVtWzZsjz/PoF7oEAL2Om3335Tq1atlJGRoZdffjnPS242bdpk8zKFrE/OfH19LW1BQUGSpAMHDjgx4svmzZunP//80/LYMAwNHTpUmZmZ6tWrl6W9atWq8vf31+LFiy2Xi2TFO2bMGJv7DgoK0okTJ+xes6p8+fJq1qyZtm3bptmzZ1ttmzlzpv755x81b9482xpeztCzZ09Jly9TufLnkpKSYvlkNKuPu7v55pvVtWtXrVmzRhMmTLA5M2D9+vWWBCA0NFQPPvig1fpaV7o66QsKCtLBgwcL7wQKqGPHjipfvrwmT55s+cPoShcvXtTq1astjzt06KBy5crpo48+svnp8ZV/2Dn6WmzWrJkefvhh7dy5Uw888ICOHTuWrU9qaqqGDh2qmTNnSrr2r4d+/fqpWLFieuaZZ7R///5s20+fPp3jH7RZstZJu/L5laRt27Zp/PjxNse42/vE8ePH9ffff2drP3XqlDIyMqzemwHgekbump27/Z+UG3JX98xdy5Qpo3vvvVe//fabpk6dqtTU1GwzZKWcc6bXX3/dZh6Sk/r16+vixYtWS3sZhqEhQ4bYXKpg4MCBOnfunJ544gmb2/fu3avExMQ8j1u5cmW9+OKLOnHihNq0aaO9e/dm63P+/HlNnjzZsmSEt7e3Hn74YZ04cSJbXvjDDz/oxx9/VOXKlfM1Ez83sbGx8vf317Bhw7Rt27Zs28+dO5fnxISsn9OaNWus1p09ePCghgwZYnNMft//Cvt5ycjI0Jo1a7K1p6Wl6ezZs/Ly8rJccQD3xhIHwFX+/fdfy38yFy5c0LFjx7Rhwwb99ddf8vT01PDhwzVy5Mg89zN//ny9//77iomJUaVKlRQQEKDt27dryZIlCgoKUmxsrKVv8+bNFR8frwcffFBt2rSRr6+vateurfbt2xf4fFq1aqVGjRqpW7duCgkJ0fLly7Vx40bdcccdeuaZZyz9vL299cwzz2jcuHGqW7euOnbsqDNnzuibb75RkyZNbC683rx5c23cuFFt2rTR3XffLW9vb8XExCgmJibHeGbMmKG77rpLTzzxhL755hvdeuut2rZtmxYvXqyQkBDNmDGjwOdsS0xMjJ555hm98847io6O1oMPPijDMPTll1/q4MGDGjhwYK5x58elS5dyXZe1W7duqlatWoGOMX36dO3cuVMvvvii5s+fr0aNGikwMFAHDhzQxo0btXv3biUlJVk+CZ4+fbr+/vtvjR07VkuWLFHz5s1lGIZ27dqln376SUePHrVcTta8eXMtWLBAnTp10m233SZPT0916NBBtWrVsiu2K19Dtrz88ssFKoL5+PgoPj5ebdq0UZMmTdS8eXPVrFlTJpNJ+/bt06+//qrg4GDLDRB8fHy0YMECtW7dWm3atFHr1q1Vu3ZtpaamasuWLTp37pylMFmtWjWFh4frs88+k4+Pj8qVKyeTyaRnnnlGJUuWzDWuWbNmyTAMffbZZ7rlllt07733qkqVKjIMQ7t379by5ct15swZy6Vw0rV9PURHR2v69Ol6+umnVbVqVbVt21aVKlXSmTNntGfPHq1atUq9evXSe++9l+M+GjRooAYNGmjBggVKSkrSHXfcof3792vx4sVq166d4uPjs41xt/eJQ4cO6bbbblPt2rVVq1YtlS1bVidPntTXX3+tixcvavDgwQ7vGwBcgdyV3LWgbvTcNb+6d++uJUuWWF5Xtgq0Tz31lOLi4vTggw+qa9euCg4O1rp16/THH3+oXbt2+u677+w61oABAxQXF6fHH39cS5cuVUhIiH799VedPn1atWvX1tatW636P/nkk1q3bp3mzp2r3377TS1atFB4eLiOHj2qHTt2aP369frkk08UGRmZ57HHjBmj8+fPa8qUKapataqaN2+u6OhoeXl5ae/evVq2bJlOnjxp9YHIG2+8oVWrVmnMmDFas2aNGjZsqMTERH3xxRcqXry44uLinFYsDAkJ0aeffqouXbqodu3aat26tapVq6aMjAwlJiZq1apVuvPOO3NdXzgsLEwPPvigvvzyS91+++265557dPToUX377be65557cnwfye/7X2E+L+np6WrcuLGqVKmievXqqXz58jp79qy+/fZbHTlyRIMH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w+Xn56eGDRtq6dKldo09dOiQunbtqsDAQAUEBKhjx47as2dPtn4mk8nm1+uvv+7s0wEAAMANws/Pz2qGapbz589bttvj119/VZ8+fdSqVSuNHTs22zEk5Xic3I4xZMgQpaSkWL5yWxMXAAAA+Vdkljjo1auX4uPj9dxzzykqKkpz5sxR27ZttWLFCt111105jjt79qyaNWumlJQUDR06VF5eXpoyZYqaNGmiLVu2KDg42Kp/y5Yt1aNHD6u22267rVDOCQAAAEVfWFiYDh06lK09a1mC8PDwPPexdetWdejQQdHR0YqPj7e6EVjWMbL2efUM2KSkJMt6t7b4+PjYnHkLAAAA5ygSBdoNGzbos88+04QJEzR48GBJUo8ePRQdHa0XX3xRa9asyXHs9OnTtXv3bm3YsEH169eXJLVp00bR0dGaNGmSxo0bZ9W/SpUqeuyxxwrvZAAAAHBDqVOnjlasWKHU1FSrG4WtX7/esj03CQkJat26tcqUKaMlS5bopptusnkMSdq4caNVMfbw4cM6ePCg+vbtW/ATAQAAgEOKxBIH8fHx8vT0tEosfX191adPH61duzbXy7Di4+NVv359S3FWkqpVq6Z77rlHCxYssDkmPT3dcskZAAAAUBCdO3dWZmamZs6caWnLyMhQXFycGjZsaJnxun//fu3YscNq7JEjR3TvvffKw8NDP/74o0JCQmweo0aNGqpWrZpmzpypzMxMS/uMGTNkMpmsbigGAACAa6tIzKDdvHmzqlSpYjXjQJJldsCWLVts3szAbDbrzz//VO/evbNta9CggX766SedOXNG/v7+lvY5c+Zo+vTpMgxD1atX1/Dhw/XII484+YwAAABwo2jYsKG6dOmiIUOG6NixY6pcubLmzp2rxMREzZo1y9KvR48eWrVqlQzDsLS1bt1ae/bs0YsvvqjVq1dr9erVlm2hoaFq2bKl5fGECRPUoUMH3XvvverWrZv+/vtvvfvuu3r88cdVvXr1a3OyAAAAyKZIFGiTkpIs62pdKavt8OHDNsclJycrIyMjz7FVq1aVJN15553q2rWrbrnlFh0+fFjTpk3To48+qpSUFD399NM5xpeRkWF1Q4bU1FT7Tw4AAABF3rx58zRixAjNnz9fp06dUq1atfTtt98qJiYm13Fbt26VJL355pvZtjVp0sSqQHvfffdp4cKFGj16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QNLlG4SNGTNGnTt31i233KLk5GR98skn+vvvvzVu3DjdfPPN1/CMAQAAcKUiUaCNj4+Xp6en+vbta2nz9fVVnz59NHToUB04cMAy88DW2Pr161uKs5JUrVo13XPPPVqwYIGlQLtixQqdPHlS/fr1sxrfv39/ffzxx/ruu+/02GOPFcLZAQAAoKibN2+eRowYofnz5+vUqVOqVauWvv32W8XExOQ67tSpUxoxYoRV26RJkyRJFSpUsBRoa9asqVtvvVUfffSRjh8/Lm9vb9WpU0cLFixQly5dCuekAAAAYJciUaDdvHmzqlSpooCAAKv2Bg0aSJK2bNlis0BrNpv1559/qnfv3tm2NWjQQD/99JPOnDkjf39/bd68WZJ0++23W/WrV6+ePDw8tHnz5vwXaM+fl7y9s7d7eFi3/+8OvjYVpG9GhnTFDAwrJpN05c0g8tP3wgXJbM45Dl9f1/f18bkctyRdvChddcmfU/peunT5yxl9vb0v//yc3dfLS/L0zH/fzMzLz0VOihW7/JXfvmbz5Z+ds/saxuXfYWf09fS8/Fw4u++1et3n1vfq55P3iMvf8x6R/768Rzi/rzu8R7gij8gtniLG19dXEyZM0IQJE3Lss3LlymxtkZGRVjNqc1KvXj0tXry4ICECAACgkBSJAm1SUpLCwsKytWe1HT582Oa45ORkZWRk5Dm2atWqSkpKkqenp8qUKWPVz9vbW8HBwTkeQ7q8hljGFX90WW4A0aPHf3+YXen226WRI/97/NhjOf/RFh0tjR//3+M+faSrbjBhERUlTZ783+N+/aRjx2z3jYiQpk//7/GgQdKBA7b7likjXXH5nV5+Wdq923bfgADp44//ezxypPT337b7+vhI/1tOQtLl89y40XZfSfrmm/++nzxZ+t9lfjZ98cV/xZpp06Tly3Pu+9FHUsmSl7//8ENpyZKc+86adfn5kKR586Svvsq577RpUvnyl79fsED69NOc+06efPnnJ0mLF0txcTn3HTdOqlnz8vc//ii9917OfV95RcqaPb5qlTR1as59X3pJuuuuy9+vXSvltvbyc89J99xz+fs//pBefTXnvk89JbVrd/n7bdukoUNz7hsbKz3wwOXvExKk//u/nPs+/LD0yCOXvz9wQOrfP+e+998vZX1Qc/z45ddRTtq2lZ5++vL3qamXX585ueeey8+F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xwUFBTklSAAAAMBR5LIAAABwZ3YVaIcOHarY2Fh99913kiSTyaSnnnpKTz31VK7jMjMzCx4hAAAAUADksgAAAHBndhVou3fvrgYNGmjlypU6evSoRo0apfvvv5+1uwAAAOD2yGUBAADgzuwq0EpS1apVVbVqVUlSXFycevbsqQ4dOhRaYAAAAICzkMsCAADAXdldoL3S3r17nR0HAAAAcE2QywIAAMCdeDg6MDU1Va+//rpatWql2267TRs2bJAkJScna/Lkyfr333+dFiQAAADgTOSyAAAAcBcOzaA9ePCgmjRpogMHDigqKko7duzQ2bNnJV2+2+3777+vffv26a233nJqsAAAAEBBkcsCAADAnThUoH3hhRd05swZbdmyRWXKlFGZMmWstnfq1EnffvutUwIEAAAAnIlcFgAAAO7EoSUOfvrpJw0cOFC33nqrTCZTtu0VK1bUgQMHChwcAAAA4GzksgAAAHAnDhVo09PTFRISkuP2M2fOOBwQAAAAUJjIZQEAAOBOHCrQ3nrrrfrll19y3L5o0SLddtttDgcFAAAAFBZyWQAAALgThwq0zz33nD777DO98cYbSklJkSSZzWb9+++/6t69u9auXatBgwY5NVAAAADAGchlAQAA4E4cuknYY489pn379mn48OEaNmyYJKl169YyDEMeHh4aN26cOnXq5Mw4AQAAAKcglwUAAIA7cahAK0nDhg1T9+7d9eWXX+rff/+V2WxWpUqV9MADD6hixYrOjBEAAABwKnJZAAAAuAuHC7SSVL58eS7/AgAAwHWJXBYAAADuoEAF2r179+r777/Xvn37JEmRkZFq3bq1brnlFqcEBwAAABQWclkAAAC4A4cLtM8//7zeeustmc1mq3YPDw8999xzmjhxYoGDAwAAAAoDuSwAAADchYcjgyZNmqQpU6bogQce0Nq1a3X69GmdPn1aa9euVefOnTVlyhRNmTLF2bECAAAABUYuCwAAAHfi0AzaDz74QB06dNCCBQus2hs2bKjPPvtM58+f1/vvv8+aXgAAAHA75LIAAABwJw7NoE1MTFSrVq1y3N6qVSslJiY6GhMAAABQaMhlAQAA4E4cKtCWKVNGW7duzXH71q1bFRIS4nBQAAAAQGEhlwUAAIA7cahA26VLF3344Yd6/fXXlZaWZmlPS0vTG2+8oQ8//FAPPfSQ04IE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9bVsd5bffvtNzz77rDp06KCnnnpKmzZtUnR0tIYOHZrrccaPH6+SJUtaviIiIpwWEwAAAPKnsHJZAAAAwBEOFWj79eunn376SW3bttVPP/2kxMREJSYm6scff1S7du20dOlSDRgwIN/7/eWXX+Tn52fX186dOyVJfn5+NteZzVojNr/ry+aHt7e3BgwYoNOnT2vTpk059hsyZIhSUlIsXwcOHCi0mAAAAJC7wsplAQAAAEcUc2RQv379dOzYMb3++uv68ccfrbZ5eXnplVde0dNPP53v/VarVk1xcXF29c1awiAsLExJSUnZtme1hYeH5zuO/MiaDZvbZXA+Pj42Z/kCAADg2iusXBYAAABwhEMFWkkaNWqUBgwYoGXLlmnfvn2SpAoVKqhFixYqXbq0Q/u8+eab1atXr3yNqVOnjn799VeZzWarG4WtX79exYsXV5UqVRyKxV579uyRJIWEhBTqcQAAAOA8hZHLAgAAAI5wuEArSaVLl1a3bt2cFYtDOnfurPj4eC1cuFCdO3eWJJ04cUJffPGF2rdvbzVzNSEhQZIcWlPs+PHj2YqwZ86c0dSpU1W6dGnVq1evAGcBAACAa80dclkAAADA7gJtSkqKHnroIcXExGjo0KE59hs7dqxWr16tL774QjfddJNTgsxN586ddccddyg2Nlbbt29X6dKlNX36dGVmZmr06NFWfe+55x5JUmJioqVt3759mj9/viRp48aNkqQxY8ZIujyLonv37pKkadOmadGiRWrfvr3Kly+vpKQkzZ49W/v379f8+fPl7e1d2KcKAAAAB7lrLgsAAADYXaB99913tWbNGksxMydPPPGE3nzzTU2bNk0vvfRSgQPMi6enp5YsWaIXXnhBb7/9ttLT01W/fn3NmTNHVatWzXP83r17NWLECKu2rMdNmjSxFGgbN26sNWvW6MMPP9TJkydVokQJNWjQQLNnz1bz5s2df2IAAABwGnfNZQEAAAC7C7RfffWVunXrludaq2XKlNHDDz+sL7/88poltaVKldKHH36oDz/8MNd+V86czdK0aVMZhpHnMVq2bKmWLVs6GiIAAABcyJ1zWQAAANzYPPLuctmOHTt0++2329W3bt26+ueffxwOCgAAAHAmclkAAAC4K7sLtPbMMr2S2WzOdzAAAABAYSCXBQAAgLuyu0Bbvnx5bdq0ya6+mzZt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Bw5URkaGbd2PfvQjlZSUaPTo0QoNDVVpaaneeOMNXbp0Sb/85S9vyL0CAACgdR1+SJjZbFZCQoI2bdrUGfEAAAAAncbdclkPDw9t375d06ZN07Jly5SZmamgoCD95S9/sbViaEtYWJj++te/KioqSs8884wWL16scePGaceOHXanb++55x6ZTCa99tprmj17tt58802NGDFCH330kZKSkjrxDgEAAHAtHT5B6+Hhodtuu63Vp8UCAAAA7swdc9mAgACtWrVKq1atanPdf56c/U8DBgzQH//4xzb3pqWlKS0tzdEQAQAA0Ik6fIJWkr7//e/rzTff1Pnz550dDwAAANCpyGUBAADgThx6SFhjY6MsFouioqKUmpqqiIgIeXl52a0xmUx2DyYAAAAA3AG5LAAAANyJQwXap556yvbfq1evbnUNSS1w82poaNDZs2ddHQacrPkz5bO9+QQHB8vT09PVYQBug1wWAAAA7sShAu2JEyecHQeALuTs2bN67bXXXB0GOsmGDRtcHQKcbM6cObr11ltdHQbgNshlAQAA4E4cKtDedtttzo4DQBcSHBysOXPmuDoMAO0UHBzs6hAAt0IuCwAAAHfiUIEWQPfm6enJaTwAAAAAAAAnMDu68eOPP9bDDz+shIQE3X777YqMjLR7RUVFOTPONl24cEGzZs1ScHCwfHx8NHLkSB08ePCa+wzD0Nq1a3X//fcrLCxMPj4+iouL00svvaS6urpW96xevVpf+cpX1KtXL0VHR2v58uXOvh0AAAB0MnfKZQEAANC9OVSgzc/PV2JiorZu3arQ0FAdP35ckZGRCg0N1WeffabevXtrxIgRzo61VYZhKCUlRevXr9fcuXO1ePFinTlzRklJSfr000/b3Hv58mVlZGTo7NmzevTRR/Xqq68qMTFR8+fPV3JyspqamuzWv/HGG3rooYc0YMAALV++XHfddZcef/xx/exnP+vMWwQAAIATuVMuCwAAAJia/m8Vsh1GjBihc+fOac+ePWpoaFDfvn21c+dOjRo1Snv37lVycrJ+85vfKDk5uTNitrNhwwZNmzZNOTk5Sk1NlfTFA4xiYmKUnJys9evXX3VvQ0OD9u/fr7vvvttu/H/+5380f/587dixQ2PGjJEk1dbWKiwsTMOGDdPWrVtta9PT05Wbm6tTp04pICCgXTFXV1fL399fVVVV8vPz6+gtAwAAdAudlTO5Uy7bFZHLAnBHp06d0pIlS/TUU08pLCzM1eEAQIdyJodO0B48eFAzZ86Un5+fPDw8JEmNjY2SpK997Wt65JFH9Pzzzzty6Q7buHGjrFarJk+ebBsLDg7W1KlTtXnzZtXX1191r6enZ4virCRNmjRJknT06FHb2Pvvv6+KigrNnj3bbu2cOXN06dIlbdu27XpvBQAAADeAO+WyAAAAgEMF2h49esjX11eSdMstt6hnz546c+aMbT4yMlJHjhxxToTXcOjQIQ0ePFhms/2tJCYm6vLlyzp27FiHr/mvf/1LkhQUFGT3PpI0ZMgQu7UJCQkym822eQAAALg3d8plAQAAAIcKtLfffrutv6vJZFL//v313nvv2ea3bdumfv36OSfCaygrK1NISEiL8eax06dPd/iaixcvlp+fn93X2srKyuTh4aG+ffvarfX09FSfPn3afJ/6+npVV1fbvQAAAOAa7pTLAgAAAA4VaMeNG6fs7GxduXJFkvTkk09q06ZNio6OVnR0tLZs2aJHHnmkw9c1DEN1dXXtejW3zq2trZXFYmlxrV69etnmO+Lll1/Wzp079dOf/lS33HKLbby2tlaenp6t7unVq1eb77No0SL5+/vbXvTDAQAAcJ3OymUBAAAARzhUoH3++edVWFho69k1ffp0/frXv1ZcXJzi4+O1Zs0aPf300x2+7gcffCAvL692vYqLiyVJXl5erfaZraurs82317vvvqvnnntOM2fO1GOPPWY35+XlpYaGhlb31dXVtfk+8+bNU1VVle116tSpdscEAAAA5+qsXBYAAABwRA9HNvXs2VN9+vSxG0tPT1d6evp1BdO/f39lZWW1a21zC4OQkBCVlZW1mG8eCw0Nbdf1duzYoQcffFApKSl6/fXXW32/xsZGnTlzxq7NQUNDgyoqKtp8H4vF0uopXwAAANx4nZXLAgAAAI5wqEDbrL6+XgcPHtSZM2c0fPhwu4dqOaJfv36aMWNGh/YMGjRIBQUFMgzD7kFhe/fulbe3t2JiYq55jb1792rSpEkaMmSINmzYoB49Wv5vGTRokCRp//79GjdunG18//79MgzDNg8AAICuwdm5LAAAAOAIh1ocSNKyZcsUEhKir3/965o8ebI+/vhjSdK5c+cUFBSkNWvWOC3ItqSmpqq8vFybNm2yjZ07d045OTkaP3683cnVkpISlZSU2O0/evSoUlJSFBERoa1bt161VcGoUaMUGBiolStX2o2vXLlS3t7eSklJceJdAQAAoDO5Sy4LAAAAOHSCNisrS//93/+t//qv/9I999yj733ve7a5oKAgjRo1Sr/97W/txjtLamqqhg0bpoyMDB05ckRBQUFasWKFGhsbtWDBAru1o0ePliSVlpZKkmpqanTvvfeqsrJSmZmZ2rZtm936qKgo3XXXXZK+6EH74osvas6cOZoyZYruvfdeFRQUaN26dVq4cKECAwM7/V4BAABw/dwplwUAAAAcKtAuXbpUEyZM0Pr161VRUdFiPiEhQcuWLbvu4NrDw8ND27dvV2ZmppYtW6ba2loNHTpUa9euVWxsbJt7KyoqbA/seuaZZ1rMT58+3VaglaTZs2erZ8+eWrp0qbZs2aKwsDC98soreuKJJ5x7UwAAAOg07pTLAgAAAA4VaP/3f/9Xjz/++FXnAwMDW012O0tAQIBWrVqlVatWtbmu+eRss4iICDU1NXXovR5++GE9/PDDHQ0RAAAAbsLdclkAAAB0bw71oL3lllt07ty5q84fOXJE/fr1czgoAAAAoLOQywIAAMCdOFSgHTdunN58801duHChxdzhw4f11ltv6f7777/e2AAAAACnI5cFAACAO3GoQPvSSy+psbFRcXFxeu6552QymfT2228rPT1dQ4YMUd++ffXCCy84O1YAAADgupHLAgAAwJ04VKANDQ3VgQMH9K1vfUvvvvuumpqa9M477+j3v/+90tLStGfPHgUFBTk7VgAAAOC6kcsCAADAnTj0kDBJ6tu3r+3BXGfPnpVhGAoODpbZ7FDNFwAAALhhyGUBAADgLq47A21qalJTU5NMJpNMJpMzYgIAAABuCHJZAAAAuJrDBdojR44oNTVVfn5+CgkJUUhIiPz8/JSamqqioiJnxggAAAA4FbksAAAA3IVDLQ4KCgqUnJwswzA0YcIExcTESJKKi4u1ZcsW5eXl6Q9/+IO+8Y1vODVYAAAA4HqRywIAAMCdOFSg/cEPfqC+ffvqr3/9q8LCwuzmTp06pREjRujJJ5/U3/72N6cECQAAADgLuSwAAADciUMtDg4fPqzZs2e3SGglKSwsTI899pgOHz583cEBAAAAzkYuCwAAAHfiUIH2tttuU319/VXnGxoaWk14AQAAAFcj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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Effect-Effect Correlation Summary:\n", + "count 145.000000\n", + "mean -0.058967\n", + "std 0.072261\n", + "min -0.296206\n", + "25% -0.091059\n", + "50% -0.037956\n", + "75% -0.008329\n", + "max 0.042079\n", + "Name: eff_eff_corr, dtype: float64\n", + "\n", + "Effect-Pvalue Correlation Summary:\n", + "count 145.000000\n", + "mean -0.002324\n", + "std 0.047957\n", + "min -0.164093\n", + "25% -0.028492\n", + "50% -0.001771\n", + "75% 0.017656\n", + "max 0.186708\n", + "Name: eff_pval_corr, dtype: float64\n" + ] + } + ], + "source": [ + "# Set up the plotting style and parameters\n", + "plt.style.use('default')\n", + "plt.rcParams['figure.figsize'] = (14, 6)\n", + "plt.rcParams['font.size'] = 12\n", + "\n", + "# Create a figure with two subplots side by side\n", + "fig, (ax1, ax2) = plt.subplots(1, 2)\n", + "\n", + "# First boxplot: Effect-Effect Correlation Distribution\n", + "sns.boxplot(data=results_df, y='eff_eff_corr', ax=ax1, color='lightblue')\n", + "ax1.set_title('Distribution of Effect-Effect Correlations', fontsize=14)\n", + "ax1.set_ylabel('Spearman Correlation Coefficient', fontsize=12)\n", + "# Add a horizontal line at y=0 for reference\n", + "ax1.axhline(y=0, color='r', linestyle='--', alpha=0.7)\n", + "# Add text showing the number of regulators\n", + "ax1.text(0.05, 0.95, f'n = {len(results_df)}', transform=ax1.transAxes, \n", + " fontsize=12, verticalalignment='top',\n", + " bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))\n", + "\n", + "# Second boxplot: Effect-Pvalue Correlation Distribution\n", + "sns.boxplot(data=results_df, y='eff_pval_corr', ax=ax2, color='lightcoral')\n", + "ax2.set_title('Distribution of Effect-Pvalue Correlations', fontsize=14)\n", + "ax2.set_ylabel('Spearman Correlation Coefficient', fontsize=12)\n", + "# Add a horizontal line at y=0 for reference\n", + "ax2.axhline(y=0, color='r', linestyle='--', alpha=0.7)\n", + "# Add text showing the number of regulators\n", + "ax2.text(0.05, 0.95, f'n = {len(results_df)}', transform=ax2.transAxes, \n", + " fontsize=12, verticalalignment='top',\n", + " bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))\n", + "\n", + "# Adjust layout and display the plot\n", + "plt.tight_layout()\n", + "plt.show()\n", + "\n", + "# Optional: Print summary statistics\n", + "print(\"Effect-Effect Correlation Summary:\")\n", + "print(results_df['eff_eff_corr'].describe())\n", + "print(\"\\nEffect-Pvalue Correlation Summary:\")\n", + "print(results_df['eff_pval_corr'].describe())\n" + ] + }, + { + "cell_type": "markdown", + "id": "4becea1e", + "metadata": {}, + "source": [ + "## Check if all final_common regulators have other timepoints" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "2cb70685", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "3b0d6da1ced149a3a36254fb394ebd6d", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, layout=Layout(width='auto'), style=ProgressStyle(bar_color='black'))" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dataset 'time_0' contains 1037400 rows of data\n", + "Dataset 'time_5' contains 1025050 rows of data\n", + "Dataset 'time_10' contains 1018875 rows of data\n", + "Dataset 'time_15' contains 1012700 rows of data\n", + "Dataset 'time_20' contains 1006525 rows of data\n", + "Dataset 'time_30' contains 1031225 rows of data\n", + "Dataset 'time_45' contains 1018875 rows of data\n", + "Dataset 'time_60' contains 6175 rows of data\n", + "Dataset 'time_90' contains 1018875 rows of data\n", + "Dataset 'time_100' contains 12350 rows of data\n", + "Dataset 'time_120' contains 6175 rows of data\n", + "Dataset 'time_180' contains 6175 rows of data\n", + "Dataset 'time_290' contains 6175 rows of data\n" + ] + } + ], + "source": [ + "hackett_2020_copy = HfQueryAPI(repo_id=\"BrentLab/hackett_2020\")\n", + "\n", + "hackett_2020_copy.set_sql_filter(\n", + " \"hackett_2020\",\n", + " f\"\"\"\n", + " mechanism = 'ZEV' \n", + " AND restriction = 'P' \n", + " \"\"\")\n", + "\n", + "hackett_data_copy = hackett_2020_copy.query(\"SELECT * FROM hackett_2020\", \"hackett_2020\")\n", + "\n", + "# Get all unique time points\n", + "unique_times = hackett_data_copy['time'].unique()\n", + "unique_times.sort() # Sort in chronological order\n", + "\n", + "# Create a dictionary to store datasets split by time point\n", + "time_datasets = {}\n", + "\n", + "# Split data by each time point\n", + "for time_point in unique_times:\n", + " # Filter data for the current time point\n", + " time_data = hackett_data_copy[hackett_data_copy['time'] == time_point].copy()\n", + " \n", + " # Create meaningful key names using time point markers\n", + " key_name = f\"time_{int(time_point)}\" # Convert float to integer for naming\n", + " \n", + " # Store the dataset in the dictionary\n", + " time_datasets[key_name] = time_data\n", + " \n", + " # Print information about each dataset\n", + " print(f\"Dataset '{key_name}' contains {len(time_data)} rows of data\")" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "8654baec", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Time 0: Contains 145/145 regulators (100.0%) - All present? True\n", + "Time 5: Contains 144/145 regulators (99.3%) - All present? False\n", + "Time 10: Contains 144/145 regulators (99.3%) - All present? False\n", + "Time 15: Contains 145/145 regulators (100.0%) - All present? True\n", + "Time 20: Contains 142/145 regulators (97.9%) - All present? False\n", + "Time 30: Contains 144/145 regulators (99.3%) - All present? False\n", + "Time 45: Contains 143/145 regulators (98.6%) - All present? False\n", + "Time 60: Contains 1/145 regulators (0.7%) - All present? False\n", + "Time 90: Contains 143/145 regulators (98.6%) - All present? False\n", + "Time 100: Contains 2/145 regulators (1.4%) - All present? False\n", + "Time 120: Contains 1/145 regulators (0.7%) - All present? False\n", + "Time 180: Contains 1/145 regulators (0.7%) - All present? False\n", + "Time 290: Contains 1/145 regulators (0.7%) - All present? False\n" + ] + } + ], + "source": [ + "# Check the percentage of final_common regulators present in each time-split dataset\n", + "\n", + "# Initialize a list to store the results\n", + "coverage_results = []\n", + "\n", + "# Get the total number of regulators in final_common for percentage calculation\n", + "total_regulators = len(final_common)\n", + "\n", + "# Iterate through each time-split dataset\n", + "for time_key, dataset in time_datasets.items():\n", + " # Extract the time point from the key name\n", + " time_point = time_key.replace('time_', '')\n", + " \n", + " # Get unique regulators in this time dataset\n", + " regulators_in_dataset = set(dataset['regulator_symbol'].unique())\n", + " \n", + " # Find the intersection with final_common regulators\n", + " common_regulators = regulators_in_dataset.intersection(set(final_common))\n", + " \n", + " # Calculate the percentage of final_common regulators present\n", + " percentage_present = (len(common_regulators) / total_regulators) * 100\n", + " \n", + " # Check if all final_common regulators are present\n", + " contains_all = len(common_regulators) == total_regulators\n", + " \n", + " # Store the result\n", + " coverage_results.append({\n", + " 'time_point': time_point,\n", + " 'contains_all_shared_regulators': contains_all,\n", + " 'percentage_present': percentage_present,\n", + " 'regulators_present': len(common_regulators),\n", + " 'total_regulators': total_regulators\n", + " })\n", + " \n", + " # Print the result for this time point\n", + " print(f\"Time {time_point}: Contains {len(common_regulators)}/{total_regulators} regulators ({percentage_present:.1f}%) - All present? {contains_all}\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 87a9ad17739c08e5d6291d4100fb35f015064149 Mon Sep 17 00:00:00 2001 From: chasem Date: Wed, 15 Oct 2025 13:24:51 -0500 Subject: [PATCH 10/49] kaiwens correlation tutorial --- docs/tutorials/correlation_tutorial.ipynb | 3420 +++++++++++++++++++++ 1 file changed, 3420 insertions(+) create mode 100644 docs/tutorials/correlation_tutorial.ipynb diff --git a/docs/tutorials/correlation_tutorial.ipynb b/docs/tutorials/correlation_tutorial.ipynb new file mode 100644 index 0000000..5c3d5d4 --- /dev/null +++ b/docs/tutorials/correlation_tutorial.ipynb @@ -0,0 +1,3420 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "fcdcb13a", + "metadata": {}, + "source": [ + "# Hackett 2020 vs Kemmeren Correlation Analysis Tutorial\n", + "\n", + "This tutorial demonstrates how to conduct comprehensive correlation analysis between Hackett 2020 (mcisaac) and Kemmeren datasets using tfbpapi, focusing on regulator-target relationships and temporal dynamics.\n", + "\n", + "## Overview\n", + "\n", + "Correlation analysis evalute how well the effects of the same regulators correlate between these two experimental approaches, and whether timing affects the correlation strength.\n", + "\n", + "### Datasets Used\n", + "- **Perturbation data**: McIsaac (ZEV system), Kemmeren (overexpression)\n", + "\n", + "### Analysis Strategy\n", + "1. Load and filter Hackett 2020 data (time=15, mechanism=\"zev\", restriction=\"p\") and Kemmeren 2014 data\n", + "2. Identify shared regulators between Hackett and Kemmeren\n", + "3. For each shared regulator, calculate Spearman correlations:\n", + " - Hackett effect vs Kemmeren effect\n", + " - Hackett effect vs Kemmeren p-value\n", + "4. Generate scatter plots for specific individual regulator\n", + "5. Create distribution plots (boxplots) across all shared regulators\n", + "6. Analyze temporal dynamics by comparing different timepoints in Hackett data" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "30abaca0", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chase/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" + ] + } + ], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "from tfbpapi.HfQueryAPI import HfQueryAPI\n", + "from tfbpapi.datainfo.datacard import DataCard\n", + "from tfbpapi.errors import DataCardError, HfDataFetchError\n", + "from scipy.stats import spearmanr\n", + "import matplotlib.pyplot as plt\n", + "import seaborn as sns\n", + "\n", + "# Configure plotting\n", + "plt.style.use('default')\n", + "sns.set_palette(\"husl\")" + ] + }, + { + "cell_type": "markdown", + "id": "12941f4d", + "metadata": {}, + "source": [ + "## Dataset Exploration with DataCard\n", + "\n", + "Before loading data, let's explore dataset structure and metadata using the DataCard interface." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "c9b9afdf", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Exploring dataset structures...\n", + "\n", + "==================================================\n", + "Exploring BrentLab/hackett_2020\n", + "Dataset types in configs: {'annotated_features'}\n", + "Configurations: 1\n", + "\n", + "Config: hackett_2020\n", + " Type: annotated_features\n", + " Features: 17\n", + " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", + " Target fields: ['target_locus_tag', 'target_symbol']\n", + " Metadata fields: ['regulator_locus_tag', 'regulator_symbol', 'time', 'mechanism', 'restriction', 'date', 'strain']\n", + "\n", + "==================================================\n", + "Exploring BrentLab/kemmeren_2014\n", + "Dataset types in configs: {'annotated_features'}\n", + "Configurations: 1\n", + "\n", + "Config: kemmeren_2014\n", + " Type: annotated_features\n", + " Features: 11\n", + " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", + " Target fields: ['target_locus_tag', 'target_symbol']\n", + " Metadata fields: ['regulator_locus_tag', 'regulator_symbol']\n", + "\n", + "==================================================\n", + "Successfully explored 2 datasets\n" + ] + } + ], + "source": [ + "# Explore dataset structure using DataCard\n", + "print(\"Exploring dataset structures...\")\n", + "\n", + "datasets_to_explore = [\n", + " \"BrentLab/hackett_2020\",\n", + " \"BrentLab/kemmeren_2014\"\n", + "]\n", + "\n", + "dataset_info = {}\n", + "\n", + "for repo_id in datasets_to_explore:\n", + " try:\n", + " print(f\"\\n{'='*50}\")\n", + " print(f\"Exploring {repo_id}\")\n", + "\n", + " # Create DataCard instance\n", + " datacard = DataCard(repo_id)\n", + " card = datacard.dataset_card\n", + "\n", + " # DatasetCard doesn't have dataset_type directly - it's on individual configs\n", + " dataset_types = [config.dataset_type.value for config in card.configs]\n", + " print(f\"Dataset types in configs: {set(dataset_types)}\")\n", + " print(f\"Configurations: {len(card.configs)}\")\n", + "\n", + " # Store dataset info for later use\n", + " dataset_info[repo_id] = {\n", + " 'datacard': datacard,\n", + " 'card': card,\n", + " 'configs': {config.config_name: config for config in card.configs}\n", + " }\n", + "\n", + " # Display configuration details\n", + " for config in card.configs:\n", + " print(f\"\\nConfig: {config.config_name}\")\n", + " print(f\" Type: {config.dataset_type.value}\")\n", + " print(f\" Features: {len(config.dataset_info.features) if config.dataset_info.features else 0}\")\n", + "\n", + " if config.dataset_info.features:\n", + " # Show regulator and target fields if available\n", + " feature_names = [f.name for f in config.dataset_info.features]\n", + " regulator_fields = [f for f in feature_names if 'regulator' in f.lower()]\n", + " target_fields = [f for f in feature_names if 'target' in f.lower()]\n", + "\n", + " if regulator_fields:\n", + " print(f\" Regulator fields: {regulator_fields}\")\n", + " if target_fields:\n", + " print(f\" Target fields: {target_fields}\")\n", + "\n", + " if config.metadata_fields:\n", + " print(f\" Metadata fields: {config.metadata_fields}\")\n", + "\n", + " except (DataCardError, HfDataFetchError) as e:\n", + " print(f\"Error exploring {repo_id}: {e}\")\n", + " continue\n", + " except Exception as e:\n", + " print(f\"Unexpected error exploring {repo_id}: {e}\")\n", + " continue\n", + "\n", + "print(f\"\\n{'='*50}\")\n", + "print(f\"Successfully explored {len(dataset_info)} datasets\")" + ] + }, + { + "cell_type": "markdown", + "id": "cf0265f0", + "metadata": {}, + "source": [ + "## Initialize dataset connections and load data" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "a6b0424f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Initializing HfQueryAPI connections...\n", + "All API connections initialized\n" + ] + } + ], + "source": [ + "# Initialize dataset connections and load data\n", + "print(\"Initializing HfQueryAPI connections...\")\n", + "\n", + "hackett_2020 = HfQueryAPI(repo_id=\"BrentLab/hackett_2020\")\n", + "kemmeren_2014 = HfQueryAPI(repo_id=\"BrentLab/kemmeren_2014\")\n", + "\n", + "print(\"All API connections initialized\")" + ] + }, + { + "cell_type": "markdown", + "id": "6e1b7269", + "metadata": {}, + "source": [ + "## Get metadata from each dataset to find common regulators" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "baafb468", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Getting metadata from datasets...\n", + "All metadata loaded\n", + "Found 185 common regulators across all datasets\n" + ] + } + ], + "source": [ + "# Get metadata from each dataset to find common regulators\n", + "print(\"Getting metadata from datasets...\")\n", + "\n", + "kemmeren_meta = kemmeren_2014.get_metadata(\"kemmeren_2014\") \n", + "hackett_2020_meta = hackett_2020.get_metadata(\"hackett_2020\")\n", + "\n", + "print(\"All metadata loaded\")\n", + "\n", + "# Get the intersection of common regulators\n", + "common_regulators = (set(hackett_2020_meta.regulator_symbol.unique())\n", + " & set(kemmeren_meta.regulator_symbol.unique()))\n", + "\n", + "print(f\"Found {len(common_regulators)} common regulators across all datasets\")\n", + "\n", + "# Create proper SQL IN clause\n", + "if common_regulators:\n", + " regulator_clause = \"(\" + \", \".join(f\"'{reg}'\" for reg in common_regulators) + \")\"" + ] + }, + { + "cell_type": "markdown", + "id": "2d89d745", + "metadata": {}, + "source": [ + "## Filter the data" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "c888c477", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Applying dataset-specific filters...\n", + "All filters applied\n" + ] + } + ], + "source": [ + "# Apply dataset-specific filters\n", + "print(\"Applying dataset-specific filters...\")\n", + "\n", + "hackett_2020.set_sql_filter(\n", + " \"hackett_2020\",\n", + " f\"\"\"\n", + " time = 15 \n", + " AND mechanism = 'ZEV' \n", + " AND restriction = 'P' \n", + " AND regulator_symbol IN {regulator_clause}\n", + " \"\"\")\n", + "\n", + "print(\"All filters applied\")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "050e3f4c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Loading both perturbation datasets...\n", + "Both datasets loaded\n", + "hackett: 145 regulators\n", + "kemmeren: 1487 regulators\n", + "Final intersection: 145 regulators\n", + "Common regulators: ['ACA1', 'ADA2', 'AFT2', 'ARO80', 'ARR1', 'ASH1', 'AZF1', 'BDF2', 'BMH1', 'CAD1']...\n" + ] + } + ], + "source": [ + "# Load both perturbation datasets to find common regulators\n", + "print(\"Loading both perturbation datasets...\")\n", + "\n", + "# Load both datasets\n", + "hackett_data = hackett_2020.query(\"SELECT * FROM hackett_2020\", \"hackett_2020\")\n", + "kemmeren_data = kemmeren_2014.query(\"SELECT * FROM kemmeren_2014\", \"kemmeren_2014\")\n", + "\n", + "print(\"Both datasets loaded\")\n", + "\n", + "# Check what regulators we actually got from each dataset\n", + "actual_regulators = {\n", + " 'hackett': set(hackett_data.regulator_symbol.unique()),\n", + " 'kemmeren': set(kemmeren_data.regulator_symbol.unique()),\n", + "}\n", + "\n", + "for name, regulators in actual_regulators.items():\n", + " print(f\"{name}: {len(regulators)} regulators\")\n", + "\n", + "# Find the intersection - regulators present in BOTH datasets\n", + "final_common = set.intersection(*actual_regulators.values())\n", + "print(f\"Final intersection: {len(final_common)} regulators\")\n", + "\n", + "if final_common:\n", + " final_common_clause = \"(\" + \", \".join(f\"'{reg}'\" for reg in final_common) + \")\"\n", + " print(f\"Common regulators: {sorted(list(final_common))[:10]}...\") # Show first 10\n", + "else:\n", + " print(\"WARNING: No common regulators found!\")\n", + " final_common_clause = \"()\"\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "06293141", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Finding common targets...\n", + "Found 6063 common targets\n", + "Loading final datasets with complete filtering...\n", + "Final datasets loaded with complete filtering\n", + "McIsaac: 891,408 rows, 145 regulators\n", + "Kemmeren: 880,730 rows, 145 regulators\n" + ] + } + ], + "source": [ + "# Find common targets across all datasets\n", + "print(\"Finding common targets...\")\n", + "\n", + "common_targets = (set(hackett_data.target_symbol.unique())\n", + " & set(kemmeren_data.target_symbol.unique()))\n", + "\n", + "print(f\"Found {len(common_targets)} common targets\")\n", + "\n", + "if common_targets:\n", + " target_clause = \"(\" + \", \".join(f\"'{tgt}'\" for tgt in common_targets) + \")\"\n", + "\n", + "# Load final datasets with both regulator and target filtering \n", + "print(\"Loading final datasets with complete filtering...\")\n", + "\n", + "\n", + "# Load final datasets with both regulator and target filtering \n", + "hackett_final = hackett_2020.query(f\"\"\"\n", + " SELECT * FROM hackett_2020\n", + " WHERE regulator_symbol IN {final_common_clause} \n", + " AND target_symbol IN {target_clause}\n", + "\"\"\", \"hackett_2020\")\n", + "\n", + "kemmeren_final = kemmeren_2014.query(f\"\"\"\n", + " SELECT * FROM kemmeren_2014\n", + " WHERE regulator_symbol IN {final_common_clause} \n", + " AND target_symbol IN {target_clause}\n", + "\"\"\", \"kemmeren_2014\")\n", + "\n", + "print(\"Final datasets loaded with complete filtering\")\n", + "\n", + "# Print final dataset sizes\n", + "datasets_info = [\n", + " ('McIsaac', hackett_final), ('Kemmeren', kemmeren_final)\n", + "]\n", + "\n", + "for name, data in datasets_info:\n", + " if len(data) > 0:\n", + " regulators = data['regulator_symbol'].nunique() if 'regulator_symbol' in data.columns else 0\n", + " print(f\"{name}: {len(data):,} rows, {regulators} regulators\")\n", + " else:\n", + " print(f\"{name}: No data loaded\")" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "549e73ae", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "regulator_locus_tag", + "rawType": "object", + "type": "string" + }, + { + "name": "regulator_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "reporterId", + "rawType": "object", + "type": "string" + }, + { + "name": "target_locus_tag", + "rawType": "object", + "type": "string" + }, + { + "name": "target_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "M", + "rawType": "float64", + "type": "float" + }, + { + "name": "Madj", + "rawType": "float64", + "type": "float" + }, + { + "name": "A", + "rawType": "float64", + "type": "float" + }, + { + "name": "pval", + "rawType": "float64", + "type": "float" + }, + { + "name": "variable_in_wt", + "rawType": "bool", + "type": "boolean" + }, + { + "name": "multiple_probes", + "rawType": "bool", + "type": "boolean" + } + ], + "ref": "31ae3af5-8283-4e49-9dc6-2dbd59140d0f", + "rows": [ + [ + "0", + "YBL008W", + "HIR1", + "SCAB000002", + "Q0017", + "Q0017", + "-0.039025753", + "-0.0397208098656576", + "5.666642", + "0.591335", + "False", + "False" + ], + [ + "1", + "YBL008W", + "HIR1", + "SCAB000004", + "Q0045", + "COX1", + "-0.10270845", + "-0.104404483712687", + "7.1872175", + "0.278744", + "False", + "False" + ], + [ + "2", + "YBL008W", + "HIR1", + "SCAB000005", + "Q0050", + "AI1", + "-0.09215408", + "-0.101312394230742", + "7.9818127", + "0.789989", + "True", + "False" + ], + [ + "3", + "YBL008W", + "HIR1", + "SCAB000006", + "Q0055", + "AI2", + "-0.11363403", + "-0.123208021645201", + "9.3025734", + "0.695235", + "True", + "False" + ], + [ + "4", + "YBL008W", + "HIR1", + "SCAB000007", + "Q0060", + "AI3", + "-0.0099832871", + "-0.0186756321118991", + "8.3151407", + "0.975177", + "False", + "False" + ], + [ + "5", + "YBL008W", + "HIR1", + "SCAB000008", + "Q0065", + "AI4", + "-0.16157419", + "-0.173001391865694", + "11.104408", + "0.508279", + "True", + "False" + ], + [ + "6", + "YBL008W", + "HIR1", + "SCAB000009", + "Q0070", + "AI5_ALPHA", + "-0.29374796", + "-0.306969229210268", + "10.721268", + "0.127481", + "True", + "False" + ], + [ + "7", + "YBL008W", + "HIR1", + "SCAB000010", + "Q0075", + "AI5_BETA", + "-0.048776282", + "-0.0512431882361536", + "7.3602157", + "0.788852", + "True", + "False" + ], + [ + "8", + "YBL008W", + "HIR1", + "SCAB000011", + "Q0080", + "ATP8", + "-0.18210337", + "-0.187242319447809", + "6.7582501", + "0.227365", + "True", + "False" + ], + [ + "9", + "YBL008W", + "HIR1", + "SCAB000012", + "Q0085", + "ATP6", + "-0.0065067789", + "-0.00701312899803988", + "6.4296849", + "0.9677", + "False", + "False" + ], + [ + "10", + "YBL008W", + "HIR1", + "SCAB000014", + "Q0105", + "COB", + "-0.023712626", + "-0.0241945156649505", + "5.6905146", + "0.819055", + "False", + "False" + ], + [ + "11", + "YBL008W", + "HIR1", + "SCAB000015", + "Q0110", + "BI2", + "-0.10026133", + "-0.102682551857025", + "6.3605368", + "0.538407", + "False", + "False" + ], + [ + "12", + "YBL008W", + "HIR1", + "SCAB000016", + "Q0115", + "BI3", + "-0.053808146", + "-0.0569359410492525", + "6.6718358", + "0.725973", + "False", + "False" + ], + [ + "13", + "YBL008W", + "HIR1", + "SCAB000017", + "Q0120", + "BI4", + "-0.020458971", + "-0.0236853183042711", + "6.9095507", + "0.927896", + "False", + "False" + ], + [ + "14", + "YBL008W", + "HIR1", + "SCAB000018", + "Q0130", + "OLI1", + "-0.043537368", + "-0.0566553964261391", + "12.556363", + "0.890456", + "True", + "False" + ], + [ + "15", + "YBL008W", + "HIR1", + "SCAB000019", + "Q0140", + "VAR1", + "-0.16343272", + "-0.163938440697184", + "6.1672218", + "0.188679", + "True", + "False" + ], + [ + "16", + "YBL008W", + "HIR1", + "SCAB000023", + "Q0160", + "SCEI", + "-0.11115273", + "-0.112545724325415", + "6.40361", + "0.233242", + "False", + "False" + ], + [ + "17", + "YBL008W", + "HIR1", + "SCAB000024", + "Q0182", + "Q0182", + "-0.074097777", + "-0.07502283029592", + "5.8703247", + "0.373117", + "False", + "False" + ], + [ + "18", + "YBL008W", + "HIR1", + "SCAB000025", + "Q0250", + "COX2", + "-0.093127871", + "-0.0967308066694721", + "7.4043715", + "0.275569", + "False", + "False" + ], + [ + "19", + "YBL008W", + "HIR1", + "SCAB000026", + "Q0255", + "Q0255", + "-0.031337077", + "-0.0317708668814822", + "5.6274759", + "0.677195", + "False", + "False" + ], + [ + "20", + "YBL008W", + "HIR1", + "SCAB000027", + "Q0275", + "COX3", + "-0.018126893", + "-0.0174743777233046", + "6.0614997", + "0.861477", + "False", + "False" + ], + [ + "21", + "YBL008W", + "HIR1", + "SCAB000028", + "Q0297", + "Q0297", + "-0.0015863937", + "-0.00165756742970601", + "6.2053847", + "0.99118", + "False", + "False" + ], + [ + "22", + "YBL008W", + "HIR1", + "SCAB000029", + "YAL001C", + "TFC3", + "-0.060650037", + "-0.0567581406799775", + "9.7950907", + "0.415566", + "False", + "False" + ], + [ + "23", + "YBL008W", + "HIR1", + "SCAB000030", + "YAL002W", + "VPS8", + "-0.0074215245", + "-0.00566180001033513", + "8.3969858", + "0.930872", + "False", + "False" + ], + [ + "24", + "YBL008W", + "HIR1", + "SCAB000031", + "YAL003W", + "EFB1", + "-0.029623397", + "-0.03985574724458", + "14.921067", + "0.84173", + "False", + "False" + ], + [ + "25", + "YBL008W", + "HIR1", + "SCAB000032", + "YAL004W", + "YAL004W", + "0.059369123", + "0.0561266464793721", + "6.6928234", + "0.54419", + "False", + "False" + ], + [ + "26", + "YBL008W", + "HIR1", + "SCAB000033", + "YAL005C", + "SSA1", + "0.042630331", + "0.0487909166720867", + "15.675554", + "0.862133", + "False", + "False" + ], + [ + "27", + "YBL008W", + "HIR1", + "SCAB000034", + "YAL007C", + "ERP2", + "-0.060302507", + "-0.0583166518417961", + "11.623729", + "0.346134", + "False", + "False" + ], + [ + "28", + "YBL008W", + "HIR1", + "SCAB000035", + "YAL008W", + "FUN14", + "-0.089632662", + "-0.0758621939217628", + "9.7471534", + "0.251776", + "False", + "False" + ], + [ + "29", + "YBL008W", + "HIR1", + "SCAB000036", + "YAL009W", + "SPO7", + "0.061596497", + "0.059807970651245", + "9.5949268", + "0.337943", + "False", + "False" + ], + [ + "30", + "YBL008W", + "HIR1", + "SCAB000037", + "YAL010C", + "MDM10", + "0.033553389", + "0.0347720388006548", + "7.8775572", + "0.707184", + "False", + "False" + ], + [ + "31", + "YBL008W", + "HIR1", + "SCAB000038", + "YAL011W", + "SWC3", + "-0.053778196", + "-0.0547258310698601", + "6.1043641", + "0.478414", + "False", + "False" + ], + [ + "32", + "YBL008W", + "HIR1", + "SCAB000039", + "YAL012W", + "CYS3", + "-0.067001533", + "-0.0704603092262692", + "12.296266", + "0.589873", + "False", + "False" + ], + [ + "33", + "YBL008W", + "HIR1", + "SCAB000040", + "YAL013W", + "DEP1", + "0.045037505", + "0.0422594728589327", + "8.8906158", + "0.705922", + "False", + "False" + ], + [ + "34", + "YBL008W", + "HIR1", + "SCAB000041", + "YAL014C", + "SYN8", + "-0.0028853882", + "-0.003421632139455", + "10.140393", + "0.984162", + "False", + "False" + ], + [ + "35", + "YBL008W", + "HIR1", + "SCAB000042", + "YAL015C", + "NTG1", + "0.17305066", + "0.178185020644813", + "9.5554939", + "0.010956", + "False", + "False" + ], + [ + "36", + "YBL008W", + "HIR1", + "SCAB000043", + "YAL016W", + "TPD3", + "-0.084858514", + "-0.0803126247904039", + "11.337088", + "0.270546", + "False", + "False" + ], + [ + "37", + "YBL008W", + "HIR1", + "SCAB000044", + "YAL017W", + "PSK1", + "-0.081218414", + "-0.0672602695566325", + "9.9839762", + "0.450345", + "False", + "False" + ], + [ + "38", + "YBL008W", + "HIR1", + "SCAB000045", + "YAL018C", + "LDS1", + "-0.0015791723", + "-0.00059224976381921", + "5.9934205", + "0.990055", + "False", + "False" + ], + [ + "39", + "YBL008W", + "HIR1", + "SCAB000046", + "YAL019W", + "FUN30", + "0.1520406", + "0.148431864200967", + "10.781851", + "0.0493148", + "False", + "False" + ], + [ + "40", + "YBL008W", + "HIR1", + "SCAB000047", + "YAL020C", + "ATS1", + "-0.020495554", + "-0.0238614082255441", + "9.3828017", + "0.836546", + "False", + "False" + ], + [ + "41", + "YBL008W", + "HIR1", + "SCAB000048", + "YAL021C", + "CCR4", + "-0.13337841", + "-0.13693929825126", + "9.4790413", + "0.171857", + "False", + "False" + ], + [ + "42", + "YBL008W", + "HIR1", + "SCAB000049", + "YAL022C", + "FUN26", + "-0.097163876", + "-0.0973296431760643", + "9.893272", + "0.226213", + "False", + "False" + ], + [ + "43", + "YBL008W", + "HIR1", + "SCAB000050", + "YAL023C", + "PMT2", + "-0.027986767", + "-0.0333328913664413", + "9.0967819", + "0.821875", + "False", + "False" + ], + [ + "44", + "YBL008W", + "HIR1", + "SCAB000051", + "YAL024C", + "LTE1", + "-0.13226007", + "-0.135436445273697", + "8.4398933", + "0.045112", + "False", + "False" + ], + [ + "45", + "YBL008W", + "HIR1", + "SCAB000052", + "YAL025C", + "MAK16", + "0.17071987", + "0.159309256880021", + "11.301871", + "0.0211462", + "False", + "False" + ], + [ + "46", + "YBL008W", + "HIR1", + "SCAB000053", + "YAL026C", + "DRS2", + "-0.1904308", + "-0.190251792611949", + "10.91405", + "0.00111783", + "False", + "False" + ], + [ + "47", + "YBL008W", + "HIR1", + "SCAB000054", + "YAL027W", + "SAW1", + "-0.18881836", + "-0.192964020762811", + "8.2938543", + "7.6856e-06", + "False", + "False" + ], + [ + "48", + "YBL008W", + "HIR1", + "SCAB000055", + "YAL028W", + "FRT2", + "-0.053743754", + "-0.0418562759081939", + "7.9800474", + "0.599747", + "False", + "False" + ], + [ + "49", + "YBL008W", + "HIR1", + "SCAB000056", + "YAL029C", + "MYO4", + "-0.025923023", + "-0.0246822755213326", + "10.036795", + "0.842811", + "False", + "False" + ] + ], + "shape": { + "columns": 11, + "rows": 880730 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
regulator_locus_tagregulator_symbolreporterIdtarget_locus_tagtarget_symbolMMadjApvalvariable_in_wtmultiple_probes
0YBL008WHIR1SCAB000002Q0017Q0017-0.039026-0.0397215.6666420.591335FalseFalse
1YBL008WHIR1SCAB000004Q0045COX1-0.102708-0.1044047.1872180.278744FalseFalse
2YBL008WHIR1SCAB000005Q0050AI1-0.092154-0.1013127.9818130.789989TrueFalse
3YBL008WHIR1SCAB000006Q0055AI2-0.113634-0.1232089.3025730.695235TrueFalse
4YBL008WHIR1SCAB000007Q0060AI3-0.009983-0.0186768.3151410.975177FalseFalse
....................................
880725YPL202CAFT2SCAB007301YML099W-AYML099W-A-0.141110-0.1164658.0925130.432310FalseFalse
880726YPL202CAFT2SCAB007304YNL067W-AYNL067W-A0.015283-0.0008496.4725350.969828FalseFalse
880727YPL202CAFT2SCAB007305YNL162W-AYNL162W-A0.1181870.0618369.3640320.696226FalseFalse
880728YPL202CAFT2SCAB007306YNR001W-AYNR001W-A0.0279180.0204606.3517640.944834FalseFalse
880729YPL202CAFT2SCAB007307YNR034W-AEGO4-0.1281200.1119417.6171170.846665FalseFalse
\n", + "

880730 rows × 11 columns

\n", + "
" + ], + "text/plain": [ + " regulator_locus_tag regulator_symbol reporterId target_locus_tag \\\n", + "0 YBL008W HIR1 SCAB000002 Q0017 \n", + "1 YBL008W HIR1 SCAB000004 Q0045 \n", + "2 YBL008W HIR1 SCAB000005 Q0050 \n", + "3 YBL008W HIR1 SCAB000006 Q0055 \n", + "4 YBL008W HIR1 SCAB000007 Q0060 \n", + "... ... ... ... ... \n", + "880725 YPL202C AFT2 SCAB007301 YML099W-A \n", + "880726 YPL202C AFT2 SCAB007304 YNL067W-A \n", + "880727 YPL202C AFT2 SCAB007305 YNL162W-A \n", + "880728 YPL202C AFT2 SCAB007306 YNR001W-A \n", + "880729 YPL202C AFT2 SCAB007307 YNR034W-A \n", + "\n", + " target_symbol M Madj A pval variable_in_wt \\\n", + "0 Q0017 -0.039026 -0.039721 5.666642 0.591335 False \n", + "1 COX1 -0.102708 -0.104404 7.187218 0.278744 False \n", + "2 AI1 -0.092154 -0.101312 7.981813 0.789989 True \n", + "3 AI2 -0.113634 -0.123208 9.302573 0.695235 True \n", + "4 AI3 -0.009983 -0.018676 8.315141 0.975177 False \n", + "... ... ... ... ... ... ... \n", + "880725 YML099W-A -0.141110 -0.116465 8.092513 0.432310 False \n", + "880726 YNL067W-A 0.015283 -0.000849 6.472535 0.969828 False \n", + "880727 YNL162W-A 0.118187 0.061836 9.364032 0.696226 False \n", + "880728 YNR001W-A 0.027918 0.020460 6.351764 0.944834 False \n", + "880729 EGO4 -0.128120 0.111941 7.617117 0.846665 False \n", + "\n", + " multiple_probes \n", + "0 False \n", + "1 False \n", + "2 False \n", + "3 False \n", + "4 False \n", + "... ... \n", + "880725 False \n", + "880726 False \n", + "880727 False \n", + "880728 False \n", + "880729 False \n", + "\n", + "[880730 rows x 11 columns]" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "kemmeren_final" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "d48a21fc", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "regulator_locus_tag", + "rawType": "object", + "type": "string" + }, + { + "name": "regulator_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "target_locus_tag", + "rawType": "object", + "type": "string" + }, + { + "name": "target_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "time", + "rawType": "float64", + "type": "float" + }, + { + "name": "mechanism", + "rawType": "object", + "type": "string" + }, + { + "name": "restriction", + "rawType": "object", + "type": "string" + }, + { + "name": "date", + "rawType": "object", + "type": "string" + }, + { + "name": "strain", + "rawType": "object", + "type": "string" + }, + { + "name": "green_median", + "rawType": "float64", + "type": "float" + }, + { + "name": "red_median", + "rawType": "float64", + "type": "float" + }, + { + "name": "log2_ratio", + "rawType": "float64", + "type": "float" + }, + { + "name": "log2_cleaned_ratio", + "rawType": "float64", + "type": "float" + }, + { + "name": "log2_noise_model", + "rawType": "float64", + "type": "float" + }, + { + "name": "log2_cleaned_ratio_zth2d", + "rawType": "float64", + "type": "float" + }, + { + "name": "log2_selected_timecourses", + "rawType": "float64", + "type": "float" + }, + { + "name": "log2_shrunken_timecourses", + "rawType": "float64", + "type": "float" + } + ], + "ref": "58a78ce1-cf9e-4895-aeda-7a6835616ff2", + "rows": [ + [ + "0", + "YER045C", + "ACA1", + "YNL055C", + "POR1", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "91090.295", + "124164.75", + "0.406527130762", + "0.0432783723328", + "0.167308051886", + "0.0", + "0.0", + "0.0" + ], + [ + "1", + "YER045C", + "ACA1", + "YIL114C", + "POR2", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "929.4918", + "662.37395", + "-0.0842615914349", + "-0.0977695029636", + "0.129415342895", + "-0.0", + "0.0", + "0.0" + ], + [ + "2", + "YER045C", + "ACA1", + "YPL188W", + "POS5", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "591.31345", + "734.7885", + "-0.0758081091353", + "-0.0508571587889", + "0.107883665412", + "-0.0", + "0.0", + "0.0" + ], + [ + "3", + "YER045C", + "ACA1", + "YIL160C", + "POT1", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "205.71685", + "121.719", + "0.5847627015", + "0.1705942734", + "0.215645578247", + "0.1705942734", + "0.170594273399", + "0.00462968359641295" + ], + [ + "4", + "YER045C", + "ACA1", + "YGL205W", + "POX1", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "119.5065", + "87.946405", + "0.179056555936", + "0.0200286797035", + "0.198205486203", + "0.0", + "0.0", + "0.0" + ], + [ + "5", + "YER045C", + "ACA1", + "YMR267W", + "PPA2", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "1275.302", + "1416.7", + "0.180721523462", + "0.0987729763456", + "0.104456428231", + "0.0", + "0.0", + "0.0" + ], + [ + "6", + "YER045C", + "ACA1", + "YHR075C", + "PPE1", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "1326.4295", + "1359.741", + "0.390555253563", + "0.266483987284", + "0.131385515049", + "0.0", + "0.0", + "0.0" + ], + [ + "7", + "YER045C", + "ACA1", + "YNR032W", + "PPG1", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "1099.959", + "1152.4355", + "-0.214915545431", + "-0.204062063627", + "0.117168825607", + "-0.0", + "0.0", + "0.0" + ], + [ + "8", + "YER045C", + "ACA1", + "YDL134C", + "PPH21", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "7493.293", + "7856.0695", + "-0.0775299490434", + "-0.133124758634", + "0.0927194595661", + "-0.0", + "0.0", + "0.0" + ], + [ + "9", + "YER045C", + "ACA1", + "YDL188C", + "PPH22", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "6011.497", + "4252.663", + "0.0215924976055", + "-0.0847941323527", + "0.126466576504", + "-0.0", + "0.0", + "0.0" + ], + [ + "10", + "YER045C", + "ACA1", + "YDR075W", + "PPH3", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "1084.69", + "953.6436", + "-0.143874021717", + "-0.0282806734827", + "0.115360567164", + "-0.0", + "0.0", + "0.0" + ], + [ + "11", + "YER045C", + "ACA1", + "YDR435C", + "PPM1", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "1241.2985", + "1179.9235", + "0.125080147412", + "0.0906497470663", + "0.119276410483", + "0.0", + "0.0", + "0.0" + ], + [ + "12", + "YER045C", + "ACA1", + "YOL141W", + "PPM2", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "6.4157995", + "2.552427", + "-1.48508070136", + "-1.37626086393", + "0.520525479607", + "-0.0", + "0.0", + "0.0" + ], + [ + "13", + "YER045C", + "ACA1", + "YDR452W", + "PPN1", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "4925.801", + "5224.871", + "-0.0386115679586", + "-0.121743225934", + "0.105781342704", + "-0.0", + "0.0", + "0.0" + ], + [ + "14", + "YER045C", + "ACA1", + "YPL179W", + "PPQ1", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "7831.3035", + "5207.4895", + "-0.235458038146", + "-0.246050278661", + "0.134560096602", + "-0.0", + "0.0", + "0.0" + ], + [ + "15", + "YER045C", + "ACA1", + "YLR014C", + "PPR1", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "525.5364", + "558.23195", + "-0.0720257307211", + "0.0530460147321", + "0.120093662406", + "0.0", + "0.0", + "0.0" + ], + [ + "16", + "YER045C", + "ACA1", + "YBR276C", + "PPS1", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "2419.3925", + "2788.6315", + "0.0122846283333", + "0.0492935299569", + "0.0963579749912", + "0.0", + "0.0", + "0.0" + ], + [ + "17", + "YER045C", + "ACA1", + "YGR123C", + "PPT1", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "1271.969", + "756.6555", + "-0.225361085921", + "0.0143144232829", + "0.143078282691", + "0.0", + "0.0", + "0.0" + ], + [ + "18", + "YER045C", + "ACA1", + "YPL148C", + "PPT2", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "219.84405", + "182.46345", + "0.0613811644696", + "0.0627513730773", + "0.156174422974", + "0.0627513730773", + "0.0627513730772", + "0.0" + ], + [ + "19", + "YER045C", + "ACA1", + "YHR201C", + "PPX1", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "3845.679", + "3410.8325", + "-0.154114687167", + "-0.117511741551", + "0.0967597414614", + "-0.0", + "0.0", + "0.0" + ], + [ + "20", + "YER045C", + "ACA1", + "YML016C", + "PPZ1", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "1342.195", + "1373.8975", + "-0.0362655421877", + "-0.0738714297714", + "0.101152319324", + "-0.0", + "0.0", + "0.0" + ], + [ + "21", + "YER045C", + "ACA1", + "YDR436W", + "PPZ2", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "1525.9705", + "1543.802", + "0.218383737172", + "0.0563081385542", + "0.113157259395", + "0.0", + "0.0", + "0.0" + ], + [ + "22", + "YER045C", + "ACA1", + "YEL060C", + "PRB1", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "5911.781", + "5569.027", + "-0.455707646615", + "-0.571860070431", + "0.207328157539", + "-0.0", + "0.0", + "0.0" + ], + [ + "23", + "YER045C", + "ACA1", + "YMR297W", + "PRC1", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "36126.48", + "39761.95", + "0.410772124663", + "0.131407081485", + "0.141859292096", + "0.131407081485", + "0.131407081485", + "0.01127754540884046" + ], + [ + "24", + "YER045C", + "ACA1", + "YCL057W", + "PRD1", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "5299.106", + "5666.927", + "0.217251799868", + "0.0958561117769", + "0.118813194578", + "0.0", + "0.0", + "0.0" + ], + [ + "25", + "YER045C", + "ACA1", + "YER012W", + "PRE1", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "3579.9625", + "3507.407", + "0.257330221212", + "0.212146176027", + "0.117287337021", + "0.0", + "0.0", + "0.0" + ], + [ + "26", + "YER045C", + "ACA1", + "YOR362C", + "PRE10", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "7441.532", + "9475.323", + "0.122188272669", + "0.0718660920117", + "0.103600609006", + "0.0", + "0.0", + "0.0" + ], + [ + "27", + "YER045C", + "ACA1", + "YPR103W", + "PRE2", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "7716.9675", + "10321.0485", + "0.0640417232106", + "0.0126514481209", + "0.0994120132179", + "0.0", + "0.0", + "0.0" + ], + [ + "28", + "YER045C", + "ACA1", + "YJL001W", + "PRE3", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "8069.2035", + "8403.125", + "-0.0893897513213", + "-0.138440536033", + "0.097284953069", + "-0.0", + "0.0", + "0.0" + ], + [ + "29", + "YER045C", + "ACA1", + "YFR050C", + "PRE4", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "6965.228", + "8014.186", + "0.130369797315", + "0.0550630647991", + "0.101602878222", + "0.0", + "0.0", + "0.0" + ], + [ + "30", + "YER045C", + "ACA1", + "YMR314W", + "PRE5", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "9141.1395", + "10320.41", + "0.137077950724", + "0.122101501008", + "0.0963541759471", + "0.0", + "0.0", + "0.0" + ], + [ + "31", + "YER045C", + "ACA1", + "YOL038W", + "PRE6", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "8436.736", + "10381.487", + "0.107476341446", + "0.0669936382585", + "0.105301291198", + "0.0", + "0.0", + "0.0" + ], + [ + "32", + "YER045C", + "ACA1", + "YBL041W", + "PRE7", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "2173.121", + "1985.315", + "-0.0822820646082", + "-0.0939006623829", + "0.105080316319", + "-0.0", + "0.0", + "0.0" + ], + [ + "33", + "YER045C", + "ACA1", + "YML092C", + "PRE8", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "7177.268", + "6808.069", + "-0.257981612196", + "-0.309732696759", + "0.0980797505299", + "-0.0", + "0.0", + "0.0" + ], + [ + "34", + "YER045C", + "ACA1", + "YGR135W", + "PRE9", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "12547.15", + "15473.24", + "0.0691947071468", + "0.0589345565803", + "0.10341285939", + "0.0", + "0.0", + "0.0" + ], + [ + "35", + "YER045C", + "ACA1", + "YIR008C", + "PRI1", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "1434.682", + "1462.7145", + "-0.131154719562", + "-0.124372895682", + "0.0957816864817", + "-0.0", + "0.0", + "0.0" + ], + [ + "36", + "YER045C", + "ACA1", + "YKL045W", + "PRI2", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "2744.2565", + "2751.2355", + "-0.0877778595728", + "0.0103854720443", + "0.109576289525", + "0.0103854720443", + "0.0103854720443", + "0.0" + ], + [ + "37", + "YER045C", + "ACA1", + "YIL095W", + "PRK1", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "175.32645", + "213.4852", + "-0.306520099792", + "-0.359214715414", + "0.147309738357", + "-0.0", + "0.0", + "0.0" + ], + [ + "38", + "YER045C", + "ACA1", + "YNL279W", + "PRM1", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "151.35995", + "60.88811", + "-0.163721814997", + "-0.155100365723", + "0.184526335745", + "-0.0", + "0.0", + "0.0" + ], + [ + "39", + "YER045C", + "ACA1", + "YJL108C", + "PRM10", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "434.85715", + "487.97225", + "0.197827906609", + "0.270562592171", + "0.155702667029", + "0.270562592171", + "0.270562592171", + "0.14228978173358325" + ], + [ + "40", + "YER045C", + "ACA1", + "YMR278W", + "PRM15", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "831.627", + "1182.736", + "-0.0430126710172", + "-0.14258375941", + "0.134093156636", + "-0.0", + "0.0", + "0.0" + ], + [ + "41", + "YER045C", + "ACA1", + "YIL037C", + "PRM2", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "211.582", + "225.26115", + "0.0187691123028", + "-0.028108956191", + "0.127184486471", + "-0.0", + "0.0", + "0.0" + ], + [ + "42", + "YER045C", + "ACA1", + "YPL192C", + "PRM3", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "10.9251915", + "5.660958", + "-0.591203647306", + "-0.509557179708", + "0.446843040608", + "-0.0", + "0.0", + "0.0" + ], + [ + "43", + "YER045C", + "ACA1", + "YPL156C", + "PRM4", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "817.6399", + "1094.5992", + "-0.114741081918", + "-0.0275536173095", + "0.141289574881", + "-0.0275536173095", + "-0.0275536173095", + "-0.0" + ], + [ + "44", + "YER045C", + "ACA1", + "YIL117C", + "PRM5", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "2402.5815", + "3609.9235", + "0.0528869881727", + "0.0523652440494", + "0.123605948584", + "0.0", + "0.0", + "0.0" + ], + [ + "45", + "YER045C", + "ACA1", + "YML047C", + "PRM6", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "10.5794195", + "2.8140655", + "-0.694608414725", + "-0.553824321969", + "0.643974825346", + "-0.0", + "0.0", + "0.0" + ], + [ + "46", + "YER045C", + "ACA1", + "YDL039C", + "PRM7", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "863.883", + "698.3094", + "-0.117281767333", + "0.0781061133085", + "0.177734867942", + "0.0781061133085", + "0.0781061133085", + "0.0" + ], + [ + "47", + "YER045C", + "ACA1", + "YGL053W", + "PRM8", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "823.95815", + "960.38885", + "0.389527968106", + "0.29532151425", + "0.127558963187", + "0.29532151425", + "0.29532151425", + "0.23594077664534263" + ], + [ + "48", + "YER045C", + "ACA1", + "YAR031W", + "PRM9", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "13.66105", + "16.78304", + "0.197818562658", + "0.201583564502", + "0.397577394418", + "0.0", + "0.0", + "0.0" + ], + [ + "49", + "YER045C", + "ACA1", + "YDR300C", + "PRO1", + "15.0", + "ZEV", + "P", + "20160229", + "SMY2049", + "803.2018", + "742.33955", + "-0.0789609053406", + "0.0695793804634", + "0.113501268363", + "0.0695793804634", + "0.0695793804634", + "0.0" + ] + ], + "shape": { + "columns": 17, + "rows": 891408 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
regulator_locus_tagregulator_symboltarget_locus_tagtarget_symboltimemechanismrestrictiondatestraingreen_medianred_medianlog2_ratiolog2_cleaned_ratiolog2_noise_modellog2_cleaned_ratio_zth2dlog2_selected_timecourseslog2_shrunken_timecourses
0YER045CACA1YNL055CPOR115.0ZEVP20160229SMY204991090.29500124164.7500000.4065270.0432780.1673080.0000000.0000000.00000
1YER045CACA1YIL114CPOR215.0ZEVP20160229SMY2049929.49180662.373950-0.084262-0.0977700.129415-0.0000000.0000000.00000
2YER045CACA1YPL188WPOS515.0ZEVP20160229SMY2049591.31345734.788500-0.075808-0.0508570.107884-0.0000000.0000000.00000
3YER045CACA1YIL160CPOT115.0ZEVP20160229SMY2049205.71685121.7190000.5847630.1705940.2156460.1705940.1705940.00463
4YER045CACA1YGL205WPOX115.0ZEVP20160229SMY2049119.5065087.9464050.1790570.0200290.1982050.0000000.0000000.00000
......................................................
891403YFL052WZNF1YGL087CMMS215.0ZEVP20161117SMY2245923.69900662.4565000.028204-0.0045660.117553-0.0000000.0000000.00000
891404YFL052WZNF1YKL175WZRT315.0ZEVP20161117SMY22455181.167503997.746500-0.136347-0.0541080.096181-0.0000000.0000000.00000
891405YFL052WZNF1YBR046CZTA115.0ZEVP20161117SMY2245466.54105581.0441000.3423840.0141900.1395310.0000000.0000000.00000
891406YFL052WZNF1YGR285CZUO115.0ZEVP20161117SMY224516498.7350017306.235000-0.143440-0.0572460.090285-0.0000000.0000000.00000
891407YFL052WZNF1YNL241CZWF115.0ZEVP20161117SMY22454058.685004520.9095000.013953-0.0964790.159377-0.0000000.0000000.00000
\n", + "

891408 rows × 17 columns

\n", + "
" + ], + "text/plain": [ + " regulator_locus_tag regulator_symbol target_locus_tag target_symbol \\\n", + "0 YER045C ACA1 YNL055C POR1 \n", + "1 YER045C ACA1 YIL114C POR2 \n", + "2 YER045C ACA1 YPL188W POS5 \n", + "3 YER045C ACA1 YIL160C POT1 \n", + "4 YER045C ACA1 YGL205W POX1 \n", + "... ... ... ... ... \n", + "891403 YFL052W ZNF1 YGL087C MMS2 \n", + "891404 YFL052W ZNF1 YKL175W ZRT3 \n", + "891405 YFL052W ZNF1 YBR046C ZTA1 \n", + "891406 YFL052W ZNF1 YGR285C ZUO1 \n", + "891407 YFL052W ZNF1 YNL241C ZWF1 \n", + "\n", + " time mechanism restriction date strain green_median \\\n", + "0 15.0 ZEV P 20160229 SMY2049 91090.29500 \n", + "1 15.0 ZEV P 20160229 SMY2049 929.49180 \n", + "2 15.0 ZEV P 20160229 SMY2049 591.31345 \n", + "3 15.0 ZEV P 20160229 SMY2049 205.71685 \n", + "4 15.0 ZEV P 20160229 SMY2049 119.50650 \n", + "... ... ... ... ... ... ... \n", + "891403 15.0 ZEV P 20161117 SMY2245 923.69900 \n", + "891404 15.0 ZEV P 20161117 SMY2245 5181.16750 \n", + "891405 15.0 ZEV P 20161117 SMY2245 466.54105 \n", + "891406 15.0 ZEV P 20161117 SMY2245 16498.73500 \n", + "891407 15.0 ZEV P 20161117 SMY2245 4058.68500 \n", + "\n", + " red_median log2_ratio log2_cleaned_ratio log2_noise_model \\\n", + "0 124164.750000 0.406527 0.043278 0.167308 \n", + "1 662.373950 -0.084262 -0.097770 0.129415 \n", + "2 734.788500 -0.075808 -0.050857 0.107884 \n", + "3 121.719000 0.584763 0.170594 0.215646 \n", + "4 87.946405 0.179057 0.020029 0.198205 \n", + "... ... ... ... ... \n", + "891403 662.456500 0.028204 -0.004566 0.117553 \n", + "891404 3997.746500 -0.136347 -0.054108 0.096181 \n", + "891405 581.044100 0.342384 0.014190 0.139531 \n", + "891406 17306.235000 -0.143440 -0.057246 0.090285 \n", + "891407 4520.909500 0.013953 -0.096479 0.159377 \n", + "\n", + " log2_cleaned_ratio_zth2d log2_selected_timecourses \\\n", + "0 0.000000 0.000000 \n", + "1 -0.000000 0.000000 \n", + "2 -0.000000 0.000000 \n", + "3 0.170594 0.170594 \n", + "4 0.000000 0.000000 \n", + "... ... ... \n", + "891403 -0.000000 0.000000 \n", + "891404 -0.000000 0.000000 \n", + "891405 0.000000 0.000000 \n", + "891406 -0.000000 0.000000 \n", + "891407 -0.000000 0.000000 \n", + "\n", + " log2_shrunken_timecourses \n", + "0 0.00000 \n", + "1 0.00000 \n", + "2 0.00000 \n", + "3 0.00463 \n", + "4 0.00000 \n", + "... ... \n", + "891403 0.00000 \n", + "891404 0.00000 \n", + "891405 0.00000 \n", + "891406 0.00000 \n", + "891407 0.00000 \n", + "\n", + "[891408 rows x 17 columns]" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hackett_final" + ] + }, + { + "cell_type": "markdown", + "id": "81a946c6", + "metadata": {}, + "source": [ + "## Individual regulator correlation analysis and scatter plots\n", + "\n", + "Now we'll analyze correlations for each regulator individually, focusing on the relationship between Hackett effects and Kemmeren effects/p-values.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "75cc1bb9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'regulator_symbol': 'ACA1', 'n_targets': 6075, 'eff_eff_corr': np.float64(0.01608402414610723), 'eff_eff_pval': np.float64(0.21004245167836072), 'eff_pval_corr': np.float64(-0.017860635301765383), 'eff_pval_pval': np.float64(0.1639456246498038)}\n", + "Correlation analysis function finished!\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "from scipy.stats import spearmanr\n", + "\n", + "# Set up plotting parameters\n", + "plt.rcParams['figure.figsize'] = (12, 5)\n", + "plt.rcParams['font.size'] = 10\n", + "\n", + "# Example regulator symbol\n", + "chosen_regulator_symbol = 'ACA1'\n", + " \n", + "# Filter data for the specific regulator\n", + "hackett_reg = hackett_final[hackett_final['regulator_symbol'] == chosen_regulator_symbol].copy()\n", + "kemmeren_reg = kemmeren_final[kemmeren_final['regulator_symbol'] == chosen_regulator_symbol].copy()\n", + " \n", + "# Check if data was found\n", + "if len(hackett_reg) == 0 or len(kemmeren_reg) == 0:\n", + " print(f\"No data found for regulator {chosen_regulator_symbol}\")\n", + " \n", + "# Merge datasets on target_symbol\n", + "merged = pd.merge(\n", + " hackett_reg[['target_symbol', 'log2_shrunken_timecourses']].rename(columns={'log2_shrunken_timecourses': 'effect_hackett'}),\n", + " kemmeren_reg[['target_symbol', 'Madj', 'pval']].rename(columns={'Madj': 'effect_kemmeren', 'pval': 'pvalue_kemmeren'}),\n", + " on='target_symbol',\n", + " how='inner'\n", + ")\n", + " \n", + "if len(merged) < 3: # Need at least 3 points for correlation\n", + " print(f\"Insufficient data points ({len(merged)}) for regulator {chosen_regulator_symbol}\")\n", + "\n", + " \n", + "# Calculate correlations\n", + "# Remove any NaN values\n", + "clean_data = merged.dropna(subset=['effect_hackett', 'effect_kemmeren', 'pvalue_kemmeren'])\n", + " \n", + "if len(clean_data) < 3:\n", + " print(f\"Insufficient clean data points ({len(clean_data)}) for regulator {chosen_regulator_symbol}\")\n", + " \n", + "# Spearman correlations\n", + "eff_eff_corr, eff_eff_pval = spearmanr(clean_data['effect_hackett'], clean_data['effect_kemmeren'])\n", + "eff_pval_corr, eff_pval_pval = spearmanr(clean_data['effect_hackett'], clean_data['pvalue_kemmeren'])\n", + " \n", + "correlation_results = {\n", + " 'regulator_symbol': chosen_regulator_symbol,\n", + " 'n_targets': len(clean_data),\n", + " 'eff_eff_corr': eff_eff_corr,\n", + " 'eff_eff_pval': eff_eff_pval,\n", + " 'eff_pval_corr': eff_pval_corr,\n", + " 'eff_pval_pval': eff_pval_pval\n", + "}\n", + " \n", + "print(correlation_results)\n", + "print(\"Correlation analysis function finished!\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "b9324fd2", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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KEJDQ4Wnp6do3ry5WLt2rdi5c6fNA9/c3Fzx7rvvigYNGpjaN3XqVHH37l2rxwshxPz580WDBg2Eu7u71YFrXFyc6Nu3rwgMDBQeHh6iYcOGYvr06Rb9FaLsBr56vV5ERESUOBgVQoht27YJAKJnz55m2w0Gg1i/fr14/PHHRXh4uPDw8BDVqlUTTZs2FS+++KI4ceKEEEKIrVu3lthuI+MslLVr15q2RUZGmmagWPuKjY0t8bxEREREReHY1RzHrhy7EpF9FCGEKHkRBSIieuCBB7Bv3z6kpqaa7vJLREREROSMOHYlInJtGkc3gIjI2SQlJVlsW7NmDfbu3YsePXpw0EtEREREToNjVyKiyokzbYmICqlevTpatWqFe++9F1qtFkeOHMGuXbvg5+eHvXv3olmzZo5uIhERERERAI5diYgqKxZtiYgK+ec//4nvv/8ely5dQkZGBkJCQtCtWzdMnz4dMTExjm4eEREREZEJx65ERJUTi7ZEREREREREREREToRr2hIRERERERERERE5ERZtiYiIiIiIiIiIiJwIi7ZEVGX997//RevWreHn5wdFUfDSSy/ZtI/sk5eXh5kzZ6JBgwbw9PSEoijYuHFjifuIiIiIqhKOTSunCxcuQFEUjBkzxtFNISIXw6ItEbk840CouK969eqZPWbfvn0YPnw40tLS8Nxzz2HGjBno06dPifvKy65du6AoCmbOnFmuz1Naq1atKjHrwgPSBQsWYNasWQgPD8e0adMwY8YM000xittXXmbOnAlFUbBr165yfR4iIiKqmjg2rTjWxqbe3t6IiYnBP/7xDyQnJzu6iURE0twc3QAiorJSv359jBgxwuq+wMBAs+83b94MIQS++OIL3HfffTbvI9VDDz2EBx54wOq+li1bmn3/ww8/wNfXF9u2bYOHh4fN+4iIiIhcGcemFafg2PTmzZvYunUr/v3vf+Pbb7/Fn3/+ierVqzu4hURE9mPRlogqjXvuucfm2QBXr14FAISHh9u1j1Q9evTA66+/btOxV69eRfXq1a0WZYvbR0REROTKODatOIXHpnl5eejduzd27tyJJUuWOP2MYSIia7g8AhFVKcaPesXGxgIAoqKiTB+lMn68ytq+CxcumM6RkJCAp59+GnXr1oWnpyfCwsIwZswYXLx40epzxsfHY8KECYiKioKnpydq1qyJrl27YtWqVQDUj+t369YNADBr1iyzj3cVfN7CVq9eDUVRMHv2bKv7Dx06BEVRMHz4cNO2s2fPYuzYsaa2BAcHo0WLFnjppZcghLA1RpsYlyFISEjAxYsXzT4OWNy+guLi4vDoo4+iRo0a8PT0RIMGDfDWW28hMzPT6nPGxcXh8ccfR2hoKDw9PVGnTh0MHDgQv/76KwCga9eumDVrFgCgW7duRT4vERERUUXg2LR8xqbu7u6YOHEiAODAgQPFHjt+/HgoioK4uDir+xcuXAhFUfCf//zHtG3lypXo378/6tWrBy8vLwQHB5uKxLaqV69ekWPQrl27QlEUi+1CCKxcuRL3338//P39Ua1aNbRt2xYrV660+XmJyHVwpi0RVSn16tXDjBkzsHHjRhw9ehRTpkwxfTytZcuWRe4z/vn777+jd+/eyMjIwCOPPIIGDRrgwoULWLt2LX788Ufs27cP0dHRpuf79ddf0a9fP9y9exe9e/fGE088gTt37uDw4cNYvHgxxowZg65du+LChQv4/PPP0aVLF3Tt2tX0+MIfnSto4MCBeO6557B27Vq8/fbbFvtXr14NABg5ciQAdZZG+/btkZGRgX79+mHYsGHIyMjA2bNnsWzZMnzwwQdwcyu7fxaM/Vi0aBEAmG6YERgYaFpCwdo+o48//hiTJk1CYGAgHn30UdSsWRMHDx7Eu+++i507d2Lnzp1mM3QXL16Ml19+Gd7e3hgwYADq1q2LxMRE/Prrr1i/fj0eeOAB03q7u3fvxujRo00D5eJyJiIiIiovHJuW/9jUWvGzoJEjR2LlypVYs2YNOnfubLXdnp6eGDJkiGnbpEmT0KJFC/To0QMhISFITEzExo0b0aNHD3z77bfo379/qdtdmBACw4cPx3//+180aNAATz31FDw8PLBt2zaMHz8eJ06cwAcffFDmz0tEDiSIiFxcQkKCACDq168vZsyYYfXrxx9/NHvM6NGjBQCRkJBgcb6i9uXm5op69eoJPz8/cejQIbN9e/bsEVqtVjzyyCOmbdnZ2aJ27dpCo9FYPL8QQly+fNn09507dwoAYsaMGXb1fcSIEQKA+P33382263Q6ERoaKmrVqiV0Op0QQogPP/xQABCLFi2yOM+tW7dser7Y2FgBQDz00ENFZn3y5Emzx0RGRorIyEir5ytq399//y3c3NxEixYtRHJystm+uXPnCgDigw8+MG07cuSI0Gg0Ijw83OJ1MxgMIjEx0fT9jBkzBACxc+dOm/pMREREZA+OTSt+bDp37lyz7Xl5eaJ79+4CgJg1a1ax5zAYDKJu3boiKChIZGdnm+3766+/BAAxePBgs+3x8fEW57l69aoIDw8XDRo0MNtuvB5Gjx5ttr24MXKXLl1E4XLNJ598IgCIsWPHitzcXNP2nJwc8eijjwoA4uDBg8X2lYhcC2faElGlcf78edNH3wubMmVKqe+y+8MPP+DChQuYPXs2WrVqZbbvgQceQP/+/bFx40akpaXB398fmzZtQmJiIkaNGmX1uSMiIkrVHkCdGbBmzRqsWbMG7du3N23/+eefcf36dbz00kvQarVmj/H29rY4T3BwsF3Pu2PHDuzYscPqvpYtWyImJsau8xW2YsUK6HQ6LFmyxOLGEa+++ioWLlyI//73v5g6darpeIPBgHfeecfiY2aKonANOCIiIqpwHJtW3Nh0+/btyM7OBgDcunULW7duxdmzZxEVFYUXXnih2Mcal2yYO3cuNm/ejIEDB5r2GWcHF76hXFRUlMV5wsLCMGjQICxZsgQXL15EZGSkXX0oydKlS+Hj44OPPvoI7u7upu0eHh5499138f333+O///0v2rRpU6bPS0SOw6ItEVUavXv3xk8//VRu59+/fz8A4PTp01ZvZnDt2jUYDAacOXMGbdu2xR9//AEA6NWrV7m16aGHHkJYWBi++uorLFy40PQRsjVr1gDI//gZADz66KN44403MGnSJOzYsQN9+vRBly5dzD4yZ6u5c+fafCMyGcast27darU47O7ujlOnTpm+r4isiYiIiOzBsWnFjU0LTijw9PREvXr18I9//ANvvPGGqQBsLaOXXnoJgYGBGDlyJObOnYvVq1ebirYGgwFffvklqlevjocfftjscfHx8Zg7dy5++eUXJCYmIicnx2z/1atXy7Rom5mZib/++gvh4eF47733LPbn5eUBgNn4mIhcH4u2REQ2un37NgBg7dq1xR6XkZEBAEhNTQUA1K5du9zapNVq8dRTT2HBggXYunUr+vXrh/T0dGzcuBH33nsvWrdubTq2Xr162L9/P2bOnIktW7bgm2++AQDExMRg9uzZZut0OZox63fffdem41NTU6EoCsLCwsqzWUREREROg2PTfLZMKLA263nMmDEIDAxE48aN0aZNG2zZsgV37txBUFAQdu3ahStXruD55583m9l67tw5tG/fHmlpaejWrRseffRR+Pv7Q6PRYNeuXdi9e7dFEbe07ty5AyEEEhMTi5y9DeS/1kRUOWgc3QAiIlfh7+8PAPj+++8hhCjyq0uXLgDyb9SQmJhYru0yzlgwzmDYsGEDMjMzzWYyGDVt2hTr16/H7du3sW/fPrz99tu4du0ahg0bhr1795ZrO+1hzDotLa3YrI0CAwMhhEBSUpKjmkxERERUoTg2tY+1bAouqzVy5Ejk5uaaiseFb5xm9O9//xt37tzBqlWrsG3bNixatAizZ8/GzJkz7VoiTKPRQKfTWd1nLLAbGV/rNm3aFPta79y50+bnJyLnx6ItEZGNOnToAADYt2+fTccb1/H6+eefSzzWuLaXXq+3u10tWrRAs2bNsGnTJty9exdr1qwxrc1VFHd3d3Ts2BGzZs3Chx9+CCEEfvjhB7ufu7wYszZ+7K8kFZU1ERERkbPg2LRsPfnkk3Bzc8OaNWuQlZWFb7/9Fvfccw86duxodtz58+cBAP379zfbLoSwq9AcFBSEGzduWBRuMzIycPbsWbNtfn5+aNy4MU6ePImUlBQ7ekVEroxFWyIiG/Xv3x9169bFwoULERcXZ7E/Ly8Pv/76q+n7xx57DBEREVizZg22bt1qcXzBWQ7GtbYuX74s1baRI0ciKysLH374IX755Rd06dIFderUMTvmzz//RFpamsVjr1+/DgDw8vKSeu7y8Pzzz8PNzQ2TJ0/GpUuXLPanpKTg8OHDpu+fffZZaLVavPXWW7h48aLZsUIIXL161fR9abMmIiIicgYcm5atmjVrolevXti7dy8WLVqEtLQ0ixuQATCtVVswWwCYN28ejh8/bvPztWvXDnl5eWbLWwgh8MYbb1hd5uDFF19EZmYmnnnmGav7ExIScOHCBZufn4icH9e0JaJK49y5c1ZvMGD0+uuvl2rw5+npifXr16Nv377o0qULunfvjmbNmkFRFFy8eBF79uxB9erVTTcA8PT0xDfffIM+ffqgb9++6NOnD1q0aIG0tDQcOXIEmZmZpsJjTEwMwsPD8dVXX8HT0xMRERFQFAWTJ09GQEBAiW176qmn8Prrr2PWrFkwGAxWP362evVqrFixAp07d0b9+vXh7++PEydOYMuWLQgODsbYsWNtzqLgHXoLq1WrFp599lmbz2VN06ZNsWzZMjz33HNo1KgRHn74YdSvXx93795FfHw8du/ejTFjxmD58uUAgGbNmmHRokV48cUX0aRJEzz++OOIjIzEtWvXEBcXh379+mHRokUAgG7dukFRFLz55pv4+++/ERAQgMDAwBLvLExERERkD45NK25sWlZGjhyJLVu2YMaMGQBgtWj77LPPIjY2FoMGDcLQoUNRvXp17N+/H4cOHUK/fv2wefNmm57rhRdeQGxsLJ5++mls27YNISEh2LNnD1JSUtCiRQscPXrU7PiJEydi//79+Pzzz7F371706NED4eHhuH79Ok6dOoXff/8dX375pdmSD0Tk4gQRkYtLSEgQAEr8unPnjukxo0ePFgBEQkKCxfmK2yeEEFeuXBFTpkwRDRo0EJ6ensLf3180btxYPP3002LHjh0Wx587d06MHz9eRERECHd3d1GzZk3RtWtX8cUXX5gdt3//ftGlSxfh5+dnanNRbbCmR48eAoDw8vISqampFvv3798vJk6cKJo2bSoCAwOFt7e3aNCggXjhhRfExYsXbXqO2NjYEnNu0aKF2WMiIyNFZGSk1fMVt08IIf744w/xxBNPiPDwcOHu7i5q1KghWrduLV5//XVx8uRJi+N37twpHnnkEREcHCw8PDxERESEGDRokNi7d6/ZcatWrRLNmjUTnp6eAkCxbSAiIiKyB8emqoocm86dO9fmdhUnMzNT+Pv7CwCiU6dORR63c+dOcf/99ws/Pz8RGBgoHn74YfHnn3+KGTNmCABi586dpmON18Po0aMtzvPLL7+IDh06CE9PT1G9enUxcuRIcf36ddGlSxdRVLnm66+/Fj169BBBQUHC3d1d1K5dW3Tt2lUsWLBA3Lx5s7QREJETUYQocCcXIiIiIiIiIiIiInIormlLRERERERERERE5ERYtCUiIiIiIiIiIiJyIizaEhERERERERERETkRFm2JiIiIiIiIiIiInAiLtkREREREREREREROhEVbIiIiIiIiIiIiIifi5ugGOJLBYMDVq1fh5+cHRVEc3RwiIiIikiCEwN27dxEeHg6NpnLOSeC4lYiIiKhysHXsWqWLtlevXkWdOnUc3QwiIiIiKgOXL19GRESEo5tRLjhuJSIiIqpcShq7VumirZ+fHwA1JH9//xKPNxgMSEpKQlhYWKWd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WvbWr19vWgt28ODBAIChQ4eiYcOGePvtt0v8tNacOXOQkZGBP//8E3Xr1gUAtG/fHj179sSqVaswYcIE07EJCQmoV68ekpOTi33PLF26FGFhYfjll19My4tMnDgRMTExWLVqFV5++eXSdrtc8We5PGYnj9nJY3bymJ1tONPWwc6fP48mTZpYDHoBoGbNmmbfK4qCF154AWvXrkWjRo3g5eWFNm3aIC4uzuKxiYmJGDduHEJDQ+Hp6YkmTZpg5cqVZsfk5ubi7bffRps2bRAQEAAfHx88+OCD2Llzp9lxFy5cgKIo+OCDD7Bo0SLUr18fnp6eOHHiBGbOnAlFUXDmzBmMGDECAQEBCAkJwfTp0yGEwOXLl9G/f38EBASgfv36Fje0kGnDJ598YmpDu3btcODAAZvz/uWXX9C4cWN4eHggICAAY8aMQUpKis2PLwvr16+HVqs1G4h6eXlh/Pjx2LdvX4kzW9evX4927dqZCraAerfGhx56CN98843ZsZGRkTZ95Gv58uXQ6/WYPXs2AHU2bxW+RyEREVGZ++qrr3D16lXk5OTgypUr+Oqrr1C/wJpproJj16o3di3K+vXrERoaioEFPkoaEhKCIUOGYPPmzWY30rNmw4YNeOSRR0wFWwDo0aMHGjZsaDGmrVevnk1tSktLQ1BQkNl60G5ubqhRowa8vb1tOgcREZGzYDnbwSIjI7Fv3z4cP34cTZs2LfH43bt34+uvv8aLL74IT09PLFu2DH369MEff/xhevz169fRsWNH00A5JCQEP/74I8aPH4+0tDS89NJLANRBzaeffoonn3wSzzzzDO7evYvPPvsMvXv3xh9//IGWLVuaPXdsbCyys7MxYcIEeHp6Ijg42LRv2LBhaNy4MebNm4fNmzfjnXfeQXBwMFasWIHu3btj3rx5WL16NV555RW0b98enTt3lmrDl19+ibt372LixIlQFAXz58/HwIEDER8fD3d392Kz++WXX9CrVy/cd999eP7553H69Gl8/PHHSEhIwK5du4otbubk5ODu3bslvj4AUKNGjWL3Hz58GA0bNrS4Q2D79u0BAEeOHEGdOnWsPtZgMODYsWMYN26cxb727dvj559/xt27d+Hn52dTW422b9+OmJgYbNmyBa+88goSExMRFBSESZMmYdasWbyJHhEREQHg2LUqjl2LcvjwYbRu3dpi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HVnD66J0OP6Sx+zk8D0rj9nJY3bymJ08ZmcbFm3JZMeOHRg3bhwaNmyIatWqITo6Gk8//TSSkpJsevzp06fx8ssv47777oOXlxcURcGFCxeKPP5///sfWrduDS8vL9StWxczZsyATqezOC4lJQUTJkxASEgIfHx80K1bNxw6dEi2m8X67bff8MADD6BatWqoVasWXnzxRaSnp5sdk56ejhkzZqBPnz4IDg6GoihYtWpVubSHiIiIiKxLSkrC66+/jm7dusHPzw+KomDXrl12n+frr79Gp06d4OPjg8DAQNx333345ZdfTPuzsrIwfvx4NG3aFAEBAfD19UWLFi2wePFi5OXlWZxv27ZtpvFkUFAQBg8eXOyYuDQ4diUiIqq8WLQlk9deew27du3CgAED8OGHH+KJJ57AN998g1atWuHatWslPn7fvn348MMPcffu3RLXJvnxxx/x+OOPIzAwEEuWLMHjjz+Od955B5MnTzY7zmAwoF+/fvjyyy/xwgsvYP78+bhx4wa6du2Ks2fPlqq/hR05cgQPPfQQMjMzsXDhQjz99NP45JNPMGTIELPjkpOTMXv2bJw8eRItWrQo0zYQERERkW1Onz6N9957D4mJiWjWrJnUOWbOnIknn3wSderUwcKFC/HOO++gefPmSExMNB2TlZWFv//+Gw8//DDmzp2LDz74AC1atMDLL7+M0aNHm53vhx9+QJ8+fZCTk4N58+Zh6tSp2L17Nx544AHcvHmzVP0tjGNXIiKiyo03IrODRqNBSEgINJrKWeteuHAhHnjgAbP+9enTB126dMHSpUvxzjvvFPv4xx57DCkpKfDz88MHH3yAI0eOmO13c8u/3KZNm4bmzZvj559/Nm339/fHnDlzMGXKFMTExAAA1q9fj99++w3r1q3D4MGDAQBDhw5Fw4YNMWPGDHz55Zdl0XUAwJtvvomgoCDs2rUL/v7+AIB69erhmWeewc8//4xevXoBAMLCwpCUlIRatWrh4MGDaNeuXZm1oSgFsyPbVfb3bHlidvKYnTxmJ4/ZkTWV/bpo06YNbt26heDgYKxfv96iWFmS/fv3Y/bs2ViwYAFefvlls31CCOTk5AAAgoODsX//frP9zz77LAICArB06VIsXLgQtWrVAqBOgoiOjsbevXvh4eEBAHj00UfRunVrzJs3DwsWLJDtrgWOXSufyv6e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+/jss8+Qlpbm9Tbedtqmp6fj2LFjTXPsbDdJfX09CgsLkZubC4fDwV0/PszJMAwUFhZi4sSJcDgcYTEnt259nd55B+Yrr2DTtGnI/fRTOK65BrjsstCeUw/vtHV/zUZHR4fFnDx1907bwsJC5OTktLiwUSjPqadep4aGhhY/K8JhTj2509b9s8LdSinU59TR2P05J/e5q3vdhcOcvB1vb041NTW2zl1FRdsVK1bg2WefxRdffIE+ffr4eveg4esJvmVZqK2tRWxsrE/bmYnZdQWzk2N2csxOjtnJMTu53p5dV4u2H3/8MS677DKMGjUKa9as8dqy4M4778T//u//4rvf/S7eeust8TnwokWL8M4772DDhg22d1kAsjn29nXRFcxOziouRm1JCWJTU6FlZgZ6OCGF606O2ckxOzlmJ9fbs+vWnrY33HADDMPAGWecgSuuuAJpaWkt/jcLUJXjX/ziF5KHD2pRUVGBHkLIYnZyzE6O2ckxOzlmJ8fs5Jid3IUXXoi///3vuOyyy3DRRRfho48+aircWpaF66+/Hi+//DKuuOIK/PGPf0RkZKTPz2FZFhYvXoy33noL69at86lg2xVcF3LMTigjA1FDhwKtfj8ke7ju5JidHLOTY3ZyzK5zuuRO27Ztw7Jly1BSUoJnnnkGd911F+644442b+HGMAwUFBS02e5MnWN2csxOjtnJMTs5ZifH7OSYXddNnz4dq1evxu7duzF9+nQcP34cjY2NmDt3Ll5++WX85Cc/wRtvvCEq2ALAzTffjD/84Q/405/+hP79++PIkSM4cuRIi/5p/sZ1Icfs5JidHLOTY3ZyzE6O2ckxO3tEO21/+tOfoqKiAitWrMDkyZNbXLSBiIiIiCgUTZ06Fe+99x5mzpyJadOmISUlBR9//DGWLFmCX/7yl1167BdeeAEAMG3atBbH8/PzMX/+/C49NhERERGFH1HRdvPmzXjkkUdw/fXX+3s8REREREQBc9555+GDDz7ApZdeiq+++grLli3zy1+QCS4jQURERES9mKho21M9uIiIiIiIult2dnabY5GRkYiKisIrr7yCV155pcXnNE3Dli1bemp4RERERNQLaZbgv/3ffPNN3HHHHfj000+Rnp7eHePqEb5ehdeyLBiGAYfD0SuvbtcVzE6O2ckxOzlmJ8fs5JidXG/PztdzutamTZvmc25r1671+Xm6QjLH3r4uuoLZyTE7OWYnx+zkmJ0cs5Pr7dnZPa8T7bTdsGEDEhISMHLkSFx00UVIT0+Ho9XVQTVNw9NPPy15+KDW0NCA2NjYQA8jJDE7OWYnx+zkmJ0cs5NjdnLMTm7dunWBHkK34bqQY3ZyzE6O2ckxOzlmJ8fs5Jhd53TJnZ599lls3boVdXV1ePfdd/HCCy/g2WefbfMWbgzDwNatW3l1OwFmJ8fs5JidHLOTY3ZyzE6O2ZE3XBdyzE6O2ckxOzlmJ8fs5JidHLOzR1S0NU2z0zcGT0RERETB7sCBAwG5LxERERFRR0RFWyIiIiKicDB8+HAsXLgQ//73v23f5/PPP8d1112HM844oxtHRkRERES9mainrSfTNFFRUQFv1zNLTEzs6sMHnda9e8k+ZifH7OSYnRyzk2N2csxOjtnJfPrpp7j//vsxZcoUDBs2DNOnT0dubi4yMjIwYMAAWJaFEydOwOl0oqCgAJ988gkOHTqECy64ABs2bAj08DvFdSHH7OSYnRyzk2N2csxOjtnJMbvOaZa3amsnGhsb8atf/Qovv/wyDhw4ANM0vd4u2FskdPVKw0REREQUeP44p9u8eTPy8/PxzjvvYP/+/QDQdDVj9+lyeno6Lr/8cixcuBDjx4/3y9jt4nkrERERUXiwe14n2ml7ww034P/+7/8wZcoUzJo1C/Hx8eKBhhLLslBRUYH4+Pimk3iyh9nJMTs5ZifH7OSYnRyzk2N2XTd+/Hg8/fTTePrpp3H48GHs2LEDx44dAwCcdtppGDVqFAYPHhzgUfqG60KO2ckxOzlmJ8fs5JidHLOTY3b2iIq2f/nLX3Dttddi5cqVfh5OcDMMAzt27EBeXh4iIrrcWaJXYXZyzE6O2ckxOzlmJ8fs5Jidfw0ePDjkCrTecF3IMTs5ZifH7OSYnRyzk2N2cszOHlEyffr0wZQpU/w9FiIiIiKigKupqcHhw4dRW1uL2NhYDB48GH379g30sIiIiIioF9Eld7r66qvx7rvv+nssREREREQBceLECdx///0YMWIE4uLiMGrUKOTk5GDUqFGIi4vDGWecgfvuu6+pZQIRERERUXcS7bRdtmwZFi5ciO9///tYuHAh0tPTvV71LTc3t8sDDCaapiE2Npb9NgSYnRyzk2N2csxOjtnJMTs5Ztc1TqcT06ZNw+HDh3HhhRfiqquuQmpqKmJiYlBXV4eSkhJ88cUXWLZsGV599VWsW7cOmZmZgR52p7gu5JidHLOTY3ZyzE6O2ckxOzlmZ49muS+H64OqqirccMMNeOONN7x+3rIsaJoGwzC6PMDuxKvwEhEREYW+rp7TzZo1C0VFRfjwww8xcuTIdm+3c+dOXHzxxcjJycHbb7/dhRH7juetREREROHB7nmdaKftwoUL8dZbb+Gqq67C5MmTER8fLx5oKDFNE+Xl5Rg4cCB0XdRZotdidnLMTo7ZyTE7OWYnx+zkmF3XrF27Fo8++miHBVsAGDlyJH7xi1/goYce6qGRdQ3XhRyzk2N2csxOjtnJMTs5ZifH7OwRFW3/8Y9/YPHixXjyySf9PZ6gZpomiouLkZiYyEXlI2Ynx+zkmJ0cs5NjdnLMTo7ZdY2u63C5XLZu63K5QiZjrgs5ZifH7OSYnRyzk2N2csxOjtnZI0omLi4Ow4cP9/dYiIiIiIh63He/+10sX74chYWFHd6usLAQy5cvx8UXX9xDIyMiIiKi3kq00/b666/Ha6+9hhtvvNHrBch6wmOPPYbVq1dj8+bNiIqKwsmTJwMyDiIiIiIKbU899RSmTZuGiRMnYuLEicjLy0Nqaiqio6NRX1+PkpISFBQUYNOmTcjMzOx1f21GRERERD1PVLQdM2YM3nnnHeTm5mLevHlIT0/3WrydPXt2lwfYnoaGBsydOxdnn302XnrppW57Hk+apiE+Pp5XtxNgdnLMTo7ZyTE7OWYnx+zkmF3XDB48GEVFRXjuueewatUqvPTSS6ivr2/6fHR0NM466ywsXboUP/vZz9CvX78AjtY+rgs5ZifH7OSYnRyzk2N2csxOjtnZo1mWZfl6Jzv9JjRNg2EYokH5YuXKlbj11ltFO215FV4iIiKi0OfvczrLsnD8+HHU1tYiNjYWiYmJAf+lguetREREROHB7nmdaKft2rVrxQMLpPr6+ha7JiorKwGoC0q4Lz6h6zp0XYdpmjBNs+m27kL1wYMHMWjQoKaP3bc3DAOe9W+HwwFN09pc1MK9I7l1Qbu94xEREbAsq8VxTdPgcDjajLG94x3NydvYu2NOpmniyJEjGDRoEKKiosJiTp6683UCgCNHjiAlJaXFL4yhPKeeep0aGxub1p2u62Exp556nUzTRGlpKYYMGQLLssJiTh2N3Z9zMk0Thw4davGzItTn5O14d8zJ/bNi8ODBiIiICIs5dTZ2f81J0zQcPnwYKSkpLf5zPZTn5Mvr5O+NApqm4bTTTvPrYwaCaZo4fPgwBg8ezIt8+IjZyTE7OWYnx+zkmJ0cs5NjdvaIirZTp0719zh6xNKlS/HII4+0OV5UVIS+ffsCAJKSkpCVlQWn04mysrKm26SlpWHQoEHYtWsXDh061FQ8y8zMRHJyMrZt24ba2tqm248aNQoJCQkoKipq8YtEdnY2oqKiUFBQ0GIMeXl5aGhowNatW5uOORwOTJw4ERUVFdixY0fT8djYWIwbNw7l5eUoLi5uOh4fH4/Ro0fj8OHDOHjwYNPxjuaUlpaGXbt2oaKioul4d8zJsiycPHkSR44cwaRJk8JiTj31OqWmpqKkpAQVFRWoqqoKizn11Ou0ZcsWnDx5EocOHUJERERYzKmnXifLslBXV4fU1FTs3r07LOYE9MzrVFtbi6+++qrpZ0U4zKmnXif3zwrLsjBs2LCwmFNPvU45OTnYv38/Dh482HSOEupz8uV16om/7nI7cOAAnE4nzj///B57TinTNNtsOCB7mJ0cs5NjdnLMTo7ZyTE7OWZnj6g9glt9fT0KCwtx9OhRnHvuuRg4cGCXBnP33XfjV7/6VYe32b59O0aNGtX0sS/tEbzttE1PT8exY8eatiN3tJvENE1s2rQJubm5Tbs/uOvH3pwMw0BhYSFyc3MRHR0dFnPy1J2vk2maKCwsRE5OTove0aE8p556nRoaGprWncPhCIs59dTr5P6anThxIjRNC4s5dTR2f87J5XKhoKCgxc+KUJ+Tt+PdMSf3upswYQKioqLCYk6djd1fc7Isq826C/U5+fI6VVVVITExsUdaBzz22GN48MEHe7RQDMjaI7i/H+Xl5SEiQrRXo9didnLMTo7ZyTE7OWYnx+zkent23doeAQB+85vf4OGHH27aWbFmzRpMnz4d5eXlGDVqFJYtW4aFCxf69Ji333475s+f3+FtMjMzpUNGdHQ0oqOj2xyPiIhos0jcv3B4Mk2z6Rea1rf3/AWp9WN39bimaV6Pexuj5Hh7Y/f3nNzZuf8dDnPy1F1zcv9y623duY93deztHQ/118ldIPDMLtTn1JOvk+duPW9CcU5u3TknTdO8/qwI5Tm1d7w75qRpWtO/w2VOnrprTi6Xq91zlFCdU0fHW8+pveciIiIiIgpVoqJtfn4+br31Vlx11VW4+OKLWxRnBw4ciOnTp+P111/3uWiblJSEpKQkyZB6hK7rSEpK8voLC3WM2ckxOzlmJ8fs5JidHLOTY3Zd8+ijj9q+7fr167txJP7FdSHH7OSYnRyzk2N2csxOjtnJMTt7RO0Rxo4dizPOOANvvfUWjh07hqSkJHz00UeYPn06AOBXv/oVfvOb3+DQoUN+H7Db/v37cfz4cfztb3/DE088gU8//RQAMHz4cPTr18/WY/AqvEREREShr6vndLqut2kF0xFN00KiPQJRQGzYAOzaBYwYAYRA72ciIqKeZve8TlTS3rNnDy699NJ2P5+YmIhjx45JHtq2Bx98EDk5OXjooYdQXV2NnJwc5OTktLmIhT+Zpom9e/e26OdG9jA7OWYnx+zkmJ0cs5NjdnLMrmuSk5NxySWXoKysrNO3u+++O9DDtY3rQo7ZCd15J8yrrsLeDz6AedVVwJ13BnpEIYXrTo7ZyTE7OWYnx+zsERVtExISUF5e3u7nv/76awwaNEg8KDtWrlwJy7LavE2bNq3bntM0TZSVlXFRCTA7OWYnx+zkmJ0cs5NjdnLMrmsmT56MLVu24LTTTuv0rW/fvoEerm1cF3LMTmDDBuCVV2DqOsomTICp68Arr6jjZAvXnRyzk2N2csxOjtnZIyrafu9738OLL76IkydPtvncV199hd/97ne47LLLujo2IiIiIqJuN2nSJJSUlGD//v2d3nbYsGE4n3/yTdTWrl1AXR2QkABoGtC3L1BdDfzzn4EeGRERUUgSFW3/53/+B4ZhYOzYsbj//vuhaRr+7//+D9dccw3y8vKQnJyMBx980N9jJSIiIiLyu/vuuw+maWLo0KGd3vaaa67B2rVre2BURCFmxAggJgY4eRJobAQOH1bv//Y3YNWqQI+OiIgo5IiKtoMHD8aXX36JGTNm4I033oBlWXj11Vfx97//HVdffTU2btyIxMREf4814HRdR1paGq9uJ8Ds5JidHLOTY3ZyzE6O2ckxO/KG60KO2Qmcfz5w3XXQGxuR9skn0F0uYORIIDERyM8HnM5AjzDocd3JMTs5ZifH7OSYnT2aZfMyuR9++CEuvvhir59z96FISkqCruuor6/H//t//w/vvPOOXwfrb7wKLxEREVHo6w3ndL1hjhQmXngBePJJtfN2yBDAMFTrhGXLgClTAj06IiKigLN7Xhdh9wFnzZqFt99+22vhNikpqenf1dXV+MEPfoANYdhw3jAM7Nq1CyNGjIDD4Qj0cEIKs5NjdnLMTo7ZyTE7OWYnx+z8Z+HChR1+XtM0xMTEIC0tDdOmTcPZZ5/dQyPzHdeFHLOTMy6+GLuOHMGIoiI4DAMoKQHi4oCUlEAPLehx3ckxOzlmJ8fs5JidPbaLtmPHjsWsWbPw17/+FTNmzPB6m2PHjmHGjBn48ssvsXz5cr8NMlhYloWKigrY3JxMHpidHLOTY3ZyzE6O2ckxOzlm5z+ffPIJamtrUVZWBgAYMGAAAODEiRMA1GYF0zRx7NgxaJqGSy65BG+++Sb69OkTsDG3h+tCjtnJWcOGoWL8eFibN6sdtnFxwMKFQEZGoIcW9Lju5JidHLOTY3ZyzM4e280jPvroI5x11ln44Q9/iPfff7/N5w8dOoTzzjsPmzdvxssvv4zbbrvNrwMlIiIiIupu77//PqKjo/Hwww/j2LFjTW/l5eV46KGHEBsbi3/+8584ceIEHnjgAXzwwQd44IEHAj1souCSmgr8+teqJcIzzwCzZwd6RERERCHHdtE2Li4OH330EcaPH4/Zs2dj9erVTZ/bvXs3zj33XDidTvzlL3/B/Pnzu2OsRERERETdatGiRfje976HBx98sGmXLQAkJibioYcewowZM7Bo0SLEx8fj4YcfxlVXXYU333wzgCMmClLDhqkettxhS0REJOLTZdr69++PNWvWIDc3F3PmzMG7776LzZs34zvf+Q6OHz+O1atXY9asWd001MDTdR2ZmZm8up0As5NjdnLMTo7ZyTE7OWYnx+z8Z+PGjRg3bly7nx83bhw+//zzpo/PO+88lJaW9sTQfMZ1Icfs5JidHLOTY3ZyzE6O2ckxO3t8Tqdfv3748MMPMXHiRFxxxRWYNm0aTNPEJ598gunTp3fHGIOGrutITk7mohJgdnLMTo7ZyTE7OWYnx+zkmJ3/JCQk4MMPP2z38x988AHi4+ObPq6uru7wqr+BxHUhx+zkmJ0cs5NjdnLMTo7ZyTE7e2ynU1hY2PS2c+dOPPbYY0hNTUVjYyOefPJJ6Lre4jaFhYXdOe6AMAwDW7ZsgWEYgR5KyGF2csxOjtnJMTs5ZifH7OSYnf9cf/31eOedd3DFFVfg448/xr59+7Bv3z58/PHHuOKKK/Duu+/i+uuvb7r9e++9h/HjxwduwB3gupBjdnLMTo7ZyTE7OWYnx+zkmJ09EXZvmJeXB03TWhxzX+Vt3rx5bY5rmhZ24VuWhdraWl7dToDZyTE7OWYnx+zkmJ0cs5Njdv7z0EMPoba2Fk8++STeeuutFp9zOBy47bbb8NBDDwEA6urqMH/+fGRnZwdiqJ3iupBjdnLMTo7ZyTE7OWYnx+zkmJ09tou2+fn53TkOIiIiIqKA0zQNv/rVr3D77bfjo48+wv79+wEAw4YNw4UXXojk5OSm28bExLTZvEBERERE5A+2i7Y8ISUiIiKi3iI5ORk/+tGPAj0MIiIiIuqlbBdtSf1J3KhRo+BwOAI9lJDD7OSYnRyzk2N2csxOjtnJMTv/W79+PVavXo19+/YBUDttZ86cialTpwZ4ZPZxXcgxOzlmJ8fs5JidHLOTY3ZyzM4ezerFDSQqKysRHx+PioqKoL3qLxERERF1zJ/ndA0NDbj66qvx9ttvw7IsJCQkAABOnjwJTdPwwx/+EK+99hoiIyP9MHL7eN5KREREFB7sntfpPTimkOdyubBp0ya4XK5ADyXkMDs5ZifH7OSYnRyzk2N2cszOfx555BG89dZbuP3221FSUoLjx4/j+PHjOHLkCO644w789a9/xaOPPhroYdrCdSHH7ORc69dj0xtvwLV+faCHEnK47uSYnRyzk2N2cszOHhZtfWQYRqCHELKYnRyzk2N2csxOjtnJMTs5Zucff/rTnzBv3jwsW7YMKSkpTceTk5Pxq1/9Ctdddx1effXVAI7QN1wXcsxO4M47gWuugbF9O3DNNepj8gnXnRyzk2N2csxOjtl1jkVbIiIiIqJvlZSUYPLkye1+fvLkyThy5EgPjogoRGzYALzyCmBZQHS0ev/KK+o4ERER+YxFWyIiIiKib6WlpWHdunXtfn79+vVIS0vruQERhYpdu4C6OiAhAdA09b6uTh0nIiIin7Fo6wOHw4Hs7Gxe3U6A2ckxOzlmJ8fs5JidHLOTY3b+M2/ePPz5z3/GjTfeiJ07d8IwDJimiZ07d+Kmm27CX/7yF8yfPz/Qw7SF60KO2QmMGAHExMBRVobst96Co6wMiIlRx8kWrjs5ZifH7OSYnRyzsyci0AMINVFRUYEeQshidnLMTo7ZyTE7OWYnx+zkmJ1/3Hvvvdi7dy9efPFF/O53v4Ouqz0OpmnCsizMmzcP9957b4BHaR/XhRyz89H55wPXXQe8/DKidu9Wu21//GN1nGzjupNjdnLMTo7ZyTG7znGnrQ8Mw0BBQQGbJQswOzlmJ8fs5JidHLOTY3ZyzM5/HA4HVq5cic2bN+Oxxx7DT37yE/zkJz/BY489hs2bNyM/P7+pkBvsuC7kmJ3QlCkwxoxBwZ13whgzBuigPzS1xXUnx+zkmJ0cs5NjdvZwpy0RERERUSvZ2dnIzs4O9DCIQofTCeTnAwMGAImJ6n1+PpCbC2RkBHp0REREISc0tgkQERERERFR8CotBSorVVuEmhr1vrJSHSciIiKfcactEREREfVauq5D0zSf7qNpGlwuVzeNiChEpaQAhw4BhYVAdjbw8cfqWEpKoEdGREQUkjTLsqxADyJQKisrER8fj4qKCsTFxXV6e8uyYBgGHA6Hzyf3vR2zk2N2csxOjtnJMTs5ZifX27Pz9ZzO08MPPyzK7KGHHvL5Pl0hmWNvXxddwewENmwAfvADWHV1MGJi4KirgxYTA/z977wYmU1cd3LMTo7ZyTE7ud6end3zOu609VFDQwNiY2MDPYyQxOzkmJ0cs5NjdnLMTo7ZyTE7mYcffjjQQ+hWXBdyzM5Hu3ap9ykpaOjXD7HV1UBFhToeyKKt06laNKSkhERvXa47OWYnx+zkmJ0cs+sce9r6wDAMbN26lVe3E2B2csxOjtnJMTs5ZifH7OSYXfexLAv79+9HQ0NDoIfiM64LOWYnMGIEAMA4cQJbr7gCxokTLY4HxKpVwOLFwJIl6v2qVYEbiw1cd3LMTo7ZyTE7OWZnD4u2RERERETtOHr0KDIyMvDZZ58FeihEwS09HRg4ELCs5reBA9XxQHA6gfx8NY4RI9T7/Hx1nIiIKASwaEtERERE1IFefAkIIvtKS4EhQ4ALLwQGDFDvhwxRxwM1nspKIDUVcDjU+8rKwI2HiIjIRyza+sjhcAR6CCGL2ckxOzlmJ8fs5JidHLOTY3bdJ5QvkMF1IcfsfJSSAsTFAZYFR2ys2tkaF6eOB3I8JSWAYaj3gRyPTVx3csxOjtnJMTs5Ztc5zerFWwe6cqVhIiIiIgoO3XlOV1paitTUVHz00UeYPn26Xx/bFzxvpZCwapVqQVBZqQqkCxcCs2dzPERERB7sntdF9OCYQp5lWaioqEB8fHxI77gIBGYnx+zkmJ0cs5NjdnLMTo7ZdZ/ExESsXbsW48ePD/RQfMZ1IcfshObMgZWTg4pDhxA/ZAi0zMyAjwe5uaolQkoKkJER2PF0gutOjtnJMTs5ZifH7OxhewQfGIaBHTt28Op2AsxOjtnJMTs5ZifH7OSYnRyz6z6RkZGYOnUq4uPjAz0Un3FdyDE7OWPoUOyIjIQxdGigh6JkZABTpgR9wRbguusKZifH7OSYnRyzs4dFWyIiIiIiIiIiIqIgwvYIRERERERE5B+PPQYkJABvvAFceWVItCUgIiIKRtxp6wNN0xAbG8t+GwLMTo7ZyTE7OWYnx+zkmJ0csyNvuC7kmJ1QVha0xx5D7Pbt0J57Dpg6FVi8WF0QzJPTCWzcqN5TE647OWYnx+zkmJ0cs7NHsyzLCvQgAoVX4SUiIiIKfb3hnK43zJFC3MMPA4880vZ4VhYwahTwzDNqx+2qVUB+PlBZCcTFAQsWqAuGERER9RJ2z+u409YHpmni6NGjME0z0EMJOcxOjtnJMTs5ZifH7OSYnRyzI2+4LuSYncC6dQAAU9dxdPx4mPq3v2rW1KgCbWmp2lmbnw9YFjBihHqfn88dt9/iupNjdnLMTo7ZyTE7e1i09YFpmiguLuaiEmB2csxOjtnJMTs5ZifH7OSYHXnDdSHH7ASGDwcAmBERKJ45E2bEt5dPcTjUjtqUFFW4rawEUlPV8dTU5oIucd11AbOTY3ZyzE6O2dnDoi0RERERERF1zX33ATExbY+PGAEsXKhaI6SkqAJuSQlgGOq9u6BLRERELbBoS0RERERERF2TkQH84Q9q96yuA8nJwNKlwEsvAbNnN99mwQJA04Bdu9R7d0GXiIiIWogI9ABCiaZpiI+P59XtBJidHLOTY3ZyzE6O2ckxOzlmR95wXcgxO6GNG6HV1iJ+3z5oDQ3AsWNtC7Jz5gC5uaolQkoKC7YeuO7kmJ0cs5NjdnLMzh7Nsiwr0IMIFF6Fl4iIiCj09YZzut4wRwpxGzYAc+eqi4sNGACcOKF20v7lL8D55wd6dEREREHD7nkd2yP4wDRNHDx4kI2SBZidHLOTY3ZyzE6O2ckxOzlmR95wXcgxO4Fdu4C6Oph9+uDgyJEw+/QB6urUcbKF606O2ckxOzlmJ8fs7GHR1gdcVHLMTo7ZyTE7OWYnx+zkmJ0csyNvuC7kmJ3AiBGAywXzyBEczM6GeeQI4HKp42QL150cs5NjdnLMTo7Z2cOiLREREREREXVd61+++cs4ERGRGIu2RERERETdbMOGDfjBD36AwYMHQ9M0vP3224EeEpF/7doFREUBgwcD0dHqfVQU2yMQEREJsWjrA13XkZSUBF1nbL5idnLMTo7ZyTE7OWYnx+zkmF3wq6mpwbhx4/Dcc8/12HNyXcgxO4ERI4CYGOi1tUhyOqHX1gIxMWyP4AOuOzlmJ8fs5JidHLOzR7Msywr0IAKFV+ElIiIiCn2hdk6naRreeustzJo1y/Z9Qm2O1EvdeSfwyivqAmQxMcC8ecCyZYEeFRERUVCxe14X0YNjCnmmacLpdCIjI4P/G+AjZifH7OSYnRyzk2N2csxOjtmFn/r6etTX1zd9XFlZCQBwuVxwuVwA1C4VXddhmmaLC3m4jzc2NuKbb77BsGHDmo7pug7DMOC5b8PhcEDTtKbH9TwOAIZh2DoeEREBy7JaHNc0DQ6Ho80Y2zve2Zxaj7275mSaJvbt24esrCxomhYWc3Lr1tfpV78Chg7F3hMnMCwxEfqNNwIuV2jPqQdfJ/e6GzZsGKKiosJiTp6683UCgH379mHo0KHQNC0s5tRTr1NjY2PTutN1PSzm1FOvk2ma2L9/PzIzM2FZVljMqaOx+3NOpmmiuLi4ad2Fw5y8HW9vTnaxaOsD0zRRVlbWYlGRPcxOjtnJMTs5ZifH7OSYnRyzCz9Lly7FI4880uZ4UVER+vbtCwBISkpCVlYWnE4nysrKmm6TlpaGtLQ07Nq1C/v370d5eTk0TUNmZiaSk5Oxbds21NbWNt1+1KhRSEhIQFFRUYtfKrKzsxEVFYWCgoIWY8jLy0NDQwO2bt3adMzhcGDixImoqKjAjh07mo7HxsZi3LhxKC8vR3FxcdPx+Ph4jB49GocPH8bBgwebjtuZU0VFRdPx7pqTZVmoqKhARkYGqqurw2JOQA+8Ths2YNAzz2Dfffepdffqq8Do0aE9px58nSzLwsmTJ3HixAlMmjQpLObUU69TamoqysrKUFdXh6qqqrCYU0+9Tlu2bMHJkydRXl6OiIiIsJhTT71OlmWhrq4Op59+Onbv3h0WcwJ65nWqra3Fnj17ms5RwmFOvrxOGRkZsIPtEXz4MzOXy4WCggLk5eUhIoL1bl8wOzlmJ8fs5JidHLOTY3ZyvT27UGsdYKc9gredtunp6Th27FjTHDvbTVJfX4/CwkLk5ubC4XBw148PczIMA4WFhZg4cSIcDkdYzMmt216nf/4T+tVXw6yqwqZf/AK5zz0HR58+wB/+AP2880JzTujZ18m97nJzcxEdHR0Wc/LUna+TaZooLCxETk5O0/OH+px66nVqaGho8bMiHObUU6+T588KTdPCYk4djd2fc3Kfu7rXXTjMydvx9uZUU1PD9ghERERERKEoOjoa0dHRbY5HRES0Kcy7f+Fozf0Lh/uXcM/j3rRX8PfluKZpXo+3N0Zfj7c39u6Yk/vPF8NpTm7dMqfdu4GSEphRUdBME47qakQcP66OT50amnPy0FOvk/tr1v3vcJiTp+6ak7uw0vr7nVsozsmtu18nbz8rQn1OPfk6uX9WhNOc3LpzTpqmeT1HCeU5tXe8vTnZwb+f84Gu60hLS/P6wlPHmJ0cs5NjdnLMTo7ZyTE7OWZH3nBdyDE7gTffBADohoG0Tz+F7t5V9O1x6hzXnRyzk2N2csxOjtnZw/YIIfSndERERETUViic01VXV2PPnj0AgJycHPz617/GBRdcgMTERAwdOrTT+4fCHKmXGzsW+OqrtsfPPBPYtq3nx0NERBSk7J7XsaTtA8MwsH379jY9KqhzzE6O2ckxOzlmJ8fs5JidHLMLfgUFBcjJyUFOTg4A4LbbbkNOTg4efPDBbntOrgs5ZieQlwcAMCIjsf3qq2FERrY4Tp3jupNjdnLMTo7ZyTE7e9jT1gfuq8j24s3JYsxOjtnJMTs5ZifH7OSYnRyzC37Tpk3r8deH60KO2QmsXAm89RasujpUZGbC0jQgPl4dJ1u47uSYnRyzk2N2cszOHu60JSIiIiIioq6rqACuuQbo31+9P3ky0CMiIiIKWSzaEhERERERkX+sWAFMnareExERkRiLtj7QdR2ZmZm8up0As5NjdnLMTo7ZyTE7OWYnx+zIG64LOWYnp+/fj8z6euj79wd6KCGH606O2ckxOzlmJ8fs7NGsXtxAglfhJSIiIgp9veGcrjfMkcLAqlVAfj5QWQnExQELFgBz5gR6VEREREHF7nkdS9o+MAwDW7Zs4dXtBJidHLOTY3ZyzE6O2ckxOzlmR95wXcgxOwGnE8jPh6Hr2DJnDgxdVwVcpzPQIwsZXHdyzE6O2ckxOzlmZw+Ltj6wLAu1tbW8up0As5NjdnLMTo7ZyTE7OWYnx+zIG64LOWYnUFoKVFbCSk1FbWwsrNRUteO2tDTQIwsZXHdyzE6O2ckxOzlmZw+LtkRERERERNQ1KSmqJUJJCWBZ6n1cnDpOREREPmPRloiIiIiIiLomI0P1sNU0oKZGvV+4UB0nIiIin7Fo6wOHw4FRo0bB4XAEeighh9nJMTs5ZifH7OSYnRyzk2N25A3XhRyzE5ozB47TT8eoDRvgOP10YPbsQI8opHDdyTE7OWYnx+zkmJ09mtWLG0jwKrxEREREoa83nNP1hjlSGMjKAoqLmz/OzAT27g3ceIiIiIKQ3fM67rT1gcvlwqZNm+ByuQI9lJDD7OSYnRyzk2N2csxOjtnJMTvyhutCjtkJPPwwUFwMV1QUNt15J1xRUaqA+/DDgR5ZyOC6k2N2csxOjtnJMTt7WLT1kWEYgR5CyGJ2csxOjtnJMTs5ZifH7OSYHXnDdSHH7Hz0xRdN/zSiorwep85x3ckxOzlmJ8fs5Jhd51i0JSIiIiIioq6ZPNm340RERNQhFm2JiIiIiIioax5+WPWw9ZSVFZztEZxOYONG9Z6IiChI8UJkPlzQwbIs1NbWIjY2Fpqm9cAIwwezk2N2csxOjtnJMTs5ZifX27PrDRfpksyxt6+LrmB2XjidQGkpkJICZGR4v42mwdI01J52GmKPHYNmWUCw/bq5ahWQnw9UVgJxccCCBcCcOYEeFQCuu65gdnLMTo7ZyfX27Hghsm4S5dmfiXzC7OSYnRyzk2N2csxOjtnJMTvyhutCjtl5WLUKWLwYWLJEvV+1qu1tJk1S7y0LUZWVzcVa9/Ge0NkOWqdTFWwtCxgxQr3Pzw+qHbdcd3LMTo7ZyTE7OWbXORZtfWAYBgoKCtgsWYDZyTE7OWYnx+zkmJ0cs5NjduQN14Ucs/Ngt9BZUABAXYSs4M47my9G9u3xbmensFxaqnbYpqYCDod6X1mpjgcBrjs5ZifH7OSYnRyzs4dFWyIiIiIiIvLObqGzvTYIPdEewW5hOSVFtUQoKQEMQ72Pi1PHiYiIggyLtkRERERERORdKBQ67RaWMzJUD1tNA3btUu8XLmy/Ry8REVEARQR6AERERERERBSk3IXO/HxV6IyL817ozMwEiovb3j89vfvH6FlYTk3tuLA8Zw6Qm9vyomp2LrJGRETUwzTLCrbLefYcX6/Ca1kWDMOAw+HolVe36wpmJ8fs5JidHLOTY3ZyzE6ut2fn6zldKJLMsbevi65gdl50Vti88ELgk09gQfW1dTQ0QAOAxETgxRdVsbQ7rVqlCsuVlc2F5dmzfb/fggXdP9Z2cN3JMTs5ZifH7OR6e3Z2z+vYHsFHDQ0NgR5CyGJ2csxOjtnJMTs5ZifH7OSYHXnDdSHH7FrJyACmTGl/J+reveq9pqEhLk61HgCAmhrv/WX9bc4c4JlngGXL1Hs7BVu7vXB7ENedHLOTY3ZyzE6O2XWORVsfGIaBrVu38up2AsxOjtnJMTs5ZifH7OSYnRyzI2+4LuR6VXZOJ7BxY9cLlcOHAwCMyEhsveEGGJGR6nhiovf+st2hs8Jya3Z74faQXrXu/IzZyTE7OWYnx+zsYU9bIiIiIiKi3sifrQHOPBP4+OO2x6Oigu/CZW6+9MIlIiLqYdxpS0RERERE1Nv4uzXA3//u/XhpqfcLlwUD90XWNE1dZE3TgnesRETU63CnrY8cDkeghxCymJ0cs5NjdnLMTo7ZyTE7OWZH3nBdyIVMdp1dIKw97tYAI0Y0twbYtUsd9+Vx3M9fXd10yOHZp7B/f3v9ZQNlzhwgN1eWYTcImXUXhJidHLOTY3ZyzK5zmmVZVqAHESi94UrDREREROGuN5zT9YY5kkBX2hs4ncDixWqHrbs1gKapi3jZLVx6Pv/27UB5edvbXHIJ8MEH9udEREQU5uye17E9gg8sy8LJkyfRi+vcYsxOjtnJMTs5ZifH7OSYnRyzI2+4LuRCIruutjfoamuA1s/frx8AwNJ1nMzMhKV/+6vm0KH+udBZLxAS6y5IMTs5ZifH7OSYnT0s2vrAMAzs2LGDV7cTYHZyzE6O2ckxOzlmJ8fs5JgdecN1IRcS2bnbG6SmNrc3qKxUx+2aM0ftrF22TL33pY1B6+f/tj2CERGBHVdfDSPi2058f/kLsGSJ2tW7apUPE+x9QmLdBSlmJ8fs5JidHLOzhz1tiYiIiIiIQk1KimqJUFLS3N4gLk4d90VGhqyPa+vnP3XK++0aGtRO3JIStTM3N1cdD5IeskRERMGKO22JiIiIiIhCTVfbG/j7+fv08X67mJiWO4Gffx645hrg5z/n7lsiIqIOcKetDzRNQ2xsLDRNC/RQQg6zk2N2csxOjtnJMTs5ZifH7Mgbrgu5kMluzhy1czVQu1Y9n/+JJ4C//hWaZSG2vByau09hTQ1w8qRqn3DihNpta5qqB67L1bz7ljtuQ2fdBSFmJ8fs5JidHLOzR7N6cddfXoWXiIiIKPT1hnO63jBHCnF9+7bfImH8eGDAAODQIaCsDEhKAmpr1ecGDQJ+8xtgypQeGyoREVEg2T2vY3sEH5imiaNHj8I0zUAPJeQwOzlmJ8fs5JidHLOTY3ZyzI684bqQY3YenE5g40b1vqPjdXUAAFPXcXT8eJi6x6+aDzwALFqkWiVERgJVVUBEhNqBaxi+9+ENU1x3csxOjtnJMTs5ZmdPSBZtv/nmG/z4xz9GRkYGYmNjkZWVhYceeggNDQ3d+rymaaK4uJiLSoDZyTE7OWYnx+zkmJ0cs5NjduQN14Ucs/vWqlWq7+ySJS37z3o7PmAAAMCMiEDxzJkwI77txNe3LzB7NnDkiHqrqVG7bfftU7tt6+uBwsIATTC4cN3JMTs5ZifH7OSYnT0h2dN2x44dME0TK1aswPDhw7Ft2zZcf/31qKmpwfLlywM9PCIiIiIiotDmdKp+s5YFjBgBlJSoj5OSvB8/dsz749TUqMd67z1gyBBVuC0rA3QdOOssdYEy9rUlIiJqIySLtjNmzMCMGTOaPs7MzMTOnTvxwgsvsGhLRERERETUVaWlQGWlKsw6HKq4umuXevN23M5j5eaqHbZffqnuO3QoMHCgun9pKYu2REREHkKyPYI3FRUVSExM7Nbn0DQN8fHxvLqdALOTY3ZyzE6O2ckxOzlmJ8fsyBuuC7lel523vrUpKUBcnNpJaxjqfVycKtZ6O/4tzbIQX1wMzfN61w0N6rZ79wKJiaqfraYB0dHN92df29637vyI2ckxOzlmJ8fs7NEsy/OnaWjas2cPJkyYgOXLl+P6669v93b19fWor69v+riyshLp6ek4duxY09XadF2HruswTbNFbw33ccMw4BlZe8cdDgc0TYPL5WoxBofDAQAwDMPW8YiICFiW1eK4pmlwOBxtxtjecc6Jc+KcOCfOiXPinDincJ5TVVUVEhMTO70Cbyize5VhIp+tWqXaE1RWquLpggXAnDneP7dwoepP63k8IgK4+GJ1sbFWX98AVBuESy8FiotVC4XERKBPH/W5vn1bPq7TqXbcpqRw1y0REYUtu+d1QdUe4e6778avfvWrDm+zfft2jBo1qunjQ4cOYcaMGZg7d26HBVsAWLp0KR555JE2x4uKitC3b18AQFJSErKysuB0OlFWVtZ0m7S0NAwePBhffvlli19YMjMzkZycjG3btqG2trbp+KhRo5CQkICioqIWv1RkZ2cjKioKBQUFLcaQl5eHhoYGbN26temYw+HAxIkTUVFRgR07djQdj42Nxbhx41BeXo7i4uKm4/Hx8Rg9ejQOHz6MgwcPNh3vaE5paWnYtWsXKioqun1OdXV16NOnDyZNmhQ2cwK6/3UaPHgwdF1HZWUlKisrw2JOPfk61dXVISYmJqzm1FOvU58+fTB27NiwmlNPvE51dXUoKChATExM2MypJ1+nuro6ZGZmYujQoWEzJ6D7X6fc3FwcPHgQR48eDZs5+fI6tS7skmKaJg4fPtx0LkH29Zrs2utb6+4vO2eO+nfrQqr7+OuvA2vWqH613xZsTYcDh889F4P/+U/ohgGYpnr8c85RO20bGoClS4H09JaP21HxuJfoNeuuGzA7OWYnx+zkmJ09QbXTtqysDMfaa2D/rczMTERFRQEADh8+jGnTpmHKlClYuXJlpy90V3famqaJTZs2ITc3t2n3B3fI2JuTYRgoLCxEbm4uoqOjw2JOnrrzdTJNE4WFhcjJyWl6/lCfU0+9Tg0NDU3rzuFwhMWceup1cn/NTpw4EZqmhcWcOhq7P+fkcrlQUFDQ4mdFqM/J2/HumJN73U2YMAFRUVFhMafOxu6vOVmW1WbdhfqcuNO2JclOW/f3o7y8PEREBNVejaDXa7LbuBFYsqS5P61hqP6yy5YBU6a0fz+nEygqAp59FoiNVX1tX3oJAOCKikLBnXci74knENHQoG7///4fkJDQ/uM7ncDixaq4m5qqiseaBjzzTK/acdtr1l03YHZyzE6O2cn19uxCcqdtUlISkpKSbN320KFDuOCCCzBhwgTk5+fbqsxHR0cjOjq6zfGIiIg2i8T9C4cn0zSbfqFpfXvPX5BaP3ZXj2ua5vW4tzFKjrc3dn/PyZ2d+9/hMCdP3TUn9y+33tad+3hXx97e8VB/ndwFAs/sQn1OPfk6ufsLhdOc3LpzTpqmef1ZEcpzau94d8xJ07Smf4fLnDx115xcLle75yihOqeOjreeU3vPRUQevLUe8Oxb6y6WdtZf1r0j9uBB4MABICcHqKnp+Lm//hoYMwaorvb++O4LlaWmAuXlQL9+aiz+vjgZ2y8QEVEICaqirV2HDh3CtGnTMGzYMCxfvrzFn+oNGjQogCMjIiIiIiIKMu21HsjIUP/Oz1c7YN39ZdsraHq2Uxg5Ejh8GPj3v1WRtSMHDqjbnn46cN99bR8/JUUVfteuVT1wTVO1T/DnxcnYfoGIiEJMSBZt16xZgz179mDPnj1IS0tr8bnu7Pag6zqSkpJs7eqllpidHLOTY3ZyzE6O2ckxOzlmR95wXciFVXbSvrXeuHfEutspDB8ObNrU4ia6aSJp82boHu1UkJcHHD8OxMernbm+jr+0VPXCjYqS7ZLtLIMgEVbrrocxOzlmJ8fs5JidPUHV07an8Sq8RERERKGvN5zT9YY5UjeR9q31pnXv2a+/Vm95ecDHH7d/v0GDgMhItSP35ZfbPq97jKmpQH09EB2tCquXXgr8859AcTFw7BiQmAhkZfm+S9afGRAREXWR3fM6lrR9YJom9u7d2+IiHGQPs5NjdnLMTo7ZyTE7OWYnx+zIG7+sC6dTFbycTv8NLASE1deUZ99aw7DXt7Y97nYKmqaKnjExqojqcd0QMyICe2fOhOnZjzouDqirA44eVRcwa72e3GOsrgYGDlTvIyKANWtU24TqalUorqlRb/n5vq1Jf2bgKx++hsJq3fUwZifH7OSYnRyzs4dFWx+YpomysjIuKgFmJ8fs5JidHLOTY3ZyzE6O2ZE3XV4Xq1apXZVLlqj3q1b5d4BBLKy+ploXWjWt4761nZkzB3jmGbVL9aWXgHvvVY/5LVPXUTZ+PEzPP3mtrAQaG4FTp9R9W68n9xhra4Evv1TvL74YcLmA/v3VfQcMUO/791ePV1oauAzs8vFrKKzWXQ9jdnLMTo7ZyTE7e0Kypy0REREREXWjEOkBSjb50rdW+tirV7d/m6QkYMcOdYGxo0dV0bS99eTu3hcfr3bDVlWp1gonTqidvVVVsl2y3ZmBN519Dbl79fbEWIiIKCSxaEtERERERC21vuBUaqraoVhaygJTqMrI8M9rt2qVKj5WVqri6YIFqhDZkX37VFuCiAhVdK2uVhcWc68nd4EzNlb1xy0pUUXg730PeO89dQGy6mpVvO3bV75L1l8Z2NHR11BhYdsMfenRS0REvQKLtj7QdR1paWm8up0As5NjdnLMTo7ZyTE7OWYnx+zImy6tC88eoKmpPdsDNAiE1ddURzs6fdnt6XSqfrTPPaeKq+7do889B0yY0HQz3TCQ9umn0A2j+b4DB6oLjAGqgNnQAJSXq/dA+wXO3Fy1K/fECVWs7dcPmDkTmD3bD8F0s/a+hhoa2t2Bqw8bFj7rroeF1ddsD2N2csxOjtnZw6KtD9yLinzH7OSYnRyzk2N2csxOjtnJMTvypkvrwt0DND9fFc/i4nqmB2iQCJuvKW+7Yt07Ojv6XHuPc/AgcOAAkJOj+tOWlgJ79rS4yJZuGEjbsKHl/RMS1H1dLlW01DR18bKoKPX5jgqcq1er+48e3bwDd8aM4F+L7X0NRUW1uwNXz8gIj3UXAGHzNRsAzE6O2ckxO3tY0vaBYRjYvn07DM//NSZbmJ0cs5NjdnLMTo7ZyTE7OWZH3nR5XXhecOqZZ0Jjd6OfhMXXVOueqpalPnY6O/5cR48zcqQqNG7aBHz0EbB1q7pomMeFyIzISGy/+moYkZHNj3HGGeq9pqmiZZ8+6vHcO23bu1CYu8CZmtpc4PT1ImSB5O1ryLNAbRgtdrGHxboLEGYnx+zkmJ0cs7OHO219YFkWKioqYLmb45NtzE6O2ckxOzlmJ8fs5JidHLMjb/yyLnqyB2gQCYuvqY56qgL2exa3fpzhw4GCArVrVtNUq4SDB5tubmkaKjIzYXkUcjF+vLpPVZUq1sbEqHYH7p22gPcLhTmdod+mo/XXUAe72C2XK/TXXYCExddsgDA7OWYnx+zsYdGWiIiIiIgo3HTWl9huMbT14wCqUHvmmeoCY/X1QGNjx2M55xzgs8+Amhqgf39VvO3bt+3z+VDgDGneCtREREStsGhLREREREQUbjoreNothrZ+nJgYtdt2wADV5mDTps7Hcv75QFmZeoyTJ30rvoZrgbOX7mInIiL7WLT1ga7ryMzM5NXtBJidHLOTY3ZyzE6O2ckxOzlmR95wXciFTXYdFTx9KYa2vm1hoSrANjQAY8YAe/eqoiwA3eVC5urV0F2u5vs7nV0rvvaSAmfYrLsAYHZyzE6O2ckxO3s0qxc3kKisrER8fDwqKioQFxcX6OEQERERkUBvOKfrDXMkP3A6vRdF2zvur+f6zW+Ap55q/7YXXADcfLMq3NoZb6gL13kREZFf2Es9ivEAADdwSURBVD2vY0nbB4ZhYMuWLby6nQCzk2N2csxOjtnJMTs5ZifH7Mgbrgu5kMrO6QQ2bgSefx5YvBhYskS9X7VKfX7VKu/H/eX994F33mn60IiMxJYbboARGdlyjPn56r1b63G98ALw17+qN8/buefnPtb6Y7uk9/NVF/IOqXUXZJidHLOTY3ZyzM4etkfwgWVZqK2t5dXtBJidHLOTY3ZyzE6O2ckxOzlmR95wXciFTHarVqli6NGj6qJgQ4aoFgQlJep4UpJ6b1nAiBHq+HPPAZoG5OTId4F6Pu/OnUBtbdOnLE1D7cCBsDSt+faHDwMOh9p9mpHRXMR1j6uoCLj7bjUuhwM4/XTg3nvVffPzgcpK1Qd39Ghg+/bmjxcsaLt7t6Px+nq/1jrbQdt6Xu7XITfXVtYhs+6CELOTY3ZyzE6O2dnDoi0REREREVGo8SwQDhoE7NkDlJcDNTVAaqq6aNiuXapQOWKEKobW1QGbNwOPPgqkpbUtXrqLkg0N6rGAtsVdp1MVfuvqAMNQz9fZL90uF3D8uHpcQD2He1ynTqni5qlT6uJm0dHAgQPA8uVAbKx6GzFC9c195RXVQ9eXgmgXC6lN7BR+PeflcDS/Du5iNRERkQ9YtCUiIiIiIgo1rQuf/foB1dWqiOouLI4Yod6XlKjP/+c/QEQEMHKkuq1n8dJdlCwuBg4dUoVWXQeSk4E77gBuukk97+uvA1u2qILtqVPqfWd0XRVko6LUxykpzeOqrwdOnFDHY2KAPn2AxkbgyBE11txcVQDt318Vivv3960g6o9Cqt3Cr+e8UlPV+7g4dTxQ2F+XiChksWjrA4fDgVGjRsHhcAR6KCGH2ckxOzlmJ8fs5JidHLOTY3bkDdeFXEhk5y4Q7t2rCplxcaoAeuSIKrQuXAicfz5QVqYKjDt3qgJrTg6QkKDu4y5eAuo2NTXAyZPNxVjLUsXdJUvUvy+9FPjb31RBt65OtTPQtBY7bR0uF0a99hocLlfzWPv3V60N3MXLjAzge99TFzD75hs1bkCNvX9/9fj19YBpAmvXAtnZajwxMUBVldp9e+CA+rizgqg/Cqmehd+aGlX8PXKkbeE3I0PtwM3PV9nGxanXwX0bp1O1ggC8tqfw+7rzV1sIXwSoSBwSX7NBitnJMTs5ZmcPi7Y+0DQNCQkJgR5GSGJ2csxOjtnJMTs5ZifH7OSYHXnDdSEXEtllZKhC6CuvqAJqTAxw+eWqSOhZMJszR+0ILSoCnn1WtR8wjJbFS3dRMiFB9ac1TVWI1XX1vrYWeOklVZDbuVN9vrFRfd5duDVNAIBmmkgoLm45VpdLjdU9plWrgDffVL1uDUPtrm1oUI9ZUaGKsllZanfuf/6jxj5uHHDddcAnn6idvpqmet8WFnZcHOyskGqHu/BbWKjaRlRXA5GRalxTprS8rTvv1oXLVauAxx9XGbrHfu+9LYqofl13/moL4YtAFIm/FRJfs0GK2ckxOzlmZ48e6AGEEpfLhU2bNsHl+b/GZAuzk2N2csxOjtnJMTs5ZifH7Mgbrgu5kMjO6VQX5RozBpg6Vb0/fNj7DseMDGD2bODmm1XBcNcu9d5dvHQXJauq1C7SbwuwANTtdF3twP3kE9WywL0zyjCaC7ffckVFYdOdd8LlboUAAMOHq7E6nc3FxLo69Vju+6emAvHx6uJpaWmquDh8OHDBBUB6OrBoEfCzn6nbjBoFXHSR6uWbn68esyNz5gDPPAMsW6bez57tW9buncGHDqmCbb9+aoyrV3t/7owMVcz13GH73HNqd3DfvqpIfeCAOuZxf7+uO3chPjW1uS1EZWXzzmp/a10ktix7r42fhMTXbJBidnLMTo7Z2cOdtj4y7PRsIq+YnRyzk2N2csxOjtnJMTs5ZkfecF3IBWV2nn963rpXa3Jy571a29sF6rkbdcAA1WPWc7dtVJTahetwqEJqUZE67i7utsrK8CzYAsAZZ6idnu6CYWWlKsTu3aueo6FBFXEdDuC001SLBHc7g+pqVSDNyVH3d7lUgdrd53brVuDjj4ELL+x8x21Xdpjm5gLDhqlCcb9+qvBqtzduaam6EJuuq6JtY6NqQeGlxYLf1l1P99cNgouwBeXXbIhgdnLMTo7ZdY5FWyIiIiIiomDX+k/PZ86UFeXaK156FnSfegr4619VcdEw1FturnqOkhLVYqFPn+YdtjU1qujant27gcGDVXG2vFztsN2zp/mxLUu1RYiJUa0YXC6167esTLUhuOaa5jF7zrmoCDh4EHjxReDtt7v3z/FTUlRh3N3OwZciaEoKkJiodkKXlam83MWKwsK2LRb8wR9tIXwRjBdhIyIKcWyPQEREREREFMy8/en56tXqT/a9tTvw5XE3bmz+E3Z3q4Rt29THDocqsGqa2hXbr58qtlZXq7f4eLXbdsCAjp/HXbRdtkwVhMvK1OO4XOq+ycmqYJucrArFR4+qC5QdO6Y+Xr1aFa3dhUhNUztsDx4EhgxRFyrryp/jt87BG8/n9jXvjAzVmiI5WRWjDUNlN2wY8N573ddCoKttIXzRlXyIiMgrzbI8LvXZy1RWViI+Ph4VFRWIi4vr9PaWZaG2thaxsbHQPPo2UeeYnRyzk2N2csxOjtnJMTu53p6dr+d0oUgyx96+Lroi6LLbuBFYsqT5T88NQxXGbr21+TY5Ob4VyNq7aNRf/wrMn6/+fB9QxVDLUq0IWrdEiIhQhVeHQz0OAEvTUHvaaYg9dgya+1fNmBjV0sDdWuDrr1XRtm/flj1zExLULt6ICNXztV8/4OKLVYFY01ThEVA7bHftUmPNzm6ZybJlvu1c9fXiWZ4tKnwtSP71r8B996lCc1KSmr/HmINu3Ul0JZ8uCIvsAoTZyTE7ud6end3zOrZH8FFU6/5MZBuzk2N2csxOjtnJMTs5ZifH7Mgbrgu5oMrO25+e19QAzz6riqbuYmN7RbLWhbTWO3dLStTHubnq9qapiqDuX6QtSxVOY2NVwdHdwiA2Vn2+qqr5uSwLUZWV6j5uLhewf78ac1WVun99vSrWpqWplgmmqY4nJKgCrvsCZUeOqGJvSQnw+uvAP/+pCqwREWqMJSWquHvggCoO+/Ln+O4LhNXVqR671dUtc/BWfOxKb9ycHCArS2Vjmqp43WrMQbXuJLraO7gLQj67AGJ2csxOjtl1ju0RfGAYBgoKCtgsWYDZyTE7OWYnx+zkmJ0cs5NjduQN14Vc0GXn/tPz2lrg889VAbS+XhVN3e0S2msNsGoVsHix2qm7eLH62H3RqH79VMG0Xz/1cVER8K9/qZYEQPMuW0AVGevqVBEVUAXYyEi1M3bs2KanM6KiUHDnnS0vRuZyqefZubP5YmK6rp6ntFTtlI2Lay7SugvG1dWqsLl2rbqQ15o1zYVmd8F43z51fOdO1Re3sLD5eTtre/D668CWLUBxsbpdfb3K4fXX22bmD+7X8cgR4KOPgB07Wow56NZdCGF2csxOjtnJMTt7uNOWiIiIiIgoFBw9Chw61FzUHDRIFTxTU9Wf2ZeWttzl2N6O2iVLVNF17drm4ml0NPDf/63u43J5f37DUEXNqip1v7IydXGx4mL7czAMVfyNjlb3bWhQrRgiI1XhtqpKPb+7/YLDoQrGLpcq4iYlNReaHQ71OKNGqQt9HT+uds4mJakduWvWtNyJ7Nn2wOlUn3c41HM3NABffgmcfro67i6Ie+5Cbr2DVNIKIDdX9bMdObLt7t70dPs5EhFR2GPRloiIiIiIyJt9+1Rhsof7c7bh/jP+o0dVAdLlUgXKoiLgtNNU4S8uThUeN25sHm9RUfPFutyFzpIS9W+3ujpVNK2oUIVcl6u5LUJ73H/ef/Jky924ndE0dVt3IRZo3tWr6829azMyVLuDPn2AM89UBdkdO1SBdO9eVWQ1TXU8Lk7d7j//UTtla2pUT96TJ1XR1/MiZZ6FV/eO37POUrttq6rUYx4/rjI599z2C+JOp9qN21FRuD3u5x0zRj1+//7Nj++taBugHrFERBR4LNoSERERERG1VlICLF0KnDjhW1GuO5SWqmKirquLVwGq0NrYqAp+Q4YAo0erC1q5L6jVrx+webMqPG/frgqbkZHNhcG+fYFJk4AvvlC7Sk+dam6B4C6oel54rDVfirWe9wFUcVXTmi8g5m6F4LZvHxAVpcYSGwvs3q12GJ86pW7vnot7J+7WrWrHraapwvWxY+r+0dHqvlOmqNfTs/Dq7hNcU6NuGxurHjc1VRVK9+5V/WdLStTt3H1nV61SBfTNmzsuCrfHW39iz8f35OtF0jrDAjARUUjRLMvXn7Thw9er8FqWBcMw4HA4euXV7bqC2ckxOzlmJ8fs5JidHLOT6+3Z+XpOF4okc+zt66IrrOJiGL/4BRwuFzR3YU3TgGeeCUyxy+kEfvxjYNs2VYgEVAEzNRW4+Wa1G/Xhh5svqPWvf6kdtu6dre4CaWysKtj94hfAX/6iCqFOZ9sLj2lac7E2IqL9dgleWFB9bR0NDWh31em6Go97l21rmqaKylFR6kJlR44AAwaoYqz7Pjk5asxnngm88Ya6LaAyiIxsbnvQ2AhkZqpdua1fv1WrgF//WuUaH6/aLAwdqvoGDxjQ3Gt34UJg9myV1eLFqoBeXKweX9ebi8LLlql/d6Z1Mfbbx2/xNfvNN+q5LKu5uNuVNejvAnCQ4fc7OWYnx+zkent2ds/ruNPWRw0NDYh1N70nnzA7OWYnx+zkmJ0cs5NjdnLMjrzhuhAqLUWDy4XY1NSOe8ZKOJ2qbQGgCo92Hi8jQxVnH38c+OYbtVPV3WJg9Wrgs8/Uzs+oKLVDtLxcFfuiotTOU9NUBT+XSz3/smWqKHr4sDrm3sfj+d5dsMzIUDtZ7RZuNQ0NcXGIPXas/Z24pqkevz3u9gs//rHqK/vii6p4vHevKtQCqg1EVhZw9dXq38eONe9g1TRVqN2zR90+JkYVRt33c+80nTNHZfD006pNwdCh6v6DBgFz56rbeb5G7gu4paergrdpqtfiwAFVFE5Jsbebdc4ctSvXy+2avmbdzzViRNfXoGdv49RUNd7nnmveGRxqO3DbGS+/38kxOzlmJ8fsOqcHegChxDAMbN26lVe3E2B2csxOjtnJMTs5ZifH7OSYHXnDdSFnJCdj6yWXwDh6VBX9OvoTdqdTFQKdzs4feNUq4IorgJ/8BLj+evXvVavsDWrOHODNN4H/+R91EavcXOCcc1SB9m9/UztKNU29dxdpDaO5cGoYzW0J6uubLwTWXmHVMFRriMJCn3baGpGR2HrDDTAiIzu+YWePmZgI3HQTcOGFqtXDzp3quLstQ12daqPw6KOqmHnwIPDVV6rQ2b+/KvKOHQvceSfw0kvqPosXq4uwLV6scr/zTpXnoUPA118D//iH2tVbUQG89hrw8stq/m7u1gbV1cDw4SrHxsbmonBhYdvnaE9GhtqV61F0bPE1636uvXvV+tu7t/012Bl3AbiuTq3V4mJV5H/9dTVGu2MOBu2Ml9/v5JidHLOTY3b2sGhLRERERNRDnnvuOZx++umIiYnB5MmT8e9//zvQQ2qfL8XIcDNsmNpNqWmqIKhpqijXeheiLwUv98XEDhxQu1z79Gne8eiZcUe5Z2SonZ99+wLJycCmTar4Vl2terMeO6baJgCqSNm6MOr+5bi2VhUafSjG+l1nXfpGjVLvMzKA735Xjdk9fk1TRVnDANavV4XT6GjVrgBQ/771VlWsvftudcy903TECPV++fLmY0OHqsJwZaUqeA8a1Hy7/Hxgwwb1mgCqrUBtrco6K0vtBl60CBg4sO1z5OfLv34yMlSf4q+/VnP8+mv1sWQnbEqKyus//1Hzc/cD/tvf1Prz15i7m+eO4VAYLxFRF7E9AhERERH5Vaj9pW1PeeONN3Dbbbfht7/9LSZPnoynnnoKl1xyCXbu3Ink5ORAD6+lMO9/aUtqqup3WlbmfTG3LiCVlHR8MaqiIlWktazmi4k1NqrdrO4/ebeTe0qKKtC+917Li3e5WwqYpmqLYFnee8YahirslpR0fKGxQHP38f3ud1VBVdebe+4Cah5RUeq9u9WCe/779qmdubNnq+OtWw3066daPpw6pYrzuq6KrgcPqmzdbTH69QP+/W/VA7hvX/WajB6tHrOhQT3m+++rgq5hqF63557rezsD9zfNpKSWx7ZvB8aMUTuHq6rUx06n799Y3YXvLVuaC9zZ2WqXdm0tkJfn/zYg3aGjlhHuC+wFC/4gJCI/YNHWR46Oei9Rh5idHLOTY3ZyzE6O2ckxO7lgyY61vvb9+te/xvXXX48FCxYAAH77299i9erVePnll3G3ezegn4nWha/FyDDlcDjUjtusLO838KXn6KpVakdjWZkqkpmm2mlrmuqCV+5eqHZzr69Xj9OaYagC5FlnAQkJwD//qf4k3pNpqh22hw93W8HW0dDQ9QeJjVUXCPviC7UrtLFRzc3lar7AWl1d845i93FNUztJ16wBrrpKZeduNVBSorLbulXd1+VSx1JTVfE8NhY47bTm2xUVqde4slLtcK6pAV55RRVXT55Ur6emqaJndLQq2u7dq9ZMRy01PHl+0xwwAI7589Xx1usrOblrBdWrrlKZuC9WV12tWlAAzRnYHXOgeL6OXsYbLD8HQ/EHYdBkF4KYnRyz6xyLtj6IiIjAxIkTAz2MkMTs5JidHLOTY3ZyzE6O2ckFS3as9bWvoaEBX375Je65556mY7qu46KLLsK//vWvNrevr69HfX1908eVlZUAAJfLBde3f9Ku6zp0XYdpmjA9im/u45qmIScnp+l+7uOGYcDy+NN095Wb3Y+LI0eAU6fgyMwEHA4YaWnA7t3qeHp60y9ZrfvQRURENF0N2k3TNDgcjjZjbO94Z3NqPXbbc/I47m3s3o7n5OR0PKfkZJgDBgBHjwKpqdCOHoUjLk4d93hefd8+6Pn5MPv2hTllCvDll8CpU9AB6KefDmPRIljp6arVwalT0LOy1JyGDIG1Z0+L3DVNg+vIEVVcTEgAqqrgaGwELAtGVJQqIPbtq4p/iYlARETzcTVRRNTVwYqNhZGQoHaa1tRAsyw4Ghth6jrMiOZfEZuOOxwwPX651k0TussFMyICpt7ccU83DEQ0NCD3qadgaRpcUVHquMsF3TRhREbC8tgt63C5oJlm0+2ajjc2AiUlMPr0UcXpyEjAsuDQdcAwYLgLtN9ejC3i1ClYug4jIkIVwydPhlZfD0dpKcxhw2Cmp6vC2YoV0P7zHzgAmJMnw9y/XxWvy8uhR0dDv+YadXzFCtUfNyICelwc9D59YDidsM44QxV6KyqgWxb0iAgYug7rwAHgvPOA48fhaGyEtmsXXAMGAPPmqQKpy+V97e3bB8e33zSN0aOBkhLkrFypdsGmpMCKj1d9lVNTgaNHoQ0YAEdKiuzrKT1dXczulVegl5ZC79dPrT3LUoXo4mLofftCX7AAxtChsDzWsD++ngA/fI9IT4e+YIH6etqzB2Z8fFPGuq5j4sSJMAyjxTi783uE1znt2wftlVfgsCyYI0fCLC1V+Y4fD+3004Py+x6AFj8rwu17OdC9P58mTJgQdnPqidfJ4XC0WHfhMCdvx9ubk10s2vrAsixUVFQgPj7ep5CJ2XUFs5NjdnLMTo7ZyTE7uWDJzp8XOw835eXlMAwDKa12sKWkpGDHjh1tbr906VI88sgjbY4XFRWh77d/Wp+UlISsrCw4nU6UlZU13SYtLQ1paWnYtWsXysvLEfltn8/MzEwkJydj27ZtqPXYpTlq1CgkJCSgqKhI/VJRWwvMmoXszz5D1IABKDjzTODMM9XxggLk5eWhoaEBW7dubXoMh8OBiRMnoqKiosV8YmNjMW7cOJSXl6O4uLjpeHx8PEaPHo3Dhw/j4MGDTcftzKmioqLpuO05fSs7OxtRUVEoKChokau3OZmmibPPPrv9OfXrh+L581XLA5cL8YMGYfS4cTgcFYWDHo+fdOoUsior4bzkEpQlJQGXXQZUViItLg5pl16KXXV1qCgoaMo9c9s2JBsGtmVmonbs2Kbcm+ZUXw/jRz9SX2wuF7J/+1tEVVai4M471RfekCFAXR3yXn0VDUlJ2DpvXlPrBEd9PSb++teoOOcc7DjvPFX4rKtDbHk5xq1YgfLsbBTPnNn8OhUXY/Rrr+Hwuefi4HnnNc9p82ZkrV6t5jR+fPPr9OmnGPLZZ9h6/fWoHzCg+XVavRrJmzdj28KFqB04sPl1eu01JBQXo+iWW1Rx2f06rViBKMtCwU9+ooqkMTFAYyPyfv97NOg6ts6frwrXDQ1wAJj4v/+LirQ07JgzR+0e1XXE1tRgXEpK89obMgT42c8Qv349Rh8+jMNjxuDgoEHqomPJyUgaPBhZF18M5969KPvZz9Q3r5gYpK1bh7RNm7Br+nRUjBgBnH02YFnI/OILJK9bh23z56M2JUUVy6dMwaixY5HQpw+K6upgxMQA364Fr2vvxAnknTqFhjPPxNazzgIsC40uF2J278bEGTNQMX8+dhw6pDI480zEpqdjXEYGyo8elX09DRkC3HIL0jQNaaefrtZeRQVwyy1AfT0yhwxBcnY2tm3Z4vevJ799jxg/HlnPPKPmBKgifUEBhgwZgv79+6OkpKTHvkd4ndOJE4jNzcW4AwdQnpyM4rw8tUN71y7ENzQE7fe9xsZGREZGhuX38u6e04ABAzBixIiwmlNPvE719fUoLCxsOkcJhzn58jpl2Dwx1izPUnIvU1lZifj4eFRUVCAuLq7T27tcLhR8e6IaEcF6ty+YnRyzk2N2csxOjtnJMTu5YMnO6VTXYrKs5r9c1TTgmWe6t2jr6zldIBw+fBhDhgzB559/jrPPPrvp+JIlS7B+/Xp88cUXLW7vbadteno6jh071jTHznaTuH8hys3NhcPh8G03yTvvqB2AlZUw3DsGL7us6fZA+O368TxuGAYKCwsxceJEOByOjue0b5/aBZmcDEdWVtux79sH/ZZbYGoazMGDm74w9CefhJ6Z2XLs77wDfeVK6BUVMAYMgNUq96Y5vfMOsGwZsGcPHFVVgKbBOP101Xv1Jz9ROzi3bAGefRZGTY3qXVpVpXalnnYarCFDYOzbp3baVldDa2iAo6HBLzttTYcDm5YsQe5TTzW1SbC101bTgPh4oLERjpoaIDoaRt++6riuA9HRcCQlAY2NagduTAxQXAzU1yNC12HFxcGoqFA7W5OSoM2bB8fs2S1fj337oN12GxwuF8zBg9UuSE0Dfv1r6BkZzWvP6QRuuw2wLOi1tdC3bIGhabDGjQMyM4HVq9VxAAYAKzJStaS48UY4fvhD+2tv3z44brlF7bRNS4Nx9CgKp09H7owZiD7jDPX1VFysdnMnJ7e7UzPYv57cuvN7hGmaKCwsRE5OTosdpAHZadvOGgvWnbYNDQ0tflaE0/dyt+6ak+fPCk3TwmJOHY3dn3Nyn7u61104zMnb8fbmVFNTY+vclb8REREREZFfZGSov0DOz1eb1OLigIULucsWAAYOHAiHw4HS0tIWx0tLSzFo0KA2t4+OjkZ0dHSb4xEREW0K8+5fOFpz/8Lh/iXc87g3LR53zhzV16K0FBHtXEjH238QaJrm9Xh7Y/T1eHtjtzUnH4+7d613OqesrBZ9b9uMPSsL+PZPuvXt25u/MDIz247dI3dHR7m7b1dU1HSxo4icnObbu8dkWYhw9ywZNAi45hpV/MvPR0R0tOprGhen+rk6HKoY29CgesgCqkcuVDFWb/WLKPBtMVbXm3vOWhZMh0MVexsaENGqt63D88JokZGq8JqUhIjGRrVzuK5OHZs3D8jIQMSaNarIffy4GufAgcDChYiwLPWNJipKzeG006CdfjoiZs5UvWc9smvxemRlAdddB+TnQ9+5E7r7tWj9+nncDvX1wNixcFx8MXDllepxn38e+OMfVYG5Xz/A83Oer5MXLY5/uzaQn4+I7duBAQOgpafD8e3jaJqGiFbrq82cunC8J7+e3Lrre4S7sNL6+51bj83J7hoLotfJ28+KcPpe7tZdc3L/rAinObl155w0TfN6jhLKc2rveHtzsoNFWyIiIiLyG4+aEy+a7SEqKgoTJkzAxx9/jFmzZgFQO8M+/vhjLFq0KLCDa09GBl9Af/HlC8Nu7nZu197zuo81NKjCZ0OD6uF68iTQr59qPeBuTL1+vdo6P3Wq+ndBgXqss85Sf/YdHw/MmKHaRLz7LvD0083Pn5en+rPOnAkcOgR8/bX6c/bhw9VjDByonj8lRd1/1y71vOefr+5/1VUtx9nRHOx+w7H7WnR0u5/9DLj0Uv98o/N8nqQkdXEzCm38QUhEfsKirQ80TUNsbCz77AkwOzlmJ8fs5JidHLOTY3ZywZYda33e3XbbbZg3bx7y8vIwadIkPPXUU6ipqcGCBQu65fmCbV2Ekm7JLlBfGN6e19sxd6G0tSuv7Pw27sc8/3xoS5cidts2aI88onrs+jLO1o/fUWZdydMfhXF/vp7fPpZmGIitrubXrEDQfb8LoR+EQZddCGF2cszOHva0DfL+Z0RERETUsVA6p3v22WfxxBNP4MiRIxg/fjx+85vfYPLkyZ3eL5TmSERERETts3te17bBA7XLNE0cPXq0RTNjsofZyTE7OWYnx+zkmJ0cs5NjdqFj0aJF2LdvH+rr6/HFF1/YKthKcV3IMTs5ZifH7OSYnRyzk2N2cszOHhZtfWCaJoqLi7moBJidHLOTY3ZyzE6O2ckxOzlmR95wXcgxOzlmJ8fs5JidHLOTY3ZyzM4eFm2JiIiIiIiIiIiIggiLtkRERERERERERERBhEVbH2iahvj4eF7dToDZyTE7OWYnx+zkmJ0cs5NjduQN14Ucs5NjdnLMTo7ZyTE7OWYnx+zs0SzLsgI9iEDhVXiJiIiIQl9vOKfrDXMkIiIi6g3sntdxp60PTNPEwYMH2ShZgNnJMTs5ZifH7OSYnRyzk2N25A3XhRyzk2N2csxOjtnJMTs5ZifH7Oxh0dYHXFRyzE6O2ckxOzlmJ8fs5JidHLMjb7gu5JidHLOTY3ZyzE6O2ckxOzlmZw+LtkRERERERERERERBhEVbIiIiIiIiIiIioiDCoq0PdF1HUlISdJ2x+YrZyTE7OWYnx+zkmJ0cs5NjduQN14Ucs5NjdnLMTo7ZyTE7OWYnx+zs0SzLsgI9iEDhVXiJiIiIQl9vOKfrDXMkIiIi6g3sntexpO0D0zSxd+9eNkoWYHZyzE6O2ckxOzlmJ8fs5JgdecN1Icfs5JidHLOTY3ZyzE6O2ckxO3tYtPWBaZooKyvjohJgdnLMTo7ZyTE7OWYnx+zkmB15w3Uhx+zkmJ0cs5NjdnLMTo7ZyTE7e1i0JSIiIiIiIiIiIgoiEYEeQCC52/lWVlbaur3L5UJNTQ0qKysREdGro/MZs5NjdnLMTo7ZyTE7OWYn19uzc5/LhfOlGnw9bwW4LrqC2ckxOzlmJ8fs5JidHLOT6+3Z2T137X3JeKiqqgIApKenB3gkRERERNRVVVVViI+PD/QwugXPW4mIiIjCS2fnrpoVzlsSOmGaJg4fPoz+/ftD07ROb19ZWYn09HQcOHCAV+31EbOTY3ZyzE6O2ckxOzlmJ9fbs7MsC1VVVRg8eDB0PTy7f/l63gpwXXQFs5NjdnLMTo7ZyTE7OWYn19uzs3vu2qt32uq6jrS0NJ/vFxcX1ysXlT8wOzlmJ8fs5JidHLOTY3ZyvTm7cN1h6yY9bwV697roKmYnx+zkmJ0cs5NjdnLMTq43Z2fn3DU8tyIQERERERERERERhSgWbYmIiIiIiIiIiIiCCIu2PoiOjsZDDz2E6OjoQA8l5DA7OWYnx+zkmJ0cs5NjdnLMjrzhupBjdnLMTo7ZyTE7OWYnx+zkmJ09vfpCZERERERERERERETBhjttiYiIiIiIiIiIiIIIi7ZEREREREREREREQYRFWyIiIiIiIiIiIqIgwqKt0DfffIMf//jHyMjIQGxsLLKysvDQQw+hoaEh0EMLCY899hjOOecc9OnTBwkJCYEeTlB77rnncPrppyMmJgaTJ0/Gv//970APKSRs2LABP/jBDzB48GBomoa333470EMKCUuXLsXEiRPRv39/JCcnY9asWdi5c2eghxUSXnjhBWRnZyMuLg5xcXE4++yz8f777wd6WCHpl7/8JTRNw6233hrooQS9hx9+GJqmtXgbNWpUoIdFQYbnrV3D81bf8NzVdzxvleO5qxzPXf2D562+4bmrb1i0FdqxYwdM08SKFSvw1Vdf4cknn8Rvf/tb3HvvvYEeWkhoaGjA3LlzcdNNNwV6KEHtjTfewG233YaHHnoIhYWFGDduHC655BIcPXo00EMLejU1NRg3bhyee+65QA8lpKxfvx4333wzNm7ciDVr1qCxsREXX3wxampqAj20oJeWloZf/vKX+PLLL1FQUIDp06fj8ssvx1dffRXooYWUTZs2YcWKFcjOzg70UELGmWeeiZKSkqa3zz77LNBDoiDD89au4XmrfTx3leF5qxzPXeV47tp1PG+V4bmrDyzym2XLllkZGRmBHkZIyc/Pt+Lj4wM9jKA1adIk6+abb2762DAMa/DgwdbSpUsDOKrQA8B66623Aj2MkHT06FELgLV+/fpADyUkDRgwwPr9738f6GGEjKqqKuuMM86w1qxZY02dOtW65ZZbAj2koPfQQw9Z48aNC/QwKATxvNV3PG/tHM9du47nrV3Dc9eu4bmrfTxvleG5q2+409aPKioqkJiYGOhhUJhoaGjAl19+iYsuuqjpmK7ruOiii/Cvf/0rgCOj3qSiogIA+L3NR4Zh4PXXX0dNTQ3OPvvsQA8nZNx8882YOXNmi+971Lndu3dj8ODByMzMxH/9139h//79gR4ShQCet5K/8dyVggHPXWV47uo7nrfK8dzVvohADyBc7NmzB8888wyWL18e6KFQmCgvL4dhGEhJSWlxPCUlBTt27AjQqKg3MU0Tt956K84991yMHTs20MMJCf/5z39w9tlno66uDv369cNbb72FMWPGBHpYIeH1119HYWEhNm3aFOihhJTJkydj5cqVGDlyJEpKSvDII4/gvPPOw7Zt29C/f/9AD4+CFM9bqTvw3JUCjeeuvuO5qwzPW+V47uob7rRt5e67727TFLn1W+uTjkOHDmHGjBmYO3curr/++gCNPPAk2RFR8Lr55puxbds2vP7664EeSsgYOXIkNm/ejC+++AI33XQT5s2bh6+//jrQwwp6Bw4cwC233II//vGPiImJCfRwQsqll16KuXPnIjs7G5dccgnee+89nDx5En/+858DPTTqATxvleN5K1H44bmr73ju6juet3YNz119w522rdx+++2YP39+h7fJzMxs+vfhw4dxwQUX4JxzzsGLL77YzaMLbr5mRx0bOHAgHA4HSktLWxwvLS3FoEGDAjQq6i0WLVqEd999Fxs2bEBaWlqghxMyoqKiMHz4cADAhAkTsGnTJjz99NNYsWJFgEcW3L788kscPXoUubm5TccMw8CGDRvw7LPPor6+Hg6HI4AjDB0JCQkYMWIE9uzZE+ihUA/geascz1v9j+euFEg8d5XhuavveN7qXzx37RiLtq0kJSUhKSnJ1m0PHTqECy64ABMmTEB+fj50vXdvXPYlO+pcVFQUJkyYgI8//hizZs0CoP7k5+OPP8aiRYsCOzgKW5ZlYfHixXjrrbewbt06ZGRkBHpIIc00TdTX1wd6GEHvwgsvxH/+858WxxYsWIBRo0bhrrvu4omvD6qrq7F3715ce+21gR4K9QCet8rxvNX/eO5KgcBzV//iuWvneN7qXzx37RiLtkKHDh3CtGnTMGzYMCxfvhxlZWVNn+P/JHdu//79OH78OPbv3w/DMLB582YAwPDhw9GvX7/ADi6I3HbbbZg3bx7y8vIwadIkPPXUU6ipqcGCBQsCPbSgV11d3eJ/65xOJzZv3ozExEQMHTo0gCMLbjfffDP+9Kc/4Z133kH//v1x5MgRAEB8fDxiY2MDPLrgds899+DSSy/F0KFDUVVVhT/96U9Yt24d/vGPfwR6aEGvf//+bXrP9e3bF6eddhp70nXijjvuwA9+8AMMGzYMhw8fxkMPPQSHw4Grr7460EOjIMLz1q7heat9PHeV4XmrHM9d5XjuKsPz1q7huauPLBLJz8+3AHh9o87NmzfPa3Zr164N9NCCzjPPPGMNHTrUioqKsiZNmmRt3Lgx0EMKCWvXrvW6xubNmxfooQW19r6v5efnB3poQW/hwoXWsGHDrKioKCspKcm68MILrQ8//DDQwwpZU6dOtW655ZZADyPoXXnllVZqaqoVFRVlDRkyxLryyiutPXv2BHpYFGR43to1PG/1Dc9dfcfzVjmeu8rx3NV/eN5qH89dfaNZlmV1TzmYiIiIiIiIiIiIiHzVu5tZEREREREREREREQUZFm2JiIiIiIiIiIiIggiLtkRERERERERERERBhEVbIiIiIiIiIiIioiDCoi0RERERERERERFREGHRloiIiIiIiIiIiCiIsGhLREREREREREREFERYtCUiIiIiIiIiIiIKIizaEhH5YNq0aRg7dmygh9Elr776KkaNGoXIyEgkJCQ0HX/iiSeQmZkJh8OB8ePHB2x8vlq3bh00TcO6desCPRQiIiKioMJz1+DDc1cisotFWyIKKStXroSmaSgoKPD6+VA6Mf3666/x8MMP45tvvmnzueeffx4rV660/ViaprX7duONNzbdbseOHZg/fz6ysrLwu9/9Di+++CIA4MMPP8SSJUtw7rnnIj8/H48//nhXp9fGe++9h4cfftj27U3TxCuvvILJkycjMTER/fv3x4gRI3Dddddh48aNfh8fERERkb/x3NU7nrsSEXUuItADICLqrb7++ms88sgjmDZtGk4//fQWn3v++ecxcOBAzJ8/3/bjffe738V1113X5viIESOa/r1u3TqYpomnn34aw4cPbzr+ySefQNd1vPTSS4iKivJ5Lna89957eO6552yf/P785z/Hc889h8svvxz/9V//hYiICOzcuRPvv/8+MjMzMWXKFADA+eefj9ra2m4bNxERERHx3LUzPHclIn9j0ZaIKEyMGDEC11xzTYe3OXr0KAC0+NMy9/HY2NigOXksLS3F888/j+uvv75pR4XbU089hbKysqaPdV1HTExMTw+RiIiIiLqA565ERB1jewQiCnv5+fmYPn06kpOTER0djTFjxuCFF17wetv3338fU6dORf/+/REXF4eJEyfiT3/6U4eP/+GHH6JPnz64+uqr4XK5AKg/5briiiuQmJiImJgY5OXl4W9/+1vTfVauXIm5c+cCAC74/+3dfUzUdRwH8PdxQtKPwyg978KSx012wZgYLWQcHeI1H0ibGnMNq+HDvJS2+EMtdtV0za0FyhgSjlNzY8uITZnGWF63dC5rY8xILdnpsjDPp4tCUrlPfzh+4/ydPGl02vu13Xb3fbrf5/MPH773+9698IJ6HOzrr79GQkICOjs74fF41Pb8/Px7zkNCQgKcTicAYMqUKdDpdHjvvfeg0+ngcrnw119/qe83+Hjb3r17kZWV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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# create scatter plots\n", + "fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6))\n", + "\n", + "# scatter plot1: Hackett effect vs Kemmeren effect\n", + "scatter1 = ax1.scatter(clean_data['effect_hackett'], clean_data['effect_kemmeren'], \n", + " alpha=0.6, s=10, c='blue')\n", + "ax1.set_xlabel('Hackett Effect Size', fontsize=12)\n", + "ax1.set_ylabel('Kemmeren Effect Size', fontsize=12)\n", + "ax1.set_title(f'Regulator: {chosen_regulator_symbol}\\nEffect vs Effect', fontsize=14)\n", + "# Add correlation coefficient text\n", + "ax1.text(0.05, 0.95, f'Spearman ρ = {eff_eff_corr:.3f}\\np = {eff_eff_pval:.3e}', \n", + " transform=ax1.transAxes, fontsize=12, verticalalignment='top',\n", + " bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))\n", + "ax1.grid(True, linestyle='--', alpha=0.7)\n", + "\n", + "# scatter plot2: Hackett effect vs Kemmeren pvalue\n", + "# Set a minimum p-value to ensure that -log10(p) does not exceed the range of the plot\n", + "min_pvalue = 10**(-5) \n", + "\n", + "# copy pvalue column and replace 0 values\n", + "pvalue_for_plot = clean_data['pvalue_kemmeren'].copy()\n", + "pvalue_for_plot[pvalue_for_plot == 0] = min_pvalue\n", + "\n", + "# calculate log_pvalue\n", + "log_pvalue = -np.log10(pvalue_for_plot)\n", + "\n", + "scatter2 = ax2.scatter(clean_data['effect_hackett'], log_pvalue, \n", + " alpha=0.6, s=10, c='red')\n", + "ax2.set_xlabel('Hackett Effect Size', fontsize=12)\n", + "ax2.set_ylabel('-log10(Kemmeren p-value)', fontsize=12)\n", + "ax2.set_title(f'Regulator: {chosen_regulator_symbol}\\nEffect vs P-value', fontsize=14)\n", + "# Add correlation coefficient text\n", + "ax2.text(0.05, 0.95, f'Spearman ρ = {eff_pval_corr:.3f}\\np = {eff_pval_pval:.3e}', \n", + " transform=ax2.transAxes, fontsize=12, verticalalignment='top',\n", + " bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))\n", + "ax2.grid(True, linestyle='--', alpha=0.7)\n", + "\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "da5d4268", + "metadata": {}, + "source": [ + "## Make distributions over the shared regulators" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "7db5d124", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import Dict, Any, Optional\n", + "\n", + "# Functional encapsulation\n", + "# Calculate the correlation between the two datasets for a given regulator\n", + "def calculate_regulator_correlation(\n", + " chosen_regulator_symbol: str, \n", + " hackett_final: pd.DataFrame, \n", + " kemmeren_final: pd.DataFrame\n", + ") -> Optional[Dict[str, Any]]:\n", + " \"\"\"\n", + " Parameters:\n", + " chosen_regulator_symbol (str): The regulator symbol to be analyzed\n", + " hackett_final (DataFrame): Filtered Hackett dataset\n", + " kemmeren_final (DataFrame): Kemmeren dataset\n", + "\n", + " Returns:\n", + " dict: Dictionary containing correlation coefficients and other \n", + " statistical information, returns None if insufficient data\n", + " \"\"\"\n", + " # Filter data for the specific regulator\n", + " hackett_reg = hackett_final[hackett_final['regulator_symbol'] == chosen_regulator_symbol].copy()\n", + " kemmeren_reg = kemmeren_final[kemmeren_final['regulator_symbol'] == chosen_regulator_symbol].copy()\n", + " \n", + " # Check if data was found\n", + " if len(hackett_reg) == 0 or len(kemmeren_reg) == 0:\n", + " print(f\"No data found for regulator {chosen_regulator_symbol}\")\n", + " return None\n", + " \n", + " # Merge datasets on target_symbol\n", + " merged = pd.merge(\n", + " hackett_reg[['target_symbol', 'log2_shrunken_timecourses']].rename(\n", + " columns={'log2_shrunken_timecourses': 'effect_hackett'}),\n", + " kemmeren_reg[['target_symbol', 'Madj', 'pval']].rename(\n", + " columns={'Madj': 'effect_kemmeren', 'pval': 'pvalue_kemmeren'}),\n", + " on='target_symbol',\n", + " how='inner'\n", + " )\n", + " \n", + " if len(merged) < 3: # Need at least 3 points for correlation\n", + " print(f\"Insufficient data points ({len(merged)}) for regulator {chosen_regulator_symbol}\")\n", + " return None\n", + " \n", + " # Calculate correlations\n", + " # Remove any NaN values\n", + " clean_data = merged.dropna(subset=['effect_hackett', 'effect_kemmeren', 'pvalue_kemmeren'])\n", + " \n", + " if len(clean_data) < 3:\n", + " print(f\"Insufficient clean data points ({len(clean_data)}) for regulator {chosen_regulator_symbol}\")\n", + " return None\n", + " \n", + " # Spearman correlations\n", + " eff_eff_corr, eff_eff_pval = spearmanr(clean_data['effect_hackett'], clean_data['effect_kemmeren'])\n", + " eff_pval_corr, eff_pval_pval = spearmanr(clean_data['effect_hackett'], clean_data['pvalue_kemmeren'])\n", + " \n", + " correlation_results = {\n", + " 'regulator_symbol': chosen_regulator_symbol,\n", + " 'n_targets': len(clean_data),\n", + " 'eff_eff_corr': eff_eff_corr,\n", + " 'eff_eff_pval': eff_eff_pval,\n", + " 'eff_pval_corr': eff_pval_corr,\n", + " 'eff_pval_pval': eff_pval_pval\n", + " }\n", + " \n", + " return correlation_results" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "f116659e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'regulator_symbol': 'ACA1',\n", + " 'n_targets': 6075,\n", + " 'eff_eff_corr': np.float64(0.01608402414610723),\n", + " 'eff_eff_pval': np.float64(0.21004245167836072),\n", + " 'eff_pval_corr': np.float64(-0.017860635301765383),\n", + " 'eff_pval_pval': np.float64(0.1639456246498038)}" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# test the function\n", + "calculate_regulator_correlation('ACA1', hackett_final, kemmeren_final)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "89b6ad09", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Successfully calculated correlations for 145 regulators\n", + " regulator_symbol n_targets eff_eff_corr eff_eff_pval eff_pval_corr \\\n", + "0 ACA1 6075 0.016084 2.100425e-01 -0.017861 \n", + "1 ADA2 6075 -0.100878 3.243911e-15 -0.031116 \n", + "2 ARO80 6075 -0.037804 3.209219e-03 -0.027915 \n", + "3 AFT2 6075 -0.004887 7.033263e-01 0.034297 \n", + "4 ARR1 6075 -0.010794 4.002741e-01 -0.002523 \n", + "\n", + " eff_pval_pval \n", + "0 0.163946 \n", + "1 0.015293 \n", + "2 0.029576 \n", + "3 0.007507 \n", + "4 0.844150 \n" + ] + } + ], + "source": [ + "\n", + "# Extract unique regulator symbols from the filtered Hackett dataset\n", + "regulator_symbols = hackett_final['regulator_symbol'].unique().tolist()\n", + "\n", + "# Initialize an empty list to store all correlation results\n", + "all_correlation_results = []\n", + "\n", + "# Loop through each regulator symbol and calculate correlations\n", + "for regulator_symbol in regulator_symbols:\n", + " # Calculate correlations for this regulator\n", + " correlation_result = calculate_regulator_correlation(\n", + " regulator_symbol, \n", + " hackett_final, \n", + " kemmeren_final\n", + " )\n", + " \n", + " # Only add to results if we got a valid result (not None)\n", + " if correlation_result is not None:\n", + " all_correlation_results.append(correlation_result)\n", + "\n", + "# Convert the list of dictionaries to a DataFrame for easier analysis\n", + "results_df = pd.DataFrame(all_correlation_results)\n", + "\n", + "print(f\"Successfully calculated correlations for {len(results_df)} regulators\")\n", + "print(results_df.head()) # Display first few rows" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "7df98119", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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n2jYsGGWsbGxsRo3blye6wgDwPWE3NVx5K7O4e65q72TC6pXr6569eppyZIlOnXqlEqVKqWVK1fq4MGD6tevn1Ux+d9//1WDBg2UmpqqZs2aqX379goICJCHh4dWrlypVatWFThnutqpU6dkGIYOHTpk87yzZD3Pubn55pu1Y8cOHTp0SFWrVrXr+IWV25YpUybbByTSf79X06ZNyzWutLS0XAu0AwcO1LvvvquIiAh16NBBYWFhlnV1R48efV3ktm+99ZZuueUWxcXFacyYMRozZox8fX3VtWtXTZo0Kdfzh/ugQAvk08qVKyVJ9evXz7NvdHS04uPjdfHiRW3atEnff/+93n77bT300EMKDw9X48aN7T6urf+U7HH06NEc200mk+XTew8PD0nSpUuXsvXNSpIKKus/mJxiOnLkiFU/ZwoICJDZbNbx48etFp2XLt+EwDCMQjluYciK8/nnn7drkf+sG0QcOnSoQMeNjIy0minpKlnn/8033+i+++7Ls7+zzj83jRs3VmJiopYvX253gbYgr4fc3g9y2pa1n9TU1Dxn7eTk999/1zfffKNWrVrpu+++k6enp2XbunXrLDcrKYhr8T5RvHhxS/K6d+9erVixQu+9957eeustpaen6/3333d43wDgbshdHUfu6hxFKXft3r27nnvuOS1YsEBPPvmk5s+fb2m/0pQpU3Tq1CnNnz9fjz32mNW2p556SqtWrbLreB4eHrpw4YLNbVf/nmc9z/Xq1dPGjRvt2n9OGjdurJUrV2r58uVq3ry5XWNcldv+9ddfio6OtivGqx07dkzTpk1TrVq1tHbtWqsP6Y8cOZJrodte1+J9pFixYho8eLAGDx6sw4cPa9WqVYqLi9O8efN05MgR/fjjjw7vG9eOh6sDAK4nu3bt0oIFC+Tj46P777/f7nFeXl664447NHr0aL399tsyDEPffvutZXtWkcOeT9Pz69dff83Wtm/fPh04cEA1atSwXCKWdXdLW4lQ1iVWV/P09MxXzFl3t/zll1+yJUuGYeiXX36x6udMt912m6T//ki5UlZbYRy3MNSvX18mk0lr1661q//tt98uDw8PrVixwnJpU248PDwK5XfRWRo2bChJdp9/1iVTP/30U559HX0tZt1ZedKkSUpPT8+1b9an8Nf69ZD1vGVdDuaIrMvH2rVrZ1WclWy/10ju/z5xyy23qHfv3lq1apVuuukmLV682Cn7BQB3QO5qzd3/T7oSuat75q4PP/ywihUrpo8++kjp6elauHChKleurDvuuMOqX1bO1LFjR6t2wzBynUV8tVKlSunYsWPZPohIS0uzLA2Rxd/fX9WrV9c///xjdfm7I3r16iVPT0/NnDlTx48fz7VvVm5brVo1+fr66vfff9e5c+ey9XP2721+/yawZc+ePTIMQy1atMh2BVVuua1k//vftX5ewsPD9fDDD+uHH35Q5cqVtWzZsjz/PoF7oEAL2Om3335Tq1atlJGRoZdffjnPS242bdpk8zKFrE/OfH19LW1BQUGSpAMHDjgx4svmzZunP//80/LYMAwNHTpUmZmZ6tWrl6W9atWq8vf31+LFiy2Xi2TFO2bMGJv7DgoK0okTJ+xes6p8+fJq1qyZtm3bptmzZ1ttmzlzpv755x81b9482xpeztCzZ09Jly9TufLnkpKSYvlkNKuPu7v55pvVtWtXrVmzRhMmTLA5M2D9+vWWBCA0NFQPPvig1fpaV7o66QsKCtLBgwcL7wQKqGPHjipfvrwmT55s+cPoShcvXtTq1astjzt06KBy5crpo48+svnp8ZV/2Dn6WmzWrJkefvhh7dy5Uw888ICOHTuWrU9qaqqGDh2qmTNnSrr2r4d+/fqpWLFieuaZZ7R///5s20+fPp3jH7RZstZJu/L5laRt27Zp/PjxNse42/vE8ePH9ffff2drP3XqlDIyMqzemwHgekbump27/Z+UG3JX98xdy5Qpo3vvvVe//fabpk6dqtTU1GwzZKWcc6bXX3/dZh6Sk/r16+vixYtWS3sZhqEhQ4bYXKpg4MCBOnfunJ544gmb2/fu3avExMQ8j1u5cmW9+OKLOnHihNq0aaO9e/dm63P+/HlNnjzZsmSEt7e3Hn74YZ04cSJbXvjDDz/oxx9/VOXKlfM1Ez83sbGx8vf317Bhw7Rt27Zs28+dO5fnxISsn9OaNWus1p09ePCghgwZYnNMft//Cvt5ycjI0Jo1a7K1p6Wl6ezZs/Ly8rJccQD3xhIHwFX+/fdfy38yFy5c0LFjx7Rhwwb99ddf8vT01PDhwzVy5Mg89zN//ny9//77iomJUaVKlRQQEKDt27dryZIlCgoKUmxsrKVv8+bNFR8frwcffFBt2rSRr6+vateurfbt2xf4fFq1aqVGjRqpW7duCgkJ0fLly7Vx40bdcccdeuaZZyz9vL299cwzz2jcuHGqW7euOnbsqDNnzuibb75RkyZNbC683rx5c23cuFFt2rTR3XffLW9vb8XExCgmJibHeGbMmKG77rpLTzzxhL755hvdeuut2rZtmxYvXqyQkBDNmDGjwOdsS0xMjJ555hm98847io6O1oMPPijDMPTll1/q4MGDGjhwYK5x58elS5dyXZe1W7duqlatWoGOMX36dO3cuVMvvvii5s+fr0aNGikwMFAHDhzQxo0btXv3biUlJVk+CZ4+fbr+/vtvjR07VkuWLFHz5s1lGIZ27dqln376SUePHrVcTta8eXMtWLBAnTp10m233SZPT0916NBBtWrVsiu2K19Dtrz88ssFKoL5+PgoPj5ebdq0UZMmTdS8eXPVrFlTJpNJ+/bt06+//qrg4GDLDRB8fHy0YMECtW7dWm3atFHr1q1Vu3ZtpaamasuWLTp37pylMFmtWjWFh4frs88+k4+Pj8qVKyeTyaRnnnlGJUuWzDWuWbNmyTAMffbZZ7rlllt07733qkqVKjIMQ7t379by5ct15swZy6Vw0rV9PURHR2v69Ol6+umnVbVqVbVt21aVKlXSmTNntGfPHq1atUq9evXSe++9l+M+GjRooAYNGmjBggVKSkrSHXfcof3792vx4sVq166d4uPjs41xt/eJQ4cO6bbbblPt2rVVq1YtlS1bVidPntTXX3+tixcvavDgwQ7vGwBcgdyV3LWgbvTcNb+6d++uJUuWWF5Xtgq0Tz31lOLi4vTggw+qa9euCg4O1rp16/THH3+oXbt2+u677+w61oABAxQXF6fHH39cS5cuVUhIiH799VedPn1atWvX1tatW636P/nkk1q3bp3mzp2r3377TS1atFB4eLiOHj2qHTt2aP369frkk08UGRmZ57HHjBmj8+fPa8qUKapataqaN2+u6OhoeXl5ae/evVq2bJlOnjxp9YHIG2+8oVWrVmnMmDFas2aNGjZsqMTERH3xxRcqXry44uLinFYsDAkJ0aeffqouXbqodu3aat26tapVq6aMjAwlJiZq1apVuvPOO3NdXzgsLEwPPvigvvzyS91+++265557dPToUX377be65557cnwfye/7X2E+L+np6WrcuLGqVKmievXqqXz58jp79qy+/fZbHTlyRIMH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w+Xn56eGDRtq6dKldo09dOiQunbtqsDAQAUEBKhjx47as2dPtn4mk8nm1+uvv+7s0wEAAMANws/Pz2qGapbz589bttvj119/VZ8+fdSqVSuNHTs22zEk5Xic3I4xZMgQpaSkWL5yWxMXAAAA+Vdkljjo1auX4uPj9dxzzykqKkpz5sxR27ZttWLFCt111105jjt79qyaNWumlJQUDR06VF5eXpoyZYqaNGmiLVu2KDg42Kp/y5Yt1aNHD6u22267rVDOCQAAAEVfWFiYDh06lK09a1mC8PDwPPexdetWdejQQdHR0YqPj7e6EVjWMbL2efUM2KSkJMt6t7b4+PjYnHkLAAAA5ygSBdoNGzbos88+04QJEzR48GBJUo8ePRQdHa0XX3xRa9asyXHs9OnTtXv3bm3YsEH169eXJLVp00bR0dGaNGmSxo0bZ9W/SpUqeuyxxwrvZAAAAHBDqVOnjlasWKHU1FSrG4WtX7/esj03CQkJat26tcqUKaMlS5bopptusnkMSdq4caNVMfbw4cM6ePCg+vbtW/ATAQAAgEOKxBIH8fHx8vT0tEosfX191adPH61duzbXy7Di4+NVv359S3FWkqpVq6Z77rlHCxYssDkmPT3dcskZAAAAUBCdO3dWZmamZs6caWnLyMhQXFycGjZsaJnxun//fu3YscNq7JEjR3TvvffKw8NDP/74o0JCQmweo0aNGqpWrZpmzpypzMxMS/uMGTNkMpmsbigGAACAa6tIzKDdvHmzqlSpYjXjQJJldsCWLVts3szAbDbrzz//VO/evbNta9CggX766SedOXNG/v7+lvY5c+Zo+vTpMgxD1atX1/Dhw/XII484+YwAAABwo2jYsKG6dOmiIUOG6NixY6pcubLmzp2rxMREzZo1y9KvR48eWrVqlQzDsLS1bt1ae/bs0YsvvqjVq1dr9erVlm2hoaFq2bKl5fGECRPUoUMH3XvvverWrZv+/vtvvfvuu3r88cdVvXr1a3OyAAAAyKZIFGiTkpIs62pdKavt8OHDNsclJycrIyMjz7FVq1aVJN15553q2rWrbrnlFh0+fFjTpk3To48+qpSUFD399NM5xpeRkWF1Q4bU1FT7Tw4AAABF3rx58zRixAjNnz9fp06dUq1atfTtt98qJiYm13Fbt26VJL355pvZtjVp0sSqQHvfffdp4cKFGj16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QNLlG4SNGTNGnTt31i233KLk5GR98skn+vvvvzVu3DjdfPPN1/CMAQAAcKUiUaCNj4+Xp6en+vbta2nz9fVVnz59NHToUB04cMAy88DW2Pr161uKs5JUrVo13XPPPVqwYIGlQLtixQqdPHlS/fr1sxrfv39/ffzxx/ruu+/02GOPFcLZAQAAoKibN2+eRowYofnz5+vUqVOqVauWvv32W8XExOQ67tSpUxoxYoRV26RJkyRJFSpUsBRoa9asqVtvvVUfffSRjh8/Lm9vb9WpU0cLFixQly5dCuekAAAAYJciUaDdvHmzqlSpooCAAKv2Bg0aSJK2bNlis0BrNpv1559/qnfv3tm2NWjQQD/99JPOnDkjf39/bd68WZJ0++23W/WrV6+ePDw8tHnz5vwXaM+fl7y9s7d7eFi3/+8OvjYVpG9GhnTFDAwrJpN05c0g8tP3wgXJbM45Dl9f1/f18bkctyRdvChddcmfU/peunT5yxl9vb0v//yc3dfLS/L0zH/fzMzLz0VOihW7/JXfvmbz5Z+ds/saxuXfYWf09fS8/Fw4u++1et3n1vfq55P3iMvf8x6R/768Rzi/rzu8R7gij8gtniLG19dXEyZM0IQJE3Lss3LlymxtkZGRVjNqc1KvXj0tXry4ICECAACgkBSJAm1SUpLCwsKytWe1HT582Oa45ORkZWRk5Dm2atWqSkpKkqenp8qUKWPVz9vbW8HBwTkeQ7q8hljGFX90WW4A0aPHf3+YXen226WRI/97/NhjOf/RFh0tjR//3+M+faSrbjBhERUlTZ783+N+/aRjx2z3jYiQpk//7/GgQdKBA7b7likjXXH5nV5+Wdq923bfgADp44//ezxypPT337b7+vhI/1tOQtLl89y40XZfSfrmm/++nzxZ+t9lfjZ98cV/xZpp06Tly3Pu+9FHUsmSl7//8ENpyZKc+86adfn5kKR586Svvsq577RpUvnyl79fsED69NOc+06efPnnJ0mLF0txcTn3HTdOqlnz8vc//ii9917OfV95RcqaPb5qlTR1as59X3pJuuuuy9+vXSvltvbyc89J99xz+fs//pBefTXnvk89JbVrd/n7bdukoUNz7hsbKz3wwOXvExKk//u/nPs+/LD0yCOXvz9wQOrfP+e+998vZX1Qc/z45ddRTtq2lZ5++vL3qamXX585ueeey8+F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xwUFBTklSAAAAMBR5LIAAABwZ3YVaIcOHarY2Fh99913kiSTyaSnnnpKTz31VK7jMjMzCx4hAAAAUADksgAAAHBndhVou3fvrgYNGmjlypU6evSoRo0apfvvv5+1uwAAAOD2yGUBAADgzuwq0EpS1apVVbVqVUlSXFycevbsqQ4dOhRaYAAAAICzkMsCAADAXdldoL3S3r17nR0HAAAAcE2QywIAAMCdeDg6MDU1Va+//rpatWql2267TRs2bJAkJScna/Lkyfr333+dFiQAAADgTOSyAAAAcBcOzaA9ePCgmjRpogMHDigqKko7duzQ2bNnJV2+2+3777+vffv26a233nJqsAAAAEBBkcsCAADAnThUoH3hhRd05swZbdmyRWXKlFGZMmWstnfq1EnffvutUwIEAAAAnIlcFgAAAO7EoSUOfvrpJw0cOFC33nqrTCZTtu0VK1bUgQMHChwcAAAA4GzksgAAAHAnDhVo09PTFRISkuP2M2fOOBwQAAAAUJjIZQEAAOBOHCrQ3nrrrfrll19y3L5o0SLddtttDgcFAAAAFBZyWQAAALgThwq0zz33nD777DO98cYbSklJkSSZzWb9+++/6t69u9auXatBgwY5NVAAAADAGchlAQAA4E4cuknYY489pn379mn48OEaNmyYJKl169YyDEMeHh4aN26cOnXq5Mw4AQAAAKcglwUAAIA7cahAK0nDhg1T9+7d9eWXX+rff/+V2WxWpUqV9MADD6hixYrOjBEAAABwKnJZAAAAuAuHC7SSVL58eS7/AgAAwHWJXBYAAADuoEAF2r179+r777/Xvn37JEmRkZFq3bq1brnlFqcEBwAAABQWclkAAAC4A4cLtM8//7zeeustmc1mq3YPDw8999xzmjhxYoGDAwAAAAoDuSwAAADchYcjgyZNmqQpU6bogQce0Nq1a3X69GmdPn1aa9euVefOnTVlyhRNmTLF2bECAAAABUYuCwAAAHfi0AzaDz74QB06dNCCBQus2hs2bKjPPvtM58+f1/vvv8+aXgAAAHA75LIAAABwJw7NoE1MTFSrVq1y3N6qVSslJiY6GhMAAABQaMhlAQAA4E4cKtCWKVNGW7duzXH71q1bFRIS4nBQAAAAQGEhlwUAAIA7cahA26VLF3344Yd6/fXXlZaWZmlPS0vTG2+8oQ8//FAPPfSQ04IE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9bVsd5bffvtNzz77rDp06KCnnnpKmzZtUnR0tIYOHZrrccaPH6+SJUtaviIiIpwWEwAAAPKnsHJZAAAAwBEOFWj79eunn376SW3bttVPP/2kxMREJSYm6scff1S7du20dOlSDRgwIN/7/eWXX+Tn52fX186dOyVJfn5+NteZzVojNr/ry+aHt7e3BgwYoNOnT2vTpk059hsyZIhSUlIsXwcOHCi0mAAAAJC7wsplAQAAAEcUc2RQv379dOzYMb3++uv68ccfrbZ5eXnplVde0dNPP53v/VarVk1xcXF29c1awiAsLExJSUnZtme1hYeH5zuO/MiaDZvbZXA+Pj42Z/kCAADg2iusXBYAAABwhEMFWkkaNWqUBgwYoGXLlmnfvn2SpAoVKqhFixYqXbq0Q/u8+eab1atXr3yNqVOnjn799VeZzWarG4WtX79exYsXV5UqVRyKxV579uyRJIWEhBTqcQAAAOA8hZHLAgAAAI5wuEArSaVLl1a3bt2cFYtDOnfurPj4eC1cuFCdO3eWJJ04cUJffPGF2rdvbzVzNSEhQZIcWlPs+PHj2YqwZ86c0dSpU1W6dGnVq1evAGcBAACAa80dclkAAADA7gJtSkqKHnroIcXExGjo0KE59hs7dqxWr16tL774QjfddJNTgsxN586ddccddyg2Nlbbt29X6dKlNX36dGVmZmr06NFWfe+55x5JUmJioqVt3759mj9/viRp48aNkqQxY8ZIujyLonv37pKkadOmadGiRWrfvr3Kly+vpKQkzZ49W/v379f8+fPl7e1d2KcKAAAAB7lrLgsAAADYXaB99913tWbNGksxMydPPPGE3nzzTU2bNk0vvfRSgQPMi6enp5YsWaIXXnhBb7/9ttLT01W/fn3NmTNHVatWzXP83r17NWLECKu2rMdNmjSxFGgbN26sNWvW6MMPP9TJkydVokQJNWjQQLNnz1bz5s2df2IAAABwGnfNZQEAAAC7C7RfffWVunXrludaq2XKlNHDDz+sL7/88poltaVKldKHH36oDz/8MNd+V86czdK0aVMZhpHnMVq2bKmWLVs6GiIAAABcyJ1zWQAAANzYPPLuctmOHTt0++2329W3bt26+ueffxwOCgAAAHAmclkAAAC4K7sLtPbMMr2S2WzOdzAAAABAYSCXBQAAgLuyu0Bbvnx5bdq0ya6+mzZt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Bw5URkaGbd2PfvQjlZSUaPTo0QoNDVVpaaneeOMNXbp0Sb/85S9vyL0CAACgdR1+SJjZbFZCQoI2bdrUGfEAAAAAncbdclkPDw9t375d06ZN07Jly5SZmamgoCD95S9/sbViaEtYWJj++te/KioqSs8884wWL16scePGaceOHXanb++55x6ZTCa99tprmj17tt58802NGDFCH330kZKSkjrxDgEAAHAtHT5B6+Hhodtuu63Vp8UCAAAA7swdc9mAgACtWrVKq1atanPdf56c/U8DBgzQH//4xzb3pqWlKS0tzdEQAQAA0Ik6fIJWkr7//e/rzTff1Pnz550dDwAAANCpyGUBAADgThx6SFhjY6MsFouioqKUmpqqiIgIeXl52a0xmUx2DyYAAAAA3AG5LAAAANyJQwXap556yvbfq1evbnUNSS1w82poaNDZs2ddHQacrPkz5bO9+QQHB8vT09PVYQBug1wWAAAA7sShAu2JEyecHQeALuTs2bN67bXXXB0GOsmGDRtcHQKcbM6cObr11ltdHQbgNshlAQAA4E4cKtDedtttzo4DQBcSHBysOXPmuDoMAO0UHBzs6hAAt0IuCwAAAHfiUIEWQPfm6enJaTwAAAAAAAAnMDu68eOPP9bDDz+shIQE3X777YqMjLR7RUVFOTPONl24cEGzZs1ScHCwfHx8NHLkSB08ePCa+wzD0Nq1a3X//fcrLCxMPj4+iouL00svvaS6urpW96xevVpf+cpX1KtXL0VHR2v58uXOvh0AAAB0MnfKZQEAANC9OVSgzc/PV2JiorZu3arQ0FAdP35ckZGRCg0N1WeffabevXtrxIgRzo61VYZhKCUlRevXr9fcuXO1ePFinTlzRklJSfr000/b3Hv58mVlZGTo7NmzevTRR/Xqq68qMTFR8+fPV3JyspqamuzWv/HGG3rooYc0YMAALV++XHfddZcef/xx/exnP+vMWwQAAIATuVMuCwAAAJia/m8Vsh1GjBihc+fOac+ePWpoaFDfvn21c+dOjRo1Snv37lVycrJ+85vfKDk5uTNitrNhwwZNmzZNOTk5Sk1NlfTFA4xiYmKUnJys9evXX3VvQ0OD9u/fr7vvvttu/H/+5380f/587dixQ2PGjJEk1dbWKiwsTMOGDdPWrVtta9PT05Wbm6tTp04pICCgXTFXV1fL399fVVVV8vPz6+gtAwAAdAudlTO5Uy7bFZHLAnBHp06d0pIlS/TUU08pLCzM1eEAQIdyJodO0B48eFAzZ86Un5+fPDw8JEmNjY2SpK997Wt65JFH9Pzzzzty6Q7buHGjrFarJk+ebBsLDg7W1KlTtXnzZtXX1191r6enZ4virCRNmjRJknT06FHb2Pvvv6+KigrNnj3bbu2cOXN06dIlbdu27XpvBQAAADeAO+WyAAAAgEMF2h49esjX11eSdMstt6hnz546c+aMbT4yMlJHjhxxToTXcOjQIQ0ePFhms/2tJCYm6vLlyzp27FiHr/mvf/1LkhQUFGT3PpI0ZMgQu7UJCQkym822eQAAALg3d8plAQAAAIcKtLfffrutv6vJZFL//v313nvv2ea3bdumfv36OSfCaygrK1NISEiL8eax06dPd/iaixcvlp+fn93X2srKyuTh4aG+ffvarfX09FSfPn3afJ/6+npVV1fbvQAAAOAa7pTLAgAAAA4VaMeNG6fs7GxduXJFkvTkk09q06ZNio6OVnR0tLZs2aJHHnmkw9c1DEN1dXXtejW3zq2trZXFYmlxrV69etnmO+Lll1/Wzp079dOf/lS33HKLbby2tlaenp6t7unVq1eb77No0SL5+/vbXvTDAQAAcJ3OymUBAAAARzhUoH3++edVWFho69k1ffp0/frXv1ZcXJzi4+O1Zs0aPf300x2+7gcffCAvL692vYqLiyVJXl5erfaZraurs82317vvvqvnnntOM2fO1GOPPWY35+XlpYaGhlb31dXVtfk+8+bNU1VVle116tSpdscEAAAA5+qsXBYAAABwRA9HNvXs2VN9+vSxG0tPT1d6evp1BdO/f39lZWW1a21zC4OQkBCVlZW1mG8eCw0Nbdf1duzYoQcffFApKSl6/fXXW32/xsZGnTlzxq7NQUNDgyoqKtp8H4vF0uopXwAAANx4nZXLAgAAAI5wqEDbrL6+XgcPHtSZM2c0fPhwu4dqOaJfv36aMWNGh/YMGjRIBQUFMgzD7kFhe/fulbe3t2JiYq55jb1792rSpEkaMmSINmzYoB49Wv5vGTRokCRp//79GjdunG18//79MgzDNg8AAICuwdm5LAAAAOAIh1ocSNKyZcsUEhKir3/965o8ebI+/vhjSdK5c+cUFBSkNWvWOC3ItqSmpqq8vFybNm2yjZ07d045OTkaP3683cnVkpISlZSU2O0/evSoUlJSFBERoa1bt161VcGoUaMUGBiolStX2o2vXLlS3t7eSklJceJdAQAAoDO5Sy4LAAAAOHSCNisrS//93/+t//qv/9I999yj733ve7a5oKAgjRo1Sr/97W/txjtLamqqhg0bpoyMDB05ckRBQUFasWKFGhsbtWDBAru1o0ePliSVlpZKkmpqanTvvfeqsrJSmZmZ2rZtm936qKgo3XXXXZK+6EH74osvas6cOZoyZYruvfdeFRQUaN26dVq4cKECAwM7/V4BAABw/dwplwUAAAAcKtAuXbpUEyZM0Pr161VRUdFiPiEhQcuWLbvu4NrDw8ND27dvV2ZmppYtW6ba2loNHTpUa9euVWxsbJt7KyoqbA/seuaZZ1rMT58+3VaglaTZs2erZ8+eWrp0qbZs2aKwsDC98soreuKJJ5x7UwAAAOg07pTLAgAAAA4VaP/3f/9Xjz/++FXnAwMDW012O0tAQIBWrVqlVatWtbmu+eRss4iICDU1NXXovR5++GE9/PDDHQ0RAAAAbsLdclkAAAB0bw71oL3lllt07ty5q84fOXJE/fr1czgoAAAAoLOQywIAAMCdOFSgHTdunN58801duHChxdzhw4f11ltv6f7777/e2AAAAACnI5cFAACAO3GoQPvSSy+psbFRcXFxeu6552QymfT2228rPT1dQ4YMUd++ffXCCy84O1YAAADgupHLAgAAwJ04VKANDQ3VgQMH9K1vfUvvvvuumpqa9M477+j3v/+90tLStGfPHgUFBTk7VgAAAOC6kcsCAADAnTj0kDBJ6tu3r+3BXGfPnpVhGAoODpbZ7FDNFwAAALhhyGUBAADgLq47A21qalJTU5NMJpNMJpMzYgIAAABuCHJZAAAAuJrDBdojR44oNTVVfn5+CgkJUUhIiPz8/JSamqqioiJnxggAAAA4FbksAAAA3IVDLQ4KCgqUnJwswzA0YcIExcTESJKKi4u1ZcsW5eXl6Q9/+IO+8Y1vODVYAAAA4HqRywIAAMCdOFSg/cEPfqC+ffvqr3/9q8LCwuzmTp06pREjRujJJ5/U3/72N6cECQAAADgLuSwAAADciUMtDg4fPqzZs2e3SGglKSwsTI899pgOHz583cEBAAAAzkYuCwAAAHfiUIH2tttuU319/VXnGxoaWk14AQAAAFcj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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Effect-Effect Correlation Summary:\n", + "count 145.000000\n", + "mean -0.058967\n", + "std 0.072261\n", + "min -0.296206\n", + "25% -0.091059\n", + "50% -0.037956\n", + "75% -0.008329\n", + "max 0.042079\n", + "Name: eff_eff_corr, dtype: float64\n", + "\n", + "Effect-Pvalue Correlation Summary:\n", + "count 145.000000\n", + "mean -0.002324\n", + "std 0.047957\n", + "min -0.164093\n", + "25% -0.028492\n", + "50% -0.001771\n", + "75% 0.017656\n", + "max 0.186708\n", + "Name: eff_pval_corr, dtype: float64\n" + ] + } + ], + "source": [ + "# Set up the plotting style and parameters\n", + "plt.style.use('default')\n", + "plt.rcParams['figure.figsize'] = (14, 6)\n", + "plt.rcParams['font.size'] = 12\n", + "\n", + "# Create a figure with two subplots side by side\n", + "fig, (ax1, ax2) = plt.subplots(1, 2)\n", + "\n", + "# First boxplot: Effect-Effect Correlation Distribution\n", + "sns.boxplot(data=results_df, y='eff_eff_corr', ax=ax1, color='lightblue')\n", + "ax1.set_title('Distribution of Effect-Effect Correlations', fontsize=14)\n", + "ax1.set_ylabel('Spearman Correlation Coefficient', fontsize=12)\n", + "# Add a horizontal line at y=0 for reference\n", + "ax1.axhline(y=0, color='r', linestyle='--', alpha=0.7)\n", + "# Add text showing the number of regulators\n", + "ax1.text(0.05, 0.95, f'n = {len(results_df)}', transform=ax1.transAxes, \n", + " fontsize=12, verticalalignment='top',\n", + " bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))\n", + "\n", + "# Second boxplot: Effect-Pvalue Correlation Distribution\n", + "sns.boxplot(data=results_df, y='eff_pval_corr', ax=ax2, color='lightcoral')\n", + "ax2.set_title('Distribution of Effect-Pvalue Correlations', fontsize=14)\n", + "ax2.set_ylabel('Spearman Correlation Coefficient', fontsize=12)\n", + "# Add a horizontal line at y=0 for reference\n", + "ax2.axhline(y=0, color='r', linestyle='--', alpha=0.7)\n", + "# Add text showing the number of regulators\n", + "ax2.text(0.05, 0.95, f'n = {len(results_df)}', transform=ax2.transAxes, \n", + " fontsize=12, verticalalignment='top',\n", + " bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))\n", + "\n", + "# Adjust layout and display the plot\n", + "plt.tight_layout()\n", + "plt.show()\n", + "\n", + "# Optional: Print summary statistics\n", + "print(\"Effect-Effect Correlation Summary:\")\n", + "print(results_df['eff_eff_corr'].describe())\n", + "print(\"\\nEffect-Pvalue Correlation Summary:\")\n", + "print(results_df['eff_pval_corr'].describe())\n" + ] + }, + { + "cell_type": "markdown", + "id": "4becea1e", + "metadata": {}, + "source": [ + "## Check if all final_common regulators have other timepoints" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "2cb70685", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dataset 'time_0' contains 1037400 rows of data\n", + "Dataset 'time_5' contains 1025050 rows of data\n", + "Dataset 'time_10' contains 1018875 rows of data\n", + "Dataset 'time_15' contains 1012700 rows of data\n", + "Dataset 'time_20' contains 1006525 rows of data\n", + "Dataset 'time_30' contains 1031225 rows of data\n", + "Dataset 'time_45' contains 1018875 rows of data\n", + "Dataset 'time_60' contains 6175 rows of data\n", + "Dataset 'time_90' contains 1018875 rows of data\n", + "Dataset 'time_100' contains 12350 rows of data\n", + "Dataset 'time_120' contains 6175 rows of data\n", + "Dataset 'time_180' contains 6175 rows of data\n", + "Dataset 'time_290' contains 6175 rows of data\n" + ] + } + ], + "source": [ + "hackett_2020_copy = HfQueryAPI(repo_id=\"BrentLab/hackett_2020\")\n", + "\n", + "hackett_2020_copy.set_sql_filter(\n", + " \"hackett_2020\",\n", + " f\"\"\"\n", + " mechanism = 'ZEV' \n", + " AND restriction = 'P' \n", + " \"\"\")\n", + "\n", + "hackett_data_copy = hackett_2020_copy.query(\"SELECT * FROM hackett_2020\", \"hackett_2020\")\n", + "\n", + "# Get all unique time points\n", + "unique_times = hackett_data_copy['time'].unique()\n", + "unique_times.sort() # Sort in chronological order\n", + "\n", + "# Create a dictionary to store datasets split by time point\n", + "time_datasets = {}\n", + "\n", + "# Split data by each time point\n", + "for time_point in unique_times:\n", + " # Filter data for the current time point\n", + " time_data = hackett_data_copy[hackett_data_copy['time'] == time_point].copy()\n", + " \n", + " # Create meaningful key names using time point markers\n", + " key_name = f\"time_{int(time_point)}\" # Convert float to integer for naming\n", + " \n", + " # Store the dataset in the dictionary\n", + " time_datasets[key_name] = time_data\n", + " \n", + " # Print information about each dataset\n", + " print(f\"Dataset '{key_name}' contains {len(time_data)} rows of data\")" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "8654baec", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Time 0: Contains 145/145 regulators (100.0%) - All present? True\n", + "Time 5: Contains 144/145 regulators (99.3%) - All present? False\n", + "Time 10: Contains 144/145 regulators (99.3%) - All present? False\n", + "Time 15: Contains 145/145 regulators (100.0%) - All present? True\n", + "Time 20: Contains 142/145 regulators (97.9%) - All present? False\n", + "Time 30: Contains 144/145 regulators (99.3%) - All present? False\n", + "Time 45: Contains 143/145 regulators (98.6%) - All present? False\n", + "Time 60: Contains 1/145 regulators (0.7%) - All present? False\n", + "Time 90: Contains 143/145 regulators (98.6%) - All present? False\n", + "Time 100: Contains 2/145 regulators (1.4%) - All present? False\n", + "Time 120: Contains 1/145 regulators (0.7%) - All present? False\n", + "Time 180: Contains 1/145 regulators (0.7%) - All present? False\n", + "Time 290: Contains 1/145 regulators (0.7%) - All present? False\n" + ] + } + ], + "source": [ + "# Check the percentage of final_common regulators present in each time-split dataset\n", + "\n", + "# Initialize a list to store the results\n", + "coverage_results = []\n", + "\n", + "# Get the total number of regulators in final_common for percentage calculation\n", + "total_regulators = len(final_common)\n", + "\n", + "# Iterate through each time-split dataset\n", + "for time_key, dataset in time_datasets.items():\n", + " # Extract the time point from the key name\n", + " time_point = time_key.replace('time_', '')\n", + " \n", + " # Get unique regulators in this time dataset\n", + " regulators_in_dataset = set(dataset['regulator_symbol'].unique())\n", + " \n", + " # Find the intersection with final_common regulators\n", + " common_regulators = regulators_in_dataset.intersection(set(final_common))\n", + " \n", + " # Calculate the percentage of final_common regulators present\n", + " percentage_present = (len(common_regulators) / total_regulators) * 100\n", + " \n", + " # Check if all final_common regulators are present\n", + " contains_all = len(common_regulators) == total_regulators\n", + " \n", + " # Store the result\n", + " coverage_results.append({\n", + " 'time_point': time_point,\n", + " 'contains_all_shared_regulators': contains_all,\n", + " 'percentage_present': percentage_present,\n", + " 'regulators_present': len(common_regulators),\n", + " 'total_regulators': total_regulators\n", + " })\n", + " \n", + " # Print the result for this time point\n", + " print(f\"Time {time_point}: Contains {len(common_regulators)}/{total_regulators} regulators ({percentage_present:.1f}%) - All present? {contains_all}\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "tfbpapi-py3.11", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 2faf66d099dc9c579eecba81eba0ae1dd8723844 Mon Sep 17 00:00:00 2001 From: chasem Date: Wed, 3 Dec 2025 17:56:22 -0600 Subject: [PATCH 11/49] documenting factor levels of experimental condition features --- docs/huggingface_datacard.md | 272 +++++++++++++++++++++++++---------- 1 file changed, 193 insertions(+), 79 deletions(-) diff --git a/docs/huggingface_datacard.md b/docs/huggingface_datacard.md index c41b062..3437112 100644 --- a/docs/huggingface_datacard.md +++ b/docs/huggingface_datacard.md @@ -1,47 +1,30 @@ # HuggingFace Dataset Card Format -This document describes the expected YAML metadata format for HuggingFace dataset repositories used with the tfbpapi package. The metadata is defined in the repository's README.md file frontmatter and provides structured information about the dataset configuration and contents. - -## Required Top-Level Fields - -### Basic Metadata -```yaml -license: mit # Dataset license -language: # Languages (usually 'en' for scientific data) -- en -tags: # Descriptive tags for discoverability -- biology -- genomics -- yeast -- transcription-factors -pretty_name: "Dataset Name" # Human-readable dataset name -size_categories: # Dataset size category -- 100K Date: Wed, 3 Dec 2025 17:59:43 -0600 Subject: [PATCH 12/49] documenting factor levels of experimental condition features --- docs/huggingface_datacard.md | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/docs/huggingface_datacard.md b/docs/huggingface_datacard.md index 3437112..2cd0b7e 100644 --- a/docs/huggingface_datacard.md +++ b/docs/huggingface_datacard.md @@ -117,6 +117,7 @@ configs: role: quantitative_measure ``` + ## Feature Definitions Each config must include detailed feature definitions in `dataset_info.features`: @@ -130,11 +131,34 @@ dataset_info: role: "target_identifier" # Optional: semantic role of the feature ``` -### Common Data Types -- `string`: Text data, identifiers, categories -- `int64`: Integer values -- `float64`: Decimal numbers, measurements -- `int32`, `float32`: For large datasets where precision/memory matters +### Categorical Fields with Value Definitions + +For fields with `role: experimental_condition` that contain categorical values, you can +provide structured definitions for each value using the `definitions` field. This allows +machine-parsable specification of what each condition value means experimentally: + +```yaml +- name: condition + dtype: + class_label: + names: ["standard", "heat_shock"] + role: experimental_condition + description: Growth condition of the sample + definitions: + standard: + growth_conditions: + media: + name: synthetic_complete + heat_shock: + growth_conditions: + temperature_celsius: 37 + duration_minutes: 10 +``` + +Each key in `definitions` must correspond to a possible value in the field. +The structure under each value provides experimental parameters specific to that +condition using the same nested format as `experimental_conditions` at config or +top level. ### Naming Conventions @@ -166,8 +190,10 @@ for annotated_features datasets. The following roles are recognized by tfbpapi: - **quantitative_measure**: Quantitative measurements (e.g., binding_score, expression_level, p_value) - **experimental_condition**: Experimental conditions or metadata + (can include `definitions` field for categorical values) - **genomic_coordinate**: Positional information (chr, start, end, pos) + ## Partitioned Datasets For large datasets (eg most genome_map datasets), use partitioning: From ebf74a6998655349c6f1f7fa71f2b318df36f724 Mon Sep 17 00:00:00 2001 From: chasem Date: Tue, 9 Dec 2025 13:25:41 -0600 Subject: [PATCH 13/49] adding metadata_manager --- docs/huggingface_datacard.md | 468 +++- .../metadata_explorer_tutorial.ipynb | 619 +++++ tfbpapi/datainfo/__init__.py | 2 + tfbpapi/datainfo/datacard.py | 50 + tfbpapi/datainfo/metadata_manager.py | 623 +++++ tfbpapi/datainfo/models.py | 356 ++- tfbpapi/tests/datainfo/example_datacards.py | 509 +++++ .../huggingface_collection_datacards.txt | 2020 +++++++++++++++++ tfbpapi/tests/datainfo/test_datacard.py | 1 - .../tests/datainfo/test_datacard_parsing.py | 169 ++ .../tests/datainfo/test_metadata_manager.py | 319 +++ tfbpapi/tests/datainfo/test_models.py | 332 ++- tfbpapi/tests/datainfo/test_real_datacards.py | 697 ++++++ 13 files changed, 6113 insertions(+), 52 deletions(-) create mode 100644 docs/tutorials/metadata_explorer_tutorial.ipynb create mode 100644 tfbpapi/datainfo/metadata_manager.py create mode 100644 tfbpapi/tests/datainfo/example_datacards.py create mode 100644 tfbpapi/tests/datainfo/huggingface_collection_datacards.txt create mode 100644 tfbpapi/tests/datainfo/test_datacard_parsing.py create mode 100644 tfbpapi/tests/datainfo/test_metadata_manager.py create mode 100644 tfbpapi/tests/datainfo/test_real_datacards.py diff --git a/docs/huggingface_datacard.md b/docs/huggingface_datacard.md index 2cd0b7e..de8dd41 100644 --- a/docs/huggingface_datacard.md +++ b/docs/huggingface_datacard.md @@ -68,7 +68,6 @@ Quality control metrics and assessments ## Experimental Conditions Experimental conditions can be specified in three ways: - 1. **Top-level** `experimental_conditions`: Apply to all configs in the repository. Use when experimental parameters are common across all datasets. This will occur at the same level as `configs` @@ -76,59 +75,403 @@ Experimental conditions can be specified in three ways: ([dataset](#dataset)). Use when certain datasets have experimental parameters that are not shared by all other datasets in the [repository](#huggingface-repo), but are common to all [samples](#sample) within that dataset. -3. **Field-level** with [`role: experimental_condition`](#feature-roles): For +3. **Field-level** with `role: experimental_condition` ([feature-roles](#feature-roles)): For per-sample or per-measurement variation in experimental conditions stored as data columns. This is specified in the - [`dataset_info.features`](#feature-definitions) section of a config. + `dataset_info.features` ([feature-definitions](#feature-definitions)) section of a config. **Example of all three methods:** ```yaml # Top-level experimental conditions (apply to all configs) experimental_conditions: - growth_media: "YPD" - temperature: "30C" - + environmental_conditions: + temperature_celsius: 30 configs: # The overexpression_data dataset has an additional experimental condition that is -# specific to this dataset and applied to all samples (`strain_background`) in addition -# to a field (`mechanism`) that varies per sample and is identified by the -# role `experimental_condition`. +# specific to this dataset and applied to all samples (strain_background) in addition +# to a field (mechanism) that varies per sample and is identified by the +# role experimental_condition. - config_name: overexpression_data description: TF overexpression perturbation data dataset_type: annotated_features experimental_conditions: strain_background: "BY4741" data_files: - - split: train - path: overexpression.parquet + - split: train + path: overexpression.parquet dataset_info: features: - - name: time - dtype: float - description: Time point in minutes - role: experimental_condition - - name: mechanism - dtype: string - description: Induction mechanism (GEV or ZEV) - role: experimental_condition - - name: log2_ratio - dtype: float - description: Log2 fold change - role: quantitative_measure + - name: time + dtype: float + description: Time point in minutes + role: experimental_condition + - name: mechanism + dtype: string + description: Induction mechanism (GEV or ZEV) + role: experimental_condition + - name: log2_ratio + dtype: float + description: Log2 fold change + role: quantitative_measure +``` + +### Environmental Conditions + +Environmental conditions are nested under `experimental_conditions` and describe the +physical and chemical environment in which samples were cultivated. This includes +growth media specifications, temperature, cultivation method, and other environmental +parameters. + +#### Core Environmental Fields + +The following fields are supported within `environmental_conditions`: + +- **temperature_celsius** (float): Growth temperature in Celsius +- **cultivation_method** (string): Method of cultivation (e.g., "liquid_culture", "plate", "chemostat") +- **growth_phase_at_harvest** (object): Growth phase information (see [Growth Phase Specification](#growth-phase-specification)) +- **media** (object): Growth medium specification (see [Growth Media Specification](#growth-media-specification)) +- **chemical_treatment** (object): Chemical treatment information (see [Chemical Treatments](#chemical-treatments)) +- **drug_treatment** (object): Drug treatment (same structure as chemical_treatment) +- **heat_treatment** (object): Heat treatment specification +- **temperature_shift** (object): Temperature shift for heat shock experiments (see [Temperature Shifts](#temperature-shifts)) +- **induction** (object): Induction system for expression experiments (see [Induction Systems](#induction-systems)) +- **incubation_duration_hours** (float): Total incubation duration in hours +- **incubation_duration_minutes** (int): Total incubation duration in minutes +- **description** (string): Additional descriptive information + +#### Growth Phase Specification + +Growth phase at harvest can be specified using: + +```yaml +growth_phase_at_harvest: + stage: mid_log_phase # or: early_log_phase, late_log_phase, stationary_phase, etc. + od600: 0.6 # Optical density at 600nm + od600_tolerance: 0.1 # Optional: measurement tolerance + description: "Additional context" +``` + +**Note**: The field `phase` is accepted as an alias for `stage` for backward compatibility. + +Recognized stage values: +- `mid_log_phase`, `early_log_phase`, `late_log_phase` +- `stationary_phase`, `early_stationary_phase`, `overnight_stationary_phase` +- `mid_log`, `early_log`, `late_log`, `exponential_phase` + +#### Chemical Treatments + +Chemical treatments (including drugs) are specified with: + +```yaml +chemical_treatment: + compound: rapamycin # Chemical compound name + concentration_percent: 0.001 # Concentration as percentage + duration_minutes: 20 # Treatment duration in minutes + duration_hours: 0.33 # Alternative: duration in hours + target_pH: 4.0 # Optional: target pH for pH adjustments + description: "TOR inhibition" # Optional: additional context +``` + +The `drug_treatment` field uses the same structure and is interchangeable with `chemical_treatment`. + +#### Temperature Shifts + +For heat shock and temperature shift experiments: + +```yaml +temperature_shift: + initial_temperature_celsius: 30 + temperature_shift_celsius: 37 + temperature_shift_duration_minutes: 45 + description: "Heat shock treatment" +``` + +#### Induction Systems + +For expression induction systems (e.g., GAL, estradiol-inducible): + +```yaml +induction: + inducer: + compound: D-galactose + concentration_percent: 2 + duration_hours: 3 + duration_minutes: 180 # Alternative to duration_hours + description: "GAL promoter induction" +``` + +#### Growth Media Specification + +The `media` field specifies the growth medium used in an experiment. Media is nested +under `environmental_conditions` and can be specified at the top-level, config-level, +or within field-level definitions depending on whether they are common across all +datasets, specific to a config, or vary per sample. + +##### Media Structure + +Each media specification has the following required structure: + +```yaml +experimental_conditions: + environmental_conditions: + media: + name: string # Canonical or descriptive media name (see below) + carbon_source: # Required + - compound: string # Chemical compound name + concentration_percent: float + nitrogen_source: # Required + - compound: string # Chemical compound name + concentration_percent: float + phosphate_source: # Optional + - compound: string + concentration_percent: float + additives: # Optional: for additional media components + - compound: string # e.g., butanol for filamentation + concentration_percent: float + description: string +``` + +Both `carbon_source` and `nitrogen_source` are **required fields**. Each can contain +one or more compound entries with their respective concentrations specified as a +percentage. + +**Handling Unknown Values**: When a component is truly unknown or not reported in the +source publication, omit the field or use `null`. Do NOT use the string `"unspecified"` as +a compound name, as this will generate validation warnings. + +##### Canonical Media Names + +Three base media types are standardized across the collection: + +1. **minimal** + - Minimal defined medium with inorganic nitrogen source + - Typically used for targeted nutrient deprivation studies + - Example: Hackett 2020 + +2. **synthetic_complete** + - Defined medium with amino acid supplements + - Contains yeast nitrogen base (without amino acids) plus amino acid dropout mix + - Used as baseline in many stress studies + - Example: Kemmeren 2014, Mahendrawada 2025, Harbison 2004 + +3. **YPD** (yeast peptone dextrose) + - Rich, complex medium with yeast extract and peptone as nitrogen sources + - Used as standard rich-media baseline condition + - Also known as: yeast_peptone_dextrose, yeast_extract_peptone (context-dependent) + - Example: Hu Reimand 2010, Harbison 2004, Rossi 2021, Barkai compendium + +**Descriptive Media Names**: While the canonical names above are preferred, descriptive +variations that provide additional specificity are acceptable (e.g., +`synthetic_complete_dextrose`, `selective_medium`, `synthetic_complete_minus_uracil`). +The key requirement is that the actual media composition be fully specified in the +`carbon_source`, `nitrogen_source`, and optional `phosphate_source` and `additives` fields. + +##### Specifying Carbon and Nitrogen Sources + +###### Carbon Sources + +Common carbon sources in yeast media: + +```yaml +carbon_source: + - compound: D-glucose + concentration_percent: 2 +``` + +Typical values: D-glucose, D-galactose, D-raffinose, D-dextrose + +Concentrations are expressed as a percentage (e.g., 2% glucose). + +###### Nitrogen Sources + +Nitrogen sources vary by media type: + +**For synthetic_complete and minimal media:** +```yaml +nitrogen_source: + - compound: yeast_nitrogen_base + concentration_g_per_l: 6.71 + specifications: + - without_amino_acids + - without_ammonium_sulfate + - compound: ammonium_sulfate + # if specified differently in the paper, add the authors' + # specification in a comment + concentration_percent: 0.5 + - compound: amino_acid_dropout_mix + # lastname et al 2025 used 20 g/L + concentration_percent: 2 +``` + +**For YPD media:** +```yaml +nitrogen_source: + - compound: yeast_extract + concentration_percent: 1 + - compound: peptone + concentration_percent: 2 ``` +##### Media Examples + +**Minimal Medium** +```yaml +experimental_conditions: + environmental_conditions: + media: + name: minimal + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: ammonium_sulfate + concentration_g_per_l: 5 +``` + +**Synthetic Complete (Base)** +```yaml +experimental_conditions: + environmental_conditions: + media: + name: synthetic_complete + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_nitrogen_base + # lastname et al 2025 used 6.71 g/L + concentration_percent: 0.671 + specifications: + - without_amino_acids + - without_ammonium_sulfate + - compound: ammonium_sulfate + # lastname et al 2025 used 5 g/L + concentration_percent: 0.5 + - compound: amino_acid_dropout_mix + # lastname et al 2025 used 2 g/L + concentration_percent: 0.2 +``` + +**Synthetic Complete with Alternative Carbon Source** +```yaml +experimental_conditions: + environmental_conditions: + media: + name: synthetic_complete + carbon_source: + - compound: D-galactose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_nitrogen_base + # lastname et al 2025 used 6.71 g/L + concentration_percent: 0.671 + specifications: + - without_amino_acids + - without_ammonium_sulfate + - compound: ammonium_sulfate + # lastname et al 2025 used 5 g/L + concentration_percent: 0.5 + - compound: amino_acid_dropout_mix + # lastname et al 2025 used 2 g/L + concentration_percent: 0.2 +``` + +**YPD** +```yaml +experimental_conditions: + environmental_conditions: + media: + name: YPD + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: 1 + - compound: peptone + concentration_percent: 2 +``` + +##### Selective/Dropout Media Variants + +When a dataset uses a selective medium with specific amino acid or nutrient dropouts, +specify this using the base `synthetic_complete` name and adjust the `nitrogen_source` +to reflect the modified composition: + +```yaml +experimental_conditions: + environmental_conditions: + media: + name: synthetic_complete + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_nitrogen_base + # lastname et al 2025 used 6.71 g/L + concentration_percent: 0.671 + specifications: + - without_amino_acids + - without_ammonium_sulfate + - compound: ammonium_sulfate + # lastname et al 2025 used 5 g/L + concentration_percent: 0.5 + - compound: amino_acid_dropout_mix + # lastname et al 2025 used 2 g/L + concentration_percent: 0.2 + specifications: + - minus_uracil + - minus_histidine + - minus_leucine +``` + +##### Media in Field-Level Definitions + +When media varies per sample and is captured in a categorical field with definitions: + +```yaml +- name: condition + dtype: + class_label: + names: ["standard", "galactose"] + role: experimental_condition + definitions: + standard: + environmental_conditions: + media: + name: YPD + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: 1 + - compound: peptone + concentration_percent: 2 + galactose: + environmental_conditions: + media: + name: YPD + carbon_source: + - compound: D-galactose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: 1 + - compound: peptone + concentration_percent: 2 +``` ## Feature Definitions Each config must include detailed feature definitions in `dataset_info.features`: - ```yaml dataset_info: features: - - name: field_name # Column name in the data - dtype: string # Data type (string, int64, float64, etc.) - description: "Detailed description of what this field contains" - role: "target_identifier" # Optional: semantic role of the feature + - name: field_name # Column name in the data + dtype: string # Data type (string, int64, float64, etc.) + description: "Detailed description of what this field contains" + role: "target_identifier" # Optional: semantic role of the feature ``` ### Categorical Fields with Value Definitions @@ -136,7 +479,6 @@ dataset_info: For fields with `role: experimental_condition` that contain categorical values, you can provide structured definitions for each value using the `definitions` field. This allows machine-parsable specification of what each condition value means experimentally: - ```yaml - name: condition dtype: @@ -146,11 +488,24 @@ machine-parsable specification of what each condition value means experimentally description: Growth condition of the sample definitions: standard: - growth_conditions: + environmental_conditions: media: name: synthetic_complete + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_nitrogen_base + concentration_g_per_l: 6.71 + specifications: + - without_amino_acids + - without_ammonium_sulfate + - compound: ammonium_sulfate + concentration_g_per_l: 5 + - compound: amino_acid_dropout_mix + concentration_g_per_l: 2 heat_shock: - growth_conditions: + environmental_conditions: temperature_celsius: 37 duration_minutes: 10 ``` @@ -180,18 +535,53 @@ Unless otherwise noted, assume that coordinates are 0-based, half-open intervals ## Feature Roles -The optional role field provides semantic meaning to features, especially useful +The optional `role` field provides semantic meaning to features, especially useful for annotated_features datasets. The following roles are recognized by tfbpapi: -- **target_identifier**: Identifies target genes/features (e.g., target_locus_tag, - target_symbol) -- **regulator_identifier**: Identifies regulatory factors (e.g., regulator_locus_tag, - regulator_symbol) -- **quantitative_measure**: Quantitative measurements (e.g., binding_score, - expression_level, p_value) +- **target_identifier**: Identifies target genes/features (e.g., `target_locus_tag`, + `target_symbol`) +- **regulator_identifier**: Identifies regulatory factors (e.g., `regulator_locus_tag`, + `regulator_symbol`) +- **quantitative_measure**: Quantitative measurements (e.g., `binding_score`, + `expression_level`, `p_value`) - **experimental_condition**: Experimental conditions or metadata (can include `definitions` field for categorical values) -- **genomic_coordinate**: Positional information (chr, start, end, pos) +- **genomic_coordinate**: Positional information (`chr`, `start`, `end`, `pos`) + +**Validation**: Only these specific role values are accepted. Other values (e.g., `"identifier"`) +will cause validation errors. + +## Strain Background and Definitions + +The `strain_background` field can appear in two locations: + +1. **Top-level or config-level** `experimental_conditions`: + ```yaml + experimental_conditions: + strain_background: + genotype: BY4741 + mating_type: MATa + markers: [his3Δ1, leu2Δ0, met15Δ0, ura3Δ0] + ``` + +2. **Within field-level definitions** for condition-specific strain information: + ```yaml + - name: heat_shock + dtype: + class_label: + names: ["control", "treated"] + role: experimental_condition + definitions: + treated: + environmental_conditions: + temperature_celsius: 37 + strain_background: + genotype: W303_derivative + description: "Heat-sensitive strain" + ``` + +The `strain_background` field accepts flexible structure as a dictionary to accommodate +varying levels of detail about strain information. ## Partitioned Datasets diff --git a/docs/tutorials/metadata_explorer_tutorial.ipynb b/docs/tutorials/metadata_explorer_tutorial.ipynb new file mode 100644 index 0000000..0ba9cda --- /dev/null +++ b/docs/tutorials/metadata_explorer_tutorial.ipynb @@ -0,0 +1,619 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Cross-Dataset Metadata Exploration Tutorial\n", + "\n", + "This tutorial demonstrates how to use the `MetadataManager` to extract and query metadata across multiple HuggingFace datasets from different repositories.\n", + "\n", + "## Overview\n", + "\n", + "The `MetadataManager` enables:\n", + "- **Cross-dataset queries**: Filter metadata across MULTIPLE datasets from MULTIPLE repos\n", + "- **Low memory usage**: DuckDB temporary views over parquet files (no data loading)\n", + "- **Role-based alignment**: Heterogeneous schemas aligned by field roles (regulator_identifier, experimental_condition, etc.)\n", + "- **Searchable conditions**: Factor level names + flattened definitions for filtering\n", + "\n", + "## Datasets Used\n", + "\n", + "We'll explore two transcription factor binding datasets:\n", + "\n", + "1. **harbison_2004**: ChIP-chip TF binding across 14 environmental conditions (YPD, RAPA, HEAT, etc.)\n", + "2. **hackett_2020**: TF overexpression with nutrient limitation conditions (P, N, M restrictions)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chase/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" + ] + } + ], + "source": [ + "import pandas as pd\n", + "from tfbpapi.datainfo import DataCard, MetadataManager" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part 1: Exploring Individual Dataset Metadata Schemas\n", + "\n", + "Before cross-dataset queries, let's understand each dataset's metadata structure using `DataCard.extract_metadata_schema()`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Harbison 2004 Metadata Schema" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "# Load harbison_2004 datacard\nharbison_card = DataCard(repo_id=\"BrentLab/harbison_2004\")\n\n# Extract metadata schema for the harbison_2004 config\nharbison_schema = harbison_card.extract_metadata_schema(\"harbison_2004\")\n\nprint(\"Harbison 2004 Metadata Schema\")\nprint(\"=\" * 60)\nprint(f\"Regulator Fields: {harbison_schema['regulator_fields']}\")\nprint(f\"Target Fields: {harbison_schema['target_fields']}\")\nprint(f\"Condition Fields: {harbison_schema['condition_fields']}\")\n\n# Check for repo-level conditions\nif harbison_schema.get('top_level_conditions'):\n print(\"\\nRepo-Level Conditions (apply to ALL samples):\")\n top_cond = harbison_schema['top_level_conditions']\n if top_cond.strain_background:\n print(f\" - Strain background: {top_cond.strain_background}\")\n if top_cond.environmental_conditions:\n print(\" - Environmental conditions defined\")\nelse:\n print(\"\\nRepo-Level Conditions: None\")\n\n# Check for config-level conditions\nif harbison_schema.get('config_level_conditions'):\n print(\"\\nConfig-Level Conditions:\")\n print(\" (Defined for this config)\")\nelse:\n print(\"\\nConfig-Level Conditions: None\")\n\n# Show field-level condition definitions\nprint(f\"\\nField-Level Conditions (vary by sample):\")\nprint(f\" - condition: {len(harbison_schema['condition_definitions'].get('condition', {}))} conditions defined\")\n\n# Show first few condition definitions\nif 'condition' in harbison_schema['condition_definitions']:\n print(\"\\n Sample Conditions:\")\n for cond_name in list(harbison_schema['condition_definitions']['condition'].keys())[:3]:\n print(f\" - {cond_name}\")" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Hackett 2020 Metadata Schema" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "# Load hackett_2020 datacard\nhackett_card = DataCard(repo_id=\"BrentLab/hackett_2020\")\n\n# Extract metadata schema\nhackett_schema = hackett_card.extract_metadata_schema(\"hackett_2020\")\n\nprint(\"Hackett 2020 Metadata Schema\")\nprint(\"=\" * 60)\nprint(f\"Regulator Fields: {hackett_schema['regulator_fields']}\")\nprint(f\"Target Fields: {hackett_schema['target_fields']}\")\nprint(f\"Condition Fields: {hackett_schema['condition_fields']}\")\n\n# Show repo-level (top-level) experimental conditions\nif hackett_schema.get('top_level_conditions'):\n print(\"\\nRepo-Level Conditions (apply to ALL samples):\")\n top_cond = hackett_schema['top_level_conditions']\n \n # Check for structured environmental_conditions\n if top_cond.environmental_conditions:\n env = top_cond.environmental_conditions\n if env.temperature_celsius is not None:\n print(f\" - Temperature: {env.temperature_celsius} C\")\n if env.cultivation_method:\n print(f\" - Cultivation: {env.cultivation_method}\")\n if env.media:\n print(f\" - Media: {env.media.name}\")\n if env.media.carbon_source:\n for cs in env.media.carbon_source:\n print(f\" - Carbon source: {cs.compound} @ {cs.concentration_percent}%\")\n \n # Check for extra fields (alternate structure)\n if hasattr(top_cond, 'model_extra') and top_cond.model_extra:\n for key, value in top_cond.model_extra.items():\n print(f\" - {key}: {value}\")\n\n# Show config-level conditions\nif hackett_schema.get('config_level_conditions'):\n print(\"\\nConfig-Level Conditions:\")\n print(\" (Conditions defined for this config)\")\nelse:\n print(\"\\nConfig-Level Conditions: None\")\n\n# Show field-level condition definitions\nif hackett_schema['condition_definitions']:\n print(\"\\nField-Level Conditions (vary by sample):\")\n for field_name, definitions in hackett_schema['condition_definitions'].items():\n print(f\" - {field_name}: {len(definitions)} levels defined\")\n print(f\" Levels: {list(definitions.keys())}\")" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Comparing Schemas\n", + "\n", + "Notice:\n", + "- **Common fields**: Both have `regulator_locus_tag` and `regulator_symbol` (aligned by `role=regulator_identifier`)\n", + "- **Different condition fields**: harbison has `condition`, hackett has `time`, `mechanism`, `restriction`\n", + "- **Three-level condition hierarchy**:\n", + " - **Repo-level**: Conditions in `dataset_card.experimental_conditions` apply to ALL configs/samples\n", + " - Example: hackett_2020 has temperature (30°C), cultivation method (chemostat), base media (minimal)\n", + " - **Config-level**: Conditions in `config.experimental_conditions` apply to all samples in that config\n", + " - Example: harbison_2004 has strain background defined at config level\n", + " - **Field-level**: Conditions in field `definitions` vary per sample (factor levels)\n", + " - Example: harbison_2004's \"condition\" field with YPD, RAPA, HEAT definitions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part 2: Cross-Dataset Metadata with MetadataManager\n", + "\n", + "Now let's use `MetadataManager` to query metadata across both datasets." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Initialize MetadataManager" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MetadataManager initialized\n", + "Cache enabled: False\n", + "Registered datasets: 0\n" + ] + } + ], + "source": [ + "# Create metadata manager (session-only by default, no caching)\n", + "mgr = MetadataManager()\n", + "\n", + "print(\"MetadataManager initialized\")\n", + "print(f\"Cache enabled: {mgr._cache_enabled}\")\n", + "print(f\"Registered datasets: {len(mgr._registered_datasets)}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Register Datasets\n", + "\n", + "Register both datasets for cross-dataset querying. The manager will:\n", + "1. Load each DataCard\n", + "2. Extract metadata schema\n", + "3. Create DuckDB temporary views\n", + "4. Build unified view with column alignment" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Registering BrentLab/harbison_2004...\n", + "Registering BrentLab/hackett_2020...\n", + "\n", + "Registration complete!\n", + "Total registered datasets: 2\n", + "Active configs: [('BrentLab/harbison_2004', 'harbison_2004'), ('BrentLab/hackett_2020', 'hackett_2020')]\n" + ] + } + ], + "source": [ + "# Register harbison_2004\n", + "print(\"Registering BrentLab/harbison_2004...\")\n", + "mgr.register(\"BrentLab/harbison_2004\")\n", + "\n", + "# Register hackett_2020 \n", + "print(\"Registering BrentLab/hackett_2020...\")\n", + "mgr.register(\"BrentLab/hackett_2020\")\n", + "\n", + "print(\"\\nRegistration complete!\")\n", + "print(f\"Total registered datasets: {len(mgr._registered_datasets)}\")\n", + "print(f\"Active configs: {mgr.get_active_configs()}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### View Summary\n", + "\n", + "Get an overview of registered datasets and their configs." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Registered Datasets Summary\n", + "============================================================\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "dataset", + "rawType": "object", + "type": "string" + }, + { + "name": "config_name", + "rawType": "object", + "type": "string" + }, + { + "name": "view_name", + "rawType": "object", + "type": "string" + } + ], + "ref": "e30d951e-7e84-475f-be44-50ac556c86b1", + "rows": [ + [ + "0", + "BrentLab/harbison_2004", + "harbison_2004", + "BrentLab_harbison_2004_harbison_2004_metadata" + ], + [ + "1", + "BrentLab/hackett_2020", + "hackett_2020", + "BrentLab_hackett_2020_hackett_2020_metadata" + ] + ], + "shape": { + "columns": 3, + "rows": 2 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
datasetconfig_nameview_name
0BrentLab/harbison_2004harbison_2004BrentLab_harbison_2004_harbison_2004_metadata
1BrentLab/hackett_2020hackett_2020BrentLab_hackett_2020_hackett_2020_metadata
\n", + "
" + ], + "text/plain": [ + " dataset config_name \\\n", + "0 BrentLab/harbison_2004 harbison_2004 \n", + "1 BrentLab/hackett_2020 hackett_2020 \n", + "\n", + " view_name \n", + "0 BrentLab_harbison_2004_harbison_2004_metadata \n", + "1 BrentLab_hackett_2020_hackett_2020_metadata " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Get summary of registered datasets\n", + "summary = mgr.get_summary()\n", + "\n", + "print(\"Registered Datasets Summary\")\n", + "print(\"=\" * 60)\n", + "display(summary)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "print(\"Three-Level Experimental Condition Hierarchy\")\nprint(\"=\" * 80)\n\n# Show hackett_2020 as example\nprint(\"\\nExample: hackett_2020 dataset\")\nprint(\"-\" * 80)\n\n# 1. Repo-level (applies to ALL samples)\nprint(\"\\n[1] REPO-LEVEL (DatasetCard.experimental_conditions)\")\nprint(\" Applies to: ALL configs and ALL samples in the repository\")\nif hackett_schema.get('top_level_conditions'):\n top_cond = hackett_schema['top_level_conditions']\n \n # Check structured environmental_conditions\n if top_cond.environmental_conditions:\n env = top_cond.environmental_conditions\n print(f\" - temperature_celsius: {env.temperature_celsius}\")\n print(f\" - cultivation_method: {env.cultivation_method}\")\n print(f\" - media.name: {env.media.name}\")\n print(f\" - media.carbon_source: {env.media.carbon_source[0].compound} @ {env.media.carbon_source[0].concentration_percent}%\")\n \n # Check extra fields (alternate structure used by some datacards)\n if hasattr(top_cond, 'model_extra') and top_cond.model_extra:\n for key, value in top_cond.model_extra.items():\n print(f\" - {key}: {value}\")\n\n# 2. Config-level (applies to samples in this config)\nprint(\"\\n[2] CONFIG-LEVEL (DatasetConfig.experimental_conditions)\")\nprint(\" Applies to: All samples in the 'hackett_2020' config\")\nif hackett_schema.get('config_level_conditions'):\n print(\" - (Specific conditions defined)\")\nelse:\n print(\" - (None - all repo-level conditions inherited)\")\n\n# 3. Field-level (varies by sample)\nprint(\"\\n[3] FIELD-LEVEL (FeatureInfo.definitions)\")\nprint(\" Applies to: Individual samples based on field value\")\nprint(\" - restriction field: 3 levels (P, N, M)\")\nprint(\" - P: Phosphate limitation\")\nprint(\" - N: Nitrogen limitation\")\nprint(\" - M: Magnesium limitation\")\nprint(\" - time field: Various time points (30.0, 60.0, etc.)\")\n\nprint(\"\\n\" + \"=\" * 80)\nprint(\"KEY CONCEPT: Hierarchy Merging\")\nprint(\"-\" * 80)\nprint(\"When MetadataManager creates metadata tables, it merges all three levels:\")\nprint(\" - Repo-level conditions -> columns for ALL samples (temperature: 30C, etc.)\")\nprint(\" - Config-level conditions -> columns for config samples (override repo-level if present)\")\nprint(\" - Field-level conditions -> vary by sample (restriction: P/N/M)\")\nprint(\"\\nPrecedence: Field-level > Config-level > Repo-level\")\nprint(\"\\nResult: Each sample gets columns from all applicable levels!\")" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part 2.5: Understanding the Three-Level Condition Hierarchy\n", + "\n", + "Experimental conditions can be specified at three different levels in the hierarchy. Let's examine how this works with a concrete example from hackett_2020." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part 3: Querying Across Datasets (Placeholder)\n", + "\n", + "**Note**: The current implementation has a placeholder for actual parquet file loading from HuggingFace. The examples below show the intended usage once parquet integration is complete.\n", + "\n", + "### Example Query 1: Find all regulators across datasets" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "# Example query (will work once parquet loading is implemented)\n# \n# regulators_query = \"\"\"\n# SELECT DISTINCT \n# dataset,\n# config_name,\n# regulator_symbol,\n# regulator_locus_tag\n# FROM unified_metadata\n# WHERE regulator_symbol != 'unspecified'\n# ORDER BY dataset, regulator_symbol\n# \"\"\"\n# \n# regulators_df = mgr.query(regulators_query)\n# print(f\"\\nFound {len(regulators_df)} unique regulators across datasets\")\n# display(regulators_df.head(10))\n\nprint(\"WARNING: Parquet file loading not yet implemented\")\nprint(\" This query will work once HuggingFace parquet integration is added\")" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example Query 2: Filter by specific regulator" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "# Example: Find GLN3 samples across both datasets\n#\n# gln3_query = \"\"\"\n# SELECT \n# dataset,\n# sample_id,\n# regulator_symbol,\n# condition,\n# time,\n# restriction,\n# growth_media,\n# components\n# FROM unified_metadata\n# WHERE regulator_symbol = 'GLN3'\n# \"\"\"\n# \n# gln3_samples = mgr.query(gln3_query)\n# print(f\"\\nGLN3 samples: {len(gln3_samples)}\")\n# display(gln3_samples)\n\nprint(\"WARNING: Parquet file loading not yet implemented\")" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example Query 3: Search by media components" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "# Example: Find samples with D-glucose at 2%\n#\n# glucose_query = \"\"\"\n# SELECT \n# dataset,\n# COUNT(*) as sample_count,\n# growth_media\n# FROM unified_metadata\n# WHERE components LIKE '%carbon_source:D-glucose@2%'\n# GROUP BY dataset, growth_media\n# \"\"\"\n# \n# glucose_samples = mgr.query(glucose_query)\n# print(\"\\nSamples with D-glucose at 2%:\")\n# display(glucose_samples)\n\nprint(\"WARNING: Parquet file loading not yet implemented\")\nprint(\" This demonstrates searching by specific media components with concentrations\")" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part 4: Understanding Metadata Flattening\n", + "\n", + "The MetadataManager flattens condition definitions into searchable fields using known separators." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Separator Conventions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "from tfbpapi.datainfo.metadata_manager import COMPONENT_SEPARATORS\n\nprint(\"\\nComponent Separator Conventions\")\nprint(\"=\" * 60)\nfor key, sep in COMPONENT_SEPARATORS.items():\n print(f\" {key:20s} '{sep}'\")\n \nprint(\"\\nUsage Examples:\")\nprint(\" growth_media: 'YPD' (simple name)\")\nprint(\" components: 'carbon_source:D-glucose@2%|nitrogen_source:yeast_extract@1%;peptone@2%'\")\nprint(\"\\n Breakdown:\")\nprint(\" - ':' separates type from value (carbon_source:D-glucose)\")\nprint(\" - '@' separates value from concentration (D-glucose@2%)\")\nprint(\" - ';' separates multiple compounds of same type (yeast_extract@1%;peptone@2%)\")\nprint(\" - '|' separates different component types (carbon | nitrogen)\")" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example: Flattening a Condition Definition\n", + "\n", + "Let's manually flatten a condition definition to see how it works." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original YPD Condition Definition\n", + "============================================================\n", + "Description: Rich media baseline condition\n", + "\n", + "Flattened Representation\n", + "============================================================\n", + "growth_media: 'unspecified'\n", + "components: ''\n", + "\n", + "This format enables SQL searches like:\n", + " WHERE growth_media = 'YPD'\n", + " WHERE components LIKE '%carbon_source:D-glucose@2%'\n", + " WHERE components LIKE '%nitrogen_source:yeast_extract%'\n" + ] + } + ], + "source": [ + "# Get YPD condition definition from harbison_2004\n", + "ypd_definition = harbison_schema['condition_definitions']['condition']['YPD']\n", + "\n", + "print(\"Original YPD Condition Definition\")\n", + "print(\"=\" * 60)\n", + "print(f\"Description: {ypd_definition.get('description', 'N/A')}\")\n", + "\n", + "if 'environmental_conditions' in ypd_definition:\n", + " env = ypd_definition['environmental_conditions']\n", + " print(f\"\\nTemperature: {env.get('temperature_celsius', 'N/A')}°C\")\n", + " \n", + " if 'media' in env:\n", + " media = env['media']\n", + " print(f\"Media Name: {media.get('name', 'N/A')}\")\n", + " print(f\"\\nCarbon Source:\")\n", + " for cs in media.get('carbon_source', []):\n", + " print(f\" - {cs.get('compound', cs)}: {cs.get('concentration_percent', 'N/A')}%\")\n", + " print(f\"\\nNitrogen Source:\")\n", + " for ns in media.get('nitrogen_source', []):\n", + " print(f\" - {ns.get('compound', ns)}: {ns.get('concentration_percent', 'N/A')}%\")\n", + "\n", + "# Flatten using MetadataManager method\n", + "flattened = mgr._flatten_condition_definition(ypd_definition)\n", + "\n", + "print(\"\\nFlattened Representation\")\n", + "print(\"=\" * 60)\n", + "print(f\"growth_media: '{flattened['growth_media']}'\")\n", + "print(f\"components: '{flattened['components']}'\")\n", + "\n", + "print(\"\\nThis format enables SQL searches like:\")\n", + "print(\" WHERE growth_media = 'YPD'\")\n", + "print(\" WHERE components LIKE '%carbon_source:D-glucose@2%'\")\n", + "print(\" WHERE components LIKE '%nitrogen_source:yeast_extract%'\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part 5: Schema Heterogeneity Handling\n", + "\n", + "Different datasets have different metadata fields. MetadataManager handles this by:\n", + "1. **Role-based alignment**: Fields grouped by semantic role (regulator_identifier, experimental_condition)\n", + "2. **Column defaulting**: Missing columns default to \"unspecified\" in unified view\n", + "3. **UNION ALL**: All views combined with column alignment" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Schema Heterogeneity Example\n", + "============================================================\n", + "\n", + "Harbison 2004 has:\n", + " - condition field (14 levels: YPD, RAPA, HEAT, etc.)\n", + " - NO time, mechanism, restriction fields\n", + "\n", + "Hackett 2020 has:\n", + " - time field (time points in minutes)\n", + " - mechanism field (GEV, ZEV induction systems)\n", + " - restriction field (P, N, M nutrient limitations)\n", + " - NO condition field\n", + "\n", + "In unified view:\n", + " - Harbison rows: condition='YPD', time='unspecified', restriction='unspecified'\n", + " - Hackett rows: condition='unspecified', time=30.0, restriction='P'\n", + "\n", + " Both can be queried together:\n", + " SELECT * FROM unified_metadata WHERE regulator_symbol = 'GLN3'\n", + " Returns samples from BOTH datasets!\n" + ] + } + ], + "source": [ + "print(\"Schema Heterogeneity Example\")\n", + "print(\"=\" * 60)\n", + "\n", + "print(\"\\nHarbison 2004 has:\")\n", + "print(f\" - condition field (14 levels: YPD, RAPA, HEAT, etc.)\")\n", + "print(f\" - NO time, mechanism, restriction fields\")\n", + "\n", + "print(\"\\nHackett 2020 has:\")\n", + "print(f\" - time field (time points in minutes)\")\n", + "print(f\" - mechanism field (GEV, ZEV induction systems)\")\n", + "print(f\" - restriction field (P, N, M nutrient limitations)\")\n", + "print(f\" - NO condition field\")\n", + "\n", + "print(\"\\nIn unified view:\")\n", + "print(\" - Harbison rows: condition='YPD', time='unspecified', restriction='unspecified'\")\n", + "print(\" - Hackett rows: condition='unspecified', time=30.0, restriction='P'\")\n", + "print(\"\\n Both can be queried together:\")\n", + "print(\" SELECT * FROM unified_metadata WHERE regulator_symbol = 'GLN3'\")\n", + "print(\" Returns samples from BOTH datasets!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part 6: Cleanup and Next Steps" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Unregister Datasets\n", + "\n", + "You can remove datasets from the manager when done." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "## Summary\n\nThis tutorial demonstrated:\n\n- **Metadata schema extraction** - Using `DataCard.extract_metadata_schema()` to understand field roles\n\n- **Three-level condition hierarchy** - Repo-level, config-level, and field-level experimental conditions\n\n- **Cross-dataset registration** - Registering multiple datasets in `MetadataManager`\n\n- **Condition flattening** - How definitions are flattened into searchable fields with known separators\n\n- **Schema heterogeneity** - How different metadata schemas are aligned by role with \"unspecified\" defaults\n\n- **Future: SQL queries** - Once parquet loading is implemented, full cross-dataset SQL queries will work\n\n### Key Takeaways\n\n1. **Role-based alignment**: Fields are grouped by semantic role (regulator_identifier, experimental_condition), not by name\n2. **Three-level hierarchy**: Conditions can be specified at repo-level (all samples), config-level (config samples), or field-level (individual samples)\n3. **Hierarchy merging**: MetadataManager merges all three levels with precedence: field > config > repo\n4. **Structured separators**: Media components use `:`, `@`, `;`, `|` for predictable parsing\n5. **Low memory**: DuckDB temp views mean no data loading until you query\n6. **Session-only default**: No caching by default (set `cache=True` if needed)\n7. **Dataset + config uniqueness**: Samples identified by (dataset, config_name, sample_id) tuple\n\n### Understanding the Condition Hierarchy\n\nThe three-level hierarchy allows flexible experimental condition specification:\n\n- **Repo-level**: Common conditions shared by all experiments (e.g., standard temperature, base strain)\n- **Config-level**: Conditions specific to a dataset configuration (e.g., growth phase for a time course)\n- **Field-level**: Sample-specific conditions that vary within an experiment (e.g., different media, treatments)\n\nThis design enables:\n- Efficient specification without repetition\n- Clear inheritance and overriding semantics\n- Consistent querying across heterogeneous datasets" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Next Steps\n", + "\n", + "Once parquet loading is implemented, you'll be able to:\n", + "\n", + "1. **Filter by regulators**: Find all samples for specific TFs across datasets\n", + "2. **Search by conditions**: Query by media, temperature, treatments, etc.\n", + "3. **Cross-dataset analysis**: Compare experimental designs across studies\n", + "4. **Metadata-driven data loading**: Use `get_active_configs()` to load actual expression/binding data\n", + "\n", + "### Integration with HfQueryAPI (Future)\n", + "\n", + "The intended workflow:\n", + "```python\n", + "# 1. Filter metadata across datasets\n", + "mgr = MetadataManager()\n", + "mgr.register(\"BrentLab/harbison_2004\")\n", + "mgr.register(\"BrentLab/hackett_2020\")\n", + "mgr.filter_by_regulator([\"GLN3\", \"GCN4\"])\n", + "mgr.filter_by_conditions(growth_media=\"YPD\")\n", + "\n", + "# 2. Get active configs\n", + "active_configs = mgr.get_active_configs()\n", + "# Returns: [('BrentLab/harbison_2004', 'harbison_2004'), ...]\n", + "\n", + "# 3. Load actual data (requires HfQueryAPI refactor)\n", + "# TBD - design after MetadataManager is complete\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "## Summary\n\nThis tutorial demonstrated:\n\n- **Metadata schema extraction** - Using `DataCard.extract_metadata_schema()` to understand field roles\n\n- **Cross-dataset registration** - Registering multiple datasets in `MetadataManager`\n\n- **Condition flattening** - How definitions are flattened into searchable fields with known separators\n\n- **Schema heterogeneity** - How different metadata schemas are aligned by role with \"unspecified\" defaults\n\n- **Future: SQL queries** - Once parquet loading is implemented, full cross-dataset SQL queries will work\n\n### Key Takeaways\n\n1. **Role-based alignment**: Fields are grouped by semantic role (regulator_identifier, experimental_condition), not by name\n2. **Structured separators**: Media components use `:`, `@`, `;`, `|` for predictable parsing\n3. **Low memory**: DuckDB temp views mean no data loading until you query\n4. **Session-only default**: No caching by default (set `cache=True` if needed)\n5. **Dataset + config uniqueness**: Samples identified by (dataset, config_name, sample_id) tuple" + } + ], + "metadata": { + "kernelspec": { + "display_name": "tfbpapi-py3.11", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} \ No newline at end of file diff --git a/tfbpapi/datainfo/__init__.py b/tfbpapi/datainfo/__init__.py index 88452eb..11f3387 100644 --- a/tfbpapi/datainfo/__init__.py +++ b/tfbpapi/datainfo/__init__.py @@ -1,5 +1,6 @@ from .datacard import DataCard from .fetchers import HfDataCardFetcher, HfRepoStructureFetcher, HfSizeInfoFetcher +from .metadata_manager import MetadataManager from .models import ( DatasetCard, DatasetConfig, @@ -14,6 +15,7 @@ "HfDataCardFetcher", "HfRepoStructureFetcher", "HfSizeInfoFetcher", + "MetadataManager", "DatasetCard", "DatasetConfig", "DatasetType", diff --git a/tfbpapi/datainfo/datacard.py b/tfbpapi/datainfo/datacard.py index 5bcbd42..279b548 100644 --- a/tfbpapi/datainfo/datacard.py +++ b/tfbpapi/datainfo/datacard.py @@ -12,6 +12,7 @@ DatasetConfig, DatasetType, ExtractedMetadata, + FieldRole, MetadataRelationship, ) @@ -328,6 +329,55 @@ def explore_config(self, config_name: str) -> dict[str, Any]: return info + def extract_metadata_schema(self, config_name: str) -> dict[str, Any]: + """ + Extract metadata schema for a configuration. + + Returns structured schema identifying metadata fields by their role, + suitable for metadata extraction by MetadataManager. Includes + experimental conditions at all three hierarchy levels: + - Top-level (repo-wide, applies to all configs/samples) + - Config-level (applies to this config's samples) + - Field-level (varies per sample, from field definitions) + + :param config_name: Configuration name to extract schema for + :return: Dict with field lists grouped by role and condition definitions + :raises DataCardError: If configuration not found + + """ + config = self.get_config(config_name) + if not config: + raise DataCardError(f"Configuration '{config_name}' not found") + + schema: dict[str, Any] = { + "regulator_fields": [], # Fields with role=regulator_identifier + "target_fields": [], # Fields with role=target_identifier + "condition_fields": [], # Fields with role=experimental_condition + "condition_definitions": {}, # Field-level condition details + "top_level_conditions": None, # Repo-level conditions + "config_level_conditions": None, # Config-level conditions + } + + for feature in config.dataset_info.features: + if feature.role == FieldRole.REGULATOR_IDENTIFIER: + schema["regulator_fields"].append(feature.name) + elif feature.role == FieldRole.TARGET_IDENTIFIER: + schema["target_fields"].append(feature.name) + elif feature.role == FieldRole.EXPERIMENTAL_CONDITION: + schema["condition_fields"].append(feature.name) + if feature.definitions: + schema["condition_definitions"][feature.name] = feature.definitions + + # Add top-level conditions (applies to all configs/samples) + if self.dataset_card.experimental_conditions: + schema["top_level_conditions"] = self.dataset_card.experimental_conditions + + # Add config-level conditions (applies to this config's samples) + if config.experimental_conditions: + schema["config_level_conditions"] = config.experimental_conditions + + return schema + def summary(self) -> str: """Get a human-readable summary of the dataset.""" card = self.dataset_card diff --git a/tfbpapi/datainfo/metadata_manager.py b/tfbpapi/datainfo/metadata_manager.py new file mode 100644 index 0000000..fabdd9a --- /dev/null +++ b/tfbpapi/datainfo/metadata_manager.py @@ -0,0 +1,623 @@ +"""MetadataManager for cross-dataset metadata filtering and querying.""" + +import logging +import re +from pathlib import Path +from typing import Any, Optional + +import duckdb +import pandas as pd + +from ..errors import DataCardError +from .datacard import DataCard +from .models import FieldRole + +# Separator conventions for concatenated fields +COMPONENT_SEPARATORS = { + "type_value": ":", # Separates component type from value + "value_conc": "@", # Separates value from concentration + "components": ";", # Separates multiple components of same type + "types": "|", # Separates different component types (future use) +} + + +class MetadataManager: + """ + Cross-dataset metadata query manager using DuckDB temp views. + + Stores metadata field values as-is from parquet files, plus optionally expands field + definitions into searchable columns for experimental conditions. + + """ + + def __init__(self, cache_dir: Path | None = None, cache: bool = False): + """ + Initialize MetadataManager with optional caching. + + :param cache_dir: Directory for cached metadata (if cache=True) + :param cache: If True, persist metadata extractions. Default: False + + """ + self.logger = logging.getLogger(self.__class__.__name__) + self._conn = duckdb.connect(":memory:") + self._registered_datasets: dict[str, DataCard] = {} + self._view_names: dict[tuple[str, str], str] = {} # (repo_id, config) -> view + self._cache_dir = cache_dir + self._cache_enabled = cache + + def register(self, repo_id: str, config_names: list[str] | None = None) -> None: + """ + Register a dataset for cross-dataset queries. + + :param repo_id: HuggingFace repository identifier + :param config_names: Optional list of config names to register. If None, + registers all configs. + + """ + self.logger.info(f"Registering dataset: {repo_id}") + + # Load datacard + if repo_id not in self._registered_datasets: + datacard = DataCard(repo_id=repo_id) + self._registered_datasets[repo_id] = datacard + else: + datacard = self._registered_datasets[repo_id] + + # Determine which configs to register + if config_names is None: + configs_to_register = [c.config_name for c in datacard.configs] + else: + configs_to_register = config_names + + # Register each config + for config_name in configs_to_register: + self._register_config(datacard, config_name) + + # Recreate unified view + self._create_unified_view() + + def _register_config(self, datacard: DataCard, config_name: str) -> None: + """ + Register a single config for querying. + + :param datacard: DataCard instance + :param config_name: Configuration name to register + + """ + self.logger.debug(f"Registering config: {datacard.repo_id}/{config_name}") + + # Extract metadata schema + try: + schema = datacard.extract_metadata_schema(config_name) + except DataCardError as e: + self.logger.error(f"Failed to extract schema for {config_name}: {e}") + raise + + # Extract metadata from parquet file + try: + metadata_df = self._extract_metadata_from_config( + datacard, config_name, schema + ) + except Exception as e: + self.logger.error(f"Failed to extract metadata for {config_name}: {e}") + raise + + # Create temp view + view_name = self._sanitize_view_name(datacard.repo_id, config_name) + self._create_temp_view(datacard.repo_id, config_name, metadata_df, view_name) + self._view_names[(datacard.repo_id, config_name)] = view_name + + def unregister(self, repo_id: str) -> None: + """ + Remove dataset from cross-dataset queries. + + :param repo_id: HuggingFace repository identifier to unregister + + """ + if repo_id not in self._registered_datasets: + self.logger.warning(f"Dataset {repo_id} not registered") + return + + # Remove all views for this repo + views_to_remove = [ + (rid, cfg) for rid, cfg in self._view_names.keys() if rid == repo_id + ] + + for rid, cfg in views_to_remove: + view_name = self._view_names.pop((rid, cfg)) + try: + self._conn.execute(f"DROP VIEW IF EXISTS {view_name}") + except Exception as e: + self.logger.warning(f"Failed to drop view {view_name}: {e}") + + # Remove datacard + del self._registered_datasets[repo_id] + + # Recreate unified view + self._create_unified_view() + + def _extract_metadata_from_config( + self, datacard: DataCard, config_name: str, schema: dict[str, Any] + ) -> pd.DataFrame: + """ + Extract metadata from parquet file for a config. + + Processes experimental conditions at all three hierarchy levels: + - Top-level (repo-wide): Applies to ALL samples + - Config-level: Applies to all samples in this config + - Field-level: Varies per sample (from field definitions) + + Hierarchy merging: field-level > config-level > top-level + + :param datacard: DataCard instance + :param config_name: Configuration name + :param schema: Metadata schema from extract_metadata_schema() + + :return: DataFrame with metadata field values + expanded columns + + """ + # Get config to access data files + config = datacard.get_config(config_name) + if not config: + raise DataCardError(f"Configuration '{config_name}' not found") + + # Determine which fields to extract + metadata_fields = ( + schema["regulator_fields"] + + schema["target_fields"] + + schema["condition_fields"] + ) + + # Add sample_id if not already in list + if "sample_id" not in metadata_fields: + metadata_fields.append("sample_id") + + # TODO: For MVP, we need actual parquet file path from HuggingFace + # This requires integration with HF datasets or direct file access + # For now, create a minimal placeholder that will work with tests + + # Create placeholder DataFrame with required structure + metadata_df = pd.DataFrame() + + # Add computed columns + metadata_df["dataset"] = datacard.repo_id + metadata_df["config_name"] = config_name + + # Add metadata fields as empty for now + for field in metadata_fields: + metadata_df[field] = "unspecified" + + # Process experimental conditions in hierarchy order (low to high priority) + # Start with top-level (repo-wide) conditions + if schema.get("top_level_conditions"): + top_conditions = self._flatten_experimental_conditions( + schema["top_level_conditions"] + ) + for col_name, col_value in top_conditions.items(): + metadata_df[col_name] = col_value + + # Apply config-level conditions (overrides top-level) + if schema.get("config_level_conditions"): + config_conditions = self._flatten_experimental_conditions( + schema["config_level_conditions"] + ) + for col_name, col_value in config_conditions.items(): + metadata_df[col_name] = col_value + + # Expand field-level condition definitions (overrides config/top-level) + if schema["condition_definitions"]: + for field_name, definitions in schema["condition_definitions"].items(): + # Add expanded columns for each condition + metadata_df["growth_media"] = "unspecified" + metadata_df["components"] = "unspecified" + + return metadata_df + + def _flatten_experimental_conditions(self, exp_conditions: Any) -> dict[str, Any]: + """ + Flatten ExperimentalConditions object into column name/value pairs. + + Processes both environmental_conditions and strain_background from repo-level or + config-level ExperimentalConditions. Also handles extra fields stored in + model_extra (Pydantic's extra='allow'). + + :param exp_conditions: ExperimentalConditions instance + :return: Dict mapping column names to values + + """ + flattened: dict[str, Any] = {} + + # Handle strain_background + if ( + hasattr(exp_conditions, "strain_background") + and exp_conditions.strain_background + ): + strain = exp_conditions.strain_background + if isinstance(strain, str): + flattened["strain_background"] = strain + elif isinstance(strain, dict): + flattened["strain_background"] = strain.get("name", "unspecified") + + # Handle extra fields (for models with extra='allow') + # Some datacards store conditions directly as extra fields + if hasattr(exp_conditions, "model_extra") and exp_conditions.model_extra: + for key, value in exp_conditions.model_extra.items(): + # Store extra fields with their original names + flattened[key] = value + + # Handle environmental_conditions + if ( + hasattr(exp_conditions, "environmental_conditions") + and exp_conditions.environmental_conditions + ): + env = exp_conditions.environmental_conditions + + # Temperature + if ( + hasattr(env, "temperature_celsius") + and env.temperature_celsius is not None + ): + flattened["temperature_celsius"] = env.temperature_celsius + + # Cultivation method + if hasattr(env, "cultivation_method") and env.cultivation_method: + flattened["cultivation_method"] = env.cultivation_method + + # Media information + if hasattr(env, "media") and env.media: + media = env.media + if hasattr(media, "name") and media.name: + flattened["growth_media"] = media.name + + # Build components string from media composition + components = [] + + # Carbon source + if hasattr(media, "carbon_source") and media.carbon_source: + for compound in media.carbon_source: + if hasattr(compound, "compound"): + components.append( + self._format_compound( + "carbon_source", compound.model_dump() + ) + ) + + # Nitrogen source + if hasattr(media, "nitrogen_source") and media.nitrogen_source: + for compound in media.nitrogen_source: + if hasattr(compound, "compound"): + components.append( + self._format_compound( + "nitrogen_source", compound.model_dump() + ) + ) + + # Phosphate source + if hasattr(media, "phosphate_source") and media.phosphate_source: + for compound in media.phosphate_source: + if hasattr(compound, "compound"): + components.append( + self._format_compound( + "phosphate_source", compound.model_dump() + ) + ) + + # Additives + if hasattr(media, "additives") and media.additives: + for additive in media.additives: + if hasattr(additive, "name"): + components.append(f"additive:{additive.name}") + + if components: + flattened["components"] = "|".join(components) + + # Growth phase + if hasattr(env, "growth_phase") and env.growth_phase: + gp = env.growth_phase + if hasattr(gp, "stage") and gp.stage: + flattened["growth_stage"] = gp.stage + if hasattr(gp, "od600") and gp.od600 is not None: + flattened["od600"] = gp.od600 + + # Chemical treatments + if hasattr(env, "chemical_treatments") and env.chemical_treatments: + treatments = [] + for treatment in env.chemical_treatments: + if hasattr(treatment, "compound") and treatment.compound: + treatments.append(treatment.compound) + if treatments: + flattened["chemical_treatments"] = ";".join(treatments) + + # Drug treatments + if hasattr(env, "drug_treatments") and env.drug_treatments: + drugs = [] + for drug in env.drug_treatments: + if hasattr(drug, "compound") and drug.compound: + drugs.append(drug.compound) + if drugs: + flattened["drug_treatments"] = ";".join(drugs) + + # Heat treatment + if hasattr(env, "heat_treatment") and env.heat_treatment: + ht = env.heat_treatment + if ( + hasattr(ht, "temperature_celsius") + and ht.temperature_celsius is not None + ): + flattened["heat_treatment_temp"] = ht.temperature_celsius + + # Induction + if hasattr(env, "induction") and env.induction: + ind = env.induction + if hasattr(ind, "system") and ind.system: + flattened["induction_system"] = ind.system + + return flattened + + def _flatten_condition_definition( + self, definition: dict[str, Any] + ) -> dict[str, str]: + """ + Flatten a single condition definition into searchable fields. + + :param definition: Condition definition dict (e.g., YPD definition) + :return: Dict with flattened fields (growth_media, components) + + """ + flattened: dict[str, str] = { + "growth_media": "unspecified", + "components": "", + } + + # Extract environmental conditions if present + if "environmental_conditions" in definition: + env_conds = definition["environmental_conditions"] + + # Extract media information + if "media" in env_conds: + media = env_conds["media"] + if isinstance(media, dict): + # Extract media name + if "name" in media: + flattened["growth_media"] = media["name"] + + # Build components string + components = [] + + # Extract carbon source + if "carbon_source" in media: + carbon = media["carbon_source"] + if isinstance(carbon, list): + for compound in carbon: + components.append( + self._format_compound("carbon_source", compound) + ) + elif isinstance(carbon, dict): + components.append( + self._format_compound("carbon_source", carbon) + ) + + # Extract nitrogen source + if "nitrogen_source" in media: + nitrogen = media["nitrogen_source"] + if isinstance(nitrogen, list): + for compound in nitrogen: + components.append( + self._format_compound("nitrogen_source", compound) + ) + elif isinstance(nitrogen, dict): + components.append( + self._format_compound("nitrogen_source", compound) + ) + + # Extract phosphate source + if "phosphate_source" in media: + phosphate = media["phosphate_source"] + if isinstance(phosphate, list): + for compound in phosphate: + components.append( + self._format_compound("phosphate_source", compound) + ) + elif isinstance(phosphate, dict): + components.append( + self._format_compound("phosphate_source", compound) + ) + + # Join components + if components: + flattened["components"] = "|".join(components) + + return flattened + + def _format_compound(self, component_type: str, compound: dict[str, Any]) -> str: + """ + Format a compound dict into searchable string. + + :param component_type: Type of component (carbon_source, nitrogen_source, etc.) + :paramcompound: Compound info dict with name and concentration + :return: Formatted string (e.g., "carbon_source:D-glucose@2%") + + """ + if isinstance(compound, str): + return f"{component_type}:{compound}" + + name = compound.get("name", "unknown") + result = f"{component_type}:{name}" + + # Add concentration if present + if "concentration_percent" in compound: + result += f"@{compound['concentration_percent']}%" + elif "concentration_g_per_l" in compound: + result += f"@{compound['concentration_g_per_l']}g/L" + elif "concentration_molar" in compound: + result += f"@{compound['concentration_molar']}M" + + return result + + def _create_temp_view( + self, repo_id: str, config_name: str, metadata_df: pd.DataFrame, view_name: str + ) -> None: + """ + Create DuckDB temp view from metadata DataFrame. + + :param repo_id: Repository identifier + :param config_name: Configuration name + :param metadata_df: Metadata DataFrame + :param view_name: Sanitized view name + + """ + try: + # Register DataFrame as temp view in DuckDB + self._conn.register(view_name, metadata_df) + self.logger.debug(f"Created temp view: {view_name}") + except Exception as e: + self.logger.error(f"Failed to create view {view_name}: {e}") + raise + + def _sanitize_view_name(self, repo_id: str, config_name: str) -> str: + """ + Create a sanitized view name from repo_id and config_name. + + :param repo_id: Repository identifier + :param config_name: Configuration name + :return: Sanitized view name safe for SQL + + """ + # Replace non-alphanumeric with underscores + safe_repo = re.sub(r"[^a-zA-Z0-9]+", "_", repo_id) + safe_config = re.sub(r"[^a-zA-Z0-9]+", "_", config_name) + return f"{safe_repo}_{safe_config}_metadata" + + def _create_unified_view(self) -> None: + """Create unified_metadata view combining all registered datasets.""" + if not self._view_names: + self.logger.debug("No views registered, skipping unified view creation") + return + + # Drop existing unified view if present + try: + self._conn.execute("DROP VIEW IF EXISTS unified_metadata") + except Exception: + pass + + # Get all column names across all views + all_columns = set() + for view_name in self._view_names.values(): + try: + cols = ( + self._conn.execute(f"DESCRIBE {view_name}") + .fetchdf()["column_name"] + .tolist() + ) + all_columns.update(cols) + except Exception as e: + self.logger.warning(f"Failed to get columns from {view_name}: {e}") + + if not all_columns: + self.logger.warning("No columns found in registered views") + return + + # Build UNION ALL query with column alignment + union_queries = [] + for view_name in self._view_names.values(): + # Get columns in this view + view_cols = ( + self._conn.execute(f"DESCRIBE {view_name}") + .fetchdf()["column_name"] + .tolist() + ) + + # Build SELECT with coalesce for missing columns + select_parts = [] + for col in sorted(all_columns): + if col in view_cols: + select_parts.append(f'"{col}"') + else: + select_parts.append(f"'unspecified' AS \"{col}\"") + + union_queries.append(f"SELECT {', '.join(select_parts)} FROM {view_name}") + + # Create unified view + unified_query = " UNION ALL ".join(union_queries) + try: + self._conn.execute(f"CREATE VIEW unified_metadata AS {unified_query}") + self.logger.debug( + f"Created unified view from {len(self._view_names)} views with {len(all_columns)} columns" + ) + except Exception as e: + self.logger.error(f"Failed to create unified view: {e}") + raise + + def query(self, sql: str) -> pd.DataFrame: + """ + Execute SQL query across all registered datasets. + + :param sql: SQL query string to execute + :return: Query results as pandas DataFrame + + """ + try: + result = self._conn.execute(sql).fetchdf() + return result + except Exception as e: + self.logger.error(f"Query failed: {e}") + raise + + def filter_by_regulator(self, regulators: list[str]) -> "MetadataManager": + """ + Filter metadata to specific regulators. + + :param regulators: List of regulator symbols to filter by + :return: Self for method chaining + + """ + # TODO: Implement filtering logic + # This will be implemented in Phase 3 + raise NotImplementedError("filter_by_regulator not yet implemented") + + def filter_by_conditions(self, **kwargs: Any) -> "MetadataManager": + """ + Filter by experimental conditions. + + :param kwargs: Condition filters (e.g., media="YPD", temperature=30) + :return: Self for method chaining + + """ + # TODO: Implement filtering logic + # This will be implemented in Phase 3 + raise NotImplementedError("filter_by_conditions not yet implemented") + + def get_active_configs(self) -> list[tuple[str, str]]: + """ + Get (repo_id, config_name) pairs that match active filters. + + :return: List of (repo_id, config_name) tuples + + """ + # TODO: Implement with filter support + # For now, return all registered configs + return list(self._view_names.keys()) + + def get_summary(self) -> pd.DataFrame: + """ + Get summary stats for registered datasets. + + :return: DataFrame with summary statistics + + """ + if not self._view_names: + return pd.DataFrame() + + # TODO: Implement summary statistics + # This will be implemented in Phase 3 + summary_data = [] + for (repo_id, config_name), view_name in self._view_names.items(): + summary_data.append( + { + "dataset": repo_id, + "config_name": config_name, + "view_name": view_name, + } + ) + + return pd.DataFrame(summary_data) diff --git a/tfbpapi/datainfo/models.py b/tfbpapi/datainfo/models.py index b152248..ce5441f 100644 --- a/tfbpapi/datainfo/models.py +++ b/tfbpapi/datainfo/models.py @@ -1,9 +1,10 @@ """Pydantic models for dataset card validation.""" +import warnings from enum import Enum -from typing import Any, Dict, List, Optional, Set, Union +from typing import Any -from pydantic import BaseModel, ConfigDict, Field, field_validator +from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator class DatasetType(str, Enum): @@ -13,6 +14,317 @@ class DatasetType(str, Enum): ANNOTATED_FEATURES = "annotated_features" GENOME_MAP = "genome_map" METADATA = "metadata" + QC_DATA = "qc_data" + + +class FieldRole(str, Enum): + """Valid role values for feature fields.""" + + REGULATOR_IDENTIFIER = "regulator_identifier" + TARGET_IDENTIFIER = "target_identifier" + QUANTITATIVE_MEASURE = "quantitative_measure" + EXPERIMENTAL_CONDITION = "experimental_condition" + GENOMIC_COORDINATE = "genomic_coordinate" + + +class GrowthStage(str, Enum): + """Common growth stages.""" + + MID_LOG_PHASE = "mid_log_phase" + EARLY_LOG_PHASE = "early_log_phase" + LATE_LOG_PHASE = "late_log_phase" + STATIONARY_PHASE = "stationary_phase" + EARLY_STATIONARY_PHASE = "early_stationary_phase" + OVERNIGHT_STATIONARY_PHASE = "overnight_stationary_phase" + MID_LOG = "mid_log" + EARLY_LOG = "early_log" + LATE_LOG = "late_log" + EXPONENTIAL_PHASE = "exponential_phase" + + +class CompoundInfo(BaseModel): + """Information about a chemical compound in media.""" + + compound: str = Field(..., description="Chemical compound name") + concentration_percent: float | None = Field( + default=None, description="Concentration as percentage (w/v)" + ) + concentration_g_per_l: float | None = Field( + default=None, description="Concentration in grams per liter" + ) + concentration_molar: float | None = Field( + default=None, description="Concentration in molar (M)" + ) + specifications: list[str] | None = Field( + default=None, + description="Additional specifications (e.g., 'without_amino_acids')", + ) + + +class MediaAdditiveInfo(BaseModel): + """Information about media additives (e.g., butanol for filamentation).""" + + compound: str = Field(..., description="Additive compound name") + concentration_percent: float | None = Field( + default=None, description="Concentration as percentage (w/v)" + ) + description: str | None = Field( + default=None, description="Additional context about the additive" + ) + + +class MediaInfo(BaseModel): + """Growth media specification.""" + + name: str = Field( + ..., + description="Canonical or descriptive media name (minimal, synthetic_complete, YPD, etc.)", + ) + carbon_source: list[CompoundInfo] = Field( + ..., description="Carbon source compounds and concentrations" + ) + nitrogen_source: list[CompoundInfo] = Field( + ..., description="Nitrogen source compounds and concentrations" + ) + phosphate_source: list[CompoundInfo] | None = Field( + default=None, description="Phosphate source compounds and concentrations" + ) + additives: list[MediaAdditiveInfo] | None = Field( + default=None, + description="Additional media components (e.g., butanol for filamentation)", + ) + + @field_validator("carbon_source", "nitrogen_source", mode="before") + @classmethod + def validate_compound_list(cls, v): + """Validate compound lists and handle 'unspecified' strings.""" + if v is None: + return [] + if isinstance(v, str): + if v == "unspecified": + warnings.warn( + "Compound source specified as string 'unspecified'. " + "Should be null/omitted or a structured list.", + UserWarning, + ) + return [] + # Try to parse as single compound + return [{"compound": v}] + return v + + +class GrowthPhaseInfo(BaseModel): + """Growth phase information at harvest.""" + + od600: float | None = Field( + default=None, description="Optical density at 600nm at harvest" + ) + od600_tolerance: float | None = Field( + default=None, description="Measurement tolerance for OD600" + ) + stage: str | None = Field( + default=None, description="Growth stage (preferred field name)" + ) + phase: str | None = Field( + default=None, + description="Growth stage (alias for 'stage' for backward compatibility)", + ) + description: str | None = Field( + default=None, description="Additional context about growth phase" + ) + + @field_validator("stage", "phase", mode="before") + @classmethod + def validate_stage(cls, v): + """Validate stage and warn if not a common value.""" + if v is None: + return None + # Get the list of known stages from the enum + known_stages = {stage.value for stage in GrowthStage} + if v not in known_stages: + warnings.warn( + f"Growth stage '{v}' not in recognized stages: {known_stages}", + UserWarning, + ) + return v + + @model_validator(mode="after") + def check_stage_phase_consistency(self): + """Ensure stage and phase are consistent if both provided.""" + if self.stage and self.phase and self.stage != self.phase: + raise ValueError( + f"Inconsistent growth phase: stage='{self.stage}' vs phase='{self.phase}'" + ) + return self + + +class ChemicalTreatmentInfo(BaseModel): + """Chemical treatment applied to cultures.""" + + compound: str = Field(..., description="Chemical compound name") + concentration_percent: float | None = Field( + default=None, description="Concentration as percentage" + ) + concentration_molar: float | None = Field( + default=None, description="Concentration in molar (M)" + ) + duration_minutes: int | None = Field( + default=None, description="Duration in minutes" + ) + duration_hours: float | None = Field(default=None, description="Duration in hours") + target_pH: float | None = Field( + default=None, description="Target pH for pH adjustments" + ) + description: str | None = Field( + default=None, description="Additional context about the treatment" + ) + + +class DrugTreatmentInfo(ChemicalTreatmentInfo): + """Drug treatment - same structure as chemical treatment.""" + + pass + + +class HeatTreatmentInfo(BaseModel): + """Heat treatment information.""" + + duration_minutes: int = Field( + ..., description="Duration of heat treatment in minutes" + ) + description: str | None = Field( + default=None, description="Additional description of treatment" + ) + + +class TemperatureShiftInfo(BaseModel): + """Temperature shift for heat shock experiments.""" + + initial_temperature_celsius: float = Field( + ..., description="Initial cultivation temperature in Celsius" + ) + temperature_shift_celsius: float = Field( + ..., description="Temperature after shift in Celsius" + ) + temperature_shift_duration_minutes: int | None = Field( + default=None, description="Duration of temperature shift in minutes" + ) + description: str | None = Field( + default=None, description="Additional context about the temperature shift" + ) + + +class InductionInfo(BaseModel): + """Induction information for expression systems.""" + + inducer: CompoundInfo = Field(..., description="Inducer compound and concentration") + duration_hours: float | None = Field( + default=None, description="Duration of induction in hours" + ) + duration_minutes: int | None = Field( + default=None, description="Duration of induction in minutes" + ) + description: str | None = Field( + default=None, description="Additional context about the induction" + ) + + +class EnvironmentalConditions(BaseModel): + """Environmental conditions for sample cultivation.""" + + temperature_celsius: float | None = Field( + default=None, description="Cultivation temperature in Celsius" + ) + cultivation_method: str | None = Field( + default=None, + description="Cultivation method (e.g., 'batch_culture', 'chemostat')", + ) + growth_phase_at_harvest: GrowthPhaseInfo | None = Field( + default=None, description="Growth phase at time of harvest" + ) + media: MediaInfo | None = Field( + default=None, description="Growth media specification" + ) + chemical_treatment: ChemicalTreatmentInfo | None = Field( + default=None, description="Chemical treatment applied" + ) + drug_treatment: DrugTreatmentInfo | None = Field( + default=None, + description="Drug treatment applied (same structure as chemical_treatment)", + ) + heat_treatment: HeatTreatmentInfo | None = Field( + default=None, description="Heat treatment applied" + ) + temperature_shift: TemperatureShiftInfo | None = Field( + default=None, description="Temperature shift for heat shock experiments" + ) + induction: InductionInfo | None = Field( + default=None, description="Induction system for expression experiments" + ) + incubation_duration_hours: float | None = Field( + default=None, description="Total incubation duration in hours" + ) + incubation_duration_minutes: int | None = Field( + default=None, description="Total incubation duration in minutes" + ) + description: str | None = Field( + default=None, description="Additional descriptive information" + ) + + model_config = ConfigDict(extra="allow") + + @model_validator(mode="after") + def warn_extra_fields(self): + """Warn about any extra fields not in the specification.""" + known_fields = { + "temperature_celsius", + "cultivation_method", + "growth_phase_at_harvest", + "media", + "chemical_treatment", + "drug_treatment", + "heat_treatment", + "temperature_shift", + "induction", + "incubation_duration_hours", + "incubation_duration_minutes", + "description", + } + extra = set(self.__dict__.keys()) - known_fields + if extra: + warnings.warn( + f"EnvironmentalConditions contains non-standard fields: {extra}. " + "Consider adding to specification.", + UserWarning, + ) + return self + + +class ExperimentalConditions(BaseModel): + """Experimental conditions including environmental and other parameters.""" + + environmental_conditions: EnvironmentalConditions | None = Field( + default=None, description="Environmental cultivation conditions" + ) + strain_background: str | dict[str, Any] | None = Field( + default=None, + description="Strain background information (string or flexible dict structure)", + ) + + model_config = ConfigDict(extra="allow") + + @model_validator(mode="after") + def warn_extra_fields(self): + """Warn about any extra fields not in the specification.""" + known_fields = {"environmental_conditions", "strain_background"} + extra = set(self.__dict__.keys()) - known_fields + if extra: + warnings.warn( + f"ExperimentalConditions contains non-standard fields: {extra}. " + "Consider adding to specification.", + UserWarning, + ) + return self class ClassLabelType(BaseModel): @@ -27,12 +339,19 @@ class FeatureInfo(BaseModel): name: str = Field(..., description="Column name in the data") dtype: str | dict[str, ClassLabelType] = Field( ..., - description="Data type (string, int64, float64, etc.) or categorical class labels", + description="Data type (string, int64, float64, etc.) " + "or categorical class labels", ) description: str = Field(..., description="Detailed description of the field") - role: str | None = Field( + role: FieldRole | None = Field( + default=None, + description="Semantic role of the feature (e.g., 'target_identifier', " + "'regulator_identifier', 'quantitative_measure')", + ) + definitions: dict[str, Any] | None = Field( default=None, - description="Semantic role of the feature (e.g., 'target_identifier', 'regulator_identifier', 'quantitative_measure')", + description="Definitions for categorical field values " + "with experimental conditions", ) @field_validator("dtype", mode="before") @@ -50,15 +369,18 @@ def validate_dtype(cls, v): return {"class_label": ClassLabelType(**class_label_data)} else: raise ValueError( - f"Invalid class_label structure: expected dict with 'names' key, got {class_label_data}" + "Invalid class_label structure: expected dict " + f"with 'names' key, got {class_label_data}" ) else: raise ValueError( - f"Unknown dtype structure: expected 'class_label' key in dict, got keys: {list(v.keys())}" + "Unknown dtype structure: expected 'class_label' key " + f"in dict, got keys: {list(v.keys())}" ) else: raise ValueError( - f"dtype must be a string or dict with class_label info, got {type(v)}: {v}" + "dtype must be a string or dict with " + f"class_label info, got {type(v)}: {v}" ) def get_dtype_summary(self) -> str: @@ -115,18 +437,22 @@ class DatasetConfig(BaseModel): metadata_fields: list[str] | None = Field( default=None, description="Fields for embedded metadata extraction" ) + experimental_conditions: ExperimentalConditions | None = Field( + default=None, description="Experimental conditions for this config" + ) data_files: list[DataFileInfo] = Field(..., description="Data file information") dataset_info: DatasetInfo = Field(..., description="Dataset structure information") @field_validator("applies_to") @classmethod def applies_to_only_for_metadata(cls, v, info): - """Validate that applies_to is only used for metadata configs.""" + """Validate that applies_to is only used for metadata or qc_data configs.""" if v is not None: dataset_type = info.data.get("dataset_type") - if dataset_type != DatasetType.METADATA: + if dataset_type not in (DatasetType.METADATA, DatasetType.QC_DATA): raise ValueError( - "applies_to field is only valid for metadata dataset types" + "applies_to field is only valid " + "for metadata and qc_data dataset types" ) return v @@ -157,6 +483,9 @@ class DatasetCard(BaseModel): """Complete dataset card model.""" configs: list[DatasetConfig] = Field(..., description="Dataset configurations") + experimental_conditions: ExperimentalConditions | None = Field( + default=None, description="Top-level experimental conditions for all configs" + ) license: str | None = Field(default=None, description="Dataset license") language: list[str] | None = Field(default=None, description="Dataset languages") tags: list[str] | None = Field(default=None, description="Descriptive tags") @@ -166,6 +495,11 @@ class DatasetCard(BaseModel): size_categories: list[str] | None = Field( default=None, description="Dataset size categories" ) + strain_information: dict[str, Any] | None = Field( + default=None, description="Strain background information" + ) + + model_config = ConfigDict(extra="allow") @field_validator("configs") @classmethod diff --git a/tfbpapi/tests/datainfo/example_datacards.py b/tfbpapi/tests/datainfo/example_datacards.py new file mode 100644 index 0000000..1d732de --- /dev/null +++ b/tfbpapi/tests/datainfo/example_datacards.py @@ -0,0 +1,509 @@ +""" +Three diverse datacard examples for testing datacard parsing and database construction. + +These examples capture different patterns of experimental condition specification: +1. Top-level conditions with field-level variations (minimal media) +2. Complex field-level definitions with multiple environmental conditions +3. Partitioned dataset with separate metadata configs using applies_to + +""" + +EXAMPLE_1_SIMPLE_TOPLEVEL = """--- +license: mit +language: + - en +tags: + - genomics + - yeast + - transcription +pretty_name: "Example Dataset 1 - TF Perturbation" +size_categories: + - 100K- + Systematic gene identifier of the ChIP-targeted transcription factor + role: regulator_identifier + - name: regulator_symbol + dtype: string + description: Standard gene symbol of the ChIP-targeted transcription factor + role: regulator_identifier + - name: target_locus_tag + dtype: string + description: Systematic gene identifier of the target gene + role: target_identifier + - name: target_symbol + dtype: string + description: Standard gene symbol of the target gene + role: target_identifier + - name: condition + dtype: + class_label: + names: ["YPD", "galactose", "heat_shock", "oxidative_stress", + "amino_acid_starvation"] + description: Environmental or stress condition of the experiment + role: experimental_condition + definitions: + YPD: + description: Rich media baseline condition + environmental_conditions: + temperature_celsius: 30 + cultivation_method: liquid_culture + growth_phase_at_harvest: + od600: 0.6 + stage: mid_log_phase + media: + name: YPD + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: 1 + - compound: peptone + concentration_percent: 2 + galactose: + description: Alternative carbon source condition + environmental_conditions: + temperature_celsius: 30 + cultivation_method: liquid_culture + growth_phase_at_harvest: + od600: 0.6 + stage: mid_log_phase + media: + name: YPD + carbon_source: + - compound: D-galactose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: 1 + - compound: peptone + concentration_percent: 2 + heat_shock: + description: Temperature stress condition + environmental_conditions: + temperature_celsius: 37 + cultivation_method: liquid_culture + growth_phase_at_harvest: + od600: 0.6 + stage: mid_log_phase + media: + name: YPD + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: 1 + - compound: peptone + concentration_percent: 2 + heat_treatment: + duration_minutes: 15 + oxidative_stress: + description: Hydrogen peroxide stress condition + environmental_conditions: + temperature_celsius: 30 + cultivation_method: liquid_culture + growth_phase_at_harvest: + od600: 0.6 + stage: mid_log_phase + media: + name: YPD + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: 1 + - compound: peptone + concentration_percent: 2 + chemical_treatment: + compound: hydrogen_peroxide + concentration_percent: 0.004 + duration_minutes: 20 + amino_acid_starvation: + description: Amino acid starvation via chemical inhibition + environmental_conditions: + temperature_celsius: 30 + cultivation_method: liquid_culture + growth_phase_at_harvest: + od600: 0.5 + stage: mid_log_phase + media: + name: synthetic_complete + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_nitrogen_base + # 6.71 g/L + concentration_percent: 0.671 + specifications: + - without_amino_acids + - without_ammonium_sulfate + - compound: ammonium_sulfate + # 5 g/L + concentration_percent: 0.5 + - compound: amino_acid_dropout_mix + # 2 g/L + concentration_percent: 0.2 + chemical_treatment: + compound: 3-amino-1,2,4-triazole + concentration_percent: 0.01 + duration_hours: 1 + - name: binding_score + dtype: float64 + description: ChIP-seq binding enrichment score + role: quantitative_measure + - name: peak_pvalue + dtype: float64 + description: Statistical significance of binding peak + role: quantitative_measure + - name: peak_qvalue + dtype: float64 + description: FDR-adjusted p-value for binding peak + role: quantitative_measure +--- +""" + + +EXAMPLE_3_PARTITIONED_WITH_METADATA = """--- +license: mit +language: + - en +tags: + - genomics + - yeast + - binding + - genome-wide + - chec-seq +pretty_name: "Example Dataset 3 - Genome Coverage Compendium" +size_categories: + - 10M- + unique identifier for a specific sample. The sample ID identifies a unique + (regulator_locus_tag, time, mechanism, restriction, date, strain) tuple. + - name: db_id + dtype: integer + description: >- + an old unique identifer, for use internally only. Deprecated and will be removed eventually. + Do not use in analysis. db_id = 0, for GEV and Z3EV, means that those samples are not + included in the original DB. + - name: regulator_locus_tag + dtype: string + description: >- + induced transcriptional regulator systematic ID. + See hf/BrentLab/yeast_genome_resources + role: regulator_identifier + - name: regulator_symbol + dtype: string + description: >- + induced transcriptional regulator common name. If no common name exists, + then the `regulator_locus_tag` is used. + role: regulator_identifier + - name: target_locus_tag + dtype: string + description: >- + The systematic ID of the feature to which the effect/pvalue is assigned. + See hf/BrentLab/yeast_genome_resources + role: target_identifier + - name: target_symbol + dtype: string + description: >- + The common name of the feature to which the effect/pvalue is assigned. + If there is no common name, the `target_locus_tag` is used. + role: target_identifier + - name: time + dtype: float + description: time point (minutes) + role: experimental_condition + - name: mechanism + dtype: + class_label: + names: ["GEV", "ZEV"] + description: Synthetic TF induction system (GEV or ZEV) + role: experimental_condition + - name: restriction + dtype: + class_label: + names: ["M", "N", "P"] + description: >- + nutrient limitation, one of P (phosphate limitation (20 mg/l).), + N (Nitrogen‐limited cultures were maintained at 40 mg/l ammonium sulfate) or + M (Not defined in the paper or on the Calico website) + role: experimental_condition + definitions: + P: + environmental_conditions: + media: + nitrogen_source: + - compound: ammonium_sulfate + # Saldanha et al 2004: 5 g/l + concentration_percent: 0.5 + phosphate_source: + - compound: potassium_phosphate_monobasic + # Hackett et al 2020: 20 mg/l + concentration_percent: 0.002 + N: + environmental_conditions: + media: + nitrogen_source: + - compound: ammonium_sulfate + # Hackett et al 2020: 40 mg/l + concentration_percent: 0.004 + M: + description: "Not defined in the paper or on the Calico website" + - name: date + dtype: string + description: date performed + role: experimental_condition + - name: strain + dtype: string + description: strain name + role: experimental_condition + - name: green_median + dtype: float + description: median of green (reference) channel fluorescence + role: quantitative_measure + - name: red_median + dtype: float + description: median of red (experimental) channel fluorescence + role: quantitative_measure + - name: log2_ratio + dtype: float + description: log2(red / green) subtracting value at time zero + role: quantitative_measure + - name: log2_cleaned_ratio + dtype: float + description: Non-specific stress response and prominent outliers removed + role: quantitative_measure + - name: log2_noise_model + dtype: float + description: estimated noise standard deviation + role: quantitative_measure + - name: log2_cleaned_ratio_zth2d + dtype: float + description: >- + cleaned timecourses hard-thresholded based on + multiple observations (or last observation) passing the noise model + role: quantitative_measure + - name: log2_selected_timecourses + dtype: float + description: >- + cleaned timecourses hard-thresholded based on single observations + passing noise model and impulse evaluation of biological feasibility + role: quantitative_measure + - name: log2_shrunken_timecourses + dtype: float + description: >- + selected timecourses with observation-level shrinkage based on + local FDR (false discovery rate). Most users of the data will want + to use this column. + role: quantitative_measure +--- + +# harbison_2004 +--- +license: mit +language: + - en +tags: + - genomics + - yeast + - transcription + - binding +pretty_name: "Harbison, 2004 ChIP-chip" +size_categories: + - 1M- + Environmental condition of the experiment. Nearly all of the 204 regulators + have a YPD condition, and some have others in addition. + role: experimental_condition + definitions: + YPD: + description: Rich media baseline condition + environmental_conditions: + # Harbison et al 2004: grown at 30°C (from HEAT condition context) + temperature_celsius: 30 + cultivation_method: unspecified + growth_phase_at_harvest: + # Harbison et al 2004: OD600 ~0.8 + od600: 0.8 + media: + # Harbison et al 2004: 1% yeast extract / 2% peptone / 2% glucose + name: YPD + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: 1 + - compound: peptone + concentration_percent: 2 + SM: + description: Amino acid starvation stress condition + environmental_conditions: + temperature_celsius: 30 + cultivation_method: unspecified + growth_phase_at_harvest: + # Harbison et al 2004: OD600 ~0.6 + od600: 0.6 + media: + # Harbison et al 2004: synthetic complete medium + name: synthetic_complete + carbon_source: unspecified + nitrogen_source: unspecified + chemical_treatment: + compound: sulfometuron_methyl + # Harbison et al 2004: 0.2 mg/ml + concentration_percent: 0.02 + duration_hours: 2 + RAPA: + description: Nutrient deprivation via TOR inhibition + environmental_conditions: + temperature_celsius: 30 + cultivation_method: unspecified + growth_phase_at_harvest: + # Harbison et al 2004: OD600 ~0.8 + od600: 0.8 + media: + name: YPD + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: 1 + - compound: peptone + concentration_percent: 2 + chemical_treatment: + compound: rapamycin + # Harbison et al 2004: 100 nM + concentration_percent: 9.142e-6 + duration_minutes: 20 + H2O2Hi: + description: High oxidative stress condition + environmental_conditions: + temperature_celsius: 30 + cultivation_method: unspecified + growth_phase_at_harvest: + # Harbison et al 2004: OD600 ~0.5 + od600: 0.5 + media: + name: YPD + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: 1 + - compound: peptone + concentration_percent: 2 + chemical_treatment: + compound: hydrogen_peroxide + # Harbison et al 2004: 4 mM + concentration_percent: 0.0136 + duration_minutes: 30 + H2O2Lo: + description: Moderate oxidative stress condition + environmental_conditions: + temperature_celsius: 30 + cultivation_method: unspecified + growth_phase_at_harvest: + # Harbison et al 2004: OD600 ~0.5 + od600: 0.5 + media: + name: YPD + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: 1 + - compound: peptone + concentration_percent: 2 + chemical_treatment: + compound: hydrogen_peroxide + # Harbison et al 2004: 0.4 mM + concentration_percent: 0.00136 + duration_minutes: 20 + Acid: + description: Acidic pH stress condition + environmental_conditions: + temperature_celsius: 30 + cultivation_method: unspecified + growth_phase_at_harvest: + # Harbison et al 2004: OD600 ~0.5 + od600: 0.5 + media: + name: YPD + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: 1 + - compound: peptone + concentration_percent: 2 + chemical_treatment: + compound: succinic_acid + # Harbison et al 2004: 0.05 M to reach pH 4.0 + concentration_percent: 0.59 + target_pH: 4.0 + duration_minutes: 30 + Alpha: + description: Mating pheromone induction condition + environmental_conditions: + temperature_celsius: 30 + cultivation_method: unspecified + growth_phase_at_harvest: + # Harbison et al 2004: OD600 ~0.8 + od600: 0.8 + media: + name: YPD + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: 1 + - compound: peptone + concentration_percent: 2 + chemical_treatment: + compound: alpha_factor_pheromone + # Harbison et al 2004: 5 mg/ml + concentration_percent: 0.5 + duration_minutes: 30 + BUT14: + description: Long-term filamentation induction with butanol + environmental_conditions: + temperature_celsius: 30 + cultivation_method: unspecified + growth_phase_at_harvest: + # Harbison et al 2004: OD600 ~0.8 + od600: 0.8 + media: + # Harbison et al 2004: YPD containing 1% butanol + name: YPD + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: 1 + - compound: peptone + concentration_percent: 2 + additives: + - compound: butanol + concentration_percent: 1 + incubation_duration_hours: 14 + BUT90: + description: Short-term filamentation induction with butanol + environmental_conditions: + temperature_celsius: 30 + cultivation_method: unspecified + growth_phase_at_harvest: + # Harbison et al 2004: OD600 ~0.8 + od600: 0.8 + media: + # Harbison et al 2004: YPD containing 1% butanol + name: YPD + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: 1 + - compound: peptone + concentration_percent: 2 + additives: + - compound: butanol + concentration_percent: 1 + incubation_duration_minutes: 90 + "Thi-": + description: Vitamin B1 deprivation stress condition + environmental_conditions: + temperature_celsius: 30 + cultivation_method: unspecified + growth_phase_at_harvest: + # Harbison et al 2004: OD600 ~0.8 + od600: 0.8 + media: + # Harbison et al 2004: synthetic complete medium lacking thiamin + name: synthetic_complete_minus_thiamine + carbon_source: unspecified + nitrogen_source: unspecified + GAL: + description: Galactose-based growth medium condition + environmental_conditions: + temperature_celsius: 30 + cultivation_method: unspecified + growth_phase_at_harvest: + # Harbison et al 2004: OD600 ~0.8 + od600: 0.8 + media: + # Harbison et al 2004: YEP medium supplemented with galactose (2%) + name: yeast_extract_peptone + carbon_source: + - compound: D-galactose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: unspecified + - compound: peptone + concentration_percent: unspecified + HEAT: + description: Heat shock stress condition + environmental_conditions: + # Harbison et al 2004: grown at 30°C, shifted to 37°C for 45 min + initial_temperature_celsius: 30 + temperature_shift_celsius: 37 + temperature_shift_duration_minutes: 45 + cultivation_method: unspecified + growth_phase_at_harvest: + # Harbison et al 2004: OD600 ~0.5 + od600: 0.5 + media: + # Harbison et al 2004: YPD + name: YPD + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: 1 + - compound: peptone + concentration_percent: 2 + "Pi-": + description: Phosphate deprivation stress condition + environmental_conditions: + temperature_celsius: 30 + cultivation_method: unspecified + growth_phase_at_harvest: + # Harbison et al 2004: OD600 ~0.8 + od600: 0.8 + media: + # Harbison et al 2004: synthetic complete medium lacking phosphate + name: synthetic_complete_minus_phosphate + carbon_source: unspecified + nitrogen_source: unspecified + RAFF: + description: Raffinose-based growth medium condition + environmental_conditions: + temperature_celsius: 30 + cultivation_method: unspecified + growth_phase_at_harvest: + # Harbison et al 2004: OD600 ~0.8 + od600: 0.8 + media: + # Harbison et al 2004: YEP medium supplemented with raffinose (2%) + name: yeast_extract_peptone + carbon_source: + - compound: D-raffinose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: unspecified + - compound: peptone + concentration_percent: unspecified + - name: regulator_locus_tag + dtype: string + description: Systematic gene name (ORF identifier) of the ChIPd transcription factor + role: regulator_identifier + - name: regulator_symbol + dtype: string + description: Standard gene symbol of the ChIPd transcription factor + role: regulator_identifier + - name: target_locus_tag + dtype: string + description: Systematic gene name (ORF identifier) of the target gene measured + role: target_identifier + - name: target_symbol + dtype: string + description: Standard gene symbol of the target gene measured + role: target_identifier + - name: effect + dtype: float64 + description: The chip channel ratio (effect size) + role: quantitative_measure + - name: pvalue + dtype: float64 + description: pvalue of the chip channel ratio (effect) + role: quantitative_measure +--- + +# hu_2007_reimand_2010 +--- +license: mit +language: + - en +tags: + - genomics + - yeast + - transcription + - perturbation + - response + - knockout + - TFKO +pretty_name: Hu 2007/Reimand 2010 TFKO +size_categories: + - 1M- + an old unique identifer, for use internally only. Deprecated and will be removed eventually. + Do not use in analysis. + - name: regulator_locus_tag + dtype: string + description: induced transcriptional regulator systematic ID. See hf/BrentLab/yeast_genome_resources + role: regulator_identifier + - name: regulator_symbol + dtype: string + description: induced transcriptional regulator common name. If no common name exists, then the `regulator_locus_tag` is used. + role: regulator_identifier + - name: target_locus_tag + dtype: string + description: The systematic ID of the feature to which the effect/pvalue is assigned. See hf/BrentLab/yeast_genome_resources + role: target_identifier + - name: target_symbol + dtype: string + description: The common name of the feature to which the effect/pvalue is assigned. If there is no common name, the `target_locus_tag` is used. + role: target_identifier + - name: effect + dtype: float + description: >- + log fold change of mutant vs wt. From the remaind methods: Differential expression + was calculated using a moderated eBayes t-test as implemented in the Limma + Bioconductor package + role: quantitative_measure + - name: pval + dtype: float + description: P-values were FDR-adjusted across the whole microarray dataset to correct for multiple testing + role: quantitative_measure + - name: average_od_of_replicates + dtype: float + description: average OD of the replicates at harvest + - name: heat_shock + dtype: bool + description: >- + `True` if the regulator strain was subjected to heat shock treatment. + Applied to 22 transcription factors implicated in heat shock response. + `False` otherwise + role: experimental_condition + definitions: + true: + environmental_conditions: + # Hu et al 2007: "15-min heat shock at 39°C" + temperature_celsius: 39 + duration_minutes: 15 + strain_background: + genotype: BY4741 + mating_type: MATa + markers: + - his3Δ1 + - leu2Δ0 + - met15Δ0 + - ura3Δ0 + source: Open_Biosystems + description: Knockout strains for nonessential transcription factors + false: + environmental_conditions: + description: Standard growth conditions at 30°C + strain_background: + genotype: BY4741 + mating_type: MATa + markers: + - his3Δ1 + - leu2Δ0 + - met15Δ0 + - ura3Δ0 + source: Open_Biosystems + description: Knockout strains for nonessential transcription factors + - name: tetracycline_treatment + dtype: bool + description: >- + `True` if the regulator strain was treated with doxycycline to repress + TetO7-promoter regulated essential transcription factors. Applied to 6 + essential transcription factors. `False` for untreated control condition. + role: experimental_condition + definitions: + true: + environmental_conditions: + drug_treatment: + compound: doxycycline + # Hu et al 2007: 10 mg/ml + concentration_percent: 1 + duration_hours_min: 14 + duration_hours_max: 16 + strain_background: + genotype: BY4741_derivative + mating_type: MATa + markers: + - URA3::CMV-tTA + - his3Δ1 + - leu2Δ0 + - met15Δ0 + source: Open_Biosystems + description: Essential transcription factors with TetO7-promoter regulation + false: + environmental_conditions: + description: No doxycycline treatment; TetO7 promoter active + strain_background: + genotype: BY4741_derivative + mating_type: MATa + markers: + - URA3::CMV-tTA + - his3Δ1 + - leu2Δ0 + - met15Δ0 + source: Open_Biosystems + description: Essential transcription factors with TetO7-promoter regulation +--- + +# hughes_2006 +--- +license: mit +language: +- en +tags: +- biology +- genomics +- yeast +- transcription-factors +- gene-expression +- perturbation-screen +- overexpression +- knockout +- microarray +- functional-genomics +pretty_name: "Hughes 2006 Yeast Transcription Factor Perturbation Dataset" +size_categories: +- 100K- + unique identifier for a specific sample. The sample ID identifies + a unique regulator_locus_tag and can be used to join to the + other datasets in this repo, including the metadata + - name: regulator_locus_tag + dtype: string + role: identifier + description: >- + Systematic gene name (ORF identifier) of the + transcription factor + - name: regulator_symbol + dtype: string + description: Standard gene symbol of the transcription factor + - name: found_domain + dtype: string + description: >- + Identified DNA-binding domain(s) or protein family classification + - name: sgd_description + dtype: string + description: >- + Functional description from Saccharomyces Genome Database (SGD) + - name: essential + dtype: bool + description: >- + Boolean indicating whether the gene is essential for viability + - name: oe_passed_qc + dtype: bool + description: >- + Boolean indicating whether overexpression experiments passed + quality control + - name: del_passed_qc + dtype: bool + description: >- + Boolean indicating whether deletion experiments passed + quality control + +- config_name: overexpression + description: Overexpression perturbation normalized log2 fold changes + dataset_type: annotated_features + data_files: + - split: train + path: overexpression.parquet + experimental_conditions: + environmental_conditions: + temperature_celsius: unspecified + cultivation_method: unspecified + media: + # Hughes et al 2006: "selective medium supplemented with 2% raffinose" + name: selective_medium + carbon_source: + - compound: D-raffinose + # Hughes et al 2006: 2% raffinose + concentration_percent: 2 + nitrogen_source: unspecified + induction: + # Hughes et al 2006: "induction with 2% galactose for 3 h" + inducer: + compound: D-galactose + concentration_percent: 2 + duration_hours: 3 + dataset_info: + features: + - name: sample_id + dtype: integer + description: >- + unique identifier for a specific sample. The sample ID identifies + a unique regulator_locus_tag and can be used to join to the + other datasets in this repo, including the metadata + - name: regulator_locus_tag + dtype: string + description: >- + Systematic gene name (ORF identifier) of the + perturbed transcription factor + role: regulator_identifier + - name: regulator_symbol + dtype: string + description: Standard gene symbol of the perturbed transcription factor + - name: target_locus_tag + dtype: string + description: >- + Systematic gene name (ORF identifier) of the + target gene measured + role: target_identifier + - name: target_symbol + dtype: string + description: Standard gene symbol of the target gene measured + role: target_identifier + - name: dye_plus + dtype: float64 + role: quantitative_measure + description: >- + Normalized log2 fold change for positive (+) dye orientation. + Positive values indicate upregulation in response to overexpression. + - name: dye_minus + dtype: float64 + role: quantitative_measure + description: >- + Normalized log2 fold change for negative (-) dye orientation. + Positive values indicate upregulation in response to overexpression. + - name: mean_norm_log2fc + dtype: float64 + role: quantitative_measure + description: >- + Average log2 fold change across dye orientations, + providing a dye-independent estimate of gene expression + change upon transcription factor overexpression. + +- config_name: knockout + description: Deletion/knockout perturbation normalized log2 fold changes + dataset_type: annotated_features + data_files: + - split: train + path: knockout.parquet + experimental_conditions: + environmental_conditions: + temperature_celsius: unspecified + cultivation_method: unspecified + media: + # Hughes et al 2006: "synthetic medium supplemented with 2% dextrose" + name: synthetic_medium + carbon_source: + - compound: D-glucose + # Hughes et al 2006: 2% dextrose + concentration_percent: 2 + nitrogen_source: unspecified + dataset_info: + features: + - name: sample_id + dtype: integer + description: >- + unique identifier for a specific sample. The sample ID identifies + a unique regulator_locus_tag and can be used to join to the + other datasets in this repo, including the metadata + - name: regulator_locus_tag + dtype: string + description: >- + Systematic gene name (ORF identifier) of the perturbed + transcription factor + role: regulator_identifier + - name: regulator_symbol + dtype: string + description: Standard gene symbol of the perturbed transcription factor + role: regulator_identifier + - name: target_locus_tag + dtype: string + description: >- + Systematic gene name (ORF identifier) of the + target gene measured + role: target_identifier + - name: target_symbol + dtype: string + description: Standard gene symbol of the target gene measured + role: target_identifier + - name: dye_plus + dtype: float64 + description: >- + Normalized log2 fold change for positive (+) dye orientation. + Positive values indicate upregulation in response to deletion. + role: quantitative_measure + - name: dye_minus + dtype: float64 + description: >- + Normalized log2 fold change for negative (-) dye orientation. + Positive values indicate upregulation in response to deletion. + role: quantitative_measure + - name: mean_norm_log2fc + dtype: float64 + description: >- + Average log2 fold change across dye orientations, providing a + dye-independent estimate of gene expression change upon + transcription factor deletion. + role: quantitative_measure +--- + +# kemmeren_2014 +--- +license: mit +language: +- en +tags: +- genomics +- yeast +- transcription +- perturbation +- response +- knockout +- TFKO +pretty_name: "Kemmeren, 2014 Overexpression" +size_categories: +- 1M- + Transcriptional regulator overexpression perturbation data with + differential expression measurements + dataset_type: annotated_features + default: true + metadata_fields: ["regulator_locus_tag", "regulator_symbol"] + data_files: + - split: train + path: kemmeren_2014.parquet + dataset_info: + features: + - name: sample_id + dtype: integer + description: >- + unique identifier for a specific sample. + The sample ID identifies a unique regulator. + - name: db_id + dtype: integer + description: >- + an old unique identifer, for use internally only. Deprecated and will be removed eventually. + Do not use in analysis. db_id = 0 for loci that were originally parsed incorrectly. + - name: regulator_locus_tag + dtype: string + description: >- + induced transcriptional regulator systematic ID. + See hf/BrentLab/yeast_genome_resources + role: regulator_identifier + - name: regulator_symbol + dtype: string + description: >- + induced transcriptional regulator common name. + If no common name exists, then the `regulator_locus_tag` is used. + role: regulator_identifier + - name: reporterId + dtype: string + description: probe ID as reported from the original data + - name: target_locus_tag + dtype: string + description: >- + The systematic ID of the feature to which the effect/pvalue is assigned. + See hf/BrentLab/yeast_genome_resources + role: target_identifier + - name: target_symbol + dtype: string + description: >- + The common name of the feature to which the effect/pvalue is assigned. + If there is no common name, the `target_locus_tag` is used. + role: target_identifier + - name: M + dtype: float64 + description: log₂ fold change (mutant vs wildtype) + role: quantitative_measure + - name: Madj + dtype: float64 + description: >- + M value with the cell cycle signal removed + (see paper cited in the introduction above) + role: quantitative_measure + - name: A + dtype: float64 + description: >- + average log2 intensity of the two channels, a proxy for expression level + (This is a guess based on microarray convention -- not specified on holstege site) + role: quantitative_measure + - name: pval + dtype: float64 + description: significance of the modeled effect (M), from limma + role: quantitative_measure + - name: variable_in_wt + dtype: string + description: >- + True if the given locus is variable in the WT condition. + Recommended to remove these from analysis. False otherwise. + See Holstege website for more information + role: experimental_condition + - name: multiple_probes + dtype: string + description: >- + True if there is more than one probe associated with + the same genomic locus. False otherwise + role: experimental_condition + - name: kemmeren_regulator + dtype: string + description: >- + True if the regulator is one of the regulators studied in the + original Kemmeren et al. (2014) global regulator study. False otherwise + role: experimental_condition + - name: regulator_desc + dtype: string + description: >- + functional description of the induced regulator + from the original paper supplement + role: experimental_condition + - name: functional_category + dtype: string + description: functional classification of the regulator from the original paper supplement + role: experimental_condition + - name: slides + dtype: string + description: identifier(s) for the microarray slide(s) used in this experiment + role: experimental_condition + - name: mating_type + dtype: string + description: mating type of the strain background used in the experiment + role: experimental_condition + - name: source_of_deletion_mutants + dtype: string + description: origin of the strain + role: experimental_condition + - name: primary_hybsets + dtype: string + description: identifier for the primary hybridization set to which this sample belongs + role: experimental_condition + - name: responsive_non_responsive + dtype: string + description: >- + classification of the regulator as responsive or not to the + deletion from the original paper supplement + role: experimental_condition + - name: nr_sign_changes + dtype: integer + description: >- + number of significant changes in expression detected for the regulator locus tag (abs(M) > log2(1.7) & pval < 0.05). + Note that there is a slight difference when calculating from the data provided here, I believe due to a difference in + the way the targets are parsed and filtered (some ORFs that have since been removed from the annotations are removed). + I didn't investigate this closely, though. + role: experimental_condition + - name: profile_first_published + dtype: string + description: citation or reference indicating where this expression profile was first published + role: experimental_condition + - name: chase_notes + dtype: string + description: notes added during data curation and parsing +--- + +# mahendrawada_2025 +--- +license: mit +language: +- en +tags: +- biology +- genomics +- yeast +- transcription-factors +- gene-expression +- binding +- chec +- perturbation +- rnaseq +- nascent rnaseq +pretty_name: "Mahendrawada 2025 ChEC-seq and Nascent RNA-seq data" +size_categories: +- 100K- + unique identifier for a specific sample, which uniquely identifies one of the 178 TFs. + Across datasets in this repo, the a given sample_id identifies the same regulator. + - name: regulator_locus_tag + dtype: string + description: Systematic gene name (ORF identifier) of the transcription factor + - name: regulator_symbol + dtype: string + description: Standard gene symbol of the transcription factor + - name: target_locus_tag + dtype: string + description: Systematic gene name (ORF identifier) of the target gene + - name: target_symbol + dtype: string + description: Standard gene symbol of the target gene + - name: peak_score + dtype: float64 + description: ChEC signal around peak center (sum of ChEC signal from -150 to +150 bp from peak summit) normalized to Drosophila spike-in control + - name: processing_method + dtype: string + description: Method used for peak calling and quantification (original authors) + +- config_name: reprocessed_chec_seq + description: ChEC-seq transcription factor binding data reprocessed with updated peak calling methodology + dataset_type: annotated_features + data_files: + - split: train + path: chec_reprocessed_mahendrawada_2025.parquet + dataset_info: + features: + - name: sample_id + dtype: integer + description: >- + unique identifier for a specific sample, which uniquely identifies one of the 178 TFs. + Across datasets in this repo, the a given sample_id identifies the same regulator. + - name: regulator_locus_tag + dtype: string + description: Systematic gene name (ORF identifier) of the transcription factor + - name: regulator_symbol + dtype: string + description: Standard gene symbol of the transcription factor + - name: target_locus_tag + dtype: string + description: Systematic gene name (ORF identifier) of the target gene + - name: target_symbol + dtype: string + description: Standard gene symbol of the target gene + - name: enrichment + dtype: float64 + description: ratio of experimental insertions to background insertions + - name: poisson_pval + dtype: float64 + description: enrichment poisson pvalue + +- config_name: reprocessed_diffcontrol_5prime + description: Comparing two different sets of control replicates, m2025 from the Mahendrawada 2025 paper, and h2021 from a previous paper from the Hahn lab + dataset_type: annotated_features + metadata_fields: + - control_source + - condition + - regulator_locus_tag + experimental_conditions: + environmental_conditions: + # Mahendrawada et al 2025: "30 °C culture" + temperature_celsius: 30 + cultivation_method: unspecified + growth_phase_at_harvest: + # Mahendrawada et al 2025: "A600 of ~1.0" + od600: 1.0 + media: + # Mahendrawada et al 2025: "synthetic complete (SC) media" + name: synthetic_complete + carbon_source: unspecified + nitrogen_source: + - compound: yeast_nitrogen_base + # Mahendrawada et al 2025: 1.7 g/L (without ammonium sulfate or amino acids (BD Difco)) + concentration_percent: 0.17 + specifications: + - without_ammonium_sulfate + - without_amino_acids + - compound: ammonium_sulfate + # Mahendrawada et al 2025: 5 g/L + concentration_percent: 0.5 + - compound: amino_acid_dropout_mix + # Mahendrawada et al 2025: 0.6 g/L + concentration_percent: 0.06 + - compound: adenine_sulfate + # Mahendrawada et al 2025: 40 μg/ml = 0.04 g/L + concentration_percent: 0.004 + - compound: uracil + # Mahendrawada et al 2025: 2 μg/ml = 0.002 g/L + concentration_percent: 0.0002 + data_files: + - split: train + path: reprocess_diffcontrol_5prime.parquet + dataset_info: + features: + - name: control_source + dtype: string + description: Source identifier for the control dataset (m2025 or h2021) + - name: condition + dtype: string + description: Experimental condition. 'standard' is YPD. + - name: regulator_locus_tag + dtype: string + description: Systematic gene name (ORF identifier) of the transcription factor + - name: target_locus_tag + dtype: string + description: Systematic gene name (ORF identifier) of the target gene + - name: chr + dtype: string + description: Chromosome name of the promoter/target region + - name: start + dtype: int64 + description: Start coordinate of the promoter region + - name: end + dtype: int64 + description: End coordinate of the promoter region + - name: strand + dtype: string + description: Strand orientation (+ or -) of the promoter/target + - name: input_vs_target_log2_fold_change + dtype: float64 + description: Log2 fold change of TF-tagged sample vs control (from DESeq2) + - name: input_vs_target_p_value + dtype: float64 + description: P-value for differential enrichment (from DESeq2) + - name: input_vs_target_adj_p_value + dtype: float64 + description: Adjusted p-value (FDR-corrected) for differential enrichment (from DESeq2) + +- config_name: rna_seq + description: Nascent RNA-seq differential expression data following transcription factor depletion using 4TU metabolic labeling + dataset_type: annotated_features + metadata_fields: + - regulator_locus_tag + - regulator_symbol + data_files: + - split: train + path: rnaseq_mahendrawada_2025.parquet + dataset_info: + features: + - name: sample_id + dtype: integer + description: >- + unique identifier for a specific sample, which uniquely identifies one of the 178 TFs. + Across datasets in this repo, the a given sample_id identifies the same regulator. + - name: db_id + dtype: integer + description: >- + an old unique identifer, for use internally only. Deprecated and will be removed eventually. + Do not use in analysis. + - name: regulator_locus_tag + dtype: string + description: Systematic gene name (ORF identifier) of the depleted transcription factor + - name: regulator_symbol + dtype: string + description: Standard gene symbol of the depleted transcription factor + - name: target_locus_tag + dtype: string + description: Systematic gene name (ORF identifier) of the differentially expressed target gene + - name: target_symbol + dtype: string + description: Standard gene symbol of the differentially expressed target gene + - name: log2fc + dtype: float64 + description: Log2 fold change (IAA/DMSO) for significantly affected genes (DESeq2, padj <0.1, FC >= 1.3) +--- + +# rossi_2021 +--- +license: mit +tags: +- transcription-factor +- binding +- chipexo +- genomics +- biology +language: +- en +pretty_name: Rossi ChIP-exo 2021 +experimental_conditions: + environmental_conditions: + temperature_celsius: 25 + cultivation_method: unspecified + growth_phase_at_harvest: + phase: mid_log + od600: 0.8 + media: + name: yeast_peptone_dextrose + carbon_source: + - compound: D-glucose + concentration_percent: unspecified + nitrogen_source: + - compound: yeast_extract + concentration_percent: unspecified + - compound: peptone + concentration_percent: unspecified + + # Heat shock applied only to SAGA strains + # note that im not sure which strains this + # applies to -- it is a TODO to better + # document this + heat_shock: + induced: true + temperature_celsius: 37 + duration_minutes: 6 + pre_induction_temperature_celsius: 25 + method: equal_volume_medium_transfer +configs: +- config_name: metadata + description: Metadata describing the tagged regulator in each experiment + dataset_type: metadata + data_files: + - split: train + path: rossi_2021_metadata.parquet + dataset_info: + features: + - name: regulator_locus_tag + dtype: string + description: Systematic gene name (ORF identifier) of the transcription factor + - name: regulator_symbol + dtype: string + description: Standard gene symbol of the transcription factor + - name: run_accession + dtype: string + description: GEO run accession identifier for the sample + - name: yeastepigenome_id + dtype: string + description: Sample identifier used by yeastepigenome.org +- config_name: genome_map + description: "ChIP-exo 5' tag coverage data partitioned by sample accession" + dataset_type: genome_map + data_files: + - split: train + path: genome_map/*/*.parquet + dataset_info: + features: + - name: chr + dtype: string + description: Chromosome name (e.g., chrI, chrII, etc.) + - name: pos + dtype: int32 + description: "Genomic position of the 5' tag" + - name: pileup + dtype: int32 + description: "Depth of coverage (number of 5' tags) at this genomic position" +- config_name: rossi_annotated_features + description: ChIP-exo regulator-target binding features with peak statistics + dataset_type: annotated_features + default: true + metadata_fields: + - regulator_locus_tag + - regulator_symbol + - target_locus_tag + data_files: + - split: train + path: yeastepigenome_annotatedfeatures.parquet + dataset_info: + features: + - name: sample_id + dtype: int32 + description: >- + Unique identifier for each ChIP-exo experimental sample. + - name: pss_id + dtype: float64 + description: >- + Current brentlab promotersetsig table id. This will eventually be removed. + - name: binding_id + dtype: float64 + description: >- + Current brentlab binding table id. This will eventually be removed. + - name: yeastepigenome_id + dtype: float64 + description: >- + Unique identifier in the yeastepigenome database. + - name: regulator_locus_tag + dtype: string + description: >- + Systematic ORF name of the regulator. + role: regulator_identifier + - name: regulator_symbol + dtype: string + description: >- + Common gene name of the regulator. + role: regulator_identifier + - name: target_locus_tag + dtype: string + description: >- + The systematic ID of the feature to which the effect/pvalue is + assigned. See hf/BrentLab/yeast_genome_resources + role: target_identifier + - name: target_symbol + dtype: string + description: >- + The common name of the feature to which the effect/pvalue is + assigned. If there is no common name, the `target_locus_tag` is + used. + role: target_identifier + - name: n_sig_peaks + dtype: float64 + description: >- + Number of peaks in the promoter region of the the target gene + role: quantitative_measure + - name: max_fc + dtype: float64 + description: >- + If there are multiple peaks in the promoter region, then the maximum is + reported. Otherwise, it is the fold change of the single peak in the + promoter. + role: quantitative_measure + - name: min_pval + dtype: float64 + description: >- + The most significant p-value among peaks for this interaction. + role: quantitative_measure +- config_name: reprocess_annotatedfeatures + description: >- + Annotated features reprocessed with updated peak + calling methodology + dataset_type: annotated_features + data_files: + - split: train + path: reprocess_annotatedfeatures.parquet + dataset_info: + features: + - name: regulator_locus_tag + dtype: string + description: Systematic gene name (ORF identifier) of the transcription factor + - name: regulator_symbol + dtype: string + description: Standard gene symbol of the transcription factor + - name: target_locus_tag + dtype: string + description: Systematic gene name (ORF identifier) of the target gene + - name: target_symbol + dtype: string + description: Standard gene symbol of the target gene + - name: baseMean + dtype: float64 + description: Average of normalized count values, dividing by size factors, taken over all samples + - name: log2FoldChange + dtype: float64 + description: Log2 fold change between comparison and control groups + - name: lfcSE + dtype: float64 + description: Standard error estimate for the log2 fold change estimate + - name: stat + dtype: float64 + description: Value of the test statistic for the gene + - name: pvalue + dtype: float64 + description: P-value of the test for the gene + - name: padj + dtype: float64 + description: Adjusted p-value for multiple testing for the gene +- config_name: reprocess_annotatedfeatures_tagcounts + description: Another version of the reprocessed data, quantified similarly to Calling Cards + dataset_type: annotated_features + data_files: + - split: train + path: reprocess_annotatedfeatures_tagcounts.parquet + dataset_info: + features: + - name: regulator_locus_tag + dtype: string + description: Systematic gene name (ORF identifier) of the transcription factor + role: regulator_identifier + - name: target_locus_tag + dtype: string + description: Systematic gene name (ORF identifier) of the target gene + role: target_identifier + - name: rank + dtype: int64 + description: Rank (ties method min rank) of the peak based on pvalue with ties broken by enrichment. Largest rank is most significant. + - name: control_count + dtype: int64 + description: Number of tags in the control condition + - name: experimental_count + dtype: int64 + description: Number of tags in the experimental condition + - name: mu + dtype: float64 + description: Expected count under the null hypothesis (control_count + 1) * (experimental_total_tags / control_total_tags) + - name: enrichment + dtype: float64 + description: Enrichment ratio of experimental over control. (experimental_counts / experimental_total) / (control_counts + pseudocount) / control_total + role: quantitative_measure + - name: log2_enrichment + dtype: float64 + description: Log2-transformed enrichment ratio + role: quantitative_measure + - name: neg_log10_pvalue + dtype: float64 + description: Negative log10 of the p-value for binding significance + role: quantitative_measure + - name: neg_log10_qvalue + dtype: float64 + description: Negative log10 of the FDR-adjusted q-value + role: quantitative_measure +--- + +# yeast_genome_resources +--- +license: mit +pretty_name: BrentLab Yeast Genome Resources +language: + - en +dataset_info: + features: + - name: start + dtype: int32 + description: Start coordinate (1-based, **inclusive**) + - name: end + dtype: int32 + description: End coordinate (1-based, **inclusive**) + - name: strand + dtype: string + levels: + - + + - "-" + description: Strand of feature + - name: type + dtype: string + levels: + - gene + - ncRNA_gene + - tRNA_gene + - snoRNA_gene + - transposable_element_gene + - pseudogene + - telomerase_RNA_gene + - snRNA_gene + - rRNA_gene + - blocked_reading_frame + description: classification of feature + - name: locus_tag + dtype: string + description: Systematic ID of feature + - name: symbol + dtype: string + description: Common name of feature + - name: alias + dtype: string + description: Alternative names of feature, typically alternative symbols + - name: source + dtype: string + description: Annotation file version/origin of the feature + - name: note + dtype: string + description: Additional feature information, typically the description from the + SGD gff/gtf + partitioning: + keys: + - name: chr + dtype: string + levels: + - chrI + - chrII + - chrVII + - chrV + - chrIII + - chrIV + - chrVIII + - chrVI + - chrX + - chrIX + - chrXI + - chrXIV + - chrXII + - chrXIII + - chrXV + - chrXVI + - chrM +configs: + - config_name: features + default: true + data_files: + - split: train + path: + - features/*/part-0.parquet +--- diff --git a/tfbpapi/tests/datainfo/test_datacard.py b/tfbpapi/tests/datainfo/test_datacard.py index ce22578..098d27f 100644 --- a/tfbpapi/tests/datainfo/test_datacard.py +++ b/tfbpapi/tests/datainfo/test_datacard.py @@ -3,7 +3,6 @@ from unittest.mock import Mock, patch import pytest -from pydantic import ValidationError from tfbpapi.datainfo import DataCard from tfbpapi.datainfo.models import DatasetType diff --git a/tfbpapi/tests/datainfo/test_datacard_parsing.py b/tfbpapi/tests/datainfo/test_datacard_parsing.py new file mode 100644 index 0000000..c7a35d0 --- /dev/null +++ b/tfbpapi/tests/datainfo/test_datacard_parsing.py @@ -0,0 +1,169 @@ +"""Test script to verify datacard parsing with new environmental_conditions.""" + +import yaml + +from tfbpapi.datainfo.models import DatasetCard +from tfbpapi.tests.datainfo.example_datacards import ( + EXAMPLE_1_SIMPLE_TOPLEVEL, + EXAMPLE_2_COMPLEX_FIELD_DEFINITIONS, + EXAMPLE_3_PARTITIONED_WITH_METADATA, +) + + +def test_example_1(): + """Test parsing example 1: simple top-level conditions.""" + print("=" * 80) + print("Testing Example 1: Simple Top-Level Conditions") + print("=" * 80) + + # Extract YAML from markdown + yaml_content = EXAMPLE_1_SIMPLE_TOPLEVEL.split("---")[1] + data = yaml.safe_load(yaml_content) + + try: + card = DatasetCard(**data) + print("✓ Successfully parsed Example 1") + print(f" - Configs: {len(card.configs)}") + print( + " - Top-level experimental_conditions: " + f"{card.experimental_conditions is not None}" + ) + + if card.experimental_conditions: + env_cond = card.experimental_conditions.environmental_conditions + if env_cond: + print(f" - Temperature: {env_cond.temperature_celsius}°C") + print(f" - Cultivation: {env_cond.cultivation_method}") + if env_cond.media: + print(f" - Media: {env_cond.media.name}") + print(f" - Carbon sources: {len(env_cond.media.carbon_source)}") + print( + f" - Nitrogen sources: {len(env_cond.media.nitrogen_source)}" + ) + + # Check field-level definitions + config = card.configs[0] + for feature in config.dataset_info.features: + if feature.definitions: + print( + f" - Feature '{feature.name}' has " + f"{len(feature.definitions)} definitions" + ) + for def_name in feature.definitions.keys(): + print(f" - {def_name}") + + print() + return True + except Exception as e: + print(f"✗ Failed to parse Example 1: {e}") + import traceback + + traceback.print_exc() + print() + return False + + +def test_example_2(): + """Test parsing example 2: complex field-level definitions.""" + print("=" * 80) + print("Testing Example 2: Complex Field-Level Definitions") + print("=" * 80) + + yaml_content = EXAMPLE_2_COMPLEX_FIELD_DEFINITIONS.split("---")[1] + data = yaml.safe_load(yaml_content) + + try: + card = DatasetCard(**data) + print("✓ Successfully parsed Example 2") + print(f" - Configs: {len(card.configs)}") + print(f" - Strain information: {card.strain_information is not None}") + + # Check field-level definitions + config = card.configs[0] + for feature in config.dataset_info.features: + if feature.definitions: + print( + f" - Feature '{feature.name}' has " + f"{len(feature.definitions)} definitions:" + ) + for def_name, def_value in feature.definitions.items(): + print(f" - {def_name}") + if "environmental_conditions" in def_value: + env = def_value["environmental_conditions"] + if "temperature_celsius" in env: + print(f" Temperature: {env['temperature_celsius']}°C") + if "media" in env: + print(f" Media: {env['media']['name']}") + + print() + return True + except Exception as e: + print(f"✗ Failed to parse Example 2: {e}") + import traceback + + traceback.print_exc() + print() + return False + + +def test_example_3(): + """Test parsing example 3: partitioned with metadata.""" + print("=" * 80) + print("Testing Example 3: Partitioned with Metadata") + print("=" * 80) + + yaml_content = EXAMPLE_3_PARTITIONED_WITH_METADATA.split("---")[1] + data = yaml.safe_load(yaml_content) + + try: + card = DatasetCard(**data) + print("✓ Successfully parsed Example 3") + print(f" - Configs: {len(card.configs)}") + print( + " - Top-level experimental_conditions: " + f"{card.experimental_conditions is not None}" + ) + + if card.experimental_conditions: + env_cond = card.experimental_conditions.environmental_conditions + if env_cond and env_cond.media: + print(f" - Top-level media: {env_cond.media.name}") + + # Check config-level experimental_conditions + for config in card.configs: + if config.experimental_conditions: + print(f" - Config '{config.config_name}' has experimental_conditions") + env_cond = config.experimental_conditions.environmental_conditions + if env_cond and env_cond.media: + print(f" - Media: {env_cond.media.name}") + print(f" - Temperature: {env_cond.temperature_celsius}°C") + + print() + return True + except Exception as e: + print(f"✗ Failed to parse Example 3: {e}") + import traceback + + traceback.print_exc() + print() + return False + + +if __name__ == "__main__": + results = [] + + results.append(test_example_1()) + results.append(test_example_2()) + results.append(test_example_3()) + + print("=" * 80) + print("Summary") + print("=" * 80) + print(f"Passed: {sum(results)}/{len(results)}") + + if all(results): + print("\n✓ All tests passed!") + exit(0) + else: + print("\n✗ Some tests failed") + exit(1) diff --git a/tfbpapi/tests/datainfo/test_metadata_manager.py b/tfbpapi/tests/datainfo/test_metadata_manager.py new file mode 100644 index 0000000..887a599 --- /dev/null +++ b/tfbpapi/tests/datainfo/test_metadata_manager.py @@ -0,0 +1,319 @@ +"""Tests for MetadataManager.""" + +import pandas as pd +import pytest + +from tfbpapi.datainfo import DataCard, MetadataManager +from tfbpapi.datainfo.models import FieldRole + + +class TestMetadataManagerBasic: + """Basic tests for MetadataManager instantiation and structure.""" + + def test_instantiation(self): + """Test MetadataManager can be instantiated.""" + mgr = MetadataManager() + assert mgr is not None + assert hasattr(mgr, "_conn") + assert hasattr(mgr, "_registered_datasets") + assert hasattr(mgr, "_view_names") + + def test_instantiation_with_cache(self): + """Test MetadataManager with cache enabled.""" + from pathlib import Path + + cache_dir = Path("/tmp/metadata_cache") + mgr = MetadataManager(cache_dir=cache_dir, cache=True) + assert mgr._cache_dir == cache_dir + assert mgr._cache_enabled is True + + def test_get_active_configs_empty(self): + """Test get_active_configs with no registered datasets.""" + mgr = MetadataManager() + configs = mgr.get_active_configs() + assert configs == [] + + def test_get_summary_empty(self): + """Test get_summary with no registered datasets.""" + mgr = MetadataManager() + summary = mgr.get_summary() + assert isinstance(summary, pd.DataFrame) + assert len(summary) == 0 + + +class TestDataCardExtractMetadataSchema: + """Tests for DataCard.extract_metadata_schema method.""" + + def test_extract_metadata_schema_structure(self): + """Test that extract_metadata_schema returns correct structure.""" + # Note: This test uses a mock since we need a real datacard + # In actual testing, we'd use a real HuggingFace dataset + # For now, we just test the structure + + # Mock test - verify the method exists + from tfbpapi.datainfo.datacard import DataCard + + assert hasattr(DataCard, "extract_metadata_schema") + + +class TestMetadataManagerHelpers: + """Tests for MetadataManager helper methods.""" + + def test_sanitize_view_name(self): + """Test view name sanitization.""" + mgr = MetadataManager() + view_name = mgr._sanitize_view_name("BrentLab/dataset-name", "config_name") + assert view_name == "BrentLab_dataset_name_config_name_metadata" + assert " " not in view_name + assert "/" not in view_name + assert "-" not in view_name + + def test_format_compound_simple(self): + """Test formatting compound as simple string.""" + mgr = MetadataManager() + result = mgr._format_compound("carbon_source", "D-glucose") + assert result == "carbon_source:D-glucose" + + def test_format_compound_with_percent(self): + """Test formatting compound with concentration percent.""" + mgr = MetadataManager() + compound = {"name": "D-glucose", "concentration_percent": 2.0} + result = mgr._format_compound("carbon_source", compound) + assert result == "carbon_source:D-glucose@2.0%" + + def test_format_compound_with_grams(self): + """Test formatting compound with g/L concentration.""" + mgr = MetadataManager() + compound = {"name": "ammonium_sulfate", "concentration_g_per_l": 5.0} + result = mgr._format_compound("nitrogen_source", compound) + assert result == "nitrogen_source:ammonium_sulfate@5.0g/L" + + def test_flatten_condition_definition_empty(self): + """Test flattening empty definition.""" + mgr = MetadataManager() + result = mgr._flatten_condition_definition({}) + assert result["growth_media"] == "unspecified" + assert result["components"] == "" + + def test_flatten_condition_definition_with_media(self): + """Test flattening definition with media.""" + mgr = MetadataManager() + definition = { + "environmental_conditions": { + "media": { + "name": "YPD", + "carbon_source": [ + {"name": "D-glucose", "concentration_percent": 2.0} + ], + "nitrogen_source": ["yeast_extract", "peptone"], + } + } + } + result = mgr._flatten_condition_definition(definition) + assert result["growth_media"] == "YPD" + assert "carbon_source:D-glucose@2.0%" in result["components"] + assert "nitrogen_source:yeast_extract" in result["components"] + assert "nitrogen_source:peptone" in result["components"] + + +class TestComponentSeparators: + """Tests for separator conventions.""" + + def test_separator_constants(self): + """Test that separator constants are defined.""" + from tfbpapi.datainfo.metadata_manager import COMPONENT_SEPARATORS + + assert COMPONENT_SEPARATORS["type_value"] == ":" + assert COMPONENT_SEPARATORS["value_conc"] == "@" + assert COMPONENT_SEPARATORS["components"] == ";" + assert COMPONENT_SEPARATORS["types"] == "|" + + +class TestThreeLevelConditionHierarchy: + """Tests for three-level experimental condition hierarchy support.""" + + def test_flatten_experimental_conditions_empty(self): + """Test flattening empty ExperimentalConditions.""" + mgr = MetadataManager() + + # Create a simple mock object with no conditions + class MockExpConditions: + environmental_conditions = None + strain_background = None + + result = mgr._flatten_experimental_conditions(MockExpConditions()) + assert isinstance(result, dict) + assert len(result) == 0 + + def test_flatten_experimental_conditions_with_temperature(self): + """Test flattening conditions with temperature.""" + mgr = MetadataManager() + + class MockEnv: + temperature_celsius = 30 + cultivation_method = None + media = None + growth_phase = None + chemical_treatments = None + drug_treatments = None + heat_treatment = None + induction = None + + class MockExpConditions: + environmental_conditions = MockEnv() + strain_background = None + + result = mgr._flatten_experimental_conditions(MockExpConditions()) + assert result["temperature_celsius"] == 30 + + def test_flatten_experimental_conditions_with_cultivation_method(self): + """Test flattening conditions with cultivation method.""" + mgr = MetadataManager() + + class MockEnv: + temperature_celsius = None + cultivation_method = "chemostat" + media = None + growth_phase = None + chemical_treatments = None + drug_treatments = None + heat_treatment = None + induction = None + + class MockExpConditions: + environmental_conditions = MockEnv() + strain_background = None + + result = mgr._flatten_experimental_conditions(MockExpConditions()) + assert result["cultivation_method"] == "chemostat" + + def test_flatten_experimental_conditions_with_media(self): + """Test flattening conditions with media information.""" + mgr = MetadataManager() + + class MockCompound: + compound = "D-glucose" + concentration_percent = 1.0 + + def model_dump(self): + return { + "name": self.compound, + "concentration_percent": self.concentration_percent, + } + + class MockMedia: + name = "minimal" + carbon_source = [MockCompound()] + nitrogen_source = None + phosphate_source = None + additives = None + + class MockEnv: + temperature_celsius = None + cultivation_method = None + media = MockMedia() + growth_phase = None + chemical_treatments = None + drug_treatments = None + heat_treatment = None + induction = None + + class MockExpConditions: + environmental_conditions = MockEnv() + strain_background = None + + result = mgr._flatten_experimental_conditions(MockExpConditions()) + assert result["growth_media"] == "minimal" + assert "carbon_source:D-glucose@1.0%" in result["components"] + + def test_flatten_experimental_conditions_with_strain_background(self): + """Test flattening conditions with strain background.""" + mgr = MetadataManager() + + class MockExpConditions: + environmental_conditions = None + strain_background = "BY4741" + + result = mgr._flatten_experimental_conditions(MockExpConditions()) + assert result["strain_background"] == "BY4741" + + def test_datacard_extract_schema_includes_top_level_conditions(self): + """Test that extract_metadata_schema includes top_level_conditions.""" + # This test needs to use real datacards with experimental conditions + # For now, we verify the keys exist in the schema + from unittest.mock import MagicMock, Mock + + # Create a mock DataCard with experimental conditions + mock_card = Mock() + mock_card.repo_id = "test/repo" + + # Mock dataset card with experimental conditions + mock_dataset_card = Mock() + mock_dataset_card.experimental_conditions = Mock() # Non-None + mock_card.dataset_card = mock_dataset_card + + # Mock config with no config-level conditions + mock_config = Mock() + mock_config.experimental_conditions = None + mock_config.dataset_info = Mock() + mock_config.dataset_info.features = [] + + mock_card.get_config = Mock(return_value=mock_config) + + # Call the method via the actual DataCard class + from tfbpapi.datainfo.datacard import DataCard + + schema = DataCard.extract_metadata_schema(mock_card, "test_config") + + # Verify top_level_conditions is in schema + assert "top_level_conditions" in schema + assert "config_level_conditions" in schema + + def test_datacard_extract_schema_includes_config_level_conditions(self): + """Test that extract_metadata_schema includes config_level_conditions.""" + from unittest.mock import Mock + + # Create a mock DataCard + mock_card = Mock() + mock_card.repo_id = "test/repo" + + # Mock dataset card with NO repo-level conditions + mock_dataset_card = Mock() + mock_dataset_card.experimental_conditions = None + mock_card.dataset_card = mock_dataset_card + + # Mock config WITH config-level conditions + mock_config = Mock() + mock_config.experimental_conditions = Mock() # Non-None + mock_config.dataset_info = Mock() + mock_config.dataset_info.features = [] + + mock_card.get_config = Mock(return_value=mock_config) + + # Call the method + from tfbpapi.datainfo.datacard import DataCard + + schema = DataCard.extract_metadata_schema(mock_card, "test_config") + + # Verify config_level_conditions is populated + assert schema["config_level_conditions"] is not None + + +# Integration test placeholder +class TestMetadataManagerIntegration: + """Integration tests for MetadataManager (require real HF datasets).""" + + @pytest.mark.skip(reason="Requires real HuggingFace dataset access") + def test_register_real_dataset(self): + """Test registering a real HuggingFace dataset.""" + # This would test with a real dataset like: + # mgr = MetadataManager() + # mgr.register("BrentLab/some_real_dataset") + # assert len(mgr.get_active_configs()) > 0 + pass + + @pytest.mark.skip(reason="Requires real HuggingFace dataset access") + def test_query_across_datasets(self): + """Test querying across multiple datasets.""" + # This would test cross-dataset queries + pass diff --git a/tfbpapi/tests/datainfo/test_models.py b/tfbpapi/tests/datainfo/test_models.py index 618a392..8bcee9b 100644 --- a/tfbpapi/tests/datainfo/test_models.py +++ b/tfbpapi/tests/datainfo/test_models.py @@ -4,13 +4,20 @@ from pydantic import ValidationError from tfbpapi.datainfo.models import ( + ChemicalTreatmentInfo, + CompoundInfo, DataFileInfo, DatasetCard, DatasetConfig, DatasetInfo, DatasetType, + EnvironmentalConditions, + ExperimentalConditions, ExtractedMetadata, FeatureInfo, + GrowthPhaseInfo, + HeatTreatmentInfo, + MediaInfo, MetadataRelationship, PartitioningInfo, ) @@ -38,6 +45,328 @@ def test_invalid_dataset_type(self): DatasetType("invalid_type") +class TestCompoundInfo: + """Test CompoundInfo model.""" + + def test_compound_info_with_percentage(self): + """Test CompoundInfo with percentage concentration.""" + compound = CompoundInfo(compound="glucose", concentration_percent=2.0) + assert compound.compound == "glucose" + assert compound.concentration_percent == 2.0 + assert compound.concentration_g_per_l is None + assert compound.specifications is None + + def test_compound_info_with_g_per_l(self): + """Test CompoundInfo with g/L concentration.""" + compound = CompoundInfo(compound="dextrose", concentration_g_per_l=20.0) + assert compound.compound == "dextrose" + assert compound.concentration_g_per_l == 20.0 + assert compound.concentration_percent is None + + def test_compound_info_with_specifications(self): + """Test CompoundInfo with specifications.""" + compound = CompoundInfo( + compound="yeast_nitrogen_base", + concentration_g_per_l=6.7, + specifications=["without_amino_acids"], + ) + assert compound.compound == "yeast_nitrogen_base" + assert compound.specifications == ["without_amino_acids"] + + def test_compound_info_minimal(self): + """Test CompoundInfo with only required fields.""" + compound = CompoundInfo(compound="ethanol") + assert compound.compound == "ethanol" + assert compound.concentration_percent is None + assert compound.concentration_g_per_l is None + + +class TestMediaInfo: + """Test MediaInfo model.""" + + def test_media_info_minimal(self): + """Test MediaInfo with required fields only.""" + media = MediaInfo( + name="minimal", + carbon_source=[CompoundInfo(compound="glucose", concentration_percent=2.0)], + nitrogen_source=[ + CompoundInfo(compound="ammonium_sulfate", concentration_g_per_l=5.0) + ], + ) + assert media.name == "minimal" + assert len(media.carbon_source) == 1 + assert len(media.nitrogen_source) == 1 + assert media.phosphate_source is None + + def test_media_info_with_phosphate(self): + """Test MediaInfo with phosphate source.""" + media = MediaInfo( + name="synthetic_complete", + carbon_source=[CompoundInfo(compound="glucose", concentration_percent=2.0)], + nitrogen_source=[ + CompoundInfo(compound="yeast_nitrogen_base", concentration_g_per_l=6.7) + ], + phosphate_source=[ + CompoundInfo(compound="potassium_phosphate", concentration_g_per_l=1.0) + ], + ) + assert media.phosphate_source is not None + assert len(media.phosphate_source) == 1 + + def test_media_info_multiple_compounds(self): + """Test MediaInfo with multiple carbon/nitrogen sources.""" + media = MediaInfo( + name="YPD", + carbon_source=[ + CompoundInfo(compound="glucose", concentration_percent=2.0), + CompoundInfo(compound="glycerol", concentration_percent=3.0), + ], + nitrogen_source=[ + CompoundInfo(compound="yeast_extract", concentration_percent=1.0), + CompoundInfo(compound="peptone", concentration_percent=2.0), + ], + ) + assert len(media.carbon_source) == 2 + assert len(media.nitrogen_source) == 2 + + +class TestGrowthPhaseInfo: + """Test GrowthPhaseInfo model.""" + + def test_growth_phase_with_od600(self): + """Test GrowthPhaseInfo with OD600.""" + phase = GrowthPhaseInfo(od600=0.5, stage="mid_log_phase") + assert phase.od600 == 0.5 + assert phase.stage == "mid_log_phase" + + def test_growth_phase_od600_only(self): + """Test GrowthPhaseInfo with only OD600.""" + phase = GrowthPhaseInfo(od600=0.8) + assert phase.od600 == 0.8 + assert phase.stage is None + + def test_growth_phase_stage_only(self): + """Test GrowthPhaseInfo with only stage.""" + phase = GrowthPhaseInfo(stage="stationary_phase") + assert phase.stage == "stationary_phase" + assert phase.od600 is None + + def test_growth_phase_empty(self): + """Test GrowthPhaseInfo with no values.""" + phase = GrowthPhaseInfo() + assert phase.od600 is None + assert phase.stage is None + + +class TestChemicalTreatmentInfo: + """Test ChemicalTreatmentInfo model.""" + + def test_chemical_treatment_with_percent(self): + """Test ChemicalTreatmentInfo with percentage concentration.""" + treatment = ChemicalTreatmentInfo( + compound="ethanol", + concentration_percent=5.0, + duration_minutes=30, + ) + assert treatment.compound == "ethanol" + assert treatment.concentration_percent == 5.0 + assert treatment.duration_minutes == 30 + assert treatment.concentration_molar is None + + def test_chemical_treatment_with_molar(self): + """Test ChemicalTreatmentInfo with molar concentration.""" + treatment = ChemicalTreatmentInfo( + compound="rapamycin", + concentration_molar=0.0002, + duration_hours=2.0, + ) + assert treatment.compound == "rapamycin" + assert treatment.concentration_molar == 0.0002 + assert treatment.duration_hours == 2.0 + + def test_chemical_treatment_minimal(self): + """Test ChemicalTreatmentInfo with only compound.""" + treatment = ChemicalTreatmentInfo(compound="hydrogen_peroxide") + assert treatment.compound == "hydrogen_peroxide" + assert treatment.concentration_percent is None + assert treatment.duration_minutes is None + + +class TestHeatTreatmentInfo: + """Test HeatTreatmentInfo model.""" + + def test_heat_treatment_basic(self): + """Test HeatTreatmentInfo with required field.""" + treatment = HeatTreatmentInfo(duration_minutes=30) + assert treatment.duration_minutes == 30 + assert treatment.description is None + + def test_heat_treatment_with_description(self): + """Test HeatTreatmentInfo with description.""" + treatment = HeatTreatmentInfo( + duration_minutes=15, description="Heat shock at 37C" + ) + assert treatment.duration_minutes == 15 + assert treatment.description == "Heat shock at 37C" + + def test_heat_treatment_missing_duration(self): + """Test HeatTreatmentInfo requires duration_minutes.""" + with pytest.raises(ValidationError): + HeatTreatmentInfo(description="Some treatment") + + +class TestEnvironmentalConditions: + """Test EnvironmentalConditions model.""" + + def test_environmental_conditions_minimal(self): + """Test EnvironmentalConditions with no fields.""" + env = EnvironmentalConditions() + assert env.temperature_celsius is None + assert env.cultivation_method is None + assert env.growth_phase_at_harvest is None + assert env.media is None + assert env.chemical_treatment is None + assert env.heat_treatment is None + + def test_environmental_conditions_temperature(self): + """Test EnvironmentalConditions with temperature.""" + env = EnvironmentalConditions(temperature_celsius=30.0) + assert env.temperature_celsius == 30.0 + + def test_environmental_conditions_with_media(self): + """Test EnvironmentalConditions with media info.""" + media = MediaInfo( + name="YPD", + carbon_source=[CompoundInfo(compound="glucose", concentration_percent=2.0)], + nitrogen_source=[ + CompoundInfo(compound="peptone", concentration_percent=2.0) + ], + ) + env = EnvironmentalConditions( + temperature_celsius=30.0, + cultivation_method="batch_culture", + media=media, + ) + assert env.media is not None + assert env.media.name == "YPD" + + def test_environmental_conditions_with_growth_phase(self): + """Test EnvironmentalConditions with growth phase.""" + growth_phase = GrowthPhaseInfo(od600=0.5, stage="mid_log_phase") + env = EnvironmentalConditions(growth_phase_at_harvest=growth_phase) + assert env.growth_phase_at_harvest is not None + assert env.growth_phase_at_harvest.od600 == 0.5 + + def test_environmental_conditions_with_chemical_treatment(self): + """Test EnvironmentalConditions with chemical treatment.""" + treatment = ChemicalTreatmentInfo( + compound="rapamycin", + concentration_molar=0.0002, + duration_minutes=120, + ) + env = EnvironmentalConditions(chemical_treatment=treatment) + assert env.chemical_treatment is not None + assert env.chemical_treatment.compound == "rapamycin" + + def test_environmental_conditions_with_heat_treatment(self): + """Test EnvironmentalConditions with heat treatment.""" + treatment = HeatTreatmentInfo(duration_minutes=30, description="Heat shock") + env = EnvironmentalConditions(heat_treatment=treatment) + assert env.heat_treatment is not None + assert env.heat_treatment.duration_minutes == 30 + + def test_environmental_conditions_complete(self): + """Test EnvironmentalConditions with all fields.""" + media = MediaInfo( + name="minimal", + carbon_source=[CompoundInfo(compound="glucose", concentration_percent=2.0)], + nitrogen_source=[ + CompoundInfo(compound="ammonium_sulfate", concentration_g_per_l=5.0) + ], + ) + growth_phase = GrowthPhaseInfo(od600=0.6, stage="mid_log_phase") + chemical = ChemicalTreatmentInfo( + compound="rapamycin", concentration_molar=0.0002, duration_minutes=60 + ) + heat = HeatTreatmentInfo(duration_minutes=15, description="Brief heat shock") + + env = EnvironmentalConditions( + temperature_celsius=30.0, + cultivation_method="batch_culture", + growth_phase_at_harvest=growth_phase, + media=media, + chemical_treatment=chemical, + heat_treatment=heat, + ) + + assert env.temperature_celsius == 30.0 + assert env.cultivation_method == "batch_culture" + assert env.growth_phase_at_harvest.od600 == 0.6 + assert env.media.name == "minimal" + assert env.chemical_treatment.compound == "rapamycin" + assert env.heat_treatment.duration_minutes == 15 + + def test_environmental_conditions_extra_fields_allowed(self): + """Test that EnvironmentalConditions allows extra fields.""" + env = EnvironmentalConditions( + temperature_celsius=30.0, custom_field="custom_value" + ) + assert env.temperature_celsius == 30.0 + + +class TestExperimentalConditions: + """Test ExperimentalConditions model.""" + + def test_experimental_conditions_minimal(self): + """Test ExperimentalConditions with no fields.""" + exp = ExperimentalConditions() + assert exp.environmental_conditions is None + assert exp.strain_background is None + + def test_experimental_conditions_with_strain(self): + """Test ExperimentalConditions with strain background.""" + exp = ExperimentalConditions(strain_background="BY4741") + assert exp.strain_background == "BY4741" + + def test_experimental_conditions_with_environmental(self): + """Test ExperimentalConditions with environmental conditions.""" + env = EnvironmentalConditions( + temperature_celsius=30.0, cultivation_method="batch_culture" + ) + exp = ExperimentalConditions(environmental_conditions=env) + assert exp.environmental_conditions is not None + assert exp.environmental_conditions.temperature_celsius == 30.0 + + def test_experimental_conditions_complete(self): + """Test ExperimentalConditions with all fields.""" + env = EnvironmentalConditions( + temperature_celsius=30.0, + cultivation_method="batch_culture", + media=MediaInfo( + name="YPD", + carbon_source=[ + CompoundInfo(compound="glucose", concentration_percent=2.0) + ], + nitrogen_source=[ + CompoundInfo(compound="peptone", concentration_percent=2.0) + ], + ), + ) + exp = ExperimentalConditions( + environmental_conditions=env, strain_background="BY4741" + ) + assert exp.strain_background == "BY4741" + assert exp.environmental_conditions.temperature_celsius == 30.0 + assert exp.environmental_conditions.media.name == "YPD" + + def test_experimental_conditions_extra_fields_allowed(self): + """Test that ExperimentalConditions allows extra fields.""" + exp = ExperimentalConditions( + strain_background="BY4741", custom_metadata="custom_value" + ) + assert exp.strain_background == "BY4741" + + class TestFeatureInfo: """Test FeatureInfo model.""" @@ -244,7 +573,8 @@ def test_dataset_config_applies_to_validation_error( with pytest.raises( ValidationError, - match="applies_to field is only valid for metadata dataset types", + match="applies_to field is only valid for " + "metadata and qc_data dataset types", ): DatasetConfig( config_name="invalid_config", diff --git a/tfbpapi/tests/datainfo/test_real_datacards.py b/tfbpapi/tests/datainfo/test_real_datacards.py new file mode 100644 index 0000000..32bdaf6 --- /dev/null +++ b/tfbpapi/tests/datainfo/test_real_datacards.py @@ -0,0 +1,697 @@ +""" +Test real datacards from the HuggingFace collection. + +This test suite validates that all real datacards from the BrentLab collection parse +correctly with the updated models.py and specification. + +""" + +import warnings + +import pytest +import yaml + +from tfbpapi.datainfo.models import DatasetCard + +# Real datacard YAML strings from the collection +BARKAI_COMPENDIUM = """ +license: mit +language: +- en +tags: +- transcription-factor +- binding +- chec-seq +- genomics +- biology +pretty_name: Barkai ChEC-seq Compendium +size_categories: + - 100M 0 + + # Verify config has required fields + config = card.configs[0] + assert config.config_name is not None + assert config.dataset_type is not None + assert config.dataset_info is not None + assert config.dataset_info.features is not None + assert len(config.dataset_info.features) > 0 + + +def test_harbison_2004_condition_definitions(): + """Test that harbison_2004 field-level definitions parse correctly.""" + data = yaml.safe_load(HARBISON_2004) + card = DatasetCard(**data) + + # Find the config + config = card.configs[0] + assert config.config_name == "harbison_2004" + + # Find condition feature + condition_feature = next( + f for f in config.dataset_info.features if f.name == "condition" + ) + + # Should have definitions + assert condition_feature.definitions is not None + assert "YPD" in condition_feature.definitions + assert "Acid" in condition_feature.definitions + assert "BUT14" in condition_feature.definitions + + # YPD definition should have environmental conditions + ypd_def = condition_feature.definitions["YPD"] + assert "environmental_conditions" in ypd_def + + # Acid definition should have target_pH in chemical_treatment + acid_def = condition_feature.definitions["Acid"] + assert "environmental_conditions" in acid_def + assert "chemical_treatment" in acid_def["environmental_conditions"] + assert "target_pH" in acid_def["environmental_conditions"]["chemical_treatment"] + + # BUT14 should have media additives + but14_def = condition_feature.definitions["BUT14"] + assert "environmental_conditions" in but14_def + assert "media" in but14_def["environmental_conditions"] + assert "additives" in but14_def["environmental_conditions"]["media"] + + +def test_hughes_2006_induction(): + """Test that hughes_2006 induction field parses correctly.""" + data = yaml.safe_load(HUGHES_2006) + card = DatasetCard(**data) + + # Check experimental conditions + assert card.configs[0].experimental_conditions is not None + exp_conds = card.configs[0].experimental_conditions + assert exp_conds.environmental_conditions is not None + env_conds = exp_conds.environmental_conditions + + # Check induction field + assert env_conds.induction is not None + assert env_conds.induction.inducer is not None + assert env_conds.induction.inducer.compound == "D-galactose" + assert env_conds.induction.duration_hours == 3 + + +def test_kemmeren_2014_growth_phase(): + """Test that kemmeren_2014 growth phase with od600_tolerance parses correctly.""" + data = yaml.safe_load(KEMMEREN_2014) + card = DatasetCard(**data) + + # Check growth phase + exp_conds = card.experimental_conditions + assert exp_conds is not None + assert exp_conds.environmental_conditions is not None + env_conds = exp_conds.environmental_conditions + + assert env_conds.growth_phase_at_harvest is not None + growth_phase = env_conds.growth_phase_at_harvest + assert growth_phase.phase == "early_mid_log" + assert growth_phase.od600 == 0.6 + assert growth_phase.od600_tolerance == 0.1 + + +def test_hu_2007_strain_background_in_definitions(): + """Test that strain_background in field definitions parses correctly.""" + data = yaml.safe_load(HU_2007) + card = DatasetCard(**data) + + # Find heat_shock feature + config = card.configs[0] + heat_shock_feature = next( + f for f in config.dataset_info.features if f.name == "heat_shock" + ) + + # Check definitions + assert heat_shock_feature.definitions is not None + assert "true" in heat_shock_feature.definitions + + # Check strain_background in definition + true_def = heat_shock_feature.definitions["true"] + assert "strain_background" in true_def + + +def test_field_role_validation(): + """Test that FieldRole enum validation works correctly.""" + # This should parse successfully with valid roles + data = yaml.safe_load(CALLINGCARDS) + card = DatasetCard(**data) + + # Find a feature with a role + config = card.configs[0] + regulator_feature = next( + f for f in config.dataset_info.features if f.name == "regulator_locus_tag" + ) + + # Verify role is a FieldRole enum + from tfbpapi.datainfo.models import FieldRole + + assert regulator_feature.role == FieldRole.REGULATOR_IDENTIFIER + + +def test_concentration_fields(): + """Test that various concentration fields parse correctly.""" + data = yaml.safe_load(KEMMEREN_2014) + card = DatasetCard(**data) + + # Check media compounds + env_conds = card.experimental_conditions.environmental_conditions + assert env_conds.media is not None + + # Check carbon source + assert len(env_conds.media.carbon_source) > 0 + carbon = env_conds.media.carbon_source[0] + assert carbon.concentration_percent is not None + + # Check nitrogen source with specifications + assert len(env_conds.media.nitrogen_source) > 0 + nitrogen = env_conds.media.nitrogen_source[0] + assert nitrogen.specifications is not None + assert "without_amino_acids" in nitrogen.specifications + + +def test_extra_fields_do_not_raise_errors(): + """Test that extra fields are accepted (with warnings) but don't raise errors.""" + # All real datacards should parse without ValidationError + # even if they have extra fields + datacards = [ + BARKAI_COMPENDIUM, + CALLINGCARDS, + HARBISON_2004, + HU_2007, + HUGHES_2006, + KEMMEREN_2014, + MAHENDRAWADA_2025, + ROSSI_2021, + ] + + for datacard_yaml in datacards: + data = yaml.safe_load(datacard_yaml) + # This should not raise ValidationError + card = DatasetCard(**data) + assert card is not None + + +def test_empty_nitrogen_source_list(): + """Test that empty nitrogen_source lists are accepted.""" + data = yaml.safe_load(BARKAI_COMPENDIUM) + card = DatasetCard(**data) + + # Check that nitrogen_source is an empty list + env_conds = card.experimental_conditions.environmental_conditions + assert env_conds.media is not None + assert env_conds.media.nitrogen_source == [] + + +def test_media_additives(): + """Test that media additives parse correctly.""" + data = yaml.safe_load(HARBISON_2004) + card = DatasetCard(**data) + + # Find BUT14 condition definition + config = card.configs[0] + condition_feature = next( + f for f in config.dataset_info.features if f.name == "condition" + ) + but14_def = condition_feature.definitions["BUT14"] + + # Check additives + env_conds_dict = but14_def["environmental_conditions"] + media = env_conds_dict["media"] + assert "additives" in media + additives = media["additives"] + assert len(additives) > 0 + assert additives[0]["compound"] == "butanol" + assert additives[0]["concentration_percent"] == 1 + + +def test_strain_background_formats(): + """Test that strain_background accepts both string and dict formats.""" + # String format + data1 = yaml.safe_load(BARKAI_COMPENDIUM) + card1 = DatasetCard(**data1) + assert card1.experimental_conditions is not None + assert card1.experimental_conditions.strain_background == "BY4741" + + # String format in rossi + data2 = yaml.safe_load(ROSSI_2021) + card2 = DatasetCard(**data2) + assert card2.experimental_conditions is not None + assert card2.experimental_conditions.strain_background == "W303" + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) From 1deb52978739a55f0f3e910cb1c4ca6ed8b2517d Mon Sep 17 00:00:00 2001 From: chasem Date: Tue, 9 Dec 2025 13:26:48 -0600 Subject: [PATCH 14/49] rendered metadata explorer notebook --- .../metadata_explorer_tutorial.ipynb | 465 +++++++++++++++++- 1 file changed, 440 insertions(+), 25 deletions(-) diff --git a/docs/tutorials/metadata_explorer_tutorial.ipynb b/docs/tutorials/metadata_explorer_tutorial.ipynb index 0ba9cda..bcea184 100644 --- a/docs/tutorials/metadata_explorer_tutorial.ipynb +++ b/docs/tutorials/metadata_explorer_tutorial.ipynb @@ -61,10 +61,74 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, - "outputs": [], - "source": "# Load harbison_2004 datacard\nharbison_card = DataCard(repo_id=\"BrentLab/harbison_2004\")\n\n# Extract metadata schema for the harbison_2004 config\nharbison_schema = harbison_card.extract_metadata_schema(\"harbison_2004\")\n\nprint(\"Harbison 2004 Metadata Schema\")\nprint(\"=\" * 60)\nprint(f\"Regulator Fields: {harbison_schema['regulator_fields']}\")\nprint(f\"Target Fields: {harbison_schema['target_fields']}\")\nprint(f\"Condition Fields: {harbison_schema['condition_fields']}\")\n\n# Check for repo-level conditions\nif harbison_schema.get('top_level_conditions'):\n print(\"\\nRepo-Level Conditions (apply to ALL samples):\")\n top_cond = harbison_schema['top_level_conditions']\n if top_cond.strain_background:\n print(f\" - Strain background: {top_cond.strain_background}\")\n if top_cond.environmental_conditions:\n print(\" - Environmental conditions defined\")\nelse:\n print(\"\\nRepo-Level Conditions: None\")\n\n# Check for config-level conditions\nif harbison_schema.get('config_level_conditions'):\n print(\"\\nConfig-Level Conditions:\")\n print(\" (Defined for this config)\")\nelse:\n print(\"\\nConfig-Level Conditions: None\")\n\n# Show field-level condition definitions\nprint(f\"\\nField-Level Conditions (vary by sample):\")\nprint(f\" - condition: {len(harbison_schema['condition_definitions'].get('condition', {}))} conditions defined\")\n\n# Show first few condition definitions\nif 'condition' in harbison_schema['condition_definitions']:\n print(\"\\n Sample Conditions:\")\n for cond_name in list(harbison_schema['condition_definitions']['condition'].keys())[:3]:\n print(f\" - {cond_name}\")" + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Harbison 2004 Metadata Schema\n", + "============================================================\n", + "Regulator Fields: ['regulator_locus_tag', 'regulator_symbol']\n", + "Target Fields: ['target_locus_tag', 'target_symbol']\n", + "Condition Fields: ['condition']\n", + "\n", + "Repo-Level Conditions: None\n", + "\n", + "Config-Level Conditions: None\n", + "\n", + "Field-Level Conditions (vary by sample):\n", + " - condition: 14 conditions defined\n", + "\n", + " Sample Conditions:\n", + " - YPD\n", + " - SM\n", + " - RAPA\n" + ] + } + ], + "source": [ + "# Load harbison_2004 datacard\n", + "harbison_card = DataCard(repo_id=\"BrentLab/harbison_2004\")\n", + "\n", + "# Extract metadata schema for the harbison_2004 config\n", + "harbison_schema = harbison_card.extract_metadata_schema(\"harbison_2004\")\n", + "\n", + "print(\"Harbison 2004 Metadata Schema\")\n", + "print(\"=\" * 60)\n", + "print(f\"Regulator Fields: {harbison_schema['regulator_fields']}\")\n", + "print(f\"Target Fields: {harbison_schema['target_fields']}\")\n", + "print(f\"Condition Fields: {harbison_schema['condition_fields']}\")\n", + "\n", + "# Check for repo-level conditions\n", + "if harbison_schema.get('top_level_conditions'):\n", + " print(\"\\nRepo-Level Conditions (apply to ALL samples):\")\n", + " top_cond = harbison_schema['top_level_conditions']\n", + " if top_cond.strain_background:\n", + " print(f\" - Strain background: {top_cond.strain_background}\")\n", + " if top_cond.environmental_conditions:\n", + " print(\" - Environmental conditions defined\")\n", + "else:\n", + " print(\"\\nRepo-Level Conditions: None\")\n", + "\n", + "# Check for config-level conditions\n", + "if harbison_schema.get('config_level_conditions'):\n", + " print(\"\\nConfig-Level Conditions:\")\n", + " print(\" (Defined for this config)\")\n", + "else:\n", + " print(\"\\nConfig-Level Conditions: None\")\n", + "\n", + "# Show field-level condition definitions\n", + "print(f\"\\nField-Level Conditions (vary by sample):\")\n", + "print(f\" - condition: {len(harbison_schema['condition_definitions'].get('condition', {}))} conditions defined\")\n", + "\n", + "# Show first few condition definitions\n", + "if 'condition' in harbison_schema['condition_definitions']:\n", + " print(\"\\n Sample Conditions:\")\n", + " for cond_name in list(harbison_schema['condition_definitions']['condition'].keys())[:3]:\n", + " print(f\" - {cond_name}\")" + ] }, { "cell_type": "markdown", @@ -75,10 +139,78 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, - "outputs": [], - "source": "# Load hackett_2020 datacard\nhackett_card = DataCard(repo_id=\"BrentLab/hackett_2020\")\n\n# Extract metadata schema\nhackett_schema = hackett_card.extract_metadata_schema(\"hackett_2020\")\n\nprint(\"Hackett 2020 Metadata Schema\")\nprint(\"=\" * 60)\nprint(f\"Regulator Fields: {hackett_schema['regulator_fields']}\")\nprint(f\"Target Fields: {hackett_schema['target_fields']}\")\nprint(f\"Condition Fields: {hackett_schema['condition_fields']}\")\n\n# Show repo-level (top-level) experimental conditions\nif hackett_schema.get('top_level_conditions'):\n print(\"\\nRepo-Level Conditions (apply to ALL samples):\")\n top_cond = hackett_schema['top_level_conditions']\n \n # Check for structured environmental_conditions\n if top_cond.environmental_conditions:\n env = top_cond.environmental_conditions\n if env.temperature_celsius is not None:\n print(f\" - Temperature: {env.temperature_celsius} C\")\n if env.cultivation_method:\n print(f\" - Cultivation: {env.cultivation_method}\")\n if env.media:\n print(f\" - Media: {env.media.name}\")\n if env.media.carbon_source:\n for cs in env.media.carbon_source:\n print(f\" - Carbon source: {cs.compound} @ {cs.concentration_percent}%\")\n \n # Check for extra fields (alternate structure)\n if hasattr(top_cond, 'model_extra') and top_cond.model_extra:\n for key, value in top_cond.model_extra.items():\n print(f\" - {key}: {value}\")\n\n# Show config-level conditions\nif hackett_schema.get('config_level_conditions'):\n print(\"\\nConfig-Level Conditions:\")\n print(\" (Conditions defined for this config)\")\nelse:\n print(\"\\nConfig-Level Conditions: None\")\n\n# Show field-level condition definitions\nif hackett_schema['condition_definitions']:\n print(\"\\nField-Level Conditions (vary by sample):\")\n for field_name, definitions in hackett_schema['condition_definitions'].items():\n print(f\" - {field_name}: {len(definitions)} levels defined\")\n print(f\" Levels: {list(definitions.keys())}\")" + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hackett 2020 Metadata Schema\n", + "============================================================\n", + "Regulator Fields: ['regulator_locus_tag', 'regulator_symbol']\n", + "Target Fields: ['target_locus_tag', 'target_symbol']\n", + "Condition Fields: ['time', 'mechanism', 'restriction', 'date', 'strain']\n", + "\n", + "Repo-Level Conditions (apply to ALL samples):\n", + " - growth_medium: minimal\n", + " - temperature: 30C\n", + " - cultivation_method: chemostat\n", + "\n", + "Config-Level Conditions: None\n" + ] + } + ], + "source": [ + "# Load hackett_2020 datacard\n", + "hackett_card = DataCard(repo_id=\"BrentLab/hackett_2020\")\n", + "\n", + "# Extract metadata schema\n", + "hackett_schema = hackett_card.extract_metadata_schema(\"hackett_2020\")\n", + "\n", + "print(\"Hackett 2020 Metadata Schema\")\n", + "print(\"=\" * 60)\n", + "print(f\"Regulator Fields: {hackett_schema['regulator_fields']}\")\n", + "print(f\"Target Fields: {hackett_schema['target_fields']}\")\n", + "print(f\"Condition Fields: {hackett_schema['condition_fields']}\")\n", + "\n", + "# Show repo-level (top-level) experimental conditions\n", + "if hackett_schema.get('top_level_conditions'):\n", + " print(\"\\nRepo-Level Conditions (apply to ALL samples):\")\n", + " top_cond = hackett_schema['top_level_conditions']\n", + " \n", + " # Check for structured environmental_conditions\n", + " if top_cond.environmental_conditions:\n", + " env = top_cond.environmental_conditions\n", + " if env.temperature_celsius is not None:\n", + " print(f\" - Temperature: {env.temperature_celsius} C\")\n", + " if env.cultivation_method:\n", + " print(f\" - Cultivation: {env.cultivation_method}\")\n", + " if env.media:\n", + " print(f\" - Media: {env.media.name}\")\n", + " if env.media.carbon_source:\n", + " for cs in env.media.carbon_source:\n", + " print(f\" - Carbon source: {cs.compound} @ {cs.concentration_percent}%\")\n", + " \n", + " # Check for extra fields (alternate structure)\n", + " if hasattr(top_cond, 'model_extra') and top_cond.model_extra:\n", + " for key, value in top_cond.model_extra.items():\n", + " print(f\" - {key}: {value}\")\n", + "\n", + "# Show config-level conditions\n", + "if hackett_schema.get('config_level_conditions'):\n", + " print(\"\\nConfig-Level Conditions:\")\n", + " print(\" (Conditions defined for this config)\")\n", + "else:\n", + " print(\"\\nConfig-Level Conditions: None\")\n", + "\n", + "# Show field-level condition definitions\n", + "if hackett_schema['condition_definitions']:\n", + " print(\"\\nField-Level Conditions (vary by sample):\")\n", + " for field_name, definitions in hackett_schema['condition_definitions'].items():\n", + " print(f\" - {field_name}: {len(definitions)} levels defined\")\n", + " print(f\" Levels: {list(definitions.keys())}\")" + ] }, { "cell_type": "markdown", @@ -230,7 +362,7 @@ "type": "string" } ], - "ref": "e30d951e-7e84-475f-be44-50ac556c86b1", + "ref": "1c9696e3-e620-4f07-b246-226e4093863f", "rows": [ [ "0", @@ -316,10 +448,105 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, - "outputs": [], - "source": "print(\"Three-Level Experimental Condition Hierarchy\")\nprint(\"=\" * 80)\n\n# Show hackett_2020 as example\nprint(\"\\nExample: hackett_2020 dataset\")\nprint(\"-\" * 80)\n\n# 1. Repo-level (applies to ALL samples)\nprint(\"\\n[1] REPO-LEVEL (DatasetCard.experimental_conditions)\")\nprint(\" Applies to: ALL configs and ALL samples in the repository\")\nif hackett_schema.get('top_level_conditions'):\n top_cond = hackett_schema['top_level_conditions']\n \n # Check structured environmental_conditions\n if top_cond.environmental_conditions:\n env = top_cond.environmental_conditions\n print(f\" - temperature_celsius: {env.temperature_celsius}\")\n print(f\" - cultivation_method: {env.cultivation_method}\")\n print(f\" - media.name: {env.media.name}\")\n print(f\" - media.carbon_source: {env.media.carbon_source[0].compound} @ {env.media.carbon_source[0].concentration_percent}%\")\n \n # Check extra fields (alternate structure used by some datacards)\n if hasattr(top_cond, 'model_extra') and top_cond.model_extra:\n for key, value in top_cond.model_extra.items():\n print(f\" - {key}: {value}\")\n\n# 2. Config-level (applies to samples in this config)\nprint(\"\\n[2] CONFIG-LEVEL (DatasetConfig.experimental_conditions)\")\nprint(\" Applies to: All samples in the 'hackett_2020' config\")\nif hackett_schema.get('config_level_conditions'):\n print(\" - (Specific conditions defined)\")\nelse:\n print(\" - (None - all repo-level conditions inherited)\")\n\n# 3. Field-level (varies by sample)\nprint(\"\\n[3] FIELD-LEVEL (FeatureInfo.definitions)\")\nprint(\" Applies to: Individual samples based on field value\")\nprint(\" - restriction field: 3 levels (P, N, M)\")\nprint(\" - P: Phosphate limitation\")\nprint(\" - N: Nitrogen limitation\")\nprint(\" - M: Magnesium limitation\")\nprint(\" - time field: Various time points (30.0, 60.0, etc.)\")\n\nprint(\"\\n\" + \"=\" * 80)\nprint(\"KEY CONCEPT: Hierarchy Merging\")\nprint(\"-\" * 80)\nprint(\"When MetadataManager creates metadata tables, it merges all three levels:\")\nprint(\" - Repo-level conditions -> columns for ALL samples (temperature: 30C, etc.)\")\nprint(\" - Config-level conditions -> columns for config samples (override repo-level if present)\")\nprint(\" - Field-level conditions -> vary by sample (restriction: P/N/M)\")\nprint(\"\\nPrecedence: Field-level > Config-level > Repo-level\")\nprint(\"\\nResult: Each sample gets columns from all applicable levels!\")" + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Three-Level Experimental Condition Hierarchy\n", + "================================================================================\n", + "\n", + "Example: hackett_2020 dataset\n", + "--------------------------------------------------------------------------------\n", + "\n", + "[1] REPO-LEVEL (DatasetCard.experimental_conditions)\n", + " Applies to: ALL configs and ALL samples in the repository\n", + " - growth_medium: minimal\n", + " - temperature: 30C\n", + " - cultivation_method: chemostat\n", + "\n", + "[2] CONFIG-LEVEL (DatasetConfig.experimental_conditions)\n", + " Applies to: All samples in the 'hackett_2020' config\n", + " - (None - all repo-level conditions inherited)\n", + "\n", + "[3] FIELD-LEVEL (FeatureInfo.definitions)\n", + " Applies to: Individual samples based on field value\n", + " - restriction field: 3 levels (P, N, M)\n", + " - P: Phosphate limitation\n", + " - N: Nitrogen limitation\n", + " - M: Magnesium limitation\n", + " - time field: Various time points (30.0, 60.0, etc.)\n", + "\n", + "================================================================================\n", + "KEY CONCEPT: Hierarchy Merging\n", + "--------------------------------------------------------------------------------\n", + "When MetadataManager creates metadata tables, it merges all three levels:\n", + " - Repo-level conditions -> columns for ALL samples (temperature: 30C, etc.)\n", + " - Config-level conditions -> columns for config samples (override repo-level if present)\n", + " - Field-level conditions -> vary by sample (restriction: P/N/M)\n", + "\n", + "Precedence: Field-level > Config-level > Repo-level\n", + "\n", + "Result: Each sample gets columns from all applicable levels!\n" + ] + } + ], + "source": [ + "print(\"Three-Level Experimental Condition Hierarchy\")\n", + "print(\"=\" * 80)\n", + "\n", + "# Show hackett_2020 as example\n", + "print(\"\\nExample: hackett_2020 dataset\")\n", + "print(\"-\" * 80)\n", + "\n", + "# 1. Repo-level (applies to ALL samples)\n", + "print(\"\\n[1] REPO-LEVEL (DatasetCard.experimental_conditions)\")\n", + "print(\" Applies to: ALL configs and ALL samples in the repository\")\n", + "if hackett_schema.get('top_level_conditions'):\n", + " top_cond = hackett_schema['top_level_conditions']\n", + " \n", + " # Check structured environmental_conditions\n", + " if top_cond.environmental_conditions:\n", + " env = top_cond.environmental_conditions\n", + " print(f\" - temperature_celsius: {env.temperature_celsius}\")\n", + " print(f\" - cultivation_method: {env.cultivation_method}\")\n", + " print(f\" - media.name: {env.media.name}\")\n", + " print(f\" - media.carbon_source: {env.media.carbon_source[0].compound} @ {env.media.carbon_source[0].concentration_percent}%\")\n", + " \n", + " # Check extra fields (alternate structure used by some datacards)\n", + " if hasattr(top_cond, 'model_extra') and top_cond.model_extra:\n", + " for key, value in top_cond.model_extra.items():\n", + " print(f\" - {key}: {value}\")\n", + "\n", + "# 2. Config-level (applies to samples in this config)\n", + "print(\"\\n[2] CONFIG-LEVEL (DatasetConfig.experimental_conditions)\")\n", + "print(\" Applies to: All samples in the 'hackett_2020' config\")\n", + "if hackett_schema.get('config_level_conditions'):\n", + " print(\" - (Specific conditions defined)\")\n", + "else:\n", + " print(\" - (None - all repo-level conditions inherited)\")\n", + "\n", + "# 3. Field-level (varies by sample)\n", + "print(\"\\n[3] FIELD-LEVEL (FeatureInfo.definitions)\")\n", + "print(\" Applies to: Individual samples based on field value\")\n", + "print(\" - restriction field: 3 levels (P, N, M)\")\n", + "print(\" - P: Phosphate limitation\")\n", + "print(\" - N: Nitrogen limitation\")\n", + "print(\" - M: Magnesium limitation\")\n", + "print(\" - time field: Various time points (30.0, 60.0, etc.)\")\n", + "\n", + "print(\"\\n\" + \"=\" * 80)\n", + "print(\"KEY CONCEPT: Hierarchy Merging\")\n", + "print(\"-\" * 80)\n", + "print(\"When MetadataManager creates metadata tables, it merges all three levels:\")\n", + "print(\" - Repo-level conditions -> columns for ALL samples (temperature: 30C, etc.)\")\n", + "print(\" - Config-level conditions -> columns for config samples (override repo-level if present)\")\n", + "print(\" - Field-level conditions -> vary by sample (restriction: P/N/M)\")\n", + "print(\"\\nPrecedence: Field-level > Config-level > Repo-level\")\n", + "print(\"\\nResult: Each sample gets columns from all applicable levels!\")" + ] }, { "cell_type": "markdown", @@ -343,10 +570,39 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, - "outputs": [], - "source": "# Example query (will work once parquet loading is implemented)\n# \n# regulators_query = \"\"\"\n# SELECT DISTINCT \n# dataset,\n# config_name,\n# regulator_symbol,\n# regulator_locus_tag\n# FROM unified_metadata\n# WHERE regulator_symbol != 'unspecified'\n# ORDER BY dataset, regulator_symbol\n# \"\"\"\n# \n# regulators_df = mgr.query(regulators_query)\n# print(f\"\\nFound {len(regulators_df)} unique regulators across datasets\")\n# display(regulators_df.head(10))\n\nprint(\"WARNING: Parquet file loading not yet implemented\")\nprint(\" This query will work once HuggingFace parquet integration is added\")" + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "WARNING: Parquet file loading not yet implemented\n", + " This query will work once HuggingFace parquet integration is added\n" + ] + } + ], + "source": [ + "# Example query (will work once parquet loading is implemented)\n", + "# \n", + "# regulators_query = \"\"\"\n", + "# SELECT DISTINCT \n", + "# dataset,\n", + "# config_name,\n", + "# regulator_symbol,\n", + "# regulator_locus_tag\n", + "# FROM unified_metadata\n", + "# WHERE regulator_symbol != 'unspecified'\n", + "# ORDER BY dataset, regulator_symbol\n", + "# \"\"\"\n", + "# \n", + "# regulators_df = mgr.query(regulators_query)\n", + "# print(f\"\\nFound {len(regulators_df)} unique regulators across datasets\")\n", + "# display(regulators_df.head(10))\n", + "\n", + "print(\"WARNING: Parquet file loading not yet implemented\")\n", + "print(\" This query will work once HuggingFace parquet integration is added\")" + ] }, { "cell_type": "markdown", @@ -357,10 +613,40 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": {}, - "outputs": [], - "source": "# Example: Find GLN3 samples across both datasets\n#\n# gln3_query = \"\"\"\n# SELECT \n# dataset,\n# sample_id,\n# regulator_symbol,\n# condition,\n# time,\n# restriction,\n# growth_media,\n# components\n# FROM unified_metadata\n# WHERE regulator_symbol = 'GLN3'\n# \"\"\"\n# \n# gln3_samples = mgr.query(gln3_query)\n# print(f\"\\nGLN3 samples: {len(gln3_samples)}\")\n# display(gln3_samples)\n\nprint(\"WARNING: Parquet file loading not yet implemented\")" + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "WARNING: Parquet file loading not yet implemented\n" + ] + } + ], + "source": [ + "# Example: Find GLN3 samples across both datasets\n", + "#\n", + "# gln3_query = \"\"\"\n", + "# SELECT \n", + "# dataset,\n", + "# sample_id,\n", + "# regulator_symbol,\n", + "# condition,\n", + "# time,\n", + "# restriction,\n", + "# growth_media,\n", + "# components\n", + "# FROM unified_metadata\n", + "# WHERE regulator_symbol = 'GLN3'\n", + "# \"\"\"\n", + "# \n", + "# gln3_samples = mgr.query(gln3_query)\n", + "# print(f\"\\nGLN3 samples: {len(gln3_samples)}\")\n", + "# display(gln3_samples)\n", + "\n", + "print(\"WARNING: Parquet file loading not yet implemented\")" + ] }, { "cell_type": "markdown", @@ -371,10 +657,38 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "metadata": {}, - "outputs": [], - "source": "# Example: Find samples with D-glucose at 2%\n#\n# glucose_query = \"\"\"\n# SELECT \n# dataset,\n# COUNT(*) as sample_count,\n# growth_media\n# FROM unified_metadata\n# WHERE components LIKE '%carbon_source:D-glucose@2%'\n# GROUP BY dataset, growth_media\n# \"\"\"\n# \n# glucose_samples = mgr.query(glucose_query)\n# print(\"\\nSamples with D-glucose at 2%:\")\n# display(glucose_samples)\n\nprint(\"WARNING: Parquet file loading not yet implemented\")\nprint(\" This demonstrates searching by specific media components with concentrations\")" + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "WARNING: Parquet file loading not yet implemented\n", + " This demonstrates searching by specific media components with concentrations\n" + ] + } + ], + "source": [ + "# Example: Find samples with D-glucose at 2%\n", + "#\n", + "# glucose_query = \"\"\"\n", + "# SELECT \n", + "# dataset,\n", + "# COUNT(*) as sample_count,\n", + "# growth_media\n", + "# FROM unified_metadata\n", + "# WHERE components LIKE '%carbon_source:D-glucose@2%'\n", + "# GROUP BY dataset, growth_media\n", + "# \"\"\"\n", + "# \n", + "# glucose_samples = mgr.query(glucose_query)\n", + "# print(\"\\nSamples with D-glucose at 2%:\")\n", + "# display(glucose_samples)\n", + "\n", + "print(\"WARNING: Parquet file loading not yet implemented\")\n", + "print(\" This demonstrates searching by specific media components with concentrations\")" + ] }, { "cell_type": "markdown", @@ -394,10 +708,50 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "metadata": {}, - "outputs": [], - "source": "from tfbpapi.datainfo.metadata_manager import COMPONENT_SEPARATORS\n\nprint(\"\\nComponent Separator Conventions\")\nprint(\"=\" * 60)\nfor key, sep in COMPONENT_SEPARATORS.items():\n print(f\" {key:20s} '{sep}'\")\n \nprint(\"\\nUsage Examples:\")\nprint(\" growth_media: 'YPD' (simple name)\")\nprint(\" components: 'carbon_source:D-glucose@2%|nitrogen_source:yeast_extract@1%;peptone@2%'\")\nprint(\"\\n Breakdown:\")\nprint(\" - ':' separates type from value (carbon_source:D-glucose)\")\nprint(\" - '@' separates value from concentration (D-glucose@2%)\")\nprint(\" - ';' separates multiple compounds of same type (yeast_extract@1%;peptone@2%)\")\nprint(\" - '|' separates different component types (carbon | nitrogen)\")" + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Component Separator Conventions\n", + "============================================================\n", + " type_value ':'\n", + " value_conc '@'\n", + " components ';'\n", + " types '|'\n", + "\n", + "Usage Examples:\n", + " growth_media: 'YPD' (simple name)\n", + " components: 'carbon_source:D-glucose@2%|nitrogen_source:yeast_extract@1%;peptone@2%'\n", + "\n", + " Breakdown:\n", + " - ':' separates type from value (carbon_source:D-glucose)\n", + " - '@' separates value from concentration (D-glucose@2%)\n", + " - ';' separates multiple compounds of same type (yeast_extract@1%;peptone@2%)\n", + " - '|' separates different component types (carbon | nitrogen)\n" + ] + } + ], + "source": [ + "from tfbpapi.datainfo.metadata_manager import COMPONENT_SEPARATORS\n", + "\n", + "print(\"\\nComponent Separator Conventions\")\n", + "print(\"=\" * 60)\n", + "for key, sep in COMPONENT_SEPARATORS.items():\n", + " print(f\" {key:20s} '{sep}'\")\n", + " \n", + "print(\"\\nUsage Examples:\")\n", + "print(\" growth_media: 'YPD' (simple name)\")\n", + "print(\" components: 'carbon_source:D-glucose@2%|nitrogen_source:yeast_extract@1%;peptone@2%'\")\n", + "print(\"\\n Breakdown:\")\n", + "print(\" - ':' separates type from value (carbon_source:D-glucose)\")\n", + "print(\" - '@' separates value from concentration (D-glucose@2%)\")\n", + "print(\" - ';' separates multiple compounds of same type (yeast_extract@1%;peptone@2%)\")\n", + "print(\" - '|' separates different component types (carbon | nitrogen)\")" + ] }, { "cell_type": "markdown", @@ -554,7 +908,46 @@ { "cell_type": "markdown", "metadata": {}, - "source": "## Summary\n\nThis tutorial demonstrated:\n\n- **Metadata schema extraction** - Using `DataCard.extract_metadata_schema()` to understand field roles\n\n- **Three-level condition hierarchy** - Repo-level, config-level, and field-level experimental conditions\n\n- **Cross-dataset registration** - Registering multiple datasets in `MetadataManager`\n\n- **Condition flattening** - How definitions are flattened into searchable fields with known separators\n\n- **Schema heterogeneity** - How different metadata schemas are aligned by role with \"unspecified\" defaults\n\n- **Future: SQL queries** - Once parquet loading is implemented, full cross-dataset SQL queries will work\n\n### Key Takeaways\n\n1. **Role-based alignment**: Fields are grouped by semantic role (regulator_identifier, experimental_condition), not by name\n2. **Three-level hierarchy**: Conditions can be specified at repo-level (all samples), config-level (config samples), or field-level (individual samples)\n3. **Hierarchy merging**: MetadataManager merges all three levels with precedence: field > config > repo\n4. **Structured separators**: Media components use `:`, `@`, `;`, `|` for predictable parsing\n5. **Low memory**: DuckDB temp views mean no data loading until you query\n6. **Session-only default**: No caching by default (set `cache=True` if needed)\n7. **Dataset + config uniqueness**: Samples identified by (dataset, config_name, sample_id) tuple\n\n### Understanding the Condition Hierarchy\n\nThe three-level hierarchy allows flexible experimental condition specification:\n\n- **Repo-level**: Common conditions shared by all experiments (e.g., standard temperature, base strain)\n- **Config-level**: Conditions specific to a dataset configuration (e.g., growth phase for a time course)\n- **Field-level**: Sample-specific conditions that vary within an experiment (e.g., different media, treatments)\n\nThis design enables:\n- Efficient specification without repetition\n- Clear inheritance and overriding semantics\n- Consistent querying across heterogeneous datasets" + "source": [ + "## Summary\n", + "\n", + "This tutorial demonstrated:\n", + "\n", + "- **Metadata schema extraction** - Using `DataCard.extract_metadata_schema()` to understand field roles\n", + "\n", + "- **Three-level condition hierarchy** - Repo-level, config-level, and field-level experimental conditions\n", + "\n", + "- **Cross-dataset registration** - Registering multiple datasets in `MetadataManager`\n", + "\n", + "- **Condition flattening** - How definitions are flattened into searchable fields with known separators\n", + "\n", + "- **Schema heterogeneity** - How different metadata schemas are aligned by role with \"unspecified\" defaults\n", + "\n", + "- **Future: SQL queries** - Once parquet loading is implemented, full cross-dataset SQL queries will work\n", + "\n", + "### Key Takeaways\n", + "\n", + "1. **Role-based alignment**: Fields are grouped by semantic role (regulator_identifier, experimental_condition), not by name\n", + "2. **Three-level hierarchy**: Conditions can be specified at repo-level (all samples), config-level (config samples), or field-level (individual samples)\n", + "3. **Hierarchy merging**: MetadataManager merges all three levels with precedence: field > config > repo\n", + "4. **Structured separators**: Media components use `:`, `@`, `;`, `|` for predictable parsing\n", + "5. **Low memory**: DuckDB temp views mean no data loading until you query\n", + "6. **Session-only default**: No caching by default (set `cache=True` if needed)\n", + "7. **Dataset + config uniqueness**: Samples identified by (dataset, config_name, sample_id) tuple\n", + "\n", + "### Understanding the Condition Hierarchy\n", + "\n", + "The three-level hierarchy allows flexible experimental condition specification:\n", + "\n", + "- **Repo-level**: Common conditions shared by all experiments (e.g., standard temperature, base strain)\n", + "- **Config-level**: Conditions specific to a dataset configuration (e.g., growth phase for a time course)\n", + "- **Field-level**: Sample-specific conditions that vary within an experiment (e.g., different media, treatments)\n", + "\n", + "This design enables:\n", + "- Efficient specification without repetition\n", + "- Clear inheritance and overriding semantics\n", + "- Consistent querying across heterogeneous datasets" + ] }, { "cell_type": "markdown", @@ -592,7 +985,29 @@ { "cell_type": "markdown", "metadata": {}, - "source": "## Summary\n\nThis tutorial demonstrated:\n\n- **Metadata schema extraction** - Using `DataCard.extract_metadata_schema()` to understand field roles\n\n- **Cross-dataset registration** - Registering multiple datasets in `MetadataManager`\n\n- **Condition flattening** - How definitions are flattened into searchable fields with known separators\n\n- **Schema heterogeneity** - How different metadata schemas are aligned by role with \"unspecified\" defaults\n\n- **Future: SQL queries** - Once parquet loading is implemented, full cross-dataset SQL queries will work\n\n### Key Takeaways\n\n1. **Role-based alignment**: Fields are grouped by semantic role (regulator_identifier, experimental_condition), not by name\n2. **Structured separators**: Media components use `:`, `@`, `;`, `|` for predictable parsing\n3. **Low memory**: DuckDB temp views mean no data loading until you query\n4. **Session-only default**: No caching by default (set `cache=True` if needed)\n5. **Dataset + config uniqueness**: Samples identified by (dataset, config_name, sample_id) tuple" + "source": [ + "## Summary\n", + "\n", + "This tutorial demonstrated:\n", + "\n", + "- **Metadata schema extraction** - Using `DataCard.extract_metadata_schema()` to understand field roles\n", + "\n", + "- **Cross-dataset registration** - Registering multiple datasets in `MetadataManager`\n", + "\n", + "- **Condition flattening** - How definitions are flattened into searchable fields with known separators\n", + "\n", + "- **Schema heterogeneity** - How different metadata schemas are aligned by role with \"unspecified\" defaults\n", + "\n", + "- **Future: SQL queries** - Once parquet loading is implemented, full cross-dataset SQL queries will work\n", + "\n", + "### Key Takeaways\n", + "\n", + "1. **Role-based alignment**: Fields are grouped by semantic role (regulator_identifier, experimental_condition), not by name\n", + "2. **Structured separators**: Media components use `:`, `@`, `;`, `|` for predictable parsing\n", + "3. **Low memory**: DuckDB temp views mean no data loading until you query\n", + "4. **Session-only default**: No caching by default (set `cache=True` if needed)\n", + "5. **Dataset + config uniqueness**: Samples identified by (dataset, config_name, sample_id) tuple" + ] } ], "metadata": { @@ -616,4 +1031,4 @@ }, "nbformat": 4, "nbformat_minor": 4 -} \ No newline at end of file +} From ce67e2cc7f33665afe1d4674199206c6a28c507f Mon Sep 17 00:00:00 2001 From: chasem Date: Tue, 9 Dec 2025 13:51:50 -0600 Subject: [PATCH 15/49] continuing to refine metadata manager --- .../metadata_explorer_tutorial.ipynb | 22 +++++++++++++------ tfbpapi/datainfo/models.py | 18 ++++++++------- .../huggingface_collection_datacards.txt | 13 +++++++++++ 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/docs/tutorials/metadata_explorer_tutorial.ipynb b/docs/tutorials/metadata_explorer_tutorial.ipynb index bcea184..adf8e35 100644 --- a/docs/tutorials/metadata_explorer_tutorial.ipynb +++ b/docs/tutorials/metadata_explorer_tutorial.ipynb @@ -153,11 +153,18 @@ "Condition Fields: ['time', 'mechanism', 'restriction', 'date', 'strain']\n", "\n", "Repo-Level Conditions (apply to ALL samples):\n", - " - growth_medium: minimal\n", - " - temperature: 30C\n", - " - cultivation_method: chemostat\n", + " - Temperature: 30.0 C\n", + " - Cultivation: chemostat\n", + " - Media: minimal\n", + " - Carbon source: D-glucose @ 1.0%\n", + "\n", + "Config-Level Conditions: None\n", "\n", - "Config-Level Conditions: None\n" + "Field-Level Conditions (vary by sample):\n", + " - mechanism: 2 levels defined\n", + " Levels: ['GEV', 'ZEV']\n", + " - restriction: 3 levels defined\n", + " Levels: ['P', 'N', 'M']\n" ] } ], @@ -362,7 +369,7 @@ "type": "string" } ], - "ref": "1c9696e3-e620-4f07-b246-226e4093863f", + "ref": "70ec99fc-d265-426c-84c6-9039ccaa0cda", "rows": [ [ "0", @@ -463,9 +470,10 @@ "\n", "[1] REPO-LEVEL (DatasetCard.experimental_conditions)\n", " Applies to: ALL configs and ALL samples in the repository\n", - " - growth_medium: minimal\n", - " - temperature: 30C\n", + " - temperature_celsius: 30.0\n", " - cultivation_method: chemostat\n", + " - media.name: minimal\n", + " - media.carbon_source: D-glucose @ 1.0%\n", "\n", "[2] CONFIG-LEVEL (DatasetConfig.experimental_conditions)\n", " Applies to: All samples in the 'hackett_2020' config\n", diff --git a/tfbpapi/datainfo/models.py b/tfbpapi/datainfo/models.py index ce5441f..0e76fc0 100644 --- a/tfbpapi/datainfo/models.py +++ b/tfbpapi/datainfo/models.py @@ -78,13 +78,14 @@ class MediaInfo(BaseModel): name: str = Field( ..., - description="Canonical or descriptive media name (minimal, synthetic_complete, YPD, etc.)", + description="Canonical or descriptive media name " + "(minimal, synthetic_complete, YPD, etc.)", ) - carbon_source: list[CompoundInfo] = Field( - ..., description="Carbon source compounds and concentrations" + carbon_source: list[CompoundInfo] | None = Field( + default=None, description="Carbon source compounds and concentrations" ) - nitrogen_source: list[CompoundInfo] = Field( - ..., description="Nitrogen source compounds and concentrations" + nitrogen_source: list[CompoundInfo] | None = Field( + default=None, description="Nitrogen source compounds and concentrations" ) phosphate_source: list[CompoundInfo] | None = Field( default=None, description="Phosphate source compounds and concentrations" @@ -99,7 +100,7 @@ class MediaInfo(BaseModel): def validate_compound_list(cls, v): """Validate compound lists and handle 'unspecified' strings.""" if v is None: - return [] + return None if isinstance(v, str): if v == "unspecified": warnings.warn( @@ -107,7 +108,7 @@ def validate_compound_list(cls, v): "Should be null/omitted or a structured list.", UserWarning, ) - return [] + return None # Try to parse as single compound return [{"compound": v}] return v @@ -153,7 +154,8 @@ def check_stage_phase_consistency(self): """Ensure stage and phase are consistent if both provided.""" if self.stage and self.phase and self.stage != self.phase: raise ValueError( - f"Inconsistent growth phase: stage='{self.stage}' vs phase='{self.phase}'" + "Inconsistent growth phase: " + f"stage='{self.stage}' vs phase='{self.phase}'" ) return self diff --git a/tfbpapi/tests/datainfo/huggingface_collection_datacards.txt b/tfbpapi/tests/datainfo/huggingface_collection_datacards.txt index 2d4d780..d5d3394 100644 --- a/tfbpapi/tests/datainfo/huggingface_collection_datacards.txt +++ b/tfbpapi/tests/datainfo/huggingface_collection_datacards.txt @@ -443,6 +443,19 @@ configs: names: ["GEV", "ZEV"] description: Synthetic TF induction system (GEV or ZEV) role: experimental_condition + definitions: + GEV: + perturbation_method: + type: inducible_overexpression + system: GEV + inducer: beta-estradiol + description: "Galactose-inducible estrogen receptor-VP16 fusion system" + ZEV: + perturbation_method: + type: inducible_overexpression + system: ZEV + inducer: beta-estradiol + description: "Z3 (synthetic zinc finger)-estrogen receptor-VP16 fusion system" - name: restriction dtype: class_label: From 3f2b04eaaa29c08848079d4a6d796e5eadd69d41 Mon Sep 17 00:00:00 2001 From: chasem Date: Tue, 9 Dec 2025 14:04:52 -0600 Subject: [PATCH 16/49] revising how cond cols are displayed --- .../metadata_explorer_tutorial.ipynb | 81 ++++++++----------- tfbpapi/datainfo/datacard.py | 63 +++++++++++++-- 2 files changed, 90 insertions(+), 54 deletions(-) diff --git a/docs/tutorials/metadata_explorer_tutorial.ipynb b/docs/tutorials/metadata_explorer_tutorial.ipynb index adf8e35..0d4104a 100644 --- a/docs/tutorials/metadata_explorer_tutorial.ipynb +++ b/docs/tutorials/metadata_explorer_tutorial.ipynb @@ -26,18 +26,9 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 14, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chase/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", - " from .autonotebook import tqdm as notebook_tqdm\n" - ] - } - ], + "outputs": [], "source": [ "import pandas as pd\n", "from tfbpapi.datainfo import DataCard, MetadataManager" @@ -61,7 +52,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 15, "metadata": {}, "outputs": [ { @@ -139,7 +130,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -150,7 +141,6 @@ "============================================================\n", "Regulator Fields: ['regulator_locus_tag', 'regulator_symbol']\n", "Target Fields: ['target_locus_tag', 'target_symbol']\n", - "Condition Fields: ['time', 'mechanism', 'restriction', 'date', 'strain']\n", "\n", "Repo-Level Conditions (apply to ALL samples):\n", " - Temperature: 30.0 C\n", @@ -158,13 +148,12 @@ " - Media: minimal\n", " - Carbon source: D-glucose @ 1.0%\n", "\n", - "Config-Level Conditions: None\n", - "\n", - "Field-Level Conditions (vary by sample):\n", - " - mechanism: 2 levels defined\n", - " Levels: ['GEV', 'ZEV']\n", - " - restriction: 3 levels defined\n", - " Levels: ['P', 'N', 'M']\n" + "Experimental Condition Fields:\n", + " - time: values from data\n", + " - mechanism: 2 defined levels\n", + " - restriction: 3 defined levels\n", + " - date: values from data\n", + " - strain: values from data\n" ] } ], @@ -179,13 +168,12 @@ "print(\"=\" * 60)\n", "print(f\"Regulator Fields: {hackett_schema['regulator_fields']}\")\n", "print(f\"Target Fields: {hackett_schema['target_fields']}\")\n", - "print(f\"Condition Fields: {hackett_schema['condition_fields']}\")\n", "\n", "# Show repo-level (top-level) experimental conditions\n", "if hackett_schema.get('top_level_conditions'):\n", " print(\"\\nRepo-Level Conditions (apply to ALL samples):\")\n", " top_cond = hackett_schema['top_level_conditions']\n", - " \n", + "\n", " # Check for structured environmental_conditions\n", " if top_cond.environmental_conditions:\n", " env = top_cond.environmental_conditions\n", @@ -198,25 +186,22 @@ " if env.media.carbon_source:\n", " for cs in env.media.carbon_source:\n", " print(f\" - Carbon source: {cs.compound} @ {cs.concentration_percent}%\")\n", - " \n", + "\n", " # Check for extra fields (alternate structure)\n", " if hasattr(top_cond, 'model_extra') and top_cond.model_extra:\n", " for key, value in top_cond.model_extra.items():\n", " print(f\" - {key}: {value}\")\n", "\n", - "# Show config-level conditions\n", - "if hackett_schema.get('config_level_conditions'):\n", - " print(\"\\nConfig-Level Conditions:\")\n", - " print(\" (Conditions defined for this config)\")\n", - "else:\n", - " print(\"\\nConfig-Level Conditions: None\")\n", - "\n", - "# Show field-level condition definitions\n", - "if hackett_schema['condition_definitions']:\n", - " print(\"\\nField-Level Conditions (vary by sample):\")\n", - " for field_name, definitions in hackett_schema['condition_definitions'].items():\n", - " print(f\" - {field_name}: {len(definitions)} levels defined\")\n", - " print(f\" Levels: {list(definitions.keys())}\")" + "# Show all experimental condition fields\n", + "if hackett_schema['condition_fields']:\n", + " print(\"\\nExperimental Condition Fields:\")\n", + " for field_name in hackett_schema['condition_fields']:\n", + " # Check if field has definitions (structured factor levels)\n", + " if field_name in hackett_schema['condition_definitions']:\n", + " num_levels = len(hackett_schema['condition_definitions'][field_name])\n", + " print(f\" - {field_name}: {num_levels} defined levels\")\n", + " else:\n", + " print(f\" - {field_name}: values from data\")" ] }, { @@ -255,7 +240,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 17, "metadata": {}, "outputs": [ { @@ -292,7 +277,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 18, "metadata": {}, "outputs": [ { @@ -333,7 +318,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 19, "metadata": {}, "outputs": [ { @@ -369,7 +354,7 @@ "type": "string" } ], - "ref": "70ec99fc-d265-426c-84c6-9039ccaa0cda", + "ref": "963ab794-8ad8-4c6c-967b-17439c5c0921", "rows": [ [ "0", @@ -455,7 +440,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 20, "metadata": {}, "outputs": [ { @@ -578,7 +563,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 21, "metadata": {}, "outputs": [ { @@ -621,7 +606,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 22, "metadata": {}, "outputs": [ { @@ -665,7 +650,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 23, "metadata": {}, "outputs": [ { @@ -716,7 +701,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 24, "metadata": {}, "outputs": [ { @@ -772,7 +757,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 25, "metadata": {}, "outputs": [ { @@ -845,7 +830,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 26, "metadata": {}, "outputs": [ { diff --git a/tfbpapi/datainfo/datacard.py b/tfbpapi/datainfo/datacard.py index 279b548..9b60c22 100644 --- a/tfbpapi/datainfo/datacard.py +++ b/tfbpapi/datainfo/datacard.py @@ -1,7 +1,7 @@ """DataCard class for easy exploration of HuggingFace dataset metadata.""" import logging -from typing import Any, Dict, List, Optional, Set, Union +from typing import Any from pydantic import ValidationError @@ -83,8 +83,10 @@ def _load_and_validate_card(self) -> None: if "dtype" in field_path and error_type == "string_type": error_details.append( - f"Field '{field_path}': Expected a simple data type string (like 'string', 'int64', 'float64') " - f"but got a complex structure. This might be a categorical field with class labels. " + f"Field '{field_path}': Expected a simple data type " + "string (like 'string', 'int64', 'float64') " + "but got a complex structure. This might be a categorical " + "field with class labels. " f"Actual value: {input_value}" ) else: @@ -185,11 +187,13 @@ def _extract_field_values(self, config: DatasetConfig, field_name: str) -> set[s values.update(partition_values) # For embedded metadata fields, we would need to query the actual data - # This is a placeholder - in practice, you might use the HF datasets server API + # This is a placeholder - in practice, you might use the + # HF datasets server API if config.metadata_fields and field_name in config.metadata_fields: # Placeholder for actual data extraction self.logger.debug( - f"Would extract embedded metadata for {field_name} in {config.config_name}" + "Would extract embedded metadata for " + f"{field_name} in {config.config_name}" ) except Exception as e: @@ -253,7 +257,8 @@ def get_metadata_relationships( ) ) - # Check for embedded metadata (always runs regardless of explicit relationships) + # Check for embedded metadata (always runs regardless of + # explicit relationships) if data_config.metadata_fields: relationships.append( MetadataRelationship( @@ -378,6 +383,52 @@ def extract_metadata_schema(self, config_name: str) -> dict[str, Any]: return schema + def get_condition_levels( + self, config_name: str, field_name: str + ) -> dict[str, Any] | list[str]: + """ + Get factor levels for an experimental condition field. + + Returns definitions if available (structured dict with descriptions), otherwise + queries distinct values from the parquet file. + + :param config_name: Configuration name + :param field_name: Experimental condition field name + :return: Dict of definitions if available, otherwise list of distinct values + :raises DataCardError: If config or field not found, or field is not an + experimental condition + + """ + config = self.get_config(config_name) + if not config: + raise DataCardError(f"Configuration '{config_name}' not found") + + # Find the feature and verify it's an experimental condition + feature = None + for f in config.dataset_info.features: + if f.name == field_name: + feature = f + break + + if not feature: + raise DataCardError( + f"Field '{field_name}' not found in config '{config_name}'" + ) + + if feature.role != FieldRole.EXPERIMENTAL_CONDITION: + raise DataCardError( + f"Field '{field_name}' is not an experimental condition " + f"(role={feature.role})" + ) + + # If field has definitions, return those + if feature.definitions: + return feature.definitions + + # Otherwise, query distinct values from parquet file + values = self.get_field_values(config_name, field_name) + return sorted(list(values)) + def summary(self) -> str: """Get a human-readable summary of the dataset.""" card = self.dataset_card From 8005ad2dd70e69fce23a999b09aa2bd330ee3b80 Mon Sep 17 00:00:00 2001 From: chasem Date: Thu, 11 Dec 2025 14:45:18 -0600 Subject: [PATCH 17/49] tmp --- docs/brentlab_yeastresources_collection.md | 358 ++++++ docs/huggingface_datacard.md | 486 +------ docs/tutorials/datacard_tutorial.ipynb | 766 +++++------ .../metadata_explorer_tutorial.ipynb | 1027 --------------- .../tutorials/metadata_manager_tutorial.ipynb | 586 +++++++++ docs/tutorials/sample_manager_tutorial.ipynb | 603 +++++++++ tfbpapi/datainfo/datacard.py | 457 ++++++- tfbpapi/datainfo/metadata_manager.py | 763 +++-------- tfbpapi/datainfo/models.py | 450 +------ tfbpapi/datainfo/sample_manager.py | 774 +++++++++++ .../huggingface_collection_datacards.txt | 843 ++++++------ tfbpapi/tests/datainfo/test_datacard.py | 158 +++ .../tests/datainfo/test_metadata_manager.py | 92 +- tfbpapi/tests/datainfo/test_models.py | 1140 +++++++---------- tfbpapi/tests/datainfo/test_real_datacards.py | 175 +-- 15 files changed, 4470 insertions(+), 4208 deletions(-) create mode 100644 docs/brentlab_yeastresources_collection.md delete mode 100644 docs/tutorials/metadata_explorer_tutorial.ipynb create mode 100644 docs/tutorials/metadata_manager_tutorial.ipynb create mode 100644 docs/tutorials/sample_manager_tutorial.ipynb create mode 100644 tfbpapi/datainfo/sample_manager.py diff --git a/docs/brentlab_yeastresources_collection.md b/docs/brentlab_yeastresources_collection.md new file mode 100644 index 0000000..29d3561 --- /dev/null +++ b/docs/brentlab_yeastresources_collection.md @@ -0,0 +1,358 @@ +# BrentLab Yeast Resources Collection + +This document describes the BrentLab yeast resources collection on HuggingFace as an example implementation of the [datacard specifications](huggingface_datacard.md). This collection demonstrates best practices for organizing transcription factor binding and perturbation datasets for *Saccharomyces cerevisiae*. + +## Collection Overview + +The BrentLab yeast resources collection contains 10 datasets related to yeast transcription factor binding and gene expression regulation: + +1. **barkai_compendium** - ChEC-seq binding data across multiple GEO series +2. **callingcards** - Calling Cards transposon-based binding data +3. **hackett_2020** - TF overexpression with nutrient limitation +4. **harbison_2004** - ChIP-chip binding across 14 environmental conditions +5. **hu_2007_reimand_2010** - TF knockout expression data +6. **hughes_2006** - TF perturbation screen (overexpression and knockout) +7. **kemmeren_2014** - TF deletion expression profiling +8. **mahendrawada_2025** - ChEC-seq and nascent RNA-seq data +9. **rossi_2021** - ChIP-exo binding data +10. **yeast_genome_resources** - Reference genomic features + +## Standardized Media Names + +The collection uses standardized media names to facilitate cross-dataset queries. When specifying media in datacards, use these canonical names: + +### Rich Media + +- **YPD** (Yeast extract Peptone Dextrose) + - Carbon source: 2% D-glucose + - Nitrogen sources: 1% yeast extract, 2% peptone + - Standard rich medium for yeast growth + +- **yeast_extract_peptone** + - Base medium without specified carbon source + - Used with galactose (YPGal) or raffinose (YPRaff) + +### Minimal/Defined Media + +- **minimal** or **minimal_glucose** + - Minimal defined medium with glucose as carbon source + - Nitrogen source varies by experiment + +- **synthetic_complete** or **synthetic_complete_dextrose** + - Defined medium with complete amino acid supplementation + - Carbon source: typically 2% D-glucose + - Nitrogen source: yeast nitrogen base + amino acid dropout mix + +- **synthetic_complete_minus_X** + - Synthetic complete medium lacking specific nutrient(s) + - Examples: `synthetic_complete_minus_thiamine`, `synthetic_complete_minus_phosphate` + - Used for nutrient deprivation experiments + +- **selective_medium** + - Defined medium for plasmid selection + - Specific composition varies by selection markers + +## Standardized Strain Backgrounds + +The collection primarily uses these strain backgrounds: + +- **BY4741** - MATa his3Δ1 leu2Δ0 met15Δ0 ura3Δ0 + - Used in: hu_2007_reimand_2010, kemmeren_2014 + +- **W303** - Common alternative strain background + - Used in: harbison_2004 (derivative Z1256) + +- **S288C** - Reference genome strain + - Used in: Various datasets + +Strain background can be specified as a string or detailed object: + +```yaml +# Simple string +experimental_conditions: + strain_background: BY4741 + +# Detailed specification +experimental_conditions: + strain_background: + genotype: BY4741 + mating_type: MATa + markers: + - his3Δ1 + - leu2Δ0 + - met15Δ0 + - ura3Δ0 + source: Open_Biosystems + description: Knockout strains for nonessential transcription factors +``` + +## Standard Experimental Conditions + +### Growth Temperature + +Standard growth temperature across the collection is **30°C** unless otherwise noted. + +Exceptions: +- **rossi_2021**: 25°C baseline with 37°C heat shock for some samples +- **hu_2007_reimand_2010**: Heat shock at 39°C for heat shock response TFs +- **callingcards**: the experiments are performed at room temperature (~22-25°C) + +### Growth Phase + +Common growth phase specifications: + +These labels are taken from the original publications. In some cases the OD600 +is noted + +- **early_log_phase** +- **mid_log_phase** +- **late_log_phase** +- **stationary_phase** - eg barkai_compendium, which are allowed to grow overnight. The + cells are harvested at a very high density (OD600 4.0). + +Example: +```yaml +experimental_conditions: + growth_phase_at_harvest: + stage: mid_log_phase + od600: 0.6 + od600_tolerance: 0.1 +``` + +### Cultivation Methods + +Standard cultivation methods used: + +- **liquid_culture** - Standard batch culture in flasks +- **batch** - Batch culture +- **plate** - Growth on agar plates +- **chemostat** - Continuous culture (hackett_2020) + +## Concentration Specifications + +**Always use `concentration_percent`** for all concentration specifications. +Convert other units to percentage: + +- **mg/ml to percent**: divide by 10 (e.g., 5 mg/ml = 0.5%) +- **g/L to percent**: divide by 10 (e.g., 6.71 g/L = 0.671%) +- **Molar to percent**: convert using molecular weight + - Example: 100 nM rapamycin = 9.142e-6% + +### Examples from the Collection + +```yaml +# Yeast nitrogen base: 6.71 g/L = 0.671% +- compound: yeast_nitrogen_base + concentration_percent: 0.671 + +# Alpha factor: 5 mg/ml = 0.5% +- compound: alpha_factor_pheromone + concentration_percent: 0.5 + +# Rapamycin: 100 nM = 9.142e-6% +chemical_treatment: + compound: rapamycin + concentration_percent: 9.142e-6 +``` + +## Field Naming Conventions + +The collection follows these field naming conventions: + +### Gene/Feature Identifiers + +- **regulator_locus_tag**: Systematic ID of regulatory factor (e.g., "YJR060W") +- **regulator_symbol**: Common name of regulatory factor (e.g., "CBF1") +- **target_locus_tag**: Systematic ID of target gene +- **target_symbol**: Common name of target gene + +All locus tags and symbols join to **yeast_genome_resources** dataset. + +### Quantitative Measurements Examples + +Common measurement field names: + +- **effect**, **log2fc**, **log2_ratio** - Log fold change measurements +- **pvalue**, **pval**, **p_value** - Statistical significance +- **padj**, **adj_p_value** - FDR-adjusted p-values +- **binding_score**, **peak_score** - Binding strength metrics +- **enrichment** - Enrichment ratios + +### Experimental Metadata Examples + +- **sample_id** - Unique sample identifier (integer) +- **db_id** - Legacy database identifier (deprecated, do not use) +- **batch** - Experimental batch identifier +- **replicate** - Biological replicate number +- **time** - Timepoint in timecourse experiments + +## Dataset Type Usage Examples + +### genomic_features + +**yeast_genome_resources** provides reference annotations: +- Gene coordinates and strand information +- Systematic IDs (locus_tag) and common names (symbol) +- Feature types (gene, ncRNA_gene, tRNA_gene, etc.) + +Used for joining regulator/target identifiers across all other datasets. + +### annotated_features + +Most common dataset type in the collection. Examples: + +- **hackett_2020**: TF overexpression with timecourse measurements +- **harbison_2004**: ChIP-chip binding with condition field definitions +- **kemmeren_2014**: TF deletion expression data +- **mahendrawada_2025**: ChEC-seq binding scores + +Typical structure: regulator × target × measurements, with optional condition fields. + +### genome_map + +Position-level data, typically partitioned by sample or accession: + +- **barkai_compendium**: ChEC-seq pileup data partitioned by Series/Accession +- **rossi_2021**: ChIP-exo 5' tag coverage partitioned by sample +- **callingcards**: Transposon insertion density partitioned by batch + +### metadata + +Separate metadata configs or embedded metadata via `metadata_fields`: + +**Separate config example** (barkai_compendium): +```yaml +- config_name: GSE178430_metadata + dataset_type: metadata + applies_to: ["genomic_coverage"] +``` + +**Embedded metadata example** (harbison_2004): +```yaml +- config_name: harbison_2004 + dataset_type: annotated_features + metadata_fields: ["regulator_locus_tag", "regulator_symbol", "condition"] +``` + +## Categorical Condition Definitions + +Many datasets define categorical experimental conditions using the `definitions` field. + +### harbison_2004 Environmental Conditions + +14 conditions with detailed specifications: +- **YPD** (rich media baseline) +- **SM** (amino acid starvation) +- **RAPA** (rapamycin treatment) +- **H2O2Hi**, **H2O2Lo** (oxidative stress) +- **HEAT** (heat shock) +- **GAL**, **RAFF** (alternative carbon sources) +- And 6 more... + +Each condition definition includes media composition, temperature, growth phase, and treatments. + +### hackett_2020 Nutrient Limitations + +```yaml +restriction: + definitions: + P: # Phosphate limitation + media: + phosphate_source: + - compound: potassium_phosphate_monobasic + concentration_percent: 0.002 + N: # Nitrogen limitation + media: + nitrogen_source: + - compound: ammonium_sulfate + concentration_percent: 0.004 + M: # Undefined limitation + description: "Not defined in the paper" +``` + +### hu_2007_reimand_2010 Treatment Conditions + +```yaml +heat_shock: + definitions: + true: + temperature_celsius: 39 + duration_minutes: 15 + false: + description: Standard growth conditions at 30°C +``` + +## Partitioning Strategies + +Large genome_map datasets use partitioning: + +**barkai_compendium** - Two-level partitioning: +```yaml +partitioning: + partition_by: ["Series", "Accession"] + path_template: "genome_map/*/*/part-0.parquet" +``` + +**callingcards** - Batch partitioning: +```yaml +partitioning: + enabled: true + partition_by: ["batch"] + path_template: "genome_map/batch={batch}/*.parquet" +``` + +## Collection-Wide Best Practices + +### 1. Omit unspecified fields with a comment + +`tfbpapi` will handle adding "unspecified" to fields which are not common across +datasets. + +```yaml +# CORRECT +experimental_conditions: + temperature_celsius: 30 + # cultivation_method is note noted in the paper and is omitted + +# INCORRECT +experimental_conditions: + temperature_celsius: unspecified +``` + +### 2. Document Source Publications + +If the original paper used something like g/L, then convert that to +`concentration_percent` and add a comment with the original value and units. + +```yaml +carbon_source: + - compound: D-glucose + # Saldanha et al 2004: 10 g/L + concentration_percent: 1 +``` + +### 3. Use Standard Field Roles + +Apply semantic roles consistently: +- `regulator_identifier` - for regulator fields +- `target_identifier` - for target fields +- `quantitative_measure` - for measurements +- `experimental_condition` - for condition fields +- `genomic_coordinate` - for positional data + +### 4. Provide sample_id + +All annotated_features datasets should include `sample_id` to uniquely identify experimental samples. This enables cross-dataset joining and metadata management. + +### 5. Specify metadata_fields or applies_to + +For datasets with metadata, either: +- Use `metadata_fields` to extract from the data itself, OR +- Create separate metadata config with `applies_to` field + +### 6. Use Consistent Gene Identifiers + +All regulator/target identifiers must be joinable to **yeast_genome_resources**: +- Use current systematic IDs (ORF names) +- Include both locus_tag and symbol fields +- Mark with appropriate roles diff --git a/docs/huggingface_datacard.md b/docs/huggingface_datacard.md index de8dd41..5cb30d7 100644 --- a/docs/huggingface_datacard.md +++ b/docs/huggingface_datacard.md @@ -78,22 +78,25 @@ Experimental conditions can be specified in three ways: 3. **Field-level** with `role: experimental_condition` ([feature-roles](#feature-roles)): For per-sample or per-measurement variation in experimental conditions stored as data columns. This is specified in the - `dataset_info.features` ([feature-definitions](#feature-definitions)) section of a config. + `dataset_info.features` ([feature-definitions](#feature-definitions)) + section of a config. `experimental_condition` fields which are categorical can are + specifically defined in [categorical fields with value definitions](#categorical-fields-with-value-definitions). + +The priority of experimental conditions is: + +field-level > config-level > top-level **Example of all three methods:** ```yaml -# Top-level experimental conditions (apply to all configs) +# Top-level experimental conditions (apply to all [datasets](#dataset) in the repo) experimental_conditions: - environmental_conditions: - temperature_celsius: 30 + temperature_celsius: 30 configs: -# The overexpression_data dataset has an additional experimental condition that is -# specific to this dataset and applied to all samples (strain_background) in addition -# to a field (mechanism) that varies per sample and is identified by the -# role experimental_condition. - config_name: overexpression_data description: TF overexpression perturbation data dataset_type: annotated_features + # The overexpression_data [dataset](#dataset) has an additional experimental + # condition that is specific to this dataset experimental_conditions: strain_background: "BY4741" data_files: @@ -109,359 +112,26 @@ configs: dtype: string description: Induction mechanism (GEV or ZEV) role: experimental_condition + definitions: + GEV: + perturbation_method: + type: inducible_overexpression + system: GEV + inducer: beta-estradiol + description: "Galactose-inducible estrogen receptor-VP16 fusion system" + ZEV: + perturbation_method: + type: inducible_overexpression + system: ZEV + inducer: beta-estradiol + description: >- + "Z3 (synthetic zinc finger)-estrogen receptor-VP16 fusion system" - name: log2_ratio dtype: float description: Log2 fold change role: quantitative_measure ``` -### Environmental Conditions - -Environmental conditions are nested under `experimental_conditions` and describe the -physical and chemical environment in which samples were cultivated. This includes -growth media specifications, temperature, cultivation method, and other environmental -parameters. - -#### Core Environmental Fields - -The following fields are supported within `environmental_conditions`: - -- **temperature_celsius** (float): Growth temperature in Celsius -- **cultivation_method** (string): Method of cultivation (e.g., "liquid_culture", "plate", "chemostat") -- **growth_phase_at_harvest** (object): Growth phase information (see [Growth Phase Specification](#growth-phase-specification)) -- **media** (object): Growth medium specification (see [Growth Media Specification](#growth-media-specification)) -- **chemical_treatment** (object): Chemical treatment information (see [Chemical Treatments](#chemical-treatments)) -- **drug_treatment** (object): Drug treatment (same structure as chemical_treatment) -- **heat_treatment** (object): Heat treatment specification -- **temperature_shift** (object): Temperature shift for heat shock experiments (see [Temperature Shifts](#temperature-shifts)) -- **induction** (object): Induction system for expression experiments (see [Induction Systems](#induction-systems)) -- **incubation_duration_hours** (float): Total incubation duration in hours -- **incubation_duration_minutes** (int): Total incubation duration in minutes -- **description** (string): Additional descriptive information - -#### Growth Phase Specification - -Growth phase at harvest can be specified using: - -```yaml -growth_phase_at_harvest: - stage: mid_log_phase # or: early_log_phase, late_log_phase, stationary_phase, etc. - od600: 0.6 # Optical density at 600nm - od600_tolerance: 0.1 # Optional: measurement tolerance - description: "Additional context" -``` - -**Note**: The field `phase` is accepted as an alias for `stage` for backward compatibility. - -Recognized stage values: -- `mid_log_phase`, `early_log_phase`, `late_log_phase` -- `stationary_phase`, `early_stationary_phase`, `overnight_stationary_phase` -- `mid_log`, `early_log`, `late_log`, `exponential_phase` - -#### Chemical Treatments - -Chemical treatments (including drugs) are specified with: - -```yaml -chemical_treatment: - compound: rapamycin # Chemical compound name - concentration_percent: 0.001 # Concentration as percentage - duration_minutes: 20 # Treatment duration in minutes - duration_hours: 0.33 # Alternative: duration in hours - target_pH: 4.0 # Optional: target pH for pH adjustments - description: "TOR inhibition" # Optional: additional context -``` - -The `drug_treatment` field uses the same structure and is interchangeable with `chemical_treatment`. - -#### Temperature Shifts - -For heat shock and temperature shift experiments: - -```yaml -temperature_shift: - initial_temperature_celsius: 30 - temperature_shift_celsius: 37 - temperature_shift_duration_minutes: 45 - description: "Heat shock treatment" -``` - -#### Induction Systems - -For expression induction systems (e.g., GAL, estradiol-inducible): - -```yaml -induction: - inducer: - compound: D-galactose - concentration_percent: 2 - duration_hours: 3 - duration_minutes: 180 # Alternative to duration_hours - description: "GAL promoter induction" -``` - -#### Growth Media Specification - -The `media` field specifies the growth medium used in an experiment. Media is nested -under `environmental_conditions` and can be specified at the top-level, config-level, -or within field-level definitions depending on whether they are common across all -datasets, specific to a config, or vary per sample. - -##### Media Structure - -Each media specification has the following required structure: - -```yaml -experimental_conditions: - environmental_conditions: - media: - name: string # Canonical or descriptive media name (see below) - carbon_source: # Required - - compound: string # Chemical compound name - concentration_percent: float - nitrogen_source: # Required - - compound: string # Chemical compound name - concentration_percent: float - phosphate_source: # Optional - - compound: string - concentration_percent: float - additives: # Optional: for additional media components - - compound: string # e.g., butanol for filamentation - concentration_percent: float - description: string -``` - -Both `carbon_source` and `nitrogen_source` are **required fields**. Each can contain -one or more compound entries with their respective concentrations specified as a -percentage. - -**Handling Unknown Values**: When a component is truly unknown or not reported in the -source publication, omit the field or use `null`. Do NOT use the string `"unspecified"` as -a compound name, as this will generate validation warnings. - -##### Canonical Media Names - -Three base media types are standardized across the collection: - -1. **minimal** - - Minimal defined medium with inorganic nitrogen source - - Typically used for targeted nutrient deprivation studies - - Example: Hackett 2020 - -2. **synthetic_complete** - - Defined medium with amino acid supplements - - Contains yeast nitrogen base (without amino acids) plus amino acid dropout mix - - Used as baseline in many stress studies - - Example: Kemmeren 2014, Mahendrawada 2025, Harbison 2004 - -3. **YPD** (yeast peptone dextrose) - - Rich, complex medium with yeast extract and peptone as nitrogen sources - - Used as standard rich-media baseline condition - - Also known as: yeast_peptone_dextrose, yeast_extract_peptone (context-dependent) - - Example: Hu Reimand 2010, Harbison 2004, Rossi 2021, Barkai compendium - -**Descriptive Media Names**: While the canonical names above are preferred, descriptive -variations that provide additional specificity are acceptable (e.g., -`synthetic_complete_dextrose`, `selective_medium`, `synthetic_complete_minus_uracil`). -The key requirement is that the actual media composition be fully specified in the -`carbon_source`, `nitrogen_source`, and optional `phosphate_source` and `additives` fields. - -##### Specifying Carbon and Nitrogen Sources - -###### Carbon Sources - -Common carbon sources in yeast media: - -```yaml -carbon_source: - - compound: D-glucose - concentration_percent: 2 -``` - -Typical values: D-glucose, D-galactose, D-raffinose, D-dextrose - -Concentrations are expressed as a percentage (e.g., 2% glucose). - -###### Nitrogen Sources - -Nitrogen sources vary by media type: - -**For synthetic_complete and minimal media:** -```yaml -nitrogen_source: - - compound: yeast_nitrogen_base - concentration_g_per_l: 6.71 - specifications: - - without_amino_acids - - without_ammonium_sulfate - - compound: ammonium_sulfate - # if specified differently in the paper, add the authors' - # specification in a comment - concentration_percent: 0.5 - - compound: amino_acid_dropout_mix - # lastname et al 2025 used 20 g/L - concentration_percent: 2 -``` - -**For YPD media:** -```yaml -nitrogen_source: - - compound: yeast_extract - concentration_percent: 1 - - compound: peptone - concentration_percent: 2 -``` - -##### Media Examples - -**Minimal Medium** -```yaml -experimental_conditions: - environmental_conditions: - media: - name: minimal - carbon_source: - - compound: D-glucose - concentration_percent: 2 - nitrogen_source: - - compound: ammonium_sulfate - concentration_g_per_l: 5 -``` - -**Synthetic Complete (Base)** -```yaml -experimental_conditions: - environmental_conditions: - media: - name: synthetic_complete - carbon_source: - - compound: D-glucose - concentration_percent: 2 - nitrogen_source: - - compound: yeast_nitrogen_base - # lastname et al 2025 used 6.71 g/L - concentration_percent: 0.671 - specifications: - - without_amino_acids - - without_ammonium_sulfate - - compound: ammonium_sulfate - # lastname et al 2025 used 5 g/L - concentration_percent: 0.5 - - compound: amino_acid_dropout_mix - # lastname et al 2025 used 2 g/L - concentration_percent: 0.2 -``` - -**Synthetic Complete with Alternative Carbon Source** -```yaml -experimental_conditions: - environmental_conditions: - media: - name: synthetic_complete - carbon_source: - - compound: D-galactose - concentration_percent: 2 - nitrogen_source: - - compound: yeast_nitrogen_base - # lastname et al 2025 used 6.71 g/L - concentration_percent: 0.671 - specifications: - - without_amino_acids - - without_ammonium_sulfate - - compound: ammonium_sulfate - # lastname et al 2025 used 5 g/L - concentration_percent: 0.5 - - compound: amino_acid_dropout_mix - # lastname et al 2025 used 2 g/L - concentration_percent: 0.2 -``` - -**YPD** -```yaml -experimental_conditions: - environmental_conditions: - media: - name: YPD - carbon_source: - - compound: D-glucose - concentration_percent: 2 - nitrogen_source: - - compound: yeast_extract - concentration_percent: 1 - - compound: peptone - concentration_percent: 2 -``` - -##### Selective/Dropout Media Variants - -When a dataset uses a selective medium with specific amino acid or nutrient dropouts, -specify this using the base `synthetic_complete` name and adjust the `nitrogen_source` -to reflect the modified composition: - -```yaml -experimental_conditions: - environmental_conditions: - media: - name: synthetic_complete - carbon_source: - - compound: D-glucose - concentration_percent: 2 - nitrogen_source: - - compound: yeast_nitrogen_base - # lastname et al 2025 used 6.71 g/L - concentration_percent: 0.671 - specifications: - - without_amino_acids - - without_ammonium_sulfate - - compound: ammonium_sulfate - # lastname et al 2025 used 5 g/L - concentration_percent: 0.5 - - compound: amino_acid_dropout_mix - # lastname et al 2025 used 2 g/L - concentration_percent: 0.2 - specifications: - - minus_uracil - - minus_histidine - - minus_leucine -``` - -##### Media in Field-Level Definitions - -When media varies per sample and is captured in a categorical field with definitions: - -```yaml -- name: condition - dtype: - class_label: - names: ["standard", "galactose"] - role: experimental_condition - definitions: - standard: - environmental_conditions: - media: - name: YPD - carbon_source: - - compound: D-glucose - concentration_percent: 2 - nitrogen_source: - - compound: yeast_extract - concentration_percent: 1 - - compound: peptone - concentration_percent: 2 - galactose: - environmental_conditions: - media: - name: YPD - carbon_source: - - compound: D-galactose - concentration_percent: 2 - nitrogen_source: - - compound: yeast_extract - concentration_percent: 1 - - compound: peptone - concentration_percent: 2 -``` - ## Feature Definitions Each config must include detailed feature definitions in `dataset_info.features`: @@ -488,26 +158,27 @@ machine-parsable specification of what each condition value means experimentally description: Growth condition of the sample definitions: standard: - environmental_conditions: - media: - name: synthetic_complete - carbon_source: - - compound: D-glucose - concentration_percent: 2 - nitrogen_source: - - compound: yeast_nitrogen_base - concentration_g_per_l: 6.71 - specifications: - - without_amino_acids - - without_ammonium_sulfate - - compound: ammonium_sulfate - concentration_g_per_l: 5 - - compound: amino_acid_dropout_mix - concentration_g_per_l: 2 + media: + name: synthetic_complete + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_nitrogen_base + # lastname et al 2025 used 6.71 g/L + concentration_percent: 0.671 + specifications: + - without_amino_acids + - without_ammonium_sulfate + - compound: ammonium_sulfate + # lastname et al 2025 used 5 g/L + concentration_percent: 0.5 + - compound: amino_acid_dropout_mix + # lastname et al 2025 used 2 g/L + concentration_percent: 0.2 heat_shock: - environmental_conditions: - temperature_celsius: 37 - duration_minutes: 10 + temperature_celsius: 37 + duration_minutes: 10 ``` Each key in `definitions` must correspond to a possible value in the field. @@ -536,53 +207,9 @@ Unless otherwise noted, assume that coordinates are 0-based, half-open intervals ## Feature Roles The optional `role` field provides semantic meaning to features, especially useful -for annotated_features datasets. The following roles are recognized by tfbpapi: - -- **target_identifier**: Identifies target genes/features (e.g., `target_locus_tag`, - `target_symbol`) -- **regulator_identifier**: Identifies regulatory factors (e.g., `regulator_locus_tag`, - `regulator_symbol`) -- **quantitative_measure**: Quantitative measurements (e.g., `binding_score`, - `expression_level`, `p_value`) -- **experimental_condition**: Experimental conditions or metadata - (can include `definitions` field for categorical values) -- **genomic_coordinate**: Positional information (`chr`, `start`, `end`, `pos`) - -**Validation**: Only these specific role values are accepted. Other values (e.g., `"identifier"`) -will cause validation errors. - -## Strain Background and Definitions - -The `strain_background` field can appear in two locations: - -1. **Top-level or config-level** `experimental_conditions`: - ```yaml - experimental_conditions: - strain_background: - genotype: BY4741 - mating_type: MATa - markers: [his3Δ1, leu2Δ0, met15Δ0, ura3Δ0] - ``` - -2. **Within field-level definitions** for condition-specific strain information: - ```yaml - - name: heat_shock - dtype: - class_label: - names: ["control", "treated"] - role: experimental_condition - definitions: - treated: - environmental_conditions: - temperature_celsius: 37 - strain_background: - genotype: W303_derivative - description: "Heat-sensitive strain" - ``` - -The `strain_background` field accepts flexible structure as a dictionary to accommodate -varying levels of detail about strain information. - +for annotated_features datasets. The following roles are recognized by tfbpapi. +**NOTE** `experimental_condition` is a reserved role with additional behavior +as described above. ## Partitioned Datasets @@ -685,23 +312,6 @@ For partitioned datasets, partition values are extracted from directory structur partition_by: ["run_accession", "regulator_symbol"] ``` -### How Embedded Metadata Works - -1. **Partition Fields**: For partitioned datasets, values are extracted from directory - names (e.g., `accession=SRR123` to `SRR123`) -2. **Data Fields**: For single files, distinct values are extracted via HuggingFace - Datasets Server API -3. **Synthetic Config**: A synthetic metadata config is created with extracted values -4. **Automatic Pairing**: The synthetic metadata automatically applies to the source - config - -### Metadata Extraction Priority - -The system tries metadata sources in this order: -1. **Explicit metadata configs** with `applies_to` field -2. **Automatic type-based pairing** between data configs and metadata configs -3. **Embedded metadata extraction** from `metadata_fields` - ## Data File Organization ### Single Files diff --git a/docs/tutorials/datacard_tutorial.ipynb b/docs/tutorials/datacard_tutorial.ipynb index 47df9b6..055f8fe 100644 --- a/docs/tutorials/datacard_tutorial.ipynb +++ b/docs/tutorials/datacard_tutorial.ipynb @@ -4,25 +4,23 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# DataCard Tutorial: Exploring HuggingFace Genomics Datasets\n", + "# DataCard Tutorial: Exploring HuggingFace Dataset Metadata\n", "\n", - "The `DataCard` class provides an easy-to-use interface for exploring HuggingFace dataset metadata without loading the actual genomic data. This is particularly useful for:\n", + "The `DataCard` class provides an interface for exploring HuggingFace dataset metadata without loading the actual genomic data. This is particularly useful for:\n", "\n", "- Understanding dataset structure and available configurations\n", - "- Exploring experimental conditions and regulators\n", + "- Exploring experimental conditions at all hierarchy levels\n", "- Discovering metadata relationships\n", - "- Planning data analysis workflows\n", + "- Planning data analysis workflows and metadata table creation\n", "\n", - "In this tutorial, we'll explore the **BrentLab/rossi_2021** dataset, which contains ChIP-exo data for transcription factor binding in yeast." + "In this tutorial, we'll explore the **BrentLab/harbison_2004** dataset, which contains ChIP-chip data for transcription factor binding across 14 environmental conditions in yeast." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## 1. Getting Started\n", - "\n", - "First, let's import the DataCard class and initialize it with our target dataset." + "## 1. Instantiating a DataCard Object" ] }, { @@ -31,27 +29,25 @@ "metadata": {}, "outputs": [ { - "name": "stdout", + "name": "stderr", "output_type": "stream", "text": [ - "Repository: BrentLab/mahendrawada_2025\n" + "/home/chase/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" ] }, { - "name": "stderr", + "name": "stdout", "output_type": "stream", "text": [ - "/home/chase/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", - " from .autonotebook import tqdm as notebook_tqdm\n" + "Repository: BrentLab/harbison_2004\n" ] } ], "source": [ "from tfbpapi.datainfo import DataCard\n", "\n", - "# Initialize DataCard with the Rossi 2021 dataset\n", - "# try this with mahendrawada_2025, which is more complex\n", - "card = DataCard('BrentLab/mahendrawada_2025')\n", + "card = DataCard('BrentLab/harbison_2004')\n", "\n", "print(f\"Repository: {card.repo_id}\")" ] @@ -76,16 +72,16 @@ "text": [ "Repository Information:\n", "========================================\n", - "repo_id : BrentLab/mahendrawada_2025\n", - "pretty_name : Mahendrawada 2025 ChEC-seq and Nascent RNA-seq data\n", + "repo_id : BrentLab/harbison_2004\n", + "pretty_name : Harbison, 2004 ChIP-chip\n", "license : mit\n", - "tags : ['biology', 'genomics', 'yeast', 'transcription-factors', 'gene-expression', 'binding', 'chec', 'perturbation', 'rnaseq', 'nascent rnaseq']\n", + "tags : ['genomics', 'yeast', 'transcription', 'binding']\n", "language : ['en']\n", - "size_categories : ['100K \u001b[39m\u001b[32m2\u001b[39m metadata_info = \u001b[43mcard\u001b[49m\u001b[43m.\u001b[49m\u001b[43mexplore_config\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43mmetadata\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[32m 4\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33mMetadata Configuration Details:\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 5\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33m=\u001b[39m\u001b[33m\"\u001b[39m * \u001b[32m40\u001b[39m)\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/code/tfbp/tfbpapi/tfbpapi/datainfo/datacard.py:291\u001b[39m, in \u001b[36mDataCard.explore_config\u001b[39m\u001b[34m(self, config_name)\u001b[39m\n\u001b[32m 289\u001b[39m config = \u001b[38;5;28mself\u001b[39m.get_config(config_name)\n\u001b[32m 290\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m config:\n\u001b[32m--> \u001b[39m\u001b[32m291\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m DataCardError(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mConfiguration \u001b[39m\u001b[33m'\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mconfig_name\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m'\u001b[39m\u001b[33m not found\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 293\u001b[39m info: \u001b[38;5;28mdict\u001b[39m[\u001b[38;5;28mstr\u001b[39m, Any] = {\n\u001b[32m 294\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mconfig_name\u001b[39m\u001b[33m\"\u001b[39m: config.config_name,\n\u001b[32m 295\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mdescription\u001b[39m\u001b[33m\"\u001b[39m: config.description,\n\u001b[32m (...)\u001b[39m\u001b[32m 305\u001b[39m ],\n\u001b[32m 306\u001b[39m }\n\u001b[32m 308\u001b[39m \u001b[38;5;66;03m# Add partitioning info if present\u001b[39;00m\n", - "\u001b[31mDataCardError\u001b[39m: Configuration 'metadata' not found" + "name": "stdout", + "output_type": "stream", + "text": [ + "Main Configuration Details:\n", + "========================================\n", + "Config name: harbison_2004\n", + "Dataset type: annotated_features\n", + "Number of features: 7\n", + "\n", + "Features:\n", + " • condition [experimental_condition]\n", + " Environmental condition of the experiment. Nearly all of the 204 regulators have a YPD condition, and some have others in addition.\n", + " • regulator_locus_tag [regulator_identifier]\n", + " Systematic gene name (ORF identifier) of the ChIPd transcription factor\n", + " • regulator_symbol [regulator_identifier]\n", + " Standard gene symbol of the ChIPd transcription factor\n", + " • target_locus_tag [target_identifier]\n", + " Systematic gene name (ORF identifier) of the target gene measured\n", + " • target_symbol [target_identifier]\n", + " Standard gene symbol of the target gene measured\n", + " • effect [quantitative_measure]\n", + " The chip channel ratio (effect size)\n", + " • pvalue [quantitative_measure]\n", + " pvalue of the chip channel ratio (effect)\n" ] } ], "source": [ - "# Explore the metadata configuration\n", - "metadata_info = card.explore_config('metadata')\n", + "# Explore the main data configuration in detail\n", + "config_info = card.explore_config('harbison_2004')\n", "\n", - "print(\"Metadata Configuration Details:\")\n", + "print(\"Main Configuration Details:\")\n", "print(\"=\" * 40)\n", - "print(f\"Config name: {metadata_info['config_name']}\")\n", - "print(f\"Dataset type: {metadata_info['dataset_type']}\")\n", - "print(f\"Number of features: {metadata_info['num_features']}\")\n", - "print(f\"Default config: {metadata_info['is_default']}\")\n", - "\n", - "print(\"\\nFeatures in metadata config:\")\n", - "for feature in metadata_info['features']:\n", - " print(f\" • {feature['name']:20} ({feature['dtype']:10}): {feature['description']}\")" + "print(f\"Config name: {config_info['config_name']}\")\n", + "print(f\"Dataset type: {config_info['dataset_type']}\")\n", + "print(f\"Number of features: {config_info['num_features']}\")\n", + "\n", + "print(\"\\nFeatures:\")\n", + "for feature in config_info['features']:\n", + " role = f\" [{feature.get('role', 'no role')}]\" if 'role' in feature else \"\"\n", + " print(f\" • {feature['name']:25} {role}\")\n", + " print(f\" {feature['description']}\")" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Genome Map Configuration Details:\n", - "========================================\n", - "Config name: genome_map\n", - "Dataset type: genome_map\n", - "Number of features: 3\n", - "\n", - "Features in genome_map config:\n", - " • chr (string ): Chromosome name (e.g., chrI, chrII, etc.)\n", - " • pos (int32 ): Genomic position of the 5' tag\n", - " • pileup (int32 ): Depth of coverage (number of 5' tags) at this genomic position\n", - "\n", - "Partitioning Information:\n", - " Enabled: True\n", - " Partition by: ['run_accession']\n", - " Path template: genome_map/accession={run_accession}/*.parquet\n" - ] - } - ], "source": [ - "# Explore the genome_map configuration\n", - "genome_map_info = card.explore_config('genome_map')\n", + "## 4. Understanding Experimental Conditions: The Three-Level Hierarchy\n", "\n", - "print(\"Genome Map Configuration Details:\")\n", - "print(\"=\" * 40)\n", - "print(f\"Config name: {genome_map_info['config_name']}\")\n", - "print(f\"Dataset type: {genome_map_info['dataset_type']}\")\n", - "print(f\"Number of features: {genome_map_info['num_features']}\")\n", - "\n", - "print(\"\\nFeatures in genome_map config:\")\n", - "for feature in genome_map_info['features']:\n", - " print(f\" • {feature['name']:15} ({feature['dtype']:10}): {feature['description']}\")\n", - "\n", - "# Check if this config has partitioning\n", - "if 'partitioning' in genome_map_info:\n", - " print(\"\\nPartitioning Information:\")\n", - " partitioning = genome_map_info['partitioning']\n", - " print(f\" Enabled: {partitioning['enabled']}\")\n", - " print(f\" Partition by: {partitioning['partition_by']}\")\n", - " print(f\" Path template: {partitioning['path_template']}\")" + "The tfbpapi system supports experimental conditions at three hierarchy levels:\n", + "\n", + "1. **Top-level (repo-wide)**: Conditions common to all datasets/samples\n", + "2. **Config-level**: Conditions specific to a dataset configuration\n", + "3. **Field-level**: Conditions that vary per sample, defined in field definitions\n", + "\n", + "Let's explore each level for the Harbison 2004 dataset." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## 4. Understanding Data Relationships\n", + "### Level 1: Top-Level Conditions\n", "\n", - "The DataCard can help you understand how different configurations relate to each other, particularly metadata relationships." + "Top-level conditions apply to all experiments in the repository." ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Found 1 metadata relationships:\n", - "\n", - "Relationship details:\n", - " • genome_map -> metadata\n", - " Type: explicit\n", - " (Metadata config explicitly specifies which data configs it applies to)\n" + "Top-Level Experimental Conditions:\n", + "========================================\n", + "No top-level conditions defined for this repository\n", + "(All conditions are defined at config or field level)\n" ] } ], "source": [ - "# Explore metadata relationships\n", - "relationships = card.get_metadata_relationships()\n", - "\n", - "print(f\"Found {len(relationships)} metadata relationships:\")\n", - "print(\"\\nRelationship details:\")\n", + "# Get top-level experimental conditions\n", + "top_conditions = card.get_experimental_conditions()\n", "\n", - "for rel in relationships:\n", - " print(f\" • {rel.data_config} -> {rel.metadata_config}\")\n", - " print(f\" Type: {rel.relationship_type}\")\n", + "print(\"Top-Level Experimental Conditions:\")\n", + "print(\"=\" * 40)\n", "\n", - " if rel.relationship_type == \"explicit\":\n", - " print(\" (Metadata config explicitly specifies which data configs it applies to)\")\n", - " elif rel.relationship_type == \"embedded\":\n", - " print(\" (Metadata is embedded within the data config itself)\")" + "if top_conditions:\n", + " for key, value in top_conditions.items():\n", + " print(f\"{key}: {value}\")\n", + "else:\n", + " print(\"No top-level conditions defined for this repository\")\n", + " print(\"(All conditions are defined at config or field level)\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## 5. Exploring Dataset Contents\n", + "### Level 2: Config-Level Conditions\n", "\n", - "Now let's explore what experimental data is available in this dataset." + "Config-level conditions apply to all samples in a specific configuration." ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Metadata configurations: ['metadata']\n", - "Data configurations: ['genome_map']\n", - "\n", - "Default configuration: metadata\n" + "Config-Level Experimental Conditions:\n", + "========================================\n", + "No config-level conditions defined\n", + "(Conditions vary per sample at field level)\n" ] } ], "source": [ - "# Get different config types\n", - "from tfbpapi.datainfo.models import DatasetType\n", - "\n", - "# Find metadata configs\n", - "metadata_configs = card.get_configs_by_type(DatasetType.METADATA)\n", - "print(f\"Metadata configurations: {[c.config_name for c in metadata_configs]}\")\n", + "# Get config-level conditions (merged with top-level)\n", + "config_conditions = card.get_experimental_conditions('harbison_2004')\n", "\n", - "# Find data configs\n", - "data_configs = card.get_configs_by_type(DatasetType.GENOME_MAP)\n", - "print(f\"Data configurations: {[c.config_name for c in data_configs]}\")\n", + "print(\"Config-Level Experimental Conditions:\")\n", + "print(\"=\" * 40)\n", "\n", - "# Get the default config\n", - "default_config = card.dataset_card.get_default_config()\n", - "if default_config:\n", - " print(f\"\\nDefault configuration: {default_config.config_name}\")" + "if config_conditions:\n", + " for key, value in config_conditions.items():\n", + " print(f\"{key}: {value}\")\n", + "else:\n", + " print(\"No config-level conditions defined\")\n", + " print(\"(Conditions vary per sample at field level)\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Extracting Field Values\n", + "### Level 3: Field-Level Conditions\n", "\n", - "For exploration purposes, we can extract unique values from specific fields. This is particularly useful for understanding what experimental conditions or regulators are available." + "Field-level conditions vary per sample and are defined in field definitions." ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Found 0 unique run accessions:\n", - "No accessions found (might require partition-based extraction)\n" + "Fields with Experimental Conditions:\n", + "========================================\n", + "Found 1 condition field(s): ['condition']\n" ] } ], "source": [ - "# Try to extract run accession information\n", - "try:\n", - " accessions = card.get_field_values('metadata', 'run_accession')\n", - " print(f\"Found {len(accessions)} unique run accessions:\")\n", - " if accessions:\n", - " sample_accessions = sorted(list(accessions))[:5]\n", - " print(f\"Sample accessions: {sample_accessions}...\")\n", - " else:\n", - " print(\"No accessions found (might require partition-based extraction)\")\n", - "except Exception as e:\n", - " print(f\"Could not extract accession values: {e}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 6. Working with Partitioned Data\n", + "# List all experimental condition fields\n", + "condition_fields = card.list_experimental_condition_fields('harbison_2004')\n", "\n", - "Many genomics datasets are partitioned for efficient storage and querying. Let's explore how partitioning works in this dataset." + "print(\"Fields with Experimental Conditions:\")\n", + "print(\"=\" * 40)\n", + "print(f\"Found {len(condition_fields)} condition field(s): {condition_fields}\")" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Partitioning Details:\n", - "==============================\n", - "Enabled: True\n", - "Partition columns: ['run_accession']\n", - "Path template: genome_map/accession={run_accession}/*.parquet\n", + "Condition Field Definitions:\n", + "========================================\n", + "Found 14 defined conditions:\n", "\n", - "This means:\n", - "• The genome map data is split into separate files for each run_accession\n", - "• Files are organized as: genome_map/accession={run_accession}/*.parquet\n", - "• This allows efficient querying of specific experimental runs\n" + " • Acid\n", + " • Alpha\n", + " • BUT14\n", + " • BUT90\n", + " • GAL\n", + " • H2O2Hi\n", + " • H2O2Lo\n", + " • HEAT\n", + " • Pi-\n", + " • RAFF\n", + " • RAPA\n", + " • SM\n", + " • Thi-\n", + " • YPD\n" ] } ], "source": [ - "# Check partitioning details for the genome_map config\n", - "genome_map_config = card.get_config('genome_map')\n", - "\n", - "if genome_map_config and genome_map_config.dataset_info.partitioning:\n", - " part_info = genome_map_config.dataset_info.partitioning\n", - "\n", - " print(\"Partitioning Details:\")\n", - " print(\"=\" * 30)\n", - " print(f\"Enabled: {part_info.enabled}\")\n", - " print(f\"Partition columns: {part_info.partition_by}\")\n", - " print(f\"Path template: {part_info.path_template}\")\n", - "\n", - " print(\"\\nThis means:\")\n", - " print(\"• The genome map data is split into separate files for each run_accession\")\n", - " print(\"• Files are organized as: genome_map/accession={run_accession}/*.parquet\")\n", - " print(\"• This allows efficient querying of specific experimental runs\")\n", - "else:\n", - " print(\"No partitioning information found.\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 7. Understanding Data Files\n", + "# Get definitions for the 'condition' field\n", + "# This maps each condition value to its detailed specification\n", + "condition_defs = card.get_field_definitions('harbison_2004', 'condition')\n", "\n", - "Let's examine how the data files are organized within each configuration." + "print(f\"Condition Field Definitions:\")\n", + "print(\"=\" * 40)\n", + "print(f\"Found {len(condition_defs)} defined conditions:\\n\")\n", + "\n", + "# Show all condition names\n", + "for cond_name in sorted(condition_defs.keys()):\n", + " print(f\" • {cond_name}\")" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "\n", - "Data files for 'metadata' config:\n", - "----------------------------------------\n", - " File 1:\n", - " Split: train\n", - " Path: rossi_2021_metadata.parquet\n", - "\n", - "Data files for 'genome_map' config:\n", - "----------------------------------------\n", - " File 1:\n", - " Split: train\n", - " Path: genome_map/*/*.parquet\n", - " - This is a glob pattern that matches multiple files\n" + "YPD Condition Definition:\n", + "========================================\n", + "{\n", + " \"description\": \"Rich media baseline condition\",\n", + " \"temperature_celsius\": 30,\n", + " \"cultivation_method\": \"unspecified\",\n", + " \"growth_phase_at_harvest\": {\n", + " \"od600\": 0.8\n", + " },\n", + " \"media\": {\n", + " \"name\": \"YPD\",\n", + " \"carbon_source\": [\n", + " {\n", + " \"compound\": \"D-glucose\",\n", + " \"concentration_percent\": 2\n", + " }\n", + " ],\n", + " \"nitrogen_source\": [\n", + " {\n", + " \"compound\": \"yeast_extract\",\n", + " \"concentration_percent\": 1\n", + " },\n", + " {\n", + " \"compound\": \"peptone\",\n", + " \"concentration_percent\": 2\n", + " }\n", + " ]\n", + " }\n", + "}\n" ] } ], "source": [ - "# Examine data files for each configuration\n", - "for config in card.configs:\n", - " print(f\"\\nData files for '{config.config_name}' config:\")\n", - " print(\"-\" * 40)\n", - "\n", - " for i, data_file in enumerate(config.data_files):\n", - " print(f\" File {i+1}:\")\n", - " print(f\" Split: {data_file.split}\")\n", - " print(f\" Path: {data_file.path}\")\n", - "\n", - " # Explain path patterns\n", - " if '*' in data_file.path:\n", - " print(f\" - This is a glob pattern that matches multiple files\")\n", - " if '=' in data_file.path:\n", - " print(f\" - This uses partitioned directory structure\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 8. Practical Use Cases\n", + "# Explore a specific condition in detail\n", + "import json\n", "\n", - "Here are some common scenarios where DataCard is useful:" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Use Case 1: Finding Datasets with Specific Data Types" + "# Let's look at the YPD baseline condition\n", + "ypd_def = condition_defs.get('YPD', {})\n", + "\n", + "print(\"YPD Condition Definition:\")\n", + "print(\"=\" * 40)\n", + "print(json.dumps(ypd_def, indent=2))" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Available dataset types: ['metadata', 'genome_map']\n", - "\n", - "Has genome-wide binding data: True\n" + "HEAT Condition Definition:\n", + "========================================\n", + "{\n", + " \"description\": \"Heat shock stress condition\",\n", + " \"initial_temperature_celsius\": 30,\n", + " \"temperature_shift_celsius\": 37,\n", + " \"temperature_shift_duration_minutes\": 45,\n", + " \"cultivation_method\": \"unspecified\",\n", + " \"growth_phase_at_harvest\": {\n", + " \"od600\": 0.5\n", + " },\n", + " \"media\": {\n", + " \"name\": \"YPD\",\n", + " \"carbon_source\": [\n", + " {\n", + " \"compound\": \"D-glucose\",\n", + " \"concentration_percent\": 2\n", + " }\n", + " ],\n", + " \"nitrogen_source\": [\n", + " {\n", + " \"compound\": \"yeast_extract\",\n", + " \"concentration_percent\": 1\n", + " },\n", + " {\n", + " \"compound\": \"peptone\",\n", + " \"concentration_percent\": 2\n", + " }\n", + " ]\n", + " }\n", + "}\n" ] } ], "source": [ - "# Check what types of data are available\n", - "available_types = [config.dataset_type.value for config in card.configs]\n", - "print(f\"Available dataset types: {available_types}\")\n", + "# Let's look at a treatment condition (HEAT shock)\n", + "heat_def = condition_defs.get('HEAT', {})\n", "\n", - "# Check if this dataset has genome-wide binding data\n", - "has_genome_map = any(config.dataset_type == DatasetType.GENOME_MAP for config in card.configs)\n", - "print(f\"\\nHas genome-wide binding data: {has_genome_map}\")" + "print(\"HEAT Condition Definition:\")\n", + "print(\"=\" * 40)\n", + "print(json.dumps(heat_def, indent=2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Use Case 2: Understanding Data Schema Before Loading" + "## 5. Working with Condition Definitions\n", + "\n", + "Now let's see how to extract specific information from condition definitions." ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Genome Map Data Schema:\n", - "==============================\n", - "Column: chr\n", - " Type: string\n", - " Description: Chromosome name (e.g., chrI, chrII, etc.)\n", - "\n", - "Column: pos\n", - " Type: int32\n", - " Description: Genomic position of the 5' tag\n", - "\n", - "Column: pileup\n", - " Type: int32\n", - " Description: Depth of coverage (number of 5' tags) at this genomic position\n", - "\n", - "This tells us:\n", - "• 'chr' column contains chromosome names (string)\n", - "• 'pos' column contains genomic positions (int32)\n", - "• 'pileup' column contains tag counts (int32)\n", - "• Data represents 5' tag coverage from ChIP-exo experiments\n" + "Growth Media Across Conditions:\n", + "========================================\n", + " Acid : YPD\n", + " Alpha : YPD\n", + " BUT14 : YPD\n", + " BUT90 : YPD\n", + " GAL : yeast_extract_peptone\n", + " H2O2Hi : YPD\n", + " H2O2Lo : YPD\n", + " HEAT : YPD\n", + " Pi- : synthetic_complete_minus_phosphate\n", + " RAFF : yeast_extract_peptone\n", + " RAPA : YPD\n", + " SM : synthetic_complete\n", + " Thi- : synthetic_complete_minus_thiamine\n", + " YPD : YPD\n" ] } ], "source": [ - "# Before loading large genome map data, understand its structure\n", - "genome_config = card.get_config('genome_map')\n", - "\n", - "if genome_config:\n", - " print(\"Genome Map Data Schema:\")\n", - " print(\"=\" * 30)\n", - "\n", - " for feature in genome_config.dataset_info.features:\n", - " print(f\"Column: {feature.name}\")\n", - " print(f\" Type: {feature.dtype}\")\n", - " print(f\" Description: {feature.description}\")\n", - " print()\n", - "\n", - " print(\"This tells us:\")\n", - " print(\"• 'chr' column contains chromosome names (string)\")\n", - " print(\"• 'pos' column contains genomic positions (int32)\")\n", - " print(\"• 'pileup' column contains tag counts (int32)\")\n", - " print(\"• Data represents 5' tag coverage from ChIP-exo experiments\")" + "# Extract growth media names for all conditions\n", + "print(\"Growth Media Across Conditions:\")\n", + "print(\"=\" * 40)\n", + "\n", + "for cond_name, cond_def in sorted(condition_defs.items()):\n", + " # Navigate the nested structure\n", + " media = cond_def.get('media', {})\n", + " media_name = media.get('name', 'unspecified')\n", + "\n", + " print(f\" {cond_name:10}: {media_name}\")" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": 13, "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'description': 'Rich media baseline condition',\n", + " 'temperature_celsius': 30,\n", + " 'cultivation_method': 'unspecified',\n", + " 'growth_phase_at_harvest': {'od600': 0.8},\n", + " 'media': {'name': 'YPD',\n", + " 'carbon_source': [{'compound': 'D-glucose', 'concentration_percent': 2}],\n", + " 'nitrogen_source': [{'compound': 'yeast_extract',\n", + " 'concentration_percent': 1},\n", + " {'compound': 'peptone', 'concentration_percent': 2}]}}" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "### Use Case 3: Planning Efficient Data Access" + "condition_defs.get(\"YPD\")" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Data Access Strategy:\n", - "=========================\n", - "• Data is partitioned by 'run_accession'\n", - "• To load data for a specific experiment, filter by run_accession\n", - "• This avoids loading data from all experiments\n", - "• Path pattern: genome_map/accession={run_accession}/*.parquet\n", - "\n", - "Example workflow:\n", - "1. Use metadata config to find interesting run_accessions\n", - "2. Load only genome_map data for those specific accessions\n", - "3. Analyze position-level binding data for selected experiments\n" + "Temperature Across Conditions:\n", + "========================================\n", + " Acid : not specified°C\n", + " Alpha : not specified°C\n", + " BUT14 : not specified°C\n", + " BUT90 : not specified°C\n", + " GAL : not specified°C\n", + " H2O2Hi : not specified°C\n", + " H2O2Lo : not specified°C\n", + " HEAT : not specified°C\n", + " Pi- : not specified°C\n", + " RAFF : not specified°C\n", + " RAPA : not specified°C\n", + " SM : not specified°C\n", + " Thi- : not specified°C\n", + " YPD : not specified°C\n" ] } ], "source": [ - "# Understanding partitioning helps plan efficient queries\n", - "if genome_config and genome_config.dataset_info.partitioning:\n", - " print(\"Data Access Strategy:\")\n", - " print(\"=\" * 25)\n", - " print(\"• Data is partitioned by 'run_accession'\")\n", - " print(\"• To load data for a specific experiment, filter by run_accession\")\n", - " print(\"• This avoids loading data from all experiments\")\n", - " print(\"• Path pattern: genome_map/accession={run_accession}/*.parquet\")\n", - "\n", - " print(\"\\nExample workflow:\")\n", - " print(\"1. Use metadata config to find interesting run_accessions\")\n", - " print(\"2. Load only genome_map data for those specific accessions\")\n", - " print(\"3. Analyze position-level binding data for selected experiments\")" + "# Extract temperature conditions\n", + "print(\"Temperature Across Conditions:\")\n", + "print(\"=\" * 40)\n", + "\n", + "for cond_name, cond_def in sorted(condition_defs.items()):\n", + " env_conds = cond_def.get('environmental_conditions', {})\n", + " temp = env_conds.get('temperature_celsius', 'not specified')\n", + "\n", + " # Also check for temperature shifts\n", + " temp_shift = env_conds.get('temperature_shift')\n", + " if temp_shift:\n", + " from_temp = temp_shift.get('from_celsius', '?')\n", + " to_temp = temp_shift.get('to_celsius', '?')\n", + " print(f\" {cond_name:10}: {from_temp}°C → {to_temp}°C\")\n", + " else:\n", + " print(f\" {cond_name:10}: {temp}°C\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## 9. Error Handling and Troubleshooting\n", + "## 6. Using extract_metadata_schema for Metadata Table Planning\n", "\n", - "The DataCard class includes validation and error handling. Here are some common scenarios:" + "The `extract_metadata_schema` method provides all condition information in one call, which is useful for planning metadata table creation." ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Non-existent config result: None\n", - "Error accessing non-existent field: Field 'nonexistent_field' not found in config 'metadata'\n", - "Config 'some_config' not found in this dataset\n" + "Metadata Schema Summary:\n", + "========================================\n", + "Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", + "Target fields: ['target_locus_tag', 'target_symbol']\n", + "Condition fields: ['condition']\n", + "\n", + "Top-level conditions: None\n", + "Config-level conditions: None\n", + "Field definitions available for: ['condition']\n" ] } ], "source": [ - "# Handling missing configurations\n", - "missing_config = card.get_config('nonexistent_config')\n", - "print(f\"Non-existent config result: {missing_config}\")\n", - "\n", - "# Handling missing fields\n", - "try:\n", - " invalid_field = card.get_field_values('metadata', 'nonexistent_field')\n", - "except Exception as e:\n", - " print(f\"Error accessing non-existent field: {e}\")\n", - "\n", - "# Checking if config exists before using\n", - "config_name = 'some_config'\n", - "if card.get_config(config_name):\n", - " print(f\"Config '{config_name}' exists\")\n", - "else:\n", - " print(f\"Config '{config_name}' not found in this dataset\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 10. Summary and Next Steps\n", - "\n", - "The DataCard class provides a powerful way to explore HuggingFace genomics datasets before committing to loading large amounts of data. \n", - "\n", - "### Key Takeaways:\n", + "# Extract complete metadata schema\n", + "schema = card.extract_metadata_schema('harbison_2004')\n", "\n", - "1. **Dataset Structure**: The Rossi 2021 dataset contains both experimental metadata and genome-wide ChIP-exo binding data\n", - "2. **Partitioning**: Data is efficiently partitioned by experimental run for fast access\n", - "3. **Metadata Relationships**: The system automatically understands how metadata relates to data configs\n", - "4. **Schema Discovery**: You can understand data types and structure before loading\n", - "\n", - "### Next Steps:\n", - "\n", - "- Use `HfQueryAPI` to load specific subsets of the data based on your exploration\n", - "- Apply filters based on experimental conditions discovered through DataCard\n", - "- Combine multiple datasets that have compatible schemas\n", - "\n", - "### Example Integration with HfQueryAPI:\n", - "\n", - "```python\n", - "from tfbpapi import HfQueryAPI\n", - "\n", - "# After exploring with DataCard, load specific data\n", - "query_api = HfQueryAPI('BrentLab/rossi_2021')\n", - "\n", - "# Load metadata for planning\n", - "metadata_df = query_api.get_pandas('metadata')\n", - "\n", - "# Load genome map data for specific experiments\n", - "# (using partition filters based on DataCard exploration)\n", - "genome_data = query_api.get_pandas('genome_map', \n", - " filters={'run_accession': 'SRR123456'})\n", - "```" + "print(\"Metadata Schema Summary:\")\n", + "print(\"=\" * 40)\n", + "print(f\"Regulator fields: {schema['regulator_fields']}\")\n", + "print(f\"Target fields: {schema['target_fields']}\")\n", + "print(f\"Condition fields: {schema['condition_fields']}\")\n", + "print(f\"\\nTop-level conditions: {schema['top_level_conditions']}\")\n", + "print(f\"Config-level conditions: {schema['config_level_conditions']}\")\n", + "print(f\"Field definitions available for: {list(schema['condition_definitions'].keys())}\")" ] } ], diff --git a/docs/tutorials/metadata_explorer_tutorial.ipynb b/docs/tutorials/metadata_explorer_tutorial.ipynb deleted file mode 100644 index 0d4104a..0000000 --- a/docs/tutorials/metadata_explorer_tutorial.ipynb +++ /dev/null @@ -1,1027 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Cross-Dataset Metadata Exploration Tutorial\n", - "\n", - "This tutorial demonstrates how to use the `MetadataManager` to extract and query metadata across multiple HuggingFace datasets from different repositories.\n", - "\n", - "## Overview\n", - "\n", - "The `MetadataManager` enables:\n", - "- **Cross-dataset queries**: Filter metadata across MULTIPLE datasets from MULTIPLE repos\n", - "- **Low memory usage**: DuckDB temporary views over parquet files (no data loading)\n", - "- **Role-based alignment**: Heterogeneous schemas aligned by field roles (regulator_identifier, experimental_condition, etc.)\n", - "- **Searchable conditions**: Factor level names + flattened definitions for filtering\n", - "\n", - "## Datasets Used\n", - "\n", - "We'll explore two transcription factor binding datasets:\n", - "\n", - "1. **harbison_2004**: ChIP-chip TF binding across 14 environmental conditions (YPD, RAPA, HEAT, etc.)\n", - "2. **hackett_2020**: TF overexpression with nutrient limitation conditions (P, N, M restrictions)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "from tfbpapi.datainfo import DataCard, MetadataManager" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Part 1: Exploring Individual Dataset Metadata Schemas\n", - "\n", - "Before cross-dataset queries, let's understand each dataset's metadata structure using `DataCard.extract_metadata_schema()`." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Harbison 2004 Metadata Schema" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Harbison 2004 Metadata Schema\n", - "============================================================\n", - "Regulator Fields: ['regulator_locus_tag', 'regulator_symbol']\n", - "Target Fields: ['target_locus_tag', 'target_symbol']\n", - "Condition Fields: ['condition']\n", - "\n", - "Repo-Level Conditions: None\n", - "\n", - "Config-Level Conditions: None\n", - "\n", - "Field-Level Conditions (vary by sample):\n", - " - condition: 14 conditions defined\n", - "\n", - " Sample Conditions:\n", - " - YPD\n", - " - SM\n", - " - RAPA\n" - ] - } - ], - "source": [ - "# Load harbison_2004 datacard\n", - "harbison_card = DataCard(repo_id=\"BrentLab/harbison_2004\")\n", - "\n", - "# Extract metadata schema for the harbison_2004 config\n", - "harbison_schema = harbison_card.extract_metadata_schema(\"harbison_2004\")\n", - "\n", - "print(\"Harbison 2004 Metadata Schema\")\n", - "print(\"=\" * 60)\n", - "print(f\"Regulator Fields: {harbison_schema['regulator_fields']}\")\n", - "print(f\"Target Fields: {harbison_schema['target_fields']}\")\n", - "print(f\"Condition Fields: {harbison_schema['condition_fields']}\")\n", - "\n", - "# Check for repo-level conditions\n", - "if harbison_schema.get('top_level_conditions'):\n", - " print(\"\\nRepo-Level Conditions (apply to ALL samples):\")\n", - " top_cond = harbison_schema['top_level_conditions']\n", - " if top_cond.strain_background:\n", - " print(f\" - Strain background: {top_cond.strain_background}\")\n", - " if top_cond.environmental_conditions:\n", - " print(\" - Environmental conditions defined\")\n", - "else:\n", - " print(\"\\nRepo-Level Conditions: None\")\n", - "\n", - "# Check for config-level conditions\n", - "if harbison_schema.get('config_level_conditions'):\n", - " print(\"\\nConfig-Level Conditions:\")\n", - " print(\" (Defined for this config)\")\n", - "else:\n", - " print(\"\\nConfig-Level Conditions: None\")\n", - "\n", - "# Show field-level condition definitions\n", - "print(f\"\\nField-Level Conditions (vary by sample):\")\n", - "print(f\" - condition: {len(harbison_schema['condition_definitions'].get('condition', {}))} conditions defined\")\n", - "\n", - "# Show first few condition definitions\n", - "if 'condition' in harbison_schema['condition_definitions']:\n", - " print(\"\\n Sample Conditions:\")\n", - " for cond_name in list(harbison_schema['condition_definitions']['condition'].keys())[:3]:\n", - " print(f\" - {cond_name}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Hackett 2020 Metadata Schema" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hackett 2020 Metadata Schema\n", - "============================================================\n", - "Regulator Fields: ['regulator_locus_tag', 'regulator_symbol']\n", - "Target Fields: ['target_locus_tag', 'target_symbol']\n", - "\n", - "Repo-Level Conditions (apply to ALL samples):\n", - " - Temperature: 30.0 C\n", - " - Cultivation: chemostat\n", - " - Media: minimal\n", - " - Carbon source: D-glucose @ 1.0%\n", - "\n", - "Experimental Condition Fields:\n", - " - time: values from data\n", - " - mechanism: 2 defined levels\n", - " - restriction: 3 defined levels\n", - " - date: values from data\n", - " - strain: values from data\n" - ] - } - ], - "source": [ - "# Load hackett_2020 datacard\n", - "hackett_card = DataCard(repo_id=\"BrentLab/hackett_2020\")\n", - "\n", - "# Extract metadata schema\n", - "hackett_schema = hackett_card.extract_metadata_schema(\"hackett_2020\")\n", - "\n", - "print(\"Hackett 2020 Metadata Schema\")\n", - "print(\"=\" * 60)\n", - "print(f\"Regulator Fields: {hackett_schema['regulator_fields']}\")\n", - "print(f\"Target Fields: {hackett_schema['target_fields']}\")\n", - "\n", - "# Show repo-level (top-level) experimental conditions\n", - "if hackett_schema.get('top_level_conditions'):\n", - " print(\"\\nRepo-Level Conditions (apply to ALL samples):\")\n", - " top_cond = hackett_schema['top_level_conditions']\n", - "\n", - " # Check for structured environmental_conditions\n", - " if top_cond.environmental_conditions:\n", - " env = top_cond.environmental_conditions\n", - " if env.temperature_celsius is not None:\n", - " print(f\" - Temperature: {env.temperature_celsius} C\")\n", - " if env.cultivation_method:\n", - " print(f\" - Cultivation: {env.cultivation_method}\")\n", - " if env.media:\n", - " print(f\" - Media: {env.media.name}\")\n", - " if env.media.carbon_source:\n", - " for cs in env.media.carbon_source:\n", - " print(f\" - Carbon source: {cs.compound} @ {cs.concentration_percent}%\")\n", - "\n", - " # Check for extra fields (alternate structure)\n", - " if hasattr(top_cond, 'model_extra') and top_cond.model_extra:\n", - " for key, value in top_cond.model_extra.items():\n", - " print(f\" - {key}: {value}\")\n", - "\n", - "# Show all experimental condition fields\n", - "if hackett_schema['condition_fields']:\n", - " print(\"\\nExperimental Condition Fields:\")\n", - " for field_name in hackett_schema['condition_fields']:\n", - " # Check if field has definitions (structured factor levels)\n", - " if field_name in hackett_schema['condition_definitions']:\n", - " num_levels = len(hackett_schema['condition_definitions'][field_name])\n", - " print(f\" - {field_name}: {num_levels} defined levels\")\n", - " else:\n", - " print(f\" - {field_name}: values from data\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Comparing Schemas\n", - "\n", - "Notice:\n", - "- **Common fields**: Both have `regulator_locus_tag` and `regulator_symbol` (aligned by `role=regulator_identifier`)\n", - "- **Different condition fields**: harbison has `condition`, hackett has `time`, `mechanism`, `restriction`\n", - "- **Three-level condition hierarchy**:\n", - " - **Repo-level**: Conditions in `dataset_card.experimental_conditions` apply to ALL configs/samples\n", - " - Example: hackett_2020 has temperature (30°C), cultivation method (chemostat), base media (minimal)\n", - " - **Config-level**: Conditions in `config.experimental_conditions` apply to all samples in that config\n", - " - Example: harbison_2004 has strain background defined at config level\n", - " - **Field-level**: Conditions in field `definitions` vary per sample (factor levels)\n", - " - Example: harbison_2004's \"condition\" field with YPD, RAPA, HEAT definitions" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Part 2: Cross-Dataset Metadata with MetadataManager\n", - "\n", - "Now let's use `MetadataManager` to query metadata across both datasets." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Initialize MetadataManager" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "MetadataManager initialized\n", - "Cache enabled: False\n", - "Registered datasets: 0\n" - ] - } - ], - "source": [ - "# Create metadata manager (session-only by default, no caching)\n", - "mgr = MetadataManager()\n", - "\n", - "print(\"MetadataManager initialized\")\n", - "print(f\"Cache enabled: {mgr._cache_enabled}\")\n", - "print(f\"Registered datasets: {len(mgr._registered_datasets)}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Register Datasets\n", - "\n", - "Register both datasets for cross-dataset querying. The manager will:\n", - "1. Load each DataCard\n", - "2. Extract metadata schema\n", - "3. Create DuckDB temporary views\n", - "4. Build unified view with column alignment" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Registering BrentLab/harbison_2004...\n", - "Registering BrentLab/hackett_2020...\n", - "\n", - "Registration complete!\n", - "Total registered datasets: 2\n", - "Active configs: [('BrentLab/harbison_2004', 'harbison_2004'), ('BrentLab/hackett_2020', 'hackett_2020')]\n" - ] - } - ], - "source": [ - "# Register harbison_2004\n", - "print(\"Registering BrentLab/harbison_2004...\")\n", - "mgr.register(\"BrentLab/harbison_2004\")\n", - "\n", - "# Register hackett_2020 \n", - "print(\"Registering BrentLab/hackett_2020...\")\n", - "mgr.register(\"BrentLab/hackett_2020\")\n", - "\n", - "print(\"\\nRegistration complete!\")\n", - "print(f\"Total registered datasets: {len(mgr._registered_datasets)}\")\n", - "print(f\"Active configs: {mgr.get_active_configs()}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### View Summary\n", - "\n", - "Get an overview of registered datasets and their configs." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Registered Datasets Summary\n", - "============================================================\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "dataset", - "rawType": "object", - "type": "string" - }, - { - "name": "config_name", - "rawType": "object", - "type": "string" - }, - { - "name": "view_name", - "rawType": "object", - "type": "string" - } - ], - "ref": "963ab794-8ad8-4c6c-967b-17439c5c0921", - "rows": [ - [ - "0", - "BrentLab/harbison_2004", - "harbison_2004", - "BrentLab_harbison_2004_harbison_2004_metadata" - ], - [ - "1", - "BrentLab/hackett_2020", - "hackett_2020", - "BrentLab_hackett_2020_hackett_2020_metadata" - ] - ], - "shape": { - "columns": 3, - "rows": 2 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
datasetconfig_nameview_name
0BrentLab/harbison_2004harbison_2004BrentLab_harbison_2004_harbison_2004_metadata
1BrentLab/hackett_2020hackett_2020BrentLab_hackett_2020_hackett_2020_metadata
\n", - "
" - ], - "text/plain": [ - " dataset config_name \\\n", - "0 BrentLab/harbison_2004 harbison_2004 \n", - "1 BrentLab/hackett_2020 hackett_2020 \n", - "\n", - " view_name \n", - "0 BrentLab_harbison_2004_harbison_2004_metadata \n", - "1 BrentLab_hackett_2020_hackett_2020_metadata " - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Get summary of registered datasets\n", - "summary = mgr.get_summary()\n", - "\n", - "print(\"Registered Datasets Summary\")\n", - "print(\"=\" * 60)\n", - "display(summary)" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Three-Level Experimental Condition Hierarchy\n", - "================================================================================\n", - "\n", - "Example: hackett_2020 dataset\n", - "--------------------------------------------------------------------------------\n", - "\n", - "[1] REPO-LEVEL (DatasetCard.experimental_conditions)\n", - " Applies to: ALL configs and ALL samples in the repository\n", - " - temperature_celsius: 30.0\n", - " - cultivation_method: chemostat\n", - " - media.name: minimal\n", - " - media.carbon_source: D-glucose @ 1.0%\n", - "\n", - "[2] CONFIG-LEVEL (DatasetConfig.experimental_conditions)\n", - " Applies to: All samples in the 'hackett_2020' config\n", - " - (None - all repo-level conditions inherited)\n", - "\n", - "[3] FIELD-LEVEL (FeatureInfo.definitions)\n", - " Applies to: Individual samples based on field value\n", - " - restriction field: 3 levels (P, N, M)\n", - " - P: Phosphate limitation\n", - " - N: Nitrogen limitation\n", - " - M: Magnesium limitation\n", - " - time field: Various time points (30.0, 60.0, etc.)\n", - "\n", - "================================================================================\n", - "KEY CONCEPT: Hierarchy Merging\n", - "--------------------------------------------------------------------------------\n", - "When MetadataManager creates metadata tables, it merges all three levels:\n", - " - Repo-level conditions -> columns for ALL samples (temperature: 30C, etc.)\n", - " - Config-level conditions -> columns for config samples (override repo-level if present)\n", - " - Field-level conditions -> vary by sample (restriction: P/N/M)\n", - "\n", - "Precedence: Field-level > Config-level > Repo-level\n", - "\n", - "Result: Each sample gets columns from all applicable levels!\n" - ] - } - ], - "source": [ - "print(\"Three-Level Experimental Condition Hierarchy\")\n", - "print(\"=\" * 80)\n", - "\n", - "# Show hackett_2020 as example\n", - "print(\"\\nExample: hackett_2020 dataset\")\n", - "print(\"-\" * 80)\n", - "\n", - "# 1. Repo-level (applies to ALL samples)\n", - "print(\"\\n[1] REPO-LEVEL (DatasetCard.experimental_conditions)\")\n", - "print(\" Applies to: ALL configs and ALL samples in the repository\")\n", - "if hackett_schema.get('top_level_conditions'):\n", - " top_cond = hackett_schema['top_level_conditions']\n", - " \n", - " # Check structured environmental_conditions\n", - " if top_cond.environmental_conditions:\n", - " env = top_cond.environmental_conditions\n", - " print(f\" - temperature_celsius: {env.temperature_celsius}\")\n", - " print(f\" - cultivation_method: {env.cultivation_method}\")\n", - " print(f\" - media.name: {env.media.name}\")\n", - " print(f\" - media.carbon_source: {env.media.carbon_source[0].compound} @ {env.media.carbon_source[0].concentration_percent}%\")\n", - " \n", - " # Check extra fields (alternate structure used by some datacards)\n", - " if hasattr(top_cond, 'model_extra') and top_cond.model_extra:\n", - " for key, value in top_cond.model_extra.items():\n", - " print(f\" - {key}: {value}\")\n", - "\n", - "# 2. Config-level (applies to samples in this config)\n", - "print(\"\\n[2] CONFIG-LEVEL (DatasetConfig.experimental_conditions)\")\n", - "print(\" Applies to: All samples in the 'hackett_2020' config\")\n", - "if hackett_schema.get('config_level_conditions'):\n", - " print(\" - (Specific conditions defined)\")\n", - "else:\n", - " print(\" - (None - all repo-level conditions inherited)\")\n", - "\n", - "# 3. Field-level (varies by sample)\n", - "print(\"\\n[3] FIELD-LEVEL (FeatureInfo.definitions)\")\n", - "print(\" Applies to: Individual samples based on field value\")\n", - "print(\" - restriction field: 3 levels (P, N, M)\")\n", - "print(\" - P: Phosphate limitation\")\n", - "print(\" - N: Nitrogen limitation\")\n", - "print(\" - M: Magnesium limitation\")\n", - "print(\" - time field: Various time points (30.0, 60.0, etc.)\")\n", - "\n", - "print(\"\\n\" + \"=\" * 80)\n", - "print(\"KEY CONCEPT: Hierarchy Merging\")\n", - "print(\"-\" * 80)\n", - "print(\"When MetadataManager creates metadata tables, it merges all three levels:\")\n", - "print(\" - Repo-level conditions -> columns for ALL samples (temperature: 30C, etc.)\")\n", - "print(\" - Config-level conditions -> columns for config samples (override repo-level if present)\")\n", - "print(\" - Field-level conditions -> vary by sample (restriction: P/N/M)\")\n", - "print(\"\\nPrecedence: Field-level > Config-level > Repo-level\")\n", - "print(\"\\nResult: Each sample gets columns from all applicable levels!\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Part 2.5: Understanding the Three-Level Condition Hierarchy\n", - "\n", - "Experimental conditions can be specified at three different levels in the hierarchy. Let's examine how this works with a concrete example from hackett_2020." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Part 3: Querying Across Datasets (Placeholder)\n", - "\n", - "**Note**: The current implementation has a placeholder for actual parquet file loading from HuggingFace. The examples below show the intended usage once parquet integration is complete.\n", - "\n", - "### Example Query 1: Find all regulators across datasets" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "WARNING: Parquet file loading not yet implemented\n", - " This query will work once HuggingFace parquet integration is added\n" - ] - } - ], - "source": [ - "# Example query (will work once parquet loading is implemented)\n", - "# \n", - "# regulators_query = \"\"\"\n", - "# SELECT DISTINCT \n", - "# dataset,\n", - "# config_name,\n", - "# regulator_symbol,\n", - "# regulator_locus_tag\n", - "# FROM unified_metadata\n", - "# WHERE regulator_symbol != 'unspecified'\n", - "# ORDER BY dataset, regulator_symbol\n", - "# \"\"\"\n", - "# \n", - "# regulators_df = mgr.query(regulators_query)\n", - "# print(f\"\\nFound {len(regulators_df)} unique regulators across datasets\")\n", - "# display(regulators_df.head(10))\n", - "\n", - "print(\"WARNING: Parquet file loading not yet implemented\")\n", - "print(\" This query will work once HuggingFace parquet integration is added\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Example Query 2: Filter by specific regulator" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "WARNING: Parquet file loading not yet implemented\n" - ] - } - ], - "source": [ - "# Example: Find GLN3 samples across both datasets\n", - "#\n", - "# gln3_query = \"\"\"\n", - "# SELECT \n", - "# dataset,\n", - "# sample_id,\n", - "# regulator_symbol,\n", - "# condition,\n", - "# time,\n", - "# restriction,\n", - "# growth_media,\n", - "# components\n", - "# FROM unified_metadata\n", - "# WHERE regulator_symbol = 'GLN3'\n", - "# \"\"\"\n", - "# \n", - "# gln3_samples = mgr.query(gln3_query)\n", - "# print(f\"\\nGLN3 samples: {len(gln3_samples)}\")\n", - "# display(gln3_samples)\n", - "\n", - "print(\"WARNING: Parquet file loading not yet implemented\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Example Query 3: Search by media components" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "WARNING: Parquet file loading not yet implemented\n", - " This demonstrates searching by specific media components with concentrations\n" - ] - } - ], - "source": [ - "# Example: Find samples with D-glucose at 2%\n", - "#\n", - "# glucose_query = \"\"\"\n", - "# SELECT \n", - "# dataset,\n", - "# COUNT(*) as sample_count,\n", - "# growth_media\n", - "# FROM unified_metadata\n", - "# WHERE components LIKE '%carbon_source:D-glucose@2%'\n", - "# GROUP BY dataset, growth_media\n", - "# \"\"\"\n", - "# \n", - "# glucose_samples = mgr.query(glucose_query)\n", - "# print(\"\\nSamples with D-glucose at 2%:\")\n", - "# display(glucose_samples)\n", - "\n", - "print(\"WARNING: Parquet file loading not yet implemented\")\n", - "print(\" This demonstrates searching by specific media components with concentrations\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Part 4: Understanding Metadata Flattening\n", - "\n", - "The MetadataManager flattens condition definitions into searchable fields using known separators." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Separator Conventions" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Component Separator Conventions\n", - "============================================================\n", - " type_value ':'\n", - " value_conc '@'\n", - " components ';'\n", - " types '|'\n", - "\n", - "Usage Examples:\n", - " growth_media: 'YPD' (simple name)\n", - " components: 'carbon_source:D-glucose@2%|nitrogen_source:yeast_extract@1%;peptone@2%'\n", - "\n", - " Breakdown:\n", - " - ':' separates type from value (carbon_source:D-glucose)\n", - " - '@' separates value from concentration (D-glucose@2%)\n", - " - ';' separates multiple compounds of same type (yeast_extract@1%;peptone@2%)\n", - " - '|' separates different component types (carbon | nitrogen)\n" - ] - } - ], - "source": [ - "from tfbpapi.datainfo.metadata_manager import COMPONENT_SEPARATORS\n", - "\n", - "print(\"\\nComponent Separator Conventions\")\n", - "print(\"=\" * 60)\n", - "for key, sep in COMPONENT_SEPARATORS.items():\n", - " print(f\" {key:20s} '{sep}'\")\n", - " \n", - "print(\"\\nUsage Examples:\")\n", - "print(\" growth_media: 'YPD' (simple name)\")\n", - "print(\" components: 'carbon_source:D-glucose@2%|nitrogen_source:yeast_extract@1%;peptone@2%'\")\n", - "print(\"\\n Breakdown:\")\n", - "print(\" - ':' separates type from value (carbon_source:D-glucose)\")\n", - "print(\" - '@' separates value from concentration (D-glucose@2%)\")\n", - "print(\" - ';' separates multiple compounds of same type (yeast_extract@1%;peptone@2%)\")\n", - "print(\" - '|' separates different component types (carbon | nitrogen)\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Example: Flattening a Condition Definition\n", - "\n", - "Let's manually flatten a condition definition to see how it works." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Original YPD Condition Definition\n", - "============================================================\n", - "Description: Rich media baseline condition\n", - "\n", - "Flattened Representation\n", - "============================================================\n", - "growth_media: 'unspecified'\n", - "components: ''\n", - "\n", - "This format enables SQL searches like:\n", - " WHERE growth_media = 'YPD'\n", - " WHERE components LIKE '%carbon_source:D-glucose@2%'\n", - " WHERE components LIKE '%nitrogen_source:yeast_extract%'\n" - ] - } - ], - "source": [ - "# Get YPD condition definition from harbison_2004\n", - "ypd_definition = harbison_schema['condition_definitions']['condition']['YPD']\n", - "\n", - "print(\"Original YPD Condition Definition\")\n", - "print(\"=\" * 60)\n", - "print(f\"Description: {ypd_definition.get('description', 'N/A')}\")\n", - "\n", - "if 'environmental_conditions' in ypd_definition:\n", - " env = ypd_definition['environmental_conditions']\n", - " print(f\"\\nTemperature: {env.get('temperature_celsius', 'N/A')}°C\")\n", - " \n", - " if 'media' in env:\n", - " media = env['media']\n", - " print(f\"Media Name: {media.get('name', 'N/A')}\")\n", - " print(f\"\\nCarbon Source:\")\n", - " for cs in media.get('carbon_source', []):\n", - " print(f\" - {cs.get('compound', cs)}: {cs.get('concentration_percent', 'N/A')}%\")\n", - " print(f\"\\nNitrogen Source:\")\n", - " for ns in media.get('nitrogen_source', []):\n", - " print(f\" - {ns.get('compound', ns)}: {ns.get('concentration_percent', 'N/A')}%\")\n", - "\n", - "# Flatten using MetadataManager method\n", - "flattened = mgr._flatten_condition_definition(ypd_definition)\n", - "\n", - "print(\"\\nFlattened Representation\")\n", - "print(\"=\" * 60)\n", - "print(f\"growth_media: '{flattened['growth_media']}'\")\n", - "print(f\"components: '{flattened['components']}'\")\n", - "\n", - "print(\"\\nThis format enables SQL searches like:\")\n", - "print(\" WHERE growth_media = 'YPD'\")\n", - "print(\" WHERE components LIKE '%carbon_source:D-glucose@2%'\")\n", - "print(\" WHERE components LIKE '%nitrogen_source:yeast_extract%'\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Part 5: Schema Heterogeneity Handling\n", - "\n", - "Different datasets have different metadata fields. MetadataManager handles this by:\n", - "1. **Role-based alignment**: Fields grouped by semantic role (regulator_identifier, experimental_condition)\n", - "2. **Column defaulting**: Missing columns default to \"unspecified\" in unified view\n", - "3. **UNION ALL**: All views combined with column alignment" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Schema Heterogeneity Example\n", - "============================================================\n", - "\n", - "Harbison 2004 has:\n", - " - condition field (14 levels: YPD, RAPA, HEAT, etc.)\n", - " - NO time, mechanism, restriction fields\n", - "\n", - "Hackett 2020 has:\n", - " - time field (time points in minutes)\n", - " - mechanism field (GEV, ZEV induction systems)\n", - " - restriction field (P, N, M nutrient limitations)\n", - " - NO condition field\n", - "\n", - "In unified view:\n", - " - Harbison rows: condition='YPD', time='unspecified', restriction='unspecified'\n", - " - Hackett rows: condition='unspecified', time=30.0, restriction='P'\n", - "\n", - " Both can be queried together:\n", - " SELECT * FROM unified_metadata WHERE regulator_symbol = 'GLN3'\n", - " Returns samples from BOTH datasets!\n" - ] - } - ], - "source": [ - "print(\"Schema Heterogeneity Example\")\n", - "print(\"=\" * 60)\n", - "\n", - "print(\"\\nHarbison 2004 has:\")\n", - "print(f\" - condition field (14 levels: YPD, RAPA, HEAT, etc.)\")\n", - "print(f\" - NO time, mechanism, restriction fields\")\n", - "\n", - "print(\"\\nHackett 2020 has:\")\n", - "print(f\" - time field (time points in minutes)\")\n", - "print(f\" - mechanism field (GEV, ZEV induction systems)\")\n", - "print(f\" - restriction field (P, N, M nutrient limitations)\")\n", - "print(f\" - NO condition field\")\n", - "\n", - "print(\"\\nIn unified view:\")\n", - "print(\" - Harbison rows: condition='YPD', time='unspecified', restriction='unspecified'\")\n", - "print(\" - Hackett rows: condition='unspecified', time=30.0, restriction='P'\")\n", - "print(\"\\n Both can be queried together:\")\n", - "print(\" SELECT * FROM unified_metadata WHERE regulator_symbol = 'GLN3'\")\n", - "print(\" Returns samples from BOTH datasets!\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Part 6: Cleanup and Next Steps" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Unregister Datasets\n", - "\n", - "You can remove datasets from the manager when done." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Summary\n", - "\n", - "This tutorial demonstrated:\n", - "\n", - "- **Metadata schema extraction** - Using `DataCard.extract_metadata_schema()` to understand field roles\n", - "\n", - "- **Three-level condition hierarchy** - Repo-level, config-level, and field-level experimental conditions\n", - "\n", - "- **Cross-dataset registration** - Registering multiple datasets in `MetadataManager`\n", - "\n", - "- **Condition flattening** - How definitions are flattened into searchable fields with known separators\n", - "\n", - "- **Schema heterogeneity** - How different metadata schemas are aligned by role with \"unspecified\" defaults\n", - "\n", - "- **Future: SQL queries** - Once parquet loading is implemented, full cross-dataset SQL queries will work\n", - "\n", - "### Key Takeaways\n", - "\n", - "1. **Role-based alignment**: Fields are grouped by semantic role (regulator_identifier, experimental_condition), not by name\n", - "2. **Three-level hierarchy**: Conditions can be specified at repo-level (all samples), config-level (config samples), or field-level (individual samples)\n", - "3. **Hierarchy merging**: MetadataManager merges all three levels with precedence: field > config > repo\n", - "4. **Structured separators**: Media components use `:`, `@`, `;`, `|` for predictable parsing\n", - "5. **Low memory**: DuckDB temp views mean no data loading until you query\n", - "6. **Session-only default**: No caching by default (set `cache=True` if needed)\n", - "7. **Dataset + config uniqueness**: Samples identified by (dataset, config_name, sample_id) tuple\n", - "\n", - "### Understanding the Condition Hierarchy\n", - "\n", - "The three-level hierarchy allows flexible experimental condition specification:\n", - "\n", - "- **Repo-level**: Common conditions shared by all experiments (e.g., standard temperature, base strain)\n", - "- **Config-level**: Conditions specific to a dataset configuration (e.g., growth phase for a time course)\n", - "- **Field-level**: Sample-specific conditions that vary within an experiment (e.g., different media, treatments)\n", - "\n", - "This design enables:\n", - "- Efficient specification without repetition\n", - "- Clear inheritance and overriding semantics\n", - "- Consistent querying across heterogeneous datasets" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Next Steps\n", - "\n", - "Once parquet loading is implemented, you'll be able to:\n", - "\n", - "1. **Filter by regulators**: Find all samples for specific TFs across datasets\n", - "2. **Search by conditions**: Query by media, temperature, treatments, etc.\n", - "3. **Cross-dataset analysis**: Compare experimental designs across studies\n", - "4. **Metadata-driven data loading**: Use `get_active_configs()` to load actual expression/binding data\n", - "\n", - "### Integration with HfQueryAPI (Future)\n", - "\n", - "The intended workflow:\n", - "```python\n", - "# 1. Filter metadata across datasets\n", - "mgr = MetadataManager()\n", - "mgr.register(\"BrentLab/harbison_2004\")\n", - "mgr.register(\"BrentLab/hackett_2020\")\n", - "mgr.filter_by_regulator([\"GLN3\", \"GCN4\"])\n", - "mgr.filter_by_conditions(growth_media=\"YPD\")\n", - "\n", - "# 2. Get active configs\n", - "active_configs = mgr.get_active_configs()\n", - "# Returns: [('BrentLab/harbison_2004', 'harbison_2004'), ...]\n", - "\n", - "# 3. Load actual data (requires HfQueryAPI refactor)\n", - "# TBD - design after MetadataManager is complete\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Summary\n", - "\n", - "This tutorial demonstrated:\n", - "\n", - "- **Metadata schema extraction** - Using `DataCard.extract_metadata_schema()` to understand field roles\n", - "\n", - "- **Cross-dataset registration** - Registering multiple datasets in `MetadataManager`\n", - "\n", - "- **Condition flattening** - How definitions are flattened into searchable fields with known separators\n", - "\n", - "- **Schema heterogeneity** - How different metadata schemas are aligned by role with \"unspecified\" defaults\n", - "\n", - "- **Future: SQL queries** - Once parquet loading is implemented, full cross-dataset SQL queries will work\n", - "\n", - "### Key Takeaways\n", - "\n", - "1. **Role-based alignment**: Fields are grouped by semantic role (regulator_identifier, experimental_condition), not by name\n", - "2. **Structured separators**: Media components use `:`, `@`, `;`, `|` for predictable parsing\n", - "3. **Low memory**: DuckDB temp views mean no data loading until you query\n", - "4. **Session-only default**: No caching by default (set `cache=True` if needed)\n", - "5. **Dataset + config uniqueness**: Samples identified by (dataset, config_name, sample_id) tuple" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "tfbpapi-py3.11", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.9" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/tutorials/metadata_manager_tutorial.ipynb b/docs/tutorials/metadata_manager_tutorial.ipynb new file mode 100644 index 0000000..891df65 --- /dev/null +++ b/docs/tutorials/metadata_manager_tutorial.ipynb @@ -0,0 +1,586 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# MetadataManager Tutorial: Enriching Data with Condition Metadata\n", + "\n", + "This tutorial demonstrates how to create enriched metadata views by combining:\n", + "1. Actual data fields from HuggingFace datasets (via HfQueryAPI)\n", + "2. Condition metadata from DataCard definitions\n", + "\n", + "We'll use the **BrentLab/harbison_2004** ChIP-chip dataset, which has 14 experimental conditions with detailed metadata about media composition, temperature, and other environmental factors.\n", + "\n", + "## Why Enrich Metadata?\n", + "\n", + "The raw data contains a `condition` field with values like \"YPD\", \"HEAT\", \"GAL\". But to analyze data by media type or carbon source, we need to:\n", + "- Extract nested information from condition definitions\n", + "- Create queryable columns for filtering and grouping\n", + "- Join this enriched metadata with the actual measurements\n", + "\n", + "This tutorial teaches you how to:\n", + "1. Use DataCard to explore nested condition metadata\n", + "2. Manually construct SQL CASE expressions to enrich your data\n", + "3. Create metadata views that combine data with DataCard information" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1. Setup and Imports" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [], + "source": [ + "from tfbpapi.datainfo import DataCard\n", + "from tfbpapi import HfQueryAPI\n", + "import duckdb\n", + "import pandas as pd\n", + "import json\n", + "\n", + "pd.set_option('display.max_columns', None)\n", + "pd.set_option('display.width', None)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2. Explore Condition Metadata with DataCard\n", + "\n", + "First, let's load the DataCard and explore what condition metadata is available." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dataset type: DatasetType.ANNOTATED_FEATURES\n", + "Number of features: 7\n", + "\n", + "Feature names:\n", + " - condition ({'class_label': {'names': ['YPD', 'SM', 'RAPA', 'H2O2Hi', 'H2O2Lo', 'Acid', 'Alpha', 'BUT14', 'BUT90', 'Thi-', 'GAL', 'HEAT', 'Pi-', 'RAFF']}})\n", + " - regulator_locus_tag (string)\n", + " - regulator_symbol (string)\n", + " - target_locus_tag (string)\n", + " - target_symbol (string)\n", + " - effect (float64)\n", + " - pvalue (float64)\n" + ] + } + ], + "source": [ + "# Load DataCard\n", + "card = DataCard('BrentLab/harbison_2004')\n", + "\n", + "# Get the config to understand the data structure\n", + "config = card.get_config('harbison_2004')\n", + "print(f\"Dataset type: {config.dataset_type}\")\n", + "print(f\"Number of features: {len(config.dataset_info.features)}\")\n", + "print(f\"\\nFeature names:\")\n", + "for feature in config.dataset_info.features:\n", + " print(f\" - {feature.name} ({feature.dtype})\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Extract Media Specifications Using get_field_attribute()\n", + "\n", + "The new `get_field_attribute()` method makes it easy to extract nested specifications from field definitions. Let's use it to explore the media specifications for each condition." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'DataCard' object has no attribute 'get_field_attribute'", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mAttributeError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[34]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;66;03m# Extract media specifications for all conditions\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m media_specs = \u001b[43mcard\u001b[49m\u001b[43m.\u001b[49m\u001b[43mget_field_attribute\u001b[49m(\u001b[33m'\u001b[39m\u001b[33mharbison_2004\u001b[39m\u001b[33m'\u001b[39m, \u001b[33m'\u001b[39m\u001b[33mcondition\u001b[39m\u001b[33m'\u001b[39m, \u001b[33m'\u001b[39m\u001b[33mmedia\u001b[39m\u001b[33m'\u001b[39m)\n\u001b[32m 4\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mFound media specifications for \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mlen\u001b[39m(media_specs)\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m conditions\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m\"\u001b[39m)\n\u001b[32m 6\u001b[39m \u001b[38;5;66;03m# Display them in a readable format\u001b[39;00m\n", + "\u001b[31mAttributeError\u001b[39m: 'DataCard' object has no attribute 'get_field_attribute'" + ] + } + ], + "source": [ + "# Extract media specifications for all conditions\n", + "media_specs = card.get_field_attribute('harbison_2004', 'condition', 'media')\n", + "\n", + "print(f\"Found media specifications for {len(media_specs)} conditions\\n\")\n", + "\n", + "# Display them in a readable format\n", + "for condition in sorted(media_specs.keys()):\n", + " print(f\"\\n{condition}:\")\n", + " print(json.dumps(media_specs[condition], indent=2))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Understanding the Nested Structure\n", + "\n", + "Notice that each media specification contains:\n", + "- `name`: The media name (e.g., \"YPD\", \"synthetic_complete\")\n", + "- `carbon_source`: Either a list of dicts with compound/concentration, or \"unspecified\"\n", + "- `nitrogen_source`: Either a list of dicts with compound/concentration, or \"unspecified\"\n", + "- Sometimes `additives`: Additional compounds like butanol\n", + "\n", + "To create queryable metadata columns, we need to flatten these nested structures." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3. Load Data with HfQueryAPI\n", + "\n", + "Now let's load the actual data using HfQueryAPI. We'll create a shared DuckDB connection that we can use for building our enriched metadata view." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Create shared DuckDB connection\n", + "conn = duckdb.connect(':memory:')\n", + "\n", + "# Initialize HfQueryAPI with shared connection\n", + "api = HfQueryAPI('BrentLab/harbison_2004', duckdb_conn=conn)\n", + "\n", + "# Load the harbison_2004 config data\n", + "api.load_config('harbison_2004')\n", + "\n", + "print(\"Data loaded successfully!\")\n", + "print(\"\\nAvailable views in DuckDB:\")\n", + "views = conn.execute(\"SHOW TABLES\").fetchall()\n", + "for view in views:\n", + " print(f\" - {view[0]}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Examine the Base Data\n", + "\n", + "Let's look at the structure of the base data to see what fields are available." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Query the base view\n", + "base_df = conn.execute(\"\"\"\n", + " SELECT *\n", + " FROM harbison_2004_train\n", + " LIMIT 5\n", + "\"\"\").df()\n", + "\n", + "print(f\"Base data shape: {base_df.shape}\")\n", + "print(f\"\\nColumn names: {list(base_df.columns)}\")\n", + "print(\"\\nSample rows:\")\n", + "display(base_df)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Check unique conditions in the data\n", + "conditions_df = conn.execute(\"\"\"\n", + " SELECT DISTINCT condition\n", + " FROM harbison_2004_train\n", + " ORDER BY condition\n", + "\"\"\").df()\n", + "\n", + "print(f\"Unique conditions in data: {len(conditions_df)}\")\n", + "print(conditions_df['condition'].tolist())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4. Flatten Media Specifications\n", + "\n", + "Before we can build SQL CASE expressions, we need to flatten the nested media specifications into simple strings. We'll create helper functions to extract:\n", + "- Media name\n", + "- Carbon source (as comma-separated compound names)\n", + "- Nitrogen source (as comma-separated compound names)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def flatten_carbon_source(media_spec):\n", + " \"\"\"Extract carbon source as comma-separated string.\"\"\"\n", + " if media_spec == 'unspecified':\n", + " return 'unspecified'\n", + " \n", + " carbon = media_spec.get('carbon_source', 'unspecified')\n", + " if carbon == 'unspecified':\n", + " return 'unspecified'\n", + " \n", + " if isinstance(carbon, list):\n", + " compounds = [c['compound'] for c in carbon]\n", + " return ', '.join(compounds)\n", + " \n", + " return str(carbon)\n", + "\n", + "def flatten_nitrogen_source(media_spec):\n", + " \"\"\"Extract nitrogen source as comma-separated string.\"\"\"\n", + " if media_spec == 'unspecified':\n", + " return 'unspecified'\n", + " \n", + " nitrogen = media_spec.get('nitrogen_source', 'unspecified')\n", + " if nitrogen == 'unspecified':\n", + " return 'unspecified'\n", + " \n", + " if isinstance(nitrogen, list):\n", + " compounds = [n['compound'] for n in nitrogen]\n", + " return ', '.join(compounds)\n", + " \n", + " return str(nitrogen)\n", + "\n", + "# Create flattened mapping for all conditions\n", + "flattened_media = {}\n", + "for condition, media_spec in media_specs.items():\n", + " flattened_media[condition] = {\n", + " 'media_name': media_spec.get('name', 'unspecified') if media_spec != 'unspecified' else 'unspecified',\n", + " 'carbon_source': flatten_carbon_source(media_spec),\n", + " 'nitrogen_source': flatten_nitrogen_source(media_spec)\n", + " }\n", + "\n", + "# Display flattened mapping\n", + "print(\"Flattened media specifications:\\n\")\n", + "for condition in sorted(flattened_media.keys()):\n", + " print(f\"{condition}:\")\n", + " for key, value in flattened_media[condition].items():\n", + " print(f\" {key}: {value}\")\n", + " print()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 5. Build SQL CASE Expressions Manually\n", + "\n", + "Now we'll construct SQL CASE expressions to map each condition value to its media specifications. This is the core of creating enriched metadata." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Build CASE expression for media_name\n", + "media_name_cases = []\n", + "for condition, specs in sorted(flattened_media.items()):\n", + " media_name_cases.append(f\" WHEN '{condition}' THEN '{specs['media_name']}'\")\n", + "media_name_sql = \"\\n\".join(media_name_cases)\n", + "\n", + "# Build CASE expression for carbon_source\n", + "carbon_source_cases = []\n", + "for condition, specs in sorted(flattened_media.items()):\n", + " carbon_source_cases.append(f\" WHEN '{condition}' THEN '{specs['carbon_source']}'\")\n", + "carbon_source_sql = \"\\n\".join(carbon_source_cases)\n", + "\n", + "# Build CASE expression for nitrogen_source\n", + "nitrogen_source_cases = []\n", + "for condition, specs in sorted(flattened_media.items()):\n", + " nitrogen_source_cases.append(f\" WHEN '{condition}' THEN '{specs['nitrogen_source']}'\")\n", + "nitrogen_source_sql = \"\\n\".join(nitrogen_source_cases)\n", + "\n", + "# Construct the complete CREATE VIEW SQL\n", + "create_view_sql = f\"\"\"\n", + "CREATE OR REPLACE VIEW harbison_2004_enriched_metadata AS\n", + "SELECT\n", + " sample_id,\n", + " regulator_locus_tag,\n", + " regulator_symbol,\n", + " condition,\n", + " CASE condition\n", + "{media_name_sql}\n", + " END AS media_name,\n", + " CASE condition\n", + "{carbon_source_sql}\n", + " END AS carbon_source,\n", + " CASE condition\n", + "{nitrogen_source_sql}\n", + " END AS nitrogen_source\n", + "FROM harbison_2004_train\n", + "\"\"\"\n", + "\n", + "print(\"Generated SQL:\")\n", + "print(\"=\"*80)\n", + "print(create_view_sql)\n", + "print(\"=\"*80)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 6. Execute SQL and Create Enriched Metadata View\n", + "\n", + "Now let's execute the SQL to create our enriched metadata view." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Execute the CREATE VIEW statement\n", + "conn.execute(create_view_sql)\n", + "\n", + "print(\"Enriched metadata view created successfully!\")\n", + "print(\"\\nAvailable views:\")\n", + "views = conn.execute(\"SHOW TABLES\").fetchall()\n", + "for view in views:\n", + " print(f\" - {view[0]}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Query the Enriched Metadata View\n", + "\n", + "Let's examine the enriched metadata to see the results." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Query enriched metadata\n", + "enriched_df = conn.execute(\"\"\"\n", + " SELECT *\n", + " FROM harbison_2004_enriched_metadata\n", + " LIMIT 10\n", + "\"\"\").df()\n", + "\n", + "print(f\"Enriched metadata shape: {enriched_df.shape}\")\n", + "print(\"\\nSample rows:\")\n", + "display(enriched_df)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 7. Practical Examples: Querying with Enriched Metadata\n", + "\n", + "Now that we have enriched metadata, we can perform analyses that wouldn't be possible with just the raw condition values." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example 1: Find All Experiments with D-glucose as Carbon Source" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "glucose_df = conn.execute(\"\"\"\n", + " SELECT DISTINCT\n", + " condition,\n", + " media_name,\n", + " carbon_source,\n", + " nitrogen_source,\n", + " COUNT(*) as sample_count\n", + " FROM harbison_2004_enriched_metadata\n", + " WHERE carbon_source LIKE '%D-glucose%'\n", + " GROUP BY condition, media_name, carbon_source, nitrogen_source\n", + " ORDER BY condition\n", + "\"\"\").df()\n", + "\n", + "print(f\"Found {len(glucose_df)} condition(s) with D-glucose\")\n", + "display(glucose_df)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example 2: Compare Rich Media vs Synthetic Media Experiments" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "media_comparison_df = conn.execute(\"\"\"\n", + " SELECT\n", + " CASE\n", + " WHEN media_name LIKE '%YPD%' OR media_name LIKE '%yeast_extract%'\n", + " THEN 'Rich Media'\n", + " WHEN media_name LIKE '%synthetic%'\n", + " THEN 'Synthetic Media'\n", + " ELSE 'Other'\n", + " END AS media_type,\n", + " COUNT(DISTINCT condition) as num_conditions,\n", + " COUNT(DISTINCT sample_id) as num_samples,\n", + " COUNT(DISTINCT regulator_symbol) as num_regulators\n", + " FROM harbison_2004_enriched_metadata\n", + " GROUP BY media_type\n", + " ORDER BY media_type\n", + "\"\"\").df()\n", + "\n", + "print(\"Media type comparison:\")\n", + "display(media_comparison_df)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example 3: Analyze Carbon Source Diversity" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "carbon_diversity_df = conn.execute(\"\"\"\n", + " SELECT\n", + " carbon_source,\n", + " COUNT(DISTINCT condition) as num_conditions,\n", + " COUNT(DISTINCT sample_id) as num_samples,\n", + " STRING_AGG(DISTINCT condition, ', ' ORDER BY condition) as conditions\n", + " FROM harbison_2004_enriched_metadata\n", + " GROUP BY carbon_source\n", + " ORDER BY num_conditions DESC\n", + "\"\"\").df()\n", + "\n", + "print(\"Carbon source diversity:\")\n", + "display(carbon_diversity_df)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example 4: Find Regulators Tested Across Multiple Carbon Sources" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "regulator_diversity_df = conn.execute(\"\"\"\n", + " SELECT\n", + " regulator_symbol,\n", + " regulator_locus_tag,\n", + " COUNT(DISTINCT carbon_source) as num_carbon_sources,\n", + " COUNT(DISTINCT condition) as num_conditions,\n", + " STRING_AGG(DISTINCT carbon_source, ', ') as carbon_sources_tested\n", + " FROM harbison_2004_enriched_metadata\n", + " GROUP BY regulator_symbol, regulator_locus_tag\n", + " HAVING COUNT(DISTINCT carbon_source) > 1\n", + " ORDER BY num_carbon_sources DESC\n", + " LIMIT 20\n", + "\"\"\").df()\n", + "\n", + "print(f\"Found {len(regulator_diversity_df)} regulators tested across multiple carbon sources\")\n", + "print(\"\\nTop 20 regulators by carbon source diversity:\")\n", + "display(regulator_diversity_df)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 8. Summary and Key Takeaways\n", + "\n", + "In this tutorial, we learned how to:\n", + "\n", + "1. **Explore nested metadata** using DataCard's `get_field_attribute()` method\n", + "2. **Flatten nested structures** into queryable strings\n", + "3. **Build SQL CASE expressions** manually to map condition values to metadata attributes\n", + "4. **Create enriched metadata views** that combine data fields with DataCard information\n", + "5. **Query by metadata attributes** that aren't present in the raw data\n", + "\n", + "### When to Use This Approach\n", + "\n", + "Use this pattern when:\n", + "- You need to analyze data by attributes that are defined in the DataCard but not in the data itself\n", + "- You want full control over the SQL being generated\n", + "- You're working with a single dataset and need custom metadata columns\n", + "\n", + "### Next Steps\n", + "\n", + "- Explore other attributes like `temperature_celsius` or `cultivation_method`\n", + "- Combine multiple attributes in your enriched views\n", + "- Use this pattern with other datasets in the BrentLab collection\n", + "- Build reusable helper functions for your specific analysis needs" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.0" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/tutorials/sample_manager_tutorial.ipynb b/docs/tutorials/sample_manager_tutorial.ipynb new file mode 100644 index 0000000..f049afa --- /dev/null +++ b/docs/tutorials/sample_manager_tutorial.ipynb @@ -0,0 +1,603 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# SampleManager Tutorial: Node-Based Filtering Across Heterogeneous Datasets\n", + "\n", + "This tutorial introduces `SampleManager`, a flexible node-based system for filtering samples across multiple datasets with varying experimental condition structures.\n", + "\n", + "## Why SampleManager?\n", + "\n", + "Traditional table-based approaches struggle with heterogeneous metadata:\n", + "- Different datasets structure conditions differently (e.g., `media.carbon_source` vs `environmental_conditions.media.carbon_source`)\n", + "- Missing fields and optional properties vary by dataset\n", + "- Multi-level hierarchies (repo/config/field) require complex joins\n", + "\n", + "**SampleManager** solves this by:\n", + "- Representing each sample as a node with dynamically discovered properties\n", + "- Supporting flexible MongoDB-style queries across heterogeneous structures\n", + "- Enabling cross-dataset filtering and set operations\n", + "- Respecting hierarchical overrides (field > config > repo)\n", + "\n", + "## Key Concepts\n", + "\n", + "- **SampleNode**: A sample with flattened properties (experimental conditions + metadata)\n", + "- **ActiveSet**: A filtered collection of sample IDs supporting set operations\n", + "- **Query Language**: MongoDB-style filters with operators like `$contains`, `$gte`, `$or`\n", + "- **Property Discovery**: No hardcoded schemas - properties are discovered from the data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1. Setup and Basic Usage" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from tfbpapi.datainfo.sample_manager import SampleManager, SampleNode\n", + "from tfbpapi.datainfo import DataCard\n", + "import json\n", + "\n", + "# Initialize manager\n", + "manager = SampleManager()\n", + "print(manager)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2. Understanding Property Discovery\n", + "\n", + "Let's explore how SampleManager discovers properties from a DataCard without hardcoded schemas." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Load a DataCard to see its experimental conditions structure\n", + "card = DataCard('BrentLab/harbison_2004')\n", + "\n", + "# Get repo-level conditions\n", + "repo_conditions = card.get_experimental_conditions('harbison_2004')\n", + "print(\"Repo-level conditions:\")\n", + "print(json.dumps(repo_conditions, indent=2, default=str))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Get field-level condition definitions\n", + "field_defs = card.get_field_definitions('harbison_2004', 'condition')\n", + "\n", + "# Show one condition definition\n", + "print(\"\\nField-level definition for 'YPD':\")\n", + "print(json.dumps(field_defs.get('YPD', {}), indent=2, default=str))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### How Properties Are Flattened\n", + "\n", + "SampleManager recursively flattens nested structures using dot notation:\n", + "\n", + "```python\n", + "# Original nested structure:\n", + "{\n", + " \"environmental_conditions\": {\n", + " \"media\": {\n", + " \"name\": \"YPD\",\n", + " \"carbon_source\": [{\"compound\": \"D-glucose\", \"concentration_percent\": 2}]\n", + " },\n", + " \"temperature_celsius\": 30\n", + " }\n", + "}\n", + "\n", + "# Becomes flattened properties:\n", + "{\n", + " \"environmental_conditions.media.name\": \"YPD\",\n", + " \"environmental_conditions.media.carbon_source\": \"D-glucose\", # Simple string\n", + " \"_environmental_conditions.media.carbon_source_structured\": [{...}], # Full structure\n", + " \"environmental_conditions.temperature_celsius\": 30\n", + "}\n", + "```\n", + "\n", + "**Key Points:**\n", + "- Nested dicts become dot-notation keys\n", + "- Lists of dicts (compounds) get both simple and `_structured` versions\n", + "- No hardcoded field names - discovers whatever exists\n", + "- Different datasets can have different structures" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3. Creating Sample Nodes Manually\n", + "\n", + "Before we load from DataCard, let's manually create nodes to understand the structure." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from tfbpapi.datainfo.sample_manager import SampleNode, ConditionFlattener\n", + "\n", + "# Simulate experimental conditions from different levels\n", + "repo_conditions = {\n", + " \"environmental_conditions\": {\n", + " \"temperature_celsius\": 30,\n", + " \"cultivation_method\": \"liquid_culture\"\n", + " },\n", + " \"strain_background\": \"BY4741\"\n", + "}\n", + "\n", + "field_conditions = {\n", + " \"environmental_conditions\": {\n", + " \"media\": {\n", + " \"name\": \"YPD\",\n", + " \"carbon_source\": [\n", + " {\"compound\": \"D-glucose\", \"concentration_percent\": 2}\n", + " ],\n", + " \"nitrogen_source\": [\n", + " {\"compound\": \"yeast_extract\", \"concentration_percent\": 1},\n", + " {\"compound\": \"peptone\", \"concentration_percent\": 2}\n", + " ]\n", + " }\n", + " }\n", + "}\n", + "\n", + "# Flatten conditions\n", + "properties, sources = ConditionFlattener.flatten_conditions(\n", + " repo_conditions, None, field_conditions\n", + ")\n", + "\n", + "print(\"Flattened properties:\")\n", + "for key, value in sorted(properties.items()):\n", + " source = sources.get(key, 'unknown')\n", + " # Skip structured versions for cleaner output\n", + " if not key.startswith('_'):\n", + " print(f\" {key}: {value} (from {source})\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Create a sample node\n", + "node = SampleNode(\n", + " sample_id=\"sample_001\",\n", + " repo_id=\"BrentLab/harbison_2004\",\n", + " config_name=\"harbison_2004\",\n", + " properties=properties,\n", + " property_sources=sources\n", + ")\n", + "\n", + "print(f\"Node: {node}\")\n", + "print(f\"Global ID: {node.global_id()}\")\n", + "print(f\"\\nSample properties:\")\n", + "print(f\" Temperature: {node.get_property('environmental_conditions.temperature_celsius')}\")\n", + "print(f\" Carbon source: {node.get_property('environmental_conditions.media.carbon_source')}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4. Filtering with the Query Language\n", + "\n", + "SampleManager uses MongoDB-style queries for flexible filtering." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from tfbpapi.datainfo.sample_manager import SampleFilter\n", + "\n", + "# Simple equality\n", + "query1 = {\"environmental_conditions.temperature_celsius\": 30}\n", + "print(f\"Query 1 matches: {SampleFilter.matches(node, query1)}\")\n", + "\n", + "# Contains check (case-insensitive)\n", + "query2 = {\"environmental_conditions.media.carbon_source\": {\"$contains\": \"glucose\"}}\n", + "print(f\"Query 2 matches: {SampleFilter.matches(node, query2)}\")\n", + "\n", + "# Numeric comparison\n", + "query3 = {\"environmental_conditions.temperature_celsius\": {\"$gte\": 25, \"$lte\": 35}}\n", + "print(f\"Query 3 matches: {SampleFilter.matches(node, query3)}\")\n", + "\n", + "# Logical OR\n", + "query4 = {\n", + " \"$or\": [\n", + " {\"environmental_conditions.media.carbon_source\": {\"$contains\": \"galactose\"}},\n", + " {\"environmental_conditions.media.carbon_source\": {\"$contains\": \"glucose\"}}\n", + " ]\n", + "}\n", + "print(f\"Query 4 matches: {SampleFilter.matches(node, query4)}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Available Query Operators\n", + "\n", + "| Operator | Description | Example |\n", + "|----------|-------------|----------|\n", + "| `$eq` | Equal (default) | `{\"temp\": 30}` or `{\"temp\": {\"$eq\": 30}}` |\n", + "| `$ne` | Not equal | `{\"temp\": {\"$ne\": 30}}` |\n", + "| `$gt` | Greater than | `{\"temp\": {\"$gt\": 30}}` |\n", + "| `$gte` | Greater than or equal | `{\"temp\": {\"$gte\": 30}}` |\n", + "| `$lt` | Less than | `{\"temp\": {\"$lt\": 30}}` |\n", + "| `$lte` | Less than or equal | `{\"temp\": {\"$lte\": 30}}` |\n", + "| `$in` | In list | `{\"strain\": {\"$in\": [\"BY4741\", \"W303\"]}}` |\n", + "| `$nin` | Not in list | `{\"strain\": {\"$nin\": [\"BY4741\"]}}` |\n", + "| `$contains` | String contains (case-insensitive) | `{\"carbon_source\": {\"$contains\": \"glucose\"}}` |\n", + "| `$exists` | Field exists | `{\"temperature\": {\"$exists\": true}}` |\n", + "| `$and` | Logical AND | `{\"$and\": [{...}, {...}]}` |\n", + "| `$or` | Logical OR | `{\"$or\": [{...}, {...}]}` |" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 5. Working with ActiveSets\n", + "\n", + "ActiveSets represent filtered collections of samples and support set operations." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from tfbpapi.datainfo.sample_manager import ActiveSet\n", + "\n", + "# Create some example active sets\n", + "set1 = ActiveSet(\n", + " sample_ids={\"BrentLab:harbison_2004:sample_001\", \"BrentLab:harbison_2004:sample_002\", \"BrentLab:harbison_2004:sample_003\"},\n", + " name=\"glucose_samples\",\n", + " description=\"Samples grown on glucose\"\n", + ")\n", + "\n", + "set2 = ActiveSet(\n", + " sample_ids={\"BrentLab:harbison_2004:sample_002\", \"BrentLab:harbison_2004:sample_003\", \"BrentLab:harbison_2004:sample_004\"},\n", + " name=\"heat_stress_samples\",\n", + " description=\"Samples with heat stress\"\n", + ")\n", + "\n", + "print(f\"Set 1: {set1}\")\n", + "print(f\"Set 2: {set2}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Set operations\n", + "\n", + "# Union - samples in either set\n", + "union = set1.union(set2, name=\"glucose_or_heat\")\n", + "print(f\"Union: {union}\")\n", + "print(f\" Sample IDs: {union.to_sample_ids()}\")\n", + "\n", + "# Intersection - samples in both sets\n", + "intersection = set1.intersection(set2, name=\"glucose_and_heat\")\n", + "print(f\"\\nIntersection: {intersection}\")\n", + "print(f\" Sample IDs: {intersection.to_sample_ids()}\")\n", + "\n", + "# Difference - samples in set1 but not set2\n", + "difference = set1.difference(set2, name=\"glucose_no_heat\")\n", + "print(f\"\\nDifference: {difference}\")\n", + "print(f\" Sample IDs: {difference.to_sample_ids()}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 6. Real-World Example: Exploring Harbison 2004\n", + "\n", + "Now let's work with real data. Note that we're manually creating nodes here since the `load_from_datacard` method needs to be implemented. This demonstrates the intended workflow." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# For this tutorial, we'll manually create nodes from DataCard information\n", + "# In production, you would use: manager.load_from_datacard(\"BrentLab/harbison_2004\", \"harbison_2004\")\n", + "\n", + "from tfbpapi.datainfo import DataCard\n", + "\n", + "card = DataCard('BrentLab/harbison_2004')\n", + "\n", + "# Get repo-level and field-level conditions\n", + "repo_conds = card.get_experimental_conditions('harbison_2004')\n", + "field_defs = card.get_field_definitions('harbison_2004', 'condition')\n", + "\n", + "print(f\"Found {len(field_defs)} condition definitions\")\n", + "print(f\"Conditions: {sorted(field_defs.keys())}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Manually create nodes for each condition (simulating samples)\n", + "# This demonstrates what load_from_datacard will do automatically\n", + "\n", + "demo_manager = SampleManager()\n", + "\n", + "for condition_name, condition_def in field_defs.items():\n", + " # Flatten conditions\n", + " properties, sources = ConditionFlattener.flatten_conditions(\n", + " repo_conds, None, condition_def\n", + " )\n", + " \n", + " # Create node (using condition name as sample_id for demo)\n", + " node = SampleNode(\n", + " sample_id=condition_name,\n", + " repo_id=\"BrentLab/harbison_2004\",\n", + " config_name=\"harbison_2004\",\n", + " properties=properties,\n", + " property_sources=sources\n", + " )\n", + " \n", + " # Add to manager's collection\n", + " demo_manager._collection.add_node(node)\n", + "\n", + "print(f\"\\nLoaded {demo_manager._collection.count_total_nodes()} sample nodes\")\n", + "print(demo_manager)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Explore what properties are available\n", + "sample_node = demo_manager.get_sample(\"BrentLab/harbison_2004:harbison_2004:YPD\")\n", + "\n", + "print(\"Properties available in YPD sample:\")\n", + "for key in sorted(sample_node.properties.keys()):\n", + " if not key.startswith('_'): # Skip structured versions\n", + " print(f\" {key}: {sample_node.properties[key]}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 7. Cross-Condition Filtering\n", + "\n", + "Now we can filter samples based on their experimental conditions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Find all samples with D-glucose as carbon source\n", + "glucose_samples = demo_manager.filter_all({\n", + " \"environmental_conditions.media.carbon_source\": {\"$contains\": \"D-glucose\"}\n", + "}, name=\"glucose_conditions\")\n", + "\n", + "print(f\"Found {len(glucose_samples)} conditions with D-glucose\")\n", + "print(f\"Conditions: {[sid.split(':')[-1] for sid in glucose_samples.to_sample_ids()]}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Find samples with alternative carbon sources\n", + "alt_carbon = demo_manager.filter_all({\n", + " \"$or\": [\n", + " {\"environmental_conditions.media.carbon_source\": {\"$contains\": \"galactose\"}},\n", + " {\"environmental_conditions.media.carbon_source\": {\"$contains\": \"raffinose\"}}\n", + " ]\n", + "}, name=\"alternative_carbon\")\n", + "\n", + "print(f\"\\nFound {len(alt_carbon)} conditions with alternative carbon sources\")\n", + "print(f\"Conditions: {[sid.split(':')[-1] for sid in alt_carbon.to_sample_ids()]}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Find samples with media additives (like butanol)\n", + "with_additives = demo_manager.filter_all({\n", + " \"environmental_conditions.media.additives\": {\"$contains\": \"butanol\"}\n", + "}, name=\"additive_conditions\")\n", + "\n", + "print(f\"\\nFound {len(with_additives)} conditions with additives\")\n", + "print(f\"Conditions: {[sid.split(':')[-1] for sid in with_additives.to_sample_ids()]}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 8. Property Distribution Analysis" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Analyze carbon source distribution\n", + "carbon_dist = demo_manager.get_property_distribution(\n", + " \"environmental_conditions.media.carbon_source\"\n", + ")\n", + "\n", + "print(\"Carbon source distribution:\")\n", + "for value, count in sorted(carbon_dist.items(), key=lambda x: x[1], reverse=True):\n", + " print(f\" {value}: {count} samples\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Analyze media names\n", + "media_dist = demo_manager.get_property_distribution(\n", + " \"environmental_conditions.media.name\"\n", + ")\n", + "\n", + "print(\"\\nMedia type distribution:\")\n", + "for value, count in sorted(media_dist.items(), key=lambda x: x[1], reverse=True):\n", + " print(f\" {value}: {count} samples\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 9. Summary Information" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Get summary of loaded data\n", + "summary = demo_manager.get_summary()\n", + "print(\"\\nSummary of loaded datasets:\")\n", + "print(summary)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 10. Key Takeaways\n", + "\n", + "### Advantages of Node-Based Approach\n", + "\n", + "1. **Flexible Schema**: No hardcoded field names - discovers whatever exists in the data\n", + "2. **Heterogeneity Handling**: Different datasets can have different structures (e.g., `media.carbon_source` vs `environmental_conditions.media.carbon_source`)\n", + "3. **Cross-Dataset Queries**: Filter samples across multiple datasets with varying structures\n", + "4. **Set Operations**: Build complex filters incrementally using union/intersection/difference\n", + "5. **Property Discovery**: Automatically flattens nested structures using dot notation\n", + "6. **Dual Representation**: Compound lists get both simple strings and structured versions\n", + "\n", + "### How to Query Different Property Paths\n", + "\n", + "Since different datasets structure their conditions differently, you need to use the actual property path:\n", + "\n", + "```python\n", + "# Dataset A (harbison_2004): media nested under environmental_conditions\n", + "manager.filter_all({\"environmental_conditions.media.carbon_source\": {\"$contains\": \"glucose\"}})\n", + "\n", + "# Dataset B (hypothetical): media at top level \n", + "manager.filter_all({\"media.carbon_source\": {\"$contains\": \"glucose\"}})\n", + "\n", + "# To query across both, use $or:\n", + "manager.filter_all({\n", + " \"$or\": [\n", + " {\"environmental_conditions.media.carbon_source\": {\"$contains\": \"glucose\"}},\n", + " {\"media.carbon_source\": {\"$contains\": \"glucose\"}}\n", + " ]\n", + "})\n", + "```\n", + "\n", + "### Future: External Property Mappings\n", + "\n", + "For cross-dataset harmonization, you can provide external YAML mappings:\n", + "\n", + "```yaml\n", + "# property_mappings.yaml\n", + "carbon_source:\n", + " paths:\n", + " - environmental_conditions.media.carbon_source\n", + " - media.carbon_source\n", + " value_aliases:\n", + " D-glucose: [glucose, dextrose, D-dextrose]\n", + "```\n", + "\n", + "This would enable canonical queries like `{\"carbon_source\": {\"$contains\": \"glucose\"}}` to work across all datasets." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Next Steps\n", + "\n", + "- Implement `load_from_datacard()` to automatically load samples from HuggingFace\n", + "- Implement `load_from_duckdb()` for integration with HfQueryAPI\n", + "- Implement `export_to_duckdb()` to convert ActiveSets back to SQL views\n", + "- Add external property mapping support via YAML configuration\n", + "- Build UI for interactive filtering and set operations" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.0" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/tfbpapi/datainfo/datacard.py b/tfbpapi/datainfo/datacard.py index 9b60c22..d11e378 100644 --- a/tfbpapi/datainfo/datacard.py +++ b/tfbpapi/datainfo/datacard.py @@ -1,4 +1,20 @@ -"""DataCard class for easy exploration of HuggingFace dataset metadata.""" +""" +DataCard class for parsing and exploring HuggingFace dataset metadata. + +This module provides the DataCard class for parsing HuggingFace dataset cards +into structured Python objects that can be easily explored. The focus is on +enabling users to drill down into the YAML structure to understand: + +- Dataset configurations and their types +- Feature definitions and roles +- Experimental conditions at all hierarchy levels (top/config/field) +- Field-level condition definitions +- Metadata relationships + +Users can then use this information to plan metadata table structures and +data loading strategies. + +""" import logging from typing import Any @@ -12,17 +28,48 @@ DatasetConfig, DatasetType, ExtractedMetadata, - FieldRole, + FeatureInfo, MetadataRelationship, ) class DataCard: """ - Easy-to-use interface for exploring HuggingFace dataset metadata. - - Provides methods to discover and explore dataset contents, configurations, and - metadata without loading the actual genomic data. + Parser and explorer for HuggingFace dataset metadata. + + DataCard parses HuggingFace dataset cards into flexible Python objects, + enabling users to drill down into the YAML structure to understand dataset + organization, experimental conditions, and metadata relationships. + + The parsed structure uses Pydantic models with `extra="allow"` to accept + arbitrary fields (like experimental_conditions) without requiring code + changes. This makes the system flexible enough to handle domain-specific + metadata variations. + + Key capabilities: + - Parse dataset card YAML into structured objects + - Navigate experimental conditions at 3 levels (top/config/field) + - Explore field definitions and roles + - Extract metadata schema for table design + - Discover metadata relationships + + Example (new API): + >>> card = DataCard("BrentLab/harbison_2004") + >>> # Use context manager for config exploration + >>> with card.config("harbison_2004") as cfg: + ... # Get all experimental conditions + ... conds = cfg.experimental_conditions() + ... # Get condition fields with definitions + ... fields = cfg.condition_fields() + ... # Drill down into specific field + ... for name, info in fields.items(): + ... for value, definition in info['definitions'].items(): + ... print(f"{name}={value}: {definition}") + + Example (legacy API still supported): + >>> card = DataCard("BrentLab/harbison_2004") + >>> conditions = card.get_experimental_conditions("harbison_2004") + >>> defs = card.get_field_definitions("harbison_2004", "condition") """ @@ -120,25 +167,97 @@ def get_configs_by_type( dataset_type = DatasetType(dataset_type) return self.dataset_card.get_configs_by_type(dataset_type) - def get_regulators(self, config_name: str | None = None) -> set[str]: + def get_card_metadata(self) -> dict[str, Any]: + """ + Get all top-level metadata fields from the dataset card. + + Returns all fields stored in model_extra (e.g., license, tags, pretty_name, + etc.) as a dict. This gives direct access to the raw YAML structure at the card + level. + + :return: Dict of all extra metadata fields + + """ + if self.dataset_card.model_extra: + return dict(self.dataset_card.model_extra) + return {} + + def get_config_metadata(self, config_name: str) -> dict[str, Any]: + """ + Get all extra metadata fields from a specific config. + + Returns all fields stored in the config's model_extra (e.g., + experimental_conditions, custom fields, etc.) as a dict. + + :param config_name: Configuration name + :return: Dict of all extra metadata fields for this config + :raises DataCardError: If config not found + """ - Get all regulators mentioned in the dataset. + config = self.get_config(config_name) + if not config: + raise DataCardError(f"Configuration '{config_name}' not found") - :param config_name: Optional specific config to search, otherwise searches all - :return: Set of regulator identifiers found + if config.model_extra: + return dict(config.model_extra) + return {} + def get_features(self, config_name: str) -> list[FeatureInfo]: """ - raise NotImplementedError("Method not yet implemented") + Get all feature definitions for a configuration. + + :param config_name: Configuration name + :return: List of FeatureInfo objects + :raises DataCardError: If config not found - def get_experimental_conditions(self, config_name: str | None = None) -> set[str]: """ - Get all experimental conditions mentioned in the dataset. + config = self.get_config(config_name) + if not config: + raise DataCardError(f"Configuration '{config_name}' not found") - :param config_name: Optional specific config to search, otherwise searches all - :return: Set of experimental conditions found + return config.dataset_info.features + def get_features_by_role( + self, config_name: str, role: str | None = None + ) -> dict[str, list[str]]: """ - raise NotImplementedError("Method not yet implemented") + Get features grouped by role. + + If role is specified, returns only features with that role. + If role is None, returns all features grouped by role. + + :param config_name: Configuration name + :param role: Optional specific role to filter by + :return: Dict mapping role -> list of field names + :raises DataCardError: If config not found + + Example: + >>> # Get all features by role + >>> by_role = card.get_features_by_role("config_name") + >>> # {'regulator_identifier': ['regulator_locus_tag'], + >>> # 'target_identifier': ['target_locus_tag'], ...} + >>> + >>> # Get only experimental condition features + >>> cond_fields = card.get_features_by_role("config_name", + ... "experimental_condition") + >>> # {'experimental_condition': ['condition', 'treatment']} + + """ + features = self.get_features(config_name) + + # Group by role + by_role: dict[str, list[str]] = {} + for feature in features: + feature_role = feature.role if feature.role else "no_role" + if feature_role not in by_role: + by_role[feature_role] = [] + by_role[feature_role].append(feature.name) + + # Filter by specific role if requested + if role is not None: + return {role: by_role.get(role, [])} + + return by_role def get_field_values(self, config_name: str, field_name: str) -> set[str]: """ @@ -164,7 +283,7 @@ def get_field_values(self, config_name: str, field_name: str) -> set[str]: return self._extract_field_values(config, field_name) def _extract_field_values(self, config: DatasetConfig, field_name: str) -> set[str]: - """Extract unique values for a field from various sources.""" + """Extract unique values for a field from partition structure only.""" values = set() # Check cache first @@ -185,19 +304,27 @@ def _extract_field_values(self, config: DatasetConfig, field_name: str) -> set[s partition_values = self._extract_partition_values(config, field_name) if partition_values: values.update(partition_values) + # Cache the result + self._metadata_cache[cache_key] = [ + ExtractedMetadata( + config_name=config.config_name, + field_name=field_name, + values=values, + extraction_method="partition_structure", + ) + ] + return values - # For embedded metadata fields, we would need to query the actual data - # This is a placeholder - in practice, you might use the - # HF datasets server API - if config.metadata_fields and field_name in config.metadata_fields: - # Placeholder for actual data extraction - self.logger.debug( - "Would extract embedded metadata for " - f"{field_name} in {config.config_name}" - ) + # For non-partitioned fields, we can no longer query parquet files + self.logger.debug( + f"Cannot extract values for {field_name} in {config.config_name}: " + "field is not partitioned and parquet querying is not supported" + ) except Exception as e: self.logger.warning(f"Failed to extract values for {field_name}: {e}") + # Return empty set on failure instead of raising + # This maintains backward compatibility return values @@ -296,8 +423,21 @@ def get_repository_info(self) -> dict[str, Any]: "has_default_config": self.dataset_card.get_default_config() is not None, } - def explore_config(self, config_name: str) -> dict[str, Any]: - """Get detailed information about a specific configuration.""" + def explore_config( + self, config_name: str, include_extra: bool = True + ) -> dict[str, Any]: + """ + Get detailed information about a specific configuration. + + Returns a comprehensive dict with config structure including features, data + files, partitioning, and optionally all extra metadata fields. + + :param config_name: Configuration name + :param include_extra: If True, include all fields from model_extra + :return: Dict with config details + :raises DataCardError: If config not found + + """ config = self.get_config(config_name) if not config: raise DataCardError(f"Configuration '{config_name}' not found") @@ -309,7 +449,13 @@ def explore_config(self, config_name: str) -> dict[str, Any]: "is_default": config.default, "num_features": len(config.dataset_info.features), "features": [ - {"name": f.name, "dtype": f.dtype, "description": f.description} + { + "name": f.name, + "dtype": f.dtype, + "description": f.description, + "role": f.role, + "has_definitions": f.definitions is not None, + } for f in config.dataset_info.features ], "data_files": [ @@ -332,23 +478,53 @@ def explore_config(self, config_name: str) -> dict[str, Any]: if config.metadata_fields: info["metadata_fields"] = config.metadata_fields + # Add all extra fields from model_extra + if include_extra and config.model_extra: + info["extra_fields"] = dict(config.model_extra) + return info def extract_metadata_schema(self, config_name: str) -> dict[str, Any]: """ - Extract metadata schema for a configuration. + Extract complete metadata schema for planning metadata table structure. + + This is the primary method for understanding what metadata is available and + how to structure it into a metadata table. It consolidates information from + all sources: - Returns structured schema identifying metadata fields by their role, - suitable for metadata extraction by MetadataManager. Includes - experimental conditions at all three hierarchy levels: - - Top-level (repo-wide, applies to all configs/samples) - - Config-level (applies to this config's samples) - - Field-level (varies per sample, from field definitions) + - **Field roles**: Which fields are regulators, targets, conditions, etc. + - **Top-level conditions**: Repo-wide conditions (constant for all samples) + - **Config-level conditions**: Config-specific conditions (constant for this config) + - **Field-level definitions**: Per-sample condition definitions + + The returned schema provides all the information needed to: + 1. Identify sample identifier fields (regulator_identifier, etc.) + 2. Determine which conditions are constant vs. variable + 3. Access condition definitions for creating flattened columns + 4. Plan metadata table structure :param config_name: Configuration name to extract schema for - :return: Dict with field lists grouped by role and condition definitions + :return: Dict with comprehensive schema including: + - regulator_fields: List of regulator identifier field names + - target_fields: List of target identifier field names + - condition_fields: List of experimental_condition field names + - condition_definitions: Dict mapping field -> value -> definition + - top_level_conditions: Dict of repo-wide conditions + - config_level_conditions: Dict of config-specific conditions :raises DataCardError: If configuration not found + Example: + >>> schema = card.extract_metadata_schema('harbison_2004') + >>> # Identify identifier fields + >>> print(f"Regulator fields: {schema['regulator_fields']}") + >>> # Check for constant conditions + >>> if schema['top_level_conditions']: + ... print("Has repo-wide constant conditions") + >>> # Get field-level definitions for metadata table + >>> for field in schema['condition_fields']: + ... defs = schema['condition_definitions'][field] + ... print(f"{field} has {len(defs)} levels") + """ config = self.get_config(config_name) if not config: @@ -364,22 +540,28 @@ def extract_metadata_schema(self, config_name: str) -> dict[str, Any]: } for feature in config.dataset_info.features: - if feature.role == FieldRole.REGULATOR_IDENTIFIER: + if feature.role == "regulator_identifier": schema["regulator_fields"].append(feature.name) - elif feature.role == FieldRole.TARGET_IDENTIFIER: + elif feature.role == "target_identifier": schema["target_fields"].append(feature.name) - elif feature.role == FieldRole.EXPERIMENTAL_CONDITION: + elif feature.role == "experimental_condition": schema["condition_fields"].append(feature.name) if feature.definitions: schema["condition_definitions"][feature.name] = feature.definitions # Add top-level conditions (applies to all configs/samples) - if self.dataset_card.experimental_conditions: - schema["top_level_conditions"] = self.dataset_card.experimental_conditions + # Stored in model_extra as dict + if self.dataset_card.model_extra: + top_level = self.dataset_card.model_extra.get("experimental_conditions") + if top_level: + schema["top_level_conditions"] = top_level # Add config-level conditions (applies to this config's samples) - if config.experimental_conditions: - schema["config_level_conditions"] = config.experimental_conditions + # Stored in model_extra as dict + if config.model_extra: + config_level = config.model_extra.get("experimental_conditions") + if config_level: + schema["config_level_conditions"] = config_level return schema @@ -415,7 +597,7 @@ def get_condition_levels( f"Field '{field_name}' not found in config '{config_name}'" ) - if feature.role != FieldRole.EXPERIMENTAL_CONDITION: + if feature.role != "experimental_condition": raise DataCardError( f"Field '{field_name}' is not an experimental condition " f"(role={feature.role})" @@ -429,6 +611,191 @@ def get_condition_levels( values = self.get_field_values(config_name, field_name) return sorted(list(values)) + def get_experimental_conditions( + self, config_name: str | None = None + ) -> dict[str, Any]: + """ + Get experimental conditions with proper hierarchy handling. + + This method enables drilling down into the experimental conditions hierarchy: + - Top-level (repo-wide): Common to all configs/samples + - Config-level: Specific to a config, common to its samples + - Field-level: Per-sample variation (use get_field_definitions instead) + + Returns experimental conditions at the appropriate level: + - If config_name is None: returns top-level (repo-wide) conditions only + - If config_name is provided: returns merged (top + config) conditions + + All conditions are returned as flexible dicts that preserve the original + YAML structure. Navigate nested dicts to access specific values. + + :param config_name: Optional config name. If provided, merges top and config levels + :return: Dict of experimental conditions (empty dict if none defined) + + Example: + >>> # Get top-level conditions + >>> top = card.get_experimental_conditions() + >>> temp = top.get('temperature_celsius', 30) + >>> + >>> # Get merged conditions for a config + >>> merged = card.get_experimental_conditions('config_name') + >>> media = merged.get('media', {}) + >>> media_name = media.get('name', 'unspecified') + + """ + # Get top-level conditions (stored in model_extra) + top_level = ( + self.dataset_card.model_extra.get("experimental_conditions", {}) + if self.dataset_card.model_extra + else {} + ) + + # If no config specified, return top-level only + if config_name is None: + return top_level.copy() if isinstance(top_level, dict) else {} + + # Get config-level conditions + config = self.get_config(config_name) + if not config: + raise DataCardError(f"Configuration '{config_name}' not found") + + config_level = ( + config.model_extra.get("experimental_conditions", {}) + if config.model_extra + else {} + ) + + # Merge: config-level overrides top-level + merged = {} + if isinstance(top_level, dict): + merged.update(top_level) + if isinstance(config_level, dict): + merged.update(config_level) + + return merged + + def get_field_definitions( + self, config_name: str, field_name: str + ) -> dict[str, Any]: + """ + Get definitions for a specific field (field-level conditions). + + This is the third level of the experimental conditions hierarchy - conditions + that vary per sample. Returns a dict mapping each possible field value to its + detailed specification. + + For fields with role=experimental_condition, the definitions typically include + nested structures like media composition, temperature, treatments, etc. that + define what each categorical value means experimentally. + + :param config_name: Configuration name + :param field_name: Field name (typically has role=experimental_condition) + :return: Dict mapping field values to their definition dicts (empty if no definitions) + :raises DataCardError: If config or field not found + + Example: + >>> # Get condition definitions + >>> defs = card.get_field_definitions('harbison_2004', 'condition') + >>> # defs = {'YPD': {...}, 'HEAT': {...}, ...} + >>> + >>> # Drill down into a specific condition + >>> ypd = defs['YPD'] + >>> env_conds = ypd.get('environmental_conditions', {}) + >>> media = env_conds.get('media', {}) + >>> media_name = media.get('name') + + """ + config = self.get_config(config_name) + if not config: + raise DataCardError(f"Configuration '{config_name}' not found") + + # Find the feature + feature = None + for f in config.dataset_info.features: + if f.name == field_name: + feature = f + break + + if not feature: + raise DataCardError( + f"Field '{field_name}' not found in config '{config_name}'" + ) + + # Return definitions if present, otherwise empty dict + return feature.definitions if feature.definitions else {} + + def get_field_attribute( + self, config_name: str, field_name: str, attribute: str + ) -> dict[str, Any]: + """ + Extract a specific attribute from field definitions. + + This is useful for exploring nested attributes in condition definitions, + such as media composition, temperature parameters, or growth phases. + + :param config_name: Configuration name + :param field_name: Field with definitions (e.g., 'condition') + :param attribute: Attribute to extract (e.g., 'media', 'temperature_celsius') + :return: Dict mapping field values to their attribute specifications. + Returns 'unspecified' if attribute doesn't exist for a value. + + Example: + >>> card = DataCard('BrentLab/harbison_2004') + >>> media = card.get_field_attribute('harbison_2004', 'condition', 'media') + >>> print(media['YPD']) + {'name': 'YPD', 'carbon_source': [...], 'nitrogen_source': [...]} + + """ + # Get all field definitions + definitions = self.get_field_definitions(config_name, field_name) + + # Extract attribute for each definition + result = {} + for field_value, definition in definitions.items(): + if attribute in definition: + result[field_value] = definition[attribute] + else: + result[field_value] = "unspecified" + + return result + + def list_experimental_condition_fields(self, config_name: str) -> list[str]: + """ + List all fields with role=experimental_condition in a config. + + These are fields that contain per-sample experimental condition variation. + They represent the field-level (third level) of the experimental conditions + hierarchy. + + Fields with this role typically have `definitions` that map each value to + its detailed experimental specification. Use `get_field_definitions()` to + access these definitions. + + :param config_name: Configuration name + :return: List of field names with experimental_condition role + :raises DataCardError: If config not found + + Example: + >>> # Find all condition fields + >>> cond_fields = card.list_experimental_condition_fields('config_name') + >>> # ['condition', 'treatment', 'time_point'] + >>> + >>> # Then get definitions for each + >>> for field in cond_fields: + ... defs = card.get_field_definitions('config_name', field) + ... print(f"{field}: {len(defs)} levels") + + """ + config = self.get_config(config_name) + if not config: + raise DataCardError(f"Configuration '{config_name}' not found") + + return [ + f.name + for f in config.dataset_info.features + if f.role == "experimental_condition" + ] + def summary(self) -> str: """Get a human-readable summary of the dataset.""" card = self.dataset_card diff --git a/tfbpapi/datainfo/metadata_manager.py b/tfbpapi/datainfo/metadata_manager.py index fabdd9a..da1efc3 100644 --- a/tfbpapi/datainfo/metadata_manager.py +++ b/tfbpapi/datainfo/metadata_manager.py @@ -1,623 +1,278 @@ -"""MetadataManager for cross-dataset metadata filtering and querying.""" +""" +MetadataManager for creating metadata table views from DataCard information. + +This module provides the MetadataManager class for generating SQL commands that create +metadata views from HuggingFace dataset information extracted via DataCard. The manager +enables users to: + +- Specify which columns to include in metadata views +- Add constant columns from top-level or config-level experimental conditions +- Flatten nested condition definitions into queryable columns +- Generate SQL CREATE VIEW statements + +The design follows a user-driven workflow: +1. User explores DataCard to understand available fields and conditions +2. User downloads data via HfCacheManager/HfQueryAPI (creates base views) +3. User specifies column selection for metadata view +4. MetadataManager generates and executes SQL to create the view + +""" -import logging -import re from pathlib import Path -from typing import Any, Optional +from typing import Any import duckdb import pandas as pd -from ..errors import DataCardError -from .datacard import DataCard -from .models import FieldRole - -# Separator conventions for concatenated fields +# Separator conventions for formatting compound metadata values COMPONENT_SEPARATORS = { - "type_value": ":", # Separates component type from value - "value_conc": "@", # Separates value from concentration - "components": ";", # Separates multiple components of same type - "types": "|", # Separates different component types (future use) + "type_value": ":", # Separates type from value (e.g., "type:value") + "value_conc": "@", # Separates value from concentration (e.g., "compound@0.5%") + "components": ";", # Separates multiple components (e.g., "comp1;comp2;comp3") + "types": "|", # Separates component types (e.g., "type1|type2|type3") } class MetadataManager: """ - Cross-dataset metadata query manager using DuckDB temp views. - - Stores metadata field values as-is from parquet files, plus optionally expands field - definitions into searchable columns for experimental conditions. + Manager for creating metadata table views from DataCard information. + + MetadataManager provides a flexible interface for generating SQL views that combine + data fields with experimental condition metadata. Users specify which columns to + include and how to handle conditions at different hierarchy levels. + + Example: + >>> # Step 1: Explore schema with DataCard + >>> card = DataCard("BrentLab/harbison_2004") + >>> schema = card.extract_metadata_schema("harbison_2004") + >>> + >>> # Step 2: Create metadata view + >>> mgr = MetadataManager() + >>> view = mgr.create_metadata_view( + ... base_view="harbison_2004_train", + ... view_name="harbison_2004_metadata", + ... include_fields=["regulator_locus_tag", "target_locus_tag", "condition"], + ... constant_columns={ + ... "temperature_celsius": 30, + ... "strain_background": "BY4741" + ... } + ... ) """ - def __init__(self, cache_dir: Path | None = None, cache: bool = False): + def __init__( + self, + cache_dir: Path | None = None, + cache: bool = False, + duckdb_conn: duckdb.DuckDBPyConnection | None = None, + ): """ - Initialize MetadataManager with optional caching. + Initialize MetadataManager. - :param cache_dir: Directory for cached metadata (if cache=True) - :param cache: If True, persist metadata extractions. Default: False + :param cache_dir: Optional directory for caching metadata views + :param cache: Whether to enable persistent caching (not yet implemented) + :param duckdb_conn: Optional DuckDB connection to use. If None, creates new + in-memory connection. Pass a shared connection to work with views created + by HfQueryAPI or other tools. """ - self.logger = logging.getLogger(self.__class__.__name__) - self._conn = duckdb.connect(":memory:") - self._registered_datasets: dict[str, DataCard] = {} - self._view_names: dict[tuple[str, str], str] = {} # (repo_id, config) -> view + self._conn = duckdb_conn if duckdb_conn is not None else duckdb.connect(":memory:") self._cache_dir = cache_dir self._cache_enabled = cache + self._registered_datasets: dict[str, Any] = {} + self._view_names: dict[tuple[str, str], str] = {} - def register(self, repo_id: str, config_names: list[str] | None = None) -> None: - """ - Register a dataset for cross-dataset queries. - - :param repo_id: HuggingFace repository identifier - :param config_names: Optional list of config names to register. If None, - registers all configs. - + def _sanitize_view_name(self, repo_id: str, config_name: str) -> str: """ - self.logger.info(f"Registering dataset: {repo_id}") + Convert repo_id and config_name to valid SQL identifier. - # Load datacard - if repo_id not in self._registered_datasets: - datacard = DataCard(repo_id=repo_id) - self._registered_datasets[repo_id] = datacard - else: - datacard = self._registered_datasets[repo_id] - - # Determine which configs to register - if config_names is None: - configs_to_register = [c.config_name for c in datacard.configs] - else: - configs_to_register = config_names + Replaces special characters (/, -, spaces) with underscores and appends + '_metadata' suffix. - # Register each config - for config_name in configs_to_register: - self._register_config(datacard, config_name) + :param repo_id: Repository ID (e.g., "BrentLab/harbison_2004") + :param config_name: Configuration name (e.g., "harbison_2004") + :return: Sanitized view name (e.g., "BrentLab_harbison_2004_harbison_2004_metadata") - # Recreate unified view - self._create_unified_view() + Example: + >>> mgr = MetadataManager() + >>> mgr._sanitize_view_name("BrentLab/dataset-name", "config_name") + 'BrentLab_dataset_name_config_name_metadata' - def _register_config(self, datacard: DataCard, config_name: str) -> None: """ - Register a single config for querying. + sanitized = f"{repo_id}_{config_name}" + sanitized = sanitized.replace("/", "_").replace("-", "_").replace(" ", "_") + return f"{sanitized}_metadata" - :param datacard: DataCard instance - :param config_name: Configuration name to register - - """ - self.logger.debug(f"Registering config: {datacard.repo_id}/{config_name}") - - # Extract metadata schema - try: - schema = datacard.extract_metadata_schema(config_name) - except DataCardError as e: - self.logger.error(f"Failed to extract schema for {config_name}: {e}") - raise - - # Extract metadata from parquet file - try: - metadata_df = self._extract_metadata_from_config( - datacard, config_name, schema - ) - except Exception as e: - self.logger.error(f"Failed to extract metadata for {config_name}: {e}") - raise - - # Create temp view - view_name = self._sanitize_view_name(datacard.repo_id, config_name) - self._create_temp_view(datacard.repo_id, config_name, metadata_df, view_name) - self._view_names[(datacard.repo_id, config_name)] = view_name - - def unregister(self, repo_id: str) -> None: + def _flatten_condition_definition(self, definition: dict) -> dict: """ - Remove dataset from cross-dataset queries. - - :param repo_id: HuggingFace repository identifier to unregister + Flatten nested condition definition dict into flat key-value pairs. + + Extracts common experimental condition fields: + - media.name -> growth_media + - temperature_celsius -> temperature_celsius + - cultivation_method -> cultivation_method + - strain_background -> strain_background + + :param definition: Nested definition dict from field.definitions[value] + :return: Flat dict with standardized keys + + Example: + >>> mgr = MetadataManager() + >>> definition = { + ... "media": {"name": "YPD"}, + ... "temperature_celsius": 30 + ... } + >>> mgr._flatten_condition_definition(definition) + {'growth_media': 'YPD', 'temperature_celsius': 30} """ - if repo_id not in self._registered_datasets: - self.logger.warning(f"Dataset {repo_id} not registered") - return - - # Remove all views for this repo - views_to_remove = [ - (rid, cfg) for rid, cfg in self._view_names.keys() if rid == repo_id - ] - - for rid, cfg in views_to_remove: - view_name = self._view_names.pop((rid, cfg)) - try: - self._conn.execute(f"DROP VIEW IF EXISTS {view_name}") - except Exception as e: - self.logger.warning(f"Failed to drop view {view_name}: {e}") - - # Remove datacard - del self._registered_datasets[repo_id] - - # Recreate unified view - self._create_unified_view() - - def _extract_metadata_from_config( - self, datacard: DataCard, config_name: str, schema: dict[str, Any] - ) -> pd.DataFrame: - """ - Extract metadata from parquet file for a config. - - Processes experimental conditions at all three hierarchy levels: - - Top-level (repo-wide): Applies to ALL samples - - Config-level: Applies to all samples in this config - - Field-level: Varies per sample (from field definitions) + result = {} + if not definition: + return result - Hierarchy merging: field-level > config-level > top-level + # Extract media name as growth_media + if "media" in definition and isinstance(definition["media"], dict): + if "name" in definition["media"]: + result["growth_media"] = definition["media"]["name"] - :param datacard: DataCard instance - :param config_name: Configuration name - :param schema: Metadata schema from extract_metadata_schema() + # Extract other top-level condition fields + for key in ["temperature_celsius", "cultivation_method", "strain_background"]: + if key in definition: + result[key] = definition[key] - :return: DataFrame with metadata field values + expanded columns + return result + def _flatten_experimental_conditions(self, exp_conds: Any) -> dict: """ - # Get config to access data files - config = datacard.get_config(config_name) - if not config: - raise DataCardError(f"Configuration '{config_name}' not found") - - # Determine which fields to extract - metadata_fields = ( - schema["regulator_fields"] - + schema["target_fields"] - + schema["condition_fields"] - ) - - # Add sample_id if not already in list - if "sample_id" not in metadata_fields: - metadata_fields.append("sample_id") - - # TODO: For MVP, we need actual parquet file path from HuggingFace - # This requires integration with HF datasets or direct file access - # For now, create a minimal placeholder that will work with tests - - # Create placeholder DataFrame with required structure - metadata_df = pd.DataFrame() - - # Add computed columns - metadata_df["dataset"] = datacard.repo_id - metadata_df["config_name"] = config_name - - # Add metadata fields as empty for now - for field in metadata_fields: - metadata_df[field] = "unspecified" - - # Process experimental conditions in hierarchy order (low to high priority) - # Start with top-level (repo-wide) conditions - if schema.get("top_level_conditions"): - top_conditions = self._flatten_experimental_conditions( - schema["top_level_conditions"] - ) - for col_name, col_value in top_conditions.items(): - metadata_df[col_name] = col_value - - # Apply config-level conditions (overrides top-level) - if schema.get("config_level_conditions"): - config_conditions = self._flatten_experimental_conditions( - schema["config_level_conditions"] - ) - for col_name, col_value in config_conditions.items(): - metadata_df[col_name] = col_value - - # Expand field-level condition definitions (overrides config/top-level) - if schema["condition_definitions"]: - for field_name, definitions in schema["condition_definitions"].items(): - # Add expanded columns for each condition - metadata_df["growth_media"] = "unspecified" - metadata_df["components"] = "unspecified" - - return metadata_df - - def _flatten_experimental_conditions(self, exp_conditions: Any) -> dict[str, Any]: - """ - Flatten ExperimentalConditions object into column name/value pairs. - - Processes both environmental_conditions and strain_background from repo-level or - config-level ExperimentalConditions. Also handles extra fields stored in - model_extra (Pydantic's extra='allow'). + Extract attributes from ExperimentalConditions object or dict. - :param exp_conditions: ExperimentalConditions instance - :return: Dict mapping column names to values + Handles both Pydantic model objects and plain dicts. Extracts: + - temperature_celsius + - cultivation_method + - strain_background + - media.name (as growth_media) - """ - flattened: dict[str, Any] = {} - - # Handle strain_background - if ( - hasattr(exp_conditions, "strain_background") - and exp_conditions.strain_background - ): - strain = exp_conditions.strain_background - if isinstance(strain, str): - flattened["strain_background"] = strain - elif isinstance(strain, dict): - flattened["strain_background"] = strain.get("name", "unspecified") - - # Handle extra fields (for models with extra='allow') - # Some datacards store conditions directly as extra fields - if hasattr(exp_conditions, "model_extra") and exp_conditions.model_extra: - for key, value in exp_conditions.model_extra.items(): - # Store extra fields with their original names - flattened[key] = value - - # Handle environmental_conditions - if ( - hasattr(exp_conditions, "environmental_conditions") - and exp_conditions.environmental_conditions - ): - env = exp_conditions.environmental_conditions - - # Temperature - if ( - hasattr(env, "temperature_celsius") - and env.temperature_celsius is not None - ): - flattened["temperature_celsius"] = env.temperature_celsius - - # Cultivation method - if hasattr(env, "cultivation_method") and env.cultivation_method: - flattened["cultivation_method"] = env.cultivation_method - - # Media information - if hasattr(env, "media") and env.media: - media = env.media - if hasattr(media, "name") and media.name: - flattened["growth_media"] = media.name - - # Build components string from media composition - components = [] - - # Carbon source - if hasattr(media, "carbon_source") and media.carbon_source: - for compound in media.carbon_source: - if hasattr(compound, "compound"): - components.append( - self._format_compound( - "carbon_source", compound.model_dump() - ) - ) - - # Nitrogen source - if hasattr(media, "nitrogen_source") and media.nitrogen_source: - for compound in media.nitrogen_source: - if hasattr(compound, "compound"): - components.append( - self._format_compound( - "nitrogen_source", compound.model_dump() - ) - ) - - # Phosphate source - if hasattr(media, "phosphate_source") and media.phosphate_source: - for compound in media.phosphate_source: - if hasattr(compound, "compound"): - components.append( - self._format_compound( - "phosphate_source", compound.model_dump() - ) - ) - - # Additives - if hasattr(media, "additives") and media.additives: - for additive in media.additives: - if hasattr(additive, "name"): - components.append(f"additive:{additive.name}") - - if components: - flattened["components"] = "|".join(components) - - # Growth phase - if hasattr(env, "growth_phase") and env.growth_phase: - gp = env.growth_phase - if hasattr(gp, "stage") and gp.stage: - flattened["growth_stage"] = gp.stage - if hasattr(gp, "od600") and gp.od600 is not None: - flattened["od600"] = gp.od600 - - # Chemical treatments - if hasattr(env, "chemical_treatments") and env.chemical_treatments: - treatments = [] - for treatment in env.chemical_treatments: - if hasattr(treatment, "compound") and treatment.compound: - treatments.append(treatment.compound) - if treatments: - flattened["chemical_treatments"] = ";".join(treatments) - - # Drug treatments - if hasattr(env, "drug_treatments") and env.drug_treatments: - drugs = [] - for drug in env.drug_treatments: - if hasattr(drug, "compound") and drug.compound: - drugs.append(drug.compound) - if drugs: - flattened["drug_treatments"] = ";".join(drugs) - - # Heat treatment - if hasattr(env, "heat_treatment") and env.heat_treatment: - ht = env.heat_treatment - if ( - hasattr(ht, "temperature_celsius") - and ht.temperature_celsius is not None - ): - flattened["heat_treatment_temp"] = ht.temperature_celsius - - # Induction - if hasattr(env, "induction") and env.induction: - ind = env.induction - if hasattr(ind, "system") and ind.system: - flattened["induction_system"] = ind.system - - return flattened - - def _flatten_condition_definition( - self, definition: dict[str, Any] - ) -> dict[str, str]: - """ - Flatten a single condition definition into searchable fields. + :param exp_conds: ExperimentalConditions object or dict + :return: Flat dict with condition values - :param definition: Condition definition dict (e.g., YPD definition) - :return: Dict with flattened fields (growth_media, components) + Example: + >>> mgr = MetadataManager() + >>> class MockConditions: + ... temperature_celsius = 30 + ... strain_background = "BY4741" + >>> mgr._flatten_experimental_conditions(MockConditions()) + {'temperature_celsius': 30, 'strain_background': 'BY4741'} """ - flattened: dict[str, str] = { - "growth_media": "unspecified", - "components": "", - } - - # Extract environmental conditions if present - if "environmental_conditions" in definition: - env_conds = definition["environmental_conditions"] - - # Extract media information - if "media" in env_conds: - media = env_conds["media"] - if isinstance(media, dict): - # Extract media name - if "name" in media: - flattened["growth_media"] = media["name"] - - # Build components string - components = [] - - # Extract carbon source - if "carbon_source" in media: - carbon = media["carbon_source"] - if isinstance(carbon, list): - for compound in carbon: - components.append( - self._format_compound("carbon_source", compound) - ) - elif isinstance(carbon, dict): - components.append( - self._format_compound("carbon_source", carbon) - ) - - # Extract nitrogen source - if "nitrogen_source" in media: - nitrogen = media["nitrogen_source"] - if isinstance(nitrogen, list): - for compound in nitrogen: - components.append( - self._format_compound("nitrogen_source", compound) - ) - elif isinstance(nitrogen, dict): - components.append( - self._format_compound("nitrogen_source", compound) - ) - - # Extract phosphate source - if "phosphate_source" in media: - phosphate = media["phosphate_source"] - if isinstance(phosphate, list): - for compound in phosphate: - components.append( - self._format_compound("phosphate_source", compound) - ) - elif isinstance(phosphate, dict): - components.append( - self._format_compound("phosphate_source", compound) - ) - - # Join components - if components: - flattened["components"] = "|".join(components) - - return flattened - - def _format_compound(self, component_type: str, compound: dict[str, Any]) -> str: - """ - Format a compound dict into searchable string. - - :param component_type: Type of component (carbon_source, nitrogen_source, etc.) - :paramcompound: Compound info dict with name and concentration - :return: Formatted string (e.g., "carbon_source:D-glucose@2%") - - """ - if isinstance(compound, str): - return f"{component_type}:{compound}" + result = {} + if exp_conds is None: + return result - name = compound.get("name", "unknown") - result = f"{component_type}:{name}" + # Handle both objects (with attributes) and dicts (with keys) + for attr in ["temperature_celsius", "cultivation_method", "strain_background"]: + if hasattr(exp_conds, attr): + val = getattr(exp_conds, attr, None) + else: + val = exp_conds.get(attr) if isinstance(exp_conds, dict) else None - # Add concentration if present - if "concentration_percent" in compound: - result += f"@{compound['concentration_percent']}%" - elif "concentration_g_per_l" in compound: - result += f"@{compound['concentration_g_per_l']}g/L" - elif "concentration_molar" in compound: - result += f"@{compound['concentration_molar']}M" + if val is not None: + result[attr] = val - return result + # Extract media.name if present + if hasattr(exp_conds, "media"): + media = getattr(exp_conds, "media", None) + else: + media = exp_conds.get("media") if isinstance(exp_conds, dict) else None - def _create_temp_view( - self, repo_id: str, config_name: str, metadata_df: pd.DataFrame, view_name: str - ) -> None: - """ - Create DuckDB temp view from metadata DataFrame. + if media: + if hasattr(media, "name"): + name = getattr(media, "name", None) + else: + name = media.get("name") if isinstance(media, dict) else None - :param repo_id: Repository identifier - :param config_name: Configuration name - :param metadata_df: Metadata DataFrame - :param view_name: Sanitized view name + if name: + result["growth_media"] = name - """ - try: - # Register DataFrame as temp view in DuckDB - self._conn.register(view_name, metadata_df) - self.logger.debug(f"Created temp view: {view_name}") - except Exception as e: - self.logger.error(f"Failed to create view {view_name}: {e}") - raise + return result - def _sanitize_view_name(self, repo_id: str, config_name: str) -> str: + def create_metadata_view( + self, + base_view: str, + view_name: str, + include_fields: list[str], + constant_columns: dict[str, Any] | None = None, + ) -> str: """ - Create a sanitized view name from repo_id and config_name. - - :param repo_id: Repository identifier - :param config_name: Configuration name - :return: Sanitized view name safe for SQL + Create metadata view from base view with user-specified columns. + + Generates SQL CREATE OR REPLACE VIEW statement that selects specified fields + from an existing base view and adds constant columns as literals. + + :param base_view: Name of existing DuckDB view (created by HfCacheManager) + :param view_name: Name for the new metadata view + :param include_fields: List of field names to select from base view + :param constant_columns: Optional dict of {column_name: value} to add as constants + :return: Name of created view + + Example: + >>> mgr = MetadataManager() + >>> view = mgr.create_metadata_view( + ... base_view="harbison_2004_train", + ... view_name="harbison_2004_metadata", + ... include_fields=["regulator_locus_tag", "target_locus_tag"], + ... constant_columns={"temperature_celsius": 30} + ... ) + >>> view + 'harbison_2004_metadata' """ - # Replace non-alphanumeric with underscores - safe_repo = re.sub(r"[^a-zA-Z0-9]+", "_", repo_id) - safe_config = re.sub(r"[^a-zA-Z0-9]+", "_", config_name) - return f"{safe_repo}_{safe_config}_metadata" - - def _create_unified_view(self) -> None: - """Create unified_metadata view combining all registered datasets.""" - if not self._view_names: - self.logger.debug("No views registered, skipping unified view creation") - return - - # Drop existing unified view if present - try: - self._conn.execute("DROP VIEW IF EXISTS unified_metadata") - except Exception: - pass - - # Get all column names across all views - all_columns = set() - for view_name in self._view_names.values(): - try: - cols = ( - self._conn.execute(f"DESCRIBE {view_name}") - .fetchdf()["column_name"] - .tolist() - ) - all_columns.update(cols) - except Exception as e: - self.logger.warning(f"Failed to get columns from {view_name}: {e}") - - if not all_columns: - self.logger.warning("No columns found in registered views") - return - - # Build UNION ALL query with column alignment - union_queries = [] - for view_name in self._view_names.values(): - # Get columns in this view - view_cols = ( - self._conn.execute(f"DESCRIBE {view_name}") - .fetchdf()["column_name"] - .tolist() - ) - - # Build SELECT with coalesce for missing columns - select_parts = [] - for col in sorted(all_columns): - if col in view_cols: - select_parts.append(f'"{col}"') + # Build SELECT clause with included fields + select_parts = include_fields.copy() + + # Add constant columns as literals + if constant_columns: + for col_name, col_value in constant_columns.items(): + if isinstance(col_value, str): + select_parts.append(f"'{col_value}' AS {col_name}") else: - select_parts.append(f"'unspecified' AS \"{col}\"") - - union_queries.append(f"SELECT {', '.join(select_parts)} FROM {view_name}") - - # Create unified view - unified_query = " UNION ALL ".join(union_queries) - try: - self._conn.execute(f"CREATE VIEW unified_metadata AS {unified_query}") - self.logger.debug( - f"Created unified view from {len(self._view_names)} views with {len(all_columns)} columns" - ) - except Exception as e: - self.logger.error(f"Failed to create unified view: {e}") - raise - - def query(self, sql: str) -> pd.DataFrame: - """ - Execute SQL query across all registered datasets. + select_parts.append(f"{col_value} AS {col_name}") - :param sql: SQL query string to execute - :return: Query results as pandas DataFrame + select_clause = ",\n ".join(select_parts) - """ - try: - result = self._conn.execute(sql).fetchdf() - return result - except Exception as e: - self.logger.error(f"Query failed: {e}") - raise - - def filter_by_regulator(self, regulators: list[str]) -> "MetadataManager": - """ - Filter metadata to specific regulators. - - :param regulators: List of regulator symbols to filter by - :return: Self for method chaining - - """ - # TODO: Implement filtering logic - # This will be implemented in Phase 3 - raise NotImplementedError("filter_by_regulator not yet implemented") - - def filter_by_conditions(self, **kwargs: Any) -> "MetadataManager": - """ - Filter by experimental conditions. + # Generate SQL + sql = f"""\ + CREATE OR REPLACE VIEW {view_name} AS + SELECT {select_clause} + FROM {base_view} +""" - :param kwargs: Condition filters (e.g., media="YPD", temperature=30) - :return: Self for method chaining + # Execute + self._conn.execute(sql) - """ - # TODO: Implement filtering logic - # This will be implemented in Phase 3 - raise NotImplementedError("filter_by_conditions not yet implemented") + return view_name def get_active_configs(self) -> list[tuple[str, str]]: """ - Get (repo_id, config_name) pairs that match active filters. + Get list of registered (repo_id, config_name) tuples. + + :return: List of active config tuples (empty for minimal implementation) - :return: List of (repo_id, config_name) tuples + Note: + This is a placeholder method for future multi-dataset support. + Currently returns empty list. """ - # TODO: Implement with filter support - # For now, return all registered configs - return list(self._view_names.keys()) + return [] def get_summary(self) -> pd.DataFrame: """ - Get summary stats for registered datasets. + Get summary DataFrame of registered datasets. + + :return: Empty DataFrame for minimal implementation - :return: DataFrame with summary statistics + Note: + This is a placeholder method for future multi-dataset support. + Currently returns empty DataFrame. """ - if not self._view_names: - return pd.DataFrame() - - # TODO: Implement summary statistics - # This will be implemented in Phase 3 - summary_data = [] - for (repo_id, config_name), view_name in self._view_names.items(): - summary_data.append( - { - "dataset": repo_id, - "config_name": config_name, - "view_name": view_name, - } - ) - - return pd.DataFrame(summary_data) + return pd.DataFrame() diff --git a/tfbpapi/datainfo/models.py b/tfbpapi/datainfo/models.py index 0e76fc0..0e3230b 100644 --- a/tfbpapi/datainfo/models.py +++ b/tfbpapi/datainfo/models.py @@ -1,10 +1,16 @@ -"""Pydantic models for dataset card validation.""" +""" +Pydantic models for dataset card validation. + +These models provide minimal structure for parsing HuggingFace dataset cards while +remaining flexible enough to accommodate diverse experimental systems. Most fields use +extra="allow" to accept domain-specific additions without requiring code changes. + +""" -import warnings from enum import Enum from typing import Any -from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator +from pydantic import BaseModel, ConfigDict, Field, field_validator class DatasetType(str, Enum): @@ -17,384 +23,29 @@ class DatasetType(str, Enum): QC_DATA = "qc_data" -class FieldRole(str, Enum): - """Valid role values for feature fields.""" - - REGULATOR_IDENTIFIER = "regulator_identifier" - TARGET_IDENTIFIER = "target_identifier" - QUANTITATIVE_MEASURE = "quantitative_measure" - EXPERIMENTAL_CONDITION = "experimental_condition" - GENOMIC_COORDINATE = "genomic_coordinate" - - -class GrowthStage(str, Enum): - """Common growth stages.""" - - MID_LOG_PHASE = "mid_log_phase" - EARLY_LOG_PHASE = "early_log_phase" - LATE_LOG_PHASE = "late_log_phase" - STATIONARY_PHASE = "stationary_phase" - EARLY_STATIONARY_PHASE = "early_stationary_phase" - OVERNIGHT_STATIONARY_PHASE = "overnight_stationary_phase" - MID_LOG = "mid_log" - EARLY_LOG = "early_log" - LATE_LOG = "late_log" - EXPONENTIAL_PHASE = "exponential_phase" - - -class CompoundInfo(BaseModel): - """Information about a chemical compound in media.""" - - compound: str = Field(..., description="Chemical compound name") - concentration_percent: float | None = Field( - default=None, description="Concentration as percentage (w/v)" - ) - concentration_g_per_l: float | None = Field( - default=None, description="Concentration in grams per liter" - ) - concentration_molar: float | None = Field( - default=None, description="Concentration in molar (M)" - ) - specifications: list[str] | None = Field( - default=None, - description="Additional specifications (e.g., 'without_amino_acids')", - ) - - -class MediaAdditiveInfo(BaseModel): - """Information about media additives (e.g., butanol for filamentation).""" - - compound: str = Field(..., description="Additive compound name") - concentration_percent: float | None = Field( - default=None, description="Concentration as percentage (w/v)" - ) - description: str | None = Field( - default=None, description="Additional context about the additive" - ) - - -class MediaInfo(BaseModel): - """Growth media specification.""" - - name: str = Field( - ..., - description="Canonical or descriptive media name " - "(minimal, synthetic_complete, YPD, etc.)", - ) - carbon_source: list[CompoundInfo] | None = Field( - default=None, description="Carbon source compounds and concentrations" - ) - nitrogen_source: list[CompoundInfo] | None = Field( - default=None, description="Nitrogen source compounds and concentrations" - ) - phosphate_source: list[CompoundInfo] | None = Field( - default=None, description="Phosphate source compounds and concentrations" - ) - additives: list[MediaAdditiveInfo] | None = Field( - default=None, - description="Additional media components (e.g., butanol for filamentation)", - ) - - @field_validator("carbon_source", "nitrogen_source", mode="before") - @classmethod - def validate_compound_list(cls, v): - """Validate compound lists and handle 'unspecified' strings.""" - if v is None: - return None - if isinstance(v, str): - if v == "unspecified": - warnings.warn( - "Compound source specified as string 'unspecified'. " - "Should be null/omitted or a structured list.", - UserWarning, - ) - return None - # Try to parse as single compound - return [{"compound": v}] - return v - - -class GrowthPhaseInfo(BaseModel): - """Growth phase information at harvest.""" - - od600: float | None = Field( - default=None, description="Optical density at 600nm at harvest" - ) - od600_tolerance: float | None = Field( - default=None, description="Measurement tolerance for OD600" - ) - stage: str | None = Field( - default=None, description="Growth stage (preferred field name)" - ) - phase: str | None = Field( - default=None, - description="Growth stage (alias for 'stage' for backward compatibility)", - ) - description: str | None = Field( - default=None, description="Additional context about growth phase" - ) - - @field_validator("stage", "phase", mode="before") - @classmethod - def validate_stage(cls, v): - """Validate stage and warn if not a common value.""" - if v is None: - return None - # Get the list of known stages from the enum - known_stages = {stage.value for stage in GrowthStage} - if v not in known_stages: - warnings.warn( - f"Growth stage '{v}' not in recognized stages: {known_stages}", - UserWarning, - ) - return v - - @model_validator(mode="after") - def check_stage_phase_consistency(self): - """Ensure stage and phase are consistent if both provided.""" - if self.stage and self.phase and self.stage != self.phase: - raise ValueError( - "Inconsistent growth phase: " - f"stage='{self.stage}' vs phase='{self.phase}'" - ) - return self - - -class ChemicalTreatmentInfo(BaseModel): - """Chemical treatment applied to cultures.""" - - compound: str = Field(..., description="Chemical compound name") - concentration_percent: float | None = Field( - default=None, description="Concentration as percentage" - ) - concentration_molar: float | None = Field( - default=None, description="Concentration in molar (M)" - ) - duration_minutes: int | None = Field( - default=None, description="Duration in minutes" - ) - duration_hours: float | None = Field(default=None, description="Duration in hours") - target_pH: float | None = Field( - default=None, description="Target pH for pH adjustments" - ) - description: str | None = Field( - default=None, description="Additional context about the treatment" - ) - - -class DrugTreatmentInfo(ChemicalTreatmentInfo): - """Drug treatment - same structure as chemical treatment.""" - - pass - - -class HeatTreatmentInfo(BaseModel): - """Heat treatment information.""" - - duration_minutes: int = Field( - ..., description="Duration of heat treatment in minutes" - ) - description: str | None = Field( - default=None, description="Additional description of treatment" - ) - - -class TemperatureShiftInfo(BaseModel): - """Temperature shift for heat shock experiments.""" - - initial_temperature_celsius: float = Field( - ..., description="Initial cultivation temperature in Celsius" - ) - temperature_shift_celsius: float = Field( - ..., description="Temperature after shift in Celsius" - ) - temperature_shift_duration_minutes: int | None = Field( - default=None, description="Duration of temperature shift in minutes" - ) - description: str | None = Field( - default=None, description="Additional context about the temperature shift" - ) - - -class InductionInfo(BaseModel): - """Induction information for expression systems.""" - - inducer: CompoundInfo = Field(..., description="Inducer compound and concentration") - duration_hours: float | None = Field( - default=None, description="Duration of induction in hours" - ) - duration_minutes: int | None = Field( - default=None, description="Duration of induction in minutes" - ) - description: str | None = Field( - default=None, description="Additional context about the induction" - ) - - -class EnvironmentalConditions(BaseModel): - """Environmental conditions for sample cultivation.""" - - temperature_celsius: float | None = Field( - default=None, description="Cultivation temperature in Celsius" - ) - cultivation_method: str | None = Field( - default=None, - description="Cultivation method (e.g., 'batch_culture', 'chemostat')", - ) - growth_phase_at_harvest: GrowthPhaseInfo | None = Field( - default=None, description="Growth phase at time of harvest" - ) - media: MediaInfo | None = Field( - default=None, description="Growth media specification" - ) - chemical_treatment: ChemicalTreatmentInfo | None = Field( - default=None, description="Chemical treatment applied" - ) - drug_treatment: DrugTreatmentInfo | None = Field( - default=None, - description="Drug treatment applied (same structure as chemical_treatment)", - ) - heat_treatment: HeatTreatmentInfo | None = Field( - default=None, description="Heat treatment applied" - ) - temperature_shift: TemperatureShiftInfo | None = Field( - default=None, description="Temperature shift for heat shock experiments" - ) - induction: InductionInfo | None = Field( - default=None, description="Induction system for expression experiments" - ) - incubation_duration_hours: float | None = Field( - default=None, description="Total incubation duration in hours" - ) - incubation_duration_minutes: int | None = Field( - default=None, description="Total incubation duration in minutes" - ) - description: str | None = Field( - default=None, description="Additional descriptive information" - ) - - model_config = ConfigDict(extra="allow") - - @model_validator(mode="after") - def warn_extra_fields(self): - """Warn about any extra fields not in the specification.""" - known_fields = { - "temperature_celsius", - "cultivation_method", - "growth_phase_at_harvest", - "media", - "chemical_treatment", - "drug_treatment", - "heat_treatment", - "temperature_shift", - "induction", - "incubation_duration_hours", - "incubation_duration_minutes", - "description", - } - extra = set(self.__dict__.keys()) - known_fields - if extra: - warnings.warn( - f"EnvironmentalConditions contains non-standard fields: {extra}. " - "Consider adding to specification.", - UserWarning, - ) - return self - - -class ExperimentalConditions(BaseModel): - """Experimental conditions including environmental and other parameters.""" - - environmental_conditions: EnvironmentalConditions | None = Field( - default=None, description="Environmental cultivation conditions" - ) - strain_background: str | dict[str, Any] | None = Field( - default=None, - description="Strain background information (string or flexible dict structure)", - ) - - model_config = ConfigDict(extra="allow") - - @model_validator(mode="after") - def warn_extra_fields(self): - """Warn about any extra fields not in the specification.""" - known_fields = {"environmental_conditions", "strain_background"} - extra = set(self.__dict__.keys()) - known_fields - if extra: - warnings.warn( - f"ExperimentalConditions contains non-standard fields: {extra}. " - "Consider adding to specification.", - UserWarning, - ) - return self - - -class ClassLabelType(BaseModel): - """Categorical data type with class labels.""" - - names: list[str] = Field(..., description="List of possible class names") +class FeatureInfo(BaseModel): + """ + Information about a dataset feature/column. + Minimal required fields with flexible dtype handling. -class FeatureInfo(BaseModel): - """Information about a dataset feature/column.""" + """ name: str = Field(..., description="Column name in the data") - dtype: str | dict[str, ClassLabelType] = Field( + dtype: str | dict[str, Any] = Field( ..., - description="Data type (string, int64, float64, etc.) " - "or categorical class labels", + description="Data type (string, int64, float64, etc.) or class_label dict", ) - description: str = Field(..., description="Detailed description of the field") - role: FieldRole | None = Field( + description: str = Field(..., description="Description of the field") + role: str | None = Field( default=None, - description="Semantic role of the feature (e.g., 'target_identifier', " - "'regulator_identifier', 'quantitative_measure')", + description="Optional semantic role. 'experimental_condition' has special behavior.", ) definitions: dict[str, Any] | None = Field( default=None, - description="Definitions for categorical field values " - "with experimental conditions", + description="For experimental_condition fields: definitions per value", ) - @field_validator("dtype", mode="before") - @classmethod - def validate_dtype(cls, v): - """Validate and normalize dtype field.""" - if isinstance(v, str): - return v - elif isinstance(v, dict): - # Handle class_label structure - if "class_label" in v: - # Convert to our ClassLabelType structure - class_label_data = v["class_label"] - if isinstance(class_label_data, dict) and "names" in class_label_data: - return {"class_label": ClassLabelType(**class_label_data)} - else: - raise ValueError( - "Invalid class_label structure: expected dict " - f"with 'names' key, got {class_label_data}" - ) - else: - raise ValueError( - "Unknown dtype structure: expected 'class_label' key " - f"in dict, got keys: {list(v.keys())}" - ) - else: - raise ValueError( - "dtype must be a string or dict with " - f"class_label info, got {type(v)}: {v}" - ) - - def get_dtype_summary(self) -> str: - """Get a human-readable summary of the data type.""" - if isinstance(self.dtype, str): - return self.dtype - elif isinstance(self.dtype, dict) and "class_label" in self.dtype: - names = self.dtype["class_label"].names - return f"categorical ({len(names)} classes: {', '.join(names)})" - else: - return str(self.dtype) - class PartitioningInfo(BaseModel): """Partitioning configuration for datasets.""" @@ -408,13 +59,6 @@ class PartitioningInfo(BaseModel): ) -class DataFileInfo(BaseModel): - """Information about data files.""" - - split: str = Field(default="train", description="Dataset split name") - path: str = Field(..., description="Path to data file(s)") - - class DatasetInfo(BaseModel): """Dataset structure information.""" @@ -424,8 +68,20 @@ class DatasetInfo(BaseModel): ) +class DataFileInfo(BaseModel): + """Information about data files.""" + + split: str = Field(default="train", description="Dataset split name") + path: str = Field(..., description="Path to data file(s)") + + class DatasetConfig(BaseModel): - """Configuration for a dataset within a repository.""" + """ + Configuration for a dataset within a repository. + + Uses extra="allow" to accept arbitrary experimental_conditions and other fields. + + """ config_name: str = Field(..., description="Unique configuration identifier") description: str = Field(..., description="Human-readable description") @@ -439,12 +95,11 @@ class DatasetConfig(BaseModel): metadata_fields: list[str] | None = Field( default=None, description="Fields for embedded metadata extraction" ) - experimental_conditions: ExperimentalConditions | None = Field( - default=None, description="Experimental conditions for this config" - ) data_files: list[DataFileInfo] = Field(..., description="Data file information") dataset_info: DatasetInfo = Field(..., description="Dataset structure information") + model_config = ConfigDict(extra="allow") + @field_validator("applies_to") @classmethod def applies_to_only_for_metadata(cls, v, info): @@ -467,39 +122,16 @@ def metadata_fields_validation(cls, v): return v -class BasicMetadata(BaseModel): - """Basic dataset metadata.""" - - license: str | None = Field(default=None, description="Dataset license") - language: list[str] | None = Field(default=None, description="Dataset languages") - tags: list[str] | None = Field(default=None, description="Descriptive tags") - pretty_name: str | None = Field( - default=None, description="Human-readable dataset name" - ) - size_categories: list[str] | None = Field( - default=None, description="Dataset size categories" - ) +class DatasetCard(BaseModel): + """ + Complete dataset card model. + Uses extra="allow" to accept arbitrary top-level metadata and + experimental_conditions. -class DatasetCard(BaseModel): - """Complete dataset card model.""" + """ configs: list[DatasetConfig] = Field(..., description="Dataset configurations") - experimental_conditions: ExperimentalConditions | None = Field( - default=None, description="Top-level experimental conditions for all configs" - ) - license: str | None = Field(default=None, description="Dataset license") - language: list[str] | None = Field(default=None, description="Dataset languages") - tags: list[str] | None = Field(default=None, description="Descriptive tags") - pretty_name: str | None = Field( - default=None, description="Human-readable dataset name" - ) - size_categories: list[str] | None = Field( - default=None, description="Dataset size categories" - ) - strain_information: dict[str, Any] | None = Field( - default=None, description="Strain background information" - ) model_config = ConfigDict(extra="allow") diff --git a/tfbpapi/datainfo/sample_manager.py b/tfbpapi/datainfo/sample_manager.py new file mode 100644 index 0000000..271a0ee --- /dev/null +++ b/tfbpapi/datainfo/sample_manager.py @@ -0,0 +1,774 @@ +""" +Node-based sample representation for flexible filtering across heterogeneous datasets. + +This module provides a lightweight, NoSQL-inspired approach to managing samples from +multiple datasets with varying experimental condition structures. Each sample is +represented as a node with flattened properties, enabling flexible filtering across +datasets with different metadata schemas. + +Key Components: +- SampleNode: Represents a single sample with flattened properties +- SampleNodeCollection: In-memory storage with efficient indexing +- ActiveSet: Filtered collection of samples supporting set operations +- SampleFilter: MongoDB-style query language for filtering +- ConditionFlattener: Handles heterogeneous experimental condition structures +- SampleManager: Main API for loading, filtering, and managing samples + +Example Usage: + >>> manager = SampleManager() + >>> manager.load_from_datacard("BrentLab/harbison_2004", "harbison_2004") + >>> active = manager.filter_all({"carbon_source": {"$contains": "glucose"}}) + >>> print(f"Found {len(active)} glucose-grown samples") +""" + +from __future__ import annotations + +from dataclasses import dataclass, field +from datetime import datetime +from pathlib import Path +from typing import Any, Iterator + +import duckdb +import pandas as pd + + +def get_nested_value(data: dict, path: str) -> Any: + """ + Navigate nested dict using dot notation. + + Handles missing intermediate keys gracefully by returning None. + + :param data: Dictionary to navigate + :param path: Dot-separated path (e.g., "environmental_conditions.media.name") + :return: Value at path or None if not found + """ + if not isinstance(data, dict): + return None + + keys = path.split(".") + current = data + + for key in keys: + if not isinstance(current, dict) or key not in current: + return None + current = current[key] + + return current + + +def flatten_compound_list(compounds: list[dict] | str | None) -> str: + """ + Flatten compound list to comma-separated string. + + Handles various representations: + - List of dicts: Extract compound names + - String "unspecified": Return as-is + - None or empty list: Return "unspecified" + + :param compounds: Compound list or string + :return: Comma-separated compound names or "unspecified" + """ + if compounds is None or compounds == "unspecified": + return "unspecified" + + if isinstance(compounds, str): + return compounds + + if isinstance(compounds, list): + if not compounds: + return "unspecified" + compound_names = [ + c.get("compound", "") for c in compounds if isinstance(c, dict) + ] + return ", ".join(compound_names) if compound_names else "unspecified" + + return "unspecified" + + +@dataclass +class SampleNode: + """ + Represents a single sample with flattened experimental condition metadata. + + A sample is uniquely identified by (repo_id, config_name, sample_id) and contains + flattened properties from the 3-level experimental conditions hierarchy plus + selected metadata field values. + + Attributes: + sample_id: Unique identifier within config + repo_id: Dataset repository (e.g., "BrentLab/harbison_2004") + config_name: Configuration name (e.g., "harbison_2004") + properties: Flattened experimental condition properties + metadata_fields: Selected data fields (regulator, target, etc.) + property_sources: Tracks which level each property came from (repo/config/field/missing) + """ + + sample_id: str + repo_id: str + config_name: str + properties: dict[str, Any] = field(default_factory=dict) + metadata_fields: dict[str, Any] = field(default_factory=dict) + property_sources: dict[str, str] = field(default_factory=dict) + + def global_id(self) -> str: + """ + Generate unique identifier across all datasets. + + Format: {repo_id}:{config_name}:{sample_id} + + :return: Global sample ID + """ + return f"{self.repo_id}:{self.config_name}:{self.sample_id}" + + def get_property(self, key: str, default: Any = None) -> Any: + """ + Get property value with default fallback. + + :param key: Property name + :param default: Default value if property not found + :return: Property value or default + """ + return self.properties.get(key, default) + + def __repr__(self) -> str: + """String representation showing global ID and property count.""" + return f"SampleNode({self.global_id()}, {len(self.properties)} properties)" + + +class ConditionFlattener: + """ + Flattens experimental conditions from 3-level hierarchy into node properties. + + Handles heterogeneity in experimental condition structures across datasets: + - Variable nesting depths + - Missing/optional fields + - Compound lists + - Multi-level hierarchical overrides + + Resolution order: repo-level -> config-level -> field-level (field overrides all) + + This flattener dynamically discovers all properties without hardcoded schemas. + It recursively flattens nested structures using dot notation for keys. + """ + + @staticmethod + def _flatten_dict(d: dict, parent_key: str = "", sep: str = ".") -> dict[str, Any]: + """ + Recursively flatten nested dict using dot notation. + + :param d: Dictionary to flatten + :param parent_key: Parent key for recursion + :param sep: Separator for nested keys + :return: Flattened dict with dot-notation keys + """ + items = [] + for k, v in d.items(): + new_key = f"{parent_key}{sep}{k}" if parent_key else k + + if isinstance(v, dict): + # Recursively flatten nested dicts + items.extend( + ConditionFlattener._flatten_dict(v, new_key, sep=sep).items() + ) + elif isinstance(v, list) and v and isinstance(v[0], dict): + # Handle list of dicts (like compound lists) + # Store both flattened string and structured version + items.append((new_key, flatten_compound_list(v))) + items.append((f"_{new_key}_structured", v)) + else: + # Store primitive values as-is + items.append((new_key, v)) + + return dict(items) + + @classmethod + def flatten_conditions( + cls, + repo_conditions: dict | None, + config_conditions: dict | None, + field_conditions: dict | None, + ) -> tuple[dict[str, Any], dict[str, str]]: + """ + Flatten 3-level hierarchy into properties dict. + + Dynamically discovers and flattens all properties without hardcoded schemas. + Nested structures are flattened using dot notation (e.g., "media.carbon_source"). + + :param repo_conditions: Top-level experimental_conditions + :param config_conditions: Config-level experimental_conditions + :param field_conditions: Field-level condition definition + :return: (properties, property_sources) tuple + """ + properties = {} + sources = {} + + # Flatten each level + levels = [ + (repo_conditions, "repo"), + (config_conditions, "config"), + (field_conditions, "field"), + ] + + for conditions, level_name in levels: + if conditions is None: + continue + + # Flatten this level + flattened = cls._flatten_dict(conditions) + + # Merge into properties (later levels override earlier) + for key, value in flattened.items(): + properties[key] = value + sources[key] = level_name + + return properties, sources + + +class SampleNodeCollection: + """ + In-memory collection of sample nodes with efficient indexing. + + Storage strategy: + - Primary index: {(repo_id, config_name): {sample_id: SampleNode}} + - Enables fast dataset-level and cross-dataset operations + - Memory-efficient for typical workloads (1K-100K samples) + + Attributes: + _nodes: Two-level dict storing all nodes + """ + + def __init__(self): + """Initialize empty collection.""" + self._nodes: dict[tuple[str, str], dict[str, SampleNode]] = {} + + def add_node(self, node: SampleNode): + """ + Add sample node to collection. + + :param node: SampleNode to add + """ + key = (node.repo_id, node.config_name) + if key not in self._nodes: + self._nodes[key] = {} + self._nodes[key][node.sample_id] = node + + def get_node( + self, repo_id: str, config_name: str, sample_id: str + ) -> SampleNode | None: + """ + Get specific node. + + :param repo_id: Repository ID + :param config_name: Config name + :param sample_id: Sample ID + :return: SampleNode or None if not found + """ + key = (repo_id, config_name) + if key not in self._nodes: + return None + return self._nodes[key].get(sample_id) + + def get_node_by_global_id(self, global_id: str) -> SampleNode | None: + """ + Get node by global ID. + + :param global_id: Global ID in format {repo_id}:{config_name}:{sample_id} + :return: SampleNode or None if not found + """ + parts = global_id.split(":", 2) + if len(parts) != 3: + return None + return self.get_node(parts[0], parts[1], parts[2]) + + def iter_dataset_nodes( + self, repo_id: str, config_name: str + ) -> Iterator[SampleNode]: + """ + Iterate over nodes in specific dataset. + + :param repo_id: Repository ID + :param config_name: Config name + :return: Iterator over SampleNodes + """ + key = (repo_id, config_name) + if key in self._nodes: + yield from self._nodes[key].values() + + def iter_all_nodes(self) -> Iterator[SampleNode]: + """ + Iterate over all nodes in collection. + + :return: Iterator over all SampleNodes + """ + for dataset_nodes in self._nodes.values(): + yield from dataset_nodes.values() + + def get_dataset_keys(self) -> list[tuple[str, str]]: + """ + Get list of loaded (repo_id, config_name) pairs. + + :return: List of dataset keys + """ + return list(self._nodes.keys()) + + def count_total_nodes(self) -> int: + """ + Count total nodes in collection. + + :return: Total number of nodes + """ + return sum(len(nodes) for nodes in self._nodes.values()) + + def count_dataset_nodes(self, repo_id: str, config_name: str) -> int: + """ + Count nodes in specific dataset. + + :param repo_id: Repository ID + :param config_name: Config name + :return: Number of nodes in dataset + """ + key = (repo_id, config_name) + return len(self._nodes.get(key, {})) + + +@dataclass +class ActiveSet: + """ + Represents a collection of active samples with provenance tracking. + + ActiveSet supports set operations (union, intersection, difference) and + maintains metadata about how the set was created. This enables building + complex filters incrementally and tracking analysis provenance. + + Attributes: + sample_ids: Set of global sample IDs + name: Optional name for this set + description: Optional description + source_filter: Filter query that created this set + created_at: Timestamp + parent_set: ID of parent set (for tracking lineage) + """ + + sample_ids: set[str] = field(default_factory=set) + name: str | None = None + description: str | None = None + source_filter: dict | None = None + created_at: datetime = field(default_factory=datetime.now) + parent_set: str | None = None + + def __len__(self) -> int: + """Return number of samples in set.""" + return len(self.sample_ids) + + def union(self, other: ActiveSet, name: str | None = None) -> ActiveSet: + """ + Create new set with samples from both sets. + + :param other: Another ActiveSet + :param name: Optional name for new set + :return: New ActiveSet with union + """ + return ActiveSet( + sample_ids=self.sample_ids | other.sample_ids, + name=name or f"{self.name}_union_{other.name}", + description=f"Union of {self.name} and {other.name}", + ) + + def intersection(self, other: ActiveSet, name: str | None = None) -> ActiveSet: + """ + Create new set with samples in both sets. + + :param other: Another ActiveSet + :param name: Optional name for new set + :return: New ActiveSet with intersection + """ + return ActiveSet( + sample_ids=self.sample_ids & other.sample_ids, + name=name or f"{self.name}_intersect_{other.name}", + description=f"Intersection of {self.name} and {other.name}", + ) + + def difference(self, other: ActiveSet, name: str | None = None) -> ActiveSet: + """ + Create new set with samples in this set but not other. + + :param other: Another ActiveSet + :param name: Optional name for new set + :return: New ActiveSet with difference + """ + return ActiveSet( + sample_ids=self.sample_ids - other.sample_ids, + name=name or f"{self.name}_minus_{other.name}", + description=f"Samples in {self.name} but not {other.name}", + ) + + def to_sample_ids(self) -> list[str]: + """ + Export as list of global sample IDs. + + :return: Sorted list of global IDs + """ + return sorted(self.sample_ids) + + def __repr__(self) -> str: + """String representation showing name and size.""" + return f"ActiveSet(name={self.name}, size={len(self)})" + + +class SampleFilter: + """ + Evaluate filter expressions against sample nodes. + + Implements MongoDB-style query language for filtering heterogeneous sample data. + + Supported operators: + - $eq, $ne: Equality/inequality (default is $eq) + - $gt, $gte, $lt, $lte: Numeric comparisons + - $in, $nin: List membership + - $contains: String/list containment (case-insensitive) + - $exists: Field presence check + - $and, $or: Logical operators + + Example queries: + {"temperature_celsius": 30} # Simple equality + {"carbon_source": {"$contains": "glucose"}} # Contains check + {"$and": [{"temp": {"$gte": 25}}, {"temp": {"$lte": 35}}]} # Range + """ + + @staticmethod + def _matches_operator(value: Any, operator: str, target: Any) -> bool: + """ + Check if value matches operator condition. + + :param value: Actual property value + :param operator: Operator string (e.g., "$eq", "$contains") + :param target: Target value to compare against + :return: True if condition matches + """ + # Handle None values + if value is None: + if operator == "$exists": + return not target # $exists: false matches None + return operator == "$eq" and target is None + + # Equality operators + if operator == "$eq": + return value == target + if operator == "$ne": + return value != target + + # Numeric comparisons + if operator in ["$gt", "$gte", "$lt", "$lte"]: + try: + value_num = ( + float(value) if not isinstance(value, (int, float)) else value + ) + target_num = ( + float(target) if not isinstance(target, (int, float)) else target + ) + if operator == "$gt": + return value_num > target_num + if operator == "$gte": + return value_num >= target_num + if operator == "$lt": + return value_num < target_num + if operator == "$lte": + return value_num <= target_num + except (ValueError, TypeError): + return False + + # List membership + if operator == "$in": + return value in target if isinstance(target, (list, set, tuple)) else False + if operator == "$nin": + return ( + value not in target if isinstance(target, (list, set, tuple)) else True + ) + + # Contains check (case-insensitive for strings) + if operator == "$contains": + if isinstance(value, str) and isinstance(target, str): + return target.lower() in value.lower() + if isinstance(value, (list, tuple)): + return target in value + return False + + # Existence check + if operator == "$exists": + return bool(target) # $exists: true matches any non-None value + + return False + + @classmethod + def _matches_condition(cls, node: SampleNode, field: str, condition: Any) -> bool: + """ + Check if node matches a single field condition. + + :param node: SampleNode to check + :param field: Property name + :param condition: Condition value or operator dict + :return: True if matches + """ + # Get value from node + value = node.get_property(field) + + # Simple equality check + if not isinstance(condition, dict): + return value == condition + + # Operator-based checks + for operator, target in condition.items(): + if not cls._matches_operator(value, operator, target): + return False + + return True + + @classmethod + def matches(cls, node: SampleNode, query: dict) -> bool: + """ + Check if node matches filter query. + + :param node: SampleNode to check + :param query: Filter query dict + :return: True if node matches all conditions + """ + # Handle logical operators + if "$and" in query: + return all(cls.matches(node, sub_query) for sub_query in query["$and"]) + + if "$or" in query: + return any(cls.matches(node, sub_query) for sub_query in query["$or"]) + + # Check all field conditions (implicit AND) + for field, condition in query.items(): + if field.startswith("$"): # Skip logical operators + continue + if not cls._matches_condition(node, field, condition): + return False + + return True + + @classmethod + def filter_nodes( + cls, nodes: Iterator[SampleNode], query: dict + ) -> Iterator[SampleNode]: + """ + Filter nodes matching query (generator for memory efficiency). + + :param nodes: Iterator of SampleNodes + :param query: Filter query dict + :return: Iterator of matching nodes + """ + for node in nodes: + if cls.matches(node, query): + yield node + + +class SampleManager: + """ + Main interface for node-based sample management. + + SampleManager provides a flexible, NoSQL-inspired approach to managing samples + from multiple heterogeneous datasets. It replaces the table-based MetadataManager + with a node-based system that handles varying experimental condition structures. + + Key Features: + - Load samples from DataCard or DuckDB + - Filter using MongoDB-style queries + - Create and manage ActiveSets + - Export to DuckDB for SQL analysis + - Handle multi-dataset scenarios + + Example: + >>> manager = SampleManager() + >>> manager.load_from_datacard("BrentLab/harbison_2004", "harbison_2004") + >>> active = manager.filter_all({"carbon_source": {"$contains": "glucose"}}) + >>> print(f"Found {len(active)} samples") + """ + + def __init__( + self, + duckdb_conn: duckdb.DuckDBPyConnection | None = None, + cache_dir: Path | None = None, + ): + """ + Initialize SampleManager. + + :param duckdb_conn: Optional shared DuckDB connection for integration with HfQueryAPI + :param cache_dir: Optional cache directory for DataCard fetches + """ + self._collection = SampleNodeCollection() + self._active_sets: dict[str, ActiveSet] = {} + self._duckdb_conn = ( + duckdb_conn if duckdb_conn is not None else duckdb.connect(":memory:") + ) + self._cache_dir = cache_dir + self._flattener = ConditionFlattener() + + def get_active_configs(self) -> list[tuple[str, str]]: + """ + Get list of loaded (repo_id, config_name) pairs. + + :return: List of dataset keys + """ + return self._collection.get_dataset_keys() + + def get_summary(self) -> pd.DataFrame: + """ + Get summary of loaded samples. + + Returns DataFrame with columns: + - repo_id: Repository ID + - config_name: Configuration name + - sample_count: Number of samples + - properties: Common properties available + + :return: Summary DataFrame + """ + rows = [] + for repo_id, config_name in self.get_active_configs(): + count = self._collection.count_dataset_nodes(repo_id, config_name) + # Sample first node to get property names + first_node = next( + self._collection.iter_dataset_nodes(repo_id, config_name), None + ) + properties = list(first_node.properties.keys()) if first_node else [] + + rows.append( + { + "repo_id": repo_id, + "config_name": config_name, + "sample_count": count, + "properties": properties, + } + ) + + return pd.DataFrame(rows) + + def filter_all(self, query: dict, name: str | None = None) -> ActiveSet: + """ + Filter across all loaded samples. + + :param query: Filter query dict (MongoDB-style) + :param name: Optional name for ActiveSet + :return: ActiveSet with matching samples + """ + matching_nodes = SampleFilter.filter_nodes( + self._collection.iter_all_nodes(), query + ) + sample_ids = {node.global_id() for node in matching_nodes} + + return ActiveSet( + sample_ids=sample_ids, + name=name or "filtered_samples", + source_filter=query, + ) + + def filter_dataset( + self, + repo_id: str, + config_name: str, + query: dict, + name: str | None = None, + ) -> ActiveSet: + """ + Filter samples within specific dataset. + + :param repo_id: Repository ID + :param config_name: Config name + :param query: Filter query dict + :param name: Optional name for ActiveSet + :return: ActiveSet with matching samples + """ + matching_nodes = SampleFilter.filter_nodes( + self._collection.iter_dataset_nodes(repo_id, config_name), query + ) + sample_ids = {node.global_id() for node in matching_nodes} + + return ActiveSet( + sample_ids=sample_ids, + name=name or f"{config_name}_filtered", + source_filter=query, + ) + + def get_sample(self, global_id: str) -> SampleNode | None: + """ + Retrieve specific sample by global ID. + + :param global_id: Global ID in format {repo_id}:{config_name}:{sample_id} + :return: SampleNode or None if not found + """ + return self._collection.get_node_by_global_id(global_id) + + def get_samples_by_ids(self, sample_ids: list[str]) -> list[SampleNode]: + """ + Batch retrieve samples. + + :param sample_ids: List of global IDs + :return: List of SampleNodes (may be shorter if some not found) + """ + nodes = [] + for global_id in sample_ids: + node = self.get_sample(global_id) + if node is not None: + nodes.append(node) + return nodes + + def save_active_set(self, name: str, active_set: ActiveSet): + """ + Save named active set for later use. + + :param name: Name to save under + :param active_set: ActiveSet to save + """ + self._active_sets[name] = active_set + + def get_active_set(self, name: str) -> ActiveSet | None: + """ + Retrieve saved active set. + + :param name: Name of saved set + :return: ActiveSet or None if not found + """ + return self._active_sets.get(name) + + def list_active_sets(self) -> list[str]: + """ + List all saved active set names. + + :return: List of set names + """ + return list(self._active_sets.keys()) + + def get_property_distribution( + self, + property_name: str, + dataset_filter: tuple[str, str] | None = None, + ) -> dict[Any, int]: + """ + Get value distribution for a property. + + :param property_name: Property name to analyze + :param dataset_filter: Optional (repo_id, config_name) to limit to specific dataset + :return: Dict mapping values to counts + """ + distribution: dict[Any, int] = {} + + if dataset_filter: + nodes = self._collection.iter_dataset_nodes( + dataset_filter[0], dataset_filter[1] + ) + else: + nodes = self._collection.iter_all_nodes() + + for node in nodes: + value = node.get_property(property_name, "missing") + distribution[value] = distribution.get(value, 0) + 1 + + return distribution + + def __repr__(self) -> str: + """String representation showing loaded datasets and sample count.""" + total = self._collection.count_total_nodes() + datasets = len(self.get_active_configs()) + return f"SampleManager({datasets} datasets, {total} samples)" diff --git a/tfbpapi/tests/datainfo/huggingface_collection_datacards.txt b/tfbpapi/tests/datainfo/huggingface_collection_datacards.txt index d5d3394..68d5482 100644 --- a/tfbpapi/tests/datainfo/huggingface_collection_datacards.txt +++ b/tfbpapi/tests/datainfo/huggingface_collection_datacards.txt @@ -13,18 +13,17 @@ pretty_name: Barkai ChEC-seq Compendium size_categories: - 100M- @@ -1611,36 +1582,35 @@ configs: - condition - regulator_locus_tag experimental_conditions: - environmental_conditions: - # Mahendrawada et al 2025: "30 °C culture" - temperature_celsius: 30 - cultivation_method: unspecified - growth_phase_at_harvest: - # Mahendrawada et al 2025: "A600 of ~1.0" - od600: 1.0 - media: - # Mahendrawada et al 2025: "synthetic complete (SC) media" - name: synthetic_complete - carbon_source: unspecified - nitrogen_source: - - compound: yeast_nitrogen_base - # Mahendrawada et al 2025: 1.7 g/L (without ammonium sulfate or amino acids (BD Difco)) - concentration_percent: 0.17 - specifications: - - without_ammonium_sulfate - - without_amino_acids - - compound: ammonium_sulfate - # Mahendrawada et al 2025: 5 g/L - concentration_percent: 0.5 - - compound: amino_acid_dropout_mix - # Mahendrawada et al 2025: 0.6 g/L - concentration_percent: 0.06 - - compound: adenine_sulfate - # Mahendrawada et al 2025: 40 μg/ml = 0.04 g/L - concentration_percent: 0.004 - - compound: uracil - # Mahendrawada et al 2025: 2 μg/ml = 0.002 g/L - concentration_percent: 0.0002 + # Mahendrawada et al 2025: "30 °C culture" + temperature_celsius: 30 + cultivation_method: unspecified + growth_phase_at_harvest: + # Mahendrawada et al 2025: "A600 of ~1.0" + od600: 1.0 + media: + # Mahendrawada et al 2025: "synthetic complete (SC) media" + name: synthetic_complete + carbon_source: unspecified + nitrogen_source: + - compound: yeast_nitrogen_base + # Mahendrawada et al 2025: 1.7 g/L (without ammonium sulfate or amino acids (BD Difco)) + concentration_percent: 0.17 + specifications: + - without_ammonium_sulfate + - without_amino_acids + - compound: ammonium_sulfate + # Mahendrawada et al 2025: 5 g/L + concentration_percent: 0.5 + - compound: amino_acid_dropout_mix + # Mahendrawada et al 2025: 0.6 g/L + concentration_percent: 0.06 + - compound: adenine_sulfate + # Mahendrawada et al 2025: 40 μg/ml = 0.04 g/L + concentration_percent: 0.004 + - compound: uracil + # Mahendrawada et al 2025: 2 μg/ml = 0.002 g/L + concentration_percent: 0.0002 data_files: - split: train path: reprocess_diffcontrol_5prime.parquet @@ -1731,22 +1701,21 @@ language: - en pretty_name: Rossi ChIP-exo 2021 experimental_conditions: - environmental_conditions: - temperature_celsius: 25 - cultivation_method: unspecified - growth_phase_at_harvest: - phase: mid_log - od600: 0.8 - media: - name: yeast_peptone_dextrose - carbon_source: - - compound: D-glucose - concentration_percent: unspecified - nitrogen_source: - - compound: yeast_extract - concentration_percent: unspecified - - compound: peptone - concentration_percent: unspecified + temperature_celsius: 25 + cultivation_method: unspecified + growth_phase_at_harvest: + phase: mid_log + od600: 0.8 + media: + name: yeast_peptone_dextrose + carbon_source: + - compound: D-glucose + concentration_percent: unspecified + nitrogen_source: + - compound: yeast_extract + concentration_percent: unspecified + - compound: peptone + concentration_percent: unspecified # Heat shock applied only to SAGA strains # note that im not sure which strains this diff --git a/tfbpapi/tests/datainfo/test_datacard.py b/tfbpapi/tests/datainfo/test_datacard.py index 098d27f..1e061ee 100644 --- a/tfbpapi/tests/datainfo/test_datacard.py +++ b/tfbpapi/tests/datainfo/test_datacard.py @@ -615,3 +615,161 @@ def test_extract_partition_values_fetch_error( # Should return empty set on error assert values == set() + + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_get_field_attribute( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + ): + """Test extracting specific attributes from field definitions.""" + # Create sample card data with condition definitions + card_data = { + "configs": [ + { + "config_name": "test_config", + "description": "Test configuration", + "dataset_type": "annotated_features", + "data_files": [{"split": "train", "path": "test.parquet"}], + "dataset_info": { + "features": [ + { + "name": "condition", + "dtype": "string", + "description": "Experimental condition", + "role": "experimental_condition", + "definitions": { + "YPD": { + "media": { + "name": "YPD", + "carbon_source": [ + { + "compound": "D-glucose", + "concentration_percent": 2, + } + ], + "nitrogen_source": [ + { + "compound": "yeast_extract", + "concentration_percent": 1, + }, + { + "compound": "peptone", + "concentration_percent": 2, + }, + ], + }, + "temperature_celsius": 30, + }, + "HEAT": { + "media": { + "name": "YPD", + "carbon_source": [ + { + "compound": "D-glucose", + "concentration_percent": 2, + } + ], + }, + "temperature_celsius": 37, + }, + "SM": { + "media": { + "name": "synthetic_complete", + "carbon_source": "unspecified", + "nitrogen_source": "unspecified", + } + }, + }, + } + ] + }, + } + ] + } + + mock_card_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_card_fetcher_instance + mock_card_fetcher_instance.fetch.return_value = card_data + + datacard = DataCard(test_repo_id) + + # Test extracting media attribute + media_specs = datacard.get_field_attribute( + "test_config", "condition", "media" + ) + + assert "YPD" in media_specs + assert "HEAT" in media_specs + assert "SM" in media_specs + + # Check YPD media specification + assert media_specs["YPD"]["name"] == "YPD" + assert len(media_specs["YPD"]["carbon_source"]) == 1 + assert media_specs["YPD"]["carbon_source"][0]["compound"] == "D-glucose" + assert len(media_specs["YPD"]["nitrogen_source"]) == 2 + + # Check HEAT media specification + assert media_specs["HEAT"]["name"] == "YPD" + assert len(media_specs["HEAT"]["carbon_source"]) == 1 + + # Check SM media specification + assert media_specs["SM"]["name"] == "synthetic_complete" + assert media_specs["SM"]["carbon_source"] == "unspecified" + + # Test extracting temperature attribute + temp_specs = datacard.get_field_attribute( + "test_config", "condition", "temperature_celsius" + ) + + assert temp_specs["YPD"] == 30 + assert temp_specs["HEAT"] == 37 + assert temp_specs["SM"] == "unspecified" # SM doesn't have temperature + + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_get_field_attribute_invalid_config( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + minimal_dataset_card_data, + ): + """Test get_field_attribute with invalid config name.""" + mock_card_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_card_fetcher_instance + mock_card_fetcher_instance.fetch.return_value = minimal_dataset_card_data + + datacard = DataCard(test_repo_id) + + with pytest.raises(DataCardError, match="Configuration 'invalid' not found"): + datacard.get_field_attribute("invalid", "condition", "media") + + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_get_field_attribute_invalid_field( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + minimal_dataset_card_data, + ): + """Test get_field_attribute with invalid field name.""" + mock_card_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_card_fetcher_instance + mock_card_fetcher_instance.fetch.return_value = minimal_dataset_card_data + + datacard = DataCard(test_repo_id) + + with pytest.raises( + DataCardError, match="Field 'invalid_field' not found in config" + ): + datacard.get_field_attribute("test_config", "invalid_field", "media") diff --git a/tfbpapi/tests/datainfo/test_metadata_manager.py b/tfbpapi/tests/datainfo/test_metadata_manager.py index 887a599..ad03c5a 100644 --- a/tfbpapi/tests/datainfo/test_metadata_manager.py +++ b/tfbpapi/tests/datainfo/test_metadata_manager.py @@ -4,7 +4,6 @@ import pytest from tfbpapi.datainfo import DataCard, MetadataManager -from tfbpapi.datainfo.models import FieldRole class TestMetadataManagerBasic: @@ -68,52 +67,23 @@ def test_sanitize_view_name(self): assert "/" not in view_name assert "-" not in view_name - def test_format_compound_simple(self): - """Test formatting compound as simple string.""" - mgr = MetadataManager() - result = mgr._format_compound("carbon_source", "D-glucose") - assert result == "carbon_source:D-glucose" - - def test_format_compound_with_percent(self): - """Test formatting compound with concentration percent.""" - mgr = MetadataManager() - compound = {"name": "D-glucose", "concentration_percent": 2.0} - result = mgr._format_compound("carbon_source", compound) - assert result == "carbon_source:D-glucose@2.0%" - - def test_format_compound_with_grams(self): - """Test formatting compound with g/L concentration.""" - mgr = MetadataManager() - compound = {"name": "ammonium_sulfate", "concentration_g_per_l": 5.0} - result = mgr._format_compound("nitrogen_source", compound) - assert result == "nitrogen_source:ammonium_sulfate@5.0g/L" - def test_flatten_condition_definition_empty(self): """Test flattening empty definition.""" mgr = MetadataManager() result = mgr._flatten_condition_definition({}) - assert result["growth_media"] == "unspecified" - assert result["components"] == "" + assert isinstance(result, dict) + assert len(result) == 0 def test_flatten_condition_definition_with_media(self): - """Test flattening definition with media.""" + """Test flattening definition with media name.""" mgr = MetadataManager() definition = { - "environmental_conditions": { - "media": { - "name": "YPD", - "carbon_source": [ - {"name": "D-glucose", "concentration_percent": 2.0} - ], - "nitrogen_source": ["yeast_extract", "peptone"], - } + "media": { + "name": "YPD", } } result = mgr._flatten_condition_definition(definition) assert result["growth_media"] == "YPD" - assert "carbon_source:D-glucose@2.0%" in result["components"] - assert "nitrogen_source:yeast_extract" in result["components"] - assert "nitrogen_source:peptone" in result["components"] class TestComponentSeparators: @@ -138,7 +108,6 @@ def test_flatten_experimental_conditions_empty(self): # Create a simple mock object with no conditions class MockExpConditions: - environmental_conditions = None strain_background = None result = mgr._flatten_experimental_conditions(MockExpConditions()) @@ -149,18 +118,8 @@ def test_flatten_experimental_conditions_with_temperature(self): """Test flattening conditions with temperature.""" mgr = MetadataManager() - class MockEnv: - temperature_celsius = 30 - cultivation_method = None - media = None - growth_phase = None - chemical_treatments = None - drug_treatments = None - heat_treatment = None - induction = None - class MockExpConditions: - environmental_conditions = MockEnv() + temperature_celsius = 30 strain_background = None result = mgr._flatten_experimental_conditions(MockExpConditions()) @@ -170,18 +129,9 @@ def test_flatten_experimental_conditions_with_cultivation_method(self): """Test flattening conditions with cultivation method.""" mgr = MetadataManager() - class MockEnv: + class MockExpConditions: temperature_celsius = None cultivation_method = "chemostat" - media = None - growth_phase = None - chemical_treatments = None - drug_treatments = None - heat_treatment = None - induction = None - - class MockExpConditions: - environmental_conditions = MockEnv() strain_background = None result = mgr._flatten_experimental_conditions(MockExpConditions()) @@ -191,47 +141,21 @@ def test_flatten_experimental_conditions_with_media(self): """Test flattening conditions with media information.""" mgr = MetadataManager() - class MockCompound: - compound = "D-glucose" - concentration_percent = 1.0 - - def model_dump(self): - return { - "name": self.compound, - "concentration_percent": self.concentration_percent, - } - class MockMedia: name = "minimal" - carbon_source = [MockCompound()] - nitrogen_source = None - phosphate_source = None - additives = None - - class MockEnv: - temperature_celsius = None - cultivation_method = None - media = MockMedia() - growth_phase = None - chemical_treatments = None - drug_treatments = None - heat_treatment = None - induction = None class MockExpConditions: - environmental_conditions = MockEnv() + media = MockMedia() strain_background = None result = mgr._flatten_experimental_conditions(MockExpConditions()) assert result["growth_media"] == "minimal" - assert "carbon_source:D-glucose@1.0%" in result["components"] def test_flatten_experimental_conditions_with_strain_background(self): """Test flattening conditions with strain background.""" mgr = MetadataManager() class MockExpConditions: - environmental_conditions = None strain_background = "BY4741" result = mgr._flatten_experimental_conditions(MockExpConditions()) diff --git a/tfbpapi/tests/datainfo/test_models.py b/tfbpapi/tests/datainfo/test_models.py index 8bcee9b..d4701e4 100644 --- a/tfbpapi/tests/datainfo/test_models.py +++ b/tfbpapi/tests/datainfo/test_models.py @@ -1,806 +1,569 @@ -"""Tests for datainfo Pydantic models.""" +""" +Tests for datainfo Pydantic models. + +These tests validate the minimal, flexible models that parse HuggingFace dataset cards. + +""" import pytest from pydantic import ValidationError from tfbpapi.datainfo.models import ( - ChemicalTreatmentInfo, - CompoundInfo, DataFileInfo, DatasetCard, DatasetConfig, DatasetInfo, DatasetType, - EnvironmentalConditions, - ExperimentalConditions, ExtractedMetadata, FeatureInfo, - GrowthPhaseInfo, - HeatTreatmentInfo, - MediaInfo, MetadataRelationship, PartitioningInfo, ) class TestDatasetType: - """Test DatasetType enum.""" + """Tests for DatasetType enum.""" def test_dataset_type_values(self): - """Test all dataset type enum values.""" + """Test that all expected dataset types are defined.""" assert DatasetType.GENOMIC_FEATURES == "genomic_features" assert DatasetType.ANNOTATED_FEATURES == "annotated_features" assert DatasetType.GENOME_MAP == "genome_map" assert DatasetType.METADATA == "metadata" + assert DatasetType.QC_DATA == "qc_data" def test_dataset_type_from_string(self): """Test creating DatasetType from string.""" - assert DatasetType("genomic_features") == DatasetType.GENOMIC_FEATURES - assert DatasetType("annotated_features") == DatasetType.ANNOTATED_FEATURES - assert DatasetType("genome_map") == DatasetType.GENOME_MAP + dt = DatasetType("genomic_features") + assert dt == DatasetType.GENOMIC_FEATURES def test_invalid_dataset_type(self): - """Test invalid dataset type raises error.""" + """Test that invalid dataset type raises error.""" with pytest.raises(ValueError): DatasetType("invalid_type") -class TestCompoundInfo: - """Test CompoundInfo model.""" - - def test_compound_info_with_percentage(self): - """Test CompoundInfo with percentage concentration.""" - compound = CompoundInfo(compound="glucose", concentration_percent=2.0) - assert compound.compound == "glucose" - assert compound.concentration_percent == 2.0 - assert compound.concentration_g_per_l is None - assert compound.specifications is None - - def test_compound_info_with_g_per_l(self): - """Test CompoundInfo with g/L concentration.""" - compound = CompoundInfo(compound="dextrose", concentration_g_per_l=20.0) - assert compound.compound == "dextrose" - assert compound.concentration_g_per_l == 20.0 - assert compound.concentration_percent is None - - def test_compound_info_with_specifications(self): - """Test CompoundInfo with specifications.""" - compound = CompoundInfo( - compound="yeast_nitrogen_base", - concentration_g_per_l=6.7, - specifications=["without_amino_acids"], - ) - assert compound.compound == "yeast_nitrogen_base" - assert compound.specifications == ["without_amino_acids"] - - def test_compound_info_minimal(self): - """Test CompoundInfo with only required fields.""" - compound = CompoundInfo(compound="ethanol") - assert compound.compound == "ethanol" - assert compound.concentration_percent is None - assert compound.concentration_g_per_l is None - - -class TestMediaInfo: - """Test MediaInfo model.""" - - def test_media_info_minimal(self): - """Test MediaInfo with required fields only.""" - media = MediaInfo( - name="minimal", - carbon_source=[CompoundInfo(compound="glucose", concentration_percent=2.0)], - nitrogen_source=[ - CompoundInfo(compound="ammonium_sulfate", concentration_g_per_l=5.0) - ], - ) - assert media.name == "minimal" - assert len(media.carbon_source) == 1 - assert len(media.nitrogen_source) == 1 - assert media.phosphate_source is None - - def test_media_info_with_phosphate(self): - """Test MediaInfo with phosphate source.""" - media = MediaInfo( - name="synthetic_complete", - carbon_source=[CompoundInfo(compound="glucose", concentration_percent=2.0)], - nitrogen_source=[ - CompoundInfo(compound="yeast_nitrogen_base", concentration_g_per_l=6.7) - ], - phosphate_source=[ - CompoundInfo(compound="potassium_phosphate", concentration_g_per_l=1.0) - ], - ) - assert media.phosphate_source is not None - assert len(media.phosphate_source) == 1 - - def test_media_info_multiple_compounds(self): - """Test MediaInfo with multiple carbon/nitrogen sources.""" - media = MediaInfo( - name="YPD", - carbon_source=[ - CompoundInfo(compound="glucose", concentration_percent=2.0), - CompoundInfo(compound="glycerol", concentration_percent=3.0), - ], - nitrogen_source=[ - CompoundInfo(compound="yeast_extract", concentration_percent=1.0), - CompoundInfo(compound="peptone", concentration_percent=2.0), - ], - ) - assert len(media.carbon_source) == 2 - assert len(media.nitrogen_source) == 2 - - -class TestGrowthPhaseInfo: - """Test GrowthPhaseInfo model.""" - - def test_growth_phase_with_od600(self): - """Test GrowthPhaseInfo with OD600.""" - phase = GrowthPhaseInfo(od600=0.5, stage="mid_log_phase") - assert phase.od600 == 0.5 - assert phase.stage == "mid_log_phase" - - def test_growth_phase_od600_only(self): - """Test GrowthPhaseInfo with only OD600.""" - phase = GrowthPhaseInfo(od600=0.8) - assert phase.od600 == 0.8 - assert phase.stage is None - - def test_growth_phase_stage_only(self): - """Test GrowthPhaseInfo with only stage.""" - phase = GrowthPhaseInfo(stage="stationary_phase") - assert phase.stage == "stationary_phase" - assert phase.od600 is None - - def test_growth_phase_empty(self): - """Test GrowthPhaseInfo with no values.""" - phase = GrowthPhaseInfo() - assert phase.od600 is None - assert phase.stage is None - - -class TestChemicalTreatmentInfo: - """Test ChemicalTreatmentInfo model.""" - - def test_chemical_treatment_with_percent(self): - """Test ChemicalTreatmentInfo with percentage concentration.""" - treatment = ChemicalTreatmentInfo( - compound="ethanol", - concentration_percent=5.0, - duration_minutes=30, - ) - assert treatment.compound == "ethanol" - assert treatment.concentration_percent == 5.0 - assert treatment.duration_minutes == 30 - assert treatment.concentration_molar is None - - def test_chemical_treatment_with_molar(self): - """Test ChemicalTreatmentInfo with molar concentration.""" - treatment = ChemicalTreatmentInfo( - compound="rapamycin", - concentration_molar=0.0002, - duration_hours=2.0, - ) - assert treatment.compound == "rapamycin" - assert treatment.concentration_molar == 0.0002 - assert treatment.duration_hours == 2.0 - - def test_chemical_treatment_minimal(self): - """Test ChemicalTreatmentInfo with only compound.""" - treatment = ChemicalTreatmentInfo(compound="hydrogen_peroxide") - assert treatment.compound == "hydrogen_peroxide" - assert treatment.concentration_percent is None - assert treatment.duration_minutes is None - - -class TestHeatTreatmentInfo: - """Test HeatTreatmentInfo model.""" - - def test_heat_treatment_basic(self): - """Test HeatTreatmentInfo with required field.""" - treatment = HeatTreatmentInfo(duration_minutes=30) - assert treatment.duration_minutes == 30 - assert treatment.description is None - - def test_heat_treatment_with_description(self): - """Test HeatTreatmentInfo with description.""" - treatment = HeatTreatmentInfo( - duration_minutes=15, description="Heat shock at 37C" - ) - assert treatment.duration_minutes == 15 - assert treatment.description == "Heat shock at 37C" +class TestFeatureInfo: + """Tests for FeatureInfo model.""" - def test_heat_treatment_missing_duration(self): - """Test HeatTreatmentInfo requires duration_minutes.""" - with pytest.raises(ValidationError): - HeatTreatmentInfo(description="Some treatment") - - -class TestEnvironmentalConditions: - """Test EnvironmentalConditions model.""" - - def test_environmental_conditions_minimal(self): - """Test EnvironmentalConditions with no fields.""" - env = EnvironmentalConditions() - assert env.temperature_celsius is None - assert env.cultivation_method is None - assert env.growth_phase_at_harvest is None - assert env.media is None - assert env.chemical_treatment is None - assert env.heat_treatment is None - - def test_environmental_conditions_temperature(self): - """Test EnvironmentalConditions with temperature.""" - env = EnvironmentalConditions(temperature_celsius=30.0) - assert env.temperature_celsius == 30.0 - - def test_environmental_conditions_with_media(self): - """Test EnvironmentalConditions with media info.""" - media = MediaInfo( - name="YPD", - carbon_source=[CompoundInfo(compound="glucose", concentration_percent=2.0)], - nitrogen_source=[ - CompoundInfo(compound="peptone", concentration_percent=2.0) - ], - ) - env = EnvironmentalConditions( - temperature_celsius=30.0, - cultivation_method="batch_culture", - media=media, - ) - assert env.media is not None - assert env.media.name == "YPD" - - def test_environmental_conditions_with_growth_phase(self): - """Test EnvironmentalConditions with growth phase.""" - growth_phase = GrowthPhaseInfo(od600=0.5, stage="mid_log_phase") - env = EnvironmentalConditions(growth_phase_at_harvest=growth_phase) - assert env.growth_phase_at_harvest is not None - assert env.growth_phase_at_harvest.od600 == 0.5 - - def test_environmental_conditions_with_chemical_treatment(self): - """Test EnvironmentalConditions with chemical treatment.""" - treatment = ChemicalTreatmentInfo( - compound="rapamycin", - concentration_molar=0.0002, - duration_minutes=120, - ) - env = EnvironmentalConditions(chemical_treatment=treatment) - assert env.chemical_treatment is not None - assert env.chemical_treatment.compound == "rapamycin" - - def test_environmental_conditions_with_heat_treatment(self): - """Test EnvironmentalConditions with heat treatment.""" - treatment = HeatTreatmentInfo(duration_minutes=30, description="Heat shock") - env = EnvironmentalConditions(heat_treatment=treatment) - assert env.heat_treatment is not None - assert env.heat_treatment.duration_minutes == 30 - - def test_environmental_conditions_complete(self): - """Test EnvironmentalConditions with all fields.""" - media = MediaInfo( - name="minimal", - carbon_source=[CompoundInfo(compound="glucose", concentration_percent=2.0)], - nitrogen_source=[ - CompoundInfo(compound="ammonium_sulfate", concentration_g_per_l=5.0) - ], - ) - growth_phase = GrowthPhaseInfo(od600=0.6, stage="mid_log_phase") - chemical = ChemicalTreatmentInfo( - compound="rapamycin", concentration_molar=0.0002, duration_minutes=60 - ) - heat = HeatTreatmentInfo(duration_minutes=15, description="Brief heat shock") - - env = EnvironmentalConditions( - temperature_celsius=30.0, - cultivation_method="batch_culture", - growth_phase_at_harvest=growth_phase, - media=media, - chemical_treatment=chemical, - heat_treatment=heat, + def test_minimal_feature_info(self): + """Test creating FeatureInfo with minimal fields.""" + feature = FeatureInfo( + name="gene_id", dtype="string", description="Gene identifier" ) + assert feature.name == "gene_id" + assert feature.dtype == "string" + assert feature.description == "Gene identifier" + assert feature.role is None + assert feature.definitions is None - assert env.temperature_celsius == 30.0 - assert env.cultivation_method == "batch_culture" - assert env.growth_phase_at_harvest.od600 == 0.6 - assert env.media.name == "minimal" - assert env.chemical_treatment.compound == "rapamycin" - assert env.heat_treatment.duration_minutes == 15 - - def test_environmental_conditions_extra_fields_allowed(self): - """Test that EnvironmentalConditions allows extra fields.""" - env = EnvironmentalConditions( - temperature_celsius=30.0, custom_field="custom_value" + def test_feature_info_with_role(self): + """Test FeatureInfo with role field.""" + feature = FeatureInfo( + name="condition", + dtype="string", + description="Experimental condition", + role="experimental_condition", ) - assert env.temperature_celsius == 30.0 - - -class TestExperimentalConditions: - """Test ExperimentalConditions model.""" - - def test_experimental_conditions_minimal(self): - """Test ExperimentalConditions with no fields.""" - exp = ExperimentalConditions() - assert exp.environmental_conditions is None - assert exp.strain_background is None + assert feature.role == "experimental_condition" - def test_experimental_conditions_with_strain(self): - """Test ExperimentalConditions with strain background.""" - exp = ExperimentalConditions(strain_background="BY4741") - assert exp.strain_background == "BY4741" - - def test_experimental_conditions_with_environmental(self): - """Test ExperimentalConditions with environmental conditions.""" - env = EnvironmentalConditions( - temperature_celsius=30.0, cultivation_method="batch_culture" - ) - exp = ExperimentalConditions(environmental_conditions=env) - assert exp.environmental_conditions is not None - assert exp.environmental_conditions.temperature_celsius == 30.0 - - def test_experimental_conditions_complete(self): - """Test ExperimentalConditions with all fields.""" - env = EnvironmentalConditions( - temperature_celsius=30.0, - cultivation_method="batch_culture", - media=MediaInfo( - name="YPD", - carbon_source=[ - CompoundInfo(compound="glucose", concentration_percent=2.0) - ], - nitrogen_source=[ - CompoundInfo(compound="peptone", concentration_percent=2.0) - ], - ), - ) - exp = ExperimentalConditions( - environmental_conditions=env, strain_background="BY4741" - ) - assert exp.strain_background == "BY4741" - assert exp.environmental_conditions.temperature_celsius == 30.0 - assert exp.environmental_conditions.media.name == "YPD" - - def test_experimental_conditions_extra_fields_allowed(self): - """Test that ExperimentalConditions allows extra fields.""" - exp = ExperimentalConditions( - strain_background="BY4741", custom_metadata="custom_value" + def test_feature_info_with_definitions(self): + """Test FeatureInfo with definitions for experimental_condition.""" + feature = FeatureInfo( + name="condition", + dtype={"class_label": {"names": ["control", "treated"]}}, + description="Treatment condition", + role="experimental_condition", + definitions={ + "control": {"temperature_celsius": 30}, + "treated": {"temperature_celsius": 37}, + }, + ) + assert feature.definitions is not None + assert "control" in feature.definitions + assert feature.definitions["control"]["temperature_celsius"] == 30 + + def test_feature_info_with_dict_dtype(self): + """Test FeatureInfo with class_label dtype.""" + feature = FeatureInfo( + name="category", + dtype={"class_label": {"names": ["A", "B", "C"]}}, + description="Categorical field", ) - assert exp.strain_background == "BY4741" - - -class TestFeatureInfo: - """Test FeatureInfo model.""" - - def test_valid_feature_info(self, sample_feature_info): - """Test creating valid FeatureInfo.""" - feature = FeatureInfo(**sample_feature_info) - assert feature.name == "gene_symbol" - assert feature.dtype == "string" - assert feature.description == "Standard gene symbol (e.g., HO, GAL1)" - - def test_feature_info_required_fields(self): - """Test that all fields are required.""" - # Test missing name - with pytest.raises(ValidationError): - FeatureInfo(dtype="string", description="test") - - # Test missing dtype - with pytest.raises(ValidationError): - FeatureInfo(name="test", description="test") - - # Test missing description - with pytest.raises(ValidationError): - FeatureInfo(name="test", dtype="string") - - def test_feature_info_serialization(self, sample_feature_info): - """Test FeatureInfo serialization.""" - feature = FeatureInfo(**sample_feature_info) - data = feature.model_dump() - assert data["name"] == "gene_symbol" - assert data["dtype"] == "string" - assert data["description"] == "Standard gene symbol (e.g., HO, GAL1)" - - def test_feature_info_categorical_dtype(self): - """Test FeatureInfo with categorical dtype.""" - categorical_feature_data = { - "name": "mechanism", - "dtype": {"class_label": {"names": ["GEV", "ZEV"]}}, - "description": "Induction system", - } - - feature = FeatureInfo(**categorical_feature_data) - assert feature.name == "mechanism" assert isinstance(feature.dtype, dict) assert "class_label" in feature.dtype - assert feature.dtype["class_label"].names == ["GEV", "ZEV"] - assert feature.get_dtype_summary() == "categorical (2 classes: GEV, ZEV)" - - def test_feature_info_simple_string_dtype(self): - """Test FeatureInfo with simple string dtype.""" - feature = FeatureInfo( - name="test_field", dtype="string", description="Test field" - ) - assert feature.dtype == "string" - assert feature.get_dtype_summary() == "string" - - def test_feature_info_invalid_categorical_dtype(self): - """Test FeatureInfo with invalid categorical dtype structure.""" - with pytest.raises(ValidationError, match="Invalid class_label structure"): - FeatureInfo( - name="test_field", - dtype={"class_label": "invalid"}, # Should be dict with names - description="Test field", - ) - - def test_feature_info_unknown_dtype_structure(self): - """Test FeatureInfo with unknown dtype structure.""" - with pytest.raises(ValidationError, match="Unknown dtype structure"): - FeatureInfo( - name="test_field", - dtype={"unknown_key": "value"}, - description="Test field", - ) class TestPartitioningInfo: - """Test PartitioningInfo model.""" + """Tests for PartitioningInfo model.""" def test_default_partitioning_info(self): - """Test default PartitioningInfo values.""" + """Test PartitioningInfo with defaults.""" partitioning = PartitioningInfo() assert partitioning.enabled is False assert partitioning.partition_by is None assert partitioning.path_template is None - def test_enabled_partitioning_info(self, sample_partitioning_info): - """Test enabled partitioning with all fields.""" - partitioning = PartitioningInfo(**sample_partitioning_info) - assert partitioning.enabled is True - assert partitioning.partition_by == ["regulator", "condition"] - assert ( - partitioning.path_template is not None - and "regulator={regulator}" in partitioning.path_template + def test_enabled_partitioning_info(self): + """Test PartitioningInfo with partitioning enabled.""" + partitioning = PartitioningInfo( + enabled=True, + partition_by=["accession"], + path_template="data/accession={accession}/*.parquet", ) - - def test_partial_partitioning_info(self): - """Test partitioning with only some fields set.""" - partitioning = PartitioningInfo(enabled=True, partition_by=["field1"]) assert partitioning.enabled is True - assert partitioning.partition_by == ["field1"] - assert partitioning.path_template is None + assert partitioning.partition_by == ["accession"] + assert partitioning.path_template == "data/accession={accession}/*.parquet" class TestDataFileInfo: - """Test DataFileInfo model.""" + """Tests for DataFileInfo model.""" def test_default_data_file_info(self): """Test DataFileInfo with default split.""" - data_file = DataFileInfo(path="test.parquet") - assert data_file.split == "train" - assert data_file.path == "test.parquet" - - def test_custom_data_file_info(self, sample_data_file_info): - """Test DataFileInfo with custom values.""" - data_file = DataFileInfo(**sample_data_file_info) + data_file = DataFileInfo(path="data.parquet") assert data_file.split == "train" - assert data_file.path == "genomic_features.parquet" + assert data_file.path == "data.parquet" - def test_data_file_info_required_path(self): - """Test that path is required.""" - with pytest.raises(ValidationError): - DataFileInfo(split="train") + def test_custom_data_file_info(self): + """Test DataFileInfo with custom split.""" + data_file = DataFileInfo(split="test", path="test_data.parquet") + assert data_file.split == "test" + assert data_file.path == "test_data.parquet" class TestDatasetInfo: - """Test DatasetInfo model.""" + """Tests for DatasetInfo model.""" - def test_minimal_dataset_info(self, sample_feature_info): - """Test minimal DatasetInfo with just features.""" - features = [FeatureInfo(**sample_feature_info)] - dataset_info = DatasetInfo(features=features) + def test_minimal_dataset_info(self): + """Test DatasetInfo with minimal features.""" + dataset_info = DatasetInfo( + features=[ + FeatureInfo( + name="gene_id", dtype="string", description="Gene identifier" + ) + ] + ) assert len(dataset_info.features) == 1 assert dataset_info.partitioning is None - def test_dataset_info_with_partitioning( - self, sample_feature_info, sample_partitioning_info - ): + def test_dataset_info_with_partitioning(self): """Test DatasetInfo with partitioning.""" - features = [FeatureInfo(**sample_feature_info)] - partitioning = PartitioningInfo(**sample_partitioning_info) - - dataset_info = DatasetInfo(features=features, partitioning=partitioning) - assert len(dataset_info.features) == 1 - assert ( - dataset_info.partitioning is not None - and dataset_info.partitioning.enabled is True + dataset_info = DatasetInfo( + features=[ + FeatureInfo(name="chr", dtype="string", description="Chromosome"), + FeatureInfo(name="pos", dtype="int32", description="Position"), + ], + partitioning=PartitioningInfo(enabled=True, partition_by=["chr"]), ) - - def test_dataset_info_empty_features_error(self): - """Test that empty features list is allowed.""" - # Pydantic allows empty lists, so this should succeed - dataset_info = DatasetInfo(features=[]) - assert len(dataset_info.features) == 0 + assert len(dataset_info.features) == 2 + assert dataset_info.partitioning.enabled is True class TestDatasetConfig: - """Test DatasetConfig model.""" - - def test_minimal_dataset_config(self, sample_feature_info, sample_data_file_info): - """Test minimal valid DatasetConfig.""" - features = [FeatureInfo(**sample_feature_info)] - data_files = [DataFileInfo(**sample_data_file_info)] - dataset_info = DatasetInfo(features=features) + """Tests for DatasetConfig model.""" + def test_minimal_dataset_config(self): + """Test DatasetConfig with minimal required fields.""" config = DatasetConfig( - config_name="test_config", - description="Test configuration", - dataset_type=DatasetType.GENOMIC_FEATURES, - data_files=data_files, - dataset_info=dataset_info, + config_name="test_data", + description="Test dataset", + dataset_type=DatasetType.ANNOTATED_FEATURES, + data_files=[DataFileInfo(path="data.parquet")], + dataset_info=DatasetInfo( + features=[FeatureInfo(name="id", dtype="string", description="ID")] + ), ) - - assert config.config_name == "test_config" - assert config.dataset_type == DatasetType.GENOMIC_FEATURES + assert config.config_name == "test_data" + assert config.dataset_type == DatasetType.ANNOTATED_FEATURES assert config.default is False assert config.applies_to is None assert config.metadata_fields is None - def test_dataset_config_with_applies_to_metadata( - self, sample_feature_info, sample_data_file_info - ): - """Test DatasetConfig with applies_to for metadata types.""" - features = [FeatureInfo(**sample_feature_info)] - data_files = [DataFileInfo(**sample_data_file_info)] - dataset_info = DatasetInfo(features=features) - + def test_dataset_config_with_applies_to(self): + """Test DatasetConfig with applies_to for metadata.""" config = DatasetConfig( - config_name="metadata_config", - description="Metadata configuration", + config_name="metadata", + description="Metadata", dataset_type=DatasetType.METADATA, - applies_to=["data_config1", "data_config2"], - data_files=data_files, - dataset_info=dataset_info, + applies_to=["data_config_1", "data_config_2"], + data_files=[DataFileInfo(path="metadata.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo( + name="sample_id", dtype="string", description="Sample ID" + ) + ] + ), ) + assert config.applies_to == ["data_config_1", "data_config_2"] - assert config.applies_to == ["data_config1", "data_config2"] - - def test_dataset_config_applies_to_validation_error( - self, sample_feature_info, sample_data_file_info - ): - """Test that applies_to is only valid for metadata types.""" - features = [FeatureInfo(**sample_feature_info)] - data_files = [DataFileInfo(**sample_data_file_info)] - dataset_info = DatasetInfo(features=features) - - with pytest.raises( - ValidationError, - match="applies_to field is only valid for " - "metadata and qc_data dataset types", - ): - DatasetConfig( - config_name="invalid_config", - description="Invalid configuration", - dataset_type=DatasetType.GENOMIC_FEATURES, # Not a metadata type - applies_to=["some_config"], # This should cause validation error - data_files=data_files, - dataset_info=dataset_info, - ) - - def test_dataset_config_empty_metadata_fields_error( - self, sample_feature_info, sample_data_file_info - ): - """Test that empty metadata_fields list raises error.""" - features = [FeatureInfo(**sample_feature_info)] - data_files = [DataFileInfo(**sample_data_file_info)] - dataset_info = DatasetInfo(features=features) - - with pytest.raises( - ValidationError, match="metadata_fields cannot be empty list" - ): + def test_dataset_config_applies_to_validation_error(self): + """Test that applies_to raises error for non-metadata configs.""" + with pytest.raises(ValidationError): DatasetConfig( - config_name="test_config", - description="Test configuration", + config_name="data", + description="Data", dataset_type=DatasetType.ANNOTATED_FEATURES, - metadata_fields=[], # Empty list should cause error - data_files=data_files, - dataset_info=dataset_info, + applies_to=["other_config"], + data_files=[DataFileInfo(path="data.parquet")], + dataset_info=DatasetInfo( + features=[FeatureInfo(name="id", dtype="string", description="ID")] + ), ) - def test_dataset_config_with_metadata_fields( - self, sample_feature_info, sample_data_file_info - ): - """Test DatasetConfig with valid metadata_fields.""" - features = [FeatureInfo(**sample_feature_info)] - data_files = [DataFileInfo(**sample_data_file_info)] - dataset_info = DatasetInfo(features=features) - + def test_dataset_config_with_metadata_fields(self): + """Test DatasetConfig with metadata_fields.""" config = DatasetConfig( - config_name="test_config", - description="Test configuration", + config_name="data", + description="Data", dataset_type=DatasetType.ANNOTATED_FEATURES, - metadata_fields=["field1", "field2"], - data_files=data_files, - dataset_info=dataset_info, + metadata_fields=["regulator_symbol", "condition"], + data_files=[DataFileInfo(path="data.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo( + name="regulator_symbol", dtype="string", description="TF symbol" + ), + FeatureInfo( + name="condition", dtype="string", description="Condition" + ), + ] + ), ) + assert config.metadata_fields == ["regulator_symbol", "condition"] - assert config.metadata_fields == ["field1", "field2"] + def test_dataset_config_empty_metadata_fields_error(self): + """Test that empty metadata_fields raises error.""" + with pytest.raises(ValidationError): + DatasetConfig( + config_name="data", + description="Data", + dataset_type=DatasetType.ANNOTATED_FEATURES, + metadata_fields=[], + data_files=[DataFileInfo(path="data.parquet")], + dataset_info=DatasetInfo( + features=[FeatureInfo(name="id", dtype="string", description="ID")] + ), + ) + def test_dataset_config_accepts_extra_fields(self): + """Test that DatasetConfig accepts extra fields like experimental_conditions.""" + config_data = { + "config_name": "data", + "description": "Data", + "dataset_type": "annotated_features", + "experimental_conditions": { + "temperature_celsius": 30, + "media": {"name": "YPD"}, + }, + "data_files": [{"path": "data.parquet"}], + "dataset_info": { + "features": [{"name": "id", "dtype": "string", "description": "ID"}] + }, + } + config = DatasetConfig(**config_data) + assert hasattr(config, "model_extra") + assert "experimental_conditions" in config.model_extra -class TestDatasetCard: - """Test DatasetCard model.""" - def test_minimal_dataset_card(self, minimal_dataset_card_data): - """Test minimal valid DatasetCard.""" - card = DatasetCard(**minimal_dataset_card_data) +class TestDatasetCard: + """Tests for DatasetCard model.""" + + def test_minimal_dataset_card(self): + """Test DatasetCard with minimal structure.""" + card = DatasetCard( + configs=[ + DatasetConfig( + config_name="data", + description="Data", + dataset_type=DatasetType.ANNOTATED_FEATURES, + data_files=[DataFileInfo(path="data.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo(name="id", dtype="string", description="ID") + ] + ), + ) + ] + ) assert len(card.configs) == 1 - assert card.configs[0].config_name == "test_config" - assert card.license is None - assert card.pretty_name is None - - def test_full_dataset_card(self, sample_dataset_card_data): - """Test full DatasetCard with all fields.""" - card = DatasetCard(**sample_dataset_card_data) - assert len(card.configs) == 4 - assert card.license == "mit" - assert card.pretty_name == "Test Genomics Dataset" - assert card.tags is not None and "biology" in card.tags + + def test_dataset_card_accepts_extra_fields(self): + """Test that DatasetCard accepts extra top-level fields.""" + card_data = { + "license": "mit", + "pretty_name": "Test Dataset", + "tags": ["biology", "genomics"], + "experimental_conditions": {"strain_background": "BY4741"}, + "configs": [ + { + "config_name": "data", + "description": "Data", + "dataset_type": "annotated_features", + "data_files": [{"path": "data.parquet"}], + "dataset_info": { + "features": [ + {"name": "id", "dtype": "string", "description": "ID"} + ] + }, + } + ], + } + card = DatasetCard(**card_data) + assert hasattr(card, "model_extra") + assert "license" in card.model_extra + assert "experimental_conditions" in card.model_extra def test_empty_configs_error(self): - """Test that empty configs list raises error.""" - with pytest.raises( - ValidationError, match="At least one dataset configuration is required" - ): + """Test that empty configs raises error.""" + with pytest.raises(ValidationError): DatasetCard(configs=[]) - def test_duplicate_config_names_error( - self, sample_feature_info, sample_data_file_info - ): - """Test that duplicate config names raise error.""" - features = [FeatureInfo(**sample_feature_info)] - data_files = [DataFileInfo(**sample_data_file_info)] - dataset_info = DatasetInfo(features=features) - - config1 = DatasetConfig( - config_name="duplicate_name", - description="First config", - dataset_type=DatasetType.GENOMIC_FEATURES, - data_files=data_files, - dataset_info=dataset_info, - ) - - config2 = DatasetConfig( - config_name="duplicate_name", # Same name - description="Second config", - dataset_type=DatasetType.ANNOTATED_FEATURES, - data_files=data_files, - dataset_info=dataset_info, - ) - - with pytest.raises(ValidationError, match="Configuration names must be unique"): - DatasetCard(configs=[config1, config2]) - - def test_multiple_default_configs_error( - self, sample_feature_info, sample_data_file_info - ): - """Test that multiple default configs raise error.""" - features = [FeatureInfo(**sample_feature_info)] - data_files = [DataFileInfo(**sample_data_file_info)] - dataset_info = DatasetInfo(features=features) - - config1 = DatasetConfig( - config_name="config1", - description="First config", - dataset_type=DatasetType.GENOMIC_FEATURES, - default=True, - data_files=data_files, - dataset_info=dataset_info, - ) - - config2 = DatasetConfig( - config_name="config2", - description="Second config", - dataset_type=DatasetType.ANNOTATED_FEATURES, - default=True, # Another default - data_files=data_files, - dataset_info=dataset_info, - ) - - with pytest.raises( - ValidationError, match="At most one configuration can be marked as default" - ): - DatasetCard(configs=[config1, config2]) + def test_duplicate_config_names_error(self): + """Test that duplicate config names raises error.""" + with pytest.raises(ValidationError): + DatasetCard( + configs=[ + DatasetConfig( + config_name="data", + description="Data 1", + dataset_type=DatasetType.ANNOTATED_FEATURES, + data_files=[DataFileInfo(path="data1.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo(name="id", dtype="string", description="ID") + ] + ), + ), + DatasetConfig( + config_name="data", + description="Data 2", + dataset_type=DatasetType.ANNOTATED_FEATURES, + data_files=[DataFileInfo(path="data2.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo(name="id", dtype="string", description="ID") + ] + ), + ), + ] + ) - def test_get_config_by_name(self, sample_dataset_card_data): - """Test getting config by name.""" - card = DatasetCard(**sample_dataset_card_data) + def test_multiple_default_configs_error(self): + """Test that multiple default configs raises error.""" + with pytest.raises(ValidationError): + DatasetCard( + configs=[ + DatasetConfig( + config_name="data1", + description="Data 1", + dataset_type=DatasetType.ANNOTATED_FEATURES, + default=True, + data_files=[DataFileInfo(path="data1.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo(name="id", dtype="string", description="ID") + ] + ), + ), + DatasetConfig( + config_name="data2", + description="Data 2", + dataset_type=DatasetType.ANNOTATED_FEATURES, + default=True, + data_files=[DataFileInfo(path="data2.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo(name="id", dtype="string", description="ID") + ] + ), + ), + ] + ) - config = card.get_config_by_name("binding_data") + def test_get_config_by_name(self): + """Test get_config_by_name method.""" + card = DatasetCard( + configs=[ + DatasetConfig( + config_name="data1", + description="Data 1", + dataset_type=DatasetType.ANNOTATED_FEATURES, + data_files=[DataFileInfo(path="data1.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo(name="id", dtype="string", description="ID") + ] + ), + ), + DatasetConfig( + config_name="data2", + description="Data 2", + dataset_type=DatasetType.METADATA, + data_files=[DataFileInfo(path="data2.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo(name="id", dtype="string", description="ID") + ] + ), + ), + ] + ) + config = card.get_config_by_name("data1") assert config is not None - assert config.config_name == "binding_data" - - # Test non-existent config + assert config.config_name == "data1" assert card.get_config_by_name("nonexistent") is None - def test_get_configs_by_type(self, sample_dataset_card_data): - """Test getting configs by type.""" - card = DatasetCard(**sample_dataset_card_data) - - genomic_configs = card.get_configs_by_type(DatasetType.GENOMIC_FEATURES) - assert len(genomic_configs) == 1 - assert genomic_configs[0].config_name == "genomic_features" - - metadata_configs = card.get_configs_by_type(DatasetType.METADATA) - assert len(metadata_configs) == 1 - assert metadata_configs[0].config_name == "experiment_metadata" - - def test_get_default_config(self, sample_dataset_card_data): - """Test getting default config.""" - card = DatasetCard(**sample_dataset_card_data) - - default_config = card.get_default_config() - assert default_config is not None - assert default_config.config_name == "genomic_features" - assert default_config.default is True - - def test_get_data_configs(self, sample_dataset_card_data): - """Test getting non-metadata configs.""" - card = DatasetCard(**sample_dataset_card_data) - + def test_get_configs_by_type(self): + """Test get_configs_by_type method.""" + card = DatasetCard( + configs=[ + DatasetConfig( + config_name="data", + description="Data", + dataset_type=DatasetType.ANNOTATED_FEATURES, + data_files=[DataFileInfo(path="data.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo(name="id", dtype="string", description="ID") + ] + ), + ), + DatasetConfig( + config_name="metadata", + description="Metadata", + dataset_type=DatasetType.METADATA, + data_files=[DataFileInfo(path="metadata.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo(name="id", dtype="string", description="ID") + ] + ), + ), + ] + ) + data_configs = card.get_configs_by_type(DatasetType.ANNOTATED_FEATURES) + assert len(data_configs) == 1 + assert data_configs[0].config_name == "data" + + def test_get_default_config(self): + """Test get_default_config method.""" + card = DatasetCard( + configs=[ + DatasetConfig( + config_name="data1", + description="Data 1", + dataset_type=DatasetType.ANNOTATED_FEATURES, + data_files=[DataFileInfo(path="data1.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo(name="id", dtype="string", description="ID") + ] + ), + ), + DatasetConfig( + config_name="data2", + description="Data 2", + dataset_type=DatasetType.ANNOTATED_FEATURES, + default=True, + data_files=[DataFileInfo(path="data2.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo(name="id", dtype="string", description="ID") + ] + ), + ), + ] + ) + default = card.get_default_config() + assert default is not None + assert default.config_name == "data2" + + def test_get_data_configs(self): + """Test get_data_configs method.""" + card = DatasetCard( + configs=[ + DatasetConfig( + config_name="data", + description="Data", + dataset_type=DatasetType.ANNOTATED_FEATURES, + data_files=[DataFileInfo(path="data.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo(name="id", dtype="string", description="ID") + ] + ), + ), + DatasetConfig( + config_name="metadata", + description="Metadata", + dataset_type=DatasetType.METADATA, + data_files=[DataFileInfo(path="metadata.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo(name="id", dtype="string", description="ID") + ] + ), + ), + ] + ) data_configs = card.get_data_configs() - assert len(data_configs) == 3 # genomic_features, binding_data, genome_map_data - config_names = [config.config_name for config in data_configs] - assert "genomic_features" in config_names - assert "binding_data" in config_names - assert "genome_map_data" in config_names - assert "experiment_metadata" not in config_names - - def test_get_metadata_configs(self, sample_dataset_card_data): - """Test getting metadata configs.""" - card = DatasetCard(**sample_dataset_card_data) - + assert len(data_configs) == 1 + assert data_configs[0].dataset_type != DatasetType.METADATA + + def test_get_metadata_configs(self): + """Test get_metadata_configs method.""" + card = DatasetCard( + configs=[ + DatasetConfig( + config_name="data", + description="Data", + dataset_type=DatasetType.ANNOTATED_FEATURES, + data_files=[DataFileInfo(path="data.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo(name="id", dtype="string", description="ID") + ] + ), + ), + DatasetConfig( + config_name="metadata", + description="Metadata", + dataset_type=DatasetType.METADATA, + data_files=[DataFileInfo(path="metadata.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo(name="id", dtype="string", description="ID") + ] + ), + ), + ] + ) metadata_configs = card.get_metadata_configs() assert len(metadata_configs) == 1 - assert metadata_configs[0].config_name == "experiment_metadata" + assert metadata_configs[0].dataset_type == DatasetType.METADATA class TestExtractedMetadata: - """Test ExtractedMetadata model.""" + """Tests for ExtractedMetadata model.""" def test_extracted_metadata_creation(self): """Test creating ExtractedMetadata.""" metadata = ExtractedMetadata( config_name="test_config", field_name="regulator_symbol", - values={"TF1", "TF2", "TF3"}, - extraction_method="partition_values", + values={"CBF1", "GAL4", "GCN4"}, + extraction_method="distinct", ) - assert metadata.config_name == "test_config" assert metadata.field_name == "regulator_symbol" - assert metadata.values == {"TF1", "TF2", "TF3"} - assert metadata.extraction_method == "partition_values" - - def test_extracted_metadata_serialization(self): - """Test ExtractedMetadata JSON serialization.""" - metadata = ExtractedMetadata( - config_name="test_config", - field_name="condition", - values={"control", "treatment"}, - extraction_method="embedded", - ) - - # Test basic serialization (sets remain as sets in model_dump) - data = metadata.model_dump() - assert isinstance(data["values"], set) - assert data["values"] == {"control", "treatment"} - - # Test JSON mode serialization where sets should become lists - json_data = metadata.model_dump(mode="json") - assert isinstance(json_data["values"], list) - assert set(json_data["values"]) == {"control", "treatment"} + assert len(metadata.values) == 3 + assert "CBF1" in metadata.values class TestMetadataRelationship: - """Test MetadataRelationship model.""" + """Tests for MetadataRelationship model.""" def test_metadata_relationship_creation(self): """Test creating MetadataRelationship.""" @@ -809,23 +572,6 @@ def test_metadata_relationship_creation(self): metadata_config="experiment_metadata", relationship_type="explicit", ) - assert relationship.data_config == "binding_data" assert relationship.metadata_config == "experiment_metadata" assert relationship.relationship_type == "explicit" - - def test_metadata_relationship_types(self): - """Test different relationship types.""" - # Test explicit relationship - explicit = MetadataRelationship( - data_config="data1", metadata_config="meta1", relationship_type="explicit" - ) - assert explicit.relationship_type == "explicit" - - # Test embedded relationship - embedded = MetadataRelationship( - data_config="data3", - metadata_config="data3_embedded", - relationship_type="embedded", - ) - assert embedded.relationship_type == "embedded" diff --git a/tfbpapi/tests/datainfo/test_real_datacards.py b/tfbpapi/tests/datainfo/test_real_datacards.py index 32bdaf6..0220797 100644 --- a/tfbpapi/tests/datainfo/test_real_datacards.py +++ b/tfbpapi/tests/datainfo/test_real_datacards.py @@ -28,18 +28,17 @@ size_categories: - 100M 0 - carbon = env_conds.media.carbon_source[0] - assert carbon.concentration_percent is not None + assert "carbon_source" in media + carbon_sources = media["carbon_source"] + assert len(carbon_sources) > 0 + carbon = carbon_sources[0] + assert carbon["concentration_percent"] is not None # Check nitrogen source with specifications - assert len(env_conds.media.nitrogen_source) > 0 - nitrogen = env_conds.media.nitrogen_source[0] - assert nitrogen.specifications is not None - assert "without_amino_acids" in nitrogen.specifications + assert "nitrogen_source" in media + nitrogen_sources = media["nitrogen_source"] + assert len(nitrogen_sources) > 0 + nitrogen = nitrogen_sources[0] + assert nitrogen["specifications"] is not None + assert "without_amino_acids" in nitrogen["specifications"] def test_extra_fields_do_not_raise_errors(): @@ -650,10 +652,13 @@ def test_empty_nitrogen_source_list(): data = yaml.safe_load(BARKAI_COMPENDIUM) card = DatasetCard(**data) - # Check that nitrogen_source is an empty list - env_conds = card.experimental_conditions.environmental_conditions - assert env_conds.media is not None - assert env_conds.media.nitrogen_source == [] + # Check that nitrogen_source is an empty list (stored as dict in model_extra) + assert card.model_extra is not None + assert "experimental_conditions" in card.model_extra + exp_conds = card.model_extra["experimental_conditions"] + assert "media" in exp_conds + media = exp_conds["media"] + assert media["nitrogen_source"] == [] def test_media_additives(): @@ -683,14 +688,18 @@ def test_strain_background_formats(): # String format data1 = yaml.safe_load(BARKAI_COMPENDIUM) card1 = DatasetCard(**data1) - assert card1.experimental_conditions is not None - assert card1.experimental_conditions.strain_background == "BY4741" + assert card1.model_extra is not None + assert "experimental_conditions" in card1.model_extra + exp_conds1 = card1.model_extra["experimental_conditions"] + assert exp_conds1["strain_background"] == "BY4741" # String format in rossi data2 = yaml.safe_load(ROSSI_2021) card2 = DatasetCard(**data2) - assert card2.experimental_conditions is not None - assert card2.experimental_conditions.strain_background == "W303" + assert card2.model_extra is not None + assert "experimental_conditions" in card2.model_extra + exp_conds2 = card2.model_extra["experimental_conditions"] + assert exp_conds2["strain_background"] == "W303" if __name__ == "__main__": From 26c686b26cf66dabda773a8aed5bd1339112d65e Mon Sep 17 00:00:00 2001 From: chasem Date: Thu, 11 Dec 2025 17:03:58 -0600 Subject: [PATCH 18/49] filter_resolver --- docs/tutorials/filter_resolver_tutorial.ipynb | 1701 +++++++++++++++++ docs/tutorials/my_filter_config.yaml | 24 + docs/tutorials/sample_manager_tutorial.ipynb | 283 ++- example_filter_config.yaml | 88 + tfbpapi/datainfo/filter_resolver.py | 601 ++++++ .../tests/datainfo/test_filter_resolver.py | 385 ++++ 6 files changed, 3046 insertions(+), 36 deletions(-) create mode 100644 docs/tutorials/filter_resolver_tutorial.ipynb create mode 100644 docs/tutorials/my_filter_config.yaml create mode 100644 example_filter_config.yaml create mode 100644 tfbpapi/datainfo/filter_resolver.py create mode 100644 tfbpapi/tests/datainfo/test_filter_resolver.py diff --git a/docs/tutorials/filter_resolver_tutorial.ipynb b/docs/tutorials/filter_resolver_tutorial.ipynb new file mode 100644 index 0000000..b52449a --- /dev/null +++ b/docs/tutorials/filter_resolver_tutorial.ipynb @@ -0,0 +1,1701 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# DatasetFilterResolver Tutorial\n", + "\n", + "This tutorial demonstrates how to use the `DatasetFilterResolver` to filter samples across heterogeneous datasets using external YAML configuration.\n", + "\n", + "## Overview\n", + "\n", + "The `DatasetFilterResolver` solves a common problem: filtering experimental data across multiple datasets where experimental conditions are structured differently. Instead of hardcoding dataset-specific logic, you specify:\n", + "\n", + "1. **Filter criteria**: Which values are acceptable (e.g., carbon_source: [\"D-glucose\", \"D-galactose\"])\n", + "2. **Property mappings**: Where to find each property in each dataset (e.g., \"environmental_conditions.media.carbon_source\")\n", + "\n", + "## Key Features\n", + "\n", + "- **Two-level filtering**:\n", + " - Level 1: Repo/config level - excludes entire datasets that don't match\n", + " - Level 2: Field level - returns matching field values within datasets\n", + "- **Three output modes**:\n", + " - `conditions`: Just matching field values (no data retrieval)\n", + " - `samples`: Sample-level metadata (one row per sample_id)\n", + " - `full_data`: All measurements for matching samples\n", + "- **External configuration**: No hardcoded field specifications in codebase\n", + "- **Automatic path resolution**: Handles different nesting levels (repo vs field level)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "import yaml\n", + "import pandas as pd\n", + "from tfbpapi.datainfo.filter_resolver import DatasetFilterResolver" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 1: Basic Filtering by Carbon Source\n", + "\n", + "Let's start with a simple example: finding all samples grown on D-glucose." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Understanding Field-Level vs Repo-Level Properties\n", + "\n", + "A key challenge with heterogeneous datasets is that experimental conditions can be located in different places:\n", + "\n", + "**Field-Level Definitions** (e.g., Harbison 2004):\n", + "- Properties are defined within a specific field's `definitions`\n", + "- Example: The `condition` field has definitions for \"YPD\", \"HEAT\", etc.\n", + "- Each definition contains `media`, `temperature_celsius`, etc.\n", + "- **Use the `field` parameter** to specify which field contains the definitions\n", + "\n", + "**Repo/Config-Level Properties** (e.g., Kemmeren 2014):\n", + "- Properties are defined at the top level of the datacard\n", + "- All samples in the dataset share the same conditions\n", + "- Found in `experimental_conditions` at repo or config level\n", + "- **No `field` parameter needed** - just specify the path\n", + "\n", + "Let's examine actual datacards to see these differences:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Configuration Implications\n", + "\n", + "Based on the actual datacard structures above:\n", + "\n", + "**For Harbison 2004** (field-level):\n", + "```yaml\n", + "\"BrentLab/harbison_2004\":\n", + " datasets:\n", + " harbison_2004:\n", + " carbon_source:\n", + " field: condition # Must specify which field\n", + " path: media.carbon_source # Path within each field value's definition\n", + "```\n", + "\n", + "**For Kemmeren 2014** (repo-level):\n", + "```yaml\n", + "\"BrentLab/kemmeren_2014\":\n", + " repo_level:\n", + " carbon_source:\n", + " path: environmental_conditions.media.carbon_source # No 'field' needed\n", + "```\n", + "\n", + "### When to Use Which Format\n", + "\n", + "**Use `field` specification when**:\n", + "- The property varies between samples (different values in the dataset)\n", + "- The property is defined in a field's `definitions` section\n", + "- You need to filter to specific field values (e.g., only \"YPD\" condition)\n", + "- Example: Harbison 2004 where each condition has its own media/temperature\n", + "\n", + "**Use repo-level (no `field`) when**:\n", + "- The property is the same for all samples in the dataset\n", + "- The property is in the top-level `experimental_conditions`\n", + "- You're filtering entire datasets in/out based on shared properties\n", + "- Example: Kemmeren 2014 where all samples share the same growth conditions" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================================================================================\n", + "KEMMEREN 2014 - Repo-Level Experimental Conditions\n", + "================================================================================\n", + "\n", + "Repo-level experimental_conditions exists: True\n", + "\n", + "Temperature: 30°C\n", + "Media name: synthetic_complete\n", + "Carbon source: ['D-glucose']\n", + "Concentrations: [2]%\n", + "\n", + "No field-level definitions found\n", + "\n", + "================================================================================\n", + "Key Observation: Properties are at repo level, shared by all samples\n", + "================================================================================\n" + ] + } + ], + "source": [ + "# Load Kemmeren 2014 (repo-level example)\n", + "from tfbpapi.datainfo import DataCard\n", + "\n", + "kemmeren_card = DataCard(\"BrentLab/kemmeren_2014\")\n", + "\n", + "print(\"=\" * 80)\n", + "print(\"KEMMEREN 2014 - Repo-Level Experimental Conditions\")\n", + "print(\"=\" * 80)\n", + "\n", + "# Get repo-level experimental conditions (returns a dict)\n", + "exp_conds = kemmeren_card.get_experimental_conditions(\"kemmeren_2014\")\n", + "print(f\"\\nRepo-level experimental_conditions exists: {bool(exp_conds)}\")\n", + "\n", + "if exp_conds:\n", + " # exp_conds is a dict, not a Pydantic model\n", + " print(f\"\\nTemperature: {exp_conds.get('temperature_celsius')}°C\")\n", + " \n", + " media = exp_conds.get('media', {})\n", + " print(f\"Media name: {media.get('name')}\")\n", + " \n", + " carbon_source = media.get('carbon_source', [])\n", + " if carbon_source:\n", + " compounds = [cs.get('compound') for cs in carbon_source]\n", + " concentrations = [cs.get('concentration_percent') for cs in carbon_source]\n", + " print(f\"Carbon source: {compounds}\")\n", + " print(f\"Concentrations: {concentrations}%\")\n", + "\n", + "# Check if there are field-level definitions\n", + "config = kemmeren_card.get_config(\"kemmeren_2014\")\n", + "has_field_defs = False\n", + "for feature in config.dataset_info.features:\n", + " if feature.definitions:\n", + " has_field_defs = True\n", + " print(f\"\\nField '{feature.name}' has definitions: {list(feature.definitions.keys())}\")\n", + "\n", + "if not has_field_defs:\n", + " print(\"\\nNo field-level definitions found\")\n", + "\n", + "print(\"\\n\" + \"=\" * 80)\n", + "print(\"Key Observation: Properties are at repo level, shared by all samples\")\n", + "print(\"=\" * 80)" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================================================================================\n", + "HARBISON 2004 - Field-Level Definitions\n", + "================================================================================\n", + "\n", + "Repo-level experimental_conditions: {}\n", + "\n", + "Field 'condition' has 14 definitions\n", + "Condition values: ['YPD', 'SM', 'RAPA', 'H2O2Hi', 'H2O2Lo', 'Acid', 'Alpha', 'BUT14', 'BUT90', 'Thi-', 'GAL', 'HEAT', 'Pi-', 'RAFF']\n", + "\n", + "Example definitions:\n", + "\n", + "YPD:\n", + " temperature_celsius: 30\n", + " media.name: YPD\n", + " media.carbon_source: ['D-glucose']\n", + "\n", + "HEAT:\n", + " temperature_celsius: None\n", + " media.name: YPD\n", + " media.carbon_source: ['D-glucose']\n", + "\n", + "GAL:\n", + " temperature_celsius: 30\n", + " media.name: yeast_extract_peptone\n", + " media.carbon_source: ['D-galactose']\n", + "\n", + "================================================================================\n", + "Key Observation: Properties are in field definitions, NOT at repo level\n", + "================================================================================\n" + ] + } + ], + "source": [ + "from tfbpapi.datainfo import DataCard\n", + "import json\n", + "\n", + "# Load Harbison 2004 (field-level example)\n", + "harbison_card = DataCard(\"BrentLab/harbison_2004\")\n", + "\n", + "print(\"=\" * 80)\n", + "print(\"HARBISON 2004 - Field-Level Definitions\")\n", + "print(\"=\" * 80)\n", + "\n", + "# Check if there are repo-level experimental conditions\n", + "exp_conds = harbison_card.get_experimental_conditions(\"harbison_2004\")\n", + "print(f\"\\nRepo-level experimental_conditions: {exp_conds}\")\n", + "\n", + "# Get field definitions for the 'condition' field\n", + "condition_defs = harbison_card.get_field_definitions(\"harbison_2004\", \"condition\")\n", + "print(f\"\\nField 'condition' has {len(condition_defs)} definitions\")\n", + "print(f\"Condition values: {list(condition_defs.keys())}\")\n", + "\n", + "# Show a few example definitions\n", + "print(\"\\nExample definitions:\")\n", + "for cond_name in [\"YPD\", \"HEAT\", \"GAL\"]:\n", + " if cond_name in condition_defs:\n", + " definition = condition_defs[cond_name]\n", + " print(f\"\\n{cond_name}:\")\n", + " print(f\" temperature_celsius: {definition.get('temperature_celsius')}\")\n", + " media = definition.get('media', {})\n", + " print(f\" media.name: {media.get('name')}\")\n", + " carbon_source = media.get('carbon_source', [])\n", + " if carbon_source:\n", + " compounds = [cs.get('compound') for cs in carbon_source]\n", + " print(f\" media.carbon_source: {compounds}\")\n", + "\n", + "print(\"\\n\" + \"=\" * 80)\n", + "print(\"Key Observation: Properties are in field definitions, NOT at repo level\")\n", + "print(\"=\" * 80)" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Configuration:\n", + "dataset_mappings:\n", + " BrentLab/harbison_2004:\n", + " datasets:\n", + " harbison_2004:\n", + " carbon_source:\n", + " field: condition\n", + " path: media.carbon_source\n", + "filters:\n", + " carbon_source:\n", + " - D-glucose\n", + "\n" + ] + } + ], + "source": [ + "# Create a filter configuration\n", + "glucose_config = {\n", + " \"filters\": {\n", + " \"carbon_source\": [\"D-glucose\"]\n", + " },\n", + " \"dataset_mappings\": {\n", + " \"BrentLab/harbison_2004\": {\n", + " \"datasets\": {\n", + " \"harbison_2004\": {\n", + " \"carbon_source\": {\n", + " \"field\": \"condition\",\n", + " \"path\": \"media.carbon_source\"\n", + " }\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "# Save to temporary file\n", + "config_path = Path(\"/tmp/glucose_filter.yaml\")\n", + "with open(config_path, 'w') as f:\n", + " yaml.dump(glucose_config, f)\n", + "\n", + "print(\"Configuration:\")\n", + "print(yaml.dump(glucose_config, default_flow_style=False))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Mode 0: Conditions Only\n", + "\n", + "Get just the matching field values without retrieving any data." + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Results:\n", + "\n", + "BrentLab/harbison_2004:\n", + " Included: True\n", + " Matching conditions:\n", + " condition: ['YPD', 'RAPA', 'H2O2Hi', 'H2O2Lo', 'Acid', 'Alpha', 'BUT14', 'BUT90', 'HEAT']\n" + ] + } + ], + "source": [ + "# Create resolver\n", + "resolver = DatasetFilterResolver(config_path)\n", + "\n", + "# Get matching conditions\n", + "results = resolver.resolve_filters(\n", + " repos=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", + " mode=\"conditions\"\n", + ")\n", + "\n", + "print(\"\\nResults:\")\n", + "for repo_id, result in results.items():\n", + " print(f\"\\n{repo_id}:\")\n", + " if result[\"included\"]:\n", + " print(f\" Included: True\")\n", + " print(f\" Matching conditions:\")\n", + " for field, values in result[\"matching_field_values\"].items():\n", + " print(f\" {field}: {values}\")\n", + " else:\n", + " print(f\" Included: False\")\n", + " print(f\" Reason: {result['reason']}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Understanding the Results\n", + "\n", + "The `matching_field_values` shows which values of the `condition` field have D-glucose:\n", + "- YPD: Rich media with 2% glucose\n", + "- HEAT: Heat stress with glucose\n", + "- H2O2Hi/H2O2Lo: Oxidative stress with glucose\n", + "- Acid: Acidic stress with glucose\n", + "- Alpha: Alpha factor arrest with glucose\n", + "- BUT14/BUT90: Butanol treatment with glucose\n", + "\n", + "Notice that GAL (galactose media) is NOT in the list - it was correctly filtered out." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 2: Multiple Filter Criteria\n", + "\n", + "Let's filter for samples with both D-glucose AND 30C temperature." + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Conditions with D-glucose AND 30C:\n", + "['YPD', 'RAPA', 'H2O2Hi', 'H2O2Lo', 'Acid', 'Alpha', 'BUT14', 'BUT90']\n" + ] + } + ], + "source": [ + "# Config with multiple filters\n", + "multi_filter_config = {\n", + " \"filters\": {\n", + " \"carbon_source\": [\"D-glucose\"],\n", + " \"temperature_celsius\": [30]\n", + " },\n", + " \"dataset_mappings\": {\n", + " \"BrentLab/harbison_2004\": {\n", + " \"datasets\": {\n", + " \"harbison_2004\": {\n", + " \"carbon_source\": {\n", + " \"field\": \"condition\",\n", + " \"path\": \"media.carbon_source\"\n", + " },\n", + " \"temperature_celsius\": {\n", + " \"field\": \"condition\",\n", + " \"path\": \"temperature_celsius\"\n", + " }\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "config_path = Path(\"/tmp/multi_filter.yaml\")\n", + "with open(config_path, 'w') as f:\n", + " yaml.dump(multi_filter_config, f)\n", + "\n", + "resolver = DatasetFilterResolver(config_path)\n", + "results = resolver.resolve_filters(\n", + " repos=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", + " mode=\"conditions\"\n", + ")\n", + "\n", + "print(\"Conditions with D-glucose AND 30C:\")\n", + "matching = results[\"BrentLab/harbison_2004\"][\"matching_field_values\"][\"condition\"]\n", + "print(matching)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Notice that HEAT is now excluded - it has glucose but is at 37C, not 30C. Multiple filter criteria use AND logic." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 3: Mode 1 - Sample-Level Metadata\n", + "\n", + "Retrieve actual sample metadata (one row per sample_id) for matching conditions." + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Retrieved 304 samples\n", + "\n", + "DataFrame shape: (304, 9)\n", + "\n", + "Columns: ['sample_id', 'db_id', 'regulator_locus_tag', 'regulator_symbol', 'condition', 'target_locus_tag', 'target_symbol', 'effect', 'pvalue']\n", + "\n", + "First few rows:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "sample_id", + "rawType": "int32", + "type": "integer" + }, + { + "name": "db_id", + "rawType": "float64", + "type": "float" + }, + { + "name": "regulator_locus_tag", + "rawType": "object", + "type": "string" + }, + { + "name": "regulator_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "condition", + "rawType": "object", + "type": "string" + }, + { + "name": "target_locus_tag", + "rawType": "object", + "type": "string" + }, + { + "name": "target_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "effect", + "rawType": "float64", + "type": "float" + }, + { + "name": "pvalue", + "rawType": "float64", + "type": "float" + } + ], + "ref": "a9bc83e5-ec64-4c54-a0b5-b8efb910402a", + "rows": [ + [ + "0", + "1", + "0.0", + "YSC0017", + "MATA1", + "YPD", + "YAL023C", + "PMT2", + "0.74920592", + "0.83707059" + ], + [ + "1", + "2", + "1.0", + "YAL051W", + "OAF1", + "YPD", + "YAL047C", + "SPC72", + "1.9661134", + "0.0076229595" + ], + [ + "2", + "3", + "2.0", + "YBL005W", + "PDR3", + "YPD", + "YBR001C", + "NTH2", + "0.97462425", + "0.56597448" + ], + [ + "3", + "4", + "3.0", + "YBL008W", + "HIR1", + "YPD", + "YBL037W", + "APL3", + "1.0555759", + "0.35839913" + ], + [ + "4", + "5", + "4.0", + "YBL021C", + "HAP3", + "YPD", + "YAL001C", + "TFC3", + "0.84731661", + "0.81162232" + ] + ], + "shape": { + "columns": 9, + "rows": 5 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sample_iddb_idregulator_locus_tagregulator_symbolconditiontarget_locus_tagtarget_symboleffectpvalue
010.0YSC0017MATA1YPDYAL023CPMT20.7492060.837071
121.0YAL051WOAF1YPDYAL047CSPC721.9661130.007623
232.0YBL005WPDR3YPDYBR001CNTH20.9746240.565974
343.0YBL008WHIR1YPDYBL037WAPL31.0555760.358399
454.0YBL021CHAP3YPDYAL001CTFC30.8473170.811622
\n", + "
" + ], + "text/plain": [ + " sample_id db_id regulator_locus_tag regulator_symbol condition \\\n", + "0 1 0.0 YSC0017 MATA1 YPD \n", + "1 2 1.0 YAL051W OAF1 YPD \n", + "2 3 2.0 YBL005W PDR3 YPD \n", + "3 4 3.0 YBL008W HIR1 YPD \n", + "4 5 4.0 YBL021C HAP3 YPD \n", + "\n", + " target_locus_tag target_symbol effect pvalue \n", + "0 YAL023C PMT2 0.749206 0.837071 \n", + "1 YAL047C SPC72 1.966113 0.007623 \n", + "2 YBR001C NTH2 0.974624 0.565974 \n", + "3 YBL037W APL3 1.055576 0.358399 \n", + "4 YAL001C TFC3 0.847317 0.811622 " + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Use same config but change mode to 'samples'\n", + "results_samples = resolver.resolve_filters(\n", + " repos=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", + " mode=\"samples\"\n", + ")\n", + "\n", + "# Get the DataFrame\n", + "df_samples = results_samples[\"BrentLab/harbison_2004\"][\"data\"]\n", + "\n", + "print(f\"Retrieved {len(df_samples)} samples\")\n", + "print(f\"\\nDataFrame shape: {df_samples.shape}\")\n", + "print(f\"\\nColumns: {list(df_samples.columns)}\")\n", + "print(f\"\\nFirst few rows:\")\n", + "df_samples.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Condition distribution:\n", + "condition\n", + "YPD 204\n", + "H2O2Hi 39\n", + "H2O2Lo 28\n", + "RAPA 14\n", + "BUT14 8\n", + "Alpha 5\n", + "BUT90 4\n", + "Acid 2\n", + "Name: count, dtype: int64\n" + ] + } + ], + "source": [ + "# Check the distribution of conditions in the retrieved samples\n", + "print(\"Condition distribution:\")\n", + "print(df_samples['condition'].value_counts())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 4: Mode 2 - Full Data\n", + "\n", + "Retrieve all measurements for matching samples (many rows per sample_id)." + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Retrieved 1,892,704 rows (measurements)\n", + "\n", + "DataFrame shape: (1892704, 9)\n", + "\n", + "Columns: ['sample_id', 'db_id', 'regulator_locus_tag', 'regulator_symbol', 'condition', 'target_locus_tag', 'target_symbol', 'effect', 'pvalue']\n", + "\n", + "First few rows:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "sample_id", + "rawType": "int32", + "type": "integer" + }, + { + "name": "db_id", + "rawType": "float64", + "type": "float" + }, + { + "name": "regulator_locus_tag", + "rawType": "object", + "type": "string" + }, + { + "name": "regulator_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "condition", + "rawType": "object", + "type": "string" + }, + { + "name": "target_locus_tag", + "rawType": "object", + "type": "string" + }, + { + "name": "target_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "effect", + "rawType": "float64", + "type": "float" + }, + { + "name": "pvalue", + "rawType": "float64", + "type": "float" + } + ], + "ref": "a828c111-7829-4bcd-a77c-ff5dbc95ba4a", + "rows": [ + [ + "0", + "1", + "0.0", + "YSC0017", + "MATA1", + "YPD", + "YAL001C", + "TFC3", + "1.697754", + "0.068704735" + ], + [ + "1", + "1", + "0.0", + "YSC0017", + "MATA1", + "YPD", + "YAL002W", + "VPS8", + null, + null + ], + [ + "2", + "1", + "0.0", + "YSC0017", + "MATA1", + "YPD", + "YAL003W", + "EFB1", + null, + null + ], + [ + "3", + "1", + "0.0", + "YSC0017", + "MATA1", + "YPD", + "YAL004W", + "YAL004W", + "0.74534215", + "0.83592938" + ], + [ + "4", + "1", + "0.0", + "YSC0017", + "MATA1", + "YPD", + "YAL005C", + "SSA1", + null, + null + ] + ], + "shape": { + "columns": 9, + "rows": 5 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sample_iddb_idregulator_locus_tagregulator_symbolconditiontarget_locus_tagtarget_symboleffectpvalue
010.0YSC0017MATA1YPDYAL001CTFC31.6977540.068705
110.0YSC0017MATA1YPDYAL002WVPS8NaNNaN
210.0YSC0017MATA1YPDYAL003WEFB1NaNNaN
310.0YSC0017MATA1YPDYAL004WYAL004W0.7453420.835929
410.0YSC0017MATA1YPDYAL005CSSA1NaNNaN
\n", + "
" + ], + "text/plain": [ + " sample_id db_id regulator_locus_tag regulator_symbol condition \\\n", + "0 1 0.0 YSC0017 MATA1 YPD \n", + "1 1 0.0 YSC0017 MATA1 YPD \n", + "2 1 0.0 YSC0017 MATA1 YPD \n", + "3 1 0.0 YSC0017 MATA1 YPD \n", + "4 1 0.0 YSC0017 MATA1 YPD \n", + "\n", + " target_locus_tag target_symbol effect pvalue \n", + "0 YAL001C TFC3 1.697754 0.068705 \n", + "1 YAL002W VPS8 NaN NaN \n", + "2 YAL003W EFB1 NaN NaN \n", + "3 YAL004W YAL004W 0.745342 0.835929 \n", + "4 YAL005C SSA1 NaN NaN " + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Use same config but change mode to 'full_data'\n", + "results_full = resolver.resolve_filters(\n", + " repos=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", + " mode=\"full_data\"\n", + ")\n", + "\n", + "# Get the DataFrame\n", + "df_full = results_full[\"BrentLab/harbison_2004\"][\"data\"]\n", + "\n", + "print(f\"Retrieved {len(df_full):,} rows (measurements)\")\n", + "print(f\"\\nDataFrame shape: {df_full.shape}\")\n", + "print(f\"\\nColumns: {list(df_full.columns)}\")\n", + "print(f\"\\nFirst few rows:\")\n", + "df_full.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Sample-level metadata: 304 samples\n", + "Full data: 1,892,704 measurements\n", + "Average measurements per sample: 6,226\n" + ] + } + ], + "source": [ + "# Compare to samples mode\n", + "n_samples = df_samples['sample_id'].nunique() if 'sample_id' in df_samples.columns else len(df_samples)\n", + "n_measurements = len(df_full)\n", + "measurements_per_sample = n_measurements / n_samples if n_samples > 0 else 0\n", + "\n", + "print(f\"Sample-level metadata: {n_samples} samples\")\n", + "print(f\"Full data: {n_measurements:,} measurements\")\n", + "print(f\"Average measurements per sample: {measurements_per_sample:,.0f}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 5: Filtering Across Multiple Datasets\n", + "\n", + "The real power comes from filtering across multiple datasets with different structures." + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Results across multiple datasets:\n", + "\n", + "BrentLab/harbison_2004:\n", + " Included: True\n", + " condition: ['YPD', 'RAPA', 'H2O2Hi', 'H2O2Lo', 'Acid', 'Alpha', 'BUT14', 'BUT90', 'GAL', 'HEAT']\n", + "\n", + "BrentLab/kemmeren_2014:\n", + " Included: True\n", + " No field-level conditions (all samples match at repo level)\n", + "\n" + ] + } + ], + "source": [ + "# Config for multiple datasets\n", + "multi_dataset_config = {\n", + " \"filters\": {\n", + " \"carbon_source\": [\"D-glucose\", \"D-galactose\"]\n", + " },\n", + " \"dataset_mappings\": {\n", + " \"BrentLab/harbison_2004\": {\n", + " \"datasets\": {\n", + " \"harbison_2004\": {\n", + " \"carbon_source\": {\n", + " \"field\": \"condition\",\n", + " \"path\": \"media.carbon_source\"\n", + " }\n", + " }\n", + " }\n", + " },\n", + " \"BrentLab/kemmeren_2014\": {\n", + " \"repo_level\": {\n", + " \"carbon_source\": {\n", + " \"path\": \"environmental_conditions.media.carbon_source\"\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "config_path = Path(\"/tmp/multi_dataset_filter.yaml\")\n", + "with open(config_path, 'w') as f:\n", + " yaml.dump(multi_dataset_config, f)\n", + "\n", + "resolver = DatasetFilterResolver(config_path)\n", + "\n", + "# Filter multiple datasets\n", + "results = resolver.resolve_filters(\n", + " repos=[\n", + " (\"BrentLab/harbison_2004\", \"harbison_2004\"),\n", + " (\"BrentLab/kemmeren_2014\", \"kemmeren_2014\")\n", + " ],\n", + " mode=\"conditions\"\n", + ")\n", + "\n", + "print(\"Results across multiple datasets:\\n\")\n", + "for repo_id, result in results.items():\n", + " print(f\"{repo_id}:\")\n", + " if result[\"included\"]:\n", + " print(f\" Included: True\")\n", + " if \"matching_field_values\" in result and result[\"matching_field_values\"]:\n", + " for field, values in result[\"matching_field_values\"].items():\n", + " print(f\" {field}: {values}\")\n", + " else:\n", + " print(\" No field-level conditions (all samples match at repo level)\")\n", + " else:\n", + " print(f\" Included: False\")\n", + " print(f\" Reason: {result['reason']}\")\n", + " print()\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 6: Galactose-Only Filter\n", + "\n", + "Let's filter for just galactose to see dataset exclusion in action." + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Galactose-only filter results:\n", + "Included: True\n", + "Matching conditions: ['GAL']\n" + ] + } + ], + "source": [ + "galactose_config = {\n", + " \"filters\": {\n", + " \"carbon_source\": [\"D-galactose\"]\n", + " },\n", + " \"dataset_mappings\": {\n", + " \"BrentLab/harbison_2004\": {\n", + " \"datasets\": {\n", + " \"harbison_2004\": {\n", + " \"carbon_source\": {\n", + " \"field\": \"condition\",\n", + " \"path\": \"media.carbon_source\"\n", + " }\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "config_path = Path(\"/tmp/galactose_filter.yaml\")\n", + "with open(config_path, 'w') as f:\n", + " yaml.dump(galactose_config, f)\n", + "\n", + "resolver = DatasetFilterResolver(config_path)\n", + "results = resolver.resolve_filters(\n", + " repos=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", + " mode=\"conditions\"\n", + ")\n", + "\n", + "print(\"Galactose-only filter results:\")\n", + "result = results[\"BrentLab/harbison_2004\"]\n", + "print(f\"Included: {result['included']}\")\n", + "if result['included']:\n", + " matching = result[\"matching_field_values\"][\"condition\"]\n", + " print(f\"Matching conditions: {matching}\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Only the GAL condition should match - it's the only one with galactose as the carbon source." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## How Path Resolution Works\n", + "\n", + "The `DatasetFilterResolver` uses a two-phase approach to handle heterogeneous dataset structures:\n", + "\n", + "### Phase 1: Property Location (Field vs Repo Level)\n", + "\n", + "**Field-Level Resolution** (when `field` is specified):\n", + "```python\n", + "# Configuration\n", + "carbon_source:\n", + " field: condition # Look in the 'condition' field\n", + " path: media.carbon_source # Path within each field value's definition\n", + "```\n", + "\n", + "The resolver:\n", + "1. Gets all definitions for the `condition` field\n", + "2. For each field value (YPD, HEAT, GAL, etc.), extracts the value at `media.carbon_source`\n", + "3. Returns field values where the extracted value matches the filter\n", + "\n", + "**Repo-Level Resolution** (no `field` specified):\n", + "```python\n", + "# Configuration\n", + "carbon_source:\n", + " path: environmental_conditions.media.carbon_source # Top-level path\n", + "```\n", + "\n", + "The resolver:\n", + "1. Looks at the top-level `experimental_conditions` in the datacard\n", + "2. Extracts the value at the specified path\n", + "3. Includes/excludes the entire dataset based on the match\n", + "\n", + "### Phase 2: Path Navigation\n", + "\n", + "Once the resolver knows WHERE to look (field vs repo level), it navigates to the property using dot notation.\n", + "\n", + "**Automatic Fallback for Nested Paths**:\n", + "\n", + "The resolver tries multiple path variants to handle different nesting depths:\n", + "\n", + "1. **Try full path first**: `environmental_conditions.media.carbon_source`\n", + "2. **If not found, try without prefix**: `media.carbon_source`\n", + "\n", + "This allows configs to work with both:\n", + "- Repo-level: `experimental_conditions.media.carbon_source` (finds it at step 1)\n", + "- Field-level: `media.carbon_source` (finds it at step 2)\n", + "\n", + "**Example**: Harbison 2004 field definitions don't have an `environmental_conditions` wrapper, so:\n", + "- Path specified: `media.carbon_source`\n", + "- Directly finds `media` in the field definition\n", + "- No fallback needed\n", + "\n", + "### Compound Extraction\n", + "\n", + "For carbon source and similar properties, the resolver handles list-of-dict format:\n", + "\n", + "```yaml\n", + "carbon_source:\n", + " - compound: D-glucose\n", + " concentration_percent: 2\n", + " - compound: D-galactose \n", + " concentration_percent: 1\n", + "```\n", + "\n", + "Automatically extracts to: `[\"D-glucose\", \"D-galactose\"]`\n", + "\n", + "### Why This Design?\n", + "\n", + "This two-phase approach (field specification + path navigation) allows:\n", + "\n", + "1. **No hardcoded field names** in the codebase\n", + "2. **Explicit configuration** - clear which field to examine \n", + "3. **Flexibility** - works with any field name (`condition`, `treatment`, `strain`, etc.)\n", + "4. **Automatic nesting handling** - paths work at different hierarchy levels\n", + "5. **Heterogeneous datasets** - each dataset can specify its own structure\n", + "\n", + "### Configuration Debugging Tips\n", + "\n", + "If your filter isn't working:\n", + "\n", + "1. **Check the datacard structure** - is the property at field or repo level?\n", + "2. **Verify the field name** - does the field exist? Is the name correct?\n", + "3. **Test the path** - use `DataCard.get_field_definitions()` to inspect\n", + "4. **Try simpler paths** - remove `environmental_conditions.` if not needed\n", + "5. **Use `conditions` mode first** - see which values match before retrieving data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Practical Example: Creating a Filter Config File\n", + "\n", + "Here's a complete example of a reusable filter configuration file." + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Saved configuration to my_filter_config.yaml\n", + "\n", + "Configuration contents:\n", + "dataset_mappings:\n", + " BrentLab/harbison_2004:\n", + " datasets:\n", + " harbison_2004:\n", + " carbon_source:\n", + " field: condition\n", + " path: media.carbon_source\n", + " temperature_celsius:\n", + " field: condition\n", + " path: temperature_celsius\n", + " BrentLab/kemmeren_2014:\n", + " repo_level:\n", + " carbon_source:\n", + " path: environmental_conditions.media.carbon_source\n", + " temperature_celsius:\n", + " path: environmental_conditions.temperature_celsius\n", + "filters:\n", + " carbon_source:\n", + " - D-glucose\n", + " - D-galactose\n", + " - D-raffinose\n", + " temperature_celsius:\n", + " - 30\n", + " - 37\n", + "\n" + ] + } + ], + "source": [ + "# Create a comprehensive filter config\n", + "comprehensive_config = {\n", + " \"filters\": {\n", + " \"carbon_source\": [\"D-glucose\", \"D-galactose\", \"D-raffinose\"],\n", + " \"temperature_celsius\": [30, 37]\n", + " },\n", + " \"dataset_mappings\": {\n", + " \"BrentLab/harbison_2004\": {\n", + " \"datasets\": {\n", + " \"harbison_2004\": {\n", + " \"carbon_source\": {\n", + " \"field\": \"condition\",\n", + " \"path\": \"media.carbon_source\"\n", + " },\n", + " \"temperature_celsius\": {\n", + " \"field\": \"condition\",\n", + " \"path\": \"temperature_celsius\"\n", + " }\n", + " }\n", + " }\n", + " },\n", + " \"BrentLab/kemmeren_2014\": {\n", + " \"repo_level\": {\n", + " \"carbon_source\": {\n", + " \"path\": \"environmental_conditions.media.carbon_source\"\n", + " },\n", + " \"temperature_celsius\": {\n", + " \"path\": \"environmental_conditions.temperature_celsius\"\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "# Save to a persistent location\n", + "config_file = Path(\"my_filter_config.yaml\")\n", + "with open(config_file, 'w') as f:\n", + " yaml.dump(comprehensive_config, f)\n", + "\n", + "print(f\"Saved configuration to {config_file}\")\n", + "print(\"\\nConfiguration contents:\")\n", + "print(yaml.dump(comprehensive_config, default_flow_style=False))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Simplifying Single-Dataset Repositories\n", + "\n", + "For repositories with a single dataset or when all datasets share the same structure, you can use `repo_level` for brevity:\n", + "\n", + "```yaml\n", + "dataset_mappings:\n", + " \"BrentLab/kemmeren_2014\":\n", + " repo_level:\n", + " carbon_source:\n", + " path: environmental_conditions.media.carbon_source\n", + " temperature_celsius:\n", + " path: environmental_conditions.temperature_celsius\n", + "```\n", + "\n", + "This applies the mappings to all datasets in the repo. For kemmeren_2014 (which has repo-level experimental_conditions), this is more concise than explicitly listing each dataset." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Advanced: Hierarchical Configuration for Multi-Dataset Repositories\n", + "\n", + "When a repository contains multiple datasets, you can use hierarchical configuration to:\n", + "1. Define repo-level mappings that apply to all datasets\n", + "2. Override or extend with dataset-specific mappings\n", + "\n", + "This is especially useful when datasets share common properties but have some differences." + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hierarchical configuration:\n", + "dataset_mappings:\n", + " BrentLab/multi_dataset_repo:\n", + " datasets:\n", + " dataset1:\n", + " temperature_celsius:\n", + " path: environmental_conditions.temperature_celsius\n", + " dataset2:\n", + " temperature_celsius:\n", + " path: custom.temp.location\n", + " dataset3:\n", + " carbon_source:\n", + " path: media.carbon\n", + " temperature_celsius:\n", + " path: temp\n", + " repo_level:\n", + " carbon_source:\n", + " path: environmental_conditions.media.carbon_source\n", + "filters:\n", + " carbon_source:\n", + " - D-glucose\n", + " temperature_celsius:\n", + " - 30\n", + "\n" + ] + } + ], + "source": [ + "# Hierarchical configuration example\n", + "hierarchical_config = {\n", + " \"filters\": {\n", + " \"carbon_source\": [\"D-glucose\"],\n", + " \"temperature_celsius\": [30]\n", + " },\n", + " \"dataset_mappings\": {\n", + " # Repository with multiple datasets\n", + " \"BrentLab/multi_dataset_repo\": {\n", + " # Repo-level mappings apply to ALL datasets in this repo\n", + " \"repo_level\": {\n", + " \"carbon_source\": {\n", + " \"path\": \"environmental_conditions.media.carbon_source\"\n", + " }\n", + " },\n", + " # Dataset-specific overrides\n", + " \"datasets\": {\n", + " \"dataset1\": {\n", + " # Inherits carbon_source from repo_level\n", + " # Adds temperature mapping for this specific dataset\n", + " \"temperature_celsius\": {\n", + " \"path\": \"environmental_conditions.temperature_celsius\"\n", + " }\n", + " },\n", + " \"dataset2\": {\n", + " # Inherits carbon_source from repo_level \n", + " # Uses different temperature path\n", + " \"temperature_celsius\": {\n", + " \"path\": \"custom.temp.location\"\n", + " }\n", + " },\n", + " \"dataset3\": {\n", + " # Completely overrides carbon_source for this dataset\n", + " \"carbon_source\": {\n", + " \"path\": \"media.carbon\" # Different structure\n", + " },\n", + " \"temperature_celsius\": {\n", + " \"path\": \"temp\" # Top-level property\n", + " }\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "print(\"Hierarchical configuration:\")\n", + "print(yaml.dump(hierarchical_config, default_flow_style=False))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Key Benefits of Hierarchical Configuration\n", + "\n", + "1. **DRY Principle**: Define common mappings once at repo level\n", + "2. **Flexibility**: Override for specific datasets that differ\n", + "3. **Maintainability**: Update repo-level mappings to affect all datasets at once\n", + "4. **Clarity**: Explicitly shows which datasets inherit vs override\n", + "\n", + "### How Merging Works\n", + "\n", + "For each dataset, the resolver:\n", + "1. Starts with repo-level mappings\n", + "2. Overlays dataset-specific mappings (these take precedence)\n", + "3. Returns the merged mapping for that dataset\n", + "\n", + "In the example above:\n", + "- `dataset1` gets `carbon_source` from repo_level + its own `temperature_celsius`\n", + "- `dataset2` gets `carbon_source` from repo_level + its own (different) `temperature_celsius`\n", + "- `dataset3` overrides both properties completely" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "The `DatasetFilterResolver` provides:\n", + "\n", + "1. **Flexible filtering** across heterogeneous datasets\n", + "2. **External configuration** - no code changes needed for new filters\n", + "3. **Two-level filtering** - dataset exclusion and field-level matching\n", + "4. **Three output modes** - from lightweight (conditions only) to full data\n", + "5. **Field-aware resolution** - explicit specification of where properties are located\n", + "6. **Automatic path resolution** - handles different nesting levels gracefully\n", + "\n", + "### When to Use Each Mode\n", + "\n", + "- **`conditions`**: Quick exploration - which datasets have matching samples? Which field values match?\n", + "- **`samples`**: Sample metadata analysis - how many samples match? What are their properties?\n", + "- **`full_data`**: Full analysis - retrieve all measurements for downstream processing\n", + "\n", + "### Best Practices for Configuration\n", + "\n", + "#### 1. Field Specification\n", + "- **Always specify `field`** when the property is in field-level definitions\n", + "- **Never specify `field`** when the property is at repo/config level\n", + "- When in doubt, check the datacard: if the property is in a field's `definitions`, use `field`\n", + "\n", + "#### 2. Property Paths\n", + "- Use the simplest path that works (avoid unnecessary nesting)\n", + "- For field-level properties, typically: `media.carbon_source` (not `environmental_conditions.media.carbon_source`)\n", + "- For repo-level properties, include full path: `environmental_conditions.media.carbon_source`\n", + "\n", + "#### 3. Hierarchical Configuration\n", + "- Use `repo_level` for properties shared across all datasets in a repository\n", + "- Use `datasets` to override or extend for specific datasets\n", + "- This keeps configuration DRY and maintainable\n", + "\n", + "#### 4. Filter Development Workflow\n", + "1. **Start with `conditions` mode** to verify your filters work correctly\n", + "2. **Check the results** - do the field values make sense?\n", + "3. **Use `samples` mode** to understand sample counts before downloading full data\n", + "4. **Only use `full_data`** when you're ready to retrieve the actual measurements\n", + "\n", + "#### 5. Configuration Management\n", + "- Create reusable YAML configs for common filter scenarios\n", + "- Document your filter configs with comments explaining the scientific rationale\n", + "- Version control your configs alongside your analysis code\n", + "- Test configs with `conditions` mode before using in production pipelines\n", + "\n", + "#### 6. Debugging\n", + "- If a dataset shows `included: False`, check the `reason` field\n", + "- Use `DataCard.get_field_definitions()` to inspect field structure\n", + "- Use `DataCard.get_experimental_conditions()` to inspect repo-level conditions\n", + "- Start simple (one filter, one dataset) then expand\n", + "\n", + "### Common Patterns\n", + "\n", + "**Pattern 1: Single property, field-level**\n", + "```yaml\n", + "filters:\n", + " carbon_source: [\"D-glucose\"]\n", + "dataset_mappings:\n", + " \"BrentLab/harbison_2004\":\n", + " datasets:\n", + " harbison_2004:\n", + " carbon_source:\n", + " field: condition\n", + " path: media.carbon_source\n", + "```\n", + "\n", + "**Pattern 2: Multiple properties, repo-level**\n", + "```yaml\n", + "filters:\n", + " carbon_source: [\"D-glucose\"]\n", + " temperature_celsius: [30]\n", + "dataset_mappings:\n", + " \"BrentLab/kemmeren_2014\":\n", + " repo_level:\n", + " carbon_source:\n", + " path: environmental_conditions.media.carbon_source\n", + " temperature_celsius:\n", + " path: environmental_conditions.temperature_celsius\n", + "```\n", + "\n", + "**Pattern 3: Mixed (repo-level + dataset overrides)**\n", + "```yaml\n", + "dataset_mappings:\n", + " \"BrentLab/multi_dataset_repo\":\n", + " repo_level:\n", + " carbon_source:\n", + " path: environmental_conditions.media.carbon_source\n", + " datasets:\n", + " dataset1:\n", + " temperature_celsius:\n", + " field: condition\n", + " path: temperature_celsius\n", + "```\n", + "\n", + "### Next Steps\n", + "\n", + "- Explore the `example_filter_config.yaml` file for more examples\n", + "- Try filtering across multiple datasets with different structures\n", + "- Integrate with downstream analysis pipelines using the exported DataFrames\n", + "- Consider creating a library of reusable filter configs for your common use cases" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "tfbpapi-py3.11", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/tutorials/my_filter_config.yaml b/docs/tutorials/my_filter_config.yaml new file mode 100644 index 0000000..c9b4f7e --- /dev/null +++ b/docs/tutorials/my_filter_config.yaml @@ -0,0 +1,24 @@ +dataset_mappings: + BrentLab/harbison_2004: + datasets: + harbison_2004: + carbon_source: + field: condition + path: media.carbon_source + temperature_celsius: + field: condition + path: temperature_celsius + BrentLab/kemmeren_2014: + repo_level: + carbon_source: + path: environmental_conditions.media.carbon_source + temperature_celsius: + path: environmental_conditions.temperature_celsius +filters: + carbon_source: + - D-glucose + - D-galactose + - D-raffinose + temperature_celsius: + - 30 + - 37 diff --git a/docs/tutorials/sample_manager_tutorial.ipynb b/docs/tutorials/sample_manager_tutorial.ipynb index f049afa..1c73ef2 100644 --- a/docs/tutorials/sample_manager_tutorial.ipynb +++ b/docs/tutorials/sample_manager_tutorial.ipynb @@ -38,9 +38,17 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 35, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "SampleManager(0 datasets, 0 samples)\n" + ] + } + ], "source": [ "from tfbpapi.datainfo.sample_manager import SampleManager, SampleNode\n", "from tfbpapi.datainfo import DataCard\n", @@ -62,9 +70,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 36, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Repo-level conditions:\n", + "{}\n" + ] + } + ], "source": [ "# Load a DataCard to see its experimental conditions structure\n", "card = DataCard('BrentLab/harbison_2004')\n", @@ -77,9 +94,45 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 37, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Field-level definition for 'YPD':\n", + "{\n", + " \"description\": \"Rich media baseline condition\",\n", + " \"temperature_celsius\": 30,\n", + " \"cultivation_method\": \"unspecified\",\n", + " \"growth_phase_at_harvest\": {\n", + " \"od600\": 0.8\n", + " },\n", + " \"media\": {\n", + " \"name\": \"YPD\",\n", + " \"carbon_source\": [\n", + " {\n", + " \"compound\": \"D-glucose\",\n", + " \"concentration_percent\": 2\n", + " }\n", + " ],\n", + " \"nitrogen_source\": [\n", + " {\n", + " \"compound\": \"yeast_extract\",\n", + " \"concentration_percent\": 1\n", + " },\n", + " {\n", + " \"compound\": \"peptone\",\n", + " \"concentration_percent\": 2\n", + " }\n", + " ]\n", + " }\n", + "}\n" + ] + } + ], "source": [ "# Get field-level condition definitions\n", "field_defs = card.get_field_definitions('harbison_2004', 'condition')\n", @@ -136,9 +189,23 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 38, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Flattened properties:\n", + " environmental_conditions.cultivation_method: liquid_culture (from repo)\n", + " environmental_conditions.media.carbon_source: D-glucose (from field)\n", + " environmental_conditions.media.name: YPD (from field)\n", + " environmental_conditions.media.nitrogen_source: yeast_extract, peptone (from field)\n", + " environmental_conditions.temperature_celsius: 30 (from repo)\n", + " strain_background: BY4741 (from repo)\n" + ] + } + ], "source": [ "from tfbpapi.datainfo.sample_manager import SampleNode, ConditionFlattener\n", "\n", @@ -181,9 +248,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 39, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Node: SampleNode(BrentLab/harbison_2004:harbison_2004:sample_001, 8 properties)\n", + "Global ID: BrentLab/harbison_2004:harbison_2004:sample_001\n", + "\n", + "Sample properties:\n", + " Temperature: 30\n", + " Carbon source: D-glucose\n" + ] + } + ], "source": [ "# Create a sample node\n", "node = SampleNode(\n", @@ -212,9 +292,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 40, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Query 1 matches: True\n", + "Query 2 matches: True\n", + "Query 3 matches: True\n", + "Query 4 matches: True\n" + ] + } + ], "source": [ "from tfbpapi.datainfo.sample_manager import SampleFilter\n", "\n", @@ -273,9 +364,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 41, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Set 1: ActiveSet(name=glucose_samples, size=3)\n", + "Set 2: ActiveSet(name=heat_stress_samples, size=3)\n" + ] + } + ], "source": [ "from tfbpapi.datainfo.sample_manager import ActiveSet\n", "\n", @@ -298,9 +398,24 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 42, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Union: ActiveSet(name=glucose_or_heat, size=4)\n", + " Sample IDs: ['BrentLab:harbison_2004:sample_001', 'BrentLab:harbison_2004:sample_002', 'BrentLab:harbison_2004:sample_003', 'BrentLab:harbison_2004:sample_004']\n", + "\n", + "Intersection: ActiveSet(name=glucose_and_heat, size=2)\n", + " Sample IDs: ['BrentLab:harbison_2004:sample_002', 'BrentLab:harbison_2004:sample_003']\n", + "\n", + "Difference: ActiveSet(name=glucose_no_heat, size=1)\n", + " Sample IDs: ['BrentLab:harbison_2004:sample_001']\n" + ] + } + ], "source": [ "# Set operations\n", "\n", @@ -331,9 +446,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 43, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 14 condition definitions\n", + "Conditions: ['Acid', 'Alpha', 'BUT14', 'BUT90', 'GAL', 'H2O2Hi', 'H2O2Lo', 'HEAT', 'Pi-', 'RAFF', 'RAPA', 'SM', 'Thi-', 'YPD']\n" + ] + } + ], "source": [ "# For this tutorial, we'll manually create nodes from DataCard information\n", "# In production, you would use: manager.load_from_datacard(\"BrentLab/harbison_2004\", \"harbison_2004\")\n", @@ -352,9 +476,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 44, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Loaded 14 sample nodes\n", + "SampleManager(1 datasets, 14 samples)\n" + ] + } + ], "source": [ "# Manually create nodes for each condition (simulating samples)\n", "# This demonstrates what load_from_datacard will do automatically\n", @@ -385,9 +519,24 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 45, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Properties available in YPD sample:\n", + " cultivation_method: unspecified\n", + " description: Rich media baseline condition\n", + " growth_phase_at_harvest.od600: 0.8\n", + " media.carbon_source: D-glucose\n", + " media.name: YPD\n", + " media.nitrogen_source: yeast_extract, peptone\n", + " temperature_celsius: 30\n" + ] + } + ], "source": [ "# Explore what properties are available\n", "sample_node = demo_manager.get_sample(\"BrentLab/harbison_2004:harbison_2004:YPD\")\n", @@ -409,9 +558,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 46, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 0 conditions with D-glucose\n", + "Conditions: []\n" + ] + } + ], "source": [ "# Find all samples with D-glucose as carbon source\n", "glucose_samples = demo_manager.filter_all({\n", @@ -424,9 +582,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 47, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Found 0 conditions with alternative carbon sources\n", + "Conditions: []\n" + ] + } + ], "source": [ "# Find samples with alternative carbon sources\n", "alt_carbon = demo_manager.filter_all({\n", @@ -442,9 +610,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 48, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Found 0 conditions with additives\n", + "Conditions: []\n" + ] + } + ], "source": [ "# Find samples with media additives (like butanol)\n", "with_additives = demo_manager.filter_all({\n", @@ -464,9 +642,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 49, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Carbon source distribution:\n", + " missing: 14 samples\n" + ] + } + ], "source": [ "# Analyze carbon source distribution\n", "carbon_dist = demo_manager.get_property_distribution(\n", @@ -480,9 +667,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 50, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Media type distribution:\n", + " missing: 14 samples\n" + ] + } + ], "source": [ "# Analyze media names\n", "media_dist = demo_manager.get_property_distribution(\n", @@ -503,9 +700,23 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 51, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Summary of loaded datasets:\n", + " repo_id config_name sample_count \\\n", + "0 BrentLab/harbison_2004 harbison_2004 14 \n", + "\n", + " properties \n", + "0 [description, temperature_celsius, cultivation... \n" + ] + } + ], "source": [ "# Get summary of loaded data\n", "summary = demo_manager.get_summary()\n", @@ -581,7 +792,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "tfbpapi-py3.11", "language": "python", "name": "python3" }, @@ -595,7 +806,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.0" + "version": "3.11.9" } }, "nbformat": 4, diff --git a/example_filter_config.yaml b/example_filter_config.yaml new file mode 100644 index 0000000..bae0ac7 --- /dev/null +++ b/example_filter_config.yaml @@ -0,0 +1,88 @@ +# Example filter configuration for DatasetFilterResolver +# +# This file demonstrates how to specify filters and dataset-specific +# property mappings for heterogeneous datasets. +# +# Configuration supports two formats: +# 1. Legacy format: Direct mappings at repo level (backward compatible) +# 2. Hierarchical format: Repo-level + dataset-specific mappings +# +# Usage: +# from tfbpapi.datainfo.filter_resolver import DatasetFilterResolver +# resolver = DatasetFilterResolver("example_filter_config.yaml") +# results = resolver.resolve_filters([("BrentLab/harbison_2004", "harbison_2004")]) + +# Filters specify acceptable values for each property +# Multiple values = OR logic (match any) +# Multiple properties = AND logic (match all) +filters: + carbon_source: + - "D-glucose" + - "D-galactose" + - "D-raffinose" + + temperature_celsius: + - 30 + - 37 + +# Dataset mappings specify where to find each property in each dataset's DataCard +# The 'path' uses dot notation to navigate nested structures +# For field-level definitions, use 'field' to specify which field contains the property +dataset_mappings: + # FIELD-LEVEL FORMAT + # For datasets where properties are in field definitions (e.g., condition field) + "BrentLab/harbison_2004": + datasets: + harbison_2004: + carbon_source: + field: condition # Look in the 'condition' field's definitions + path: "media.carbon_source" + temperature_celsius: + field: condition + path: "temperature_celsius" + + # REPO-LEVEL FORMAT + # For datasets where properties are at repo/config level + "BrentLab/kemmeren_2014": + repo_level: + carbon_source: + path: "environmental_conditions.media.carbon_source" + temperature_celsius: + path: "environmental_conditions.temperature_celsius" + + # NEW HIERARCHICAL FORMAT + # Supports repo-level mappings that apply to all datasets + # and dataset-specific overrides + "BrentLab/example_multi_dataset_repo": + # Repo-level mappings apply to all datasets in this repository + repo_level: + carbon_source: + path: "environmental_conditions.media.carbon_source" + temperature_celsius: + path: "environmental_conditions.temperature_celsius" + + # Dataset-specific mappings override repo-level for particular datasets + datasets: + dataset1: + # This dataset has field-level definitions + # Override to look in 'condition' field instead of repo level + carbon_source: + field: condition + path: "media.carbon_source" + temperature_celsius: + field: condition + path: "temperature_celsius" + + dataset2: + # Inherits repo-level mappings (no field specification) + # Can add dataset-specific properties + growth_phase: + path: "growth_conditions.phase" + + dataset3: + # Mixed: some properties in 'treatment' field, some at repo level + carbon_source: + field: treatment + path: "media.carbon" + temperature_celsius: + path: "temp" # Direct repo/config level property diff --git a/tfbpapi/datainfo/filter_resolver.py b/tfbpapi/datainfo/filter_resolver.py new file mode 100644 index 0000000..f83f69f --- /dev/null +++ b/tfbpapi/datainfo/filter_resolver.py @@ -0,0 +1,601 @@ +""" +Dataset filter resolver with external configuration for heterogeneous datasets. + +This module provides a simple, configuration-driven approach to filtering samples +across datasets with varying experimental condition structures. Users specify +filters and dataset-specific property paths in external YAML files. + +Key Components: +- DatasetFilterResolver: Main class for applying filters across datasets +- Three output modes: conditions, samples, full_data +- External YAML configuration for filters and property mappings +- Automatic detection of property location (repo/config/field level) + +Example Configuration: + ```yaml + filters: + carbon_source: ["D-glucose", "D-galactose"] + temperature_celsius: [30, 37] + + dataset_mappings: + "BrentLab/harbison_2004": + carbon_source: + path: "environmental_conditions.media.carbon_source" + temperature_celsius: + path: "environmental_conditions.temperature_celsius" + ``` + +Example Usage: + >>> resolver = DatasetFilterResolver("filters.yaml") + >>> results = resolver.resolve_filters( + ... repos=[("BrentLab/harbison_2004", "harbison_2004")], + ... mode="conditions" + ... ) +""" + +from __future__ import annotations + +from pathlib import Path +from typing import Any + +import pandas as pd +import yaml + +from tfbpapi.datainfo import DataCard +from tfbpapi.errors import DataCardError +from tfbpapi.HfQueryAPI import HfQueryAPI + + +def get_nested_value(data: dict, path: str) -> Any: + """ + Navigate nested dict using dot notation. + + Handles missing intermediate keys gracefully by returning None. + If the full path is not found, tries removing common prefixes + like 'environmental_conditions' to handle different nesting levels. + + :param data: Dictionary to navigate + :param path: Dot-separated path (e.g., "environmental_conditions.media.name") + :return: Value at path or None if not found + """ + if not isinstance(data, dict): + return None + + keys = path.split(".") + current = data + + # Try the full path first + for key in keys: + if not isinstance(current, dict) or key not in current: + # Full path failed, try fallback paths + fallback_paths = [] + + # If path starts with environmental_conditions, try without it + if keys[0] == "environmental_conditions" and len(keys) > 1: + fallback_paths.append(".".join(keys[1:])) + + # Try each fallback + for fallback_path in fallback_paths: + fallback_keys = fallback_path.split(".") + fallback_current = data + fallback_success = True + + for fallback_key in fallback_keys: + if ( + not isinstance(fallback_current, dict) + or fallback_key not in fallback_current + ): + fallback_success = False + break + fallback_current = fallback_current[fallback_key] + + if fallback_success: + return fallback_current + + return None + current = current[key] + + return current + + +def extract_compound_names(value: Any) -> list[str]: + """ + Extract compound names from various representations. + + Handles: + - List of dicts: [{"compound": "D-glucose", ...}] -> ["D-glucose"] + - String: "D-glucose" -> ["D-glucose"] + - None or "unspecified" -> [] + + :param value: Value to extract from + :return: List of compound names + """ + if value is None or value == "unspecified": + return [] + + if isinstance(value, str): + return [value] + + if isinstance(value, list): + compounds = [] + for item in value: + if isinstance(item, dict) and "compound" in item: + compounds.append(item["compound"]) + elif isinstance(item, str): + compounds.append(item) + return compounds + + return [] + + +class DatasetFilterResolver: + """ + Resolve filters across heterogeneous datasets using external configuration. + + This class takes an external YAML configuration specifying: + 1. Filter criteria (which values are acceptable for each property) + 2. Repository and dataset-specific property paths (where to find each property) + + Configuration structure supports: + - Repo-level mappings: Apply to all datasets in a repo + - Dataset-level mappings: Override repo-level for specific datasets + + Example: + dataset_mappings: + "BrentLab/my_repo": + repo_level: + carbon_source: + path: environmental_conditions.media.carbon_source + datasets: + dataset1: + temperature_celsius: + path: custom.temp.path + dataset2: + temperature_celsius: + path: other.temp.path + + It then resolves filters across datasets with three output modes: + - conditions: Just which field values match (no data retrieval) + - samples: Sample-level metadata (one row per sample_id) + - full_data: All measurements for matching samples + + Attributes: + config: Loaded configuration dict + filters: Filter criteria from config + mappings: Repository/dataset-specific property paths + """ + + def __init__(self, config_path: Path | str): + """ + Initialize resolver with external configuration. + + :param config_path: Path to YAML configuration file + """ + self.config = self._load_config(Path(config_path)) + self.filters = self.config.get("filters", {}) + self.mappings = self.config.get("dataset_mappings", {}) + + def _load_config(self, config_path: Path) -> dict: + """ + Load YAML configuration file. + + :param config_path: Path to YAML file + :return: Configuration dict + :raises FileNotFoundError: If config file doesn't exist + :raises yaml.YAMLError: If config is invalid YAML + """ + if not config_path.exists(): + raise FileNotFoundError(f"Configuration file not found: {config_path}") + + with open(config_path) as f: + config = yaml.safe_load(f) + + if not isinstance(config, dict): + raise ValueError("Configuration must be a dict") + + return config + + def _get_property_mappings(self, repo_id: str, config_name: str) -> dict: + """ + Get property mappings for a specific repo/dataset combination. + + Merges repo-level and dataset-level mappings, with dataset-level taking precedence. + + Supports configuration formats: + 1. Field-specific path (for field-level definitions): + carbon_source: + field: condition + path: media.carbon_source + + 2. Direct path (for repo/config level): + carbon_source: + path: environmental_conditions.media.carbon_source + + 3. Hierarchical with repo_level and datasets: + repo_level: + carbon_source: + path: ... + datasets: + dataset1: + carbon_source: + field: condition + path: ... + + :param repo_id: Repository ID + :param config_name: Dataset/config name + :return: Merged property mappings dict + """ + if repo_id not in self.mappings: + return {} + + repo_config = self.mappings[repo_id] + + # Check if this is the new hierarchical format + if "repo_level" in repo_config or "datasets" in repo_config: + # New format: merge repo_level and dataset-specific + mappings = {} + + # Start with repo-level mappings (apply to all datasets) + if "repo_level" in repo_config: + mappings.update(repo_config["repo_level"]) + + # Override with dataset-specific mappings + if "datasets" in repo_config and config_name in repo_config["datasets"]: + mappings.update(repo_config["datasets"][config_name]) + + return mappings + else: + # Legacy format: direct property mappings + # This assumes the config at repo level applies to all datasets + return repo_config + + def resolve_filters( + self, + repos: list[tuple[str, str]], + mode: str = "conditions", + token: str | None = None, + ) -> dict[str, Any]: + """ + Resolve filters across datasets. + + :param repos: List of (repo_id, config_name) tuples to check + :param mode: Output mode - "conditions", "samples", or "full_data" + :param token: Optional HuggingFace token for private repos + :return: Dict mapping repo_id to results + :raises ValueError: If mode is invalid + """ + if mode not in ["conditions", "samples", "full_data"]: + raise ValueError( + f"Invalid mode: {mode}. Must be 'conditions', 'samples', or 'full_data'" + ) + + results = {} + + for repo_id, config_name in repos: + try: + # Load DataCard + card = DataCard(repo_id, token=token) + + # Check if this dataset has mappings + if repo_id not in self.mappings: + results[repo_id] = { + "included": False, + "reason": f"No property mappings defined for {repo_id}", + } + continue + + # Check repo/config level filters (Level 1) + included, reason = self._check_repo_config_level( + card, config_name, repo_id + ) + + if not included: + results[repo_id] = {"included": False, "reason": reason} + continue + + # Dataset passes Level 1, check field-level filters (Level 2) + matching_field_values = self._check_field_level( + card, config_name, repo_id + ) + + # Build result based on mode + result = { + "included": True, + "matching_field_values": matching_field_values, + } + + if mode == "conditions": + # Mode 0: Just conditions, no data + pass + elif mode == "samples": + # Mode 1: Sample-level metadata + result["data"] = self._get_sample_metadata( + repo_id, config_name, matching_field_values, token + ) + elif mode == "full_data": + # Mode 2: Full data with measurements + result["data"] = self._get_full_data( + repo_id, config_name, matching_field_values, token + ) + + results[repo_id] = result + + except Exception as e: + results[repo_id] = { + "included": False, + "reason": f"Error processing dataset: {str(e)}", + } + + return results + + def _check_repo_config_level( + self, + card: DataCard, + config_name: str, + repo_id: str, + ) -> tuple[bool, str]: + """ + Check if repo/config level conditions match filters (Level 1). + + :param card: DataCard instance + :param config_name: Configuration name + :param repo_id: Repository ID + :return: (included, reason) tuple + """ + # Get repo and config level conditions + try: + conditions = card.get_experimental_conditions(config_name) + except DataCardError: + conditions = {} + + if not conditions: + # No repo/config conditions to check, include by default + return True, "" + + # Check each filter property at repo/config level + property_mappings = self._get_property_mappings(repo_id, config_name) + + for filter_prop, acceptable_values in self.filters.items(): + if filter_prop not in property_mappings: + continue + + # Get path for this property + path = property_mappings[filter_prop]["path"] + + # Try to get value at this path + value = get_nested_value(conditions, path) + + if value is None: + # Property not specified at repo/config level, skip + continue + + # Extract compound names if this is a compound list + if isinstance(value, list) and value and isinstance(value[0], dict): + actual_values = extract_compound_names(value) + else: + actual_values = [value] if not isinstance(value, list) else value + + # Check if any actual value matches acceptable values + matches = False + for actual in actual_values: + if actual in acceptable_values: + matches = True + break + + if not matches: + return ( + False, + f"{filter_prop}: found {actual_values}, wanted {acceptable_values}", + ) + + return True, "" + + def _check_field_level( + self, + card: DataCard, + config_name: str, + repo_id: str, + ) -> dict[str, list[str]]: + """ + Check field-level conditions and return matching field values (Level 2). + + :param card: DataCard instance + :param config_name: Configuration name + :param repo_id: Repository ID + :return: Dict mapping field names to lists of matching values + """ + matching = {} + property_mappings = self._get_property_mappings(repo_id, config_name) + + # Get config to find fields with role=experimental_condition + config = card.get_config(config_name) + if not config: + return matching + + # Group property mappings by field (if field is specified) + # field_mappings: {field_name: {prop: path, ...}} + field_mappings = {} + general_mappings = {} # Properties without field specification + + for prop, mapping in property_mappings.items(): + if "field" in mapping: + # Field-specific mapping + field_name = mapping["field"] + if field_name not in field_mappings: + field_mappings[field_name] = {} + field_mappings[field_name][prop] = mapping["path"] + else: + # General mapping (repo/config level) + general_mappings[prop] = mapping["path"] + + # Process each field that has mappings + for field_name, prop_paths in field_mappings.items(): + # Check if this field has definitions + definitions = card.get_field_definitions(config_name, field_name) + if not definitions: + continue + + matching_values = [] + + for field_value, definition in definitions.items(): + # Check if this field value matches all filter criteria + matches_all = True + + for filter_prop, acceptable_values in self.filters.items(): + if filter_prop not in prop_paths: + continue + + # Get path for this property + path = prop_paths[filter_prop] + + # Get value from this field value's definition + value = get_nested_value(definition, path) + + if value is None: + # Property not in this definition, doesn't match + matches_all = False + break + + # Extract compound names if needed + if isinstance(value, list) and value and isinstance(value[0], dict): + actual_values = extract_compound_names(value) + else: + actual_values = ( + [value] if not isinstance(value, list) else value + ) + + # Check if any actual value matches acceptable values + matches = False + for actual in actual_values: + if actual in acceptable_values: + matches = True + break + + if not matches: + matches_all = False + break + + if matches_all: + matching_values.append(field_value) + + if matching_values: + matching[field_name] = matching_values + + return matching + + def _get_sample_metadata( + self, + repo_id: str, + config_name: str, + matching_field_values: dict[str, list[str]], + token: str | None = None, + ) -> pd.DataFrame: + """ + Get sample-level metadata (Mode 1). + + Returns one row per sample_id with metadata columns. + + :param repo_id: Repository ID + :param config_name: Configuration name + :param matching_field_values: Dict of field names to matching values + :param token: Optional HuggingFace token + :return: DataFrame with sample metadata + """ + # Initialize query API + api = HfQueryAPI(repo_id, token=token) + + # Build WHERE clause from matching field values + where_conditions = [] + for field_name, values in matching_field_values.items(): + if len(values) == 1: + # Single value: exact match + where_conditions.append(f"{field_name} = '{values[0]}'") + else: + # Multiple values: IN clause + values_str = "', '".join(values) + where_conditions.append(f"{field_name} IN ('{values_str}')") + + where_clause = " AND ".join(where_conditions) if where_conditions else "1=1" + + # Query for distinct sample_id with metadata fields + # Get all columns to understand structure + sql = f""" + SELECT DISTINCT * + FROM {config_name} + WHERE {where_clause} + """ + + try: + df = api.query(sql, config_name) + + # For sample-level, we want one row per sample_id + # Group by sample_id and take first value for other columns + if "sample_id" in df.columns: + # Get metadata columns (exclude measurement columns if possible) + # This is a heuristic - may need refinement + df_samples = df.groupby("sample_id").first().reset_index() + return df_samples + else: + # No sample_id column, return distinct rows + return df + + except Exception as e: + # Return error info + return pd.DataFrame( + {"error": [str(e)], "note": ["Failed to retrieve sample metadata"]} + ) + + def _get_full_data( + self, + repo_id: str, + config_name: str, + matching_field_values: dict[str, list[str]], + token: str | None = None, + ) -> pd.DataFrame: + """ + Get full data with all measurements (Mode 2). + + Returns many rows per sample_id (one per measured feature/target). + + :param repo_id: Repository ID + :param config_name: Configuration name + :param matching_field_values: Dict of field names to matching values + :param token: Optional HuggingFace token + :return: DataFrame with full data + """ + # Initialize query API + api = HfQueryAPI(repo_id, token=token) + + # Build WHERE clause from matching field values + where_conditions = [] + for field_name, values in matching_field_values.items(): + if len(values) == 1: + # Single value: exact match + where_conditions.append(f"{field_name} = '{values[0]}'") + else: + # Multiple values: IN clause + values_str = "', '".join(values) + where_conditions.append(f"{field_name} IN ('{values_str}')") + + where_clause = " AND ".join(where_conditions) if where_conditions else "1=1" + + # Query for all data matching the conditions + sql = f""" + SELECT * + FROM {config_name} + WHERE {where_clause} + """ + + try: + return api.query(sql, config_name) + except Exception as e: + # Return error info + return pd.DataFrame( + {"error": [str(e)], "note": ["Failed to retrieve full data"]} + ) + + def __repr__(self) -> str: + """String representation.""" + n_filters = len(self.filters) + n_datasets = len(self.mappings) + return f"DatasetFilterResolver({n_filters} filters, {n_datasets} datasets configured)" diff --git a/tfbpapi/tests/datainfo/test_filter_resolver.py b/tfbpapi/tests/datainfo/test_filter_resolver.py new file mode 100644 index 0000000..38cfa4b --- /dev/null +++ b/tfbpapi/tests/datainfo/test_filter_resolver.py @@ -0,0 +1,385 @@ +"""Tests for DatasetFilterResolver.""" + +from pathlib import Path +from tempfile import NamedTemporaryFile + +import pandas as pd +import pytest +import yaml + +from tfbpapi.datainfo.filter_resolver import ( + DatasetFilterResolver, + extract_compound_names, + get_nested_value, +) + + +class TestHelperFunctions: + """Test helper functions.""" + + def test_get_nested_value_simple(self): + """Test getting nested values with dot notation.""" + data = { + "environmental_conditions": { + "media": { + "name": "YPD", + "carbon_source": [{"compound": "D-glucose"}] + }, + "temperature_celsius": 30 + } + } + + assert get_nested_value(data, "environmental_conditions.temperature_celsius") == 30 + assert get_nested_value(data, "environmental_conditions.media.name") == "YPD" + assert get_nested_value(data, "nonexistent.path") is None + assert get_nested_value(data, "environmental_conditions.media.nonexistent") is None + + def test_get_nested_value_fallback(self): + """Test fallback path resolution for field-level definitions.""" + # Field-level definition (no environmental_conditions wrapper) + field_def = { + "media": { + "name": "YPD", + "carbon_source": [{"compound": "D-glucose"}] + }, + "temperature_celsius": 30 + } + + # Path with environmental_conditions should fallback to path without it + assert get_nested_value(field_def, "environmental_conditions.media.name") == "YPD" + assert get_nested_value(field_def, "environmental_conditions.temperature_celsius") == 30 + + # Direct path should still work + assert get_nested_value(field_def, "media.name") == "YPD" + assert get_nested_value(field_def, "temperature_celsius") == 30 + + def test_extract_compound_names(self): + """Test extracting compound names from various formats.""" + # List of dicts + value1 = [ + {"compound": "D-glucose", "concentration_percent": 2}, + {"compound": "D-galactose", "concentration_percent": 1} + ] + assert extract_compound_names(value1) == ["D-glucose", "D-galactose"] + + # String + assert extract_compound_names("D-glucose") == ["D-glucose"] + + # None + assert extract_compound_names(None) == [] + + # "unspecified" + assert extract_compound_names("unspecified") == [] + + # Empty list + assert extract_compound_names([]) == [] + + +class TestDatasetFilterResolver: + """Test DatasetFilterResolver class.""" + + @pytest.fixture + def simple_config(self): + """Create a simple test configuration.""" + return { + "filters": { + "carbon_source": ["D-glucose", "D-galactose"], + "temperature_celsius": [30] + }, + "dataset_mappings": { + "BrentLab/harbison_2004": { + "datasets": { + "harbison_2004": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source" + }, + "temperature_celsius": { + "field": "condition", + "path": "temperature_celsius" + } + } + } + } + } + } + + @pytest.fixture + def config_file(self, simple_config, tmp_path): + """Create a temporary config file.""" + config_path = tmp_path / "test_config.yaml" + with open(config_path, 'w') as f: + yaml.dump(simple_config, f) + return config_path + + def test_init(self, config_file): + """Test initialization.""" + resolver = DatasetFilterResolver(config_file) + + assert len(resolver.filters) == 2 + assert "carbon_source" in resolver.filters + assert resolver.filters["carbon_source"] == ["D-glucose", "D-galactose"] + assert len(resolver.mappings) == 1 + assert "BrentLab/harbison_2004" in resolver.mappings + + def test_init_missing_file(self): + """Test initialization with missing config file.""" + with pytest.raises(FileNotFoundError): + DatasetFilterResolver("nonexistent.yaml") + + def test_resolve_filters_mode_conditions(self, config_file): + """Test resolve_filters in conditions mode.""" + resolver = DatasetFilterResolver(config_file) + + # This will actually try to load the DataCard, so it's more of an integration test + # For now, test the structure + results = resolver.resolve_filters( + repos=[("BrentLab/harbison_2004", "harbison_2004")], + mode="conditions" + ) + + assert "BrentLab/harbison_2004" in results + result = results["BrentLab/harbison_2004"] + + # Should have included field + assert "included" in result + + # If included, should have matching_field_values + if result["included"]: + assert "matching_field_values" in result + + def test_resolve_filters_invalid_mode(self, config_file): + """Test resolve_filters with invalid mode.""" + resolver = DatasetFilterResolver(config_file) + + with pytest.raises(ValueError, match="Invalid mode"): + resolver.resolve_filters( + repos=[("BrentLab/harbison_2004", "harbison_2004")], + mode="invalid" + ) + + def test_repr(self, config_file): + """Test string representation.""" + resolver = DatasetFilterResolver(config_file) + repr_str = repr(resolver) + + assert "DatasetFilterResolver" in repr_str + assert "2 filters" in repr_str + assert "1 datasets" in repr_str + + def test_hierarchical_config(self, tmp_path): + """Test hierarchical configuration with repo_level and dataset-specific mappings.""" + config = { + "filters": { + "carbon_source": ["D-glucose"], + "temperature_celsius": [30] + }, + "dataset_mappings": { + "BrentLab/test_repo": { + "repo_level": { + "carbon_source": { + "path": "environmental_conditions.media.carbon_source" + } + }, + "datasets": { + "dataset1": { + "temperature_celsius": { + "field": "condition", + "path": "environmental_conditions.temperature_celsius" + } + }, + "dataset2": { + "temperature_celsius": { + "field": "condition", + "path": "custom.temp.path" + } + } + } + } + } + } + + config_path = tmp_path / "hierarchical_config.yaml" + with open(config_path, 'w') as f: + yaml.dump(config, f) + + resolver = DatasetFilterResolver(config_path) + + # Test property mapping resolution + mappings1 = resolver._get_property_mappings("BrentLab/test_repo", "dataset1") + assert "carbon_source" in mappings1 # From repo_level + assert "temperature_celsius" in mappings1 # From dataset-specific + assert mappings1["temperature_celsius"]["path"] == "environmental_conditions.temperature_celsius" + + mappings2 = resolver._get_property_mappings("BrentLab/test_repo", "dataset2") + assert "carbon_source" in mappings2 # From repo_level + assert "temperature_celsius" in mappings2 # Overridden by dataset-specific + assert mappings2["temperature_celsius"]["path"] == "custom.temp.path" + + +class TestRealDataCards: + """Integration tests with real datacards.""" + + def test_harbison_2004_glucose_filter(self, tmp_path): + """Test filtering harbison_2004 for glucose samples.""" + # Create config for glucose filtering + config = { + "filters": { + "carbon_source": ["D-glucose"] + }, + "dataset_mappings": { + "BrentLab/harbison_2004": { + "datasets": { + "harbison_2004": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source" + } + } + } + } + } + } + + config_path = tmp_path / "glucose_config.yaml" + with open(config_path, 'w') as f: + yaml.dump(config, f) + + resolver = DatasetFilterResolver(config_path) + results = resolver.resolve_filters( + repos=[("BrentLab/harbison_2004", "harbison_2004")], + mode="conditions" + ) + + assert "BrentLab/harbison_2004" in results + result = results["BrentLab/harbison_2004"] + + # Should be included + assert result["included"] is True + + # Should have matching field values + assert "matching_field_values" in result + + # Should have condition field with some matching values + if "condition" in result["matching_field_values"]: + matching = result["matching_field_values"]["condition"] + # YPD, HEAT, H2O2Hi, H2O2Lo, Acid, Alpha, BUT14, BUT90 all have D-glucose + expected_glucose_conditions = ["YPD", "HEAT", "H2O2Hi", "H2O2Lo", "Acid", "Alpha", "BUT14", "BUT90"] + for cond in expected_glucose_conditions: + assert cond in matching, f"{cond} should be in matching conditions" + + def test_harbison_2004_galactose_filter(self, tmp_path): + """Test filtering harbison_2004 for galactose samples.""" + config = { + "filters": { + "carbon_source": ["D-galactose"] + }, + "dataset_mappings": { + "BrentLab/harbison_2004": { + "datasets": { + "harbison_2004": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source" + } + } + } + } + } + } + + config_path = tmp_path / "galactose_config.yaml" + with open(config_path, 'w') as f: + yaml.dump(config, f) + + resolver = DatasetFilterResolver(config_path) + results = resolver.resolve_filters( + repos=[("BrentLab/harbison_2004", "harbison_2004")], + mode="conditions" + ) + + result = results["BrentLab/harbison_2004"] + assert result["included"] is True + + # GAL condition has D-galactose + if "condition" in result["matching_field_values"]: + matching = result["matching_field_values"]["condition"] + assert "GAL" in matching + + def test_harbison_2004_samples_mode(self, tmp_path): + """Test retrieving sample-level metadata.""" + config = { + "filters": { + "carbon_source": ["D-glucose"] + }, + "dataset_mappings": { + "BrentLab/harbison_2004": { + "datasets": { + "harbison_2004": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source" + } + } + } + } + } + } + + config_path = tmp_path / "samples_config.yaml" + with open(config_path, 'w') as f: + yaml.dump(config, f) + + resolver = DatasetFilterResolver(config_path) + results = resolver.resolve_filters( + repos=[("BrentLab/harbison_2004", "harbison_2004")], + mode="samples" + ) + + result = results["BrentLab/harbison_2004"] + assert result["included"] is True + assert "data" in result + + # Should have a DataFrame + df = result["data"] + assert isinstance(df, pd.DataFrame) + assert len(df) > 0 # Should have some samples + + def test_harbison_2004_full_data_mode(self, tmp_path): + """Test retrieving full data.""" + config = { + "filters": { + "carbon_source": ["D-glucose"] + }, + "dataset_mappings": { + "BrentLab/harbison_2004": { + "datasets": { + "harbison_2004": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source" + } + } + } + } + } + } + + config_path = tmp_path / "full_data_config.yaml" + with open(config_path, 'w') as f: + yaml.dump(config, f) + + resolver = DatasetFilterResolver(config_path) + results = resolver.resolve_filters( + repos=[("BrentLab/harbison_2004", "harbison_2004")], + mode="full_data" + ) + + result = results["BrentLab/harbison_2004"] + assert result["included"] is True + assert "data" in result + + # Should have a DataFrame + df = result["data"] + assert isinstance(df, pd.DataFrame) + assert len(df) > 0 # Should have data rows From 10e042c4912fbebe249a7ee4a22bb70e4d1a4398 Mon Sep 17 00:00:00 2001 From: chasem Date: Mon, 15 Dec 2025 11:21:31 -0600 Subject: [PATCH 19/49] intermediate --- docs/tutorials/filter_resolver_tutorial.ipynb | 1358 +++++++---------- docs/tutorials/my_comprehensive_filter.yaml | 22 + docs/tutorials/my_filter_config.yaml | 39 +- example_filter_config.yaml | 105 +- pyproject.toml | 1 + tfbpapi/datainfo/filter_resolver.py | 212 +-- tfbpapi/datainfo/metadata_manager.py | 20 +- tfbpapi/tests/datainfo/conftest.py | 1142 ++++++++++++++ .../tests/datainfo/test_filter_resolver.py | 491 +++--- 9 files changed, 2207 insertions(+), 1183 deletions(-) create mode 100644 docs/tutorials/my_comprehensive_filter.yaml diff --git a/docs/tutorials/filter_resolver_tutorial.ipynb b/docs/tutorials/filter_resolver_tutorial.ipynb index b52449a..16d95f2 100644 --- a/docs/tutorials/filter_resolver_tutorial.ipynb +++ b/docs/tutorials/filter_resolver_tutorial.ipynb @@ -10,22 +10,24 @@ "\n", "## Overview\n", "\n", - "The `DatasetFilterResolver` solves a common problem: filtering experimental data across multiple datasets where experimental conditions are structured differently. Instead of hardcoding dataset-specific logic, you specify:\n", + "The `DatasetFilterResolver` solves a common problem when working with multiple genomics datasets: **experimental conditions are structured differently across datasets**. Some datasets define conditions at the repository level (all samples share the same conditions), while others define conditions per-sample in field definitions.\n", "\n", - "1. **Filter criteria**: Which values are acceptable (e.g., carbon_source: [\"D-glucose\", \"D-galactose\"])\n", - "2. **Property mappings**: Where to find each property in each dataset (e.g., \"environmental_conditions.media.carbon_source\")\n", + "Instead of writing dataset-specific code, you specify:\n", + "\n", + "1. **Filter criteria**: Which values you want (e.g., `carbon_source: [\"D-glucose\", \"D-galactose\"]`)\n", + "2. **Property mappings**: Where to find each property in each dataset (e.g., repo-level vs field-level)\n", "\n", "## Key Features\n", "\n", "- **Two-level filtering**:\n", - " - Level 1: Repo/config level - excludes entire datasets that don't match\n", - " - Level 2: Field level - returns matching field values within datasets\n", + " - **Level 1** (Repo/Config): Excludes entire datasets that don't match\n", + " - **Level 2** (Field): Returns specific field values that match within datasets\n", "- **Three output modes**:\n", " - `conditions`: Just matching field values (no data retrieval)\n", " - `samples`: Sample-level metadata (one row per sample_id)\n", " - `full_data`: All measurements for matching samples\n", - "- **External configuration**: No hardcoded field specifications in codebase\n", - "- **Automatic path resolution**: Handles different nesting levels (repo vs field level)" + "- **External configuration**: No hardcoded dataset logic in your analysis code\n", + "- **Automatic path resolution**: Handles `experimental_conditions` prepending automatically" ] }, { @@ -37,244 +39,199 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chase/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" + ] + } + ], "source": [ "from pathlib import Path\n", "import yaml\n", "import pandas as pd\n", - "from tfbpapi.datainfo.filter_resolver import DatasetFilterResolver" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Example 1: Basic Filtering by Carbon Source\n", - "\n", - "Let's start with a simple example: finding all samples grown on D-glucose." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Understanding Field-Level vs Repo-Level Properties\n", - "\n", - "A key challenge with heterogeneous datasets is that experimental conditions can be located in different places:\n", - "\n", - "**Field-Level Definitions** (e.g., Harbison 2004):\n", - "- Properties are defined within a specific field's `definitions`\n", - "- Example: The `condition` field has definitions for \"YPD\", \"HEAT\", etc.\n", - "- Each definition contains `media`, `temperature_celsius`, etc.\n", - "- **Use the `field` parameter** to specify which field contains the definitions\n", - "\n", - "**Repo/Config-Level Properties** (e.g., Kemmeren 2014):\n", - "- Properties are defined at the top level of the datacard\n", - "- All samples in the dataset share the same conditions\n", - "- Found in `experimental_conditions` at repo or config level\n", - "- **No `field` parameter needed** - just specify the path\n", - "\n", - "Let's examine actual datacards to see these differences:" + "from tfbpapi.datainfo.filter_resolver import DatasetFilterResolver\n", + "from tfbpapi.datainfo import DataCard" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Configuration Implications\n", - "\n", - "Based on the actual datacard structures above:\n", - "\n", - "**For Harbison 2004** (field-level):\n", - "```yaml\n", - "\"BrentLab/harbison_2004\":\n", - " datasets:\n", - " harbison_2004:\n", - " carbon_source:\n", - " field: condition # Must specify which field\n", - " path: media.carbon_source # Path within each field value's definition\n", - "```\n", - "\n", - "**For Kemmeren 2014** (repo-level):\n", - "```yaml\n", - "\"BrentLab/kemmeren_2014\":\n", - " repo_level:\n", - " carbon_source:\n", - " path: environmental_conditions.media.carbon_source # No 'field' needed\n", - "```\n", + "## Understanding Dataset Structures\n", "\n", - "### When to Use Which Format\n", + "Before filtering, let's examine how experimental conditions are structured in two representative datasets.\n", "\n", - "**Use `field` specification when**:\n", - "- The property varies between samples (different values in the dataset)\n", - "- The property is defined in a field's `definitions` section\n", - "- You need to filter to specific field values (e.g., only \"YPD\" condition)\n", - "- Example: Harbison 2004 where each condition has its own media/temperature\n", + "### Repo-Level Conditions: Kemmeren 2014\n", "\n", - "**Use repo-level (no `field`) when**:\n", - "- The property is the same for all samples in the dataset\n", - "- The property is in the top-level `experimental_conditions`\n", - "- You're filtering entire datasets in/out based on shared properties\n", - "- Example: Kemmeren 2014 where all samples share the same growth conditions" + "All samples in this dataset share the same experimental conditions defined at the repository level." ] }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "================================================================================\n", - "KEMMEREN 2014 - Repo-Level Experimental Conditions\n", - "================================================================================\n", - "\n", - "Repo-level experimental_conditions exists: True\n", - "\n", + "Kemmeren 2014 - Repo-Level Experimental Conditions\n", + "============================================================\n", "Temperature: 30°C\n", - "Media name: synthetic_complete\n", + "Media: synthetic_complete\n", "Carbon source: ['D-glucose']\n", - "Concentrations: [2]%\n", "\n", - "No field-level definitions found\n", - "\n", - "================================================================================\n", - "Key Observation: Properties are at repo level, shared by all samples\n", - "================================================================================\n" + "All samples in this dataset share these conditions.\n" ] } ], "source": [ - "# Load Kemmeren 2014 (repo-level example)\n", - "from tfbpapi.datainfo import DataCard\n", - "\n", + "# Load Kemmeren 2014 datacard\n", "kemmeren_card = DataCard(\"BrentLab/kemmeren_2014\")\n", "\n", - "print(\"=\" * 80)\n", - "print(\"KEMMEREN 2014 - Repo-Level Experimental Conditions\")\n", - "print(\"=\" * 80)\n", - "\n", - "# Get repo-level experimental conditions (returns a dict)\n", + "# Get repo-level experimental conditions\n", "exp_conds = kemmeren_card.get_experimental_conditions(\"kemmeren_2014\")\n", - "print(f\"\\nRepo-level experimental_conditions exists: {bool(exp_conds)}\")\n", - "\n", - "if exp_conds:\n", - " # exp_conds is a dict, not a Pydantic model\n", - " print(f\"\\nTemperature: {exp_conds.get('temperature_celsius')}°C\")\n", - " \n", - " media = exp_conds.get('media', {})\n", - " print(f\"Media name: {media.get('name')}\")\n", - " \n", - " carbon_source = media.get('carbon_source', [])\n", - " if carbon_source:\n", - " compounds = [cs.get('compound') for cs in carbon_source]\n", - " concentrations = [cs.get('concentration_percent') for cs in carbon_source]\n", - " print(f\"Carbon source: {compounds}\")\n", - " print(f\"Concentrations: {concentrations}%\")\n", - "\n", - "# Check if there are field-level definitions\n", - "config = kemmeren_card.get_config(\"kemmeren_2014\")\n", - "has_field_defs = False\n", - "for feature in config.dataset_info.features:\n", - " if feature.definitions:\n", - " has_field_defs = True\n", - " print(f\"\\nField '{feature.name}' has definitions: {list(feature.definitions.keys())}\")\n", - "\n", - "if not has_field_defs:\n", - " print(\"\\nNo field-level definitions found\")\n", - "\n", - "print(\"\\n\" + \"=\" * 80)\n", - "print(\"Key Observation: Properties are at repo level, shared by all samples\")\n", - "print(\"=\" * 80)" + "\n", + "print(\"Kemmeren 2014 - Repo-Level Experimental Conditions\")\n", + "print(\"=\" * 60)\n", + "print(f\"Temperature: {exp_conds.get('temperature_celsius')}°C\")\n", + "print(f\"Media: {exp_conds.get('media', {}).get('name')}\")\n", + "\n", + "carbon_source = exp_conds.get('media', {}).get('carbon_source', [])\n", + "if carbon_source:\n", + " compounds = [cs.get('compound') for cs in carbon_source]\n", + " print(f\"Carbon source: {compounds}\")\n", + "\n", + "print(\"\\nAll samples in this dataset share these conditions.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Field-Level Conditions: Harbison 2004\n", + "\n", + "This dataset has different experimental conditions for different samples, defined in the `condition` field's definitions." ] }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "================================================================================\n", - "HARBISON 2004 - Field-Level Definitions\n", - "================================================================================\n", - "\n", "Repo-level experimental_conditions: {}\n", "\n", - "Field 'condition' has 14 definitions\n", - "Condition values: ['YPD', 'SM', 'RAPA', 'H2O2Hi', 'H2O2Lo', 'Acid', 'Alpha', 'BUT14', 'BUT90', 'Thi-', 'GAL', 'HEAT', 'Pi-', 'RAFF']\n", - "\n", - "Example definitions:\n", - "\n", - "YPD:\n", - " temperature_celsius: 30\n", - " media.name: YPD\n", - " media.carbon_source: ['D-glucose']\n", + "Harbison 2004 - Field-Level Definitions\n", + "============================================================\n", + "Number of condition definitions: 14\n", + "Condition names: ['YPD', 'SM', 'RAPA', 'H2O2Hi', 'H2O2Lo', 'Acid', 'Alpha', 'BUT14', 'BUT90', 'Thi-', 'GAL', 'HEAT', 'Pi-', 'RAFF']\n", "\n", - "HEAT:\n", - " temperature_celsius: None\n", - " media.name: YPD\n", - " media.carbon_source: ['D-glucose']\n", + "Example conditions:\n", + " YPD: ['D-glucose'], 30°C\n", + " GAL: ['D-galactose'], 30°C\n", + " HEAT: ['D-glucose'], varies°C\n", "\n", - "GAL:\n", - " temperature_celsius: 30\n", - " media.name: yeast_extract_peptone\n", - " media.carbon_source: ['D-galactose']\n", - "\n", - "================================================================================\n", - "Key Observation: Properties are in field definitions, NOT at repo level\n", - "================================================================================\n" + "Each sample can have a different condition value.\n" ] } ], "source": [ - "from tfbpapi.datainfo import DataCard\n", - "import json\n", - "\n", - "# Load Harbison 2004 (field-level example)\n", + "# Load Harbison 2004 datacard\n", "harbison_card = DataCard(\"BrentLab/harbison_2004\")\n", "\n", - "print(\"=\" * 80)\n", - "print(\"HARBISON 2004 - Field-Level Definitions\")\n", - "print(\"=\" * 80)\n", - "\n", - "# Check if there are repo-level experimental conditions\n", + "# Check for repo-level conditions\n", "exp_conds = harbison_card.get_experimental_conditions(\"harbison_2004\")\n", - "print(f\"\\nRepo-level experimental_conditions: {exp_conds}\")\n", + "print(f\"Repo-level experimental_conditions: {exp_conds}\")\n", "\n", - "# Get field definitions for the 'condition' field\n", + "# Get field-level definitions\n", "condition_defs = harbison_card.get_field_definitions(\"harbison_2004\", \"condition\")\n", - "print(f\"\\nField 'condition' has {len(condition_defs)} definitions\")\n", - "print(f\"Condition values: {list(condition_defs.keys())}\")\n", "\n", - "# Show a few example definitions\n", - "print(\"\\nExample definitions:\")\n", - "for cond_name in [\"YPD\", \"HEAT\", \"GAL\"]:\n", + "print(\"\\nHarbison 2004 - Field-Level Definitions\")\n", + "print(\"=\" * 60)\n", + "print(f\"Number of condition definitions: {len(condition_defs)}\")\n", + "print(f\"Condition names: {list(condition_defs.keys())}\")\n", + "\n", + "# Show examples\n", + "print(\"\\nExample conditions:\")\n", + "for cond_name in [\"YPD\", \"GAL\", \"HEAT\"]:\n", " if cond_name in condition_defs:\n", " definition = condition_defs[cond_name]\n", - " print(f\"\\n{cond_name}:\")\n", - " print(f\" temperature_celsius: {definition.get('temperature_celsius')}\")\n", " media = definition.get('media', {})\n", - " print(f\" media.name: {media.get('name')}\")\n", - " carbon_source = media.get('carbon_source', [])\n", - " if carbon_source:\n", - " compounds = [cs.get('compound') for cs in carbon_source]\n", - " print(f\" media.carbon_source: {compounds}\")\n", - "\n", - "print(\"\\n\" + \"=\" * 80)\n", - "print(\"Key Observation: Properties are in field definitions, NOT at repo level\")\n", - "print(\"=\" * 80)" + " carbon = media.get('carbon_source', [])\n", + " compounds = [cs.get('compound') for cs in carbon] if carbon else []\n", + " temp = definition.get('temperature_celsius', 'varies')\n", + " print(f\" {cond_name}: {compounds}, {temp}°C\")\n", + "\n", + "print(\"\\nEach sample can have a different condition value.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Configuration Format\n", + "\n", + "The filter configuration has two main sections:\n", + "\n", + "```yaml\n", + "# 1. Filter criteria - what values you want\n", + "filters:\n", + " carbon_source: [\"D-glucose\", \"D-galactose\"]\n", + " temperature_celsius: [30]\n", + "\n", + "# 2. Property mappings - where to find each property in each dataset\n", + "BrentLab/repo_name:\n", + " property1:\n", + " path: media.name # Repo-wide property\n", + " \n", + " dataset:\n", + " dataset_name:\n", + " property2:\n", + " path: temperature_celsius # Dataset-specific property\n", + " \n", + " property3:\n", + " field: condition # Field-level property\n", + " path: media.carbon_source\n", + "```\n", + "\n", + "### Three Types of Property Configurations\n", + "\n", + "1. **Repo-wide**: Property applies to ALL datasets in the repository\n", + " - Paths automatically get `experimental_conditions.` prepended\n", + " - Example: `path: media.name` → resolves to `experimental_conditions.media.name`\n", + "\n", + "2. **Dataset-specific**: Property applies only to a specific dataset (at config level)\n", + " - Also gets `experimental_conditions.` prepended\n", + " - Example: `path: temperature_celsius` → resolves to `experimental_conditions.temperature_celsius`\n", + "\n", + "3. **Field-level**: Property varies per sample, defined in field definitions\n", + " - Requires `field` parameter to specify which field\n", + " - Path is used directly on field definitions (NO prepending)\n", + " - Example: `field: condition, path: media.carbon_source` → looks in condition field definitions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 1: Basic Filtering by Carbon Source\n", + "\n", + "Let's filter for samples grown on D-glucose in Harbison 2004." ] }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -282,13 +239,12 @@ "output_type": "stream", "text": [ "Configuration:\n", - "dataset_mappings:\n", - " BrentLab/harbison_2004:\n", - " datasets:\n", - " harbison_2004:\n", - " carbon_source:\n", - " field: condition\n", - " path: media.carbon_source\n", + "BrentLab/harbison_2004:\n", + " dataset:\n", + " harbison_2004:\n", + " carbon_source:\n", + " field: condition\n", + " path: media.carbon_source\n", "filters:\n", " carbon_source:\n", " - D-glucose\n", @@ -297,32 +253,30 @@ } ], "source": [ - "# Create a filter configuration\n", + "# Create filter configuration\n", "glucose_config = {\n", " \"filters\": {\n", " \"carbon_source\": [\"D-glucose\"]\n", " },\n", - " \"dataset_mappings\": {\n", - " \"BrentLab/harbison_2004\": {\n", - " \"datasets\": {\n", - " \"harbison_2004\": {\n", - " \"carbon_source\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"media.carbon_source\"\n", - " }\n", + " \"BrentLab/harbison_2004\": {\n", + " \"dataset\": {\n", + " \"harbison_2004\": {\n", + " \"carbon_source\": {\n", + " \"field\": \"condition\",\n", + " \"path\": \"media.carbon_source\"\n", " }\n", " }\n", " }\n", " }\n", "}\n", "\n", - "# Save to temporary file\n", + "# Save to file\n", "config_path = Path(\"/tmp/glucose_filter.yaml\")\n", "with open(config_path, 'w') as f:\n", " yaml.dump(glucose_config, f)\n", "\n", "print(\"Configuration:\")\n", - "print(yaml.dump(glucose_config, default_flow_style=False))\n" + "print(yaml.dump(glucose_config, default_flow_style=False))" ] }, { @@ -331,66 +285,57 @@ "source": [ "### Mode 0: Conditions Only\n", "\n", - "Get just the matching field values without retrieving any data." + "Get just the matching field values without retrieving any data. This is the fastest mode for exploration." ] }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ + "Included: True\n", "\n", - "Results:\n", - "\n", - "BrentLab/harbison_2004:\n", - " Included: True\n", - " Matching conditions:\n", - " condition: ['YPD', 'RAPA', 'H2O2Hi', 'H2O2Lo', 'Acid', 'Alpha', 'BUT14', 'BUT90', 'HEAT']\n" + "Matching conditions:\n", + " condition: ['YPD', 'RAPA', 'H2O2Hi', 'H2O2Lo', 'Acid', 'Alpha', 'BUT14', 'BUT90', 'HEAT']\n" ] } ], "source": [ - "# Create resolver\n", + "# Create resolver and run filter\n", "resolver = DatasetFilterResolver(config_path)\n", "\n", - "# Get matching conditions\n", "results = resolver.resolve_filters(\n", " repos=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", " mode=\"conditions\"\n", ")\n", "\n", - "print(\"\\nResults:\")\n", - "for repo_id, result in results.items():\n", - " print(f\"\\n{repo_id}:\")\n", - " if result[\"included\"]:\n", - " print(f\" Included: True\")\n", - " print(f\" Matching conditions:\")\n", - " for field, values in result[\"matching_field_values\"].items():\n", - " print(f\" {field}: {values}\")\n", - " else:\n", - " print(f\" Included: False\")\n", - " print(f\" Reason: {result['reason']}\")" + "# Display results\n", + "result = results[\"BrentLab/harbison_2004\"]\n", + "print(f\"Included: {result['included']}\")\n", + "\n", + "if result['included']:\n", + " print(\"\\nMatching conditions:\")\n", + " for field, values in result['matching_field_values'].items():\n", + " print(f\" {field}: {values}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Understanding the Results\n", + "**Understanding the results:**\n", "\n", - "The `matching_field_values` shows which values of the `condition` field have D-glucose:\n", - "- YPD: Rich media with 2% glucose\n", + "The resolver found all condition field values where `media.carbon_source` contains D-glucose:\n", + "- YPD: Rich media with glucose\n", "- HEAT: Heat stress with glucose\n", "- H2O2Hi/H2O2Lo: Oxidative stress with glucose\n", - "- Acid: Acidic stress with glucose\n", - "- Alpha: Alpha factor arrest with glucose\n", - "- BUT14/BUT90: Butanol treatment with glucose\n", + "- Acid, Alpha, BUT14, BUT90, RAPA: Various stresses with glucose\n", "\n", - "Notice that GAL (galactose media) is NOT in the list - it was correctly filtered out." + "Notice GAL is NOT included - it uses galactose, not glucose." ] }, { @@ -399,42 +344,39 @@ "source": [ "## Example 2: Multiple Filter Criteria\n", "\n", - "Let's filter for samples with both D-glucose AND 30C temperature." + "Filter for samples with both D-glucose AND 30°C temperature." ] }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Conditions with D-glucose AND 30C:\n", + "Conditions with D-glucose AND 30°C:\n", "['YPD', 'RAPA', 'H2O2Hi', 'H2O2Lo', 'Acid', 'Alpha', 'BUT14', 'BUT90']\n" ] } ], "source": [ - "# Config with multiple filters\n", "multi_filter_config = {\n", " \"filters\": {\n", " \"carbon_source\": [\"D-glucose\"],\n", " \"temperature_celsius\": [30]\n", " },\n", - " \"dataset_mappings\": {\n", - " \"BrentLab/harbison_2004\": {\n", - " \"datasets\": {\n", - " \"harbison_2004\": {\n", - " \"carbon_source\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"media.carbon_source\"\n", - " },\n", - " \"temperature_celsius\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"temperature_celsius\"\n", - " }\n", + " \"BrentLab/harbison_2004\": {\n", + " \"dataset\": {\n", + " \"harbison_2004\": {\n", + " \"carbon_source\": {\n", + " \"field\": \"condition\",\n", + " \"path\": \"media.carbon_source\"\n", + " },\n", + " \"temperature_celsius\": {\n", + " \"field\": \"condition\",\n", + " \"path\": \"temperature_celsius\"\n", " }\n", " }\n", " }\n", @@ -451,40 +393,191 @@ " mode=\"conditions\"\n", ")\n", "\n", - "print(\"Conditions with D-glucose AND 30C:\")\n", "matching = results[\"BrentLab/harbison_2004\"][\"matching_field_values\"][\"condition\"]\n", - "print(matching)\n" + "print(\"Conditions with D-glucose AND 30°C:\")\n", + "print(matching)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Notice HEAT is now excluded - it has glucose but uses a temperature shift to 37°C, not 30°C.\n", + "\n", + "**Multiple filter criteria use AND logic** - all criteria must match." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 3: Repo-Level Filtering\n", + "\n", + "Filter Kemmeren 2014, which has repo-level experimental conditions (no field specification needed)." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Included: True\n", + "\n", + "No field-level filtering - entire dataset matches at repo level\n" + ] + } + ], + "source": [ + "repo_level_config = {\n", + " \"filters\": {\n", + " \"carbon_source\": [\"D-glucose\"],\n", + " \"temperature_celsius\": [30]\n", + " },\n", + " \"BrentLab/kemmeren_2014\": {\n", + " \"dataset\": {\n", + " \"kemmeren_2014\": {\n", + " \"carbon_source\": {\n", + " \"path\": \"media.carbon_source\" # No 'field' - repo-level property\n", + " },\n", + " \"temperature_celsius\": {\n", + " \"path\": \"temperature_celsius\"\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "config_path = Path(\"/tmp/repo_level_filter.yaml\")\n", + "with open(config_path, 'w') as f:\n", + " yaml.dump(repo_level_config, f)\n", + "\n", + "resolver = DatasetFilterResolver(config_path)\n", + "results = resolver.resolve_filters(\n", + " repos=[(\"BrentLab/kemmeren_2014\", \"kemmeren_2014\")],\n", + " mode=\"conditions\"\n", + ")\n", + "\n", + "result = results[\"BrentLab/kemmeren_2014\"]\n", + "print(f\"Included: {result['included']}\")\n", + "\n", + "if result['included']:\n", + " if result['matching_field_values']:\n", + " print(\"\\nMatching field values:\")\n", + " for field, values in result['matching_field_values'].items():\n", + " print(f\" {field}: {values}\")\n", + " else:\n", + " print(\"\\nNo field-level filtering - entire dataset matches at repo level\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Notice that HEAT is now excluded - it has glucose but is at 37C, not 30C. Multiple filter criteria use AND logic." + "**Key difference**: Repo-level filtering includes/excludes the entire dataset. Since kemmeren_2014 has D-glucose at 30°C at the repo level, ALL samples in the dataset match." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Example 3: Mode 1 - Sample-Level Metadata\n", + "## Example 4: Multiple Datasets\n", "\n", - "Retrieve actual sample metadata (one row per sample_id) for matching conditions." + "Filter across both harbison_2004 (field-level) and kemmeren_2014 (repo-level) in a single query." ] }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Retrieved 304 samples\n", + "Results across multiple datasets:\n", "\n", - "DataFrame shape: (304, 9)\n", + "BrentLab/harbison_2004:\n", + " Included: True\n", + " condition: ['YPD', 'RAPA', 'H2O2Hi']...\n", "\n", + "BrentLab/kemmeren_2014:\n", + " Included: True\n", + "\n" + ] + } + ], + "source": [ + "multi_dataset_config = {\n", + " \"filters\": {\n", + " \"carbon_source\": [\"D-glucose\"]\n", + " },\n", + " \"BrentLab/harbison_2004\": {\n", + " \"dataset\": {\n", + " \"harbison_2004\": {\n", + " \"carbon_source\": {\n", + " \"field\": \"condition\",\n", + " \"path\": \"media.carbon_source\"\n", + " }\n", + " }\n", + " }\n", + " },\n", + " \"BrentLab/kemmeren_2014\": {\n", + " \"dataset\": {\n", + " \"kemmeren_2014\": {\n", + " \"carbon_source\": {\n", + " \"path\": \"media.carbon_source\"\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "config_path = Path(\"/tmp/multi_dataset_filter.yaml\")\n", + "with open(config_path, 'w') as f:\n", + " yaml.dump(multi_dataset_config, f)\n", + "\n", + "resolver = DatasetFilterResolver(config_path)\n", + "results = resolver.resolve_filters(\n", + " repos=[\n", + " (\"BrentLab/harbison_2004\", \"harbison_2004\"),\n", + " (\"BrentLab/kemmeren_2014\", \"kemmeren_2014\")\n", + " ],\n", + " mode=\"conditions\"\n", + ")\n", + "\n", + "print(\"Results across multiple datasets:\\n\")\n", + "for repo_id, result in results.items():\n", + " print(f\"{repo_id}:\")\n", + " print(f\" Included: {result['included']}\")\n", + " if result['included'] and result.get('matching_field_values'):\n", + " for field, values in result['matching_field_values'].items():\n", + " print(f\" {field}: {values[:3]}...\") # Show first 3\n", + " print()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 5: Retrieving Sample Metadata (Mode 1)\n", + "\n", + "Once you've identified matching conditions, retrieve sample-level metadata." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Retrieved 310 samples\n", "Columns: ['sample_id', 'db_id', 'regulator_locus_tag', 'regulator_symbol', 'condition', 'target_locus_tag', 'target_symbol', 'effect', 'pvalue']\n", "\n", "First few rows:\n" @@ -545,7 +638,7 @@ "type": "float" } ], - "ref": "a9bc83e5-ec64-4c54-a0b5-b8efb910402a", + "ref": "b8896bc0-bb22-468e-9c3f-f8e1715f804c", "rows": [ [ "0", @@ -554,10 +647,10 @@ "YSC0017", "MATA1", "YPD", - "YAL023C", - "PMT2", - "0.74920592", - "0.83707059" + "YBL068W", + "PRS4", + "0.8599428", + "0.72834544" ], [ "1", @@ -566,10 +659,10 @@ "YAL051W", "OAF1", "YPD", - "YAL047C", - "SPC72", - "1.9661134", - "0.0076229595" + "YAL015C", + "NTG1", + "1.3079075", + "0.17689452" ], [ "2", @@ -578,10 +671,10 @@ "YBL005W", "PDR3", "YPD", - "YBR001C", - "NTH2", - "0.97462425", - "0.56597448" + "YAL005C", + "SSA1", + "0.76584559", + "0.92357641" ], [ "3", @@ -590,10 +683,10 @@ "YBL008W", "HIR1", "YPD", - "YBL037W", - "APL3", - "1.0555759", - "0.35839913" + "YAL030W", + "SNC1", + "0.92566636", + "0.69746121" ], [ "4", @@ -602,10 +695,10 @@ "YBL021C", "HAP3", "YPD", - "YAL001C", - "TFC3", - "0.84731661", - "0.81162232" + "YAR035W", + "YAT1", + "0.83719358", + "0.85707131" ] ], "shape": { @@ -651,10 +744,10 @@ " YSC0017\n", " MATA1\n", " YPD\n", - " YAL023C\n", - " PMT2\n", - " 0.749206\n", - " 0.837071\n", + " YBL068W\n", + " PRS4\n", + " 0.859943\n", + " 0.728345\n", " \n", " \n", " 1\n", @@ -663,10 +756,10 @@ " YAL051W\n", " OAF1\n", " YPD\n", - " YAL047C\n", - " SPC72\n", - " 1.966113\n", - " 0.007623\n", + " YAL015C\n", + " NTG1\n", + " 1.307908\n", + " 0.176895\n", " \n", " \n", " 2\n", @@ -675,10 +768,10 @@ " YBL005W\n", " PDR3\n", " YPD\n", - " YBR001C\n", - " NTH2\n", - " 0.974624\n", - " 0.565974\n", + " YAL005C\n", + " SSA1\n", + " 0.765846\n", + " 0.923576\n", " \n", " \n", " 3\n", @@ -687,10 +780,10 @@ " YBL008W\n", " HIR1\n", " YPD\n", - " YBL037W\n", - " APL3\n", - " 1.055576\n", - " 0.358399\n", + " YAL030W\n", + " SNC1\n", + " 0.925666\n", + " 0.697461\n", " \n", " \n", " 4\n", @@ -699,10 +792,10 @@ " YBL021C\n", " HAP3\n", " YPD\n", - " YAL001C\n", - " TFC3\n", - " 0.847317\n", - " 0.811622\n", + " YAR035W\n", + " YAT1\n", + " 0.837194\n", + " 0.857071\n", " \n", " \n", "\n", @@ -717,51 +810,73 @@ "4 5 4.0 YBL021C HAP3 YPD \n", "\n", " target_locus_tag target_symbol effect pvalue \n", - "0 YAL023C PMT2 0.749206 0.837071 \n", - "1 YAL047C SPC72 1.966113 0.007623 \n", - "2 YBR001C NTH2 0.974624 0.565974 \n", - "3 YBL037W APL3 1.055576 0.358399 \n", - "4 YAL001C TFC3 0.847317 0.811622 " + "0 YBL068W PRS4 0.859943 0.728345 \n", + "1 YAL015C NTG1 1.307908 0.176895 \n", + "2 YAL005C SSA1 0.765846 0.923576 \n", + "3 YAL030W SNC1 0.925666 0.697461 \n", + "4 YAR035W YAT1 0.837194 0.857071 " ] }, - "execution_count": 57, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "# Use same config but change mode to 'samples'\n", - "results_samples = resolver.resolve_filters(\n", + "# Reuse the glucose filter config\n", + "glucose_config = {\n", + " \"filters\": {\n", + " \"carbon_source\": [\"D-glucose\"]\n", + " },\n", + " \"BrentLab/harbison_2004\": {\n", + " \"dataset\": {\n", + " \"harbison_2004\": {\n", + " \"carbon_source\": {\n", + " \"field\": \"condition\",\n", + " \"path\": \"media.carbon_source\"\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "config_path = Path(\"/tmp/glucose_filter.yaml\")\n", + "with open(config_path, 'w') as f:\n", + " yaml.dump(glucose_config, f)\n", + "\n", + "resolver = DatasetFilterResolver(config_path)\n", + "\n", + "# Use samples mode\n", + "results = resolver.resolve_filters(\n", " repos=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", " mode=\"samples\"\n", ")\n", "\n", - "# Get the DataFrame\n", - "df_samples = results_samples[\"BrentLab/harbison_2004\"][\"data\"]\n", + "df_samples = results[\"BrentLab/harbison_2004\"][\"data\"]\n", "\n", "print(f\"Retrieved {len(df_samples)} samples\")\n", - "print(f\"\\nDataFrame shape: {df_samples.shape}\")\n", - "print(f\"\\nColumns: {list(df_samples.columns)}\")\n", - "print(f\"\\nFirst few rows:\")\n", + "print(f\"Columns: {list(df_samples.columns)}\")\n", + "print(\"\\nFirst few rows:\")\n", "df_samples.head()" ] }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Condition distribution:\n", + "Condition distribution in retrieved samples:\n", "condition\n", "YPD 204\n", "H2O2Hi 39\n", "H2O2Lo 28\n", "RAPA 14\n", "BUT14 8\n", + "HEAT 6\n", "Alpha 5\n", "BUT90 4\n", "Acid 2\n", @@ -770,8 +885,8 @@ } ], "source": [ - "# Check the distribution of conditions in the retrieved samples\n", - "print(\"Condition distribution:\")\n", + "# Analyze condition distribution\n", + "print(\"Condition distribution in retrieved samples:\")\n", "print(df_samples['condition'].value_counts())" ] }, @@ -779,25 +894,22 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Example 4: Mode 2 - Full Data\n", + "## Example 6: Retrieving Full Data (Mode 2)\n", "\n", - "Retrieve all measurements for matching samples (many rows per sample_id)." + "Retrieve all measurements for matching samples (many rows per sample)." ] }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Retrieved 1,892,704 rows (measurements)\n", - "\n", - "DataFrame shape: (1892704, 9)\n", - "\n", - "Columns: ['sample_id', 'db_id', 'regulator_locus_tag', 'regulator_symbol', 'condition', 'target_locus_tag', 'target_symbol', 'effect', 'pvalue']\n", + "Retrieved 1,930,060 rows (measurements)\n", + "DataFrame shape: (1930060, 9)\n", "\n", "First few rows:\n" ] @@ -857,7 +969,7 @@ "type": "float" } ], - "ref": "a828c111-7829-4bcd-a77c-ff5dbc95ba4a", + "ref": "94bbe0c4-757e-406b-aef0-b82efd0905fe", "rows": [ [ "0", @@ -1036,51 +1148,49 @@ "4 YAL005C SSA1 NaN NaN " ] }, - "execution_count": 59, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "# Use same config but change mode to 'full_data'\n", + "# Use full_data mode\n", "results_full = resolver.resolve_filters(\n", " repos=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", " mode=\"full_data\"\n", ")\n", "\n", - "# Get the DataFrame\n", "df_full = results_full[\"BrentLab/harbison_2004\"][\"data\"]\n", "\n", "print(f\"Retrieved {len(df_full):,} rows (measurements)\")\n", - "print(f\"\\nDataFrame shape: {df_full.shape}\")\n", - "print(f\"\\nColumns: {list(df_full.columns)}\")\n", - "print(f\"\\nFirst few rows:\")\n", + "print(f\"DataFrame shape: {df_full.shape}\")\n", + "print(\"\\nFirst few rows:\")\n", "df_full.head()" ] }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Sample-level metadata: 304 samples\n", - "Full data: 1,892,704 measurements\n", + "Samples mode: 310 samples\n", + "Full data mode: 1,930,060 measurements\n", "Average measurements per sample: 6,226\n" ] } ], "source": [ - "# Compare to samples mode\n", - "n_samples = df_samples['sample_id'].nunique() if 'sample_id' in df_samples.columns else len(df_samples)\n", + "# Compare modes\n", + "n_samples = len(df_samples)\n", "n_measurements = len(df_full)\n", "measurements_per_sample = n_measurements / n_samples if n_samples > 0 else 0\n", "\n", - "print(f\"Sample-level metadata: {n_samples} samples\")\n", - "print(f\"Full data: {n_measurements:,} measurements\")\n", + "print(f\"Samples mode: {n_samples} samples\")\n", + "print(f\"Full data mode: {n_measurements:,} measurements\")\n", "print(f\"Average measurements per sample: {measurements_per_sample:,.0f}\")" ] }, @@ -1088,295 +1198,91 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Example 5: Filtering Across Multiple Datasets\n", + "## Example 7: Using Repo-Wide Properties\n", "\n", - "The real power comes from filtering across multiple datasets with different structures." + "For repositories where a property applies to ALL datasets, use repo-wide configuration." ] }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Results across multiple datasets:\n", - "\n", - "BrentLab/harbison_2004:\n", - " Included: True\n", - " condition: ['YPD', 'RAPA', 'H2O2Hi', 'H2O2Lo', 'Acid', 'Alpha', 'BUT14', 'BUT90', 'GAL', 'HEAT']\n", - "\n", + "Configuration with repo-wide property:\n", "BrentLab/kemmeren_2014:\n", - " Included: True\n", - " No field-level conditions (all samples match at repo level)\n", + " dataset:\n", + " kemmeren_2014:\n", + " carbon_source:\n", + " path: media.carbon_source\n", + " temperature_celsius:\n", + " path: temperature_celsius\n", + "filters:\n", + " temperature_celsius:\n", + " - 30\n", "\n" ] } ], "source": [ - "# Config for multiple datasets\n", - "multi_dataset_config = {\n", + "repo_wide_config = {\n", " \"filters\": {\n", - " \"carbon_source\": [\"D-glucose\", \"D-galactose\"]\n", + " \"temperature_celsius\": [30]\n", " },\n", - " \"dataset_mappings\": {\n", - " \"BrentLab/harbison_2004\": {\n", - " \"datasets\": {\n", - " \"harbison_2004\": {\n", - " \"carbon_source\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"media.carbon_source\"\n", - " }\n", - " }\n", - " }\n", + " \"BrentLab/kemmeren_2014\": {\n", + " \"temperature_celsius\": { # Repo-wide property (no dataset key)\n", + " \"path\": \"temperature_celsius\"\n", " },\n", - " \"BrentLab/kemmeren_2014\": {\n", - " \"repo_level\": {\n", - " \"carbon_source\": {\n", - " \"path\": \"environmental_conditions.media.carbon_source\"\n", + " \"dataset\": {\n", + " \"kemmeren_2014\": {\n", + " \"carbon_source\": { # Dataset-specific property\n", + " \"path\": \"media.carbon_source\"\n", " }\n", " }\n", " }\n", " }\n", "}\n", "\n", - "config_path = Path(\"/tmp/multi_dataset_filter.yaml\")\n", - "with open(config_path, 'w') as f:\n", - " yaml.dump(multi_dataset_config, f)\n", - "\n", - "resolver = DatasetFilterResolver(config_path)\n", - "\n", - "# Filter multiple datasets\n", - "results = resolver.resolve_filters(\n", - " repos=[\n", - " (\"BrentLab/harbison_2004\", \"harbison_2004\"),\n", - " (\"BrentLab/kemmeren_2014\", \"kemmeren_2014\")\n", - " ],\n", - " mode=\"conditions\"\n", - ")\n", - "\n", - "print(\"Results across multiple datasets:\\n\")\n", - "for repo_id, result in results.items():\n", - " print(f\"{repo_id}:\")\n", - " if result[\"included\"]:\n", - " print(f\" Included: True\")\n", - " if \"matching_field_values\" in result and result[\"matching_field_values\"]:\n", - " for field, values in result[\"matching_field_values\"].items():\n", - " print(f\" {field}: {values}\")\n", - " else:\n", - " print(\" No field-level conditions (all samples match at repo level)\")\n", - " else:\n", - " print(f\" Included: False\")\n", - " print(f\" Reason: {result['reason']}\")\n", - " print()\n" + "print(\"Configuration with repo-wide property:\")\n", + "print(yaml.dump(repo_wide_config, default_flow_style=False))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Example 6: Galactose-Only Filter\n", - "\n", - "Let's filter for just galactose to see dataset exclusion in action." - ] - }, - { - "cell_type": "code", - "execution_count": 62, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Galactose-only filter results:\n", - "Included: True\n", - "Matching conditions: ['GAL']\n" - ] - } - ], - "source": [ - "galactose_config = {\n", - " \"filters\": {\n", - " \"carbon_source\": [\"D-galactose\"]\n", - " },\n", - " \"dataset_mappings\": {\n", - " \"BrentLab/harbison_2004\": {\n", - " \"datasets\": {\n", - " \"harbison_2004\": {\n", - " \"carbon_source\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"media.carbon_source\"\n", - " }\n", - " }\n", - " }\n", - " }\n", - " }\n", - "}\n", - "\n", - "config_path = Path(\"/tmp/galactose_filter.yaml\")\n", - "with open(config_path, 'w') as f:\n", - " yaml.dump(galactose_config, f)\n", - "\n", - "resolver = DatasetFilterResolver(config_path)\n", - "results = resolver.resolve_filters(\n", - " repos=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", - " mode=\"conditions\"\n", - ")\n", - "\n", - "print(\"Galactose-only filter results:\")\n", - "result = results[\"BrentLab/harbison_2004\"]\n", - "print(f\"Included: {result['included']}\")\n", - "if result['included']:\n", - " matching = result[\"matching_field_values\"][\"condition\"]\n", - " print(f\"Matching conditions: {matching}\")\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Only the GAL condition should match - it's the only one with galactose as the carbon source." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## How Path Resolution Works\n", - "\n", - "The `DatasetFilterResolver` uses a two-phase approach to handle heterogeneous dataset structures:\n", - "\n", - "### Phase 1: Property Location (Field vs Repo Level)\n", - "\n", - "**Field-Level Resolution** (when `field` is specified):\n", - "```python\n", - "# Configuration\n", - "carbon_source:\n", - " field: condition # Look in the 'condition' field\n", - " path: media.carbon_source # Path within each field value's definition\n", - "```\n", - "\n", - "The resolver:\n", - "1. Gets all definitions for the `condition` field\n", - "2. For each field value (YPD, HEAT, GAL, etc.), extracts the value at `media.carbon_source`\n", - "3. Returns field values where the extracted value matches the filter\n", - "\n", - "**Repo-Level Resolution** (no `field` specified):\n", - "```python\n", - "# Configuration\n", - "carbon_source:\n", - " path: environmental_conditions.media.carbon_source # Top-level path\n", - "```\n", - "\n", - "The resolver:\n", - "1. Looks at the top-level `experimental_conditions` in the datacard\n", - "2. Extracts the value at the specified path\n", - "3. Includes/excludes the entire dataset based on the match\n", - "\n", - "### Phase 2: Path Navigation\n", - "\n", - "Once the resolver knows WHERE to look (field vs repo level), it navigates to the property using dot notation.\n", - "\n", - "**Automatic Fallback for Nested Paths**:\n", + "In this example:\n", + "- `temperature_celsius` is defined at repo level - applies to ALL datasets in BrentLab/kemmeren_2014\n", + "- `carbon_source` is defined at dataset level - applies only to the kemmeren_2014 dataset\n", "\n", - "The resolver tries multiple path variants to handle different nesting depths:\n", - "\n", - "1. **Try full path first**: `environmental_conditions.media.carbon_source`\n", - "2. **If not found, try without prefix**: `media.carbon_source`\n", - "\n", - "This allows configs to work with both:\n", - "- Repo-level: `experimental_conditions.media.carbon_source` (finds it at step 1)\n", - "- Field-level: `media.carbon_source` (finds it at step 2)\n", - "\n", - "**Example**: Harbison 2004 field definitions don't have an `environmental_conditions` wrapper, so:\n", - "- Path specified: `media.carbon_source`\n", - "- Directly finds `media` in the field definition\n", - "- No fallback needed\n", - "\n", - "### Compound Extraction\n", - "\n", - "For carbon source and similar properties, the resolver handles list-of-dict format:\n", - "\n", - "```yaml\n", - "carbon_source:\n", - " - compound: D-glucose\n", - " concentration_percent: 2\n", - " - compound: D-galactose \n", - " concentration_percent: 1\n", - "```\n", - "\n", - "Automatically extracts to: `[\"D-glucose\", \"D-galactose\"]`\n", - "\n", - "### Why This Design?\n", - "\n", - "This two-phase approach (field specification + path navigation) allows:\n", - "\n", - "1. **No hardcoded field names** in the codebase\n", - "2. **Explicit configuration** - clear which field to examine \n", - "3. **Flexibility** - works with any field name (`condition`, `treatment`, `strain`, etc.)\n", - "4. **Automatic nesting handling** - paths work at different hierarchy levels\n", - "5. **Heterogeneous datasets** - each dataset can specify its own structure\n", - "\n", - "### Configuration Debugging Tips\n", - "\n", - "If your filter isn't working:\n", - "\n", - "1. **Check the datacard structure** - is the property at field or repo level?\n", - "2. **Verify the field name** - does the field exist? Is the name correct?\n", - "3. **Test the path** - use `DataCard.get_field_definitions()` to inspect\n", - "4. **Try simpler paths** - remove `environmental_conditions.` if not needed\n", - "5. **Use `conditions` mode first** - see which values match before retrieving data" + "This is useful when a repository has multiple datasets that share some common properties." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Practical Example: Creating a Filter Config File\n", + "## Creating a Reusable Configuration File\n", "\n", - "Here's a complete example of a reusable filter configuration file." + "For production use, save your configuration to a YAML file." ] }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Saved configuration to my_filter_config.yaml\n", + "Saved configuration to my_comprehensive_filter.yaml\n", "\n", - "Configuration contents:\n", - "dataset_mappings:\n", - " BrentLab/harbison_2004:\n", - " datasets:\n", - " harbison_2004:\n", - " carbon_source:\n", - " field: condition\n", - " path: media.carbon_source\n", - " temperature_celsius:\n", - " field: condition\n", - " path: temperature_celsius\n", - " BrentLab/kemmeren_2014:\n", - " repo_level:\n", - " carbon_source:\n", - " path: environmental_conditions.media.carbon_source\n", - " temperature_celsius:\n", - " path: environmental_conditions.temperature_celsius\n", - "filters:\n", - " carbon_source:\n", - " - D-glucose\n", - " - D-galactose\n", - " - D-raffinose\n", - " temperature_celsius:\n", - " - 30\n", - " - 37\n", - "\n" + "You can now use this config file in your analysis:\n", + " resolver = DatasetFilterResolver('my_comprehensive_filter.yaml')\n" ] } ], @@ -1384,189 +1290,45 @@ "# Create a comprehensive filter config\n", "comprehensive_config = {\n", " \"filters\": {\n", - " \"carbon_source\": [\"D-glucose\", \"D-galactose\", \"D-raffinose\"],\n", - " \"temperature_celsius\": [30, 37]\n", + " \"carbon_source\": [\"D-glucose\", \"D-galactose\"],\n", + " \"temperature_celsius\": [30]\n", " },\n", - " \"dataset_mappings\": {\n", - " \"BrentLab/harbison_2004\": {\n", - " \"datasets\": {\n", - " \"harbison_2004\": {\n", - " \"carbon_source\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"media.carbon_source\"\n", - " },\n", - " \"temperature_celsius\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"temperature_celsius\"\n", - " }\n", - " }\n", - " }\n", - " },\n", - " \"BrentLab/kemmeren_2014\": {\n", - " \"repo_level\": {\n", + " \"BrentLab/harbison_2004\": {\n", + " \"dataset\": {\n", + " \"harbison_2004\": {\n", " \"carbon_source\": {\n", - " \"path\": \"environmental_conditions.media.carbon_source\"\n", + " \"field\": \"condition\",\n", + " \"path\": \"media.carbon_source\"\n", " },\n", " \"temperature_celsius\": {\n", - " \"path\": \"environmental_conditions.temperature_celsius\"\n", + " \"field\": \"condition\",\n", + " \"path\": \"temperature_celsius\"\n", " }\n", " }\n", " }\n", - " }\n", - "}\n", - "\n", - "# Save to a persistent location\n", - "config_file = Path(\"my_filter_config.yaml\")\n", - "with open(config_file, 'w') as f:\n", - " yaml.dump(comprehensive_config, f)\n", - "\n", - "print(f\"Saved configuration to {config_file}\")\n", - "print(\"\\nConfiguration contents:\")\n", - "print(yaml.dump(comprehensive_config, default_flow_style=False))\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Simplifying Single-Dataset Repositories\n", - "\n", - "For repositories with a single dataset or when all datasets share the same structure, you can use `repo_level` for brevity:\n", - "\n", - "```yaml\n", - "dataset_mappings:\n", - " \"BrentLab/kemmeren_2014\":\n", - " repo_level:\n", - " carbon_source:\n", - " path: environmental_conditions.media.carbon_source\n", - " temperature_celsius:\n", - " path: environmental_conditions.temperature_celsius\n", - "```\n", - "\n", - "This applies the mappings to all datasets in the repo. For kemmeren_2014 (which has repo-level experimental_conditions), this is more concise than explicitly listing each dataset." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Advanced: Hierarchical Configuration for Multi-Dataset Repositories\n", - "\n", - "When a repository contains multiple datasets, you can use hierarchical configuration to:\n", - "1. Define repo-level mappings that apply to all datasets\n", - "2. Override or extend with dataset-specific mappings\n", - "\n", - "This is especially useful when datasets share common properties but have some differences." - ] - }, - { - "cell_type": "code", - "execution_count": 64, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hierarchical configuration:\n", - "dataset_mappings:\n", - " BrentLab/multi_dataset_repo:\n", - " datasets:\n", - " dataset1:\n", - " temperature_celsius:\n", - " path: environmental_conditions.temperature_celsius\n", - " dataset2:\n", - " temperature_celsius:\n", - " path: custom.temp.location\n", - " dataset3:\n", - " carbon_source:\n", - " path: media.carbon\n", - " temperature_celsius:\n", - " path: temp\n", - " repo_level:\n", - " carbon_source:\n", - " path: environmental_conditions.media.carbon_source\n", - "filters:\n", - " carbon_source:\n", - " - D-glucose\n", - " temperature_celsius:\n", - " - 30\n", - "\n" - ] - } - ], - "source": [ - "# Hierarchical configuration example\n", - "hierarchical_config = {\n", - " \"filters\": {\n", - " \"carbon_source\": [\"D-glucose\"],\n", - " \"temperature_celsius\": [30]\n", " },\n", - " \"dataset_mappings\": {\n", - " # Repository with multiple datasets\n", - " \"BrentLab/multi_dataset_repo\": {\n", - " # Repo-level mappings apply to ALL datasets in this repo\n", - " \"repo_level\": {\n", + " \"BrentLab/kemmeren_2014\": {\n", + " \"dataset\": {\n", + " \"kemmeren_2014\": {\n", " \"carbon_source\": {\n", - " \"path\": \"environmental_conditions.media.carbon_source\"\n", - " }\n", - " },\n", - " # Dataset-specific overrides\n", - " \"datasets\": {\n", - " \"dataset1\": {\n", - " # Inherits carbon_source from repo_level\n", - " # Adds temperature mapping for this specific dataset\n", - " \"temperature_celsius\": {\n", - " \"path\": \"environmental_conditions.temperature_celsius\"\n", - " }\n", - " },\n", - " \"dataset2\": {\n", - " # Inherits carbon_source from repo_level \n", - " # Uses different temperature path\n", - " \"temperature_celsius\": {\n", - " \"path\": \"custom.temp.location\"\n", - " }\n", + " \"path\": \"media.carbon_source\"\n", " },\n", - " \"dataset3\": {\n", - " # Completely overrides carbon_source for this dataset\n", - " \"carbon_source\": {\n", - " \"path\": \"media.carbon\" # Different structure\n", - " },\n", - " \"temperature_celsius\": {\n", - " \"path\": \"temp\" # Top-level property\n", - " }\n", + " \"temperature_celsius\": {\n", + " \"path\": \"temperature_celsius\"\n", " }\n", " }\n", " }\n", " }\n", "}\n", "\n", - "print(\"Hierarchical configuration:\")\n", - "print(yaml.dump(hierarchical_config, default_flow_style=False))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Key Benefits of Hierarchical Configuration\n", - "\n", - "1. **DRY Principle**: Define common mappings once at repo level\n", - "2. **Flexibility**: Override for specific datasets that differ\n", - "3. **Maintainability**: Update repo-level mappings to affect all datasets at once\n", - "4. **Clarity**: Explicitly shows which datasets inherit vs override\n", - "\n", - "### How Merging Works\n", - "\n", - "For each dataset, the resolver:\n", - "1. Starts with repo-level mappings\n", - "2. Overlays dataset-specific mappings (these take precedence)\n", - "3. Returns the merged mapping for that dataset\n", + "# Save to file\n", + "config_file = Path(\"my_comprehensive_filter.yaml\")\n", + "with open(config_file, 'w') as f:\n", + " yaml.dump(comprehensive_config, f)\n", "\n", - "In the example above:\n", - "- `dataset1` gets `carbon_source` from repo_level + its own `temperature_celsius`\n", - "- `dataset2` gets `carbon_source` from repo_level + its own (different) `temperature_celsius`\n", - "- `dataset3` overrides both properties completely" + "print(f\"Saved configuration to {config_file}\")\n", + "print(\"\\nYou can now use this config file in your analysis:\")\n", + "print(f\" resolver = DatasetFilterResolver('{config_file}')\")" ] }, { @@ -1575,105 +1337,69 @@ "source": [ "## Summary\n", "\n", - "The `DatasetFilterResolver` provides:\n", - "\n", - "1. **Flexible filtering** across heterogeneous datasets\n", - "2. **External configuration** - no code changes needed for new filters\n", - "3. **Two-level filtering** - dataset exclusion and field-level matching\n", - "4. **Three output modes** - from lightweight (conditions only) to full data\n", - "5. **Field-aware resolution** - explicit specification of where properties are located\n", - "6. **Automatic path resolution** - handles different nesting levels gracefully\n", - "\n", - "### When to Use Each Mode\n", - "\n", - "- **`conditions`**: Quick exploration - which datasets have matching samples? Which field values match?\n", - "- **`samples`**: Sample metadata analysis - how many samples match? What are their properties?\n", - "- **`full_data`**: Full analysis - retrieve all measurements for downstream processing\n", + "### Key Concepts\n", "\n", - "### Best Practices for Configuration\n", + "1. **Configuration Structure**:\n", + " - `filters`: What values you want\n", + " - Repository mappings: Where to find properties in each dataset\n", "\n", - "#### 1. Field Specification\n", - "- **Always specify `field`** when the property is in field-level definitions\n", - "- **Never specify `field`** when the property is at repo/config level\n", - "- When in doubt, check the datacard: if the property is in a field's `definitions`, use `field`\n", + "2. **Three Property Types**:\n", + " - **Repo-wide**: Applies to all datasets (paths get `experimental_conditions.` prepended)\n", + " - **Dataset-specific**: Applies to one dataset (paths get `experimental_conditions.` prepended)\n", + " - **Field-level**: Per-sample variation (requires `field`, no prepending)\n", "\n", - "#### 2. Property Paths\n", - "- Use the simplest path that works (avoid unnecessary nesting)\n", - "- For field-level properties, typically: `media.carbon_source` (not `environmental_conditions.media.carbon_source`)\n", - "- For repo-level properties, include full path: `environmental_conditions.media.carbon_source`\n", + "3. **Three Output Modes**:\n", + " - `conditions`: Fast exploration, no data download\n", + " - `samples`: Sample metadata, one row per sample\n", + " - `full_data`: All measurements, many rows per sample\n", "\n", - "#### 3. Hierarchical Configuration\n", - "- Use `repo_level` for properties shared across all datasets in a repository\n", - "- Use `datasets` to override or extend for specific datasets\n", - "- This keeps configuration DRY and maintainable\n", + "### Best Practices\n", "\n", - "#### 4. Filter Development Workflow\n", - "1. **Start with `conditions` mode** to verify your filters work correctly\n", - "2. **Check the results** - do the field values make sense?\n", - "3. **Use `samples` mode** to understand sample counts before downloading full data\n", - "4. **Only use `full_data`** when you're ready to retrieve the actual measurements\n", - "\n", - "#### 5. Configuration Management\n", - "- Create reusable YAML configs for common filter scenarios\n", - "- Document your filter configs with comments explaining the scientific rationale\n", - "- Version control your configs alongside your analysis code\n", - "- Test configs with `conditions` mode before using in production pipelines\n", - "\n", - "#### 6. Debugging\n", - "- If a dataset shows `included: False`, check the `reason` field\n", - "- Use `DataCard.get_field_definitions()` to inspect field structure\n", - "- Use `DataCard.get_experimental_conditions()` to inspect repo-level conditions\n", - "- Start simple (one filter, one dataset) then expand\n", + "1. **Start with `conditions` mode** to verify your filters work\n", + "2. **Use `field` parameter** when properties vary between samples\n", + "3. **Omit `field` parameter** when properties are at repo/config level\n", + "4. **Keep paths simple** - use minimal nesting that works\n", + "5. **Version control your configs** - they document your filter criteria\n", "\n", "### Common Patterns\n", "\n", - "**Pattern 1: Single property, field-level**\n", + "**Field-level filtering** (per-sample variation):\n", "```yaml\n", - "filters:\n", - " carbon_source: [\"D-glucose\"]\n", - "dataset_mappings:\n", - " \"BrentLab/harbison_2004\":\n", - " datasets:\n", - " harbison_2004:\n", - " carbon_source:\n", - " field: condition\n", - " path: media.carbon_source\n", - "```\n", - "\n", - "**Pattern 2: Multiple properties, repo-level**\n", - "```yaml\n", - "filters:\n", - " carbon_source: [\"D-glucose\"]\n", - " temperature_celsius: [30]\n", - "dataset_mappings:\n", - " \"BrentLab/kemmeren_2014\":\n", - " repo_level:\n", + "BrentLab/harbison_2004:\n", + " dataset:\n", + " harbison_2004:\n", " carbon_source:\n", - " path: environmental_conditions.media.carbon_source\n", - " temperature_celsius:\n", - " path: environmental_conditions.temperature_celsius\n", + " field: condition\n", + " path: media.carbon_source\n", "```\n", "\n", - "**Pattern 3: Mixed (repo-level + dataset overrides)**\n", + "**Repo-level filtering** (all samples share conditions):\n", "```yaml\n", - "dataset_mappings:\n", - " \"BrentLab/multi_dataset_repo\":\n", - " repo_level:\n", + "BrentLab/kemmeren_2014:\n", + " dataset:\n", + " kemmeren_2014:\n", " carbon_source:\n", - " path: environmental_conditions.media.carbon_source\n", - " datasets:\n", - " dataset1:\n", - " temperature_celsius:\n", - " field: condition\n", - " path: temperature_celsius\n", + " path: media.carbon_source\n", "```\n", "\n", - "### Next Steps\n", + "**Repo-wide property** (applies to all datasets in repo):\n", + "```yaml\n", + "BrentLab/kemmeren_2014:\n", + " temperature_celsius:\n", + " path: temperature_celsius\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Next Steps\n", "\n", - "- Explore the `example_filter_config.yaml` file for more examples\n", - "- Try filtering across multiple datasets with different structures\n", - "- Integrate with downstream analysis pipelines using the exported DataFrames\n", - "- Consider creating a library of reusable filter configs for your common use cases" + "- See [my_filter_config.yaml](my_filter_config.yaml) for a working example\n", + "- Explore the [experimental_conditions_tutorial.ipynb](experimental_conditions_tutorial.ipynb) to understand datacard structures\n", + "- Read the [DatasetFilterResolver API documentation](../../api/datainfo/filter_resolver.md) for detailed reference\n", + "- Try filtering across additional datasets in the BrentLab collection" ] } ], diff --git a/docs/tutorials/my_comprehensive_filter.yaml b/docs/tutorials/my_comprehensive_filter.yaml new file mode 100644 index 0000000..6bd4668 --- /dev/null +++ b/docs/tutorials/my_comprehensive_filter.yaml @@ -0,0 +1,22 @@ +BrentLab/harbison_2004: + dataset: + harbison_2004: + carbon_source: + field: condition + path: media.carbon_source + temperature_celsius: + field: condition + path: temperature_celsius +BrentLab/kemmeren_2014: + dataset: + kemmeren_2014: + carbon_source: + path: media.carbon_source + temperature_celsius: + path: temperature_celsius +filters: + carbon_source: + - D-glucose + - D-galactose + temperature_celsius: + - 30 diff --git a/docs/tutorials/my_filter_config.yaml b/docs/tutorials/my_filter_config.yaml index c9b4f7e..8c10ac6 100644 --- a/docs/tutorials/my_filter_config.yaml +++ b/docs/tutorials/my_filter_config.yaml @@ -1,24 +1,19 @@ -dataset_mappings: - BrentLab/harbison_2004: - datasets: - harbison_2004: - carbon_source: - field: condition - path: media.carbon_source - temperature_celsius: - field: condition - path: temperature_celsius - BrentLab/kemmeren_2014: - repo_level: - carbon_source: - path: environmental_conditions.media.carbon_source - temperature_celsius: - path: environmental_conditions.temperature_celsius filters: carbon_source: - - D-glucose - - D-galactose - - D-raffinose - temperature_celsius: - - 30 - - 37 + - "D-glucose" + - "D-galactose" + +BrentLab/harbison_2004: + dataset: + harbison_2004: + carbon_source: + field: condition + path: media.carbon_source + +BrentLab/kemmeren_2014: + temperature: + path: temperature_celsius + dataset: + kemmeren_2014: + carbon_source: + path: media.carbon_source diff --git a/example_filter_config.yaml b/example_filter_config.yaml index bae0ac7..a2c66ca 100644 --- a/example_filter_config.yaml +++ b/example_filter_config.yaml @@ -3,9 +3,7 @@ # This file demonstrates how to specify filters and dataset-specific # property mappings for heterogeneous datasets. # -# Configuration supports two formats: -# 1. Legacy format: Direct mappings at repo level (backward compatible) -# 2. Hierarchical format: Repo-level + dataset-specific mappings +# New format: repositories as top-level keys (not nested under dataset_mappings) # # Usage: # from tfbpapi.datainfo.filter_resolver import DatasetFilterResolver @@ -19,70 +17,59 @@ filters: carbon_source: - "D-glucose" - "D-galactose" - - "D-raffinose" temperature_celsius: - 30 - 37 -# Dataset mappings specify where to find each property in each dataset's DataCard -# The 'path' uses dot notation to navigate nested structures -# For field-level definitions, use 'field' to specify which field contains the property -dataset_mappings: - # FIELD-LEVEL FORMAT - # For datasets where properties are in field definitions (e.g., condition field) - "BrentLab/harbison_2004": - datasets: - harbison_2004: - carbon_source: - field: condition # Look in the 'condition' field's definitions - path: "media.carbon_source" - temperature_celsius: - field: condition - path: "temperature_celsius" +# Repository configurations specify where to find each property +# Top-level keys are repository IDs (e.g., "BrentLab/harbison_2004") - # REPO-LEVEL FORMAT - # For datasets where properties are at repo/config level - "BrentLab/kemmeren_2014": - repo_level: +# FIELD-LEVEL EXAMPLE: Harbison 2004 +# Properties are in field definitions (condition field) +BrentLab/harbison_2004: + dataset: + harbison_2004: carbon_source: - path: "environmental_conditions.media.carbon_source" + field: condition # Look in the 'condition' field's definitions + path: media.carbon_source temperature_celsius: - path: "environmental_conditions.temperature_celsius" + field: condition + path: temperature_celsius - # NEW HIERARCHICAL FORMAT - # Supports repo-level mappings that apply to all datasets - # and dataset-specific overrides - "BrentLab/example_multi_dataset_repo": - # Repo-level mappings apply to all datasets in this repository - repo_level: - carbon_source: - path: "environmental_conditions.media.carbon_source" - temperature_celsius: - path: "environmental_conditions.temperature_celsius" - - # Dataset-specific mappings override repo-level for particular datasets - datasets: - dataset1: - # This dataset has field-level definitions - # Override to look in 'condition' field instead of repo level - carbon_source: - field: condition - path: "media.carbon_source" - temperature_celsius: - field: condition - path: "temperature_celsius" +# REPO-WIDE + DATASET-SPECIFIC EXAMPLE: Kemmeren 2014 +# temperature_celsius is repo-wide (applies to all datasets) +# carbon_source is dataset-specific +BrentLab/kemmeren_2014: + # Repo-wide property (applies to all datasets in this repository) + temperature_celsius: + path: temperature_celsius - dataset2: - # Inherits repo-level mappings (no field specification) - # Can add dataset-specific properties - growth_phase: - path: "growth_conditions.phase" + # Dataset-specific properties + dataset: + kemmeren_2014: + carbon_source: + path: media.carbon_source - dataset3: - # Mixed: some properties in 'treatment' field, some at repo level - carbon_source: - field: treatment - path: "media.carbon" - temperature_celsius: - path: "temp" # Direct repo/config level property +# CONFIGURATION STRUCTURE EXPLAINED: +# +# Repo-wide properties: +# - Properties directly under repository ID +# - Apply to ALL datasets in that repository +# - Path is relative to experimental_conditions +# - Example: temperature_celsius: path: temperature_celsius +# -> looks in experimental_conditions.temperature_celsius +# +# Dataset-specific properties: +# - Properties under dataset.{dataset_name} +# - Override repo-wide for that specific dataset +# - Can use 'field' to specify field-level definitions +# - Example with field: carbon_source: field: condition, path: media.carbon_source +# -> looks in condition field definitions +# - Example without field: carbon_source: path: media.carbon_source +# -> looks in experimental_conditions.media.carbon_source +# +# Property name aliasing: +# - The filter property name (e.g., "temperature") can differ from the datacard property name +# - Example: temperature: path: temperature_celsius +# Filter uses "temperature", datacard has "temperature_celsius" diff --git a/pyproject.toml b/pyproject.toml index ee98517..b8024b0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,6 +37,7 @@ mkdocs-jupyter = "^0.25.1" mkdocstrings = {extras = ["python"], version = "^0.30.0"} matplotlib = "^3.10.6" seaborn = "^0.13.2" +types-pyyaml = "^6.0.12.20250915" [tool.pytest.ini_options] diff --git a/tfbpapi/datainfo/filter_resolver.py b/tfbpapi/datainfo/filter_resolver.py index f83f69f..13a53a5 100644 --- a/tfbpapi/datainfo/filter_resolver.py +++ b/tfbpapi/datainfo/filter_resolver.py @@ -31,6 +31,7 @@ ... repos=[("BrentLab/harbison_2004", "harbison_2004")], ... mode="conditions" ... ) + """ from __future__ import annotations @@ -39,7 +40,7 @@ from typing import Any import pandas as pd -import yaml +import yaml # type: ignore[import-untyped] from tfbpapi.datainfo import DataCard from tfbpapi.errors import DataCardError @@ -51,12 +52,11 @@ def get_nested_value(data: dict, path: str) -> Any: Navigate nested dict using dot notation. Handles missing intermediate keys gracefully by returning None. - If the full path is not found, tries removing common prefixes - like 'environmental_conditions' to handle different nesting levels. :param data: Dictionary to navigate - :param path: Dot-separated path (e.g., "environmental_conditions.media.name") + :param path: Dot-separated path (e.g., "media.carbon_source") :return: Value at path or None if not found + """ if not isinstance(data, dict): return None @@ -64,34 +64,8 @@ def get_nested_value(data: dict, path: str) -> Any: keys = path.split(".") current = data - # Try the full path first for key in keys: if not isinstance(current, dict) or key not in current: - # Full path failed, try fallback paths - fallback_paths = [] - - # If path starts with environmental_conditions, try without it - if keys[0] == "environmental_conditions" and len(keys) > 1: - fallback_paths.append(".".join(keys[1:])) - - # Try each fallback - for fallback_path in fallback_paths: - fallback_keys = fallback_path.split(".") - fallback_current = data - fallback_success = True - - for fallback_key in fallback_keys: - if ( - not isinstance(fallback_current, dict) - or fallback_key not in fallback_current - ): - fallback_success = False - break - fallback_current = fallback_current[fallback_key] - - if fallback_success: - return fallback_current - return None current = current[key] @@ -109,6 +83,7 @@ def extract_compound_names(value: Any) -> list[str]: :param value: Value to extract from :return: List of compound names + """ if value is None or value == "unspecified": return [] @@ -136,23 +111,62 @@ class DatasetFilterResolver: 1. Filter criteria (which values are acceptable for each property) 2. Repository and dataset-specific property paths (where to find each property) - Configuration structure supports: - - Repo-level mappings: Apply to all datasets in a repo - - Dataset-level mappings: Override repo-level for specific datasets + Configuration structure: + filters: + property_name: [value1, value2] + + BrentLab/repo_name: + # Repo-wide properties (apply to all datasets in this repository) + # Paths are relative to experimental_conditions at the repository level + property1: + path: media.name + + # Dataset-specific section + dataset: + dataset_name: + # Dataset-specific properties (apply only to this dataset) + # Paths are relative to experimental_conditions at the config level + property2: + path: temperature_celsius + + # Field-level properties (per-sample variation) + # Paths are relative to field definitions (NOT experimental_conditions) + property3: + field: condition + path: media.carbon_source + + Path Resolution: + - Repo-wide & dataset-specific: Paths automatically + prepended with "experimental_conditions." + Example: path "media.name" resolves to experimental_conditions.media.name + + - Field-level: Paths used directly on field definitions (no prepending) + Example: field "condition", path "media.carbon_source" + looks in condition field's definitions for media.carbon_source + + Examples: + # Repo-wide property (applies to all datasets in BrentLab/kemmeren_2014) + filters: + temperature_celsius: [30] + + BrentLab/kemmeren_2014: + temperature_celsius: + path: temperature_celsius + + # Dataset-specific property (applies only to kemmeren_2014 dataset) + BrentLab/kemmeren_2014: + dataset: + kemmeren_2014: + carbon_source: + path: media.carbon_source - Example: - dataset_mappings: - "BrentLab/my_repo": - repo_level: + # Field-level property (per-sample variation via condition field) + BrentLab/harbison_2004: + dataset: + harbison_2004: carbon_source: - path: environmental_conditions.media.carbon_source - datasets: - dataset1: - temperature_celsius: - path: custom.temp.path - dataset2: - temperature_celsius: - path: other.temp.path + field: condition + path: media.carbon_source It then resolves filters across datasets with three output modes: - conditions: Just which field values match (no data retrieval) @@ -163,6 +177,7 @@ class DatasetFilterResolver: config: Loaded configuration dict filters: Filter criteria from config mappings: Repository/dataset-specific property paths + """ def __init__(self, config_path: Path | str): @@ -170,10 +185,12 @@ def __init__(self, config_path: Path | str): Initialize resolver with external configuration. :param config_path: Path to YAML configuration file + """ self.config = self._load_config(Path(config_path)) self.filters = self.config.get("filters", {}) - self.mappings = self.config.get("dataset_mappings", {}) + # Extract mappings: all keys except 'filters' are repository IDs + self.mappings = {k: v for k, v in self.config.items() if k != "filters"} def _load_config(self, config_path: Path) -> dict: """ @@ -183,6 +200,7 @@ def _load_config(self, config_path: Path) -> dict: :return: Configuration dict :raises FileNotFoundError: If config file doesn't exist :raises yaml.YAMLError: If config is invalid YAML + """ if not config_path.exists(): raise FileNotFoundError(f"Configuration file not found: {config_path}") @@ -199,55 +217,54 @@ def _get_property_mappings(self, repo_id: str, config_name: str) -> dict: """ Get property mappings for a specific repo/dataset combination. - Merges repo-level and dataset-level mappings, with dataset-level taking precedence. + Merges repo-wide and dataset-specific mappings, + with dataset-specific taking precedence. - Supports configuration formats: - 1. Field-specific path (for field-level definitions): - carbon_source: - field: condition - path: media.carbon_source + Configuration format: + BrentLab/repo_name: + # Repo-wide properties (apply to all datasets) + property1: + path: path.in.experimental_conditions - 2. Direct path (for repo/config level): - carbon_source: - path: environmental_conditions.media.carbon_source + # Dataset-specific section + dataset: + dataset_name: + property2: + field: field_name # For field-level definitions + path: path.within.field - 3. Hierarchical with repo_level and datasets: - repo_level: + Examples: + 1. Field-specific path (for field-level definitions): carbon_source: - path: ... - datasets: - dataset1: - carbon_source: - field: condition - path: ... + field: condition + path: media.carbon_source + + 2. Repo-level path (for experimental_conditions): + temperature: + path: temperature_celsius :param repo_id: Repository ID :param config_name: Dataset/config name :return: Merged property mappings dict + """ if repo_id not in self.mappings: return {} repo_config = self.mappings[repo_id] + mappings = {} - # Check if this is the new hierarchical format - if "repo_level" in repo_config or "datasets" in repo_config: - # New format: merge repo_level and dataset-specific - mappings = {} + # Add repo-wide properties (all keys except 'dataset') + for key, value in repo_config.items(): + if key != "dataset": + mappings[key] = value - # Start with repo-level mappings (apply to all datasets) - if "repo_level" in repo_config: - mappings.update(repo_config["repo_level"]) + # Override with dataset-specific properties + if "dataset" in repo_config: + if config_name in repo_config["dataset"]: + mappings.update(repo_config["dataset"][config_name]) - # Override with dataset-specific mappings - if "datasets" in repo_config and config_name in repo_config["datasets"]: - mappings.update(repo_config["datasets"][config_name]) - - return mappings - else: - # Legacy format: direct property mappings - # This assumes the config at repo level applies to all datasets - return repo_config + return mappings def resolve_filters( self, @@ -263,10 +280,12 @@ def resolve_filters( :param token: Optional HuggingFace token for private repos :return: Dict mapping repo_id to results :raises ValueError: If mode is invalid + """ if mode not in ["conditions", "samples", "full_data"]: raise ValueError( - f"Invalid mode: {mode}. Must be 'conditions', 'samples', or 'full_data'" + f"Invalid mode: {mode}. Must be 'conditions', " + "'samples', or 'full_data'" ) results = {} @@ -341,6 +360,7 @@ def _check_repo_config_level( :param config_name: Configuration name :param repo_id: Repository ID :return: (included, reason) tuple + """ # Get repo and config level conditions try: @@ -359,11 +379,22 @@ def _check_repo_config_level( if filter_prop not in property_mappings: continue + mapping = property_mappings[filter_prop] + + # Check if this is a field-level mapping + # (should not be checked at repo level) + if "field" in mapping: + # Skip field-level properties at repo/config level + continue + # Get path for this property - path = property_mappings[filter_prop]["path"] + path = mapping["path"] + + # Prepend experimental_conditions for repo-level paths + full_path = f"experimental_conditions.{path}" # Try to get value at this path - value = get_nested_value(conditions, path) + value = get_nested_value(conditions, full_path) if value is None: # Property not specified at repo/config level, skip @@ -385,7 +416,8 @@ def _check_repo_config_level( if not matches: return ( False, - f"{filter_prop}: found {actual_values}, wanted {acceptable_values}", + f"{filter_prop}: found {actual_values}, " + f"wanted {acceptable_values}", ) return True, "" @@ -403,8 +435,9 @@ def _check_field_level( :param config_name: Configuration name :param repo_id: Repository ID :return: Dict mapping field names to lists of matching values + """ - matching = {} + matching: dict[str, list[str]] = {} property_mappings = self._get_property_mappings(repo_id, config_name) # Get config to find fields with role=experimental_condition @@ -414,8 +447,8 @@ def _check_field_level( # Group property mappings by field (if field is specified) # field_mappings: {field_name: {prop: path, ...}} - field_mappings = {} - general_mappings = {} # Properties without field specification + field_mappings: dict[str, dict[str, str]] = {} + general_mappings: dict[str, str] = {} # Properties without field specification for prop, mapping in property_mappings.items(): if "field" in mapping: @@ -500,6 +533,7 @@ def _get_sample_metadata( :param matching_field_values: Dict of field names to matching values :param token: Optional HuggingFace token :return: DataFrame with sample metadata + """ # Initialize query API api = HfQueryAPI(repo_id, token=token) @@ -562,6 +596,7 @@ def _get_full_data( :param matching_field_values: Dict of field names to matching values :param token: Optional HuggingFace token :return: DataFrame with full data + """ # Initialize query API api = HfQueryAPI(repo_id, token=token) @@ -598,4 +633,7 @@ def __repr__(self) -> str: """String representation.""" n_filters = len(self.filters) n_datasets = len(self.mappings) - return f"DatasetFilterResolver({n_filters} filters, {n_datasets} datasets configured)" + return ( + f"DatasetFilterResolver({n_filters} filters, " + f"{n_datasets} datasets configured)" + ) diff --git a/tfbpapi/datainfo/metadata_manager.py b/tfbpapi/datainfo/metadata_manager.py index da1efc3..d20b05a 100644 --- a/tfbpapi/datainfo/metadata_manager.py +++ b/tfbpapi/datainfo/metadata_manager.py @@ -71,12 +71,14 @@ def __init__( :param cache_dir: Optional directory for caching metadata views :param cache: Whether to enable persistent caching (not yet implemented) - :param duckdb_conn: Optional DuckDB connection to use. If None, creates new - in-memory connection. Pass a shared connection to work with views created - by HfQueryAPI or other tools. + :param duckdb_conn: Optional DuckDB connection to use. If None, creates new in- + memory connection. Pass a shared connection to work with views created by + HfQueryAPI or other tools. """ - self._conn = duckdb_conn if duckdb_conn is not None else duckdb.connect(":memory:") + self._conn = ( + duckdb_conn if duckdb_conn is not None else duckdb.connect(":memory:") + ) self._cache_dir = cache_dir self._cache_enabled = cache self._registered_datasets: dict[str, Any] = {} @@ -91,7 +93,8 @@ def _sanitize_view_name(self, repo_id: str, config_name: str) -> str: :param repo_id: Repository ID (e.g., "BrentLab/harbison_2004") :param config_name: Configuration name (e.g., "harbison_2004") - :return: Sanitized view name (e.g., "BrentLab_harbison_2004_harbison_2004_metadata") + :return: Sanitized view name + (e.g., "BrentLab_harbison_2004_harbison_2004_metadata") Example: >>> mgr = MetadataManager() @@ -126,7 +129,7 @@ def _flatten_condition_definition(self, definition: dict) -> dict: {'growth_media': 'YPD', 'temperature_celsius': 30} """ - result = {} + result: dict[str, Any] = {} if not definition: return result @@ -164,7 +167,7 @@ def _flatten_experimental_conditions(self, exp_conds: Any) -> dict: {'temperature_celsius': 30, 'strain_background': 'BY4741'} """ - result = {} + result: dict[str, Any] = {} if exp_conds is None: return result @@ -211,7 +214,8 @@ def create_metadata_view( :param base_view: Name of existing DuckDB view (created by HfCacheManager) :param view_name: Name for the new metadata view :param include_fields: List of field names to select from base view - :param constant_columns: Optional dict of {column_name: value} to add as constants + :param constant_columns: Optional dict of {column_name: value} + to add as constants :return: Name of created view Example: diff --git a/tfbpapi/tests/datainfo/conftest.py b/tfbpapi/tests/datainfo/conftest.py index 09aa58f..2b459ff 100644 --- a/tfbpapi/tests/datainfo/conftest.py +++ b/tfbpapi/tests/datainfo/conftest.py @@ -290,3 +290,1145 @@ def sample_partitioning_info(): def sample_data_file_info(): """Sample data file information.""" return {"split": "train", "path": "genomic_features.parquet"} + + +# ============================================================================ +# Filter Resolver Fixtures +# ============================================================================ + + +@pytest.fixture +def write_config(tmp_path): + """ + Helper to write config dict to temp file. + + :param tmp_path: pytest tmp_path fixture + :return: Function that writes config dict and returns Path + + """ + import yaml # type: ignore[import-untyped] + + def _write(config_dict): + config_path = tmp_path / "config.yaml" + with open(config_path, "w") as f: + yaml.dump(config_dict, f) + return config_path + + return _write + + +# ============================================================================ +# Datacard Fixtures (from huggingface_collection_datacards.txt) +# ============================================================================ + + +# Datacard fixtures copied directly from huggingface_collection_datacards.txt +# These contain the complete YAML metadata for each repository + + +@pytest.fixture +def hackett_2020_datacard(): + """Complete hackett_2020 datacard YAML from huggingface_collection_datacards.txt.""" + return { + "license": "mit", + "language": ["en"], + "tags": [ + "genomics", + "yeast", + "transcription", + "perturbation", + "response", + "overexpression", + ], + "pretty_name": "Hackett, 2020 Overexpression", + "size_categories": ["1M log2(1.7) & " + "pval < 0.05). Note that " + "there is a slight " + "difference when " + "calculating from the data " + "provided here, I believe " + "due to a difference in " + "the way the targets are " + "parsed and filtered (some " + "ORFs that have since been " + "removed from the " + "annotations are removed). " + "I didn't investigate this " + "closely, though.", + "role": "experimental_condition", + }, + { + "name": "profile_first_published", + "dtype": "string", + "description": "citation or reference " + "indicating where this " + "expression profile was " + "first published", + "role": "experimental_condition", + }, + { + "name": "chase_notes", + "dtype": "string", + "description": "notes added during data " + "curation and parsing", + }, + ] + }, + } + ], + } diff --git a/tfbpapi/tests/datainfo/test_filter_resolver.py b/tfbpapi/tests/datainfo/test_filter_resolver.py index 38cfa4b..a56d888 100644 --- a/tfbpapi/tests/datainfo/test_filter_resolver.py +++ b/tfbpapi/tests/datainfo/test_filter_resolver.py @@ -1,11 +1,7 @@ """Tests for DatasetFilterResolver.""" -from pathlib import Path -from tempfile import NamedTemporaryFile - import pandas as pd import pytest -import yaml from tfbpapi.datainfo.filter_resolver import ( DatasetFilterResolver, @@ -21,44 +17,64 @@ def test_get_nested_value_simple(self): """Test getting nested values with dot notation.""" data = { "environmental_conditions": { - "media": { - "name": "YPD", - "carbon_source": [{"compound": "D-glucose"}] - }, - "temperature_celsius": 30 + "media": {"name": "YPD", "carbon_source": [{"compound": "D-glucose"}]}, + "temperature_celsius": 30, } } - assert get_nested_value(data, "environmental_conditions.temperature_celsius") == 30 + assert ( + get_nested_value(data, "environmental_conditions.temperature_celsius") == 30 + ) assert get_nested_value(data, "environmental_conditions.media.name") == "YPD" assert get_nested_value(data, "nonexistent.path") is None - assert get_nested_value(data, "environmental_conditions.media.nonexistent") is None + assert ( + get_nested_value(data, "environmental_conditions.media.nonexistent") is None + ) - def test_get_nested_value_fallback(self): - """Test fallback path resolution for field-level definitions.""" - # Field-level definition (no environmental_conditions wrapper) + def test_get_nested_value_no_fallback(self): + """Test that paths must be correct - no automatic fallback.""" + # Field-level definition (no experimental_conditions wrapper) field_def = { - "media": { - "name": "YPD", - "carbon_source": [{"compound": "D-glucose"}] - }, - "temperature_celsius": 30 + "media": {"name": "YPD", "carbon_source": [{"compound": "D-glucose"}]}, + "temperature_celsius": 30, } - # Path with environmental_conditions should fallback to path without it - assert get_nested_value(field_def, "environmental_conditions.media.name") == "YPD" - assert get_nested_value(field_def, "environmental_conditions.temperature_celsius") == 30 + # Wrong path should return None (no fallback) + assert get_nested_value(field_def, "experimental_conditions.media.name") is None + assert ( + get_nested_value(field_def, "experimental_conditions.temperature_celsius") + is None + ) - # Direct path should still work + # Correct direct paths work assert get_nested_value(field_def, "media.name") == "YPD" assert get_nested_value(field_def, "temperature_celsius") == 30 + # Repo-level definition (with experimental_conditions wrapper) + repo_def = { + "experimental_conditions": { + "media": {"name": "SC", "carbon_source": [{"compound": "D-glucose"}]}, + "temperature_celsius": 30, + } + } + + # Full path works + assert get_nested_value(repo_def, "experimental_conditions.media.name") == "SC" + assert ( + get_nested_value(repo_def, "experimental_conditions.temperature_celsius") + == 30 + ) + + # Shortened path does NOT work (no fallback) + assert get_nested_value(repo_def, "media.name") is None + assert get_nested_value(repo_def, "temperature_celsius") is None + def test_extract_compound_names(self): """Test extracting compound names from various formats.""" # List of dicts value1 = [ {"compound": "D-glucose", "concentration_percent": 2}, - {"compound": "D-galactose", "concentration_percent": 1} + {"compound": "D-galactose", "concentration_percent": 1}, ] assert extract_compound_names(value1) == ["D-glucose", "D-galactose"] @@ -78,42 +94,29 @@ def test_extract_compound_names(self): class TestDatasetFilterResolver: """Test DatasetFilterResolver class.""" - @pytest.fixture - def simple_config(self): - """Create a simple test configuration.""" - return { + def test_init(self, write_config): + """Test initialization.""" + config = { "filters": { "carbon_source": ["D-glucose", "D-galactose"], - "temperature_celsius": [30] + "temperature_celsius": [30], }, - "dataset_mappings": { - "BrentLab/harbison_2004": { - "datasets": { - "harbison_2004": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source" - }, - "temperature_celsius": { - "field": "condition", - "path": "temperature_celsius" - } - } + "BrentLab/harbison_2004": { + "dataset": { + "harbison_2004": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source", + }, + "temperature_celsius": { + "field": "condition", + "path": "temperature_celsius", + }, } } - } + }, } - - @pytest.fixture - def config_file(self, simple_config, tmp_path): - """Create a temporary config file.""" - config_path = tmp_path / "test_config.yaml" - with open(config_path, 'w') as f: - yaml.dump(simple_config, f) - return config_path - - def test_init(self, config_file): - """Test initialization.""" + config_file = write_config(config) resolver = DatasetFilterResolver(config_file) assert len(resolver.filters) == 2 @@ -127,15 +130,35 @@ def test_init_missing_file(self): with pytest.raises(FileNotFoundError): DatasetFilterResolver("nonexistent.yaml") - def test_resolve_filters_mode_conditions(self, config_file): + def test_resolve_filters_mode_conditions(self, write_config): """Test resolve_filters in conditions mode.""" + config = { + "filters": { + "carbon_source": ["D-glucose", "D-galactose"], + "temperature_celsius": [30], + }, + "BrentLab/harbison_2004": { + "dataset": { + "harbison_2004": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source", + }, + "temperature_celsius": { + "field": "condition", + "path": "temperature_celsius", + }, + } + } + }, + } + config_file = write_config(config) resolver = DatasetFilterResolver(config_file) - # This will actually try to load the DataCard, so it's more of an integration test - # For now, test the structure + # This will actually try to load the DataCard, + # so it's more of an integration test For now, test the structure results = resolver.resolve_filters( - repos=[("BrentLab/harbison_2004", "harbison_2004")], - mode="conditions" + repos=[("BrentLab/harbison_2004", "harbison_2004")], mode="conditions" ) assert "BrentLab/harbison_2004" in results @@ -148,107 +171,89 @@ def test_resolve_filters_mode_conditions(self, config_file): if result["included"]: assert "matching_field_values" in result - def test_resolve_filters_invalid_mode(self, config_file): + def test_resolve_filters_invalid_mode(self, write_config): """Test resolve_filters with invalid mode.""" + config = { + "filters": { + "carbon_source": ["D-glucose", "D-galactose"], + "temperature_celsius": [30], + }, + "BrentLab/harbison_2004": { + "dataset": { + "harbison_2004": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source", + }, + "temperature_celsius": { + "field": "condition", + "path": "temperature_celsius", + }, + } + } + }, + } + config_file = write_config(config) resolver = DatasetFilterResolver(config_file) with pytest.raises(ValueError, match="Invalid mode"): resolver.resolve_filters( - repos=[("BrentLab/harbison_2004", "harbison_2004")], - mode="invalid" + repos=[("BrentLab/harbison_2004", "harbison_2004")], mode="invalid" ) - def test_repr(self, config_file): + def test_repr(self, write_config): """Test string representation.""" - resolver = DatasetFilterResolver(config_file) - repr_str = repr(resolver) - - assert "DatasetFilterResolver" in repr_str - assert "2 filters" in repr_str - assert "1 datasets" in repr_str - - def test_hierarchical_config(self, tmp_path): - """Test hierarchical configuration with repo_level and dataset-specific mappings.""" config = { "filters": { - "carbon_source": ["D-glucose"], - "temperature_celsius": [30] + "carbon_source": ["D-glucose", "D-galactose"], + "temperature_celsius": [30], }, - "dataset_mappings": { - "BrentLab/test_repo": { - "repo_level": { + "BrentLab/harbison_2004": { + "dataset": { + "harbison_2004": { "carbon_source": { - "path": "environmental_conditions.media.carbon_source" - } - }, - "datasets": { - "dataset1": { - "temperature_celsius": { - "field": "condition", - "path": "environmental_conditions.temperature_celsius" - } + "field": "condition", + "path": "media.carbon_source", + }, + "temperature_celsius": { + "field": "condition", + "path": "temperature_celsius", }, - "dataset2": { - "temperature_celsius": { - "field": "condition", - "path": "custom.temp.path" - } - } } } - } + }, } + config_file = write_config(config) + resolver = DatasetFilterResolver(config_file) + repr_str = repr(resolver) - config_path = tmp_path / "hierarchical_config.yaml" - with open(config_path, 'w') as f: - yaml.dump(config, f) - - resolver = DatasetFilterResolver(config_path) - - # Test property mapping resolution - mappings1 = resolver._get_property_mappings("BrentLab/test_repo", "dataset1") - assert "carbon_source" in mappings1 # From repo_level - assert "temperature_celsius" in mappings1 # From dataset-specific - assert mappings1["temperature_celsius"]["path"] == "environmental_conditions.temperature_celsius" - - mappings2 = resolver._get_property_mappings("BrentLab/test_repo", "dataset2") - assert "carbon_source" in mappings2 # From repo_level - assert "temperature_celsius" in mappings2 # Overridden by dataset-specific - assert mappings2["temperature_celsius"]["path"] == "custom.temp.path" + assert "DatasetFilterResolver" in repr_str + assert "2 filters" in repr_str + assert "1 datasets" in repr_str class TestRealDataCards: """Integration tests with real datacards.""" - def test_harbison_2004_glucose_filter(self, tmp_path): + def test_harbison_2004_glucose_filter(self, harbison_2004_datacard, write_config): """Test filtering harbison_2004 for glucose samples.""" - # Create config for glucose filtering config = { - "filters": { - "carbon_source": ["D-glucose"] - }, - "dataset_mappings": { - "BrentLab/harbison_2004": { - "datasets": { - "harbison_2004": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source" - } + "filters": {"carbon_source": ["D-glucose"]}, + "BrentLab/harbison_2004": { + "dataset": { + "harbison_2004": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source", } } } - } + }, } - - config_path = tmp_path / "glucose_config.yaml" - with open(config_path, 'w') as f: - yaml.dump(config, f) - + config_path = write_config(config) resolver = DatasetFilterResolver(config_path) results = resolver.resolve_filters( - repos=[("BrentLab/harbison_2004", "harbison_2004")], - mode="conditions" + repos=[("BrentLab/harbison_2004", "harbison_2004")], mode="conditions" ) assert "BrentLab/harbison_2004" in results @@ -264,38 +269,38 @@ def test_harbison_2004_glucose_filter(self, tmp_path): if "condition" in result["matching_field_values"]: matching = result["matching_field_values"]["condition"] # YPD, HEAT, H2O2Hi, H2O2Lo, Acid, Alpha, BUT14, BUT90 all have D-glucose - expected_glucose_conditions = ["YPD", "HEAT", "H2O2Hi", "H2O2Lo", "Acid", "Alpha", "BUT14", "BUT90"] + expected_glucose_conditions = [ + "YPD", + "HEAT", + "H2O2Hi", + "H2O2Lo", + "Acid", + "Alpha", + "BUT14", + "BUT90", + ] for cond in expected_glucose_conditions: assert cond in matching, f"{cond} should be in matching conditions" - def test_harbison_2004_galactose_filter(self, tmp_path): + def test_harbison_2004_galactose_filter(self, harbison_2004_datacard, write_config): """Test filtering harbison_2004 for galactose samples.""" config = { - "filters": { - "carbon_source": ["D-galactose"] - }, - "dataset_mappings": { - "BrentLab/harbison_2004": { - "datasets": { - "harbison_2004": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source" - } + "filters": {"carbon_source": ["D-galactose"]}, + "BrentLab/harbison_2004": { + "dataset": { + "harbison_2004": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source", } } } - } + }, } - - config_path = tmp_path / "galactose_config.yaml" - with open(config_path, 'w') as f: - yaml.dump(config, f) - + config_path = write_config(config) resolver = DatasetFilterResolver(config_path) results = resolver.resolve_filters( - repos=[("BrentLab/harbison_2004", "harbison_2004")], - mode="conditions" + repos=[("BrentLab/harbison_2004", "harbison_2004")], mode="conditions" ) result = results["BrentLab/harbison_2004"] @@ -306,34 +311,25 @@ def test_harbison_2004_galactose_filter(self, tmp_path): matching = result["matching_field_values"]["condition"] assert "GAL" in matching - def test_harbison_2004_samples_mode(self, tmp_path): + def test_harbison_2004_samples_mode(self, harbison_2004_datacard, write_config): """Test retrieving sample-level metadata.""" config = { - "filters": { - "carbon_source": ["D-glucose"] - }, - "dataset_mappings": { - "BrentLab/harbison_2004": { - "datasets": { - "harbison_2004": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source" - } + "filters": {"carbon_source": ["D-glucose"]}, + "BrentLab/harbison_2004": { + "dataset": { + "harbison_2004": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source", } } } - } + }, } - - config_path = tmp_path / "samples_config.yaml" - with open(config_path, 'w') as f: - yaml.dump(config, f) - + config_path = write_config(config) resolver = DatasetFilterResolver(config_path) results = resolver.resolve_filters( - repos=[("BrentLab/harbison_2004", "harbison_2004")], - mode="samples" + repos=[("BrentLab/harbison_2004", "harbison_2004")], mode="samples" ) result = results["BrentLab/harbison_2004"] @@ -345,34 +341,25 @@ def test_harbison_2004_samples_mode(self, tmp_path): assert isinstance(df, pd.DataFrame) assert len(df) > 0 # Should have some samples - def test_harbison_2004_full_data_mode(self, tmp_path): + def test_harbison_2004_full_data_mode(self, harbison_2004_datacard, write_config): """Test retrieving full data.""" config = { - "filters": { - "carbon_source": ["D-glucose"] - }, - "dataset_mappings": { - "BrentLab/harbison_2004": { - "datasets": { - "harbison_2004": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source" - } + "filters": {"carbon_source": ["D-glucose"]}, + "BrentLab/harbison_2004": { + "dataset": { + "harbison_2004": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source", } } } - } + }, } - - config_path = tmp_path / "full_data_config.yaml" - with open(config_path, 'w') as f: - yaml.dump(config, f) - + config_path = write_config(config) resolver = DatasetFilterResolver(config_path) results = resolver.resolve_filters( - repos=[("BrentLab/harbison_2004", "harbison_2004")], - mode="full_data" + repos=[("BrentLab/harbison_2004", "harbison_2004")], mode="full_data" ) result = results["BrentLab/harbison_2004"] @@ -383,3 +370,125 @@ def test_harbison_2004_full_data_mode(self, tmp_path): df = result["data"] assert isinstance(df, pd.DataFrame) assert len(df) > 0 # Should have data rows + + def test_hackett_2020_glucose_filter(self, hackett_2020_datacard, write_config): + """Test filtering hackett_2020 for glucose samples.""" + config = { + "filters": {"carbon_source": ["D-glucose"]}, + "BrentLab/hackett_2020": { + "dataset": { + "hackett_2020": {"carbon_source": {"path": "media.carbon_source"}} + } + }, + } + config_path = write_config(config) + resolver = DatasetFilterResolver(config_path) + results = resolver.resolve_filters( + repos=[("BrentLab/hackett_2020", "hackett_2020")], mode="conditions" + ) + + assert "BrentLab/hackett_2020" in results + result = results["BrentLab/hackett_2020"] + + # Should be included (hackett_2020 has D-glucose at repo level) + assert result["included"] is True + + def test_kemmeren_2014_glucose_filter(self, kemmeren_2014_datacard, write_config): + """Test filtering kemmeren_2014 for glucose samples.""" + config = { + "filters": {"carbon_source": ["D-glucose"]}, + "BrentLab/kemmeren_2014": { + "dataset": { + "kemmeren_2014": {"carbon_source": {"path": "media.carbon_source"}} + } + }, + } + config_path = write_config(config) + resolver = DatasetFilterResolver(config_path) + results = resolver.resolve_filters( + repos=[("BrentLab/kemmeren_2014", "kemmeren_2014")], mode="conditions" + ) + + assert "BrentLab/kemmeren_2014" in results + result = results["BrentLab/kemmeren_2014"] + + # Should be included (kemmeren_2014 has D-glucose at repo level) + assert result["included"] is True + + def test_kemmeren_2014_temperature_filter( + self, kemmeren_2014_datacard, write_config + ): + """Test filtering kemmeren_2014 for temperature.""" + config = { + "filters": {"temperature_celsius": [30]}, + "BrentLab/kemmeren_2014": { + "dataset": { + "kemmeren_2014": { + "temperature_celsius": {"path": "temperature_celsius"} + } + } + }, + } + config_path = write_config(config) + resolver = DatasetFilterResolver(config_path) + results = resolver.resolve_filters( + repos=[("BrentLab/kemmeren_2014", "kemmeren_2014")], mode="conditions" + ) + + assert "BrentLab/kemmeren_2014" in results + result = results["BrentLab/kemmeren_2014"] + + # Should be included (kemmeren_2014 has temperature_celsius: 30 at repo level) + assert result["included"] is True + + def test_multi_repo_glucose_filter( + self, + harbison_2004_datacard, + hackett_2020_datacard, + kemmeren_2014_datacard, + write_config, + ): + """Test filtering D-glucose across multiple repos.""" + config = { + "filters": {"carbon_source": ["D-glucose"]}, + "BrentLab/harbison_2004": { + "dataset": { + "harbison_2004": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source", + } + } + } + }, + "BrentLab/hackett_2020": { + "dataset": { + "hackett_2020": {"carbon_source": {"path": "media.carbon_source"}} + } + }, + "BrentLab/kemmeren_2014": { + "dataset": { + "kemmeren_2014": {"carbon_source": {"path": "media.carbon_source"}} + } + }, + } + config_path = write_config(config) + resolver = DatasetFilterResolver(config_path) + results = resolver.resolve_filters( + repos=[ + ("BrentLab/harbison_2004", "harbison_2004"), + ("BrentLab/hackett_2020", "hackett_2020"), + ("BrentLab/kemmeren_2014", "kemmeren_2014"), + ], + mode="conditions", + ) + + # All three repos should be included + assert "BrentLab/harbison_2004" in results + assert results["BrentLab/harbison_2004"]["included"] is True + + assert "BrentLab/hackett_2020" in results + assert results["BrentLab/hackett_2020"]["included"] is True + + assert "BrentLab/kemmeren_2014" in results + assert results["BrentLab/kemmeren_2014"]["included"] is True From 1d7da51e1457f010008beb13b21ea136f2fef31d Mon Sep 17 00:00:00 2001 From: chasem Date: Mon, 15 Dec 2025 20:44:26 -0600 Subject: [PATCH 20/49] tmp --- tfbpapi/datainfo/__init__.py | 6 + tfbpapi/datainfo/filter_resolver.py | 639 ------------------ tfbpapi/datainfo/metadata_builder.py | 551 +++++++++++++++ tfbpapi/datainfo/metadata_config_models.py | 312 +++++++++ .../tests/datainfo/test_filter_resolver.py | 494 -------------- .../tests/datainfo/test_metadata_builder.py | 332 +++++++++ .../datainfo/test_metadata_config_models.py | 386 +++++++++++ 7 files changed, 1587 insertions(+), 1133 deletions(-) delete mode 100644 tfbpapi/datainfo/filter_resolver.py create mode 100644 tfbpapi/datainfo/metadata_builder.py create mode 100644 tfbpapi/datainfo/metadata_config_models.py delete mode 100644 tfbpapi/tests/datainfo/test_filter_resolver.py create mode 100644 tfbpapi/tests/datainfo/test_metadata_builder.py create mode 100644 tfbpapi/tests/datainfo/test_metadata_config_models.py diff --git a/tfbpapi/datainfo/__init__.py b/tfbpapi/datainfo/__init__.py index 11f3387..c1643f6 100644 --- a/tfbpapi/datainfo/__init__.py +++ b/tfbpapi/datainfo/__init__.py @@ -1,5 +1,7 @@ from .datacard import DataCard from .fetchers import HfDataCardFetcher, HfRepoStructureFetcher, HfSizeInfoFetcher +from .metadata_builder import MetadataBuilder +from .metadata_config_models import MetadataConfig, PropertyMapping, RepositoryConfig from .metadata_manager import MetadataManager from .models import ( DatasetCard, @@ -15,6 +17,10 @@ "HfDataCardFetcher", "HfRepoStructureFetcher", "HfSizeInfoFetcher", + "MetadataBuilder", + "MetadataConfig", + "PropertyMapping", + "RepositoryConfig", "MetadataManager", "DatasetCard", "DatasetConfig", diff --git a/tfbpapi/datainfo/filter_resolver.py b/tfbpapi/datainfo/filter_resolver.py deleted file mode 100644 index 13a53a5..0000000 --- a/tfbpapi/datainfo/filter_resolver.py +++ /dev/null @@ -1,639 +0,0 @@ -""" -Dataset filter resolver with external configuration for heterogeneous datasets. - -This module provides a simple, configuration-driven approach to filtering samples -across datasets with varying experimental condition structures. Users specify -filters and dataset-specific property paths in external YAML files. - -Key Components: -- DatasetFilterResolver: Main class for applying filters across datasets -- Three output modes: conditions, samples, full_data -- External YAML configuration for filters and property mappings -- Automatic detection of property location (repo/config/field level) - -Example Configuration: - ```yaml - filters: - carbon_source: ["D-glucose", "D-galactose"] - temperature_celsius: [30, 37] - - dataset_mappings: - "BrentLab/harbison_2004": - carbon_source: - path: "environmental_conditions.media.carbon_source" - temperature_celsius: - path: "environmental_conditions.temperature_celsius" - ``` - -Example Usage: - >>> resolver = DatasetFilterResolver("filters.yaml") - >>> results = resolver.resolve_filters( - ... repos=[("BrentLab/harbison_2004", "harbison_2004")], - ... mode="conditions" - ... ) - -""" - -from __future__ import annotations - -from pathlib import Path -from typing import Any - -import pandas as pd -import yaml # type: ignore[import-untyped] - -from tfbpapi.datainfo import DataCard -from tfbpapi.errors import DataCardError -from tfbpapi.HfQueryAPI import HfQueryAPI - - -def get_nested_value(data: dict, path: str) -> Any: - """ - Navigate nested dict using dot notation. - - Handles missing intermediate keys gracefully by returning None. - - :param data: Dictionary to navigate - :param path: Dot-separated path (e.g., "media.carbon_source") - :return: Value at path or None if not found - - """ - if not isinstance(data, dict): - return None - - keys = path.split(".") - current = data - - for key in keys: - if not isinstance(current, dict) or key not in current: - return None - current = current[key] - - return current - - -def extract_compound_names(value: Any) -> list[str]: - """ - Extract compound names from various representations. - - Handles: - - List of dicts: [{"compound": "D-glucose", ...}] -> ["D-glucose"] - - String: "D-glucose" -> ["D-glucose"] - - None or "unspecified" -> [] - - :param value: Value to extract from - :return: List of compound names - - """ - if value is None or value == "unspecified": - return [] - - if isinstance(value, str): - return [value] - - if isinstance(value, list): - compounds = [] - for item in value: - if isinstance(item, dict) and "compound" in item: - compounds.append(item["compound"]) - elif isinstance(item, str): - compounds.append(item) - return compounds - - return [] - - -class DatasetFilterResolver: - """ - Resolve filters across heterogeneous datasets using external configuration. - - This class takes an external YAML configuration specifying: - 1. Filter criteria (which values are acceptable for each property) - 2. Repository and dataset-specific property paths (where to find each property) - - Configuration structure: - filters: - property_name: [value1, value2] - - BrentLab/repo_name: - # Repo-wide properties (apply to all datasets in this repository) - # Paths are relative to experimental_conditions at the repository level - property1: - path: media.name - - # Dataset-specific section - dataset: - dataset_name: - # Dataset-specific properties (apply only to this dataset) - # Paths are relative to experimental_conditions at the config level - property2: - path: temperature_celsius - - # Field-level properties (per-sample variation) - # Paths are relative to field definitions (NOT experimental_conditions) - property3: - field: condition - path: media.carbon_source - - Path Resolution: - - Repo-wide & dataset-specific: Paths automatically - prepended with "experimental_conditions." - Example: path "media.name" resolves to experimental_conditions.media.name - - - Field-level: Paths used directly on field definitions (no prepending) - Example: field "condition", path "media.carbon_source" - looks in condition field's definitions for media.carbon_source - - Examples: - # Repo-wide property (applies to all datasets in BrentLab/kemmeren_2014) - filters: - temperature_celsius: [30] - - BrentLab/kemmeren_2014: - temperature_celsius: - path: temperature_celsius - - # Dataset-specific property (applies only to kemmeren_2014 dataset) - BrentLab/kemmeren_2014: - dataset: - kemmeren_2014: - carbon_source: - path: media.carbon_source - - # Field-level property (per-sample variation via condition field) - BrentLab/harbison_2004: - dataset: - harbison_2004: - carbon_source: - field: condition - path: media.carbon_source - - It then resolves filters across datasets with three output modes: - - conditions: Just which field values match (no data retrieval) - - samples: Sample-level metadata (one row per sample_id) - - full_data: All measurements for matching samples - - Attributes: - config: Loaded configuration dict - filters: Filter criteria from config - mappings: Repository/dataset-specific property paths - - """ - - def __init__(self, config_path: Path | str): - """ - Initialize resolver with external configuration. - - :param config_path: Path to YAML configuration file - - """ - self.config = self._load_config(Path(config_path)) - self.filters = self.config.get("filters", {}) - # Extract mappings: all keys except 'filters' are repository IDs - self.mappings = {k: v for k, v in self.config.items() if k != "filters"} - - def _load_config(self, config_path: Path) -> dict: - """ - Load YAML configuration file. - - :param config_path: Path to YAML file - :return: Configuration dict - :raises FileNotFoundError: If config file doesn't exist - :raises yaml.YAMLError: If config is invalid YAML - - """ - if not config_path.exists(): - raise FileNotFoundError(f"Configuration file not found: {config_path}") - - with open(config_path) as f: - config = yaml.safe_load(f) - - if not isinstance(config, dict): - raise ValueError("Configuration must be a dict") - - return config - - def _get_property_mappings(self, repo_id: str, config_name: str) -> dict: - """ - Get property mappings for a specific repo/dataset combination. - - Merges repo-wide and dataset-specific mappings, - with dataset-specific taking precedence. - - Configuration format: - BrentLab/repo_name: - # Repo-wide properties (apply to all datasets) - property1: - path: path.in.experimental_conditions - - # Dataset-specific section - dataset: - dataset_name: - property2: - field: field_name # For field-level definitions - path: path.within.field - - Examples: - 1. Field-specific path (for field-level definitions): - carbon_source: - field: condition - path: media.carbon_source - - 2. Repo-level path (for experimental_conditions): - temperature: - path: temperature_celsius - - :param repo_id: Repository ID - :param config_name: Dataset/config name - :return: Merged property mappings dict - - """ - if repo_id not in self.mappings: - return {} - - repo_config = self.mappings[repo_id] - mappings = {} - - # Add repo-wide properties (all keys except 'dataset') - for key, value in repo_config.items(): - if key != "dataset": - mappings[key] = value - - # Override with dataset-specific properties - if "dataset" in repo_config: - if config_name in repo_config["dataset"]: - mappings.update(repo_config["dataset"][config_name]) - - return mappings - - def resolve_filters( - self, - repos: list[tuple[str, str]], - mode: str = "conditions", - token: str | None = None, - ) -> dict[str, Any]: - """ - Resolve filters across datasets. - - :param repos: List of (repo_id, config_name) tuples to check - :param mode: Output mode - "conditions", "samples", or "full_data" - :param token: Optional HuggingFace token for private repos - :return: Dict mapping repo_id to results - :raises ValueError: If mode is invalid - - """ - if mode not in ["conditions", "samples", "full_data"]: - raise ValueError( - f"Invalid mode: {mode}. Must be 'conditions', " - "'samples', or 'full_data'" - ) - - results = {} - - for repo_id, config_name in repos: - try: - # Load DataCard - card = DataCard(repo_id, token=token) - - # Check if this dataset has mappings - if repo_id not in self.mappings: - results[repo_id] = { - "included": False, - "reason": f"No property mappings defined for {repo_id}", - } - continue - - # Check repo/config level filters (Level 1) - included, reason = self._check_repo_config_level( - card, config_name, repo_id - ) - - if not included: - results[repo_id] = {"included": False, "reason": reason} - continue - - # Dataset passes Level 1, check field-level filters (Level 2) - matching_field_values = self._check_field_level( - card, config_name, repo_id - ) - - # Build result based on mode - result = { - "included": True, - "matching_field_values": matching_field_values, - } - - if mode == "conditions": - # Mode 0: Just conditions, no data - pass - elif mode == "samples": - # Mode 1: Sample-level metadata - result["data"] = self._get_sample_metadata( - repo_id, config_name, matching_field_values, token - ) - elif mode == "full_data": - # Mode 2: Full data with measurements - result["data"] = self._get_full_data( - repo_id, config_name, matching_field_values, token - ) - - results[repo_id] = result - - except Exception as e: - results[repo_id] = { - "included": False, - "reason": f"Error processing dataset: {str(e)}", - } - - return results - - def _check_repo_config_level( - self, - card: DataCard, - config_name: str, - repo_id: str, - ) -> tuple[bool, str]: - """ - Check if repo/config level conditions match filters (Level 1). - - :param card: DataCard instance - :param config_name: Configuration name - :param repo_id: Repository ID - :return: (included, reason) tuple - - """ - # Get repo and config level conditions - try: - conditions = card.get_experimental_conditions(config_name) - except DataCardError: - conditions = {} - - if not conditions: - # No repo/config conditions to check, include by default - return True, "" - - # Check each filter property at repo/config level - property_mappings = self._get_property_mappings(repo_id, config_name) - - for filter_prop, acceptable_values in self.filters.items(): - if filter_prop not in property_mappings: - continue - - mapping = property_mappings[filter_prop] - - # Check if this is a field-level mapping - # (should not be checked at repo level) - if "field" in mapping: - # Skip field-level properties at repo/config level - continue - - # Get path for this property - path = mapping["path"] - - # Prepend experimental_conditions for repo-level paths - full_path = f"experimental_conditions.{path}" - - # Try to get value at this path - value = get_nested_value(conditions, full_path) - - if value is None: - # Property not specified at repo/config level, skip - continue - - # Extract compound names if this is a compound list - if isinstance(value, list) and value and isinstance(value[0], dict): - actual_values = extract_compound_names(value) - else: - actual_values = [value] if not isinstance(value, list) else value - - # Check if any actual value matches acceptable values - matches = False - for actual in actual_values: - if actual in acceptable_values: - matches = True - break - - if not matches: - return ( - False, - f"{filter_prop}: found {actual_values}, " - f"wanted {acceptable_values}", - ) - - return True, "" - - def _check_field_level( - self, - card: DataCard, - config_name: str, - repo_id: str, - ) -> dict[str, list[str]]: - """ - Check field-level conditions and return matching field values (Level 2). - - :param card: DataCard instance - :param config_name: Configuration name - :param repo_id: Repository ID - :return: Dict mapping field names to lists of matching values - - """ - matching: dict[str, list[str]] = {} - property_mappings = self._get_property_mappings(repo_id, config_name) - - # Get config to find fields with role=experimental_condition - config = card.get_config(config_name) - if not config: - return matching - - # Group property mappings by field (if field is specified) - # field_mappings: {field_name: {prop: path, ...}} - field_mappings: dict[str, dict[str, str]] = {} - general_mappings: dict[str, str] = {} # Properties without field specification - - for prop, mapping in property_mappings.items(): - if "field" in mapping: - # Field-specific mapping - field_name = mapping["field"] - if field_name not in field_mappings: - field_mappings[field_name] = {} - field_mappings[field_name][prop] = mapping["path"] - else: - # General mapping (repo/config level) - general_mappings[prop] = mapping["path"] - - # Process each field that has mappings - for field_name, prop_paths in field_mappings.items(): - # Check if this field has definitions - definitions = card.get_field_definitions(config_name, field_name) - if not definitions: - continue - - matching_values = [] - - for field_value, definition in definitions.items(): - # Check if this field value matches all filter criteria - matches_all = True - - for filter_prop, acceptable_values in self.filters.items(): - if filter_prop not in prop_paths: - continue - - # Get path for this property - path = prop_paths[filter_prop] - - # Get value from this field value's definition - value = get_nested_value(definition, path) - - if value is None: - # Property not in this definition, doesn't match - matches_all = False - break - - # Extract compound names if needed - if isinstance(value, list) and value and isinstance(value[0], dict): - actual_values = extract_compound_names(value) - else: - actual_values = ( - [value] if not isinstance(value, list) else value - ) - - # Check if any actual value matches acceptable values - matches = False - for actual in actual_values: - if actual in acceptable_values: - matches = True - break - - if not matches: - matches_all = False - break - - if matches_all: - matching_values.append(field_value) - - if matching_values: - matching[field_name] = matching_values - - return matching - - def _get_sample_metadata( - self, - repo_id: str, - config_name: str, - matching_field_values: dict[str, list[str]], - token: str | None = None, - ) -> pd.DataFrame: - """ - Get sample-level metadata (Mode 1). - - Returns one row per sample_id with metadata columns. - - :param repo_id: Repository ID - :param config_name: Configuration name - :param matching_field_values: Dict of field names to matching values - :param token: Optional HuggingFace token - :return: DataFrame with sample metadata - - """ - # Initialize query API - api = HfQueryAPI(repo_id, token=token) - - # Build WHERE clause from matching field values - where_conditions = [] - for field_name, values in matching_field_values.items(): - if len(values) == 1: - # Single value: exact match - where_conditions.append(f"{field_name} = '{values[0]}'") - else: - # Multiple values: IN clause - values_str = "', '".join(values) - where_conditions.append(f"{field_name} IN ('{values_str}')") - - where_clause = " AND ".join(where_conditions) if where_conditions else "1=1" - - # Query for distinct sample_id with metadata fields - # Get all columns to understand structure - sql = f""" - SELECT DISTINCT * - FROM {config_name} - WHERE {where_clause} - """ - - try: - df = api.query(sql, config_name) - - # For sample-level, we want one row per sample_id - # Group by sample_id and take first value for other columns - if "sample_id" in df.columns: - # Get metadata columns (exclude measurement columns if possible) - # This is a heuristic - may need refinement - df_samples = df.groupby("sample_id").first().reset_index() - return df_samples - else: - # No sample_id column, return distinct rows - return df - - except Exception as e: - # Return error info - return pd.DataFrame( - {"error": [str(e)], "note": ["Failed to retrieve sample metadata"]} - ) - - def _get_full_data( - self, - repo_id: str, - config_name: str, - matching_field_values: dict[str, list[str]], - token: str | None = None, - ) -> pd.DataFrame: - """ - Get full data with all measurements (Mode 2). - - Returns many rows per sample_id (one per measured feature/target). - - :param repo_id: Repository ID - :param config_name: Configuration name - :param matching_field_values: Dict of field names to matching values - :param token: Optional HuggingFace token - :return: DataFrame with full data - - """ - # Initialize query API - api = HfQueryAPI(repo_id, token=token) - - # Build WHERE clause from matching field values - where_conditions = [] - for field_name, values in matching_field_values.items(): - if len(values) == 1: - # Single value: exact match - where_conditions.append(f"{field_name} = '{values[0]}'") - else: - # Multiple values: IN clause - values_str = "', '".join(values) - where_conditions.append(f"{field_name} IN ('{values_str}')") - - where_clause = " AND ".join(where_conditions) if where_conditions else "1=1" - - # Query for all data matching the conditions - sql = f""" - SELECT * - FROM {config_name} - WHERE {where_clause} - """ - - try: - return api.query(sql, config_name) - except Exception as e: - # Return error info - return pd.DataFrame( - {"error": [str(e)], "note": ["Failed to retrieve full data"]} - ) - - def __repr__(self) -> str: - """String representation.""" - n_filters = len(self.filters) - n_datasets = len(self.mappings) - return ( - f"DatasetFilterResolver({n_filters} filters, " - f"{n_datasets} datasets configured)" - ) diff --git a/tfbpapi/datainfo/metadata_builder.py b/tfbpapi/datainfo/metadata_builder.py new file mode 100644 index 0000000..66208eb --- /dev/null +++ b/tfbpapi/datainfo/metadata_builder.py @@ -0,0 +1,551 @@ +""" +Metadata builder for creating standardized tables across heterogeneous datasets. + +This module provides tools for building standardized metadata views by normalizing +factor levels across datasets with varying experimental condition structures. Users +specify optional alias mappings in external YAML files to standardize factor level +names (e.g., "D-glucose" -> "glucose"). + +Key Components: +- MetadataBuilder: Main class for building normalized metadata across datasets +- normalize_value(): Function for normalizing values using optional alias mappings +- Three output modes: conditions, samples, full_data +- External YAML configuration for aliases and property mappings + +Example Configuration: + ```yaml + factor_aliases: + carbon_source: + glucose: ["D-glucose", "dextrose"] + galactose: ["D-galactose", "Galactose"] + + BrentLab/harbison_2004: + dataset: + harbison_2004: + carbon_source: + field: condition + path: media.carbon_source + ``` + +Example Usage: + >>> builder = MetadataBuilder("metadata_config.yaml") + >>> results = builder.build_metadata( + ... repos=[("BrentLab/harbison_2004", "harbison_2004")], + ... mode="conditions" + ... ) + +""" + +from __future__ import annotations + +from pathlib import Path +from typing import Any, Optional + +import pandas as pd + +from tfbpapi.datainfo import DataCard +from tfbpapi.datainfo.metadata_config_models import MetadataConfig, PropertyMapping +from tfbpapi.errors import DataCardError +from tfbpapi.HfQueryAPI import HfQueryAPI + + +def get_nested_value(data: dict, path: str) -> Any: + """ + Navigate nested dict using dot notation. + + Handles missing intermediate keys gracefully by returning None. + + :param data: Dictionary to navigate + :param path: Dot-separated path (e.g., "media.carbon_source") + :return: Value at path or None if not found + + """ + if not isinstance(data, dict): + return None + + keys = path.split(".") + current = data + + for key in keys: + if not isinstance(current, dict) or key not in current: + return None + current = current[key] + + return current + + +def extract_compound_names(value: Any) -> list[str]: + """ + Extract compound names from various representations. + + Handles: + - List of dicts: [{"compound": "D-glucose", ...}] -> ["D-glucose"] + - String: "D-glucose" -> ["D-glucose"] + - None or "unspecified" -> [] + + :param value: Value to extract from + :return: List of compound names + + """ + if value is None or value == "unspecified": + return [] + + if isinstance(value, str): + return [value] + + if isinstance(value, list): + compounds = [] + for item in value: + if isinstance(item, dict) and "compound" in item: + compounds.append(item["compound"]) + elif isinstance(item, str): + compounds.append(item) + return compounds + + return [] + + +def normalize_value(actual_value: Any, aliases: dict[str, list[Any]] | None) -> str: + """ + Normalize a value using optional alias mappings (case-insensitive). + + Returns the alias name if a match is found, otherwise returns the + original value as a string. This enables standardizing factor level + names across heterogeneous datasets. + + :param actual_value: The value from the data to normalize + :param aliases: Optional dict mapping alias names to lists of actual values. + Example: {"glucose": ["D-glucose", "dextrose"]} + :return: Alias name if match found, otherwise str(actual_value) + + Examples: + # With aliases - exact match + normalize_value("D-glucose", {"glucose": ["D-glucose", "dextrose"]}) + -> "glucose" + + # With aliases - case-insensitive match + normalize_value("DEXTROSE", {"glucose": ["D-glucose", "dextrose"]}) + -> "glucose" + + # No alias match - pass through + normalize_value("maltose", {"glucose": ["D-glucose"]}) + -> "maltose" + + # No aliases provided - pass through + normalize_value("D-glucose", None) + -> "D-glucose" + + # Numeric value + normalize_value(30, {"thirty": [30, "30"]}) + -> "thirty" + + """ + if aliases is None: + return str(actual_value) + + # Convert to string for comparison (case-insensitive) + actual_str = str(actual_value).lower() + + # Check each alias mapping + for alias_name, actual_values in aliases.items(): + for val in actual_values: + if str(val).lower() == actual_str: + return alias_name + + # No match found - pass through original value + return str(actual_value) + + +class MetadataBuilder: + """ + Build standardized metadata tables across heterogeneous datasets. + + This class creates metadata views with normalized factor level names using + optional alias mappings. Unlike a filtering system, this includes ALL data + and simply normalizes the factor level names for standardization. + + Configuration specifies: + 1. Optional factor aliases (for normalizing factor level names) + 2. Repository and dataset-specific property paths (where to find each property) + + Configuration structure: + factor_aliases: # Optional + property_name: + alias_name: [actual_value1, actual_value2] + + BrentLab/repo_name: + # Repo-wide properties (apply to all datasets in this repository) + # Paths are relative to experimental_conditions at the repository level + property1: + path: media.name + + # Dataset-specific section + dataset: + dataset_name: + # Dataset-specific properties (apply only to this dataset) + # Paths are relative to experimental_conditions at the config level + property2: + path: temperature_celsius + + # Field-level properties (per-sample variation) + # Paths are relative to field definitions (NOT experimental_conditions) + property3: + field: condition + path: media.carbon_source + + Path Resolution: + - Repo-wide & dataset-specific: Paths automatically + prepended with "experimental_conditions." + Example: path "media.name" resolves to experimental_conditions.media.name + + - Field-level: Paths used directly on field definitions (no prepending) + Example: field "condition", path "media.carbon_source" + looks in condition field's definitions for media.carbon_source + + Three output modes: + - conditions: Extract normalized metadata (no data retrieval) + - samples: Sample-level metadata (one row per sample_id) + - full_data: All measurements with metadata + + Attributes: + config: Validated MetadataConfig instance + factor_aliases: Optional alias mappings for normalization + + """ + + def __init__(self, config_path: Path | str): + """ + Initialize metadata builder with external configuration. + + :param config_path: Path to YAML configuration file + :raises FileNotFoundError: If config file doesn't exist + :raises ValueError: If configuration is invalid + + """ + self.config = MetadataConfig.from_yaml(config_path) + self.factor_aliases = self.config.factor_aliases + + def _get_property_mappings( + self, repo_id: str, config_name: str + ) -> dict[str, PropertyMapping]: + """ + Get property mappings for a specific repo/dataset combination. + + Merges repo-wide and dataset-specific mappings, with dataset-specific taking + precedence. + + :param repo_id: Repository ID + :param config_name: Dataset/config name + :return: Dict mapping property names to PropertyMapping objects + + """ + return self.config.get_property_mappings(repo_id, config_name) + + def build_metadata( + self, + repos: list[tuple[str, str]], + mode: str = "conditions", + token: str | None = None, + ) -> dict[str, Any]: + """ + Build metadata tables across datasets with normalized factor levels. + + Note: ALL repositories are processed - no filtering/exclusion occurs. + Factor aliases are used to normalize factor level names, not to filter. + + :param repos: List of (repo_id, config_name) tuples to process + :param mode: Output mode - "conditions", "samples", or "full_data" + :param token: Optional HuggingFace token for private repos + :return: Dict mapping repo_id to metadata results + :raises ValueError: If mode is invalid + + """ + if mode not in ["conditions", "samples", "full_data"]: + raise ValueError( + f"Invalid mode: {mode}. Must be 'conditions', " + "'samples', or 'full_data'" + ) + + results = {} + + for repo_id, config_name in repos: + try: + # Load DataCard + card = DataCard(repo_id, token=token) + + # Check if this repository has configuration + if repo_id not in self.config.repositories: + results[repo_id] = { + "error": f"No property mappings defined for {repo_id}" + } + continue + + # Extract and normalize metadata for ALL data + metadata = self._extract_metadata(card, repo_id, config_name) + + # Build result based on mode + result = {"metadata": metadata} + + if mode == "conditions": + # Mode 0: Just metadata, no data + pass + elif mode == "samples": + # Mode 1: Sample-level metadata + result["data"] = self._get_sample_metadata( + repo_id, config_name, metadata, token + ) + elif mode == "full_data": + # Mode 2: Full data with measurements + result["data"] = self._get_full_data( + repo_id, config_name, metadata, token + ) + + results[repo_id] = result + + except Exception as e: + results[repo_id] = {"error": f"Error processing dataset: {str(e)}"} + + return results + + def _extract_metadata( + self, + card: DataCard, + repo_id: str, + config_name: str, + ) -> dict[str, Any]: + """ + Extract and normalize metadata from datacard. + + Extracts ALL metadata with normalized factor level names. + No filtering occurs - all data is included. + + :param card: DataCard instance + :param repo_id: Repository ID + :param config_name: Configuration name + :return: Dict with normalized metadata + + """ + metadata = {} + property_mappings = self._get_property_mappings(repo_id, config_name) + + # Extract repo/config level metadata + repo_metadata = self._extract_repo_level(card, config_name, property_mappings) + metadata.update(repo_metadata) + + # Extract field-level metadata + field_metadata = self._extract_field_level(card, config_name, property_mappings) + if field_metadata: + metadata["field_values"] = field_metadata + + return metadata + + def _extract_repo_level( + self, + card: DataCard, + config_name: str, + property_mappings: dict[str, PropertyMapping], + ) -> dict[str, list[str]]: + """ + Extract and normalize repo/config-level metadata. + + :param card: DataCard instance + :param config_name: Configuration name + :param property_mappings: Property mappings for this repo/dataset + :return: Dict mapping property names to normalized values + + """ + metadata = {} + + # Get repo and config level conditions + try: + conditions = card.get_experimental_conditions(config_name) + except DataCardError: + conditions = {} + + if not conditions: + return metadata + + # Extract each mapped property + for prop_name, mapping in property_mappings.items(): + # Skip field-level mappings (handled separately) + if mapping.field is not None: + continue + + # Get path for this property + path = mapping.path + full_path = f"experimental_conditions.{path}" + + # Get value at this path + value = get_nested_value(conditions, full_path) + + if value is None: + continue + + # Extract compound names if needed + if isinstance(value, list) and value and isinstance(value[0], dict): + actual_values = extract_compound_names(value) + else: + actual_values = [value] if not isinstance(value, list) else value + + # Normalize using aliases (if configured) + aliases = self.factor_aliases.get(prop_name) + normalized_values = [normalize_value(v, aliases) for v in actual_values] + + metadata[prop_name] = normalized_values + + return metadata + + def _extract_field_level( + self, + card: DataCard, + config_name: str, + property_mappings: dict[str, PropertyMapping], + ) -> dict[str, dict[str, Any]]: + """ + Extract and normalize field-level metadata. + + Returns metadata for ALL field values (no filtering). + + :param card: DataCard instance + :param config_name: Configuration name + :param property_mappings: Property mappings for this repo/dataset + :return: Dict mapping field values to their normalized metadata + + """ + field_metadata: dict[str, dict[str, Any]] = {} + + # Group property mappings by field + field_mappings: dict[str, dict[str, str]] = {} + for prop_name, mapping in property_mappings.items(): + if mapping.field is not None: + field_name = mapping.field + if field_name not in field_mappings: + field_mappings[field_name] = {} + field_mappings[field_name][prop_name] = mapping.path + + # Process each field that has mappings + for field_name, prop_paths in field_mappings.items(): + # Get field definitions + definitions = card.get_field_definitions(config_name, field_name) + if not definitions: + continue + + # Extract metadata for ALL field values (no filtering!) + for field_value, definition in definitions.items(): + if field_value not in field_metadata: + field_metadata[field_value] = {} + + for prop_name, path in prop_paths.items(): + # Get value at path + value = get_nested_value(definition, path) + + if value is None: + continue + + # Extract compound names if needed + if isinstance(value, list) and value and isinstance(value[0], dict): + actual_values = extract_compound_names(value) + else: + actual_values = ( + [value] if not isinstance(value, list) else value + ) + + # Normalize using aliases (if configured) + aliases = self.factor_aliases.get(prop_name) + normalized_values = [ + normalize_value(v, aliases) for v in actual_values + ] + + field_metadata[field_value][prop_name] = normalized_values + + return field_metadata + + def _get_sample_metadata( + self, + repo_id: str, + config_name: str, + metadata: dict[str, Any], + token: str | None = None, + ) -> pd.DataFrame: + """ + Get sample-level metadata (Mode 1). + + Returns one row per sample_id with metadata columns. Includes ALL samples (no + filtering). + + :param repo_id: Repository ID + :param config_name: Configuration name + :param metadata: Extracted metadata dict + :param token: Optional HuggingFace token + :return: DataFrame with sample metadata + + """ + # Initialize query API + api = HfQueryAPI(repo_id, token=token) + + # Query for all samples (no WHERE clause filtering) + sql = f""" + SELECT DISTINCT * + FROM {config_name} + """ + + try: + df = api.query(sql, config_name) + + # For sample-level, we want one row per sample_id + if "sample_id" in df.columns: + df_samples = df.groupby("sample_id").first().reset_index() + return df_samples + else: + # No sample_id column, return distinct rows + return df + + except Exception as e: + return pd.DataFrame( + {"error": [str(e)], "note": ["Failed to retrieve sample metadata"]} + ) + + def _get_full_data( + self, + repo_id: str, + config_name: str, + metadata: dict[str, Any], + token: str | None = None, + ) -> pd.DataFrame: + """ + Get full data with all measurements (Mode 2). + + Returns many rows per sample_id (one per measured feature/target). Includes ALL + data (no filtering). + + :param repo_id: Repository ID + :param config_name: Configuration name + :param metadata: Extracted metadata dict + :param token: Optional HuggingFace token + :return: DataFrame with full data + + """ + # Initialize query API + api = HfQueryAPI(repo_id, token=token) + + # Query for all data (no WHERE clause filtering) + sql = f""" + SELECT * + FROM {config_name} + """ + + try: + return api.query(sql, config_name) + except Exception as e: + return pd.DataFrame( + {"error": [str(e)], "note": ["Failed to retrieve full data"]} + ) + + def __repr__(self) -> str: + """String representation.""" + n_props = len(self.factor_aliases) + n_repos = len(self.config.repositories) + return ( + f"MetadataBuilder({n_props} properties with aliases, " + f"{n_repos} repositories configured)" + ) diff --git a/tfbpapi/datainfo/metadata_config_models.py b/tfbpapi/datainfo/metadata_config_models.py new file mode 100644 index 0000000..2cd4ded --- /dev/null +++ b/tfbpapi/datainfo/metadata_config_models.py @@ -0,0 +1,312 @@ +""" +Pydantic models for metadata normalization configuration. + +This module defines the schema for MetadataBuilder configuration files, providing +validation for factor alias mappings and repository configurations. + +""" + +from __future__ import annotations + +from pathlib import Path +from typing import Any, Dict, List, Optional + +import yaml # type: ignore[import-untyped] +from pydantic import BaseModel, Field, field_validator, model_validator + + +class PropertyMapping(BaseModel): + """ + Mapping specification for a single property. + + Attributes: + path: Dot-notation path to the property value. + For repo/config-level: relative to experimental_conditions + For field-level: relative to field definitions + field: Optional field name for field-level properties. + When specified, looks in this field's definitions. + When omitted, looks in repo/config-level experimental_conditions. + + Examples: + Field-level property: + PropertyMapping(field="condition", path="media.carbon_source") + + Repo/config-level property: + PropertyMapping(path="temperature_celsius") + + """ + + field: str | None = Field(None, description="Field name for field-level properties") + path: str = Field(..., min_length=1, description="Dot-notation path to property") + + @field_validator("path") + @classmethod + def validate_path(cls, v: str) -> str: + """Ensure path is not just whitespace.""" + if not v.strip(): + raise ValueError("path cannot be empty or whitespace") + return v.strip() + + @field_validator("field") + @classmethod + def validate_field(cls, v: str | None) -> str | None: + """Ensure field is not empty string if provided.""" + if v is not None and not v.strip(): + raise ValueError("field cannot be empty or whitespace") + return v.strip() if v else None + + +class RepositoryConfig(BaseModel): + """ + Configuration for a single repository. Eg BrentLab/harbison_2004. + + Attributes: + properties: Repo-wide property mappings that apply to all datasets + dataset: Dataset-specific property mappings (override repo-wide) + + Example: + ```python + config = RepositoryConfig( + properties={ + "temperature_celsius": PropertyMapping(path="temperature_celsius") + }, + dataset={ + "dataset_name": { + "carbon_source": PropertyMapping( + field="condition", + path="media.carbon_source" + ) + } + } + ) + ``` + + """ + + properties: dict[str, PropertyMapping] = Field( + default_factory=dict, description="Repo-wide property mappings" + ) + dataset: dict[str, dict[str, PropertyMapping]] | None = Field( + None, description="Dataset-specific property mappings" + ) + + @model_validator(mode="before") + @classmethod + def parse_structure(cls, data: Any) -> Any: + """Parse raw dict structure into typed PropertyMapping objects.""" + if not isinstance(data, dict): + return data + + # Extract and parse dataset section + dataset_section = data.get("dataset") + parsed_datasets: dict[str, dict[str, PropertyMapping]] | None = None + + if dataset_section: + if not isinstance(dataset_section, dict): + raise ValueError("'dataset' key must contain a dict") + + parsed_datasets = {} + for dataset_name, properties in dataset_section.items(): + if not isinstance(properties, dict): + raise ValueError( + f"Dataset '{dataset_name}' must contain a dict of properties" + ) + + # Parse each property mapping into PropertyMapping object + parsed_datasets[dataset_name] = {} + for prop_name, mapping in properties.items(): + try: + parsed_datasets[dataset_name][prop_name] = ( + PropertyMapping.model_validate(mapping) + ) + except Exception as e: + raise ValueError( + f"Invalid property '{prop_name}' in dataset " + f"'{dataset_name}': {e}" + ) from e + + # Parse repo-wide properties (all keys except 'dataset') + parsed_properties = {} + for key, value in data.items(): + if key == "dataset": + continue + + try: + parsed_properties[key] = PropertyMapping.model_validate(value) + except Exception as e: + raise ValueError(f"Invalid repo-wide property '{key}': {e}") from e + + return {"properties": parsed_properties, "dataset": parsed_datasets} + + +class MetadataConfig(BaseModel): + """ + Configuration for building standardized metadata tables. + + Specifies optional alias mappings for normalizing factor levels across + heterogeneous datasets, plus property path mappings for each repository. + + Attributes: + factor_aliases: Optional mappings of standardized names to actual values. + Example: {"carbon_source": {"glucose": ["D-glucose", "dextrose"]}} + repositories: Dict mapping repository IDs to their configurations + + Example: + ```yaml + factor_aliases: + carbon_source: + glucose: ["D-glucose", "dextrose"] + galactose: ["D-galactose", "Galactose"] + + BrentLab/harbison_2004: + dataset: + harbison_2004: + carbon_source: + field: condition + path: media.carbon_source + + BrentLab/kemmeren_2014: + temperature: + path: temperature_celsius + dataset: + kemmeren_2014: + carbon_source: + path: media.carbon_source + ``` + + """ + + factor_aliases: dict[str, dict[str, list[Any]]] = Field( + default_factory=dict, + description="Optional alias mappings for normalizing factor levels", + ) + repositories: dict[str, RepositoryConfig] = Field( + ..., description="Repository configurations keyed by repo ID" + ) + + @field_validator("factor_aliases") + @classmethod + def validate_factor_aliases( + cls, v: dict[str, dict[str, list[Any]]] + ) -> dict[str, dict[str, list[Any]]]: + """Validate factor alias structure.""" + # Empty is OK - aliases are optional + if not v: + return v + + for prop_name, aliases in v.items(): + if not isinstance(aliases, dict): + raise ValueError( + f"Property '{prop_name}' aliases must be a dict, " + f"got {type(aliases).__name__}" + ) + + # Validate each alias mapping + for alias_name, actual_values in aliases.items(): + if not isinstance(actual_values, list): + raise ValueError( + f"Alias '{alias_name}' for '{prop_name}' must map " + f"to a list of values" + ) + if not actual_values: + raise ValueError( + f"Alias '{alias_name}' for '{prop_name}' cannot " + f"have empty value list" + ) + for val in actual_values: + if not isinstance(val, (str, int, float, bool)): + raise ValueError( + f"Alias '{alias_name}' for '{prop_name}' contains " + f"invalid value type: {type(val).__name__}" + ) + + return v + + @model_validator(mode="before") + @classmethod + def parse_repositories(cls, data: Any) -> Any: + """Parse repository configurations from top-level keys.""" + if not isinstance(data, dict): + return data + + # Extract repositories (all keys except 'factor_aliases') + repositories = {} + for key, value in data.items(): + if key != "factor_aliases": + try: + repositories[key] = RepositoryConfig.model_validate(value) + except Exception as e: + raise ValueError( + f"Invalid configuration for repository '{key}': {e}" + ) from e + + if not repositories: + raise ValueError( + "Configuration must have at least one repository configuration" + ) + + return { + "factor_aliases": data.get("factor_aliases", {}), + "repositories": repositories, + } + + @classmethod + def from_yaml(cls, path: Path | str) -> MetadataConfig: + """ + Load and validate configuration from YAML file. + + :param path: Path to YAML configuration file + :return: Validated MetadataConfig instance + :raises FileNotFoundError: If file doesn't exist + :raises ValueError: If configuration is invalid + + """ + path = Path(path) + + if not path.exists(): + raise FileNotFoundError(f"Configuration file not found: {path}") + + with open(path) as f: + data = yaml.safe_load(f) + + if not isinstance(data, dict): + raise ValueError("Configuration must be a YAML dict") + + return cls.model_validate(data) + + def get_repository_config(self, repo_id: str) -> RepositoryConfig | None: + """ + Get configuration for a specific repository. + + :param repo_id: Repository ID (e.g., "BrentLab/harbison_2004") + :return: RepositoryConfig instance or None if not found + + """ + return self.repositories.get(repo_id) + + def get_property_mappings( + self, repo_id: str, config_name: str + ) -> dict[str, PropertyMapping]: + """ + Get merged property mappings for a repo/dataset combination. + + Merges repo-wide and dataset-specific mappings, with dataset-specific taking + precedence. + + :param repo_id: Repository ID + :param config_name: Dataset/config name + :return: Dict mapping property names to PropertyMapping objects + + """ + repo_config = self.get_repository_config(repo_id) + if not repo_config: + return {} + + # Start with repo-wide properties + mappings: dict[str, PropertyMapping] = dict(repo_config.properties) + + # Override with dataset-specific properties + if repo_config.dataset and config_name in repo_config.dataset: + mappings.update(repo_config.dataset[config_name]) + + return mappings diff --git a/tfbpapi/tests/datainfo/test_filter_resolver.py b/tfbpapi/tests/datainfo/test_filter_resolver.py deleted file mode 100644 index a56d888..0000000 --- a/tfbpapi/tests/datainfo/test_filter_resolver.py +++ /dev/null @@ -1,494 +0,0 @@ -"""Tests for DatasetFilterResolver.""" - -import pandas as pd -import pytest - -from tfbpapi.datainfo.filter_resolver import ( - DatasetFilterResolver, - extract_compound_names, - get_nested_value, -) - - -class TestHelperFunctions: - """Test helper functions.""" - - def test_get_nested_value_simple(self): - """Test getting nested values with dot notation.""" - data = { - "environmental_conditions": { - "media": {"name": "YPD", "carbon_source": [{"compound": "D-glucose"}]}, - "temperature_celsius": 30, - } - } - - assert ( - get_nested_value(data, "environmental_conditions.temperature_celsius") == 30 - ) - assert get_nested_value(data, "environmental_conditions.media.name") == "YPD" - assert get_nested_value(data, "nonexistent.path") is None - assert ( - get_nested_value(data, "environmental_conditions.media.nonexistent") is None - ) - - def test_get_nested_value_no_fallback(self): - """Test that paths must be correct - no automatic fallback.""" - # Field-level definition (no experimental_conditions wrapper) - field_def = { - "media": {"name": "YPD", "carbon_source": [{"compound": "D-glucose"}]}, - "temperature_celsius": 30, - } - - # Wrong path should return None (no fallback) - assert get_nested_value(field_def, "experimental_conditions.media.name") is None - assert ( - get_nested_value(field_def, "experimental_conditions.temperature_celsius") - is None - ) - - # Correct direct paths work - assert get_nested_value(field_def, "media.name") == "YPD" - assert get_nested_value(field_def, "temperature_celsius") == 30 - - # Repo-level definition (with experimental_conditions wrapper) - repo_def = { - "experimental_conditions": { - "media": {"name": "SC", "carbon_source": [{"compound": "D-glucose"}]}, - "temperature_celsius": 30, - } - } - - # Full path works - assert get_nested_value(repo_def, "experimental_conditions.media.name") == "SC" - assert ( - get_nested_value(repo_def, "experimental_conditions.temperature_celsius") - == 30 - ) - - # Shortened path does NOT work (no fallback) - assert get_nested_value(repo_def, "media.name") is None - assert get_nested_value(repo_def, "temperature_celsius") is None - - def test_extract_compound_names(self): - """Test extracting compound names from various formats.""" - # List of dicts - value1 = [ - {"compound": "D-glucose", "concentration_percent": 2}, - {"compound": "D-galactose", "concentration_percent": 1}, - ] - assert extract_compound_names(value1) == ["D-glucose", "D-galactose"] - - # String - assert extract_compound_names("D-glucose") == ["D-glucose"] - - # None - assert extract_compound_names(None) == [] - - # "unspecified" - assert extract_compound_names("unspecified") == [] - - # Empty list - assert extract_compound_names([]) == [] - - -class TestDatasetFilterResolver: - """Test DatasetFilterResolver class.""" - - def test_init(self, write_config): - """Test initialization.""" - config = { - "filters": { - "carbon_source": ["D-glucose", "D-galactose"], - "temperature_celsius": [30], - }, - "BrentLab/harbison_2004": { - "dataset": { - "harbison_2004": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source", - }, - "temperature_celsius": { - "field": "condition", - "path": "temperature_celsius", - }, - } - } - }, - } - config_file = write_config(config) - resolver = DatasetFilterResolver(config_file) - - assert len(resolver.filters) == 2 - assert "carbon_source" in resolver.filters - assert resolver.filters["carbon_source"] == ["D-glucose", "D-galactose"] - assert len(resolver.mappings) == 1 - assert "BrentLab/harbison_2004" in resolver.mappings - - def test_init_missing_file(self): - """Test initialization with missing config file.""" - with pytest.raises(FileNotFoundError): - DatasetFilterResolver("nonexistent.yaml") - - def test_resolve_filters_mode_conditions(self, write_config): - """Test resolve_filters in conditions mode.""" - config = { - "filters": { - "carbon_source": ["D-glucose", "D-galactose"], - "temperature_celsius": [30], - }, - "BrentLab/harbison_2004": { - "dataset": { - "harbison_2004": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source", - }, - "temperature_celsius": { - "field": "condition", - "path": "temperature_celsius", - }, - } - } - }, - } - config_file = write_config(config) - resolver = DatasetFilterResolver(config_file) - - # This will actually try to load the DataCard, - # so it's more of an integration test For now, test the structure - results = resolver.resolve_filters( - repos=[("BrentLab/harbison_2004", "harbison_2004")], mode="conditions" - ) - - assert "BrentLab/harbison_2004" in results - result = results["BrentLab/harbison_2004"] - - # Should have included field - assert "included" in result - - # If included, should have matching_field_values - if result["included"]: - assert "matching_field_values" in result - - def test_resolve_filters_invalid_mode(self, write_config): - """Test resolve_filters with invalid mode.""" - config = { - "filters": { - "carbon_source": ["D-glucose", "D-galactose"], - "temperature_celsius": [30], - }, - "BrentLab/harbison_2004": { - "dataset": { - "harbison_2004": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source", - }, - "temperature_celsius": { - "field": "condition", - "path": "temperature_celsius", - }, - } - } - }, - } - config_file = write_config(config) - resolver = DatasetFilterResolver(config_file) - - with pytest.raises(ValueError, match="Invalid mode"): - resolver.resolve_filters( - repos=[("BrentLab/harbison_2004", "harbison_2004")], mode="invalid" - ) - - def test_repr(self, write_config): - """Test string representation.""" - config = { - "filters": { - "carbon_source": ["D-glucose", "D-galactose"], - "temperature_celsius": [30], - }, - "BrentLab/harbison_2004": { - "dataset": { - "harbison_2004": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source", - }, - "temperature_celsius": { - "field": "condition", - "path": "temperature_celsius", - }, - } - } - }, - } - config_file = write_config(config) - resolver = DatasetFilterResolver(config_file) - repr_str = repr(resolver) - - assert "DatasetFilterResolver" in repr_str - assert "2 filters" in repr_str - assert "1 datasets" in repr_str - - -class TestRealDataCards: - """Integration tests with real datacards.""" - - def test_harbison_2004_glucose_filter(self, harbison_2004_datacard, write_config): - """Test filtering harbison_2004 for glucose samples.""" - config = { - "filters": {"carbon_source": ["D-glucose"]}, - "BrentLab/harbison_2004": { - "dataset": { - "harbison_2004": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source", - } - } - } - }, - } - config_path = write_config(config) - resolver = DatasetFilterResolver(config_path) - results = resolver.resolve_filters( - repos=[("BrentLab/harbison_2004", "harbison_2004")], mode="conditions" - ) - - assert "BrentLab/harbison_2004" in results - result = results["BrentLab/harbison_2004"] - - # Should be included - assert result["included"] is True - - # Should have matching field values - assert "matching_field_values" in result - - # Should have condition field with some matching values - if "condition" in result["matching_field_values"]: - matching = result["matching_field_values"]["condition"] - # YPD, HEAT, H2O2Hi, H2O2Lo, Acid, Alpha, BUT14, BUT90 all have D-glucose - expected_glucose_conditions = [ - "YPD", - "HEAT", - "H2O2Hi", - "H2O2Lo", - "Acid", - "Alpha", - "BUT14", - "BUT90", - ] - for cond in expected_glucose_conditions: - assert cond in matching, f"{cond} should be in matching conditions" - - def test_harbison_2004_galactose_filter(self, harbison_2004_datacard, write_config): - """Test filtering harbison_2004 for galactose samples.""" - config = { - "filters": {"carbon_source": ["D-galactose"]}, - "BrentLab/harbison_2004": { - "dataset": { - "harbison_2004": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source", - } - } - } - }, - } - config_path = write_config(config) - resolver = DatasetFilterResolver(config_path) - results = resolver.resolve_filters( - repos=[("BrentLab/harbison_2004", "harbison_2004")], mode="conditions" - ) - - result = results["BrentLab/harbison_2004"] - assert result["included"] is True - - # GAL condition has D-galactose - if "condition" in result["matching_field_values"]: - matching = result["matching_field_values"]["condition"] - assert "GAL" in matching - - def test_harbison_2004_samples_mode(self, harbison_2004_datacard, write_config): - """Test retrieving sample-level metadata.""" - config = { - "filters": {"carbon_source": ["D-glucose"]}, - "BrentLab/harbison_2004": { - "dataset": { - "harbison_2004": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source", - } - } - } - }, - } - config_path = write_config(config) - resolver = DatasetFilterResolver(config_path) - results = resolver.resolve_filters( - repos=[("BrentLab/harbison_2004", "harbison_2004")], mode="samples" - ) - - result = results["BrentLab/harbison_2004"] - assert result["included"] is True - assert "data" in result - - # Should have a DataFrame - df = result["data"] - assert isinstance(df, pd.DataFrame) - assert len(df) > 0 # Should have some samples - - def test_harbison_2004_full_data_mode(self, harbison_2004_datacard, write_config): - """Test retrieving full data.""" - config = { - "filters": {"carbon_source": ["D-glucose"]}, - "BrentLab/harbison_2004": { - "dataset": { - "harbison_2004": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source", - } - } - } - }, - } - config_path = write_config(config) - resolver = DatasetFilterResolver(config_path) - results = resolver.resolve_filters( - repos=[("BrentLab/harbison_2004", "harbison_2004")], mode="full_data" - ) - - result = results["BrentLab/harbison_2004"] - assert result["included"] is True - assert "data" in result - - # Should have a DataFrame - df = result["data"] - assert isinstance(df, pd.DataFrame) - assert len(df) > 0 # Should have data rows - - def test_hackett_2020_glucose_filter(self, hackett_2020_datacard, write_config): - """Test filtering hackett_2020 for glucose samples.""" - config = { - "filters": {"carbon_source": ["D-glucose"]}, - "BrentLab/hackett_2020": { - "dataset": { - "hackett_2020": {"carbon_source": {"path": "media.carbon_source"}} - } - }, - } - config_path = write_config(config) - resolver = DatasetFilterResolver(config_path) - results = resolver.resolve_filters( - repos=[("BrentLab/hackett_2020", "hackett_2020")], mode="conditions" - ) - - assert "BrentLab/hackett_2020" in results - result = results["BrentLab/hackett_2020"] - - # Should be included (hackett_2020 has D-glucose at repo level) - assert result["included"] is True - - def test_kemmeren_2014_glucose_filter(self, kemmeren_2014_datacard, write_config): - """Test filtering kemmeren_2014 for glucose samples.""" - config = { - "filters": {"carbon_source": ["D-glucose"]}, - "BrentLab/kemmeren_2014": { - "dataset": { - "kemmeren_2014": {"carbon_source": {"path": "media.carbon_source"}} - } - }, - } - config_path = write_config(config) - resolver = DatasetFilterResolver(config_path) - results = resolver.resolve_filters( - repos=[("BrentLab/kemmeren_2014", "kemmeren_2014")], mode="conditions" - ) - - assert "BrentLab/kemmeren_2014" in results - result = results["BrentLab/kemmeren_2014"] - - # Should be included (kemmeren_2014 has D-glucose at repo level) - assert result["included"] is True - - def test_kemmeren_2014_temperature_filter( - self, kemmeren_2014_datacard, write_config - ): - """Test filtering kemmeren_2014 for temperature.""" - config = { - "filters": {"temperature_celsius": [30]}, - "BrentLab/kemmeren_2014": { - "dataset": { - "kemmeren_2014": { - "temperature_celsius": {"path": "temperature_celsius"} - } - } - }, - } - config_path = write_config(config) - resolver = DatasetFilterResolver(config_path) - results = resolver.resolve_filters( - repos=[("BrentLab/kemmeren_2014", "kemmeren_2014")], mode="conditions" - ) - - assert "BrentLab/kemmeren_2014" in results - result = results["BrentLab/kemmeren_2014"] - - # Should be included (kemmeren_2014 has temperature_celsius: 30 at repo level) - assert result["included"] is True - - def test_multi_repo_glucose_filter( - self, - harbison_2004_datacard, - hackett_2020_datacard, - kemmeren_2014_datacard, - write_config, - ): - """Test filtering D-glucose across multiple repos.""" - config = { - "filters": {"carbon_source": ["D-glucose"]}, - "BrentLab/harbison_2004": { - "dataset": { - "harbison_2004": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source", - } - } - } - }, - "BrentLab/hackett_2020": { - "dataset": { - "hackett_2020": {"carbon_source": {"path": "media.carbon_source"}} - } - }, - "BrentLab/kemmeren_2014": { - "dataset": { - "kemmeren_2014": {"carbon_source": {"path": "media.carbon_source"}} - } - }, - } - config_path = write_config(config) - resolver = DatasetFilterResolver(config_path) - results = resolver.resolve_filters( - repos=[ - ("BrentLab/harbison_2004", "harbison_2004"), - ("BrentLab/hackett_2020", "hackett_2020"), - ("BrentLab/kemmeren_2014", "kemmeren_2014"), - ], - mode="conditions", - ) - - # All three repos should be included - assert "BrentLab/harbison_2004" in results - assert results["BrentLab/harbison_2004"]["included"] is True - - assert "BrentLab/hackett_2020" in results - assert results["BrentLab/hackett_2020"]["included"] is True - - assert "BrentLab/kemmeren_2014" in results - assert results["BrentLab/kemmeren_2014"]["included"] is True diff --git a/tfbpapi/tests/datainfo/test_metadata_builder.py b/tfbpapi/tests/datainfo/test_metadata_builder.py new file mode 100644 index 0000000..1419264 --- /dev/null +++ b/tfbpapi/tests/datainfo/test_metadata_builder.py @@ -0,0 +1,332 @@ +""" +Tests for MetadataBuilder. + +Tests normalization logic, metadata extraction, and builder functionality. + +""" + +from pathlib import Path + +import pytest +import yaml + +from tfbpapi.datainfo.metadata_builder import ( + MetadataBuilder, + extract_compound_names, + get_nested_value, + normalize_value, +) + + +class TestGetNestedValue: + """Test get_nested_value function.""" + + def test_simple_path(self): + """Test simple one-level path.""" + data = {"temperature": 30} + assert get_nested_value(data, "temperature") == 30 + + def test_nested_path(self): + """Test multi-level nested path.""" + data = {"media": {"carbon_source": "D-glucose"}} + assert get_nested_value(data, "media.carbon_source") == "D-glucose" + + def test_deeply_nested_path(self): + """Test deeply nested path.""" + data = {"level1": {"level2": {"level3": {"value": 42}}}} + assert get_nested_value(data, "level1.level2.level3.value") == 42 + + def test_missing_key(self): + """Test that missing keys return None.""" + data = {"temperature": 30} + assert get_nested_value(data, "pressure") is None + + def test_missing_intermediate_key(self): + """Test that missing intermediate keys return None.""" + data = {"media": {"temperature": 30}} + assert get_nested_value(data, "media.carbon_source.compound") is None + + def test_non_dict_input(self): + """Test that non-dict input returns None.""" + assert get_nested_value("not a dict", "path") is None + assert get_nested_value(None, "path") is None + + +class TestExtractCompoundNames: + """Test extract_compound_names function.""" + + def test_list_of_dicts(self): + """Test extracting from list of dicts with compound field.""" + value = [ + {"compound": "D-glucose", "concentration_percent": 2}, + {"compound": "D-galactose", "concentration_percent": 1}, + ] + assert extract_compound_names(value) == ["D-glucose", "D-galactose"] + + def test_string_value(self): + """Test extracting from simple string.""" + assert extract_compound_names("D-glucose") == ["D-glucose"] + + def test_none_value(self): + """Test that None returns empty list.""" + assert extract_compound_names(None) == [] + + def test_unspecified_value(self): + """Test that 'unspecified' returns empty list.""" + assert extract_compound_names("unspecified") == [] + + def test_list_of_strings(self): + """Test extracting from list of strings.""" + assert extract_compound_names(["glucose", "galactose"]) == [ + "glucose", + "galactose", + ] + + def test_mixed_list(self): + """Test extracting from mixed list.""" + value = [ + {"compound": "D-glucose"}, + "maltose", + ] + assert extract_compound_names(value) == ["D-glucose", "maltose"] + + +class TestNormalizeValue: + """Test normalize_value function.""" + + def test_normalize_with_alias_match(self): + """Test normalization when value matches an alias.""" + aliases = {"glucose": ["D-glucose", "dextrose"]} + assert normalize_value("D-glucose", aliases) == "glucose" + assert normalize_value("dextrose", aliases) == "glucose" + + def test_normalize_case_insensitive(self): + """Test that matching is case-insensitive.""" + aliases = {"glucose": ["D-glucose"]} + assert normalize_value("d-glucose", aliases) == "glucose" + assert normalize_value("D-GLUCOSE", aliases) == "glucose" + assert normalize_value("D-Glucose", aliases) == "glucose" + + def test_normalize_no_match_passthrough(self): + """Test pass-through when no alias matches.""" + aliases = {"glucose": ["D-glucose"]} + assert normalize_value("maltose", aliases) == "maltose" + assert normalize_value("galactose", aliases) == "galactose" + + def test_normalize_no_aliases(self): + """Test pass-through when no aliases provided.""" + assert normalize_value("D-glucose", None) == "D-glucose" + assert normalize_value("maltose", None) == "maltose" + + def test_normalize_empty_aliases(self): + """Test pass-through when empty aliases dict.""" + assert normalize_value("D-glucose", {}) == "D-glucose" + + def test_normalize_numeric_values(self): + """Test normalization with numeric actual values.""" + aliases = {"thirty": [30, "30"]} + assert normalize_value(30, aliases) == "thirty" + assert normalize_value("30", aliases) == "thirty" + + def test_normalize_numeric_passthrough(self): + """Test that unmatched numeric values pass through as strings.""" + aliases = {"thirty": [30]} + assert normalize_value(37, aliases) == "37" + + def test_normalize_boolean_values(self): + """Test normalization with boolean values.""" + aliases = {"present": [True, "true", "True"]} + assert normalize_value(True, aliases) == "present" + # Note: bool to str conversion makes this "True" + assert normalize_value("true", aliases) == "present" + + def test_normalize_multiple_aliases(self): + """Test with multiple alias mappings.""" + aliases = { + "glucose": ["D-glucose", "dextrose"], + "galactose": ["D-galactose", "Galactose"], + } + assert normalize_value("D-glucose", aliases) == "glucose" + assert normalize_value("DEXTROSE", aliases) == "glucose" + assert normalize_value("d-galactose", aliases) == "galactose" + assert normalize_value("Galactose", aliases) == "galactose" + # No match + assert normalize_value("maltose", aliases) == "maltose" + + +@pytest.fixture +def write_config(tmp_path): + """Fixture to write YAML config files.""" + + def _write(config_data): + config_path = tmp_path / "config.yaml" + with open(config_path, "w") as f: + yaml.dump(config_data, f) + return config_path + + return _write + + +class TestMetadataBuilder: + """Test MetadataBuilder class.""" + + def test_init_with_aliases(self, write_config): + """Test initialization with factor aliases.""" + config = { + "factor_aliases": {"carbon_source": {"glucose": ["D-glucose", "dextrose"]}}, + "BrentLab/test": { + "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + }, + } + config_file = write_config(config) + builder = MetadataBuilder(config_file) + + assert len(builder.factor_aliases) == 1 + assert "carbon_source" in builder.factor_aliases + assert "glucose" in builder.factor_aliases["carbon_source"] + + def test_init_without_aliases(self, write_config): + """Test initialization without factor aliases.""" + config = { + "BrentLab/test": { + "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + } + } + config_file = write_config(config) + builder = MetadataBuilder(config_file) + + assert builder.factor_aliases == {} + + def test_invalid_mode(self, write_config): + """Test that invalid mode raises ValueError.""" + config = { + "BrentLab/test": { + "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + } + } + config_file = write_config(config) + builder = MetadataBuilder(config_file) + + with pytest.raises(ValueError) as exc_info: + builder.build_metadata( + repos=[("BrentLab/test", "test")], mode="invalid_mode" + ) + assert "Invalid mode" in str(exc_info.value) + + def test_build_metadata_missing_repo_config(self, write_config): + """Test handling of missing repository configuration.""" + config = { + "BrentLab/test": { + "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + } + } + config_file = write_config(config) + builder = MetadataBuilder(config_file) + + results = builder.build_metadata( + repos=[("BrentLab/missing", "dataset")], mode="conditions" + ) + + assert "BrentLab/missing" in results + assert "error" in results["BrentLab/missing"] + assert "No property mappings" in results["BrentLab/missing"]["error"] + + def test_repr(self, write_config): + """Test string representation.""" + config = { + "factor_aliases": { + "carbon_source": {"glucose": ["D-glucose"]}, + "temperature": {"thirty": [30]}, + }, + "BrentLab/test1": { + "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + }, + "BrentLab/test2": { + "dataset": {"test": {"temperature": {"path": "temperature_celsius"}}} + }, + } + config_file = write_config(config) + builder = MetadataBuilder(config_file) + + repr_str = repr(builder) + assert "MetadataBuilder" in repr_str + assert "2 properties" in repr_str + assert "2 repositories" in repr_str + + def test_repr_no_aliases(self, write_config): + """Test string representation with no aliases.""" + config = { + "BrentLab/test": { + "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + } + } + config_file = write_config(config) + builder = MetadataBuilder(config_file) + + repr_str = repr(builder) + assert "MetadataBuilder" in repr_str + assert "0 properties" in repr_str + assert "1 repositories" in repr_str + + +class TestMetadataBuilderIntegration: + """Integration tests with real datacards (if available).""" + + def test_build_metadata_conditions_mode(self, write_config): + """Test building metadata in conditions mode.""" + # This is a minimal test that doesn't require actual datacards + # In practice, you'd use real datacards from HuggingFace + config = { + "factor_aliases": {"carbon_source": {"glucose": ["D-glucose"]}}, + "BrentLab/test": { + "dataset": { + "test": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source", + } + } + } + }, + } + config_file = write_config(config) + builder = MetadataBuilder(config_file) + + # This would fail without a real datacard, but tests the structure + # In actual usage, you'd mock DataCard or use real repos + # For now, just verify the method exists and can be called + assert hasattr(builder, "build_metadata") + assert callable(builder.build_metadata) + + def test_get_property_mappings(self, write_config): + """Test _get_property_mappings method.""" + config = { + "BrentLab/test": { + "temperature": {"path": "temperature_celsius"}, + "dataset": { + "test_dataset": {"carbon_source": {"path": "media.carbon_source"}} + }, + } + } + config_file = write_config(config) + builder = MetadataBuilder(config_file) + + mappings = builder._get_property_mappings("BrentLab/test", "test_dataset") + + assert "temperature" in mappings + assert "carbon_source" in mappings + assert mappings["temperature"].path == "temperature_celsius" + assert mappings["carbon_source"].path == "media.carbon_source" + + def test_get_property_mappings_missing_repo(self, write_config): + """Test _get_property_mappings with missing repository.""" + config = { + "BrentLab/test": { + "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + } + } + config_file = write_config(config) + builder = MetadataBuilder(config_file) + + mappings = builder._get_property_mappings("BrentLab/missing", "dataset") + assert mappings == {} diff --git a/tfbpapi/tests/datainfo/test_metadata_config_models.py b/tfbpapi/tests/datainfo/test_metadata_config_models.py new file mode 100644 index 0000000..9a82598 --- /dev/null +++ b/tfbpapi/tests/datainfo/test_metadata_config_models.py @@ -0,0 +1,386 @@ +""" +Tests for metadata configuration Pydantic models. + +Tests validation, error messages, and config loading for MetadataBuilder. + +""" + +from pathlib import Path + +import pytest +import yaml +from pydantic import ValidationError + +from tfbpapi.datainfo.metadata_config_models import ( + MetadataConfig, + PropertyMapping, + RepositoryConfig, +) + + +class TestPropertyMapping: + """Tests for PropertyMapping model.""" + + def test_valid_field_level_mapping(self): + """Test valid field-level property mapping.""" + mapping = PropertyMapping(field="condition", path="media.carbon_source") + assert mapping.field == "condition" + assert mapping.path == "media.carbon_source" + + def test_valid_repo_level_mapping(self): + """Test valid repo-level property mapping (no field).""" + mapping = PropertyMapping(path="temperature_celsius") + assert mapping.field is None + assert mapping.path == "temperature_celsius" + + def test_invalid_empty_path(self): + """Test that empty path is rejected.""" + with pytest.raises(ValidationError) as exc_info: + PropertyMapping(path="") + # Pydantic catches this with min_length=1 before our custom validator + assert "at least 1 character" in str(exc_info.value) + + def test_invalid_whitespace_path(self): + """Test that whitespace-only path is rejected.""" + with pytest.raises(ValidationError) as exc_info: + PropertyMapping(path=" ") + assert "path cannot be empty" in str(exc_info.value) + + def test_invalid_empty_field(self): + """Test that empty field string is rejected.""" + with pytest.raises(ValidationError) as exc_info: + PropertyMapping(field="", path="media.carbon_source") + assert "field cannot be empty" in str(exc_info.value) + + def test_path_whitespace_stripped(self): + """Test that path whitespace is stripped.""" + mapping = PropertyMapping(path=" media.carbon_source ") + assert mapping.path == "media.carbon_source" + + +class TestRepositoryConfig: + """Tests for RepositoryConfig model.""" + + def test_valid_repo_config_with_datasets(self): + """Test valid repository config with dataset section.""" + config_data = { + "temperature_celsius": {"path": "temperature_celsius"}, + "dataset": { + "dataset1": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source", + } + } + }, + } + config = RepositoryConfig.model_validate(config_data) + assert config.dataset is not None + assert "dataset1" in config.dataset + + def test_valid_repo_config_no_datasets(self): + """Test valid repository config without dataset section.""" + config_data = {"temperature_celsius": {"path": "temperature_celsius"}} + config = RepositoryConfig.model_validate(config_data) + assert config.dataset is None + + def test_invalid_dataset_not_dict(self): + """Test that dataset section must be a dict.""" + config_data = {"dataset": "not a dict"} + with pytest.raises(ValidationError) as exc_info: + RepositoryConfig.model_validate(config_data) + assert "'dataset' key must contain a dict" in str(exc_info.value) + + def test_invalid_property_missing_path(self): + """Test that properties must have 'path' field.""" + config_data = { + "dataset": {"dataset1": {"carbon_source": {"field": "condition"}}} + } + with pytest.raises(ValidationError) as exc_info: + RepositoryConfig.model_validate(config_data) + # Pydantic's Field required error comes before our custom validation + assert "Field required" in str( + exc_info.value + ) or "missing required 'path' field" in str(exc_info.value) + + def test_invalid_repo_wide_property_missing_path(self): + """Test that repo-wide properties must have 'path' field.""" + config_data = {"temperature_celsius": {"field": "something"}} + with pytest.raises(ValidationError) as exc_info: + RepositoryConfig.model_validate(config_data) + # Pydantic's Field required error comes before our custom validation + assert "Field required" in str( + exc_info.value + ) or "missing required 'path' field" in str(exc_info.value) + + +class TestMetadataConfig: + """Tests for MetadataConfig model.""" + + def test_valid_config_with_aliases(self, tmp_path): + """Test valid config with factor aliases.""" + config_data = { + "factor_aliases": { + "carbon_source": { + "glucose": ["D-glucose", "dextrose"], + "galactose": ["D-galactose", "Galactose"], + } + }, + "BrentLab/test": { + "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + }, + } + + config_path = tmp_path / "config.yaml" + with open(config_path, "w") as f: + yaml.dump(config_data, f) + + config = MetadataConfig.from_yaml(config_path) + assert "carbon_source" in config.factor_aliases + assert "glucose" in config.factor_aliases["carbon_source"] + assert config.factor_aliases["carbon_source"]["glucose"] == [ + "D-glucose", + "dextrose", + ] + + def test_valid_config_without_aliases(self, tmp_path): + """Test that factor_aliases is optional.""" + config_data = { + "BrentLab/test": { + "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + } + } + + config_path = tmp_path / "config.yaml" + with open(config_path, "w") as f: + yaml.dump(config_data, f) + + config = MetadataConfig.from_yaml(config_path) + assert config.factor_aliases == {} + + def test_valid_config_empty_aliases(self, tmp_path): + """Test that empty factor_aliases dict is allowed.""" + config_data = { + "factor_aliases": {}, + "BrentLab/test": { + "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + }, + } + + config_path = tmp_path / "config.yaml" + with open(config_path, "w") as f: + yaml.dump(config_data, f) + + config = MetadataConfig.from_yaml(config_path) + assert config.factor_aliases == {} + + def test_invalid_alias_not_dict(self): + """Test that property aliases must be a dict.""" + config_data = { + "factor_aliases": { + "carbon_source": ["D-glucose"] # Should be dict, not list + }, + "BrentLab/test": {"dataset": {"test": {"prop": {"path": "path"}}}}, + } + + with pytest.raises(ValidationError) as exc_info: + MetadataConfig.model_validate(config_data) + # Pydantic catches this with type validation before our custom validator + assert "valid dictionary" in str(exc_info.value) or "must be a dict" in str( + exc_info.value + ) + + def test_invalid_alias_value_not_list(self): + """Test that alias values must be lists.""" + config_data = { + "factor_aliases": { + "carbon_source": {"glucose": "D-glucose"} # Should be list, not string + }, + "BrentLab/test": {"dataset": {"test": {"prop": {"path": "path"}}}}, + } + + with pytest.raises(ValidationError) as exc_info: + MetadataConfig.model_validate(config_data) + # Pydantic catches this with type validation before our custom validator + assert "valid list" in str(exc_info.value) or "must map to a list" in str( + exc_info.value + ) + + def test_invalid_alias_empty_list(self): + """Test that alias value lists cannot be empty.""" + config_data = { + "factor_aliases": {"carbon_source": {"glucose": []}}, + "BrentLab/test": {"dataset": {"test": {"prop": {"path": "path"}}}}, + } + + with pytest.raises(ValidationError) as exc_info: + MetadataConfig.model_validate(config_data) + assert "cannot have empty value list" in str(exc_info.value) + + def test_aliases_allow_numeric_values(self): + """Test that aliases can map to numeric values.""" + config_data = { + "factor_aliases": { + "temperature_celsius": { + "thirty": [30, "30"], # Integer and string + "thirty_seven": [37, 37.0], # Integer and float + } + }, + "BrentLab/test": { + "dataset": {"test": {"temperature": {"path": "temperature_celsius"}}} + }, + } + + config = MetadataConfig.model_validate(config_data) + assert config.factor_aliases["temperature_celsius"]["thirty"] == [30, "30"] + assert config.factor_aliases["temperature_celsius"]["thirty_seven"] == [ + 37, + 37.0, + ] + + def test_invalid_no_repositories(self): + """Test that at least one repository is required.""" + config_data = {"factor_aliases": {"carbon_source": {"glucose": ["D-glucose"]}}} + with pytest.raises(ValidationError) as exc_info: + MetadataConfig.model_validate(config_data) + assert "at least one repository configuration" in str(exc_info.value) + + def test_get_repository_config(self, tmp_path): + """Test get_repository_config method.""" + config_data = { + "factor_aliases": {"carbon_source": {"glucose": ["D-glucose"]}}, + "BrentLab/harbison_2004": { + "dataset": { + "harbison_2004": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source", + } + } + } + }, + } + + config_path = tmp_path / "config.yaml" + with open(config_path, "w") as f: + yaml.dump(config_data, f) + + config = MetadataConfig.from_yaml(config_path) + repo_config = config.get_repository_config("BrentLab/harbison_2004") + assert repo_config is not None + assert isinstance(repo_config, RepositoryConfig) + assert repo_config.dataset is not None + assert "harbison_2004" in repo_config.dataset + + # Non-existent repo + assert config.get_repository_config("BrentLab/nonexistent") is None + + def test_get_property_mappings(self, tmp_path): + """Test get_property_mappings method.""" + config_data = { + "factor_aliases": { + "carbon_source": {"glucose": ["D-glucose"]}, + "temperature": {"thirty": [30]}, + }, + "BrentLab/kemmeren_2014": { + "temperature": {"path": "temperature_celsius"}, # Repo-wide + "dataset": { + "kemmeren_2014": {"carbon_source": {"path": "media.carbon_source"}} + }, + }, + } + + config_path = tmp_path / "config.yaml" + with open(config_path, "w") as f: + yaml.dump(config_data, f) + + config = MetadataConfig.from_yaml(config_path) + mappings = config.get_property_mappings( + "BrentLab/kemmeren_2014", "kemmeren_2014" + ) + + # Should have both repo-wide and dataset-specific + assert "temperature" in mappings + assert "carbon_source" in mappings + # Mappings are PropertyMapping objects, not dicts + assert isinstance(mappings["temperature"], PropertyMapping) + assert mappings["temperature"].path == "temperature_celsius" + assert mappings["carbon_source"].path == "media.carbon_source" + + def test_dataset_specific_overrides_repo_wide(self, tmp_path): + """Test that dataset-specific mappings override repo-wide.""" + config_data = { + "BrentLab/test": { + "carbon_source": {"path": "repo.level.path"}, # Repo-wide + "dataset": { + "test_dataset": { + "carbon_source": {"path": "dataset.level.path"} # Override + } + }, + }, + } + + config_path = tmp_path / "config.yaml" + with open(config_path, "w") as f: + yaml.dump(config_data, f) + + config = MetadataConfig.from_yaml(config_path) + mappings = config.get_property_mappings("BrentLab/test", "test_dataset") + + # Dataset-specific should win + assert mappings["carbon_source"].path == "dataset.level.path" + + def test_file_not_found(self): + """Test that FileNotFoundError is raised for missing file.""" + with pytest.raises(FileNotFoundError): + MetadataConfig.from_yaml("/nonexistent/path/config.yaml") + + def test_invalid_yaml_structure(self, tmp_path): + """Test that non-dict YAML is rejected.""" + config_path = tmp_path / "config.yaml" + with open(config_path, "w") as f: + f.write("- not\\n- a\\n- dict\\n") + + with pytest.raises(ValueError) as exc_info: + MetadataConfig.from_yaml(config_path) + assert "Configuration must be a YAML dict" in str(exc_info.value) + + def test_nested_alias_property_names(self, tmp_path): + """Test that alias property names can use dot notation.""" + config_data = { + "factor_aliases": { + "carbon_source": {"glucose": ["D-glucose"]}, + "carbon_source.concentration_percent": {"two_percent": [2]}, + "carbon_source.specifications": {"no_aa": ["without_amino_acids"]}, + }, + "BrentLab/test": { + "dataset": { + "test": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source", + } + } + } + }, + } + + config_path = tmp_path / "config.yaml" + with open(config_path, "w") as f: + yaml.dump(config_data, f) + + config = MetadataConfig.from_yaml(config_path) + + # All alias properties should be preserved + assert "carbon_source" in config.factor_aliases + assert "carbon_source.concentration_percent" in config.factor_aliases + assert "carbon_source.specifications" in config.factor_aliases + + # Values should be correct + assert config.factor_aliases["carbon_source"]["glucose"] == ["D-glucose"] + assert config.factor_aliases["carbon_source.concentration_percent"][ + "two_percent" + ] == [2] + assert config.factor_aliases["carbon_source.specifications"]["no_aa"] == [ + "without_amino_acids" + ] From d73a82ac1f4234170590cfaa95a4532f311da170 Mon Sep 17 00:00:00 2001 From: chasem Date: Tue, 16 Dec 2025 14:34:28 -0600 Subject: [PATCH 21/49] tmp --- docs/tutorials/filter_resolver_tutorial.ipynb | 1427 ----------------- .../tutorials/metadata_builder_tutorial.ipynb | 938 +++++++++++ ...er.yaml => my_comprehensive_metadata.yaml} | 0 docs/tutorials/my_filter_config.yaml | 19 - docs/tutorials/my_metadata_config.yaml | 35 + tfbpapi/datainfo/datacard.py | 90 ++ tfbpapi/datainfo/metadata_builder.py | 192 ++- .../tests/datainfo/test_metadata_builder.py | 113 +- 8 files changed, 1271 insertions(+), 1543 deletions(-) delete mode 100644 docs/tutorials/filter_resolver_tutorial.ipynb create mode 100644 docs/tutorials/metadata_builder_tutorial.ipynb rename docs/tutorials/{my_comprehensive_filter.yaml => my_comprehensive_metadata.yaml} (100%) delete mode 100644 docs/tutorials/my_filter_config.yaml create mode 100644 docs/tutorials/my_metadata_config.yaml diff --git a/docs/tutorials/filter_resolver_tutorial.ipynb b/docs/tutorials/filter_resolver_tutorial.ipynb deleted file mode 100644 index 16d95f2..0000000 --- a/docs/tutorials/filter_resolver_tutorial.ipynb +++ /dev/null @@ -1,1427 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# DatasetFilterResolver Tutorial\n", - "\n", - "This tutorial demonstrates how to use the `DatasetFilterResolver` to filter samples across heterogeneous datasets using external YAML configuration.\n", - "\n", - "## Overview\n", - "\n", - "The `DatasetFilterResolver` solves a common problem when working with multiple genomics datasets: **experimental conditions are structured differently across datasets**. Some datasets define conditions at the repository level (all samples share the same conditions), while others define conditions per-sample in field definitions.\n", - "\n", - "Instead of writing dataset-specific code, you specify:\n", - "\n", - "1. **Filter criteria**: Which values you want (e.g., `carbon_source: [\"D-glucose\", \"D-galactose\"]`)\n", - "2. **Property mappings**: Where to find each property in each dataset (e.g., repo-level vs field-level)\n", - "\n", - "## Key Features\n", - "\n", - "- **Two-level filtering**:\n", - " - **Level 1** (Repo/Config): Excludes entire datasets that don't match\n", - " - **Level 2** (Field): Returns specific field values that match within datasets\n", - "- **Three output modes**:\n", - " - `conditions`: Just matching field values (no data retrieval)\n", - " - `samples`: Sample-level metadata (one row per sample_id)\n", - " - `full_data`: All measurements for matching samples\n", - "- **External configuration**: No hardcoded dataset logic in your analysis code\n", - "- **Automatic path resolution**: Handles `experimental_conditions` prepending automatically" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Setup" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chase/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", - " from .autonotebook import tqdm as notebook_tqdm\n" - ] - } - ], - "source": [ - "from pathlib import Path\n", - "import yaml\n", - "import pandas as pd\n", - "from tfbpapi.datainfo.filter_resolver import DatasetFilterResolver\n", - "from tfbpapi.datainfo import DataCard" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Understanding Dataset Structures\n", - "\n", - "Before filtering, let's examine how experimental conditions are structured in two representative datasets.\n", - "\n", - "### Repo-Level Conditions: Kemmeren 2014\n", - "\n", - "All samples in this dataset share the same experimental conditions defined at the repository level." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Kemmeren 2014 - Repo-Level Experimental Conditions\n", - "============================================================\n", - "Temperature: 30°C\n", - "Media: synthetic_complete\n", - "Carbon source: ['D-glucose']\n", - "\n", - "All samples in this dataset share these conditions.\n" - ] - } - ], - "source": [ - "# Load Kemmeren 2014 datacard\n", - "kemmeren_card = DataCard(\"BrentLab/kemmeren_2014\")\n", - "\n", - "# Get repo-level experimental conditions\n", - "exp_conds = kemmeren_card.get_experimental_conditions(\"kemmeren_2014\")\n", - "\n", - "print(\"Kemmeren 2014 - Repo-Level Experimental Conditions\")\n", - "print(\"=\" * 60)\n", - "print(f\"Temperature: {exp_conds.get('temperature_celsius')}°C\")\n", - "print(f\"Media: {exp_conds.get('media', {}).get('name')}\")\n", - "\n", - "carbon_source = exp_conds.get('media', {}).get('carbon_source', [])\n", - "if carbon_source:\n", - " compounds = [cs.get('compound') for cs in carbon_source]\n", - " print(f\"Carbon source: {compounds}\")\n", - "\n", - "print(\"\\nAll samples in this dataset share these conditions.\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Field-Level Conditions: Harbison 2004\n", - "\n", - "This dataset has different experimental conditions for different samples, defined in the `condition` field's definitions." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Repo-level experimental_conditions: {}\n", - "\n", - "Harbison 2004 - Field-Level Definitions\n", - "============================================================\n", - "Number of condition definitions: 14\n", - "Condition names: ['YPD', 'SM', 'RAPA', 'H2O2Hi', 'H2O2Lo', 'Acid', 'Alpha', 'BUT14', 'BUT90', 'Thi-', 'GAL', 'HEAT', 'Pi-', 'RAFF']\n", - "\n", - "Example conditions:\n", - " YPD: ['D-glucose'], 30°C\n", - " GAL: ['D-galactose'], 30°C\n", - " HEAT: ['D-glucose'], varies°C\n", - "\n", - "Each sample can have a different condition value.\n" - ] - } - ], - "source": [ - "# Load Harbison 2004 datacard\n", - "harbison_card = DataCard(\"BrentLab/harbison_2004\")\n", - "\n", - "# Check for repo-level conditions\n", - "exp_conds = harbison_card.get_experimental_conditions(\"harbison_2004\")\n", - "print(f\"Repo-level experimental_conditions: {exp_conds}\")\n", - "\n", - "# Get field-level definitions\n", - "condition_defs = harbison_card.get_field_definitions(\"harbison_2004\", \"condition\")\n", - "\n", - "print(\"\\nHarbison 2004 - Field-Level Definitions\")\n", - "print(\"=\" * 60)\n", - "print(f\"Number of condition definitions: {len(condition_defs)}\")\n", - "print(f\"Condition names: {list(condition_defs.keys())}\")\n", - "\n", - "# Show examples\n", - "print(\"\\nExample conditions:\")\n", - "for cond_name in [\"YPD\", \"GAL\", \"HEAT\"]:\n", - " if cond_name in condition_defs:\n", - " definition = condition_defs[cond_name]\n", - " media = definition.get('media', {})\n", - " carbon = media.get('carbon_source', [])\n", - " compounds = [cs.get('compound') for cs in carbon] if carbon else []\n", - " temp = definition.get('temperature_celsius', 'varies')\n", - " print(f\" {cond_name}: {compounds}, {temp}°C\")\n", - "\n", - "print(\"\\nEach sample can have a different condition value.\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Configuration Format\n", - "\n", - "The filter configuration has two main sections:\n", - "\n", - "```yaml\n", - "# 1. Filter criteria - what values you want\n", - "filters:\n", - " carbon_source: [\"D-glucose\", \"D-galactose\"]\n", - " temperature_celsius: [30]\n", - "\n", - "# 2. Property mappings - where to find each property in each dataset\n", - "BrentLab/repo_name:\n", - " property1:\n", - " path: media.name # Repo-wide property\n", - " \n", - " dataset:\n", - " dataset_name:\n", - " property2:\n", - " path: temperature_celsius # Dataset-specific property\n", - " \n", - " property3:\n", - " field: condition # Field-level property\n", - " path: media.carbon_source\n", - "```\n", - "\n", - "### Three Types of Property Configurations\n", - "\n", - "1. **Repo-wide**: Property applies to ALL datasets in the repository\n", - " - Paths automatically get `experimental_conditions.` prepended\n", - " - Example: `path: media.name` → resolves to `experimental_conditions.media.name`\n", - "\n", - "2. **Dataset-specific**: Property applies only to a specific dataset (at config level)\n", - " - Also gets `experimental_conditions.` prepended\n", - " - Example: `path: temperature_celsius` → resolves to `experimental_conditions.temperature_celsius`\n", - "\n", - "3. **Field-level**: Property varies per sample, defined in field definitions\n", - " - Requires `field` parameter to specify which field\n", - " - Path is used directly on field definitions (NO prepending)\n", - " - Example: `field: condition, path: media.carbon_source` → looks in condition field definitions" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Example 1: Basic Filtering by Carbon Source\n", - "\n", - "Let's filter for samples grown on D-glucose in Harbison 2004." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Configuration:\n", - "BrentLab/harbison_2004:\n", - " dataset:\n", - " harbison_2004:\n", - " carbon_source:\n", - " field: condition\n", - " path: media.carbon_source\n", - "filters:\n", - " carbon_source:\n", - " - D-glucose\n", - "\n" - ] - } - ], - "source": [ - "# Create filter configuration\n", - "glucose_config = {\n", - " \"filters\": {\n", - " \"carbon_source\": [\"D-glucose\"]\n", - " },\n", - " \"BrentLab/harbison_2004\": {\n", - " \"dataset\": {\n", - " \"harbison_2004\": {\n", - " \"carbon_source\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"media.carbon_source\"\n", - " }\n", - " }\n", - " }\n", - " }\n", - "}\n", - "\n", - "# Save to file\n", - "config_path = Path(\"/tmp/glucose_filter.yaml\")\n", - "with open(config_path, 'w') as f:\n", - " yaml.dump(glucose_config, f)\n", - "\n", - "print(\"Configuration:\")\n", - "print(yaml.dump(glucose_config, default_flow_style=False))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Mode 0: Conditions Only\n", - "\n", - "Get just the matching field values without retrieving any data. This is the fastest mode for exploration." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Included: True\n", - "\n", - "Matching conditions:\n", - " condition: ['YPD', 'RAPA', 'H2O2Hi', 'H2O2Lo', 'Acid', 'Alpha', 'BUT14', 'BUT90', 'HEAT']\n" - ] - } - ], - "source": [ - "# Create resolver and run filter\n", - "resolver = DatasetFilterResolver(config_path)\n", - "\n", - "results = resolver.resolve_filters(\n", - " repos=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", - " mode=\"conditions\"\n", - ")\n", - "\n", - "# Display results\n", - "result = results[\"BrentLab/harbison_2004\"]\n", - "print(f\"Included: {result['included']}\")\n", - "\n", - "if result['included']:\n", - " print(\"\\nMatching conditions:\")\n", - " for field, values in result['matching_field_values'].items():\n", - " print(f\" {field}: {values}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Understanding the results:**\n", - "\n", - "The resolver found all condition field values where `media.carbon_source` contains D-glucose:\n", - "- YPD: Rich media with glucose\n", - "- HEAT: Heat stress with glucose\n", - "- H2O2Hi/H2O2Lo: Oxidative stress with glucose\n", - "- Acid, Alpha, BUT14, BUT90, RAPA: Various stresses with glucose\n", - "\n", - "Notice GAL is NOT included - it uses galactose, not glucose." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Example 2: Multiple Filter Criteria\n", - "\n", - "Filter for samples with both D-glucose AND 30°C temperature." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Conditions with D-glucose AND 30°C:\n", - "['YPD', 'RAPA', 'H2O2Hi', 'H2O2Lo', 'Acid', 'Alpha', 'BUT14', 'BUT90']\n" - ] - } - ], - "source": [ - "multi_filter_config = {\n", - " \"filters\": {\n", - " \"carbon_source\": [\"D-glucose\"],\n", - " \"temperature_celsius\": [30]\n", - " },\n", - " \"BrentLab/harbison_2004\": {\n", - " \"dataset\": {\n", - " \"harbison_2004\": {\n", - " \"carbon_source\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"media.carbon_source\"\n", - " },\n", - " \"temperature_celsius\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"temperature_celsius\"\n", - " }\n", - " }\n", - " }\n", - " }\n", - "}\n", - "\n", - "config_path = Path(\"/tmp/multi_filter.yaml\")\n", - "with open(config_path, 'w') as f:\n", - " yaml.dump(multi_filter_config, f)\n", - "\n", - "resolver = DatasetFilterResolver(config_path)\n", - "results = resolver.resolve_filters(\n", - " repos=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", - " mode=\"conditions\"\n", - ")\n", - "\n", - "matching = results[\"BrentLab/harbison_2004\"][\"matching_field_values\"][\"condition\"]\n", - "print(\"Conditions with D-glucose AND 30°C:\")\n", - "print(matching)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Notice HEAT is now excluded - it has glucose but uses a temperature shift to 37°C, not 30°C.\n", - "\n", - "**Multiple filter criteria use AND logic** - all criteria must match." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Example 3: Repo-Level Filtering\n", - "\n", - "Filter Kemmeren 2014, which has repo-level experimental conditions (no field specification needed)." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Included: True\n", - "\n", - "No field-level filtering - entire dataset matches at repo level\n" - ] - } - ], - "source": [ - "repo_level_config = {\n", - " \"filters\": {\n", - " \"carbon_source\": [\"D-glucose\"],\n", - " \"temperature_celsius\": [30]\n", - " },\n", - " \"BrentLab/kemmeren_2014\": {\n", - " \"dataset\": {\n", - " \"kemmeren_2014\": {\n", - " \"carbon_source\": {\n", - " \"path\": \"media.carbon_source\" # No 'field' - repo-level property\n", - " },\n", - " \"temperature_celsius\": {\n", - " \"path\": \"temperature_celsius\"\n", - " }\n", - " }\n", - " }\n", - " }\n", - "}\n", - "\n", - "config_path = Path(\"/tmp/repo_level_filter.yaml\")\n", - "with open(config_path, 'w') as f:\n", - " yaml.dump(repo_level_config, f)\n", - "\n", - "resolver = DatasetFilterResolver(config_path)\n", - "results = resolver.resolve_filters(\n", - " repos=[(\"BrentLab/kemmeren_2014\", \"kemmeren_2014\")],\n", - " mode=\"conditions\"\n", - ")\n", - "\n", - "result = results[\"BrentLab/kemmeren_2014\"]\n", - "print(f\"Included: {result['included']}\")\n", - "\n", - "if result['included']:\n", - " if result['matching_field_values']:\n", - " print(\"\\nMatching field values:\")\n", - " for field, values in result['matching_field_values'].items():\n", - " print(f\" {field}: {values}\")\n", - " else:\n", - " print(\"\\nNo field-level filtering - entire dataset matches at repo level\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Key difference**: Repo-level filtering includes/excludes the entire dataset. Since kemmeren_2014 has D-glucose at 30°C at the repo level, ALL samples in the dataset match." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Example 4: Multiple Datasets\n", - "\n", - "Filter across both harbison_2004 (field-level) and kemmeren_2014 (repo-level) in a single query." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Results across multiple datasets:\n", - "\n", - "BrentLab/harbison_2004:\n", - " Included: True\n", - " condition: ['YPD', 'RAPA', 'H2O2Hi']...\n", - "\n", - "BrentLab/kemmeren_2014:\n", - " Included: True\n", - "\n" - ] - } - ], - "source": [ - "multi_dataset_config = {\n", - " \"filters\": {\n", - " \"carbon_source\": [\"D-glucose\"]\n", - " },\n", - " \"BrentLab/harbison_2004\": {\n", - " \"dataset\": {\n", - " \"harbison_2004\": {\n", - " \"carbon_source\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"media.carbon_source\"\n", - " }\n", - " }\n", - " }\n", - " },\n", - " \"BrentLab/kemmeren_2014\": {\n", - " \"dataset\": {\n", - " \"kemmeren_2014\": {\n", - " \"carbon_source\": {\n", - " \"path\": \"media.carbon_source\"\n", - " }\n", - " }\n", - " }\n", - " }\n", - "}\n", - "\n", - "config_path = Path(\"/tmp/multi_dataset_filter.yaml\")\n", - "with open(config_path, 'w') as f:\n", - " yaml.dump(multi_dataset_config, f)\n", - "\n", - "resolver = DatasetFilterResolver(config_path)\n", - "results = resolver.resolve_filters(\n", - " repos=[\n", - " (\"BrentLab/harbison_2004\", \"harbison_2004\"),\n", - " (\"BrentLab/kemmeren_2014\", \"kemmeren_2014\")\n", - " ],\n", - " mode=\"conditions\"\n", - ")\n", - "\n", - "print(\"Results across multiple datasets:\\n\")\n", - "for repo_id, result in results.items():\n", - " print(f\"{repo_id}:\")\n", - " print(f\" Included: {result['included']}\")\n", - " if result['included'] and result.get('matching_field_values'):\n", - " for field, values in result['matching_field_values'].items():\n", - " print(f\" {field}: {values[:3]}...\") # Show first 3\n", - " print()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Example 5: Retrieving Sample Metadata (Mode 1)\n", - "\n", - "Once you've identified matching conditions, retrieve sample-level metadata." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Retrieved 310 samples\n", - "Columns: ['sample_id', 'db_id', 'regulator_locus_tag', 'regulator_symbol', 'condition', 'target_locus_tag', 'target_symbol', 'effect', 'pvalue']\n", - "\n", - "First few rows:\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "sample_id", - "rawType": "int32", - "type": "integer" - }, - { - "name": "db_id", - "rawType": "float64", - "type": "float" - }, - { - "name": "regulator_locus_tag", - "rawType": "object", - "type": "string" - }, - { - "name": "regulator_symbol", - "rawType": "object", - "type": "string" - }, - { - "name": "condition", - "rawType": "object", - "type": "string" - }, - { - "name": "target_locus_tag", - "rawType": "object", - "type": "string" - }, - { - "name": "target_symbol", - "rawType": "object", - "type": "string" - }, - { - "name": "effect", - "rawType": "float64", - "type": "float" - }, - { - "name": "pvalue", - "rawType": "float64", - "type": "float" - } - ], - "ref": "b8896bc0-bb22-468e-9c3f-f8e1715f804c", - "rows": [ - [ - "0", - "1", - "0.0", - "YSC0017", - "MATA1", - "YPD", - "YBL068W", - "PRS4", - "0.8599428", - "0.72834544" - ], - [ - "1", - "2", - "1.0", - "YAL051W", - "OAF1", - "YPD", - "YAL015C", - "NTG1", - "1.3079075", - "0.17689452" - ], - [ - "2", - "3", - "2.0", - "YBL005W", - "PDR3", - "YPD", - "YAL005C", - "SSA1", - "0.76584559", - "0.92357641" - ], - [ - "3", - "4", - "3.0", - "YBL008W", - "HIR1", - "YPD", - "YAL030W", - "SNC1", - "0.92566636", - "0.69746121" - ], - [ - "4", - "5", - "4.0", - "YBL021C", - "HAP3", - "YPD", - "YAR035W", - "YAT1", - "0.83719358", - "0.85707131" - ] - ], - "shape": { - "columns": 9, - "rows": 5 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sample_iddb_idregulator_locus_tagregulator_symbolconditiontarget_locus_tagtarget_symboleffectpvalue
010.0YSC0017MATA1YPDYBL068WPRS40.8599430.728345
121.0YAL051WOAF1YPDYAL015CNTG11.3079080.176895
232.0YBL005WPDR3YPDYAL005CSSA10.7658460.923576
343.0YBL008WHIR1YPDYAL030WSNC10.9256660.697461
454.0YBL021CHAP3YPDYAR035WYAT10.8371940.857071
\n", - "
" - ], - "text/plain": [ - " sample_id db_id regulator_locus_tag regulator_symbol condition \\\n", - "0 1 0.0 YSC0017 MATA1 YPD \n", - "1 2 1.0 YAL051W OAF1 YPD \n", - "2 3 2.0 YBL005W PDR3 YPD \n", - "3 4 3.0 YBL008W HIR1 YPD \n", - "4 5 4.0 YBL021C HAP3 YPD \n", - "\n", - " target_locus_tag target_symbol effect pvalue \n", - "0 YBL068W PRS4 0.859943 0.728345 \n", - "1 YAL015C NTG1 1.307908 0.176895 \n", - "2 YAL005C SSA1 0.765846 0.923576 \n", - "3 YAL030W SNC1 0.925666 0.697461 \n", - "4 YAR035W YAT1 0.837194 0.857071 " - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Reuse the glucose filter config\n", - "glucose_config = {\n", - " \"filters\": {\n", - " \"carbon_source\": [\"D-glucose\"]\n", - " },\n", - " \"BrentLab/harbison_2004\": {\n", - " \"dataset\": {\n", - " \"harbison_2004\": {\n", - " \"carbon_source\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"media.carbon_source\"\n", - " }\n", - " }\n", - " }\n", - " }\n", - "}\n", - "\n", - "config_path = Path(\"/tmp/glucose_filter.yaml\")\n", - "with open(config_path, 'w') as f:\n", - " yaml.dump(glucose_config, f)\n", - "\n", - "resolver = DatasetFilterResolver(config_path)\n", - "\n", - "# Use samples mode\n", - "results = resolver.resolve_filters(\n", - " repos=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", - " mode=\"samples\"\n", - ")\n", - "\n", - "df_samples = results[\"BrentLab/harbison_2004\"][\"data\"]\n", - "\n", - "print(f\"Retrieved {len(df_samples)} samples\")\n", - "print(f\"Columns: {list(df_samples.columns)}\")\n", - "print(\"\\nFirst few rows:\")\n", - "df_samples.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Condition distribution in retrieved samples:\n", - "condition\n", - "YPD 204\n", - "H2O2Hi 39\n", - "H2O2Lo 28\n", - "RAPA 14\n", - "BUT14 8\n", - "HEAT 6\n", - "Alpha 5\n", - "BUT90 4\n", - "Acid 2\n", - "Name: count, dtype: int64\n" - ] - } - ], - "source": [ - "# Analyze condition distribution\n", - "print(\"Condition distribution in retrieved samples:\")\n", - "print(df_samples['condition'].value_counts())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Example 6: Retrieving Full Data (Mode 2)\n", - "\n", - "Retrieve all measurements for matching samples (many rows per sample)." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Retrieved 1,930,060 rows (measurements)\n", - "DataFrame shape: (1930060, 9)\n", - "\n", - "First few rows:\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "sample_id", - "rawType": "int32", - "type": "integer" - }, - { - "name": "db_id", - "rawType": "float64", - "type": "float" - }, - { - "name": "regulator_locus_tag", - "rawType": "object", - "type": "string" - }, - { - "name": "regulator_symbol", - "rawType": "object", - "type": "string" - }, - { - "name": "condition", - "rawType": "object", - "type": "string" - }, - { - "name": "target_locus_tag", - "rawType": "object", - "type": "string" - }, - { - "name": "target_symbol", - "rawType": "object", - "type": "string" - }, - { - "name": "effect", - "rawType": "float64", - "type": "float" - }, - { - "name": "pvalue", - "rawType": "float64", - "type": "float" - } - ], - "ref": "94bbe0c4-757e-406b-aef0-b82efd0905fe", - "rows": [ - [ - "0", - "1", - "0.0", - "YSC0017", - "MATA1", - "YPD", - "YAL001C", - "TFC3", - "1.697754", - "0.068704735" - ], - [ - "1", - "1", - "0.0", - "YSC0017", - "MATA1", - "YPD", - "YAL002W", - "VPS8", - null, - null - ], - [ - "2", - "1", - "0.0", - "YSC0017", - "MATA1", - "YPD", - "YAL003W", - "EFB1", - null, - null - ], - [ - "3", - "1", - "0.0", - "YSC0017", - "MATA1", - "YPD", - "YAL004W", - "YAL004W", - "0.74534215", - "0.83592938" - ], - [ - "4", - "1", - "0.0", - "YSC0017", - "MATA1", - "YPD", - "YAL005C", - "SSA1", - null, - null - ] - ], - "shape": { - "columns": 9, - "rows": 5 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sample_iddb_idregulator_locus_tagregulator_symbolconditiontarget_locus_tagtarget_symboleffectpvalue
010.0YSC0017MATA1YPDYAL001CTFC31.6977540.068705
110.0YSC0017MATA1YPDYAL002WVPS8NaNNaN
210.0YSC0017MATA1YPDYAL003WEFB1NaNNaN
310.0YSC0017MATA1YPDYAL004WYAL004W0.7453420.835929
410.0YSC0017MATA1YPDYAL005CSSA1NaNNaN
\n", - "
" - ], - "text/plain": [ - " sample_id db_id regulator_locus_tag regulator_symbol condition \\\n", - "0 1 0.0 YSC0017 MATA1 YPD \n", - "1 1 0.0 YSC0017 MATA1 YPD \n", - "2 1 0.0 YSC0017 MATA1 YPD \n", - "3 1 0.0 YSC0017 MATA1 YPD \n", - "4 1 0.0 YSC0017 MATA1 YPD \n", - "\n", - " target_locus_tag target_symbol effect pvalue \n", - "0 YAL001C TFC3 1.697754 0.068705 \n", - "1 YAL002W VPS8 NaN NaN \n", - "2 YAL003W EFB1 NaN NaN \n", - "3 YAL004W YAL004W 0.745342 0.835929 \n", - "4 YAL005C SSA1 NaN NaN " - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Use full_data mode\n", - "results_full = resolver.resolve_filters(\n", - " repos=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", - " mode=\"full_data\"\n", - ")\n", - "\n", - "df_full = results_full[\"BrentLab/harbison_2004\"][\"data\"]\n", - "\n", - "print(f\"Retrieved {len(df_full):,} rows (measurements)\")\n", - "print(f\"DataFrame shape: {df_full.shape}\")\n", - "print(\"\\nFirst few rows:\")\n", - "df_full.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Samples mode: 310 samples\n", - "Full data mode: 1,930,060 measurements\n", - "Average measurements per sample: 6,226\n" - ] - } - ], - "source": [ - "# Compare modes\n", - "n_samples = len(df_samples)\n", - "n_measurements = len(df_full)\n", - "measurements_per_sample = n_measurements / n_samples if n_samples > 0 else 0\n", - "\n", - "print(f\"Samples mode: {n_samples} samples\")\n", - "print(f\"Full data mode: {n_measurements:,} measurements\")\n", - "print(f\"Average measurements per sample: {measurements_per_sample:,.0f}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Example 7: Using Repo-Wide Properties\n", - "\n", - "For repositories where a property applies to ALL datasets, use repo-wide configuration." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Configuration with repo-wide property:\n", - "BrentLab/kemmeren_2014:\n", - " dataset:\n", - " kemmeren_2014:\n", - " carbon_source:\n", - " path: media.carbon_source\n", - " temperature_celsius:\n", - " path: temperature_celsius\n", - "filters:\n", - " temperature_celsius:\n", - " - 30\n", - "\n" - ] - } - ], - "source": [ - "repo_wide_config = {\n", - " \"filters\": {\n", - " \"temperature_celsius\": [30]\n", - " },\n", - " \"BrentLab/kemmeren_2014\": {\n", - " \"temperature_celsius\": { # Repo-wide property (no dataset key)\n", - " \"path\": \"temperature_celsius\"\n", - " },\n", - " \"dataset\": {\n", - " \"kemmeren_2014\": {\n", - " \"carbon_source\": { # Dataset-specific property\n", - " \"path\": \"media.carbon_source\"\n", - " }\n", - " }\n", - " }\n", - " }\n", - "}\n", - "\n", - "print(\"Configuration with repo-wide property:\")\n", - "print(yaml.dump(repo_wide_config, default_flow_style=False))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this example:\n", - "- `temperature_celsius` is defined at repo level - applies to ALL datasets in BrentLab/kemmeren_2014\n", - "- `carbon_source` is defined at dataset level - applies only to the kemmeren_2014 dataset\n", - "\n", - "This is useful when a repository has multiple datasets that share some common properties." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Creating a Reusable Configuration File\n", - "\n", - "For production use, save your configuration to a YAML file." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saved configuration to my_comprehensive_filter.yaml\n", - "\n", - "You can now use this config file in your analysis:\n", - " resolver = DatasetFilterResolver('my_comprehensive_filter.yaml')\n" - ] - } - ], - "source": [ - "# Create a comprehensive filter config\n", - "comprehensive_config = {\n", - " \"filters\": {\n", - " \"carbon_source\": [\"D-glucose\", \"D-galactose\"],\n", - " \"temperature_celsius\": [30]\n", - " },\n", - " \"BrentLab/harbison_2004\": {\n", - " \"dataset\": {\n", - " \"harbison_2004\": {\n", - " \"carbon_source\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"media.carbon_source\"\n", - " },\n", - " \"temperature_celsius\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"temperature_celsius\"\n", - " }\n", - " }\n", - " }\n", - " },\n", - " \"BrentLab/kemmeren_2014\": {\n", - " \"dataset\": {\n", - " \"kemmeren_2014\": {\n", - " \"carbon_source\": {\n", - " \"path\": \"media.carbon_source\"\n", - " },\n", - " \"temperature_celsius\": {\n", - " \"path\": \"temperature_celsius\"\n", - " }\n", - " }\n", - " }\n", - " }\n", - "}\n", - "\n", - "# Save to file\n", - "config_file = Path(\"my_comprehensive_filter.yaml\")\n", - "with open(config_file, 'w') as f:\n", - " yaml.dump(comprehensive_config, f)\n", - "\n", - "print(f\"Saved configuration to {config_file}\")\n", - "print(\"\\nYou can now use this config file in your analysis:\")\n", - "print(f\" resolver = DatasetFilterResolver('{config_file}')\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Summary\n", - "\n", - "### Key Concepts\n", - "\n", - "1. **Configuration Structure**:\n", - " - `filters`: What values you want\n", - " - Repository mappings: Where to find properties in each dataset\n", - "\n", - "2. **Three Property Types**:\n", - " - **Repo-wide**: Applies to all datasets (paths get `experimental_conditions.` prepended)\n", - " - **Dataset-specific**: Applies to one dataset (paths get `experimental_conditions.` prepended)\n", - " - **Field-level**: Per-sample variation (requires `field`, no prepending)\n", - "\n", - "3. **Three Output Modes**:\n", - " - `conditions`: Fast exploration, no data download\n", - " - `samples`: Sample metadata, one row per sample\n", - " - `full_data`: All measurements, many rows per sample\n", - "\n", - "### Best Practices\n", - "\n", - "1. **Start with `conditions` mode** to verify your filters work\n", - "2. **Use `field` parameter** when properties vary between samples\n", - "3. **Omit `field` parameter** when properties are at repo/config level\n", - "4. **Keep paths simple** - use minimal nesting that works\n", - "5. **Version control your configs** - they document your filter criteria\n", - "\n", - "### Common Patterns\n", - "\n", - "**Field-level filtering** (per-sample variation):\n", - "```yaml\n", - "BrentLab/harbison_2004:\n", - " dataset:\n", - " harbison_2004:\n", - " carbon_source:\n", - " field: condition\n", - " path: media.carbon_source\n", - "```\n", - "\n", - "**Repo-level filtering** (all samples share conditions):\n", - "```yaml\n", - "BrentLab/kemmeren_2014:\n", - " dataset:\n", - " kemmeren_2014:\n", - " carbon_source:\n", - " path: media.carbon_source\n", - "```\n", - "\n", - "**Repo-wide property** (applies to all datasets in repo):\n", - "```yaml\n", - "BrentLab/kemmeren_2014:\n", - " temperature_celsius:\n", - " path: temperature_celsius\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Next Steps\n", - "\n", - "- See [my_filter_config.yaml](my_filter_config.yaml) for a working example\n", - "- Explore the [experimental_conditions_tutorial.ipynb](experimental_conditions_tutorial.ipynb) to understand datacard structures\n", - "- Read the [DatasetFilterResolver API documentation](../../api/datainfo/filter_resolver.md) for detailed reference\n", - "- Try filtering across additional datasets in the BrentLab collection" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "tfbpapi-py3.11", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.9" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/tutorials/metadata_builder_tutorial.ipynb b/docs/tutorials/metadata_builder_tutorial.ipynb new file mode 100644 index 0000000..774f413 --- /dev/null +++ b/docs/tutorials/metadata_builder_tutorial.ipynb @@ -0,0 +1,938 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# MetadataBuilder Tutorial\n", + "\n", + "This tutorial demonstrates how to use the `MetadataBuilder` to create standardized metadata tables across heterogeneous datasets using optional alias mappings.\n", + "\n", + "## Overview\n", + "\n", + "The `MetadataBuilder` solves a common problem when working with multiple genomics datasets: **factor levels are named inconsistently across datasets**. For example, carbon sources might be labeled as \"D-glucose\", \"dextrose\", \"glucose\", etc.\n", + "\n", + "**Key difference from filtering**: This system does NOT filter or exclude any data. Instead, it normalizes factor level names to create standardized metadata views. ALL data is included, just with standardized names.\n", + "\n", + "You specify:\n", + "\n", + "1. **Factor aliases** (optional): Mappings to normalize factor level names (e.g., `glucose: [\"D-glucose\", \"dextrose\"]`)\n", + "2. **Property mappings**: Where to find each property in each dataset (e.g., repo-level vs field-level)\n", + "\n", + "## Key Features\n", + "\n", + "- **No filtering**: ALL data is included with normalized names\n", + "- **Optional aliases**: If no alias is configured, original values pass through unchanged\n", + "- **Case-insensitive matching**: \"D-glucose\" matches \"d-glucose\"\n", + "- **Three output modes**:\n", + " - `conditions`: Just normalized metadata (no data retrieval)\n", + " - `samples`: Sample-level metadata (one row per sample_id)\n", + " - `full_data`: All measurements with normalized metadata\n", + "- **External configuration**: No hardcoded dataset logic in your analysis code" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chase/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" + ] + } + ], + "source": [ + "from pathlib import Path\n", + "import yaml\n", + "import pandas as pd\n", + "from tfbpapi.datainfo.metadata_builder import MetadataBuilder, normalize_value\n", + "from tfbpapi.datainfo import DataCard" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Understanding Dataset Structures\n", + "\n", + "Before normalizing, let's examine how experimental conditions are structured in two representative datasets.\n", + "\n", + "### Repo-Level Conditions: Kemmeren 2014\n", + "\n", + "All samples in this dataset share the same experimental conditions defined at the repository level." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Kemmeren 2014 - Repo-Level Experimental Conditions\n", + "============================================================\n", + "Temperature: 30°C\n", + "Media: synthetic_complete\n", + "Carbon source: ['D-glucose']\n", + "\n", + "All samples in this dataset share these conditions.\n" + ] + } + ], + "source": [ + "# Load Kemmeren 2014 datacard\n", + "kemmeren_card = DataCard(\"BrentLab/kemmeren_2014\")\n", + "\n", + "# Get repo-level experimental conditions\n", + "exp_conds = kemmeren_card.get_experimental_conditions(\"kemmeren_2014\")\n", + "\n", + "print(\"Kemmeren 2014 - Repo-Level Experimental Conditions\")\n", + "print(\"=\" * 60)\n", + "print(f\"Temperature: {exp_conds.get('temperature_celsius')}°C\")\n", + "print(f\"Media: {exp_conds.get('media', {}).get('name')}\")\n", + "\n", + "carbon_source = exp_conds.get('media', {}).get('carbon_source', [])\n", + "if carbon_source:\n", + " compounds = [cs.get('compound') for cs in carbon_source]\n", + " print(f\"Carbon source: {compounds}\")\n", + "\n", + "print(\"\\nAll samples in this dataset share these conditions.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Field-Level Conditions: Harbison 2004\n", + "\n", + "This dataset has different experimental conditions for different samples, defined in the `condition` field's definitions." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Field: condition\n", + "Levels: 14\n", + "\n", + "YPD:\n", + " media.name: YPD\n", + " media.carbon_source: [{'compound': 'D-glucose', 'concentration_percent': 2}]\n", + "\n", + "SM:\n", + " media.name: synthetic_complete\n", + " media.carbon_source: None\n", + "\n", + "RAPA:\n", + " media.name: YPD\n", + " media.carbon_source: [{'compound': 'D-glucose', 'concentration_percent': 2}]\n", + "\n", + "H2O2Hi:\n", + " media.name: YPD\n", + " media.carbon_source: [{'compound': 'D-glucose', 'concentration_percent': 2}]\n", + "\n", + "H2O2Lo:\n", + " media.name: YPD\n", + " media.carbon_source: [{'compound': 'D-glucose', 'concentration_percent': 2}]\n", + "\n", + "Acid:\n", + " media.name: YPD\n", + " media.carbon_source: [{'compound': 'D-glucose', 'concentration_percent': 2}]\n", + "\n", + "... and 8 more levels\n" + ] + } + ], + "source": [ + "# Load Harbison 2004 datacard\n", + "harbison_card = DataCard(\"BrentLab/harbison_2004\")\n", + "\n", + "# Get a summary of condition field levels\n", + "print(harbison_card.summarize_field_levels(\n", + " \"harbison_2004\",\n", + " \"condition\",\n", + " properties=[\"media.name\", \"media.carbon_source\"],\n", + " max_levels=6\n", + "))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 8: Extracting Properties from Field Definitions\n", + "\n", + "The MetadataBuilder can extract properties from field definitions and add them as new columns. This is useful for creating analysis-ready metadata tables.\n", + "\n", + "For harbison_2004, the base metadata has `condition` as a field, but we want to extract:\n", + "- `growth_media` from `media.name`\n", + "- `carbon_source` from `media.carbon_source.compound`\n", + "\n", + "And normalize carbon source names using aliases." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Fetching 1 files: 100%|██████████| 1/1 [00:00<00:00, 2.88it/s]\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "sample_id", + "rawType": "int32", + "type": "integer" + }, + { + "name": "regulator_locus_tag", + "rawType": "object", + "type": "string" + }, + { + "name": "regulator_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "condition", + "rawType": "object", + "type": "string" + }, + { + "name": "carbon_source", + "rawType": "object", + "type": "unknown" + }, + { + "name": "growth_media", + "rawType": "object", + "type": "string" + } + ], + "ref": "95a0f05b-86e0-4ac9-bda0-b91b5f5e4de5", + "rows": [ + [ + "0", + "1", + "YSC0017", + "MATA1", + "YPD", + "glucose", + "YPD" + ], + [ + "1", + "2", + "YAL051W", + "OAF1", + "YPD", + "glucose", + "YPD" + ], + [ + "2", + "3", + "YBL005W", + "PDR3", + "YPD", + "glucose", + "YPD" + ], + [ + "3", + "4", + "YBL008W", + "HIR1", + "YPD", + "glucose", + "YPD" + ], + [ + "4", + "5", + "YBL021C", + "HAP3", + "YPD", + "glucose", + "YPD" + ], + [ + "5", + "6", + "YBL054W", + "TOD6", + "YPD", + "glucose", + "YPD" + ], + [ + "6", + "7", + "YBL103C", + "RTG3", + "H2O2Hi", + "glucose", + "YPD" + ], + [ + "7", + "8", + "YBL103C", + "RTG3", + "H2O2Lo", + "glucose", + "YPD" + ], + [ + "8", + "9", + "YBL103C", + "RTG3", + "RAPA", + "glucose", + "YPD" + ], + [ + "9", + "10", + "YBL103C", + "RTG3", + "SM", + null, + "synthetic_complete" + ], + [ + "10", + "11", + "YBL103C", + "RTG3", + "YPD", + "glucose", + "YPD" + ], + [ + "11", + "12", + "YBR033W", + "EDS1", + "YPD", + "glucose", + "YPD" + ], + [ + "12", + "13", + "YBR049C", + "REB1", + "H2O2Hi", + "glucose", + "YPD" + ], + [ + "13", + "14", + "YBR049C", + "REB1", + "H2O2Lo", + "glucose", + "YPD" + ], + [ + "14", + "15", + "YBR049C", + "REB1", + "YPD", + "glucose", + "YPD" + ], + [ + "15", + "16", + "YBR083W", + "TEC1", + "Alpha", + "glucose", + "YPD" + ], + [ + "16", + "17", + "YBR083W", + "TEC1", + "BUT14", + "glucose", + "YPD" + ], + [ + "17", + "18", + "YBR083W", + "TEC1", + "YPD", + "glucose", + "YPD" + ], + [ + "18", + "19", + "YBR150C", + "TBS1", + "YPD", + "glucose", + "YPD" + ], + [ + "19", + "20", + "YBR182C", + "SMP1", + "YPD", + "glucose", + "YPD" + ], + [ + "20", + "21", + "YBR239C", + "ERT1", + "YPD", + "glucose", + "YPD" + ], + [ + "21", + "22", + "YBR240C", + "THI2", + "Thi-", + null, + "synthetic_complete_minus_thiamine" + ], + [ + "22", + "23", + "YBR240C", + "THI2", + "YPD", + "glucose", + "YPD" + ], + [ + "23", + "24", + "YBR267W", + "REI1", + "YPD", + "glucose", + "YPD" + ], + [ + "24", + "25", + "YBR297W", + "MAL33", + "H2O2Hi", + "glucose", + "YPD" + ], + [ + "25", + "26", + "YBR297W", + "MAL33", + "H2O2Lo", + "glucose", + "YPD" + ], + [ + "26", + "27", + "YBR297W", + "MAL33", + "YPD", + "glucose", + "YPD" + ], + [ + "27", + "28", + "YCR018C", + "SRD1", + "YPD", + "glucose", + "YPD" + ], + [ + "28", + "29", + "YCR106W", + "RDS1", + "H2O2Hi", + "glucose", + "YPD" + ], + [ + "29", + "30", + "YCR106W", + "RDS1", + "YPD", + "glucose", + "YPD" + ], + [ + "30", + "31", + "YDL020C", + "RPN4", + "H2O2Hi", + "glucose", + "YPD" + ], + [ + "31", + "32", + "YDL020C", + "RPN4", + "H2O2Lo", + "glucose", + "YPD" + ], + [ + "32", + "33", + "YDL020C", + "RPN4", + "YPD", + "glucose", + "YPD" + ], + [ + "33", + "34", + "YDL048C", + "STP4", + "YPD", + "glucose", + "YPD" + ], + [ + "34", + "35", + "YDL056W", + "MBP1", + "H2O2Hi", + "glucose", + "YPD" + ], + [ + "35", + "36", + "YDL056W", + "MBP1", + "H2O2Lo", + "glucose", + "YPD" + ], + [ + "36", + "37", + "YDL056W", + "MBP1", + "YPD", + "glucose", + "YPD" + ], + [ + "37", + "38", + "YDL106C", + "PHO2", + "H2O2Hi", + "glucose", + "YPD" + ], + [ + "38", + "39", + "YDL106C", + "PHO2", + "H2O2Lo", + "glucose", + "YPD" + ], + [ + "39", + "40", + "YDL106C", + "PHO2", + "Pi-", + null, + "synthetic_complete_minus_phosphate" + ], + [ + "40", + "41", + "YDL106C", + "PHO2", + "SM", + null, + "synthetic_complete" + ], + [ + "41", + "42", + "YDL106C", + "PHO2", + "YPD", + "glucose", + "YPD" + ], + [ + "42", + "43", + "YDL166C", + "FAP7", + "YPD", + "glucose", + "YPD" + ], + [ + "43", + "44", + "YDL170W", + "UGA3", + "RAPA", + "glucose", + "YPD" + ], + [ + "44", + "45", + "YDL170W", + "UGA3", + "SM", + null, + "synthetic_complete" + ], + [ + "45", + "46", + "YDL170W", + "UGA3", + "YPD", + "glucose", + "YPD" + ], + [ + "46", + "47", + "YDR009W", + "GAL3", + "YPD", + "glucose", + "YPD" + ], + [ + "47", + "48", + "YDR026C", + "NSI1", + "YPD", + "glucose", + "YPD" + ], + [ + "48", + "49", + "YDR043C", + "NRG1", + "H2O2Hi", + "glucose", + "YPD" + ], + [ + "49", + "50", + "YDR043C", + "NRG1", + "H2O2Lo", + "glucose", + "YPD" + ] + ], + "shape": { + "columns": 6, + "rows": 352 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sample_idregulator_locus_tagregulator_symbolconditioncarbon_sourcegrowth_media
01YSC0017MATA1YPDglucoseYPD
12YAL051WOAF1YPDglucoseYPD
23YBL005WPDR3YPDglucoseYPD
34YBL008WHIR1YPDglucoseYPD
45YBL021CHAP3YPDglucoseYPD
.....................
347348YPR104CFHL1YPDglucoseYPD
348349YPR196WYPR196WYPDglucoseYPD
349350YPR199CARR1H2O2HiglucoseYPD
350351YPR199CARR1YPDglucoseYPD
351352YKL043WPHD1BUT14glucoseYPD
\n", + "

352 rows × 6 columns

\n", + "
" + ], + "text/plain": [ + " sample_id regulator_locus_tag regulator_symbol condition carbon_source \\\n", + "0 1 YSC0017 MATA1 YPD glucose \n", + "1 2 YAL051W OAF1 YPD glucose \n", + "2 3 YBL005W PDR3 YPD glucose \n", + "3 4 YBL008W HIR1 YPD glucose \n", + "4 5 YBL021C HAP3 YPD glucose \n", + ".. ... ... ... ... ... \n", + "347 348 YPR104C FHL1 YPD glucose \n", + "348 349 YPR196W YPR196W YPD glucose \n", + "349 350 YPR199C ARR1 H2O2Hi glucose \n", + "350 351 YPR199C ARR1 YPD glucose \n", + "351 352 YKL043W PHD1 BUT14 glucose \n", + "\n", + " growth_media \n", + "0 YPD \n", + "1 YPD \n", + "2 YPD \n", + "3 YPD \n", + "4 YPD \n", + ".. ... \n", + "347 YPD \n", + "348 YPD \n", + "349 YPD \n", + "350 YPD \n", + "351 YPD \n", + "\n", + "[352 rows x 6 columns]" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Configuration with property extraction and aliases\n", + "extraction_config = {\n", + " \"factor_aliases\": {\n", + " \"carbon_source\": {\n", + " \"glucose\": [\"D-glucose\", \"Glucose\", \"dextrose\", \"Dextrose\"],\n", + " \"galactose\": [\"D-galactose\", \"Galactose\"],\n", + " \"raffinose\": [\"D-raffinose\", \"Raffinose\"]\n", + " }\n", + " },\n", + " \"BrentLab/harbison_2004\": {\n", + " \"dataset\": {\n", + " \"harbison_2004\": {\n", + " \"growth_media\": {\n", + " \"field\": \"condition\",\n", + " \"path\": \"media.name\"\n", + " },\n", + " \"carbon_source\": {\n", + " \"field\": \"condition\",\n", + " \"path\": \"media.carbon_source.compound\"\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "config_path_extraction = Path(\"/tmp/metadata_extraction.yaml\")\n", + "with open(config_path_extraction, 'w') as f:\n", + " yaml.dump(extraction_config, f)\n", + "\n", + "# Build metadata with extraction\n", + "builder_extraction = MetadataBuilder(config_path_extraction)\n", + "results_extraction = builder_extraction.build_metadata(\n", + " repos=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", + " mode=\"samples\"\n", + ")\n", + "\n", + "df_extracted = results_extraction[\"BrentLab/harbison_2004\"][\"data\"]\n", + "\n", + "df_extracted\n", + "\n", + "# print(f\"Columns: {list(df_extracted.columns)}\")\n", + "# print(f\"\\nShape: {df_extracted.shape}\")\n", + "# print(\"\\nSample data showing base + extracted columns:\")\n", + "# print(df_extracted[['sample_id', 'condition', 'growth_media', 'carbon_source']].head(10))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "tfbpapi-py3.11", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/tutorials/my_comprehensive_filter.yaml b/docs/tutorials/my_comprehensive_metadata.yaml similarity index 100% rename from docs/tutorials/my_comprehensive_filter.yaml rename to docs/tutorials/my_comprehensive_metadata.yaml diff --git a/docs/tutorials/my_filter_config.yaml b/docs/tutorials/my_filter_config.yaml deleted file mode 100644 index 8c10ac6..0000000 --- a/docs/tutorials/my_filter_config.yaml +++ /dev/null @@ -1,19 +0,0 @@ -filters: - carbon_source: - - "D-glucose" - - "D-galactose" - -BrentLab/harbison_2004: - dataset: - harbison_2004: - carbon_source: - field: condition - path: media.carbon_source - -BrentLab/kemmeren_2014: - temperature: - path: temperature_celsius - dataset: - kemmeren_2014: - carbon_source: - path: media.carbon_source diff --git a/docs/tutorials/my_metadata_config.yaml b/docs/tutorials/my_metadata_config.yaml new file mode 100644 index 0000000..2781954 --- /dev/null +++ b/docs/tutorials/my_metadata_config.yaml @@ -0,0 +1,35 @@ +BrentLab/harbison_2004: + dataset: + harbison_2004: + carbon_source: + field: condition + path: media.carbon_source + temperature_celsius: + field: condition + path: temperature_celsius +BrentLab/kemmeren_2014: + dataset: + kemmeren_2014: + carbon_source: + path: media.carbon_source + temperature_celsius: + path: temperature_celsius +factor_aliases: + carbon_source: + galactose: + - D-galactose + - Galactose + - galactose + glucose: + - D-glucose + - dextrose + - glucose + raffinose: + - D-raffinose + - raffinose + temperature_celsius: + heat_shock: + - 37 + - 39 + standard: + - 30 diff --git a/tfbpapi/datainfo/datacard.py b/tfbpapi/datainfo/datacard.py index d11e378..370a677 100644 --- a/tfbpapi/datainfo/datacard.py +++ b/tfbpapi/datainfo/datacard.py @@ -796,6 +796,96 @@ def list_experimental_condition_fields(self, config_name: str) -> list[str]: if f.role == "experimental_condition" ] + def summarize_field_levels( + self, + config_name: str, + field_name: str, + properties: list[str] | None = None, + max_levels: int | None = None, + ) -> str: + """ + Get a human-readable summary of field levels and their key properties. + + Provides a formatted summary showing each level (value) and selected properties + from its definition. Useful for quickly exploring experimental condition + structures without writing loops. + + :param config_name: Configuration name + :param field_name: Field name to summarize + :param properties: Optional list of property paths to display (e.g., ["media.name", "temperature_celsius"]). + If None, shows all top-level keys. + :param max_levels: Optional limit on number of levels to show + :return: Formatted string summary + :raises DataCardError: If config or field not found + + Example: + >>> card = DataCard("BrentLab/harbison_2004") + >>> # Show summary with specific properties + >>> print(card.summarize_field_levels( + ... "harbison_2004", + ... "condition", + ... properties=["media.carbon_source.compound", "temperature_celsius"] + ... )) + Field: condition + Levels: 14 + + YPD: + media.carbon_source.compound: ['D-glucose'] + temperature_celsius: 30 + + GAL: + media.carbon_source.compound: ['D-galactose'] + temperature_celsius: 30 + ... + + """ + from tfbpapi.datainfo.metadata_builder import get_nested_value + + # Get definitions + definitions = self.get_field_definitions(config_name, field_name) + + if not definitions: + return f"Field '{field_name}' has no definitions" + + lines = [ + f"Field: {field_name}", + f"Levels: {len(definitions)}", + "", + ] + + # Determine how many levels to show + levels_to_show = list(definitions.keys()) + if max_levels: + levels_to_show = levels_to_show[:max_levels] + + for level_name in levels_to_show: + definition = definitions[level_name] + lines.append(f"{level_name}:") + + if properties: + # Show only specified properties + for prop_path in properties: + value = get_nested_value(definition, prop_path) + lines.append(f" {prop_path}: {value}") + else: + # Show all top-level keys + if isinstance(definition, dict): + for key in definition.keys(): + value = definition[key] + # Truncate long values + value_str = str(value) + if len(value_str) > 80: + value_str = value_str[:77] + "..." + lines.append(f" {key}: {value_str}") + + lines.append("") + + if max_levels and len(definitions) > max_levels: + remaining = len(definitions) - max_levels + lines.append(f"... and {remaining} more levels") + + return "\n".join(lines) + def summary(self) -> str: """Get a human-readable summary of the dataset.""" card = self.dataset_card diff --git a/tfbpapi/datainfo/metadata_builder.py b/tfbpapi/datainfo/metadata_builder.py index 66208eb..112c46a 100644 --- a/tfbpapi/datainfo/metadata_builder.py +++ b/tfbpapi/datainfo/metadata_builder.py @@ -51,14 +51,34 @@ def get_nested_value(data: dict, path: str) -> Any: """ - Navigate nested dict using dot notation. + Navigate nested dict/list using dot notation. Handles missing intermediate keys gracefully by returning None. + Supports extracting properties from lists of dicts. :param data: Dictionary to navigate - :param path: Dot-separated path (e.g., "media.carbon_source") + :param path: Dot-separated path (e.g., "media.carbon_source.compound") :return: Value at path or None if not found + Examples: + # Simple nested dict + get_nested_value({"media": {"name": "YPD"}}, "media.name") + -> "YPD" + + # List of dicts - extract property from each item + get_nested_value( + {"media": {"carbon_source": [{"compound": "glucose"}, {"compound": "galactose"}]}}, + "media.carbon_source.compound" + ) + -> ["glucose", "galactose"] + + # List of dicts - get the list itself + get_nested_value( + {"media": {"carbon_source": [{"compound": "glucose"}]}}, + "media.carbon_source" + ) + -> [{"compound": "glucose"}] + """ if not isinstance(data, dict): return None @@ -66,43 +86,29 @@ def get_nested_value(data: dict, path: str) -> Any: keys = path.split(".") current = data - for key in keys: - if not isinstance(current, dict) or key not in current: + for i, key in enumerate(keys): + if isinstance(current, dict): + if key not in current: + return None + current = current[key] + elif isinstance(current, list): + # If current is a list and we have more keys, extract property from each item + if i < len(keys): + # Extract the remaining path from each list item + remaining_path = ".".join(keys[i:]) + results = [] + for item in current: + if isinstance(item, dict): + val = get_nested_value(item, remaining_path) + if val is not None: + results.append(val) + return results if results else None + else: return None - current = current[key] return current -def extract_compound_names(value: Any) -> list[str]: - """ - Extract compound names from various representations. - - Handles: - - List of dicts: [{"compound": "D-glucose", ...}] -> ["D-glucose"] - - String: "D-glucose" -> ["D-glucose"] - - None or "unspecified" -> [] - - :param value: Value to extract from - :return: List of compound names - - """ - if value is None or value == "unspecified": - return [] - - if isinstance(value, str): - return [value] - - if isinstance(value, list): - compounds = [] - for item in value: - if isinstance(item, dict) and "compound" in item: - compounds.append(item["compound"]) - elif isinstance(item, str): - compounds.append(item) - return compounds - - return [] def normalize_value(actual_value: Any, aliases: dict[str, list[Any]] | None) -> str: @@ -381,11 +387,8 @@ def _extract_repo_level( if value is None: continue - # Extract compound names if needed - if isinstance(value, list) and value and isinstance(value[0], dict): - actual_values = extract_compound_names(value) - else: - actual_values = [value] if not isinstance(value, list) else value + # Ensure value is a list for consistent processing + actual_values = [value] if not isinstance(value, list) else value # Normalize using aliases (if configured) aliases = self.factor_aliases.get(prop_name) @@ -442,13 +445,8 @@ def _extract_field_level( if value is None: continue - # Extract compound names if needed - if isinstance(value, list) and value and isinstance(value[0], dict): - actual_values = extract_compound_names(value) - else: - actual_values = ( - [value] if not isinstance(value, list) else value - ) + # Ensure value is a list for consistent processing + actual_values = [value] if not isinstance(value, list) else value # Normalize using aliases (if configured) aliases = self.factor_aliases.get(prop_name) @@ -471,40 +469,107 @@ def _get_sample_metadata( Get sample-level metadata (Mode 1). Returns one row per sample_id with metadata columns. Includes ALL samples (no - filtering). + filtering). Selects base metadata_fields columns plus adds extracted columns + from field-level property mappings. :param repo_id: Repository ID :param config_name: Configuration name - :param metadata: Extracted metadata dict + :param metadata: Extracted metadata dict with field_values :param token: Optional HuggingFace token - :return: DataFrame with sample metadata + :return: DataFrame with sample metadata including extracted columns """ - # Initialize query API - api = HfQueryAPI(repo_id, token=token) - - # Query for all samples (no WHERE clause filtering) - sql = f""" - SELECT DISTINCT * - FROM {config_name} - """ - try: + # Load DataCard to get metadata_fields + card = DataCard(repo_id, token=token) + config = card.get_config(config_name) + + # Initialize query API + api = HfQueryAPI(repo_id, token=token) + + # Determine which columns to select + if config and hasattr(config, 'metadata_fields') and config.metadata_fields: + # Select only metadata fields + columns = ", ".join(config.metadata_fields) + # Add sample_id if not already in metadata_fields + if "sample_id" not in config.metadata_fields: + columns = f"sample_id, {columns}" + sql = f""" + SELECT DISTINCT {columns} + FROM {config_name} + """ + else: + # No metadata_fields specified, select all + sql = f""" + SELECT DISTINCT * + FROM {config_name} + """ + df = api.query(sql, config_name) # For sample-level, we want one row per sample_id if "sample_id" in df.columns: df_samples = df.groupby("sample_id").first().reset_index() - return df_samples else: # No sample_id column, return distinct rows - return df + df_samples = df + + # Add extracted columns from field-level metadata + if "field_values" in metadata: + df_samples = self._add_extracted_columns(df_samples, metadata["field_values"]) + + return df_samples except Exception as e: return pd.DataFrame( {"error": [str(e)], "note": ["Failed to retrieve sample metadata"]} ) + def _add_extracted_columns( + self, + df: pd.DataFrame, + field_values: dict[str, dict[str, Any]] + ) -> pd.DataFrame: + """ + Add columns extracted from field-level property mappings. + + For each field that has property mappings, creates new columns by looking up + the field value in field_values and extracting the mapped properties. + + :param df: DataFrame with base metadata + :param field_values: Dict mapping field values to their extracted properties + :return: DataFrame with additional extracted columns + + """ + # Group properties by source field + # field_values is like: {"YPD": {"growth_media": ["YPD"], "carbon_source": ["glucose"]}} + + # We need to determine which field each row's value comes from + # For harbison_2004, the field is "condition" + + # Find fields that have definitions (these are the ones we can map) + for field_value, properties in field_values.items(): + # Each property becomes a new column + for prop_name, prop_values in properties.items(): + # Initialize column if it doesn't exist + if prop_name not in df.columns: + df[prop_name] = None + + # For each row where the field matches this field_value, set the property + # We need to find which column contains field_value + for col in df.columns: + if col in [prop_name, "sample_id"]: + continue + # Check if this column contains field_value + mask = df[col] == field_value + if mask.any(): + # Set the property value for matching rows + # Take first value from list (normalized values are lists) + value = prop_values[0] if prop_values else None + df.loc[mask, prop_name] = value + + return df + def _get_full_data( self, repo_id: str, @@ -516,13 +581,16 @@ def _get_full_data( Get full data with all measurements (Mode 2). Returns many rows per sample_id (one per measured feature/target). Includes ALL - data (no filtering). + data (no filtering) and ALL columns (both metadata and measurements). + + Unlike samples mode which respects metadata_fields, this mode returns the complete + dataset including quantitative measurements. :param repo_id: Repository ID :param config_name: Configuration name :param metadata: Extracted metadata dict :param token: Optional HuggingFace token - :return: DataFrame with full data + :return: DataFrame with full data (all columns) """ # Initialize query API diff --git a/tfbpapi/tests/datainfo/test_metadata_builder.py b/tfbpapi/tests/datainfo/test_metadata_builder.py index 1419264..cee3b71 100644 --- a/tfbpapi/tests/datainfo/test_metadata_builder.py +++ b/tfbpapi/tests/datainfo/test_metadata_builder.py @@ -12,7 +12,6 @@ from tfbpapi.datainfo.metadata_builder import ( MetadataBuilder, - extract_compound_names, get_nested_value, normalize_value, ) @@ -52,43 +51,87 @@ def test_non_dict_input(self): assert get_nested_value(None, "path") is None -class TestExtractCompoundNames: - """Test extract_compound_names function.""" +class TestGetNestedValueListExtraction: + """Test get_nested_value list extraction functionality.""" - def test_list_of_dicts(self): - """Test extracting from list of dicts with compound field.""" - value = [ - {"compound": "D-glucose", "concentration_percent": 2}, - {"compound": "D-galactose", "concentration_percent": 1}, - ] - assert extract_compound_names(value) == ["D-glucose", "D-galactose"] - - def test_string_value(self): - """Test extracting from simple string.""" - assert extract_compound_names("D-glucose") == ["D-glucose"] - - def test_none_value(self): - """Test that None returns empty list.""" - assert extract_compound_names(None) == [] - - def test_unspecified_value(self): - """Test that 'unspecified' returns empty list.""" - assert extract_compound_names("unspecified") == [] - - def test_list_of_strings(self): - """Test extracting from list of strings.""" - assert extract_compound_names(["glucose", "galactose"]) == [ - "glucose", - "galactose", - ] - - def test_mixed_list(self): - """Test extracting from mixed list.""" - value = [ + def test_extract_from_list_of_dicts(self): + """Test extracting property from list of dicts.""" + data = { + "media": { + "carbon_source": [ + {"compound": "D-glucose", "concentration_percent": 2}, + {"compound": "D-galactose", "concentration_percent": 1}, + ] + } + } + result = get_nested_value(data, "media.carbon_source.compound") + assert result == ["D-glucose", "D-galactose"] + + def test_extract_concentration_from_list(self): + """Test extracting numeric property from list of dicts.""" + data = { + "media": { + "carbon_source": [ + {"compound": "D-glucose", "concentration_percent": 2}, + {"compound": "D-galactose", "concentration_percent": 1}, + ] + } + } + result = get_nested_value(data, "media.carbon_source.concentration_percent") + assert result == [2, 1] + + def test_get_list_itself(self): + """Test getting the list without extracting a property.""" + data = { + "media": { + "carbon_source": [ + {"compound": "D-glucose"}, + {"compound": "D-galactose"}, + ] + } + } + result = get_nested_value(data, "media.carbon_source") + expected = [ {"compound": "D-glucose"}, - "maltose", + {"compound": "D-galactose"}, ] - assert extract_compound_names(value) == ["D-glucose", "maltose"] + assert result == expected + + def test_extract_from_single_item_list(self): + """Test extracting from list with single item.""" + data = { + "media": { + "carbon_source": [{"compound": "D-glucose"}] + } + } + result = get_nested_value(data, "media.carbon_source.compound") + assert result == ["D-glucose"] + + def test_extract_missing_property_from_list(self): + """Test extracting non-existent property from list items.""" + data = { + "media": { + "carbon_source": [ + {"compound": "D-glucose"}, + {"compound": "D-galactose"}, + ] + } + } + result = get_nested_value(data, "media.carbon_source.missing_key") + assert result is None + + def test_nested_list_extraction(self): + """Test extracting from nested structures with lists.""" + data = { + "level1": { + "level2": [ + {"level3": {"value": "a"}}, + {"level3": {"value": "b"}}, + ] + } + } + result = get_nested_value(data, "level1.level2.level3.value") + assert result == ["a", "b"] class TestNormalizeValue: From a51fa914df75217f0e6f5247e92beca820f7394a Mon Sep 17 00:00:00 2001 From: chasem Date: Tue, 16 Dec 2025 15:17:20 -0600 Subject: [PATCH 22/49] tmp --- docs/tutorials/my_comprehensive_metadata.yaml | 22 ------------------- docs/tutorials/my_metadata_config.yaml | 22 +++++++++++-------- 2 files changed, 13 insertions(+), 31 deletions(-) delete mode 100644 docs/tutorials/my_comprehensive_metadata.yaml diff --git a/docs/tutorials/my_comprehensive_metadata.yaml b/docs/tutorials/my_comprehensive_metadata.yaml deleted file mode 100644 index 6bd4668..0000000 --- a/docs/tutorials/my_comprehensive_metadata.yaml +++ /dev/null @@ -1,22 +0,0 @@ -BrentLab/harbison_2004: - dataset: - harbison_2004: - carbon_source: - field: condition - path: media.carbon_source - temperature_celsius: - field: condition - path: temperature_celsius -BrentLab/kemmeren_2014: - dataset: - kemmeren_2014: - carbon_source: - path: media.carbon_source - temperature_celsius: - path: temperature_celsius -filters: - carbon_source: - - D-glucose - - D-galactose - temperature_celsius: - - 30 diff --git a/docs/tutorials/my_metadata_config.yaml b/docs/tutorials/my_metadata_config.yaml index 2781954..6338159 100644 --- a/docs/tutorials/my_metadata_config.yaml +++ b/docs/tutorials/my_metadata_config.yaml @@ -1,25 +1,31 @@ BrentLab/harbison_2004: + nitrogen_source: + path: media.nitrogen_source.name dataset: harbison_2004: + phosphate_source: + path: media.phosphate_source.compound carbon_source: field: condition - path: media.carbon_source + path: media.carbon_source.compound temperature_celsius: field: condition path: temperature_celsius + BrentLab/kemmeren_2014: dataset: kemmeren_2014: carbon_source: - path: media.carbon_source + path: media.carbon_source.compound temperature_celsius: path: temperature_celsius + factor_aliases: carbon_source: galactose: - D-galactose + # should not have to include the actual name of the column -- that can be inferred - Galactose - - galactose glucose: - D-glucose - dextrose @@ -27,9 +33,7 @@ factor_aliases: raffinose: - D-raffinose - raffinose - temperature_celsius: - heat_shock: - - 37 - - 39 - standard: - - 30 + +description: + carbon_source: + media: \ No newline at end of file From 9608f578f33636ce2c8cce397991dd9db4e535dc Mon Sep 17 00:00:00 2001 From: chasem Date: Wed, 17 Dec 2025 19:56:55 -0600 Subject: [PATCH 23/49] tmp --- docs/HfCacheManager.md | 1 - docs/HfQueryAPI.md | 1 - docs/HfRankResponse.md | 1 - docs/IncrementalAnalysisDB.md | 1 - docs/datacard.md | 6 + docs/datainfo.md | 133 - docs/errors.md | 29 +- docs/fetchers.md | 16 + docs/hf_cache_manager.md | 6 + docs/index.md | 14 +- docs/models.md | 41 + docs/tutorials/cache_manager_tutorial.ipynb | 266 +- docs/tutorials/correlation_tutorial.ipynb | 3420 ----------------- docs/tutorials/datacard_tutorial.ipynb | 74 +- docs/tutorials/hfqueryapi_tutorial.ipynb | 2214 ----------- .../tutorials/metadata_builder_tutorial.ipynb | 938 ----- .../tutorials/metadata_manager_tutorial.ipynb | 586 --- docs/tutorials/my_metadata_config.yaml | 39 - docs/tutorials/passing_callingcards.csv | 82 - docs/tutorials/rank_response_tutorial.ipynb | 968 ----- docs/tutorials/sample_manager_tutorial.ipynb | 814 ---- docs/tutorials/virtual_db_tutorial.ipynb | 721 ++++ docs/virtual_database_concepts.md | 295 ++ docs/virtual_db.md | 16 + example_filter_config.yaml | 75 - mkdocs.yml | 27 +- tfbpapi/HfQueryAPI.py | 739 ---- tfbpapi/HfRankResponse.py | 482 --- tfbpapi/IncrementalAnalysisDB.py | 340 -- tfbpapi/RankResponseAnalysis.py | 277 -- tfbpapi/__init__.py | 33 + tfbpapi/constants.py | 44 - tfbpapi/{datainfo => }/datacard.py | 387 +- tfbpapi/datainfo/__init__.py | 31 - tfbpapi/datainfo/metadata_builder.py | 619 --- tfbpapi/datainfo/metadata_config_models.py | 312 -- tfbpapi/datainfo/metadata_manager.py | 282 -- tfbpapi/datainfo/models.py | 222 -- tfbpapi/datainfo/sample_manager.py | 774 ---- tfbpapi/errors.py | 220 +- tfbpapi/{datainfo => }/fetchers.py | 4 +- ...{HfCacheManager.py => hf_cache_manager.py} | 66 +- tfbpapi/models.py | 584 +++ tfbpapi/tests/conftest.py | 1439 +++++++ tfbpapi/tests/datainfo/__init__.py | 1 - tfbpapi/tests/datainfo/conftest.py | 1434 ------- tfbpapi/tests/datainfo/test_datacard.py | 775 ---- .../tests/datainfo/test_metadata_builder.py | 375 -- .../tests/datainfo/test_metadata_manager.py | 243 -- .../tests/{datainfo => }/example_datacards.py | 0 .../huggingface_collection_datacards.txt | 0 tfbpapi/tests/test_HfQueryAPI.py | 859 ----- tfbpapi/tests/test_HfRankResponse.py | 505 --- tfbpapi/tests/test_IncrementalAnalysisDB.py | 341 -- tfbpapi/tests/test_datacard.py | 449 +++ .../{datainfo => }/test_datacard_parsing.py | 4 +- tfbpapi/tests/{datainfo => }/test_fetchers.py | 38 +- ...cheManager.py => test_hf_cache_manager.py} | 35 +- .../test_metadata_config_models.py | 142 +- tfbpapi/tests/{datainfo => }/test_models.py | 2 +- .../{datainfo => }/test_real_datacards.py | 2 +- tfbpapi/tests/test_virtual_db.py | 507 +++ tfbpapi/virtual_db.py | 796 ++++ 63 files changed, 5313 insertions(+), 18834 deletions(-) delete mode 100644 docs/HfCacheManager.md delete mode 100644 docs/HfQueryAPI.md delete mode 100644 docs/HfRankResponse.md delete mode 100644 docs/IncrementalAnalysisDB.md create mode 100644 docs/datacard.md delete mode 100644 docs/datainfo.md create mode 100644 docs/fetchers.md create mode 100644 docs/hf_cache_manager.md create mode 100644 docs/models.md delete mode 100644 docs/tutorials/correlation_tutorial.ipynb delete mode 100644 docs/tutorials/hfqueryapi_tutorial.ipynb delete mode 100644 docs/tutorials/metadata_builder_tutorial.ipynb delete mode 100644 docs/tutorials/metadata_manager_tutorial.ipynb delete mode 100644 docs/tutorials/my_metadata_config.yaml delete mode 100644 docs/tutorials/passing_callingcards.csv delete mode 100644 docs/tutorials/rank_response_tutorial.ipynb delete mode 100644 docs/tutorials/sample_manager_tutorial.ipynb create mode 100644 docs/tutorials/virtual_db_tutorial.ipynb create mode 100644 docs/virtual_database_concepts.md create mode 100644 docs/virtual_db.md delete mode 100644 example_filter_config.yaml delete mode 100644 tfbpapi/HfQueryAPI.py delete mode 100644 tfbpapi/HfRankResponse.py delete mode 100644 tfbpapi/IncrementalAnalysisDB.py delete mode 100644 tfbpapi/RankResponseAnalysis.py rename tfbpapi/{datainfo => }/datacard.py (59%) delete mode 100644 tfbpapi/datainfo/__init__.py delete mode 100644 tfbpapi/datainfo/metadata_builder.py delete mode 100644 tfbpapi/datainfo/metadata_config_models.py delete mode 100644 tfbpapi/datainfo/metadata_manager.py delete mode 100644 tfbpapi/datainfo/models.py delete mode 100644 tfbpapi/datainfo/sample_manager.py rename tfbpapi/{datainfo => }/fetchers.py (98%) rename tfbpapi/{HfCacheManager.py => hf_cache_manager.py} (88%) create mode 100644 tfbpapi/models.py delete mode 100644 tfbpapi/tests/datainfo/__init__.py delete mode 100644 tfbpapi/tests/datainfo/conftest.py delete mode 100644 tfbpapi/tests/datainfo/test_datacard.py delete mode 100644 tfbpapi/tests/datainfo/test_metadata_builder.py delete mode 100644 tfbpapi/tests/datainfo/test_metadata_manager.py rename tfbpapi/tests/{datainfo => }/example_datacards.py (100%) rename tfbpapi/tests/{datainfo => }/huggingface_collection_datacards.txt (100%) delete mode 100644 tfbpapi/tests/test_HfQueryAPI.py delete mode 100644 tfbpapi/tests/test_HfRankResponse.py delete mode 100644 tfbpapi/tests/test_IncrementalAnalysisDB.py create mode 100644 tfbpapi/tests/test_datacard.py rename tfbpapi/tests/{datainfo => }/test_datacard_parsing.py (98%) rename tfbpapi/tests/{datainfo => }/test_fetchers.py (93%) rename tfbpapi/tests/{test_HfCacheManager.py => test_hf_cache_manager.py} (94%) rename tfbpapi/tests/{datainfo => }/test_metadata_config_models.py (75%) rename tfbpapi/tests/{datainfo => }/test_models.py (99%) rename tfbpapi/tests/{datainfo => }/test_real_datacards.py (99%) create mode 100644 tfbpapi/tests/test_virtual_db.py create mode 100644 tfbpapi/virtual_db.py diff --git a/docs/HfCacheManager.md b/docs/HfCacheManager.md deleted file mode 100644 index 89d5c77..0000000 --- a/docs/HfCacheManager.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.HfCacheManager.HfCacheManager diff --git a/docs/HfQueryAPI.md b/docs/HfQueryAPI.md deleted file mode 100644 index 357ea30..0000000 --- a/docs/HfQueryAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.HfQueryAPI.HfQueryAPI \ No newline at end of file diff --git a/docs/HfRankResponse.md b/docs/HfRankResponse.md deleted file mode 100644 index e2ffd53..0000000 --- a/docs/HfRankResponse.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.HfRankResponse.HfRankResponse \ No newline at end of file diff --git a/docs/IncrementalAnalysisDB.md b/docs/IncrementalAnalysisDB.md deleted file mode 100644 index 7dc07d7..0000000 --- a/docs/IncrementalAnalysisDB.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.IncrementalAnalysisDB.IncrementalAnalysisDB \ No newline at end of file diff --git a/docs/datacard.md b/docs/datacard.md new file mode 100644 index 0000000..cfab1f1 --- /dev/null +++ b/docs/datacard.md @@ -0,0 +1,6 @@ +# DataCard + +::: tfbpapi.datacard.DataCard + options: + show_root_heading: true + show_source: true diff --git a/docs/datainfo.md b/docs/datainfo.md deleted file mode 100644 index e159e7b..0000000 --- a/docs/datainfo.md +++ /dev/null @@ -1,133 +0,0 @@ -# DataInfo Package - -The `datainfo` package provides dataset information management for HuggingFace datasets. It enables exploration of dataset metadata, structure, and relationships without loading actual genomic data. - -## Overview - -The datainfo package consists of three main components: - -- **DataCard**: High-level interface for exploring dataset metadata -- **Fetchers**: Low-level components for retrieving data from HuggingFace Hub -- **Models**: Pydantic models for validation and type safety - -## Main Interface - -### DataCard -::: tfbpapi.datainfo.datacard.DataCard - options: - show_root_heading: true - show_source: true - -The `DataCard` class is the primary interface for exploring HuggingFace datasets. It provides methods to: - -- Discover dataset configurations and types -- Explore feature schemas and data types -- Understand metadata relationships -- Extract field values and experimental conditions -- Navigate partitioned dataset structures - -## Data Models - -### Core Models -::: tfbpapi.datainfo.models.DatasetCard - options: - show_root_heading: true - -::: tfbpapi.datainfo.models.DatasetConfig - options: - show_root_heading: true - -::: tfbpapi.datainfo.models.FeatureInfo - options: - show_root_heading: true - -### Dataset Types -::: tfbpapi.datainfo.models.DatasetType - options: - show_root_heading: true - -### Relationship Models -::: tfbpapi.datainfo.models.MetadataRelationship - options: - show_root_heading: true - -::: tfbpapi.datainfo.models.ExtractedMetadata - options: - show_root_heading: true - -## Data Fetchers - -### HuggingFace Integration -::: tfbpapi.datainfo.fetchers.HfDataCardFetcher - options: - show_root_heading: true - -::: tfbpapi.datainfo.fetchers.HfRepoStructureFetcher - options: - show_root_heading: true - -::: tfbpapi.datainfo.fetchers.HfSizeInfoFetcher - options: - show_root_heading: true - -## Usage Examples - -### Basic Dataset Exploration - -```python -from tfbpapi.datainfo import DataCard - -# Initialize DataCard for a repository -card = DataCard('BrentLab/rossi_2021') - -# Get repository overview -repo_info = card.get_repository_info() -print(f"Dataset: {repo_info['pretty_name']}") -print(f"Configurations: {repo_info['num_configs']}") - -# Explore configurations -for config in card.configs: - print(f"{config.config_name}: {config.dataset_type.value}") -``` - -### Understanding Dataset Structure - -```python -# Get detailed config information -config_info = card.explore_config('metadata') -print(f"Features: {config_info['num_features']}") - -# Check for partitioned data -if 'partitioning' in config_info: - partition_info = config_info['partitioning'] - print(f"Partitioned by: {partition_info['partition_by']}") -``` - -### Metadata Relationships - -```python -# Discover metadata relationships -relationships = card.get_metadata_relationships() -for rel in relationships: - print(f"{rel.data_config} -> {rel.metadata_config} ({rel.relationship_type})") -``` - -## Integration with HfQueryAPI - -The datainfo package is designed to work seamlessly with `HfQueryAPI` for efficient data loading: - -```python -from tfbpapi import HfQueryAPI -from tfbpapi.datainfo import DataCard - -# Explore dataset structure first -card = DataCard('BrentLab/rossi_2021') -config_info = card.explore_config('genome_map') - -# Use insights to load data efficiently -query_api = HfQueryAPI('BrentLab/rossi_2021') -data = query_api.get_pandas('genome_map', - filters={'run_accession': 'SRR123456'}) -``` - -For a complete tutorial, see the [DataCard Tutorial](tutorials/datacard_tutorial.ipynb). \ No newline at end of file diff --git a/docs/errors.md b/docs/errors.md index cfbf1df..6ba92ff 100644 --- a/docs/errors.md +++ b/docs/errors.md @@ -1 +1,28 @@ -::: tfbpapi.errors \ No newline at end of file +# Custom Exceptions + +## HfDataFetchError + +::: tfbpapi.errors.HfDataFetchError + options: + show_root_heading: true + show_source: true + +Raised when HuggingFace API requests fail during data fetching operations. + +## DataCardError + +::: tfbpapi.errors.DataCardError + options: + show_root_heading: true + show_source: true + +Base exception for DataCard operations. + +## DataCardValidationError + +::: tfbpapi.errors.DataCardValidationError + options: + show_root_heading: true + show_source: true + +Raised when dataset card validation fails during parsing or loading. \ No newline at end of file diff --git a/docs/fetchers.md b/docs/fetchers.md new file mode 100644 index 0000000..2901a79 --- /dev/null +++ b/docs/fetchers.md @@ -0,0 +1,16 @@ +# Data Fetchers + +::: tfbpapi.fetchers.HfDataCardFetcher + options: + show_root_heading: true + show_source: true + +::: tfbpapi.fetchers.HfRepoStructureFetcher + options: + show_root_heading: true + show_source: true + +::: tfbpapi.fetchers.HfSizeInfoFetcher + options: + show_root_heading: true + show_source: true diff --git a/docs/hf_cache_manager.md b/docs/hf_cache_manager.md new file mode 100644 index 0000000..752b712 --- /dev/null +++ b/docs/hf_cache_manager.md @@ -0,0 +1,6 @@ +# HfCacheManager + +::: tfbpapi.hf_cache_manager.HfCacheManager + options: + show_root_heading: true + show_source: true diff --git a/docs/index.md b/docs/index.md index a50a31b..44b9d63 100644 --- a/docs/index.md +++ b/docs/index.md @@ -24,17 +24,17 @@ This is a Python package for interfacing with a collection of datasets hosted on ### Core Components -- **HfQueryAPI** (`tfbpapi/HfQueryAPI.py`): Main interface for querying HF datasets with intelligent downloading and SQL querying capabilities. Supports automatic dataset size detection, selective downloading, and DuckDB-based querying. +- **VirtualDB** (`tfbpapi/virtual_db.py`): Primary API for unified cross-dataset queries. Provides standardized query interface across heterogeneous datasets with varying experimental condition structures through external YAML configuration. -- **HfCacheManager** (`tfbpapi/HfCacheManager.py`): Manages HF cache with cleanup and size management features. Provides automatic cache cleanup based on age and size thresholds. +- **DataCard** (`tfbpapi/datacard.py`): Interface for exploring HuggingFace dataset metadata without loading actual data. Enables dataset structure discovery, experimental condition exploration, and query planning. -- **HfRankResponse** (`tfbpapi/HfRankResponse.py`): Response handling for HF-based ranking operations. Computes and analyzes "rank response" - the cumulative number of responsive targets binned by binding rank scores. +- **HfCacheManager** (`tfbpapi/hf_cache_manager.py`): Manages HuggingFace cache with intelligent downloading, DuckDB-based SQL querying, and automatic cleanup based on age/size thresholds. -- **IncrementalAnalysisDB** (`tfbpapi/IncrementalAnalysisDB.py`): Database management for incremental analysis workflows with shared result storage. +### Supporting Components -### Dataset Information Management +- **Models** (`tfbpapi/models.py`): Pydantic models for dataset cards, configurations, features, and VirtualDB configuration (MetadataConfig, PropertyMapping, RepositoryConfig). -- **datainfo package** (`tfbpapi/datainfo/`): Comprehensive dataset exploration and metadata management for HuggingFace datasets. Provides the `DataCard` class for exploring dataset structure, configurations, and relationships without loading actual data. Includes Pydantic models for validation and fetchers for HuggingFace Hub integration. +- **Fetchers** (`tfbpapi/fetchers.py`): Low-level components for retrieving data from HuggingFace Hub (HfDataCardFetcher, HfRepoStructureFetcher, HfSizeInfoFetcher). ### Data Types @@ -49,7 +49,7 @@ Data is stored in Apache Parquet format, either as single files or parquet datas ### Error Handling -- **errors.py** (`tfbpapi/errors.py`): Custom exception classes for dataset management including `DatasetError`, `RepoTooLargeError`, `DataCardParsingError`, `HfDataFetchError`, and more. +- **errors.py** (`tfbpapi/errors.py`): Custom exception classes for dataset management including `HfDataFetchError`, `DataCardError`, and `DataCardValidationError`. ## Configuration diff --git a/docs/models.md b/docs/models.md new file mode 100644 index 0000000..631494d --- /dev/null +++ b/docs/models.md @@ -0,0 +1,41 @@ +# Pydantic Models + +## VirtualDB Configuration Models + +::: tfbpapi.models.MetadataConfig + options: + show_root_heading: true + +::: tfbpapi.models.RepositoryConfig + options: + show_root_heading: true + +::: tfbpapi.models.PropertyMapping + options: + show_root_heading: true + +## DataCard Models + +::: tfbpapi.models.DatasetCard + options: + show_root_heading: true + +::: tfbpapi.models.DatasetConfig + options: + show_root_heading: true + +::: tfbpapi.models.DatasetType + options: + show_root_heading: true + +::: tfbpapi.models.FeatureInfo + options: + show_root_heading: true + +::: tfbpapi.models.MetadataRelationship + options: + show_root_heading: true + +::: tfbpapi.models.ExtractedMetadata + options: + show_root_heading: true diff --git a/docs/tutorials/cache_manager_tutorial.ipynb b/docs/tutorials/cache_manager_tutorial.ipynb index a043417..a905a8b 100644 --- a/docs/tutorials/cache_manager_tutorial.ipynb +++ b/docs/tutorials/cache_manager_tutorial.ipynb @@ -30,25 +30,33 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chase/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" + ] + }, { "name": "stdout", "output_type": "stream", "text": [ - "HfCacheManager initialized for: BrentLab/hackett_2020\n", + "HfCacheManager initialized for: BrentLab/mahendrawada_2025\n", "DuckDB connection: Active\n", "Logger configured: Yes\n", - "Current HF cache size: 4.6G\n", - "Cached repositories: 9\n" + "Current HF cache size: 5.2G\n", + "Cached repositories: 10\n" ] } ], "source": [ "import duckdb\n", "import logging\n", - "from tfbpapi.HfCacheManager import HfCacheManager\n", + "from tfbpapi.hf_cache_manager import HfCacheManager\n", "from huggingface_hub import scan_cache_dir\n", "\n", "# Set up logging to see cache management activities\n", @@ -78,16 +86,16 @@ }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "HFCacheInfo(size_on_disk=4576042428, repos=frozenset({CachedRepoInfo(repo_id='BrentLab/mahendrawada_2025', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025'), size_on_disk=1997004, nb_files=9, revisions=frozenset({CachedRevisionInfo(commit_hash='8bea431be57e21633be4174df291540b1fae212a', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/8bea431be57e21633be4174df291540b1fae212a'), size_on_disk=18603, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/8bea431be57e21633be4174df291540b1fae212a/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/6a2a5a6a8006e386c0e47a2a98151442046d3d41'), size_on_disk=18603, blob_last_accessed=1758400385.1923163, blob_last_modified=1758155849.9076133)}), refs=frozenset({'main'}), last_modified=1758155849.9076133), CachedRevisionInfo(commit_hash='d602c1651f3117dd8d3c7443440eb819b7aa2ec2', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2'), size_on_disk=1963211, files=frozenset({CachedFileInfo(file_name='rnaseq_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2/rnaseq_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/4411789f50aab449658c100f2758ceb410022a0a30f956fd5fe41152915580f9'), size_on_disk=286605, blob_last_accessed=1757439452.1947446, blob_last_modified=1756858618.0563447), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/1bf59ab7ca7d0595586675ff09c4767b04037f29'), size_on_disk=15355, blob_last_accessed=1758055881.95639, blob_last_modified=1757439229.7199314), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756858617.9613454, blob_last_modified=1756858618.033345), CachedFileInfo(file_name='parse_mahendrawada.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2/scripts/parse_mahendrawada.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/c1130d6f8e06f94b1441f73821e3ae727663d0fa'), size_on_disk=6546, blob_last_accessed=1757439229.7259312, blob_last_modified=1757439229.7949307), CachedFileInfo(file_name='chec_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2/chec_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/dd3e967d8c54c6056dc9fd6bdb921c3eed9d10df3a449273bd08faeb1b936765'), size_on_disk=1220601, blob_last_accessed=1757439235.362876, blob_last_modified=1756858618.0613449), CachedFileInfo(file_name='features_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2/features_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/618ea89d880c8a1b4c6d05cc3c13a70cbb05784d10bf2b6c4fc954705203096c'), size_on_disk=431436, blob_last_accessed=1757439459.1206765, blob_last_modified=1756858618.1383443), CachedFileInfo(file_name='checksums.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2/checksums.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/bee0d3fd2c2bd4f07ec16489cdba8d5b4b6a2ae2'), size_on_disk=207, blob_last_accessed=1757439229.7169313, blob_last_modified=1757439229.7729309)}), refs=frozenset(), last_modified=1757439229.7949307), CachedRevisionInfo(commit_hash='66e0cf1fc85e136990b8230d6fa80b64c1091c7c', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/66e0cf1fc85e136990b8230d6fa80b64c1091c7c'), size_on_disk=1956293, files=frozenset({CachedFileInfo(file_name='features_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/66e0cf1fc85e136990b8230d6fa80b64c1091c7c/features_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/618ea89d880c8a1b4c6d05cc3c13a70cbb05784d10bf2b6c4fc954705203096c'), size_on_disk=431436, blob_last_accessed=1757439459.1206765, blob_last_modified=1756858618.1383443), CachedFileInfo(file_name='rnaseq_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/66e0cf1fc85e136990b8230d6fa80b64c1091c7c/rnaseq_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/4411789f50aab449658c100f2758ceb410022a0a30f956fd5fe41152915580f9'), size_on_disk=286605, blob_last_accessed=1757439452.1947446, blob_last_modified=1756858618.0563447), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/66e0cf1fc85e136990b8230d6fa80b64c1091c7c/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756858617.9613454, blob_last_modified=1756858618.033345), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/66e0cf1fc85e136990b8230d6fa80b64c1091c7c/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/2ababbde8810cb0f65bfbceb4f2b5b4b65e491c1'), size_on_disk=15190, blob_last_accessed=1756858550.24473, blob_last_modified=1756858550.24373), CachedFileInfo(file_name='chec_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/66e0cf1fc85e136990b8230d6fa80b64c1091c7c/chec_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/dd3e967d8c54c6056dc9fd6bdb921c3eed9d10df3a449273bd08faeb1b936765'), size_on_disk=1220601, blob_last_accessed=1757439235.362876, blob_last_modified=1756858618.0613449)}), refs=frozenset(), last_modified=1756858618.1383443)}), last_accessed=1758400385.1923163, last_modified=1758155849.9076133), CachedRepoInfo(repo_id='BrentLab/yeast_genome_resources', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources'), size_on_disk=114475, nb_files=7, revisions=frozenset({CachedRevisionInfo(commit_hash='15fdb72f8c31ae58f3e3ce7c90be279383743a2f', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/15fdb72f8c31ae58f3e3ce7c90be279383743a2f'), size_on_disk=88406, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/15fdb72f8c31ae58f3e3ce7c90be279383743a2f/features/chr=chrII/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/d64638adb2290412a962abffe0fbd0546ff139b29064bbf313b0a768449454a5'), size_on_disk=82276, blob_last_accessed=1755812630.656903, blob_last_modified=1755812631.0999012), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/15fdb72f8c31ae58f3e3ce7c90be279383743a2f/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/645da8028fd6abe86ee305f76eab78cdf29be364'), size_on_disk=6130, blob_last_accessed=1755819093.2316637, blob_last_modified=1755819093.2306638)}), refs=frozenset(), last_modified=1755819093.2306638), CachedRevisionInfo(commit_hash='42beb28478e27c5d4c5bb2308b12100e6489ef07', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/42beb28478e27c5d4c5bb2308b12100e6489ef07'), size_on_disk=6125, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/42beb28478e27c5d4c5bb2308b12100e6489ef07/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/ab6cc475062a2767a41b2aaa006faf4d9d2d60d6'), size_on_disk=6125, blob_last_accessed=1758155946.5559895, blob_last_modified=1758155946.5549896)}), refs=frozenset({'main'}), last_modified=1758155946.5549896), CachedRevisionInfo(commit_hash='25b47a191e40728efc2437d906e84317f922b36b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/25b47a191e40728efc2437d906e84317f922b36b'), size_on_disk=5426, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/25b47a191e40728efc2437d906e84317f922b36b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/9cdf7b342b249f25f2c2cbe46a2e0a26ded4f692'), size_on_disk=5426, blob_last_accessed=1755815405.3308983, blob_last_modified=1755815405.3298984)}), refs=frozenset(), last_modified=1755815405.3298984), CachedRevisionInfo(commit_hash='aae6e5ea8139e23940d11b4d7e77684c78bb7f6b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/aae6e5ea8139e23940d11b4d7e77684c78bb7f6b'), size_on_disk=4585, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/aae6e5ea8139e23940d11b4d7e77684c78bb7f6b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/4320f8f18620d988a5fc32872a671ace406fd0a2'), size_on_disk=4585, blob_last_accessed=1755814342.1923234, blob_last_modified=1755814342.1913235)}), refs=frozenset(), last_modified=1755814342.1913235), CachedRevisionInfo(commit_hash='7441b9a8c858332e730e7ddb9d255460ae698626', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/7441b9a8c858332e730e7ddb9d255460ae698626'), size_on_disk=87714, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/7441b9a8c858332e730e7ddb9d255460ae698626/features/chr=chrII/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/d64638adb2290412a962abffe0fbd0546ff139b29064bbf313b0a768449454a5'), size_on_disk=82276, blob_last_accessed=1755812630.656903, blob_last_modified=1755812631.0999012), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/7441b9a8c858332e730e7ddb9d255460ae698626/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/e63c2087b2e56dc15e8ac6cf5693c2391127b732'), size_on_disk=5438, blob_last_accessed=1755816785.70087, blob_last_modified=1755816785.6988702)}), refs=frozenset(), last_modified=1755816785.6988702), CachedRevisionInfo(commit_hash='a5ecb243ba27ca4b4d6ad1b60cc160c033ca2be6', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/a5ecb243ba27ca4b4d6ad1b60cc160c033ca2be6'), size_on_disk=82276, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/a5ecb243ba27ca4b4d6ad1b60cc160c033ca2be6/features/chr=chrII/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/d64638adb2290412a962abffe0fbd0546ff139b29064bbf313b0a768449454a5'), size_on_disk=82276, blob_last_accessed=1755812630.656903, blob_last_modified=1755812631.0999012)}), refs=frozenset(), last_modified=1755812631.0999012), CachedRevisionInfo(commit_hash='6607f7be76546563db0a68a9365c0a858bb5f3a1', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/6607f7be76546563db0a68a9365c0a858bb5f3a1'), size_on_disk=4495, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/6607f7be76546563db0a68a9365c0a858bb5f3a1/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/a31c4647c502767c22b5fe725ec8de58686a02d4'), size_on_disk=4495, blob_last_accessed=1755813454.8008156, blob_last_modified=1755813454.7998157)}), refs=frozenset(), last_modified=1755813454.7998157)}), last_accessed=1758155946.5559895, last_modified=1758155946.5549896), CachedRepoInfo(repo_id='BrentLab/barkai_compendium', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium'), size_on_disk=3579068970, nb_files=504, revisions=frozenset({CachedRevisionInfo(commit_hash='a987ef37c72fcd07b18828d320bc4305480daade', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade'), size_on_disk=3579068970, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417769/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e640018be0b2de04e0ed77c7586c413990876e91348531f43511c20da5fe279a'), size_on_disk=11568261, blob_last_accessed=1756926331.59533, blob_last_modified=1756926333.0913236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417930/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62f17648a07c7675d6a101e3d12efeae247c742e37eaacf96f95e03dceedefde'), size_on_disk=20574235, blob_last_accessed=1756926361.2242014, blob_last_modified=1756926362.5701957), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417693/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/98a5478f5bd4feb033ac69510f2bf785c87777377b7b8b6dce24b8f89dbd6a03'), size_on_disk=18580692, blob_last_accessed=1756926319.7013817, blob_last_modified=1756926321.9783719), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417686/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6a42f24b250baf0d88696bc123f01494a400b3f03f2ff2a1fc828f9295721a16'), size_on_disk=12070748, blob_last_accessed=1756926318.2063882, blob_last_modified=1756926319.627382), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417618/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d2ce91297551bc97b4fca46007f80c8846e5f9e9c78d9c1d7ded02fc31b3ef38'), size_on_disk=9239111, blob_last_accessed=1756926302.3654573, blob_last_modified=1756926304.3714485), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417724/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e5beee12039dfe6b4dae180a31ac0897dea725754554db43e2df9f99da94db58'), size_on_disk=4535402, blob_last_accessed=1756926324.69836, blob_last_modified=1756926325.9703546), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417794/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f15be566c029c52cf46f6258e589529afb3a002f010710a54d04dd6151f8b684'), size_on_disk=10205023, blob_last_accessed=1756926335.9063113, blob_last_modified=1756926337.151306), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417788/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fd6f44471f1b86568bc059f25e5ccec7bda2b85b9f8be536aeaf2305fe015f5d'), size_on_disk=9112879, blob_last_accessed=1756926334.546317, blob_last_modified=1756926335.971311), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4331f53e52f628f99a9695fa70339b08eb69a011cc8a3cbdb08dfa8e6c5d3ddd'), size_on_disk=10779086, blob_last_accessed=1756926376.8381338, blob_last_modified=1756926377.9141293), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417777/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bd953fd8a3ee2a5c90382c1c9adc8091fbca801aa87f07a409b6dd5a9704421f'), size_on_disk=1267333, blob_last_accessed=1756926332.9623241, blob_last_modified=1756926333.6353211), CachedFileInfo(file_name='genome_map.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b8f0ed936cb43c4649126e9b7596cef0ab626a2d'), size_on_disk=142786, blob_last_accessed=1756926300.410466, blob_last_modified=1756926300.4954655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380950/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f508a5da82832b920577592b853da2c2d76ebf0adca587e10b63507f46dc5066'), size_on_disk=3452513, blob_last_accessed=1756926368.7781687, blob_last_modified=1756926369.7081647), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417846/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a3dc440aac6a69d75ec73242a30e59d8183023a2677f1576b5e73d037d6e6654'), size_on_disk=2680468, blob_last_accessed=1756926346.0562673, blob_last_modified=1756926346.7392642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417662/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/90008c75533e5a395bf92b2a4382cfb8d6c27af4921615cac22bc1965375efb5'), size_on_disk=12701661, blob_last_accessed=1756926313.0204108, blob_last_modified=1756926314.158406), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417781/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9dd7989f90f8762246868ccd7d11c9ce876b2b33099efc3a4c5bf1bc7cbcca80'), size_on_disk=6923944, blob_last_accessed=1756926333.4783218, blob_last_modified=1756926335.3743136), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417689/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e27d1d153e5b135a09ec5db80bb05ace721fd60ac90f0693ac34ca3160fdfbdd'), size_on_disk=5015020, blob_last_accessed=1756926319.2973835, blob_last_modified=1756926321.220375), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417909/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c5de46a74c16aee32dfb2be89a50dceaa5704c0c550076dcec483d1c21d983af'), size_on_disk=5969725, blob_last_accessed=1756926357.3362184, blob_last_modified=1756926358.5162132), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417856/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3dc4e868f31759c9ac3d43f40097f5ed2f0efb6307e3bc1ccbed70c50a16ee4e'), size_on_disk=6126482, blob_last_accessed=1756926347.3832614, blob_last_modified=1756926348.3162575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417734/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/10d4c6d9acc5b56370fc32062262ae3871008e20e960c6b352ac27a4e229c905'), size_on_disk=1495194, blob_last_accessed=1756926325.9453547, blob_last_modified=1756926326.745351), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417669/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8eaf08a45dbd72787c5250403f3d3079db9902e77527c7ca438abc740fdfb753'), size_on_disk=7376929, blob_last_accessed=1756926314.6724036, blob_last_modified=1756926316.0383978), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381044/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/89a79f2c5f6258380ff4776af49db6119ac0c04342686705cfde94feebd7c9ca'), size_on_disk=3576881, blob_last_accessed=1756926776.065759, blob_last_modified=1756926778.8987432), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380932/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f214422d02adb2b0a1037eb424d8574123cf27d8a5cd1418feb5733b0249f60c'), size_on_disk=6251591, blob_last_accessed=1756926365.6181824, blob_last_modified=1756926366.5551784), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417937/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e432985b09ba72eab54fc008b87b20a718dbae96d920a61b47de7e16d214227c'), size_on_disk=6445266, blob_last_accessed=1756926362.6341953, blob_last_modified=1756926363.6641908), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417754/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0d840353f1148a08928dfb3177a0abfe635c406fc77d6d9958743585ade6b872'), size_on_disk=1101891, blob_last_accessed=1756926329.758338, blob_last_modified=1756926330.215336), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417757/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6c9c5dbd0513759ace8599b2542a1f99fe0c30feb318665ebdb55bfb111c8c67'), size_on_disk=5834465, blob_last_accessed=1756926330.011337, blob_last_modified=1756926330.7623336), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380964/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3a63df07de544662fb04d5388f34b0c8068efbc19f464b673d8a860a57b5b422'), size_on_disk=956844, blob_last_accessed=1756926370.333162, blob_last_modified=1756926370.8741596), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380926/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fd708dfb8084d767fde32c52acb58e785083ce7becbd2e160277f31460719346'), size_on_disk=6579161, blob_last_accessed=1756926364.8451858, blob_last_modified=1756926365.5441828), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417874/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6674c316eab3b03bbbe3502565efff7650445f5e1453933336d74938e42a14bf'), size_on_disk=6201897, blob_last_accessed=1756926350.5772476, blob_last_modified=1756926351.5142436), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417610/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a93c708ade83c1c4521589a51835a1dcc98972ab900e0eefdb8ce34e64394bf4'), size_on_disk=2566580, blob_last_accessed=1756926301.328462, blob_last_modified=1756926302.4814568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417793/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8a51bd4d953e697022a85f9b527afd45126988ba4bbb796d71c9e5f6c8fffc44'), size_on_disk=10797388, blob_last_accessed=1756926335.4883132, blob_last_modified=1756926336.7013078), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381050/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f3c3bac37a1d4ec633edf53ba305c970eb3de412fd54dfbaf0cdf89f39574334'), size_on_disk=1518412, blob_last_accessed=1756926777.1687527, blob_last_modified=1756926778.028748), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381016/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0e597280417f8f8b5b476bc9d66033afc9412759f5e2f9bd9f3367932cc6f03f'), size_on_disk=820782, blob_last_accessed=1756926377.9791288, blob_last_modified=1756926378.4041271), CachedFileInfo(file_name='GSE178430_metadata.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE178430_metadata.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9673758f13ec096ec4a89642bbe34d60975f5f05'), size_on_disk=61, blob_last_accessed=1756926300.2374666, blob_last_modified=1756926300.2994664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417670/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5e36b63ef1fbb55f821ed2c3d61f0d0bf86cf00846e86ff9dbe6f4d9597f9dc8'), size_on_disk=10881106, blob_last_accessed=1756926314.7704034, blob_last_modified=1756926315.980398), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380944/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7331bfc92dc902a721083ac4ea027481c90ce204d7dca7c493fec8f284db0155'), size_on_disk=8432479, blob_last_accessed=1756926367.299175, blob_last_modified=1756926368.6141694), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380928/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/251ffcb8e06f2ac0db0791065248327a9d1eeef33a07f5c6b25946e04b6c46d8'), size_on_disk=2289693, blob_last_accessed=1756926365.037185, blob_last_modified=1756926366.1961799), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380992/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dbc6b1de202e7fb25701c02607facc3e8c332550907ca0b70ba177533a311d6d'), size_on_disk=2551903, blob_last_accessed=1756926374.2281451, blob_last_modified=1756926375.0341415), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381068/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/16e4412527449a3cb9d2da129c3309a71b2ec9b33a7a39cb9bb31d41ce5d2e04'), size_on_disk=14909067, blob_last_accessed=1756926780.5687337, blob_last_modified=1756926782.3187242), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380961/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fc4356aaa2a30217145b4aecc4f3fdd2575fcd696a2603fea3d8112998f770c7'), size_on_disk=798599, blob_last_accessed=1756926370.1791627, blob_last_modified=1756926370.589161), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417612/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ee40f7af39e729ee79ef18d6777bbd632b7cc7811041a32d54f4a02b42638464'), size_on_disk=3509045, blob_last_accessed=1756926301.5874608, blob_last_modified=1756926302.6274562), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380933/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/07c22e11c56ce5ecb6c22c910c3d3a0d9b54fd341bef0513b4bf42a9c53ec6a0'), size_on_disk=10138164, blob_last_accessed=1756926365.8761814, blob_last_modified=1756926366.7471776), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380955/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/369e0f1de51ea197e928618e562613eeed2ed59ce5c1389ce07ff6dd96d391b4'), size_on_disk=3596907, blob_last_accessed=1756926369.4561658, blob_last_modified=1756926370.2661622), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380934/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d264f2a91c87696f6a07f573f1d921c91e1d53e3afca59610ba4703b3683b617'), size_on_disk=4601537, blob_last_accessed=1756926366.1091802, blob_last_modified=1756926366.9191768), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417755/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f4ffef985aed69a80417b5179ef9923bd73378a93a97f2dff128b1f7716b4599'), size_on_disk=8437424, blob_last_accessed=1756926329.777338, blob_last_modified=1756926331.2423315), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380967/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3abcaaa4db306d580dc27882172ced53d591b8aa69a1805bd2a9eea0448ff941'), size_on_disk=1245882, blob_last_accessed=1756926370.6731606, blob_last_modified=1756926371.1021585), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417903/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3ddfaef9dc57d0f63b8613408aba3af803dabd5ee87a1584edd9391b2b41bec3'), size_on_disk=11218813, blob_last_accessed=1756926355.9432244, blob_last_modified=1756926357.2232187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417888/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/961a35161b04a0e892f46a0a90b6d4776d7fe07ef82fc7016c95b1a885c4274f'), size_on_disk=9651505, blob_last_accessed=1756926353.298236, blob_last_modified=1756926354.65023), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417666/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/38008595447ab722c1889d0e19babf74299822ac7733504a4f09e1af1e4ad1ac'), size_on_disk=7712422, blob_last_accessed=1756926313.945407, blob_last_modified=1756926315.073402), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417837/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/04d099809ca78bd87dcb9921398681ac64e827439049a37e97a71ba0828debfb'), size_on_disk=8348990, blob_last_accessed=1756926344.4012744, blob_last_modified=1756926345.6152692), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380939/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dc6382d9dba7855504a6c7fa2a4adc44f564e25ff5c54eedd082c3cbe0a520a0'), size_on_disk=9235430, blob_last_accessed=1756926366.630178, blob_last_modified=1756926367.5861738), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417667/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef18f0ec136ef4b0facd217b32604556a7c9f514d75e26d2be17eb8da9b74a14'), size_on_disk=7979245, blob_last_accessed=1756926314.2284057, blob_last_modified=1756926315.734399), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417814/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9f70b1469a6d0ad8758aa7a44e754e07ccc401906f8af403a72075d763919535'), size_on_disk=7618977, blob_last_accessed=1756926339.5142956, blob_last_modified=1756926341.0872889), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381058/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9dc97b5892fcc8af593c03fad2040396b9b10626a655319ad40b38acd75e1d7b'), size_on_disk=11534270, blob_last_accessed=1756926778.543745, blob_last_modified=1756926779.7317386), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417730/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f2554716083cfa1bb54c2098315b92e1b3537eb265da884c00ba7e7d4c050da'), size_on_disk=14980864, blob_last_accessed=1756926325.5143564, blob_last_modified=1756926326.803351), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bbfeb8571ee8915d793698c2116cef9f2ea61573c66b4ae7066078271c6172d6'), size_on_disk=5132301, blob_last_accessed=1756926362.6441953, blob_last_modified=1756926363.7271905), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417830/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/21e2dbb49f07cb7c2d7277f5bf1a442e216a2991677c3c4ece5c51316795ea77'), size_on_disk=12760016, blob_last_accessed=1756926342.9432807, blob_last_modified=1756926344.1602755), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417614/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dac699f09872b0c58b742c692b341f0b00b7d83284cec5ef838c276a6f48908c'), size_on_disk=1759475, blob_last_accessed=1756926301.7274601, blob_last_modified=1756926302.4774568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381026/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e41f2d7a1b084a404e7f5e9e003b27f88665a983ec73859f7eb42cd8bfa2bdd3'), size_on_disk=2857045, blob_last_accessed=1756926378.898125, blob_last_modified=1756926379.7281213), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417721/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5457a24eab864fb369ae0e6fbdb302876e2a6c65ae83444f18bc01ad08ecfc0c'), size_on_disk=2073045, blob_last_accessed=1756926324.3243616, blob_last_modified=1756926324.9983587), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417843/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6569e23f0eb660e9f7111b9cdd69e7ae797ba265647ee44e4cbf2d8babdeca9e'), size_on_disk=2706587, blob_last_accessed=1756926345.3402703, blob_last_modified=1756926346.125267), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417752/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/efc9f1ecf4a7b06b23da46fb8c7d1879b67a9d6603ed753d09e2028396ec05c5'), size_on_disk=3420764, blob_last_accessed=1756926329.26534, blob_last_modified=1756926330.0613368), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380988/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/27b6e71986cf2b7da20b64dbda0ba47e08753f7184ddd57ed288f11df88d21e1'), size_on_disk=16063760, blob_last_accessed=1756926373.7421472, blob_last_modified=1756926374.932142), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381054/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1a9f9c650973937d852d2de54c0d73336d1516184d29e203563fd9bf8d7fefa5'), size_on_disk=3346485, blob_last_accessed=1756926778.1077476, blob_last_modified=1756926778.8637433), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417665/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7c9b3d448e53fcac9334df092cc003f438cbc462e6785ac3d61202bd65a7125f'), size_on_disk=7481605, blob_last_accessed=1756926313.490409, blob_last_modified=1756926314.7744033), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417945/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6689ba375c993d00d1f0e716d3bc39604d68df654a67b2518c56f4d54c7f4ca6'), size_on_disk=11289854, blob_last_accessed=1756926364.0191894, blob_last_modified=1756926364.9011855), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380981/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/198a0b08846a11e72de25e0e9c44f495ef88c9260de2e036d240715004c04953'), size_on_disk=8535317, blob_last_accessed=1756926372.5771523, blob_last_modified=1756926373.8291469), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381040/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a7c622a8ae1f5083eef26ecbcc2444055a9265b674ae80002e5468c94c0eb070'), size_on_disk=9682671, blob_last_accessed=1756926774.782766, blob_last_modified=1756926776.7967548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381019/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3023e878b223b00b77d3411a6d20195b85aa7e61a8445fe725ff694c77816053'), size_on_disk=1022843, blob_last_accessed=1756926378.3191273, blob_last_modified=1756926378.864125), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381032/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/335a75b123ae38273f1b1a10f16e9547eec356f68a1d3078926f011d3332e2b5'), size_on_disk=656385, blob_last_accessed=1756926379.6781216, blob_last_modified=1756926775.4077625), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380979/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef743a2a6a8fe44b62963b981ef0429676e7854243ab07d450d9c3ab569c7e8e'), size_on_disk=3735979, blob_last_accessed=1756926372.2201538, blob_last_modified=1756926373.6791475), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417867/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f4783b00d4674df92d84f9a304fb9d143c7b9e4754e3efcb9c072a49a9b9298b'), size_on_disk=8536709, blob_last_accessed=1756926349.3882527, blob_last_modified=1756926350.2882488), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417644/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0c788a7ba70689a796af823df5cc14504dfdc854617952459367364c4498cdeb'), size_on_disk=949200, blob_last_accessed=1756926309.758425, blob_last_modified=1756926310.6444213), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417899/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b4349f94cb0679468e9c28a9439c1a71c88d7288adc3bdad0ca31a2590f1d4e0'), size_on_disk=7910137, blob_last_accessed=1756926355.3832269, blob_last_modified=1756926356.3022227), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380957/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5a2362d35ac90fb1926051e6313886a649074b0022b128741a22a5ee24bb095d'), size_on_disk=5388789, blob_last_accessed=1756926369.5311654, blob_last_modified=1756926370.8571596), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417876/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0713329434bfbd55f1998400d9d44c1d8c7fec6cb1b646c0fdb926f1046f9b47'), size_on_disk=11246388, blob_last_accessed=1756926350.971246, blob_last_modified=1756926352.0472412), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417636/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4a3b2c7076107dcc47a87179cecab9e2051350c94e951a7aadc2ff0cf6fdbaca'), size_on_disk=43479381, blob_last_accessed=1756926307.6494343, blob_last_modified=1756926311.4484177), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380968/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/549ef4e39a437adb99efecdc9b05fee3ca4b31c878b99f7a4cf1398f405ca049'), size_on_disk=3575926, blob_last_accessed=1756926370.7441602, blob_last_modified=1756926371.5941565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380925/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdf190d30101829153acfd62edd4d689ac65a7d737f0e80978c7d460819b5caa'), size_on_disk=9220100, blob_last_accessed=1756926364.6101868, blob_last_modified=1756926366.1331801), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381046/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a96c1819b1010c40fc4d92418e407cc5b5f48f9e24883d4b4cdf3e02d23b688a'), size_on_disk=3071378, blob_last_accessed=1756926776.6277559, blob_last_modified=1756926777.855749), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380974/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ea9eb19e91fedadd9e46d5d7dc9169995065af5b6b8e823f1667117be9d737d0'), size_on_disk=8889742, blob_last_accessed=1756926371.5481567, blob_last_modified=1756926372.2801535), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380931/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/be287270314fed3dded81e0c24a66d8bd991b219fd6a157f1aeaa1690262e563'), size_on_disk=4324179, blob_last_accessed=1756926365.1611843, blob_last_modified=1756926366.1241803), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380940/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4f59a7f9287c427bed27910f08a041e460eff2490c21e2fbdaa5cc9348f5921e'), size_on_disk=6062382, blob_last_accessed=1756926366.8141773, blob_last_modified=1756926368.3051708), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417635/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d2d1c241467bd5c55cf059f793b4236c99d48e59bec853d1052ff6c644e50d1b'), size_on_disk=3044373, blob_last_accessed=1756926307.2014363, blob_last_modified=1756926308.391431), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417855/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ac2218ccb9f80664ce377351ea1dc84bf3f70b36006a86cffdb761a40ddab791'), size_on_disk=1981158, blob_last_accessed=1756926347.3992615, blob_last_modified=1756926348.3062575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417653/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/59233342d3c3b71fce95d5295cf7c1e5ef3f4254dbc120229cb83b6c8115c759'), size_on_disk=12208357, blob_last_accessed=1756926311.136419, blob_last_modified=1756926312.3904135), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380977/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/74237b16fd38090785d203d96bae72e1f5ce919d423f793cfb9d3b86623358e6'), size_on_disk=11114601, blob_last_accessed=1756926371.6651561, blob_last_modified=1756926373.12615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417894/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/12f262a12783317d135ffc5e1d281a3d43b746a82ed8053c15de824b94bd5ef8'), size_on_disk=6449687, blob_last_accessed=1756926354.4772308, blob_last_modified=1756926355.4742265), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417771/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8fe5513ae86b28ecca3d4c70bdb73097f578dd31b4d08a072960b61550f582fa'), size_on_disk=3970195, blob_last_accessed=1756926331.9313285, blob_last_modified=1756926332.8643246), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381070/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/32c8f4c4efdbe6e137add61b0e31c27ae6a9eb5e8b463e9e4410a27cc51a57d3'), size_on_disk=29864184, blob_last_accessed=1756926780.7487328, blob_last_modified=1756926783.3167186), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417764/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8a30e54d96d5b83981e37a0d8f6cae9e6584819a76a200f2730d9de32e282d02'), size_on_disk=2641764, blob_last_accessed=1756926331.099332, blob_last_modified=1756926332.088328), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417749/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8460c24102627d611c8918a1620078ee1bdf9929c44cdca863cd0dbb23f9cf1f'), size_on_disk=4938551, blob_last_accessed=1756926328.814342, blob_last_modified=1756926329.7143383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417924/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f2053fcaa7f21b1703e3491e167b457c29759b2fe9535f0cb2bfa257a1b86cea'), size_on_disk=6988370, blob_last_accessed=1756926359.940207, blob_last_modified=1756926360.8152032), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417732/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f99cdb10c12a75e1d020125d68b5c5302263eaef050b5c6b0032e27a0ac9a23c'), size_on_disk=685742, blob_last_accessed=1756926325.7463555, blob_last_modified=1756926326.326353), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417631/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b4b8c985528935ec46c6c7e78482af1ed26238137fc63b2196c6631e2d3796ab'), size_on_disk=27651513, blob_last_accessed=1756926306.2814403, blob_last_modified=1756926309.1994276), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380929/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aeddf7cf3aee11ee08c90d305e66fc682e675959c171ecfaa591bc1d9015ebbc'), size_on_disk=6656759, blob_last_accessed=1756926365.1011846, blob_last_modified=1756926366.0181806), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380975/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ea018f4882a60f5fb07a4bbae51bd494e44e6df9b215d9da5e405687a567dcbf'), size_on_disk=8369150, blob_last_accessed=1756926371.6051564, blob_last_modified=1756926372.7471516), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417727/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/414c702fdf3dd97c536f3237d2c38e0ef5ad8d08bb7c991ee252e16d57b201b6'), size_on_disk=2636905, blob_last_accessed=1756926325.0683584, blob_last_modified=1756926325.8093553), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417673/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/535d074f70f4553e24f3171fe5cfbd1821635ce7ca6ef93f8665876de620d6c9'), size_on_disk=6559693, blob_last_accessed=1756926315.1844015, blob_last_modified=1756926317.0733933), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417659/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/20f21bb1ba86fdbee1918776793b1ff2275be49933c72eaba90b8be8582f7692'), size_on_disk=15311232, blob_last_accessed=1756926312.2364142, blob_last_modified=1756926313.8474073), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417826/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3532f474125bbdc0445e46fe8be83801ca9c079afb1dedd7cd95e25994c50d17'), size_on_disk=11492099, blob_last_accessed=1756926341.9132853, blob_last_modified=1756926343.2502794), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417609/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e3d8451242fe9ecf9f82e2ac1a406144840a6182178228e6e1fa67e6a04a318a'), size_on_disk=3176651, blob_last_accessed=1756926300.651465, blob_last_modified=1756926302.0864584), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380942/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a958d997c1aff07ec07304485ee818280bc7714dd9c485ea73ef7fda81f2fe63'), size_on_disk=11407010, blob_last_accessed=1756926367.109176, blob_last_modified=1756926368.6211693), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380982/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d1e44b0b82ea22fb6c513ce02b49facf5f38b64b2baa91b24942fc6858125e37'), size_on_disk=6977161, blob_last_accessed=1756926372.8241513, blob_last_modified=1756926373.4951482), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417792/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/196cd0cf279f6bc85ca0dbeb1f089721f63eb167644d999b45d8cfe9a1f52acf'), size_on_disk=11982413, blob_last_accessed=1756926335.4593132, blob_last_modified=1756926336.651308), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417681/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6e4ea424e9eab4dc3ea304621992e1db158565a8ce30374980b1084a653a0132'), size_on_disk=7689435, blob_last_accessed=1756926316.934394, blob_last_modified=1756926318.231388), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417859/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e4c5915df6cb5bb450e1948d8c43bbe031d09ec345688ab51f2d8ea60f3be09f'), size_on_disk=12709203, blob_last_accessed=1756926347.8872592, blob_last_modified=1756926348.9972544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417692/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/82405dc0a21bd225cc6a9e813e62d4dc0522719abf1e677c954a6ec38763b306'), size_on_disk=4873514, blob_last_accessed=1756926319.6253822, blob_last_modified=1756926320.6443777), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417875/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8dc3634dae23cad7f562d15903341a67d3048929bd2198e53f71529b00a3508a'), size_on_disk=11490812, blob_last_accessed=1756926350.6762471, blob_last_modified=1756926351.7832425), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381042/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/80a0c18e8d146043e9c95c6a86552d28e28267a489b3e5c6f2a11319cf47b877'), size_on_disk=4477936, blob_last_accessed=1756926775.660761, blob_last_modified=1756926776.9177542), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381053/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6ca1dc55841bbb6d8d8a83d1a87d00570c1eea7cf1963db8cb04ddd427711fb6'), size_on_disk=10277654, blob_last_accessed=1756926777.9197485, blob_last_modified=1756926779.9187374), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417746/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/432442dbdd5578acedcbf6eddc14cfe0d460facc7b0dea857225fbd4e6a4242c'), size_on_disk=2726090, blob_last_accessed=1756926328.6353428, blob_last_modified=1756926329.1983404), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417628/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/291f0e4d2d0fa1656de1583bd9e131fd4d2fc1934d0bb70a6774f7e7f90e62ae'), size_on_disk=15444530, blob_last_accessed=1756926305.1314452, blob_last_modified=1756926307.1334364), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417944/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/01e0e606e4e8b2203e6497ffc8c231bce8672a43d83098ba838c8edfdb052c27'), size_on_disk=4563261, blob_last_accessed=1756926363.89319, blob_last_modified=1756926364.6951864), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417883/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8e23cc97e00adfd244a989f83702f4ce7a998ba7998dd95908a1b4ec256bc054'), size_on_disk=10709227, blob_last_accessed=1756926352.4542394, blob_last_modified=1756926353.675234), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380980/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e36f58da696f0827c72fed8647c2a45a0499fdc5d1859386dbf025388f07e16d'), size_on_disk=2288305, blob_last_accessed=1756926372.371153, blob_last_modified=1756926373.2051497), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381035/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/410edf5a6097a77bcec6880ec2618b4aec4bc10c44607163fe2b27aca0a05117'), size_on_disk=1451911, blob_last_accessed=1756926379.8691208, blob_last_modified=1756926380.5191178), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417706/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4f1ea5592a855f3bf5739b0caae1c69c7606276d125c3dfe8e98379d12bedf1b'), size_on_disk=1154142, blob_last_accessed=1756926322.9053679, blob_last_modified=1756926323.5173652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417655/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/25d98ba17be9b354b0ec7a0e513a6ff9d4593dd1cadf269f28bcea1ca9c71565'), size_on_disk=5461224, blob_last_accessed=1756926311.5504172, blob_last_modified=1756926312.9474113), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417790/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f88bed27521de8285bbb92cf95242820116e6fa2c23ab578e6608a6a25e0c04e'), size_on_disk=10836858, blob_last_accessed=1756926335.078315, blob_last_modified=1756926336.1843102), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417679/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bc48687cfd5c03c1a71787981180295dd3a27cf4d2c46bd26bb68cdf8b35925a'), size_on_disk=17346576, blob_last_accessed=1756926316.1203973, blob_last_modified=1756926317.84439), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380954/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/338886924ec42501a99a4b842e18fdbed28200d52daaf43e00ee601a147e0188'), size_on_disk=4903852, blob_last_accessed=1756926369.2841666, blob_last_modified=1756926370.101163), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417696/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2e1bce702520ca07d9037bd4de4d21bb30ef6b7fdf7de9d75ea232acc036a72e'), size_on_disk=31303382, blob_last_accessed=1756926320.7203774, blob_last_modified=1756926777.0657532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417627/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4027ab6ecd21364ca365bbe7192797ed924c817e6edc841d750111b2e3f6f45a'), size_on_disk=21089243, blob_last_accessed=1756926305.0144458, blob_last_modified=1756926309.6264257), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417763/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/91d8a58ead35fc52f7c2649ce9509a92236a20ca8c26f5f2ef4ba1a38e64afb1'), size_on_disk=230495, blob_last_accessed=1756926330.8943331, blob_last_modified=1756926331.4653306), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417902/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2d72fc510cd348af7611ca170b7804b228b74bd38e7ff5e437375c1cc926fa95'), size_on_disk=8952511, blob_last_accessed=1756926355.9322243, blob_last_modified=1756926357.2422187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417939/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a0fc7804b026f95809f33087deb75f00beeb9635475b62864d3e58e948361bfa'), size_on_disk=9099632, blob_last_accessed=1756926362.9701939, blob_last_modified=1756926363.9411898), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381038/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/415f62b8333a02df8a87ba85e1d0de30a6cc9fdb07f7c8dc02d30f81ed4c14ef'), size_on_disk=2424198, blob_last_accessed=1756926379.8751206, blob_last_modified=1756926380.4921181), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417783/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9603c95097c451e616c54d52d3474b866453409905ea16365d019e77c6ca31b3'), size_on_disk=4176778, blob_last_accessed=1756926333.6983209, blob_last_modified=1756926334.4773176), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417947/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/50bb9cc74904c9d3b6cc3ec8bc12c7ad1fb89204be6f89a863e703a94d224512'), size_on_disk=4023877, blob_last_accessed=1756926364.0161893, blob_last_modified=1756926364.9561853), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417823/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1887333f71d11fe7c43748662f58d9ba3fb12f3f6ca30a61d7e1c7a0c95f7064'), size_on_disk=9444476, blob_last_accessed=1756926341.6372864, blob_last_modified=1756926342.8442812), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417678/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/825d9bcd10c008b68cc84e92f20608c0d52770ac028bbc4a9219f1cd210ab8c4'), size_on_disk=29320273, blob_last_accessed=1756926316.0503976, blob_last_modified=1756926318.3993874), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417860/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/72e751d246e9b938ce9ab4380e2a1114ecd3c36d5829e3298f391c1edfefae24'), size_on_disk=3996315, blob_last_accessed=1756926347.9312592, blob_last_modified=1756926348.819255), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417932/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f040d4f594fe69143a7c3f04f0498be2a30fb9b5e78d42ec464fee65efcc42dd'), size_on_disk=9231914, blob_last_accessed=1756926361.4262006, blob_last_modified=1756926362.5771956), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417731/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/39ec5969bc8f3acceb3aa30b72f0c0101785a36da340f3983ad00d8cc80160a4'), size_on_disk=1557745, blob_last_accessed=1756926325.658356, blob_last_modified=1756926326.2043536), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417759/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b2dad4cf4b03c846adf9d5229aeab340e58034c751bc68b37bb0c8402a7c071b'), size_on_disk=1372312, blob_last_accessed=1756926330.1243365, blob_last_modified=1756926331.1623318), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417791/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8d2eb1bdd1a5cae023abb271e5858cf111879953cb0d6267f4ebf21a3d30c4ad'), size_on_disk=5232904, blob_last_accessed=1756926335.2033143, blob_last_modified=1756926336.417309), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417919/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f5504428a85399cb212c74235abf9cec068dba03136a87ea79eae6d63a3cf430'), size_on_disk=7011211, blob_last_accessed=1756926358.6892123, blob_last_modified=1756926359.8312075), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417911/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3ca8b1b14564fbc90b44995a61a34d5a67482eb5fd867ce4f9f2196de2b73539'), size_on_disk=3051177, blob_last_accessed=1756926357.404218, blob_last_modified=1756926358.3022141), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381027/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ba9aba2cc5b9bd9e83599ccba5920f028ac9c6936436dac50f45c606f3abd841'), size_on_disk=6537548, blob_last_accessed=1756926379.0151243, blob_last_modified=1756926775.5467618), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417779/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/918376b5cb0b63e3e531ac990854e5a7b461e7251f5ed562c5a92cbf6a5a0141'), size_on_disk=9459105, blob_last_accessed=1756926333.1593232, blob_last_modified=1756926334.2033186), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417926/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/303d44a38fbb646cf211ce1bd03fe2d24bb004f385c63d05ab8e707c5b283f0c'), size_on_disk=10766909, blob_last_accessed=1756926360.6072042, blob_last_modified=1756926361.6751995), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380993/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d568d29486ad3ec6f67f87b78cacb8b7102c591f979ec2bdb17ff75bde789670'), size_on_disk=3031778, blob_last_accessed=1756926374.496144, blob_last_modified=1756926375.2661407), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417857/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/64f710e0b8a62bb6eddf3508f133b6b82af8bd21dd07f2319af6d07981085b1e'), size_on_disk=11109477, blob_last_accessed=1756926347.471261, blob_last_modified=1756926349.6202517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417929/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/85b194132215311994e48908799e31357d1094663a232e8170a4edcfa5a11eff'), size_on_disk=9155231, blob_last_accessed=1756926360.9862025, blob_last_modified=1756926361.9231985), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417923/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a32971eb62fe5c4c08298b1bdd97a916d8ef8f5898aee42aaf3fbaa8069e92d1'), size_on_disk=10004280, blob_last_accessed=1756926359.919207, blob_last_modified=1756926362.0851977), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417890/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3eca80a3220fd0294fdfca83740ba70a20af764d28833494948705c4d542ded3'), size_on_disk=9018889, blob_last_accessed=1756926353.7652338, blob_last_modified=1756926354.7252295), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417739/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1c9e3fcff1dc467673efe70177cb4d678d7eff7b5b2bb3ded247d7a458b466dc'), size_on_disk=21974051, blob_last_accessed=1756926326.8623507, blob_last_modified=1756926328.7393425), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bd149efcdaa899981b965dc424d16fb66668a7666a059c6bc5043572331394c2'), size_on_disk=4513644, blob_last_accessed=1756926377.1751323, blob_last_modified=1756926378.1161282), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417865/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/348bfc1685f79e9ec7f559e96b61b8c43628d611378b6c4ead8bffea44415f1d'), size_on_disk=9233047, blob_last_accessed=1756926348.8982549, blob_last_modified=1756926349.9182506), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380995/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e0cae334f1cb71be0be1e8ceb5fc6ad23d22e4db20cb64b1314c3d080f2bb5f7'), size_on_disk=6315860, blob_last_accessed=1756926374.931142, blob_last_modified=1756926375.7271385), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417941/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/30188c510d2d7041e1525e304edc8f25b569a1771cf00844cb27300a052441e8'), size_on_disk=7032333, blob_last_accessed=1756926363.2551925, blob_last_modified=1756926364.2891881), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417824/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/65d997db1339fc5a8380a61fcf1ab1a6d28fb9142fc7d78c89236dc218b91ea3'), size_on_disk=8218856, blob_last_accessed=1756926341.7612858, blob_last_modified=1756926343.0512803), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417626/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1981a2a1e321acc5c35bbeb9b61a12636c27f28d6e216de20093afdbee9f8689'), size_on_disk=12046611, blob_last_accessed=1756926304.4464483, blob_last_modified=1756926306.2084405), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417832/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/268a3b7a91ccc3f3909042a711454c336b1887705de99442bda022bcbc26a01e'), size_on_disk=7655304, blob_last_accessed=1756926343.2332795, blob_last_modified=1756926344.3042748), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380946/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62fe0084bf7a8f9f484e76366f203e1c68862a211d5d501c08e19b8f28678d6b'), size_on_disk=8090103, blob_last_accessed=1756926367.6871734, blob_last_modified=1756926368.7681687), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417722/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f29055ed874a3a40735526808a6739884efecd8d7c5e7e9eb1fcaa8e495ade3'), size_on_disk=345818, blob_last_accessed=1756926324.5753605, blob_last_modified=1756926325.2483575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417633/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0b38824aeb2fac57eca256e3652539deb4bc221eeb5afc3701a997885f9ad9d0'), size_on_disk=19311587, blob_last_accessed=1756926306.8324378, blob_last_modified=1756926308.847429), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417821/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c29db68a3430cf8cb01716d90a92105387777ed1361992eb9501271c35359cfb'), size_on_disk=6597050, blob_last_accessed=1756926341.1692884, blob_last_modified=1756926342.2702837), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417690/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/283af9b17d776b75f6ff6ad1f426d6d0e0e5aa1f2089a20910edcb6763191b69'), size_on_disk=12265448, blob_last_accessed=1756926319.171384, blob_last_modified=1756926321.4153743), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381069/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aa8f6186814bf973f8b4448a0c4b692c31594e03905c75d5691b982d6ccde97d'), size_on_disk=13705106, blob_last_accessed=1756926780.5807338, blob_last_modified=1756926782.319724), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381011/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/839fea471c67e08e0b68ae6344432c76da559e67c505c3f6120b20db2f1f753e'), size_on_disk=4378212, blob_last_accessed=1756926377.2971318, blob_last_modified=1756926378.0891285), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380952/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3811d6e6194d8b88c84fc0931cba844da23677e76e1e9a25e2890df968a7e529'), size_on_disk=1003172, blob_last_accessed=1756926368.8351684, blob_last_modified=1756926369.4641657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417660/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c394d7b0cfca3f3782dca86380e1a55903111d32efde98173af8a0b726ef5e7d'), size_on_disk=6441490, blob_last_accessed=1756926312.4744132, blob_last_modified=1756926313.4024093), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417811/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/da92b1c1579a2295da085050305367bd02a47fd52a74d1ec2a9f227b5e808b11'), size_on_disk=6127159, blob_last_accessed=1756926338.8782983, blob_last_modified=1756926340.1172931), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417829/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/63a9af789279180a07d8e8afe07bb0e9b8aa32c4340e01347a6f7ee809198308'), size_on_disk=5677879, blob_last_accessed=1756926342.904281, blob_last_modified=1756926343.7332773), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417716/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cd98cf97346b62e4f1efcace598c9c273d9ad9b8d8692e35f50b11d46f7ed8a6'), size_on_disk=5100666, blob_last_accessed=1756926323.8513637, blob_last_modified=1756926324.8793592), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381039/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5f95b6de8d0d73589c612313eaed6ce1e6adf734e795719f23df6f54cbbedc69'), size_on_disk=1656495, blob_last_accessed=1756926379.8751206, blob_last_modified=1756926380.283119), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417872/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7d71ca12594ab85797fca8c1b88947cb0f3ece93596f1a4c5adc04c11c0abf68'), size_on_disk=8522283, blob_last_accessed=1756926350.1742494, blob_last_modified=1756926351.180245), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417695/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d6ae8d39c2949235dd4d82e7a0e56d9f20c20ba5f1b7a445fbba1f0df29c88b7'), size_on_disk=7710772, blob_last_accessed=1756926320.4913783, blob_last_modified=1756926321.959372), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417887/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/58707813ec63af2eb1a72b0c8317b4701b4777df8f828f2e08a0cbb1ccfa534f'), size_on_disk=9080847, blob_last_accessed=1756926353.0712368, blob_last_modified=1756926354.412231), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417709/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/395b1dd171ab9759e06a22580672bef65aac2acd7d548e38098d207c80071d89'), size_on_disk=717131, blob_last_accessed=1756926322.9983675, blob_last_modified=1756926323.6373646), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381023/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/22e3e0f4cd1f2c755ec1ac13b99d2d6c98d0158319478f2deca4e89aefc5e88e'), size_on_disk=3860195, blob_last_accessed=1756926378.5651264, blob_last_modified=1756926379.6101217), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417949/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6123bc7bd312e6e37fabadee561c7dacad32d26bada92c9785c783f94fc50a37'), size_on_disk=1320707, blob_last_accessed=1756926364.4151876, blob_last_modified=1756926365.0891848), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417625/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/96b04f28e74e25bd1502a4ea9aca8047bfa8462e61cced0d726af4a336e1a585'), size_on_disk=17384720, blob_last_accessed=1756926304.4014485, blob_last_modified=1756926306.2074406), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417853/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5995033a04f017892b30c025a1527f9b6420bf1e7636c466cdd8d1462cf228c8'), size_on_disk=2186899, blob_last_accessed=1756926346.9012635, blob_last_modified=1756926347.8172596), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417713/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c5d75f4a32cadceee844da0c5ce703483f575fb927990a1615ee37205137d986'), size_on_disk=524444, blob_last_accessed=1756926323.533365, blob_last_modified=1756926324.045363), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417921/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/05fb4ff0001b88b711eee9f29ae30642d940667f7166121c9782f464057880f5'), size_on_disk=7891276, blob_last_accessed=1756926359.3782094, blob_last_modified=1756926361.2442014), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417611/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c82d882f6538d65316dd01e26a82af13e41717952c79e20dae6c3f1d9724ab60'), size_on_disk=3720891, blob_last_accessed=1756926301.4614613, blob_last_modified=1756926302.2704577), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417684/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eee25a9a91c54d0404db884532d1b85555f2e1de0c664abcbda4343f9d729842'), size_on_disk=10310520, blob_last_accessed=1756926317.7173905, blob_last_modified=1756926319.3473833), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417772/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a21b204a2da7654dd5bf4e7965a3c1e19159c16052232030787175a830ff0233'), size_on_disk=8204405, blob_last_accessed=1756926332.1323278, blob_last_modified=1756926333.453322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417725/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4195b9ed6506437a735fd8061cd1b79211c99169c4319c4f72b801a501f2b0f9'), size_on_disk=2233032, blob_last_accessed=1756926324.958359, blob_last_modified=1756926325.594356), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417820/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9d36f1c17151e3203186142abff734c0182377d5287b3f27d00917608501abb3'), size_on_disk=8116588, blob_last_accessed=1756926340.9562893, blob_last_modified=1756926342.0902843), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417869/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8dd08ab28007f90a664a6fbde23298ae2bb0ded917d5d75b1275c41e3eb5f7dd'), size_on_disk=16604892, blob_last_accessed=1756926349.6872516, blob_last_modified=1756926352.7202382), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/059cc8a8b1f7918ab1b6499d3d9843100b3b37fe6dcd57dfe9ae512c6030cad6'), size_on_disk=2109635, blob_last_accessed=1756926376.7021344, blob_last_modified=1756926377.469131), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380996/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/09e663ce2a60df5635f4e4ea77d6350e7752a9bb5e81fbdfd818869ac53ca805'), size_on_disk=13309208, blob_last_accessed=1756926375.0511415, blob_last_modified=1756926376.0221374), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417654/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/67d658902a2b91d0d36345be06dc00c85af1b88ba40c37deaa091705d97b3ffc'), size_on_disk=6323092, blob_last_accessed=1756926311.166419, blob_last_modified=1756926314.4344046), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417885/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6d5faeda491268a8ec88393883d743aac6cd14d4837698164ca32ca1052da36f'), size_on_disk=7668243, blob_last_accessed=1756926352.806238, blob_last_modified=1756926354.1422322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417707/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/89192f5007f730a90c823219ea107c799935f5e824963090abad813603160fa8'), size_on_disk=2615872, blob_last_accessed=1756926322.9183679, blob_last_modified=1756926323.9473634), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380987/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2eb045d9f88b952eed8f8354e77c682bc3003bd2e9eacb0eb0c2df5ca44a34cc'), size_on_disk=4463796, blob_last_accessed=1756926373.559148, blob_last_modified=1756926374.4241443), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417617/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9ad746ec09ed51ed497d87f3dd64b11916b70e6833ce66141bc8feb6ba73d1d3'), size_on_disk=23082279, blob_last_accessed=1756926302.1754582, blob_last_modified=1756926303.6944516), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8168980e59c50149bfb9866a1e8855c235f2882b988df99331c9247f65e9813e'), size_on_disk=2289686, blob_last_accessed=1756926376.4721353, blob_last_modified=1756926377.2151322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417917/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2118491b5ba6b4af1c0d394eec3139b0258cf3a4e42b4d1a02ab177247852081'), size_on_disk=14019020, blob_last_accessed=1756926358.4622135, blob_last_modified=1756926359.8632073), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381036/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ff76dabe83d2948240dd846fcc2a969380b7bd9f8d2eff2c71c299f0b14134cd'), size_on_disk=2593393, blob_last_accessed=1756926379.8661208, blob_last_modified=1756926380.5611176), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380984/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a15ab5e5a0ead07dc2a530a1e4abb714027a95ce090c7a0eda5ec2bb714d903f'), size_on_disk=8070704, blob_last_accessed=1756926373.2251494, blob_last_modified=1756926374.1601455), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417726/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/739e474fc54ffee0c7e6300d3abb1f83d33d48c78d137ed848f837fa186b9308'), size_on_disk=2430088, blob_last_accessed=1756926325.0663583, blob_last_modified=1756926325.6803558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417712/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/59cbfa83b9abe13f60d6a80b35afe857e3ffd761366133f60505312a2b5a72e2'), size_on_disk=641243, blob_last_accessed=1756926323.2163665, blob_last_modified=1756926323.7173643), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417934/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5816750a355a277e532d46ffa5159f8a2ec1ce66686fb12f07ccab4b4f7b3c98'), size_on_disk=7324082, blob_last_accessed=1756926361.995198, blob_last_modified=1756926363.89619), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417774/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ad00f794fb29d2a609375d63b09170f396f055ae392cd05a31d5f8fb5ede7a33'), size_on_disk=2914273, blob_last_accessed=1756926332.1763275, blob_last_modified=1756926333.217323), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380997/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/add73ab9723ef88a47192fa169b89aaf17c16a0841fc9e53c1c5d73cfdc2ad50'), size_on_disk=15536494, blob_last_accessed=1756926375.1291413, blob_last_modified=1756926377.6141305), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417661/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c6effb515a20066646b7863cb86e9dc012e14187e432ba7458cd6af18662bfd6'), size_on_disk=22122748, blob_last_accessed=1756926312.9054115, blob_last_modified=1756926315.6813993), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381037/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5cef560daffa3a5b1e841a312937eca595ced0bbdb3e6c641b13359a0340fea1'), size_on_disk=2090047, blob_last_accessed=1756926379.8821206, blob_last_modified=1756926380.4171183), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380976/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d27d3efdccd104ca208d69fe9d94fe9011cb6212b34739679b1c8f550c5e9df2'), size_on_disk=12889551, blob_last_accessed=1756926371.6141565, blob_last_modified=1756926372.8021512), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381066/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ae017c72cf3b91ea3fdd40f63a126f640e7049340baca502ff3228c6ee40a7b4'), size_on_disk=13139272, blob_last_accessed=1756926780.000737, blob_last_modified=1756926782.6217225), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417668/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b426513aab4e071a24b7d27100a4da4da8c71c8e9b3e82163808b0ec6a42b8d0'), size_on_disk=6011682, blob_last_accessed=1756926314.5374043, blob_last_modified=1756926315.4354005), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381022/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/980ed07a51dfad581fc2f7bce44031c92445822ba7e994cbfc9b5508cdf97c85'), size_on_disk=2103246, blob_last_accessed=1756926378.5151265, blob_last_modified=1756926378.9441247), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417862/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cea0cfbd611f8c6482ec33f8ea32f304a481d1c1225d3dacea432dd359fc976b'), size_on_disk=5244392, blob_last_accessed=1756926348.381257, blob_last_modified=1756926349.6162517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417868/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/52e3f224a0147264d4ce477d26f4e11ff0745c328eb3e2ac44209dba1c8c55eb'), size_on_disk=5039768, blob_last_accessed=1756926349.6852515, blob_last_modified=1756926350.6082475), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417845/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ca2b89837de406b10d72533a3e3d5457b3512b704f7cea395fb0e1b88828393e'), size_on_disk=4648586, blob_last_accessed=1756926345.6932688, blob_last_modified=1756926346.5862648), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417895/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2daca9b311da1afa40f578c6df7dc4b0039dd4857663c98cd87c665a547b7144'), size_on_disk=11915424, blob_last_accessed=1756926354.7232296, blob_last_modified=1756926355.8262248), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417908/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/128e54326b088977000f8caaba7d09e324281ed7fe0e3e5f2779783f0a82f293'), size_on_disk=3614805, blob_last_accessed=1756926357.1272192, blob_last_modified=1756926358.2542143), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417744/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/faf407f0b251cf5f58c8ac7becee4b995327fd9a7d6599c3045fde8a78e1e811'), size_on_disk=2548979, blob_last_accessed=1756926327.7843466, blob_last_modified=1756926328.7443423), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417736/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/671a67d7275810840cafc9db32e43385c4ba61b007d702e7eb5cb244a0e4afa9'), size_on_disk=13855124, blob_last_accessed=1756926326.1693537, blob_last_modified=1756926327.261349), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381030/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e5baa8386faa327b26d27d6c8e6b265dddac1d55565e81d724a1980b93d871a3'), size_on_disk=1867415, blob_last_accessed=1756926379.2021236, blob_last_modified=1756926379.6681216), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417841/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cb829be078dd8b533a90c2c548dfb2ce0455026be54c36572337e0a1ddeeb2cf'), size_on_disk=5791736, blob_last_accessed=1756926345.138271, blob_last_modified=1756926345.9902675), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380989/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3c8b646955209386a1d97882163b7c9816463d2dea68fbed2cc960817ffeb332'), size_on_disk=12022144, blob_last_accessed=1756926373.7761471, blob_last_modified=1756926375.1511412), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417634/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/529d984782684991b2eb7be2d345b5a4e8a6e08d3dbe30ad34495d8380464e6e'), size_on_disk=16306189, blob_last_accessed=1756926306.810438, blob_last_modified=1756926310.239423), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417789/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6bd6339b995a5730b6b65ec65f680b57195469ebee9f2e15de6e604b069d4b0e'), size_on_disk=9612750, blob_last_accessed=1756926334.6493168, blob_last_modified=1756926335.8183117), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417604/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e6dd3abdf75462f1f5d16f19383b98a8a4c817dc3b13c7cea2b4bea611c40c62'), size_on_disk=961320, blob_last_accessed=1756926300.390466, blob_last_modified=1756926301.3534617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417621/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/354c654ace9bc123bcd9009c56af518b98f1a547a7ccd79f348d0e533607a41e'), size_on_disk=20457307, blob_last_accessed=1756926302.5744565, blob_last_modified=1756926307.7874336), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/225bf60f7c9796682210dc48d07a30d3dabab8ebc13abb7a84834279fa131020'), size_on_disk=5307353, blob_last_accessed=1756926376.2401364, blob_last_modified=1756926377.3751316), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417622/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/28eed3b67a6206b453d8adb775edf2bea32cfd54ba5b37d51787f67280e0317f'), size_on_disk=20411775, blob_last_accessed=1756926302.674456, blob_last_modified=1756926304.8564465), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417646/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2b3d78fc052de8647b1ba6e83245b5f375b4e6c10952feff795855160458a1d5'), size_on_disk=396843, blob_last_accessed=1756926310.1544235, blob_last_modified=1756926311.0694194), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417786/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdd4770c9b3e18e86f9a46ff924e3a562e37b632d6f0e8ad11627956a3416e09'), size_on_disk=11354740, blob_last_accessed=1756926334.3243182, blob_last_modified=1756926335.4083135), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417704/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/477a83e4a5eace957a036c5ec65ef4539442c078058c1a44a877763cb850c86d'), size_on_disk=459034, blob_last_accessed=1756926322.1393712, blob_last_modified=1756926322.8993678), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417718/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3ba23a0e7c5b3a455e95fbf6a22b51687d5330c19207241bdfd189e3776f5ba3'), size_on_disk=3304954, blob_last_accessed=1756926324.0233629, blob_last_modified=1756926324.986359), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417683/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6131abcd72bd4c6a8627685c4df8a295af50e824f52a5cde156adae7cd694bba'), size_on_disk=15545733, blob_last_accessed=1756926317.382392, blob_last_modified=1756926319.0483847), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417918/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9f1cf3832969364b6cee1c817d3583a554b765bdbc1ff3f5b29692b7ea98ee7e'), size_on_disk=6936136, blob_last_accessed=1756926358.5802128, blob_last_modified=1756926360.9132028), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417922/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fba17c65fa210922e57c533f5146b4237677149a5022fb836c1cd52dc89855b2'), size_on_disk=10194446, blob_last_accessed=1756926359.5812085, blob_last_modified=1756926360.610204), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380951/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b9b68679dd2da94e4aa4852ae3de3ee5b823c1fb1483f4ece3eee5d947014785'), size_on_disk=1154119, blob_last_accessed=1756926368.8281684, blob_last_modified=1756926369.410166), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417801/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4bbb1b3faac1a4c15eaac8c81260f4f18493d2a082915cbe4c480ce76a94140e'), size_on_disk=4600965, blob_last_accessed=1756926337.090306, blob_last_modified=1756926337.9153025), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380947/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3d6062f97d18f4945c36590355a312df2a16206c26626392cfb44d9613aea49d'), size_on_disk=9201129, blob_last_accessed=1756926368.5321698, blob_last_modified=1756926369.383166), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417737/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2e1d5b2306dd24257d4ba261baa42b324c9f4f8ca3a1d30e0f920c4f4a5f99ba'), size_on_disk=1342511, blob_last_accessed=1756926326.2713532, blob_last_modified=1756926326.9043505), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381057/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4e6c617d22a655d32847df8b5334f4fb7236a15b8bf6e30aaabcbb0a4c55782f'), size_on_disk=17494867, blob_last_accessed=1756926778.5417452, blob_last_modified=1756926780.4997342), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380936/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5a931bc1181001314c3822051c493ba7a8b87b5bb26b8866783612f2bcf49c62'), size_on_disk=3467608, blob_last_accessed=1756926366.19118, blob_last_modified=1756926367.2261755), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381017/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1d9b2dbb5079eca871182c2dce1d681b943f1362072c18e255a4bd6d1068b840'), size_on_disk=2436549, blob_last_accessed=1756926378.161128, blob_last_modified=1756926378.7931254), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380971/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0147090dd5a74f558b67151503ff635423a8af661b56d1faf06bb50a59adfb3a'), size_on_disk=1276600, blob_last_accessed=1756926370.9411592, blob_last_modified=1756926371.462157), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381071/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cf15be48b47b6c369c6a4290b783b9b22b01d6cb89d04c738c275241548b811b'), size_on_disk=2543373, blob_last_accessed=1756926781.2297301, blob_last_modified=1756926782.309724), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417676/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/53c35af59283469a22148b2841cb8574faf80d59162a33d269560283c9228af2'), size_on_disk=12006227, blob_last_accessed=1756926315.8083987, blob_last_modified=1756926316.8563943), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417800/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8e563a7d64bf06e7fe26c2817271f626273eed4c7dd19db0b9fd8e14c25f73b3'), size_on_disk=9904697, blob_last_accessed=1756926336.7953074, blob_last_modified=1756926338.958298), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417893/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f3b9f2c711cb806350320b93dba528efd6433d0ca10c39e5e2dd6aa89a408bd'), size_on_disk=9896637, blob_last_accessed=1756926354.2372317, blob_last_modified=1756926355.535226), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417805/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/661f37ed294c22f6c3b02d5c1ea25794f5e33090c246185e891ee82153e2e5f2'), size_on_disk=3071166, blob_last_accessed=1756926337.8643029, blob_last_modified=1756926339.3292964), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417884/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ff7d3efa4a231f5130be299d8d0ff67fe4973856e4db18e8a6c53d47ba6b28a7'), size_on_disk=11684836, blob_last_accessed=1756926352.791238, blob_last_modified=1756926354.0932324), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417642/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/591f688d0d9f98350a1d636e6f93ed37563f3ea9765ca6b633490533b0cf32d4'), size_on_disk=11498542, blob_last_accessed=1756926309.2804272, blob_last_modified=1756926310.8714201), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417671/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bced5a56092fea7de5754bebc33caaab643f27236f8d9020e7bc6b30b9045d6e'), size_on_disk=5347180, blob_last_accessed=1756926314.7704034, blob_last_modified=1756926315.8293986), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417879/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6714b3f5155dd540962a7541625f45e14e6a93b4c01e913790b70818e50c821b'), size_on_disk=8601872, blob_last_accessed=1756926351.4612439, blob_last_modified=1756926352.8182378), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fb5958ec4e10f5c7e19ebc55b24f26012e6d8ccf'), size_on_disk=8004, blob_last_accessed=1756926300.2594666, blob_last_modified=1756926300.3214662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417815/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ee8a37cd03dfe154008718f2a0f7fd95b3fdf468f99b1fc73ca515433fa45930'), size_on_disk=6563669, blob_last_accessed=1756926339.6062953, blob_last_modified=1756926341.8062856), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417912/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4451877e599fa65e1f00f67b5fcd41fe74719d8bcb19ddacac419fb347c77cf5'), size_on_disk=4943909, blob_last_accessed=1756926357.7152166, blob_last_modified=1756926358.6302128), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380973/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/343eb57057b5af866386fb4c02d0c41b888e206f5473ed7b4041200cfb93dbd5'), size_on_disk=1511721, blob_last_accessed=1756926371.3611574, blob_last_modified=1756926372.4931526), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417700/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1240fbb2497074bb7ff98b1bc6399b8a8ad5d320246285abfb6738e27e2bbdeb'), size_on_disk=19312447, blob_last_accessed=1756926321.6763732, blob_last_modified=1756926322.8383682), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417877/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b170370a220db5677b9c6193707d32d1d14b9082ca574b9562c4ac8ea4c72c26'), size_on_disk=10449263, blob_last_accessed=1756926351.0392456, blob_last_modified=1756926352.32624), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381060/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1bb05aa71fe55db65fc3868b131854c1e6666dca8cc370874f517aa0756d21ac'), size_on_disk=9137992, blob_last_accessed=1756926778.9637427, blob_last_modified=1756926780.2307358), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417785/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/209799965b03585e1143e881d87c5cea80d37e06637d2fc3f7e1ad2eaadfd3bc'), size_on_disk=11557515, blob_last_accessed=1756926334.1813188, blob_last_modified=1756926335.1353147), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417762/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c8b34d14be6dd44af03432d758f705cf81e86a46831c6deb4b0944248709630a'), size_on_disk=3024339, blob_last_accessed=1756926330.943333, blob_last_modified=1756926331.59933), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417699/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0c64ecbe6b245299fe04c04239d792fbae0f1b288562fe0c7912a24de56f0ad5'), size_on_disk=5707915, blob_last_accessed=1756926321.6203735, blob_last_modified=1756926322.8483682), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417838/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0dcd4dc268902c8e0845a1ee81938a81a00412014d5b198a41a523cc3450dfc4'), size_on_disk=10178872, blob_last_accessed=1756926344.5452738, blob_last_modified=1756926345.3902702), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417702/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/41123195fa339a4ffabf6d686a284c343d0519089a18b828818c92ec43a26eae'), size_on_disk=6213776, blob_last_accessed=1756926322.0263717, blob_last_modified=1756926323.0323672), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4c4a265f9e3e8d373550ea58ca1cde6094c823f090336974fde3e7cee117fd99'), size_on_disk=1770007, blob_last_accessed=1756926376.6771345, blob_last_modified=1756926377.1011326), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417608/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d225f561536fca4d073d2418a32efc3df68e9666a3403c7d8b1ee0a520861322'), size_on_disk=873305, blob_last_accessed=1756926300.603465, blob_last_modified=1756926301.521461), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417778/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/54e8ea03e214967eb4a1c7b50a086c4d7083d2c9c95dd0419e545fb0e8f18422'), size_on_disk=9345257, blob_last_accessed=1756926333.1513233, blob_last_modified=1756926334.0903192), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417624/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/28c1de2a4bd6b421cd05c3bb211ffde83e4b830127f106ec7be42ad288e26ef6'), size_on_disk=16758887, blob_last_accessed=1756926303.7884512, blob_last_modified=1756926305.6184433), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417641/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/355d8669b1d4c5576d8aa900582800fdf1819360f0db91d5e0299342b49884fb'), size_on_disk=6322689, blob_last_accessed=1756926309.3414268, blob_last_modified=1756926310.7974205), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380949/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/65090ef93158269c98bc2e747e505cea9d460f0959d0b2957e58543cd1944163'), size_on_disk=2110872, blob_last_accessed=1756926368.705169, blob_last_modified=1756926369.142167), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417658/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b6cf4c024743be14fcd5730f0ddbb960dbcd83d07adf8668639f9a1cdf195368'), size_on_disk=8754108, blob_last_accessed=1756926311.5764172, blob_last_modified=1756926313.1134105), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381063/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1e4361a7d20f8e92e8efc238365e0ed06eb021243864321c93d4638fa21634c4'), size_on_disk=10916323, blob_last_accessed=1756926779.3497405, blob_last_modified=1756926780.685733), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417697/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/198a3cd31740f347f0308db2914df4fc0fc303b4a9f040fbaf79fb5ca0170e7f'), size_on_disk=5521438, blob_last_accessed=1756926321.4153743, blob_last_modified=1756926322.4743698), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417809/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/486100d34df42ce97476d9753f8d57edb5f53596eaeca2d0cf81f5f95a4f4e12'), size_on_disk=7376702, blob_last_accessed=1756926338.5872996, blob_last_modified=1756926339.9172938), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417711/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e30dbc09cd2dfb4722d60d0833123542e9c5096f70b97867cd15d466aa7b927a'), size_on_disk=96859, blob_last_accessed=1756926323.1863666, blob_last_modified=1756926323.7823641), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417925/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8cdb783613764a35fd3a73db89463db6876951d443f5651a9a46877a53917f3f'), size_on_disk=912848, blob_last_accessed=1756926360.409205, blob_last_modified=1756926361.1532018), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417640/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/411d9146359847b5805450f67cbde4f1cbd51e3c2fae4bbcd6295db7426cb2ca'), size_on_disk=7000528, blob_last_accessed=1756926308.9124289, blob_last_modified=1756926310.0344238), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380953/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5b3410430d33f3123b935982be90037f2d0a8e657f6b0b70a7042846d5ddd1b0'), size_on_disk=1192961, blob_last_accessed=1756926368.8401685, blob_last_modified=1756926369.627165), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417657/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62398c518588ad11a939f95736cf0ed01a39890567cd4a8f05e9c9ba1155f9c1'), size_on_disk=10144285, blob_last_accessed=1756926311.5634172, blob_last_modified=1756926313.1174104), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381014/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5c5106dfb24bcfb1436aeb87b8218df873417ca2809aed71ca58e9b48ffacd29'), size_on_disk=1425306, blob_last_accessed=1756926377.5811305, blob_last_modified=1756926378.2571278), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380994/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7c5f237d9c32233a005e39386360f02d9e1932f9ac267ac3458f96aaf59e01ef'), size_on_disk=2496053, blob_last_accessed=1756926374.8051426, blob_last_modified=1756926375.3471403), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417742/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1a87c492aab479dd0c013fbe80e006f18ba80de751da600b95babcb3f6e88dad'), size_on_disk=2054520, blob_last_accessed=1756926327.3293486, blob_last_modified=1756926328.4213438), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380999/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/acb2ee4711dab5800afb7ccbea72c8c80268b0a848723e7bd8ecf197decb982d'), size_on_disk=2605563, blob_last_accessed=1756926375.2281408, blob_last_modified=1756926376.1321368), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417729/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8383a0e86c2bfe15c46045980d6768eb4e7e3c4c157ff1ae7ce20144e245ca80'), size_on_disk=3058876, blob_last_accessed=1756926325.3113573, blob_last_modified=1756926326.103354), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381048/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/53cd017f084781da16e69ae01b406ac0d8824a4d991e39b22c428e664a3808f2'), size_on_disk=2181901, blob_last_accessed=1756926776.9907537, blob_last_modified=1756926777.7067497), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417740/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e91586589c967e7bcbd521989874b21c024da79365d0d9916ffc66b774043e01'), size_on_disk=11875677, blob_last_accessed=1756926326.8903506, blob_last_modified=1756926327.6843472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417835/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6245fc8d220aad556115d4b831f72ae6a9beac48ed6e2aba811284aa0ccab4f7'), size_on_disk=7888215, blob_last_accessed=1756926343.795277, blob_last_modified=1756926344.942272), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417705/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/92b4f5af6793c98b854bcca83c86ac0cb41ec4e40ec23d3ba9c4ff287852c63c'), size_on_disk=332184, blob_last_accessed=1756926322.5923693, blob_last_modified=1756926323.1503668), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381059/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/59daa71f55c9c0da6f8d56fc2497491477312fbdbcb9e92d4951874f2ed72686'), size_on_disk=13124154, blob_last_accessed=1756926778.926743, blob_last_modified=1756926781.8157268), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417852/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/43431a444b2761ed9b6fa4c7e6edfc8da31b64ff933408c517bc771e9f3e94dc'), size_on_disk=6967416, blob_last_accessed=1756926346.8272638, blob_last_modified=1756926347.8462594), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417748/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/db4c67c001a1f688f70918f0e25d3ddd996392b33b9f1144a14551bc0026d036'), size_on_disk=3246481, blob_last_accessed=1756926328.8123422, blob_last_modified=1756926329.6943383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417892/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7ac6b85018422c74c894467b7b53b1c2dc7c5298fc31654bdea5ca70493e0a87'), size_on_disk=8194725, blob_last_accessed=1756926354.176232, blob_last_modified=1756926354.9712286), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417906/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/34d3381dd45e579dcc298e91cc95174c4219cf548401b8f2392cf746fd508c5b'), size_on_disk=13795318, blob_last_accessed=1756926356.3642225, blob_last_modified=1756926357.640217), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380927/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/38e67ea2fef57610ff63c0c298a6db65ae1c0a159f2b1e61940f2e65306b8fa9'), size_on_disk=7297320, blob_last_accessed=1756926364.969185, blob_last_modified=1756926365.7781818), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417753/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a8969c75211310735ad33d5af57327f2babc86276adc7cefcda254cc9fc9baa5'), size_on_disk=6433095, blob_last_accessed=1756926329.577339, blob_last_modified=1756926330.4023352), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417817/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ccf81aa26021a009a212ef8f8bee71c12f5c8d0a2b97ffdb2c4d86d13c8b8133'), size_on_disk=7521086, blob_last_accessed=1756926340.0802932, blob_last_modified=1756926341.3222878), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417816/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/86256a1b98c6d44750fd21ac9ea099262f1f0ec03d182b65741d669fb80f289e'), size_on_disk=10618044, blob_last_accessed=1756926339.7852945, blob_last_modified=1756926341.8372855), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381041/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a53caa1961d9245d82db10989b8a8d601760ccf26f67666d5558937830c51e31'), size_on_disk=6621865, blob_last_accessed=1756926774.784766, blob_last_modified=1756926775.9747593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417878/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d3d105cc45280a5ea88c2bba754c0c5849d0cec7112a0926e9ed0c946e7f5cfd'), size_on_disk=8838404, blob_last_accessed=1756926351.2492447, blob_last_modified=1756926352.7352383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381043/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f6ded8e6496dcfdd499edc382d19da2071f25daebbb250b375b9a2706e9d8211'), size_on_disk=9139336, blob_last_accessed=1756926775.6057615, blob_last_modified=1756926777.1057532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417645/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/761eba472193b23cbf7735946e08aa1c4db5b1abd5125f1aad22ddabbf872f04'), size_on_disk=2080044, blob_last_accessed=1756926309.765425, blob_last_modified=1756926311.5084174), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417623/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/562a43ff381a2ca177fb1cc3dbe47fc2cf7cade495bad511ca7ed75aefca7a98'), size_on_disk=19049552, blob_last_accessed=1756926302.696456, blob_last_modified=1756926305.4934437), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417897/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/309aab637d037638082524649ef7b0828885a98e786c64af01072d68968b7d66'), size_on_disk=7857749, blob_last_accessed=1756926354.8022292, blob_last_modified=1756926355.9902241), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417873/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d282d59c3c191bd7b7fa7251108224d0ac3929bc98f96a2c43f283f8a5cd046a'), size_on_disk=7609540, blob_last_accessed=1756926350.3852484, blob_last_modified=1756926351.373244), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417825/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/efc4971b63b21d6aee96e17d9e9b2cc2e3fd34608cc3722e36032c594fc03e48'), size_on_disk=8550531, blob_last_accessed=1756926341.8732853, blob_last_modified=1756926345.0782714), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417927/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8e639025209cff8d34b7e35a5fa371282bb725173225097a39f8f2101a49deed'), size_on_disk=7241057, blob_last_accessed=1756926360.6762037, blob_last_modified=1756926362.1171975), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417913/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/35c1a160b767f9f5870216ed33a1dc24804690772a5baee58f623c6dc0632127'), size_on_disk=9135350, blob_last_accessed=1756926357.9462156, blob_last_modified=1756926359.3142097), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381064/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8569620c4d2efb8dd163aa69d8edf8f855b96eba637e754a126a88e6f0c73192'), size_on_disk=20598404, blob_last_accessed=1756926779.800738, blob_last_modified=1756926781.1587305), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381061/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d44d57e9c03bb6e6e750809eea88657c2960ed251b8acaa1352a07f4cb346a82'), size_on_disk=3789431, blob_last_accessed=1756926779.0527422, blob_last_modified=1756926779.8977375), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380958/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ef3849004b6c72205ea1b298cf6a586f1114061ca94925229eda191d4666885'), size_on_disk=3288900, blob_last_accessed=1756926369.617165, blob_last_modified=1756926370.303162), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381049/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c571bcc5f87a79c7cd38b9614ee83b0d5cc742094e17a7990c829394fca193be'), size_on_disk=3753119, blob_last_accessed=1756926777.145753, blob_last_modified=1756926778.2097468), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417915/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9fbfc50c3e1f756169d25442880ba33e5cf237e3c83a0d674b64ace47e8d3322'), size_on_disk=13801295, blob_last_accessed=1756926358.318214, blob_last_modified=1756926359.4592092), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381024/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5182750a0c8af8253cc98ecad8715519463d5c18adbe067488dfef91198c0d80'), size_on_disk=603678, blob_last_accessed=1756926378.7151258, blob_last_modified=1756926379.128124), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381052/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/652334151cb539b888d9cfcb60d9711d80bd74ec65cfa3799de10b2970ab9c09'), size_on_disk=5788579, blob_last_accessed=1756926777.7767494, blob_last_modified=1756926779.283741), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381065/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/99cc73bc4c0908da04c01c7c59eff995a478ffc023c31fb970c7bb9789b2d70b'), size_on_disk=14673323, blob_last_accessed=1756926780.0087368, blob_last_modified=1756926781.5847282), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417848/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eec4ffff5d145c0ee0850d5e68787ab90e6510cffa5803579db44e3a575822dc'), size_on_disk=13556826, blob_last_accessed=1756926346.1882668, blob_last_modified=1756926347.2852619), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380943/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/987771cb978dbc949eff5044071149a00223c6771efcc870850e3c8cbe0648c3'), size_on_disk=13397144, blob_last_accessed=1756926367.295175, blob_last_modified=1756926368.7601688), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417680/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aa9e972e7427b1db5556008756651933c8792891339607dee2ee231733072d1c'), size_on_disk=18443288, blob_last_accessed=1756926316.5853953, blob_last_modified=1756926321.4263742), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417750/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aedd94fe4b42547108a807242052397b6dfbfda0eb4e56a9f2e267b312251147'), size_on_disk=1531584, blob_last_accessed=1756926329.1973405, blob_last_modified=1756926329.9403372), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417813/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f7d5239d0d39e171aad1e84964f8cdc3c7a325fe8c38b6829e114cb3997e658'), size_on_disk=10238322, blob_last_accessed=1756926339.4142962, blob_last_modified=1756926340.8722897), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417933/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4fe347d88754646d39d93dd8a78e857d04d1903f7163a21495038b087d5f6b8a'), size_on_disk=12389872, blob_last_accessed=1756926361.7481992, blob_last_modified=1756926362.9061942), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417741/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/734d5947cb26e877b6ae9e9adaf84575974a4fe2fb464ba11c4320906f17a42a'), size_on_disk=4566889, blob_last_accessed=1756926326.96635, blob_last_modified=1756926328.5653431), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380930/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/734e9bc8119c7a507e9cf84f9926b1f9d06ccc8b0062a9b4dc36631f6f1bc6d1'), size_on_disk=4604638, blob_last_accessed=1756926365.1301844, blob_last_modified=1756926365.9251812), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380956/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/15047f69259f2cbc2cb08949d07ac6fde505bade6a0e3bb3912b183a48a4b288'), size_on_disk=2282680, blob_last_accessed=1756926369.4771657, blob_last_modified=1756926370.1431627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381045/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdc8f9e6447280a7fc713c2a8f2157eb9076a5c01a7eb8437176e9ee30d6d4ab'), size_on_disk=4939421, blob_last_accessed=1756926776.3437574, blob_last_modified=1756926777.2507522), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417807/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ebc732f5d06d745bb63af3c7f5966722972fa722ef9d0194d71cf6d9485fda83'), size_on_disk=14049230, blob_last_accessed=1756926338.268301, blob_last_modified=1756926339.5272956), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417898/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ced2c80d4f9d2aa28260b885a20b1c90b41d8eb54ee92f418ed592b97b954a9e'), size_on_disk=6422129, blob_last_accessed=1756926355.1262279, blob_last_modified=1756926356.0962236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417910/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/694d86a1e469836a53e1ce5dba0d64b77b8f68590a2034e2a1ef5310457dc7fb'), size_on_disk=4494010, blob_last_accessed=1756926357.3112185, blob_last_modified=1756926358.3942137), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417701/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a0dd9587c7525a610a3794cf41f6db24a6ba22a12ed852dc27381a68db3e8fca'), size_on_disk=8781514, blob_last_accessed=1756926321.6273735, blob_last_modified=1756926323.1263669), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417871/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e44cb883893b5e30baa752dfbd773b31795f2072a7ec4f473371f435989cad42'), size_on_disk=6264896, blob_last_accessed=1756926349.9852502, blob_last_modified=1756926350.937246), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417847/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4dd39704bc9c1d60a8271f16206887171ee92abde023c53501c1560533a688c1'), size_on_disk=4755120, blob_last_accessed=1756926346.125267, blob_last_modified=1756926346.8372638), CachedFileInfo(file_name='GSE209631_metadata.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE209631_metadata.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fe31b19357a717d3d51d1d201620d37fca79b1c8'), size_on_disk=61, blob_last_accessed=1756926300.2214668, blob_last_modified=1756926300.2814665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417728/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2be09708238c50a31b4e9b334c6202c26eaa6a950ebf3de4287651999d45097e'), size_on_disk=3385901, blob_last_accessed=1756926325.0603585, blob_last_modified=1756926325.869355), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417703/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e82b21d919b84770e2d141eaea09aff82ef57c3ff3dbd15c588c1b8cff212c9e'), size_on_disk=1462841, blob_last_accessed=1756926322.0533717, blob_last_modified=1756926322.898368), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417840/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7e32bef3c0f7f7520382325aba12f5cf9405c22ebce8554b1d877fe24d2da52b'), size_on_disk=9679209, blob_last_accessed=1756926345.0092719, blob_last_modified=1756926346.0542672), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417720/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/961772a081bde1795af5caea9d7562e2b1f889981bd3c3f1c792ad9093a758d7'), size_on_disk=659095, blob_last_accessed=1756926324.1233625, blob_last_modified=1756926324.6243603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417643/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/66dfa3feea4410ba34f5d5d37c7b01e1fa2cdd4ff38f9dfb10d20d55e40e449f'), size_on_disk=1012185, blob_last_accessed=1756926309.326427, blob_last_modified=1756926310.0784237), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417839/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a04fc093d2a4fe873de135165bd62a38a1cb9b29860881ed973022ff128dc6d8'), size_on_disk=6789775, blob_last_accessed=1756926344.5502737, blob_last_modified=1756926346.4242656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417606/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f76c81d98337c9e127bf5516af0dd135458fd622c01f2b5c9fd85ce6edfa4c7f'), size_on_disk=9502783, blob_last_accessed=1756926300.5794652, blob_last_modified=1756926301.6394606), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417882/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ac2bd9710196abc4469d4d78ccbde6ddc67e9ef39ade7d50c32b0d951866e653'), size_on_disk=10076041, blob_last_accessed=1756926352.116241, blob_last_modified=1756926353.223236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417733/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/542261ed8bf189205b611485f1a878f28cae5c90e452ba9cad687eacfdfc2814'), size_on_disk=2645461, blob_last_accessed=1756926325.8883548, blob_last_modified=1756926327.3873484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417776/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/01c4a97f92cd5e2919759d52d083eb824408a2fd7ade9413a060c6b20d233963'), size_on_disk=2759569, blob_last_accessed=1756926332.8593245, blob_last_modified=1756926333.629321), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380963/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c0a30240a0a99347e114928a15a24268495fd37480fde6822f503438bdf3ed48'), size_on_disk=579777, blob_last_accessed=1756926370.2681623, blob_last_modified=1756926370.81716), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417858/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2d7539fcb6297592fe3755ccc88e122441925fbaf9f5600d4daa88639630fd8b'), size_on_disk=1021831, blob_last_accessed=1756926347.8042595, blob_last_modified=1756926348.5072565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417866/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b0424cd7567723443472cb266e090a30e2b64ee6b09bb1b94d8dcfdb9f0b0c43'), size_on_disk=9685580, blob_last_accessed=1756926349.109254, blob_last_modified=1756926350.493248), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756926300.2174668, blob_last_modified=1756926300.2804666), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ad0e80a28e4cd2027893adee5afceab851576ed2637c1a8e94d0af2adae6ace1'), size_on_disk=5088900, blob_last_accessed=1756926375.41114, blob_last_modified=1756926376.3821359), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417842/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c763016dcbf1b53df840e3ea3c1b45234e2d9e797114000efaf692e2c394d487'), size_on_disk=9967694, blob_last_accessed=1756926345.1472712, blob_last_modified=1756926347.1812623), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417656/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/03f8707a323ef7563c8ec2d6352d24c6454d043c4bbbca417680df5c7db208f8'), size_on_disk=10029839, blob_last_accessed=1756926311.588417, blob_last_modified=1756926312.8104117), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417738/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c3d82a351044aa8cd5d43979369088ccf2f563022d4f7aa3683ef20923202770'), size_on_disk=19772535, blob_last_accessed=1756926326.3913527, blob_last_modified=1756926328.6913426), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417844/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ae034bb207abc0c649fb60aae78e798d46647f53b4e0352c6510fb7c8339a234'), size_on_disk=5408445, blob_last_accessed=1756926345.5212696, blob_last_modified=1756926346.4342656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380972/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/91a8106ca7ac2babcabd1549072b2b730909e1386f827e732428bff2bb5251cb'), size_on_disk=6712599, blob_last_accessed=1756926371.1721582, blob_last_modified=1756926372.0711544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417942/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f3a5b1eb0f954ddf66a8dfa1a38465726d2251730d3898865535269a43c7fa4a'), size_on_disk=6911573, blob_last_accessed=1756926363.415192, blob_last_modified=1756926364.3521879), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380978/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a1d811dc3a4bfd44683ddf8bd0401a71d4941497c5cac8d8040a597fc5d26587'), size_on_disk=5902739, blob_last_accessed=1756926372.160154, blob_last_modified=1756926373.09715), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417602/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/26ae91a914e9ad095615c88ac3c62c99e28ae2687ccd66007f13ac8fbbffa3fe'), size_on_disk=3717181, blob_last_accessed=1756926448.0367467, blob_last_modified=1756926301.5714607), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380966/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f27a802d18bf78781f3d168370e0c0e241a130d04a5871d7416442be3ce3e40f'), size_on_disk=4241927, blob_last_accessed=1756926370.3881617, blob_last_modified=1756926371.272158), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417831/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/53d810292e5a513a8ab543fe18783f8d493cedbadc7c97400dc103a4675954df'), size_on_disk=6767934, blob_last_accessed=1756926343.12528, blob_last_modified=1756926344.4612741), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417891/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cabef83ee373e37d528b127d813f1535faede4b98c5524d83e9906df83558b07'), size_on_disk=12596609, blob_last_accessed=1756926353.9052331, blob_last_modified=1756926355.2992272), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417946/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef43c84affe53bbdefcd70cb97a4a4d1e483824b8b6f91e3360a11094609f815'), size_on_disk=4853893, blob_last_accessed=1756926364.0141892, blob_last_modified=1756926365.0591848), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417798/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0ac62fa7573f4b3c756fd9c280be4b97ec0c7c756e8c76307d4fab56d29e1c08'), size_on_disk=10509245, blob_last_accessed=1756926336.5193086, blob_last_modified=1756926337.800303), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417664/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/91fc5dce8497d1fc5466fb5ac31e44a20b295d8f97825500f256f6830ea2f983'), size_on_disk=8203399, blob_last_accessed=1756926313.1894102, blob_last_modified=1756926314.6624038), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417796/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/52d8262071c9c9de8cd8fb52be9e67acf125b8ce67033749fb92989f0d1f9345'), size_on_disk=10184069, blob_last_accessed=1756926336.2803097, blob_last_modified=1756926337.4333048), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417851/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2d8a571fae50598143744a1f13d40cec2ea8c2198e87aae17f96d7c8a00460d0'), size_on_disk=11051527, blob_last_accessed=1756926346.6472647, blob_last_modified=1756926347.6582603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417745/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ab84f2e8e76311cfe45a4b525b239186e835cab37f1e5222fdc35c286064e5e'), size_on_disk=1270105, blob_last_accessed=1756926328.596343, blob_last_modified=1756926329.1233408), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380959/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4693c1876e880fee1ec277fd5ae9127aafa9f40d41a3d5ed482dfcfa8cef55f1'), size_on_disk=873902, blob_last_accessed=1756926369.7061646, blob_last_modified=1756926370.1731627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417896/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/71e393229e9071bad31abe5dc3d41bd5d719a5dcb68077827861b4b4e9a49a85'), size_on_disk=6055777, blob_last_accessed=1756926354.7882292, blob_last_modified=1756926355.8442247), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417652/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d38c028233981eb275c44995eafa73172388a9e0e704fe278b0cedbab49d429f'), size_on_disk=12947754, blob_last_accessed=1756926311.0344195, blob_last_modified=1756926312.1414146), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417827/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8bfd1221f930e86ffd6150c82aae85f00a23fff33c672e40f47d671f5a2e4d17'), size_on_disk=7291599, blob_last_accessed=1756926342.156284, blob_last_modified=1756926343.1502798), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417818/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3b30217b127ff88f165762c5054c392d9489b9007ba7ca8b86f18b44f9988e4d'), size_on_disk=8388230, blob_last_accessed=1756926340.2372925, blob_last_modified=1756926341.5632868), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417663/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cc7010c3ed70c3083d49d88c0f95dbe8f6eff873d4b5848eeae06a22df71a479'), size_on_disk=8938116, blob_last_accessed=1756926313.18441, blob_last_modified=1756926314.5674043), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417715/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ac5effc2ec07a8fb49bac4d77b7d4129a18ae2180807874ea02d1c252a3b68bd'), size_on_disk=520286, blob_last_accessed=1756926323.8693635, blob_last_modified=1756926324.477361), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381028/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8d40caa48e7b9d32d2a0548732ef91c01ede9d0f4f6d74f045817e818c52c808'), size_on_disk=18141950, blob_last_accessed=1756926378.9691246, blob_last_modified=1756926778.2687466), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417694/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/058b507f75cb01bb5912e14e7bbb4e25a1de945e578323bce9f920f31a9ced82'), size_on_disk=11980395, blob_last_accessed=1756926320.09838, blob_last_modified=1756926321.5623736), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417685/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/32edfbf9226b7ef1060ebe3e6986a1803d60408ccda969528cf4a59c5785c722'), size_on_disk=16967633, blob_last_accessed=1756926317.9163895, blob_last_modified=1756926319.5263824), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417828/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eb4987a8ef94e1691c74c1d828d7d9313da4691b577118a73524def2a90c6d0b'), size_on_disk=4827634, blob_last_accessed=1756926342.3472834, blob_last_modified=1756926343.2782793), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380937/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e31f54581c4a4a47033065033f3f9266913edad654543dd07b20d0ffe54847dd'), size_on_disk=2691574, blob_last_accessed=1756926366.2071798, blob_last_modified=1756926367.1951756), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417916/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2abad8555b89fbb5009e8f2a95e553a5ce579d1841ae4e71eec7b137f3282ef1'), size_on_disk=9236466, blob_last_accessed=1756926358.371214, blob_last_modified=1756926359.0582108), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417768/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8bb8e088eb5123049e418c868ab42d94fb48946f934a8313f4cf283e3bfa09ba'), size_on_disk=4124375, blob_last_accessed=1756926331.3153312, blob_last_modified=1756926332.2313273), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417770/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0e7437c90c616b78ccc0963e0388385028b70d66fbe73e0ceb4e30e2d0239816'), size_on_disk=8252889, blob_last_accessed=1756926331.6563299, blob_last_modified=1756926333.0863235), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fc6783c3340c869bc3838a9a9d5abb8bca67361feeef12c42b6cf478d73bfd66'), size_on_disk=9020794, blob_last_accessed=1756926366.2571797, blob_last_modified=1756926367.2561753), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417850/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c08fca103aa3dd1944dcf5c73b67397263842dc687b4101c8ab2c1a63d50205a'), size_on_disk=1182282, blob_last_accessed=1756926346.5122652, blob_last_modified=1756926347.3082619), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417863/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4aabb9d1a4b105c7669deb0f13cd635ab7433ed1ae02f5785aad014d604f1504'), size_on_disk=6900487, blob_last_accessed=1756926348.4532568, blob_last_modified=1756926349.2742534), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380991/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dd86db1779581d5853d4d6a0fa2aeacdb00f92c832683ad788743f17db625db4'), size_on_disk=2640507, blob_last_accessed=1756926374.030146, blob_last_modified=1756926374.8661423), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381018/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/878bf028aca9daeebe7ed0f8ee522580d512771ec4a5e35bfc62ac675176ad15'), size_on_disk=701951, blob_last_accessed=1756926378.2281277, blob_last_modified=1756926378.643126), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417773/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d7980069a740f581dbcfd9416372452203d636d8ad4c9352c3b2d6483cd27379'), size_on_disk=5178664, blob_last_accessed=1756926332.1683276, blob_last_modified=1756926333.411322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417904/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cf9194917da2f4c6c3f0411256b2604e8c14959373d7431047de2e23cffbb9bf'), size_on_disk=7269877, blob_last_accessed=1756926356.062224, blob_last_modified=1756926358.1762147), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417787/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/17f33e06b6bd77c0bbd3141b53fc2d9550b0c54e99b18fe79ddad931e3a283a9'), size_on_disk=12355842, blob_last_accessed=1756926334.5003173, blob_last_modified=1756926336.2953095), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417881/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bf380975f31e362e8964000688bff364809bbe2e8e3efa9ceaa71253ca6beefb'), size_on_disk=8717095, blob_last_accessed=1756926351.848242, blob_last_modified=1756926352.9732373), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417650/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c4d103888f768ff3cb0c68f4b747cb8d8aad8058b75174160cfc42b2504407f1'), size_on_disk=1337049, blob_last_accessed=1756926310.89842, blob_last_modified=1756926311.4924176), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/43f12fb4826be4c7a3dd64800fc72cbcab2a9ce5bed7f7b5b9aee974deb7c06d'), size_on_disk=1116877, blob_last_accessed=1756926376.094137, blob_last_modified=1756926376.6491346), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380945/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/51efeee4439e81d920c4d9e860de321d90c72703600044e52215c774e1540650'), size_on_disk=6902367, blob_last_accessed=1756926367.356175, blob_last_modified=1756926368.6011696), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417795/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b995a48f28ae3baa145438a39c9fed73fa8689eb31e07ff02a3bc6bc827eb76e'), size_on_disk=8032882, blob_last_accessed=1756926336.1053104, blob_last_modified=1756926336.9993064), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381025/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aea8c4d9f3783191d3e89f4566811f01089f3b0b76b736c5167bda54693b0bc4'), size_on_disk=339954, blob_last_accessed=1756926378.7781255, blob_last_modified=1756926379.125124), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417649/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6baf4bcd82d9fe55b8dcdc7602c0d39d20954675ef4c5484331ff25dac8ad3f4'), size_on_disk=2672976, blob_last_accessed=1756926310.8234205, blob_last_modified=1756926311.4704177), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417784/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fd3dd53cf34f48c235e4f771378f42a5605446bbc62d108a5637a042513fd75d'), size_on_disk=4826642, blob_last_accessed=1756926333.7233207, blob_last_modified=1756926334.576317), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c103eef2a9834296fb1328b7012b5b9184c5a3f131817dfca257843e863d34b2'), size_on_disk=9829417, blob_last_accessed=1756926362.1531975, blob_last_modified=1756926363.3531923), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381020/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c85f953439e9f6918c7f7c8194bcbbebe0ef8a96070119813025a42bde8c08fe'), size_on_disk=897644, blob_last_accessed=1756926378.3451273, blob_last_modified=1756926378.7061257), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417861/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef65c45e0e86b7d04e274c39408b332cc94879c0804f16acf7c537b14bb6498c'), size_on_disk=10436399, blob_last_accessed=1756926348.3832572, blob_last_modified=1756926349.6192517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417651/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b95b6be3a7a6a5b4af3bbf15c5a4a6b3893f3030ef6d1dcfe79e6c6554e3d3cb'), size_on_disk=13551797, blob_last_accessed=1756926310.9544199, blob_last_modified=1756926314.6814036), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380970/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bdec4a51127eaf09abb5d25d7f3a732afc3b561105fd18ba1df44d04e6dd90a2'), size_on_disk=3545790, blob_last_accessed=1756926370.9301593, blob_last_modified=1756926371.5451567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417775/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2ca9ba5e1394191206945f41168d555519fe3963d4c39f251c1ead57f2a323f4'), size_on_disk=1059891, blob_last_accessed=1756926332.3723266, blob_last_modified=1756926332.773325), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381029/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6431cec7b7404a0773d8715761e2b161f01ebd8471950e71e17dad8976d83f3c'), size_on_disk=7229691, blob_last_accessed=1756926379.0071244, blob_last_modified=1756926776.565756), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380960/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/60771297311bf96c3213c2a96321062a06067c91d2bba2fce69a7a6d7a0c980e'), size_on_disk=560607, blob_last_accessed=1756926369.7811644, blob_last_modified=1756926370.3121622), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417907/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b24926bd36cf33ea9107043b9a47db8239ad6208745c56002513225e8cb80a6e'), size_on_disk=7594380, blob_last_accessed=1756926356.7332208, blob_last_modified=1756926357.882216), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417717/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/689842dffacf7efa4ba6cd9b4314d195e89f9105eb71675f27019a9e25be63db'), size_on_disk=102777, blob_last_accessed=1756926323.9603631, blob_last_modified=1756926324.5793605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417889/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/295f9efb0d78e3a116ce4022a7830297570446e2017a3fcd3b3d423c0c20664b'), size_on_disk=9617979, blob_last_accessed=1756926353.3102357, blob_last_modified=1756926354.7322295), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417803/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1fd8b542fb1b0e18bc5346c50ad85da440ff6190a987467ec6876ed3ec51a4e8'), size_on_disk=4928668, blob_last_accessed=1756926337.5433042, blob_last_modified=1756926338.7182992), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417638/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c8cface311cd55cd1542ed94d609da72cd2b3087422c28ad26c60e1d81a3e6bd'), size_on_disk=993699, blob_last_accessed=1756926308.4724307, blob_last_modified=1756926309.2634273), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381067/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/da9c5ab3c0f970f9c2af4c0f5f0e9a95d47769e8096ff6b9d76092becbc8517e'), size_on_disk=9837433, blob_last_accessed=1756926780.2977352, blob_last_modified=1756926781.5777283), CachedFileInfo(file_name='GSE178430_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE178430_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/be2c4af55a502d7fa22abb28afe6db347950327bde1bc7e97dbb68fdb0c59f3d'), size_on_disk=9398, blob_last_accessed=1756926861.9192877, blob_last_modified=1756926300.5344653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380998/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6808b8e1fa74b807321826b2c54182a3b6e257f0a223be0a6a94caf9463959e9'), size_on_disk=16847111, blob_last_accessed=1756926375.2231407, blob_last_modified=1756926376.572135), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417928/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/97e74e01fd641bfef68b6206a3c62ab41b5c28d8ea8369ce4198df1b3387b266'), size_on_disk=2449472, blob_last_accessed=1756926360.9072027, blob_last_modified=1756926361.3542008), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381056/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/382194ca9758ddc18f059a649b13c5a21ead3a7f1fa5e012065315047a037a58'), size_on_disk=2351268, blob_last_accessed=1756926778.362746, blob_last_modified=1756926778.9827425), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380985/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2ec986412ef2a9e6e59f4ea3dc278b27ab3975b5025d00bd1cd38fe1867b0fea'), size_on_disk=7208882, blob_last_accessed=1756926373.2251494, blob_last_modified=1756926373.9591463), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417940/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/270559ca1db4605de241e8642461b4d7304d124e4fc624f7d8a8a59867301a4c'), size_on_disk=2328150, blob_last_accessed=1756926363.182193, blob_last_modified=1756926363.9021897), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417639/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/43c5fb0a4f049fa5895f8167928d047cc4954aa37db1fc8a9892fe9f99c401bf'), size_on_disk=782319, blob_last_accessed=1756926308.7694294, blob_last_modified=1756926309.6284256), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417674/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5970db11c3efe2bba4eb50d07480cb0b46ed74dfc3ddf12d611ecd61f1d5fb68'), size_on_disk=4619941, blob_last_accessed=1756926315.5114, blob_last_modified=1756926317.630391), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417691/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/48de5a2968610e5ab855522d27f535d40e1973c1835ec32fcfa5bcd91a0795dc'), size_on_disk=14481663, blob_last_accessed=1756926319.447383, blob_last_modified=1756926321.2093751), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417708/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b58038866c334daaf757a086f99c3a5c5962b8563769ee0a3205bcdcbb0144ec'), size_on_disk=250863, blob_last_accessed=1756926323.0003674, blob_last_modified=1756926323.366366), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381051/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ba017d941936d755013757711f5de4661acd187ef79524662d85485ba6588f34'), size_on_disk=8199472, blob_last_accessed=1756926777.322752, blob_last_modified=1756926778.4757454), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417808/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0251ae27bd961d6c962253c555963b42148092cdaa2616a78f6872cf1a4e5e4d'), size_on_disk=12900784, blob_last_accessed=1756926338.3973005, blob_last_modified=1756926339.439296), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380948/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6ac219aa7f5be77c21bde60c80ccad59edf825e2a009d6a4b7fc8bd4d8be83d4'), size_on_disk=6046323, blob_last_accessed=1756926368.6691692, blob_last_modified=1756926369.5471654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417901/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/69432386b347f5b364c02d270b38891358fa2068465354d1ada84d5450ea5bf0'), size_on_disk=9484337, blob_last_accessed=1756926355.6292257, blob_last_modified=1756926357.0582194), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417804/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/16ee23ae4dbe8a4d968a614f5e6de65028b6edce012231a50eb776d65f949d34'), size_on_disk=2107502, blob_last_accessed=1756926337.7883031, blob_last_modified=1756926338.5083), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380969/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/10410488c74f35a493cef2ffc4a64b28b336be1fb32a2b6dac692e0d7fbd4271'), size_on_disk=1973405, blob_last_accessed=1756926371.041159, blob_last_modified=1756926372.1191542), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380941/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9a23f03fee8d60d752bd0bc1e59e9205abea4f3a78e3272b9a03447f076383d5'), size_on_disk=3514837, blob_last_accessed=1756926366.9851766, blob_last_modified=1756926368.7591689), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381031/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b4705ac4577c8e7b116d1a1b677967f69412b0e4716e0de7fa7ee97bc9be4d74'), size_on_disk=4551217, blob_last_accessed=1756926379.2931232, blob_last_modified=1756926776.2707577), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417943/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0e4191a403f5070636bcb281bd80d39e11c70fb502fa2b18150032d58f2e0740'), size_on_disk=5396199, blob_last_accessed=1756926363.7271905, blob_last_modified=1756926364.542187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417747/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7011e7ff4b64ca9c57bdab93cfcfc52938e7b0574b48417feb4f6185f6c118b9'), size_on_disk=4791771, blob_last_accessed=1756926328.7593424, blob_last_modified=1756926329.9543371), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417948/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d43ea802a452aa9d57004381e1688222e61bd313b107077c1c663555547d1d44'), size_on_disk=1748124, blob_last_accessed=1756926364.3661878, blob_last_modified=1756926365.038185), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417620/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b973f1a83f111c4e114a40d3731ff24779cafcb7b71523fe8571cf2ed849b316'), size_on_disk=20780203, blob_last_accessed=1756926302.5494566, blob_last_modified=1756926306.7334383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417687/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fa4f79173f5a5ed278c7ef80d9fb220aa9c615de97bae7963969e27ecf0e587c'), size_on_disk=13352339, blob_last_accessed=1756926318.3003879, blob_last_modified=1756926320.3913789), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381062/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/76151e17f242c5108010debea961b0b13f3821be2fcb77fd923a36cc0e672695'), size_on_disk=4880117, blob_last_accessed=1756926779.3357406, blob_last_modified=1756926780.5167341), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417743/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d4eb93049648498d2fdc1f125574429a4dfdec65e626ad9fa16bdf0d0cf599d8'), size_on_disk=2280534, blob_last_accessed=1756926327.462348, blob_last_modified=1756926329.1343408), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417880/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b08db0a2399fc4e5b6f59c19b7af05b1294d0fac10b2c336b627234fa828f1be'), size_on_disk=9818886, blob_last_accessed=1756926351.5782433, blob_last_modified=1756926353.246236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381055/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0aad0d3724742845da7f0afae0c18ab5c992bec76c02e0ffda057f00d0894d35'), size_on_disk=3355008, blob_last_accessed=1756926778.3377461, blob_last_modified=1756926779.263741), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417819/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a1ba6808d672dbc9c58c26364c5cd9ed0b8a3041e6714844846648d3cd1aa050'), size_on_disk=3244517, blob_last_accessed=1756926340.7022905, blob_last_modified=1756926341.6952863), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417886/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62fe87e7519370c4ac46e6d9d0abf314a8f92d9a5d6804ebddae0ed095641219'), size_on_disk=7968977, blob_last_accessed=1756926352.9472373, blob_last_modified=1756926353.7992337), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417637/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ca1a954c7b2aca56529daf9a89249ad4e6ec2373e29ac7c19d2af1533afc4263'), size_on_disk=2776397, blob_last_accessed=1756926307.8804333, blob_last_modified=1756926309.1794276), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380990/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4e04122d2830db35f2b82b29efaca846fe10d7638c88c3e4d3cf78bf10cf245a'), size_on_disk=2093813, blob_last_accessed=1756926373.9261465, blob_last_modified=1756926375.1611412), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417714/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d6abeb724a8c8b23217f3465284564da5baffee59ee4e3af582e0a3705ae32c6'), size_on_disk=262443, blob_last_accessed=1756926323.756364, blob_last_modified=1756926324.256362), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417799/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cba2fb579c7706a7ff38fad3c7c649000560c1052e681adea986c5cc02c02cfb'), size_on_disk=5688906, blob_last_accessed=1756926336.7133079, blob_last_modified=1756926337.7073035), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417782/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d6624acaa7c4ec21ae02d6d1322b8669a148308095e714b41bff868483fe2fa9'), size_on_disk=11742567, blob_last_accessed=1756926333.5273216, blob_last_modified=1756926334.824316), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381033/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5fff567b96a4649d787e93ef8cdca2d8ce977b7f2eb25d6f78e50bc30c798bce'), size_on_disk=1712156, blob_last_accessed=1756926379.7391212, blob_last_modified=1756926380.3661187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417648/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fb706eb700e8fc003822b897accd3501360d737fd1a9ffcf26c3e9d9b25a5e35'), size_on_disk=1285054, blob_last_accessed=1756926310.3104227, blob_last_modified=1756926311.0174196), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417854/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d52c8b14e197b179a9b43fbc89bb3205d09d458161ee49ac15a62b7c1f7a39fe'), size_on_disk=6203801, blob_last_accessed=1756926347.311262, blob_last_modified=1756926348.387257), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/64a119c8028083521d3872e2daca1545f2f8753eb8acfc751bf0c6d25803a3f8'), size_on_disk=2003211, blob_last_accessed=1756926376.794134, blob_last_modified=1756926377.2761319), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417836/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4fb964c54f4a37387c7844902d6171011a9a81efff244f365efcb156f630158f'), size_on_disk=3312893, blob_last_accessed=1756926344.2532752, blob_last_modified=1756926345.0702715), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417677/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cfcdac65879c0c26771c1cd1c4a656d4db20e6adb2ed3ad833cf23cd35a9d848'), size_on_disk=13135194, blob_last_accessed=1756926315.9083984, blob_last_modified=1756926318.1283886), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417616/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0b0f3c9481fd0e254abd544ba803de0c9da9b3a686451975f981d02fc481148d'), size_on_disk=15058562, blob_last_accessed=1756926301.8254597, blob_last_modified=1756926305.0234458), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417723/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f397cef2d9c491fb034826727c63b3c224f6ed791c9b4dcf884c63c55c9961c'), size_on_disk=2800893, blob_last_accessed=1756926324.70836, blob_last_modified=1756926325.4453568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417812/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9e8445f413d26051897f0fe3e7566b116a9a55062b78201990de334f17aecb31'), size_on_disk=19905089, blob_last_accessed=1756926339.0272977, blob_last_modified=1756926340.6232908), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380962/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/72be01aa42db736ffac8236c23c16edea00bdf5da88c3114fc94d9eb3daa4ee9'), size_on_disk=1460258, blob_last_accessed=1756926370.2361624, blob_last_modified=1756926370.6811604), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380965/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6b384b53be751cec9056e74c2c8ce9fe2e532cfbf6a38e43bb09ce74ec817f3a'), size_on_disk=12974487, blob_last_accessed=1756926370.3761618, blob_last_modified=1756926371.4361572), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417629/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/29c9c86be94792d797005b106ab00d066634bea44fbf17e81738a00cbffa264b'), size_on_disk=22750780, blob_last_accessed=1756926305.5594435, blob_last_modified=1756926307.5804346), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417822/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62cbbb0172158a2c0776000136424032a794757ca9e9f971e5f445deb20e8579'), size_on_disk=6040431, blob_last_accessed=1756926341.3882875, blob_last_modified=1756926342.8302813), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4c942b91ac018330aeb4da512a3e47ae4531a434b6e9933cf3e2bb476a77f9ca'), size_on_disk=6312758, blob_last_accessed=1756926366.1041803, blob_last_modified=1756926367.0141764), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417615/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3b8b3c4c686238c7c642151bbdcb2ba7796de56c1922e558a1e7f08507dc5abb'), size_on_disk=2866472, blob_last_accessed=1756926301.8314598, blob_last_modified=1756926302.5744565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417756/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ede027f2c13a1e6669becf6194ace85af5800d5fe77db16ec47bfb3abaa735d3'), size_on_disk=8620421, blob_last_accessed=1756926329.968337, blob_last_modified=1756926331.0303326), CachedFileInfo(file_name='GSE209631_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE209631_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/be49eef90682fbd55a5e60ada8b4c1c554e1b5a2ec1209996269db020d3bb074'), size_on_disk=4258, blob_last_accessed=1756926300.173467, blob_last_modified=1756926300.594465), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417905/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b9d291469b7a414bab157f45bfd3a1f7ffaaf931de25fec9eb18012a18b70939'), size_on_disk=4418829, blob_last_accessed=1756926356.1612234, blob_last_modified=1756926357.2652185), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417936/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8ba89583b8c822b64968df9eca8273d12270429f0f820d6027f13c66504c383d'), size_on_disk=4552381, blob_last_accessed=1756926362.205197, blob_last_modified=1756926363.171193), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381021/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3e80794742d10b6d120e70c4e4f1d7f77123ddf8aa18499f686b4abc8eadd325'), size_on_disk=1524611, blob_last_accessed=1756926378.410127, blob_last_modified=1756926378.897125), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417675/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/79072181ebce12769f2f0a1971977d6691dde1841b53d7b54cf9d4f47f53b3dc'), size_on_disk=6478433, blob_last_accessed=1756926315.763399, blob_last_modified=1756926317.2643924), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417630/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/26fdbadf7c1252a8fd73ac85ea7430d942d2d11ddc43ea1b2590aaa008e08337'), size_on_disk=3015575, blob_last_accessed=1756926305.7104428, blob_last_modified=1756926306.7214384), CachedFileInfo(file_name='GSE222268_metadata.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE222268_metadata.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4459325dc2deeb9f00d803b89cd8fdc823ed383f'), size_on_disk=61, blob_last_accessed=1756926300.2714665, blob_last_modified=1756926300.3444662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417719/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cc0a365d1d79f0469571216394f0cb211b9096f2a567e0a347a18f0c496bac06'), size_on_disk=2076369, blob_last_accessed=1756926324.010363, blob_last_modified=1756926324.951359), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417849/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/23b6606ffcf116cf066b103bf1b6b4fb33c40e544e20e4565fa44c5bbcea4bdd'), size_on_disk=5689527, blob_last_accessed=1756926346.584265, blob_last_modified=1756926347.3892615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381034/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/112a8e705f7536fa06c630165d3c409f87a5784ad626b891cc2b3919e24bdd14'), size_on_disk=250813, blob_last_accessed=1756926379.795121, blob_last_modified=1756926380.1791193), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417710/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9837ecd3769eaaf407d939ff09d6ae74d832630b27353251942bd262cd70fc35'), size_on_disk=330457, blob_last_accessed=1756926323.098367, blob_last_modified=1756926323.797364), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417607/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/623d085888c300fba1d05a9018ec3d4c31912f2b91a872ef34feda3322889b70'), size_on_disk=6809356, blob_last_accessed=1756926300.611465, blob_last_modified=1756926301.75846), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417766/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/39da4f16b82cc0b5019d8526f2e3eed49de9760e319f8e839db621bf4c1ab021'), size_on_disk=6482957, blob_last_accessed=1756926331.2123318, blob_last_modified=1756926332.1133277), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417920/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/00a869703f03e3df3f0f51a7d93d52dd7eac5174ed2a1b752207460e71c7b41a'), size_on_disk=14880236, blob_last_accessed=1756926359.1242106, blob_last_modified=1756926360.409205), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417698/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d240b652ebca64784e037cc8ffbda424e2c92f368c5bb9c730930f48f29facc2'), size_on_disk=1855826, blob_last_accessed=1756926321.2993748, blob_last_modified=1756926322.0703714), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417672/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f0741c46f1460c30d4d9b88ca1f7c0ca77a040cf25a34d82fab4c777a921ac26'), size_on_disk=18268758, blob_last_accessed=1756926314.9764023, blob_last_modified=1756926316.5143957), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417864/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/15d259c52e80a2d2d9c20274ef940237c90001d32f2aecb80779335a708222ff'), size_on_disk=3706383, blob_last_accessed=1756926348.5722563, blob_last_modified=1756926350.0962496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3fe2828766efd936f02efae2da3e068ef9b28ea374e594e09850e3e8ff0d8164'), size_on_disk=4421540, blob_last_accessed=1756926375.7961383, blob_last_modified=1756926376.7581341), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417780/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4f1b7c3ded6441c8e906c3494ae63750d25303ee6f8d8e05120dc11ce8d79080'), size_on_disk=9025914, blob_last_accessed=1756926333.2923226, blob_last_modified=1756926334.387318), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417931/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7e70415ed0dfb1eb42e46789c905ccf2b593dc192d907d939d87acd59cd963e1'), size_on_disk=4991063, blob_last_accessed=1756926361.306201, blob_last_modified=1756926363.1161933), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417751/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9a5733130de69115a731511dac8bf577fc5609a7f9649106ae9a12ffae7011ba'), size_on_disk=1660869, blob_last_accessed=1756926329.2023404, blob_last_modified=1756926329.9043374), CachedFileInfo(file_name='GSE222268_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE222268_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e0f1d5b82fd97798d0c02d992c49c0e83d8dcf82e07e001284e78d9656355481'), size_on_disk=4411, blob_last_accessed=1756926300.1784668, blob_last_modified=1756926300.5344653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381013/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c831ca7cc9cfd2cc2ccd92a0168b2e14dedf2bde9e29780dfaf6d281b31e9462'), size_on_disk=4648281, blob_last_accessed=1756926377.4421313, blob_last_modified=1756926378.3451273), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417900/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/45d5a0bbf39e542f22aa4e36186673d1246533dc513f006312a26286496f6ead'), size_on_disk=11235417, blob_last_accessed=1756926355.535226, blob_last_modified=1756926356.6632211), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417758/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/54f8e8d84e5b8b9457aaa143829fcfd5a4715d0fb184478ce659cd6c6efe7be1'), size_on_disk=4176901, blob_last_accessed=1756926330.0203369, blob_last_modified=1756926331.1383321), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380983/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eec33f1158aa017b073fcb16d6400cd800eb79e8c0b78b5e2f831d7413e4a736'), size_on_disk=4478939, blob_last_accessed=1756926372.869151, blob_last_modified=1756926373.6721475), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381047/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dda6f6ca669a0e5c9d34b548305cbeb6b82f1b70086e06d3604e7e096fd7fb25'), size_on_disk=6799004, blob_last_accessed=1756926776.8777544, blob_last_modified=1756926778.4717455), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417682/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/83b3100cfd1dc6918855c4a711ff607b36f40971655733855a7df353b4d5fca5'), size_on_disk=14037100, blob_last_accessed=1756926317.1663928, blob_last_modified=1756926319.1013844), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417605/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f689b77eccd8d95d70fb616f4e2aa03a41201aec2a8c3c86849b7b145cb0f796'), size_on_disk=1787545, blob_last_accessed=1756926300.5184655, blob_last_modified=1756926301.74346), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417806/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2befcad73b2aa8170ceaf8c249ceba45e87037771e86c9bb4bd7addc5815bbec'), size_on_disk=5276603, blob_last_accessed=1756926337.9863024, blob_last_modified=1756926338.8112986), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417802/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ea68aebb727f77d846a145d43ec16fa2496e9c05d49a8791aa131b8e3e536452'), size_on_disk=4881005, blob_last_accessed=1756926337.2223055, blob_last_modified=1756926338.1273017), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417632/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/23c561fde2e6c2973ec1913373c343a5c49d6cc0dbb44ff21eb5aff87cd77d3a'), size_on_disk=18481828, blob_last_accessed=1756926306.2764404, blob_last_modified=1756926308.5744302), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417765/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bd2b5a1b1593db002ea9c159d4c40d1d8addddbf124207a7b32b0bb43e2e4d72'), size_on_disk=2696437, blob_last_accessed=1756926331.2103317, blob_last_modified=1756926332.046328), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f00bc431128183affc8964f9610f3e9486a043bcea5b31b3ebbcbd9b0070a448'), size_on_disk=6096918, blob_last_accessed=1756926377.3431315, blob_last_modified=1756926378.2571278), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417688/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f21ccaca0f6782b6ce2b70b50c9dc93487b6aaec03db6c30d9cb0c9fc01dda42'), size_on_disk=16023102, blob_last_accessed=1756926318.5463867, blob_last_modified=1756926320.0283804), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417647/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8c01c928abe00b5afa6a2292820695797391b3671cc110b9026f5fc5cb8564ae'), size_on_disk=2891716, blob_last_accessed=1756926310.1614234, blob_last_modified=1756926310.93942), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417613/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/695d3095619f3a313d25c506b9d0493f171896f1439bcf3bd1cd932d1f6fed5d'), size_on_disk=2812565, blob_last_accessed=1756926301.6434605, blob_last_modified=1756926302.4814568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417810/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b807197acb33acdd1b70eddedd59dc4d831e271d231d6c9bb6f1281fb4d5813f'), size_on_disk=9808564, blob_last_accessed=1756926338.8082986, blob_last_modified=1756926339.7202947), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417834/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a69c65a4d32399355863fa14d4a1e92ecff2c92498e122e8e77796de16d42948'), size_on_disk=8537233, blob_last_accessed=1756926343.3672788, blob_last_modified=1756926345.2462707), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417870/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ca1bc9825b47ee9ac59e53bc6ada3b1a9e8aba1b57c57c57947afae7926054b'), size_on_disk=10710319, blob_last_accessed=1756926349.7052515, blob_last_modified=1756926350.7552469), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417833/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/48792db399d7f0aab6765109e8c01abd61a2da93e5d0bdf7c325bcffe01fe113'), size_on_disk=8139822, blob_last_accessed=1756926343.3262792, blob_last_modified=1756926344.480274), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381015/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/345c83c5ab13d230542683014d3d6073ba5a348e3063fea325843981c6c6e7c1'), size_on_disk=3807314, blob_last_accessed=1756926377.67913, blob_last_modified=1756926378.4971266), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417767/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8d0afed33cd85298c709656da4b210e3fe87aa4d6822208d744200816d59b95b'), size_on_disk=3739401, blob_last_accessed=1756926331.2333317, blob_last_modified=1756926331.8623288), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417760/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0500e5835116a61477f2d98554d0dd97e8d932a6b564f709c40c4884f2b49052'), size_on_disk=2234621, blob_last_accessed=1756926330.2873356, blob_last_modified=1756926330.8273335), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417735/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/634551506800bc76183ce179c8c8f324cb6f0f577f6cf0b629b74ce4f1e995d5'), size_on_disk=19171195, blob_last_accessed=1756926326.0393543, blob_last_modified=1756926329.4873393), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417914/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d062dbf660e1ef240ff8ef99ff2d486d9caaabda14575e6365ce6f725fa91ccf'), size_on_disk=8599250, blob_last_accessed=1756926358.2422144, blob_last_modified=1756926360.3412054), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417797/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/12ee278356fa75b0ad555fe06376c78b71630324b7b89aa28abe960c187d256f'), size_on_disk=14391201, blob_last_accessed=1756926336.3743093, blob_last_modified=1756926338.322301), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417619/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0a4d4159941caa34a5e079821d49047d1434077926b2933c27d384e04ed2e5b5'), size_on_disk=9949802, blob_last_accessed=1756926302.5744565, blob_last_modified=1756926304.05045), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417761/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ba86b2041414034d38b95ccbf3c65957642c56b56914240999986ebc76b9e193'), size_on_disk=1635118, blob_last_accessed=1756926330.478335, blob_last_modified=1756926331.0933323), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381000/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/503f854145f3a220aa936072c3e66410d34914fef948992399a4addbfc977186'), size_on_disk=3904727, blob_last_accessed=1756926375.3401403, blob_last_modified=1756926376.5601351), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417603/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdfb1fd9b21341104e6423c684b0ae8a52ed1701eb3e232babb98340f953ea5b'), size_on_disk=4460026, blob_last_accessed=1756926300.366466, blob_last_modified=1756926301.2574623), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380986/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f23d3060df5cb96778fbdf10fff0155c931e1a1e8cc077c7f1582542d54827f5'), size_on_disk=5858046, blob_last_accessed=1756926373.285149, blob_last_modified=1756926374.5971434)}), refs=frozenset({'main'}), last_modified=1756926783.3167186)}), last_accessed=1756926861.9192877, last_modified=1756926783.3167186), CachedRepoInfo(repo_id='BrentLab/rossi_2021', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021'), size_on_disk=213725645, nb_files=47, revisions=frozenset({CachedRevisionInfo(commit_hash='6b29baae54286d5fbdd1c70f499c03319badad51', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51'), size_on_disk=213707016, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466110/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/d5283e08e898b4f59837f7ff2064f79c8a828994f868c637894f4e07df36daa5'), size_on_disk=13123795, blob_last_accessed=1757598690.8318212, blob_last_modified=1757104407.6787057), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466116/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/587a7b86dffe3d7ade9adeeb94029ea1de54e61fb2c2445dc527979b7f1c24ac'), size_on_disk=5596311, blob_last_accessed=1757598690.988821, blob_last_modified=1757104407.3507075), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/04bacc0f8c03ee000ba3b69320522d0b1be909686474b6e8a126173a0ceae8c4'), size_on_disk=15547870, blob_last_accessed=1757598690.8528214, blob_last_modified=1756944357.1972551), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466142/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/3dc044b5cc1b7bc8070d8de4b7b870524b296f26b4f43b22dce2dcbf161d4c74'), size_on_disk=1739520, blob_last_accessed=1757598691.2588208, blob_last_modified=1757104415.5896657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466112/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8f3441964a046d3c148b41f335948a085d26884e1a890e515711a2d1b48a40be'), size_on_disk=4883601, blob_last_accessed=1757598690.9128213, blob_last_modified=1757104408.5027015), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466125/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/0bba21490ea0a6297031b0ff3219fc0158d9bbcf15099906c86a770b1a1312fc'), size_on_disk=2624759, blob_last_accessed=1757598691.081821, blob_last_modified=1757104409.4426968), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466139/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/25c7d3ffbacca76fd998edffe0c762e220f448140019aa2d389facff5117ce48'), size_on_disk=176811, blob_last_accessed=1757598691.2478209, blob_last_modified=1757104412.6266806), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466108/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/56875f81a3cc7c1d40b3042b2c443058eb0f9a2b7e7ebc2e1704c098be612628'), size_on_disk=1765819, blob_last_accessed=1757598690.8238213, blob_last_modified=1757104407.1947083), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466117/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/49065d4cee74c20f34303405e34e284e29f19082b76fa883a38aeab24cadf674'), size_on_disk=1742958, blob_last_accessed=1757598690.995821, blob_last_modified=1757104411.8276846), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466150/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/80cd2d325e5d7a50c6ecbf79a052f45c54a93b9423e90735bf8989aada9bb9eb'), size_on_disk=3234303, blob_last_accessed=1757598691.3798206, blob_last_modified=1757104417.837654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466111/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/ba431fc9a05f40581e450429d467226a8e3c49195de355437f9044bb0fdfe524'), size_on_disk=5749460, blob_last_accessed=1757598690.9028213, blob_last_modified=1757104404.9707196), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466128/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/f631797d2bc13a580f570637d94a0b9ea37deb2fd7ef92e32c66fd9014260525'), size_on_disk=1059587, blob_last_accessed=1757598691.118821, blob_last_modified=1757104410.6996903), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466140/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/25f98a891820b5a91c11629ed5615d40ffe1fe4a6cba1b2c6642067d76b5be87'), size_on_disk=68174, blob_last_accessed=1757598691.2588208, blob_last_modified=1757104415.6456654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466141/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/e9efb807ce35fc3e43b1ff0d27b80646c7d137c587abd9d6247393e44d4f30a0'), size_on_disk=986450, blob_last_accessed=1757598691.2628207, blob_last_modified=1757104413.129678), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/09f6dce90ec0e64d8f18614091dd18ad2e86213a'), size_on_disk=4403, blob_last_accessed=1757598690.987821, blob_last_modified=1757104409.9196944), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466149/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/ae4d5237fde214a92d3267fbe11f6f1f88569cc7f3f7ccb40fed200871bf33b1'), size_on_disk=10933582, blob_last_accessed=1757598691.3518207, blob_last_modified=1757104415.1316679), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466114/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/2c07f671cca471d6ad3e10e071cd1b976a89a45ff32920913d8fc36dbeaa1678'), size_on_disk=10992797, blob_last_accessed=1757598690.9498212, blob_last_modified=1757104406.3287127), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466136/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/f5e7fd644d30ae0c8e2c72bc341155bfe5df2ce102b68b08ff4d243470026fcc'), size_on_disk=343033, blob_last_accessed=1757598691.1958208, blob_last_modified=1757104411.7116852), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466144/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/57b3d2c7917bfa29312cc51c921beff914858c61762e28a658eb29d8eaf3348b'), size_on_disk=918271, blob_last_accessed=1757598691.2758207, blob_last_modified=1757104413.554676), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466124/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/39ef32412cae257b724c3b797ea482eeffb5b29afef4246d997ea525a2124054'), size_on_disk=7162800, blob_last_accessed=1757598691.075821, blob_last_modified=1757104409.5966961), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466113/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/acec3b041a74ab45dbeb03e7f183c2b3d16479aadd64a81f80a74aa42d56368d'), size_on_disk=5866774, blob_last_accessed=1757598690.921821, blob_last_modified=1757104409.0226989), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466129/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/b290d4c9436756b444d0da1af1229b0a2a6e877fd968c5d01b73efa96e62cff8'), size_on_disk=6955953, blob_last_accessed=1757598691.196821, blob_last_modified=1757104410.78769), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466122/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/6362da9e07f02d4c9dc05f15f638ca1b1bf0de5989da4ee8c6f0768f2453f98f'), size_on_disk=1249990, blob_last_accessed=1757598691.0398211, blob_last_modified=1757104411.9016843), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1757598690.9228213, blob_last_modified=1757104403.6017265), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466118/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/192057e6004dd5cd303e8b1abf54cb72005e211c315665e28eec7b617a84d95a'), size_on_disk=728586, blob_last_accessed=1757598691.0068212, blob_last_modified=1757104407.0917087), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466130/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/73db821736e3e47c09da250e369fa06812e02e5abe7a4a3ceab5d1020422b66c'), size_on_disk=1920920, blob_last_accessed=1757598691.1328208, blob_last_modified=1757104410.9306893), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466107/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/2f924bc58d82a21b621b051addd1913c77369afaa738171ef0962548fc1a1021'), size_on_disk=5670198, blob_last_accessed=1757598690.8608212, blob_last_modified=1757104405.0417192), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466126/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/34292b90d99b5a5be542c7dd10b11336605330639d92e93b092e567a56fe7d93'), size_on_disk=2850164, blob_last_accessed=1757598691.086821, blob_last_modified=1757104410.0976934), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466143/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/54bd4af6ed8caf52a5810598d96c9b235266542586a692112cb247a209c2649f'), size_on_disk=10801408, blob_last_accessed=1757598691.2688208, blob_last_modified=1757104413.3726768), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466135/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/d44d381db0b8c0cb4960796e48d3d16e3d766aff87cbc686789a0c5799ba0a98'), size_on_disk=5204256, blob_last_accessed=1757598691.180821, blob_last_modified=1757104412.4376817), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466115/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/c08ce1194dd98cb028a6038944317e70ac1545e32b21dd323eafa57b16611e6e'), size_on_disk=3503431, blob_last_accessed=1757598690.9468212, blob_last_modified=1757104405.9427147), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466121/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/78d0f377f8a756b28f2f5296932368c9c6b764d2972dc93aa6dd354591d29860'), size_on_disk=7053621, blob_last_accessed=1757598691.0328212, blob_last_modified=1757104408.5097015), CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1757101035.6211069, blob_last_modified=1756944310.9395182), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466127/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/1565c5175e05c322f160529cb4bb74d5e8468458f150f228d6aadf2b670c3b4a'), size_on_disk=11670434, blob_last_accessed=1757598691.092821, blob_last_modified=1757104411.0776885), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466123/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/57b7ca5d419676590538573f295b99db05a952e20cb4c6b2a1618974611d5051'), size_on_disk=3672899, blob_last_accessed=1757598691.073821, blob_last_modified=1757104409.590696), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466134/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/0b23434181eb1fc864e858cfca82d2d8fed1524a8f7dbea3bf576f6f452cf08b'), size_on_disk=12398132, blob_last_accessed=1757598691.169821, blob_last_modified=1757104412.552681), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466133/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/b6d6913e14b52d5d51dab2f254815da89e03cebe75e80356b27cf18e31eb048f'), size_on_disk=5090722, blob_last_accessed=1757598691.169821, blob_last_modified=1757104412.180683), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466131/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/bdce0842b5568e406a799dbe36b4b9645a30fa58eb0a707f24ed3bb3900fbf67'), size_on_disk=11759994, blob_last_accessed=1757598691.157821, blob_last_modified=1757104411.1146884), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466132/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/cc41509e63473d565341ed36fd6881fecfd537e59c003f4779d4c4ec7b49cb54'), size_on_disk=1906184, blob_last_accessed=1757598691.161821, blob_last_modified=1757104413.4366765), CachedFileInfo(file_name='genome_map.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/51978dc2125afb40a41a29672f8ae750308d9a63'), size_on_disk=5311747, blob_last_accessed=1757598690.9468212, blob_last_modified=1757104406.477712), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466109/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/6588704c47423e671054df9cb2318c045138b0018f2eefff61ef91a25848eb58'), size_on_disk=5259413, blob_last_accessed=1757598690.8298213, blob_last_modified=1757104404.9987195), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466119/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/0157d9136c2c2bb5640866449adf4bab8c62b3d7f7fb4262981908c77986f89e'), size_on_disk=12516152, blob_last_accessed=1757598691.0068212, blob_last_modified=1757104412.3166823), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466120/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/4220a31e6a7be79341828d41da77ef77fb5a8026980d4445fec18c57b5229c1f'), size_on_disk=3644577, blob_last_accessed=1757598691.034821, blob_last_modified=1757104408.3467023)}), refs=frozenset(), last_modified=1757104417.837654), CachedRevisionInfo(commit_hash='6aac54b4b3fbb414561a8b6cc7acf87880c3c3c2', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6aac54b4b3fbb414561a8b6cc7acf87880c3c3c2'), size_on_disk=4665, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6aac54b4b3fbb414561a8b6cc7acf87880c3c3c2/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/14e1e02ad3804c0a4146a52509460e9cbb759d93'), size_on_disk=4665, blob_last_accessed=1758306843.4906857, blob_last_modified=1758156479.1516545)}), refs=frozenset(), last_modified=1758156479.1516545), CachedRevisionInfo(commit_hash='1e07e46fd0b5348243487ac4a573b570a2a3946b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1e07e46fd0b5348243487ac4a573b570a2a3946b'), size_on_disk=4683, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1e07e46fd0b5348243487ac4a573b570a2a3946b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/bb690d71edd44885609ed660d926f3cf2c4c473e'), size_on_disk=4683, blob_last_accessed=1758306349.7238934, blob_last_modified=1758156991.1235294)}), refs=frozenset({'main'}), last_modified=1758156991.1235294), CachedRevisionInfo(commit_hash='22ebbfcf62a7ed665fb8eceaea53b58b45a1709e', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/22ebbfcf62a7ed665fb8eceaea53b58b45a1709e'), size_on_disk=4602, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/22ebbfcf62a7ed665fb8eceaea53b58b45a1709e/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/e03b8d10ba59c549907a309ad8343133b7b902fc'), size_on_disk=4602, blob_last_accessed=1757613581.062093, blob_last_modified=1757613581.0610929)}), refs=frozenset(), last_modified=1757613581.0610929), CachedRevisionInfo(commit_hash='1acaa5629922414e8efe23a5d023bd44965824c8', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1acaa5629922414e8efe23a5d023bd44965824c8'), size_on_disk=4683, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1acaa5629922414e8efe23a5d023bd44965824c8/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/bb690d71edd44885609ed660d926f3cf2c4c473e'), size_on_disk=4683, blob_last_accessed=1758306349.7238934, blob_last_modified=1758156991.1235294)}), refs=frozenset(), last_modified=1758156991.1235294), CachedRevisionInfo(commit_hash='664d95cdd482d753bc139d26119061c2fa6eaa76', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/664d95cdd482d753bc139d26119061c2fa6eaa76'), size_on_disk=4679, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/664d95cdd482d753bc139d26119061c2fa6eaa76/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/ff6f75db42c0690f0e0285e56c1aa95bc5cf1300'), size_on_disk=4679, blob_last_accessed=1758157110.1705706, blob_last_modified=1758157110.1695707)}), refs=frozenset(), last_modified=1758157110.1695707), CachedRevisionInfo(commit_hash='3ce77eb2070d7bb43049ba26bb462fded0ff7863', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/3ce77eb2070d7bb43049ba26bb462fded0ff7863'), size_on_disk=15562566, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/3ce77eb2070d7bb43049ba26bb462fded0ff7863/genome_map/accession=SRR11466106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/04bacc0f8c03ee000ba3b69320522d0b1be909686474b6e8a126173a0ceae8c4'), size_on_disk=15547870, blob_last_accessed=1757598690.8528214, blob_last_modified=1756944357.1972551), CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/3ce77eb2070d7bb43049ba26bb462fded0ff7863/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1757101035.6211069, blob_last_modified=1756944310.9395182)}), refs=frozenset(), last_modified=1756944357.1972551)}), last_accessed=1758306843.4906857, last_modified=1758157110.1695707), CachedRepoInfo(repo_id='BrentLab/harbison_2004', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004'), size_on_disk=12305, nb_files=2, revisions=frozenset({CachedRevisionInfo(commit_hash='2916ad316cc0d8a1ce2e7f35d3707b831b203e87', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/2916ad316cc0d8a1ce2e7f35d3707b831b203e87'), size_on_disk=7160, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/2916ad316cc0d8a1ce2e7f35d3707b831b203e87/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/7e3ec66f5479346ca9d082545ff0b6a960fb60d5'), size_on_disk=7160, blob_last_accessed=1758155946.7689884, blob_last_modified=1758155946.7669883)}), refs=frozenset({'main'}), last_modified=1758155946.7669883), CachedRevisionInfo(commit_hash='073cdeb1b541c28b1a97df9dbc6be6af1d9c23b6', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/073cdeb1b541c28b1a97df9dbc6be6af1d9c23b6'), size_on_disk=5145, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/073cdeb1b541c28b1a97df9dbc6be6af1d9c23b6/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/f7a6cf3dd4477f08e508f38665350e8d11589963'), size_on_disk=5145, blob_last_accessed=1755876214.0380862, blob_last_modified=1755876188.1642818)}), refs=frozenset(), last_modified=1755876188.1642818)}), last_accessed=1758155946.7689884, last_modified=1758155946.7669883), CachedRepoInfo(repo_id='BrentLab/hughes_2006', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006'), size_on_disk=11943331, nb_files=8, revisions=frozenset({CachedRevisionInfo(commit_hash='cc20720c4fe7de9151a051aafbd41e6de2b4cd84', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84'), size_on_disk=11937630, files=frozenset({CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756856028.69735, blob_last_modified=1756856028.7793496), CachedFileInfo(file_name='overexpression.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/overexpression.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/9b8cc1feede780efe0e8537555ed1cbbf067e7fa9aa7a7edc74e5a343c64a443'), size_on_disk=6337781, blob_last_accessed=1756856028.536351, blob_last_modified=1756856029.230347), CachedFileInfo(file_name='metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/e27a67137876123c68c229705cfbb8e06ed3ee76a3a868b66d80d624f8d03671'), size_on_disk=7942, blob_last_accessed=1756856028.533351, blob_last_modified=1756856028.9083488), CachedFileInfo(file_name='checksum.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/checksum.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/973b686313e32c96b6e0ef1dbc84020857c073ef'), size_on_disk=159, blob_last_accessed=1756856028.5883508, blob_last_modified=1756856028.6513503), CachedFileInfo(file_name='parse_hughes_2006.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/scripts/parse_hughes_2006.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/0c2645bdc0ab7f6be38a428a0a92752e9d3f88d5'), size_on_disk=9255, blob_last_accessed=1756856028.5803509, blob_last_modified=1756856028.6393504), CachedFileInfo(file_name='knockout.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/knockout.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/5e000e2e9401011c9ecc81655c94d55b7f9469ed856216fa21cef2883a904c93'), size_on_disk=5571518, blob_last_accessed=1756856029.2493467, blob_last_modified=1756856029.2143471), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/842b551fb3d1ba1e6f1daa35157a0c7c616f7655'), size_on_disk=8514, blob_last_accessed=1756855276.4871445, blob_last_modified=1756855276.4861445)}), refs=frozenset({'main'}), last_modified=1756856029.230347), CachedRevisionInfo(commit_hash='51ae7addf429c955a97934a15b242ff8a2ccd653', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/51ae7addf429c955a97934a15b242ff8a2ccd653'), size_on_disk=5701, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/51ae7addf429c955a97934a15b242ff8a2ccd653/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/f799807a7bf81229b0d063e6c939339c1f5c90f4'), size_on_disk=5701, blob_last_accessed=1756855057.8437808, blob_last_modified=1756855057.8417807)}), refs=frozenset(), last_modified=1756855057.8417807)}), last_accessed=1756856029.2493467, last_modified=1756856029.230347), CachedRepoInfo(repo_id='BrentLab/hackett_2020', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020'), size_on_disk=407271569, nb_files=17, revisions=frozenset({CachedRevisionInfo(commit_hash='60b3ecf6a06e709f0397b327ece5c31016303b5c', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/60b3ecf6a06e709f0397b327ece5c31016303b5c'), size_on_disk=9543, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/60b3ecf6a06e709f0397b327ece5c31016303b5c/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/6f54100c2bb84a6bc059c17e0954dad0ea451666'), size_on_disk=9543, blob_last_accessed=1758400469.288775, blob_last_modified=1758155372.1328666)}), refs=frozenset({'main'}), last_modified=1758155372.1328666), CachedRevisionInfo(commit_hash='b9d39535e175295d9cba23489a49fafebbac5ca0', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0'), size_on_disk=404565563, files=frozenset({CachedFileInfo(file_name='hackett_2020.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/hackett_2020.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4f47951c65c799b5c74ee3782b49c16cbb038d50'), size_on_disk=55, blob_last_accessed=1757105553.3228161, blob_last_modified=1756843395.5719483), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756843395.5059488, blob_last_modified=1756843395.5679483), CachedFileInfo(file_name='parse_mcisaac_data.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/scripts/parse_mcisaac_data.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0'), size_on_disk=4691, blob_last_accessed=1756843395.7579472, blob_last_modified=1756843395.820947), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/03e579e00c632cdb393c0ffafd9d15b069390e5c'), size_on_disk=9066, blob_last_accessed=1757439136.0598524, blob_last_modified=1757105945.6476595), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1757541699.4460278, blob_last_modified=1756843405.225893)}), refs=frozenset(), last_modified=1757105945.6476595), CachedRevisionInfo(commit_hash='2c196866d4f057cb5cf0adf1fe35f2c712c61025', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025'), size_on_disk=404565376, files=frozenset({CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756843395.5059488, blob_last_modified=1756843395.5679483), CachedFileInfo(file_name='hackett_2020.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/hackett_2020.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4f47951c65c799b5c74ee3782b49c16cbb038d50'), size_on_disk=55, blob_last_accessed=1757105553.3228161, blob_last_modified=1756843395.5719483), CachedFileInfo(file_name='parse_mcisaac_data.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/scripts/parse_mcisaac_data.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0'), size_on_disk=4691, blob_last_accessed=1756843395.7579472, blob_last_modified=1756843395.820947), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1757541699.4460278, blob_last_modified=1756843405.225893), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c3e72ccb1b8deba4bbfd18abe6081de7ec3914d9'), size_on_disk=8879, blob_last_accessed=1757105552.290822, blob_last_modified=1757104206.2667632)}), refs=frozenset(), last_modified=1757104206.2667632), CachedRevisionInfo(commit_hash='1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43'), size_on_disk=2678425, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=20/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/97adc0bb2b436c325555d82bb7f0ffb0e250d73092fcbf03ac0c00fa2b762e51'), size_on_disk=351379, blob_last_accessed=1756145253.7895415, blob_last_modified=1756145254.6265287), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=10/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4a2eb1051cad96c5b39a014f19b092f042b03700cc1b1a40c2de9e7edd73edf9'), size_on_disk=352201, blob_last_accessed=1756145253.7915416, blob_last_modified=1756145254.6375287), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=0/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/409962e1914ac9e9d631e4ae73e04b167822d0915d8d8d4a87703d4c7281ffb9'), size_on_disk=209695, blob_last_accessed=1756145254.7305272, blob_last_modified=1756145254.608529), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=15/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/51dfb68ed2518f0dd8ab4d4070d4896b6eca912ecb7e4aa773d857e3096020b5'), size_on_disk=351273, blob_last_accessed=1756145217.972108, blob_last_modified=1756142799.7054222), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=90/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/ebd06af83cc386d9094ce6b8d795b155c76c47c96da1a458743348264f1638ae'), size_on_disk=349917, blob_last_accessed=1756145253.7845416, blob_last_modified=1756145254.7235274), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/45cb0687c3ea72e7b32ad17dd799bc38ad02f80b'), size_on_disk=16034, blob_last_accessed=1756138543.6766906, blob_last_modified=1756138543.6746907), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=5/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/ed331d9d2b27a0958b12368c7b5d2d3383b111d3d1c39ed0e77b78561dc78b6a'), size_on_disk=348626, blob_last_accessed=1756145253.7845416, blob_last_modified=1756145254.6395288), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=45/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/db8595cd20dd7440c890c46025cf0643da8ec6be3e15bc18a8759b3fecdc02af'), size_on_disk=349256, blob_last_accessed=1756145253.7795417, blob_last_modified=1756145254.6225288), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=30/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/a85bd6b418d9644d9adaa1269c27f97469a4aaee51af63cf1aa041f62cd8ba2c'), size_on_disk=350044, blob_last_accessed=1756145253.7915416, blob_last_modified=1756145254.6385286)}), refs=frozenset(), last_modified=1756145254.7235274), CachedRevisionInfo(commit_hash='7192b0e2275ba5aa3ffb3752b87f1c3595f2331a', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a'), size_on_disk=404565656, files=frozenset({CachedFileInfo(file_name='hackett_2020.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/hackett_2020.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4f47951c65c799b5c74ee3782b49c16cbb038d50'), size_on_disk=55, blob_last_accessed=1757105553.3228161, blob_last_modified=1756843395.5719483), CachedFileInfo(file_name='parse_mcisaac_data.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/scripts/parse_mcisaac_data.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0'), size_on_disk=4691, blob_last_accessed=1756843395.7579472, blob_last_modified=1756843395.820947), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756843395.5059488, blob_last_modified=1756843395.5679483), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/f3899cd97da03a3543db10d76c15ee0442bb136e'), size_on_disk=9159, blob_last_accessed=1758145252.5929751, blob_last_modified=1757541416.40237), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1757541699.4460278, blob_last_modified=1756843405.225893)}), refs=frozenset(), last_modified=1757541416.40237)}), last_accessed=1758400469.288775, last_modified=1758155372.1328666), CachedRepoInfo(repo_id='BrentLab/hu_2007_reimand_2010', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010'), size_on_disk=42682732, nb_files=4, revisions=frozenset({CachedRevisionInfo(commit_hash='31ccd35daf785420014a1346a7db064dd306d4f8', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8'), size_on_disk=42682732, files=frozenset({CachedFileInfo(file_name='hu_2007_reimand_2010.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/hu_2007_reimand_2010.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/eb9be5a1f01907a81eb2ab4fbbee36ff8c50186f'), size_on_disk=63, blob_last_accessed=1756844741.7997973, blob_last_modified=1756844741.8557973), CachedFileInfo(file_name='hu_2007_reimand_2010.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/hu_2007_reimand_2010.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/47a15f3e23f8cb50af3217d00c6439d29a7422b5190116e4453a5f78cb22540d'), size_on_disk=42677516, blob_last_accessed=1756844742.1787965, blob_last_modified=1756844742.1477966), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756844741.8097973, blob_last_modified=1756844741.8767972), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/886f4614dfd1ef33709d462908ae18608c13e8ba'), size_on_disk=2692, blob_last_accessed=1756844742.0937967, blob_last_modified=1756844742.1537964)}), refs=frozenset({'main'}), last_modified=1756844742.1537964)}), last_accessed=1756844742.1787965, last_modified=1756844742.1537964), CachedRepoInfo(repo_id='BrentLab/kemmeren_2014', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014'), size_on_disk=319226397, nb_files=3, revisions=frozenset({CachedRevisionInfo(commit_hash='a6c9a954a1def1774b68582f117ad1abc5864a07', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07'), size_on_disk=319226397, files=frozenset({CachedFileInfo(file_name='kemmeren_2014.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07/kemmeren_2014.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/3c463017444bd7690959c0d6fff0d0ebe2faa7b916dd9c44e305156f6c98b863'), size_on_disk=319218929, blob_last_accessed=1756832531.471449, blob_last_modified=1756832530.9644527), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756832529.5094638, blob_last_modified=1756832529.5434635), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/a29a64128c10ef9691ee773131972c61f720ba07'), size_on_disk=5007, blob_last_accessed=1756832529.5094638, blob_last_modified=1756832529.5404634)}), refs=frozenset({'main'}), last_modified=1756832530.9644527)}), last_accessed=1756832531.471449, last_modified=1756832530.9644527)}), warnings=[CorruptedCacheException(\"Snapshots dir doesn't exist in cached repo: /home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots\")])" + "HFCacheInfo(size_on_disk=5198144267, repos=frozenset({CachedRepoInfo(repo_id='BrentLab/barkai_compendium', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium'), size_on_disk=3579068970, nb_files=504, revisions=frozenset({CachedRevisionInfo(commit_hash='a987ef37c72fcd07b18828d320bc4305480daade', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade'), size_on_disk=3579068970, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417639/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/43c5fb0a4f049fa5895f8167928d047cc4954aa37db1fc8a9892fe9f99c401bf'), size_on_disk=782319, blob_last_accessed=1756926308.7694294, blob_last_modified=1756926309.6284256), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417694/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/058b507f75cb01bb5912e14e7bbb4e25a1de945e578323bce9f920f31a9ced82'), size_on_disk=11980395, blob_last_accessed=1756926320.09838, blob_last_modified=1756926321.5623736), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417643/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/66dfa3feea4410ba34f5d5d37c7b01e1fa2cdd4ff38f9dfb10d20d55e40e449f'), size_on_disk=1012185, blob_last_accessed=1756926309.326427, blob_last_modified=1756926310.0784237), CachedFileInfo(file_name='genome_map.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b8f0ed936cb43c4649126e9b7596cef0ab626a2d'), size_on_disk=142786, blob_last_accessed=1756926300.410466, blob_last_modified=1756926300.4954655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417701/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a0dd9587c7525a610a3794cf41f6db24a6ba22a12ed852dc27381a68db3e8fca'), size_on_disk=8781514, blob_last_accessed=1756926321.6273735, blob_last_modified=1756926323.1263669), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417706/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4f1ea5592a855f3bf5739b0caae1c69c7606276d125c3dfe8e98379d12bedf1b'), size_on_disk=1154142, blob_last_accessed=1756926322.9053679, blob_last_modified=1756926323.5173652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381054/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1a9f9c650973937d852d2de54c0d73336d1516184d29e203563fd9bf8d7fefa5'), size_on_disk=3346485, blob_last_accessed=1756926778.1077476, blob_last_modified=1756926778.8637433), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417619/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0a4d4159941caa34a5e079821d49047d1434077926b2933c27d384e04ed2e5b5'), size_on_disk=9949802, blob_last_accessed=1756926302.5744565, blob_last_modified=1756926304.05045), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380996/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/09e663ce2a60df5635f4e4ea77d6350e7752a9bb5e81fbdfd818869ac53ca805'), size_on_disk=13309208, blob_last_accessed=1756926375.0511415, blob_last_modified=1756926376.0221374), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380977/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/74237b16fd38090785d203d96bae72e1f5ce919d423f793cfb9d3b86623358e6'), size_on_disk=11114601, blob_last_accessed=1756926371.6651561, blob_last_modified=1756926373.12615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417906/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/34d3381dd45e579dcc298e91cc95174c4219cf548401b8f2392cf746fd508c5b'), size_on_disk=13795318, blob_last_accessed=1756926356.3642225, blob_last_modified=1756926357.640217), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417920/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/00a869703f03e3df3f0f51a7d93d52dd7eac5174ed2a1b752207460e71c7b41a'), size_on_disk=14880236, blob_last_accessed=1756926359.1242106, blob_last_modified=1756926360.409205), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381060/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1bb05aa71fe55db65fc3868b131854c1e6666dca8cc370874f517aa0756d21ac'), size_on_disk=9137992, blob_last_accessed=1756926778.9637427, blob_last_modified=1756926780.2307358), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417890/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3eca80a3220fd0294fdfca83740ba70a20af764d28833494948705c4d542ded3'), size_on_disk=9018889, blob_last_accessed=1756926353.7652338, blob_last_modified=1756926354.7252295), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380943/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/987771cb978dbc949eff5044071149a00223c6771efcc870850e3c8cbe0648c3'), size_on_disk=13397144, blob_last_accessed=1756926367.295175, blob_last_modified=1756926368.7601688), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417662/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/90008c75533e5a395bf92b2a4382cfb8d6c27af4921615cac22bc1965375efb5'), size_on_disk=12701661, blob_last_accessed=1756926313.0204108, blob_last_modified=1756926314.158406), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417752/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/efc9f1ecf4a7b06b23da46fb8c7d1879b67a9d6603ed753d09e2028396ec05c5'), size_on_disk=3420764, blob_last_accessed=1756926329.26534, blob_last_modified=1756926330.0613368), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417848/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eec4ffff5d145c0ee0850d5e68787ab90e6510cffa5803579db44e3a575822dc'), size_on_disk=13556826, blob_last_accessed=1756926346.1882668, blob_last_modified=1756926347.2852619), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380950/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f508a5da82832b920577592b853da2c2d76ebf0adca587e10b63507f46dc5066'), size_on_disk=3452513, blob_last_accessed=1756926368.7781687, blob_last_modified=1756926369.7081647), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417940/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/270559ca1db4605de241e8642461b4d7304d124e4fc624f7d8a8a59867301a4c'), size_on_disk=2328150, blob_last_accessed=1756926363.182193, blob_last_modified=1756926363.9021897), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417730/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f2554716083cfa1bb54c2098315b92e1b3537eb265da884c00ba7e7d4c050da'), size_on_disk=14980864, blob_last_accessed=1756926325.5143564, blob_last_modified=1756926326.803351), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417632/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/23c561fde2e6c2973ec1913373c343a5c49d6cc0dbb44ff21eb5aff87cd77d3a'), size_on_disk=18481828, blob_last_accessed=1756926306.2764404, blob_last_modified=1756926308.5744302), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417736/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/671a67d7275810840cafc9db32e43385c4ba61b007d702e7eb5cb244a0e4afa9'), size_on_disk=13855124, blob_last_accessed=1756926326.1693537, blob_last_modified=1756926327.261349), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417684/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eee25a9a91c54d0404db884532d1b85555f2e1de0c664abcbda4343f9d729842'), size_on_disk=10310520, blob_last_accessed=1756926317.7173905, blob_last_modified=1756926319.3473833), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417761/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ba86b2041414034d38b95ccbf3c65957642c56b56914240999986ebc76b9e193'), size_on_disk=1635118, blob_last_accessed=1756926330.478335, blob_last_modified=1756926331.0933323), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417813/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f7d5239d0d39e171aad1e84964f8cdc3c7a325fe8c38b6829e114cb3997e658'), size_on_disk=10238322, blob_last_accessed=1756926339.4142962, blob_last_modified=1756926340.8722897), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417823/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1887333f71d11fe7c43748662f58d9ba3fb12f3f6ca30a61d7e1c7a0c95f7064'), size_on_disk=9444476, blob_last_accessed=1756926341.6372864, blob_last_modified=1756926342.8442812), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417791/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8d2eb1bdd1a5cae023abb271e5858cf111879953cb0d6267f4ebf21a3d30c4ad'), size_on_disk=5232904, blob_last_accessed=1756926335.2033143, blob_last_modified=1756926336.417309), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417915/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9fbfc50c3e1f756169d25442880ba33e5cf237e3c83a0d674b64ace47e8d3322'), size_on_disk=13801295, blob_last_accessed=1756926358.318214, blob_last_modified=1756926359.4592092), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417614/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dac699f09872b0c58b742c692b341f0b00b7d83284cec5ef838c276a6f48908c'), size_on_disk=1759475, blob_last_accessed=1756926301.7274601, blob_last_modified=1756926302.4774568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417648/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fb706eb700e8fc003822b897accd3501360d737fd1a9ffcf26c3e9d9b25a5e35'), size_on_disk=1285054, blob_last_accessed=1756926310.3104227, blob_last_modified=1756926311.0174196), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381013/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c831ca7cc9cfd2cc2ccd92a0168b2e14dedf2bde9e29780dfaf6d281b31e9462'), size_on_disk=4648281, blob_last_accessed=1756926377.4421313, blob_last_modified=1756926378.3451273), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381040/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a7c622a8ae1f5083eef26ecbcc2444055a9265b674ae80002e5468c94c0eb070'), size_on_disk=9682671, blob_last_accessed=1756926774.782766, blob_last_modified=1756926776.7967548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417797/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/12ee278356fa75b0ad555fe06376c78b71630324b7b89aa28abe960c187d256f'), size_on_disk=14391201, blob_last_accessed=1756926336.3743093, blob_last_modified=1756926338.322301), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417665/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7c9b3d448e53fcac9334df092cc003f438cbc462e6785ac3d61202bd65a7125f'), size_on_disk=7481605, blob_last_accessed=1756926313.490409, blob_last_modified=1756926314.7744033), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417914/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d062dbf660e1ef240ff8ef99ff2d486d9caaabda14575e6365ce6f725fa91ccf'), size_on_disk=8599250, blob_last_accessed=1756926358.2422144, blob_last_modified=1756926360.3412054), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381018/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/878bf028aca9daeebe7ed0f8ee522580d512771ec4a5e35bfc62ac675176ad15'), size_on_disk=701951, blob_last_accessed=1756926378.2281277, blob_last_modified=1756926378.643126), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8168980e59c50149bfb9866a1e8855c235f2882b988df99331c9247f65e9813e'), size_on_disk=2289686, blob_last_accessed=1756926376.4721353, blob_last_modified=1756926377.2151322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417725/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4195b9ed6506437a735fd8061cd1b79211c99169c4319c4f72b801a501f2b0f9'), size_on_disk=2233032, blob_last_accessed=1756926324.958359, blob_last_modified=1756926325.594356), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417863/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4aabb9d1a4b105c7669deb0f13cd635ab7433ed1ae02f5785aad014d604f1504'), size_on_disk=6900487, blob_last_accessed=1756926348.4532568, blob_last_modified=1756926349.2742534), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417617/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9ad746ec09ed51ed497d87f3dd64b11916b70e6833ce66141bc8feb6ba73d1d3'), size_on_disk=23082279, blob_last_accessed=1756926302.1754582, blob_last_modified=1756926303.6944516), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417943/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0e4191a403f5070636bcb281bd80d39e11c70fb502fa2b18150032d58f2e0740'), size_on_disk=5396199, blob_last_accessed=1756926363.7271905, blob_last_modified=1756926364.542187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417790/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f88bed27521de8285bbb92cf95242820116e6fa2c23ab578e6608a6a25e0c04e'), size_on_disk=10836858, blob_last_accessed=1756926335.078315, blob_last_modified=1756926336.1843102), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417794/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f15be566c029c52cf46f6258e589529afb3a002f010710a54d04dd6151f8b684'), size_on_disk=10205023, blob_last_accessed=1756926335.9063113, blob_last_modified=1756926337.151306), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380952/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3811d6e6194d8b88c84fc0931cba844da23677e76e1e9a25e2890df968a7e529'), size_on_disk=1003172, blob_last_accessed=1756926368.8351684, blob_last_modified=1756926369.4641657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417847/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4dd39704bc9c1d60a8271f16206887171ee92abde023c53501c1560533a688c1'), size_on_disk=4755120, blob_last_accessed=1756926346.125267, blob_last_modified=1756926346.8372638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381023/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/22e3e0f4cd1f2c755ec1ac13b99d2d6c98d0158319478f2deca4e89aefc5e88e'), size_on_disk=3860195, blob_last_accessed=1756926378.5651264, blob_last_modified=1756926379.6101217), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417827/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8bfd1221f930e86ffd6150c82aae85f00a23fff33c672e40f47d671f5a2e4d17'), size_on_disk=7291599, blob_last_accessed=1756926342.156284, blob_last_modified=1756926343.1502798), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417886/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62fe87e7519370c4ac46e6d9d0abf314a8f92d9a5d6804ebddae0ed095641219'), size_on_disk=7968977, blob_last_accessed=1756926352.9472373, blob_last_modified=1756926353.7992337), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380934/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d264f2a91c87696f6a07f573f1d921c91e1d53e3afca59610ba4703b3683b617'), size_on_disk=4601537, blob_last_accessed=1756926366.1091802, blob_last_modified=1756926366.9191768), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417609/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e3d8451242fe9ecf9f82e2ac1a406144840a6182178228e6e1fa67e6a04a318a'), size_on_disk=3176651, blob_last_accessed=1756926300.651465, blob_last_modified=1756926302.0864584), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417849/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/23b6606ffcf116cf066b103bf1b6b4fb33c40e544e20e4565fa44c5bbcea4bdd'), size_on_disk=5689527, blob_last_accessed=1756926346.584265, blob_last_modified=1756926347.3892615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417808/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0251ae27bd961d6c962253c555963b42148092cdaa2616a78f6872cf1a4e5e4d'), size_on_disk=12900784, blob_last_accessed=1756926338.3973005, blob_last_modified=1756926339.439296), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417628/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/291f0e4d2d0fa1656de1583bd9e131fd4d2fc1934d0bb70a6774f7e7f90e62ae'), size_on_disk=15444530, blob_last_accessed=1756926305.1314452, blob_last_modified=1756926307.1334364), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417671/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bced5a56092fea7de5754bebc33caaab643f27236f8d9020e7bc6b30b9045d6e'), size_on_disk=5347180, blob_last_accessed=1756926314.7704034, blob_last_modified=1756926315.8293986), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381062/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/76151e17f242c5108010debea961b0b13f3821be2fcb77fd923a36cc0e672695'), size_on_disk=4880117, blob_last_accessed=1756926779.3357406, blob_last_modified=1756926780.5167341), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417843/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6569e23f0eb660e9f7111b9cdd69e7ae797ba265647ee44e4cbf2d8babdeca9e'), size_on_disk=2706587, blob_last_accessed=1756926345.3402703, blob_last_modified=1756926346.125267), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c103eef2a9834296fb1328b7012b5b9184c5a3f131817dfca257843e863d34b2'), size_on_disk=9829417, blob_last_accessed=1756926362.1531975, blob_last_modified=1756926363.3531923), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bbfeb8571ee8915d793698c2116cef9f2ea61573c66b4ae7066078271c6172d6'), size_on_disk=5132301, blob_last_accessed=1756926362.6441953, blob_last_modified=1756926363.7271905), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417756/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ede027f2c13a1e6669becf6194ace85af5800d5fe77db16ec47bfb3abaa735d3'), size_on_disk=8620421, blob_last_accessed=1756926329.968337, blob_last_modified=1756926331.0303326), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417700/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1240fbb2497074bb7ff98b1bc6399b8a8ad5d320246285abfb6738e27e2bbdeb'), size_on_disk=19312447, blob_last_accessed=1756926321.6763732, blob_last_modified=1756926322.8383682), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380951/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b9b68679dd2da94e4aa4852ae3de3ee5b823c1fb1483f4ece3eee5d947014785'), size_on_disk=1154119, blob_last_accessed=1756926368.8281684, blob_last_modified=1756926369.410166), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417687/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fa4f79173f5a5ed278c7ef80d9fb220aa9c615de97bae7963969e27ecf0e587c'), size_on_disk=13352339, blob_last_accessed=1756926318.3003879, blob_last_modified=1756926320.3913789), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3fe2828766efd936f02efae2da3e068ef9b28ea374e594e09850e3e8ff0d8164'), size_on_disk=4421540, blob_last_accessed=1756926375.7961383, blob_last_modified=1756926376.7581341), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417642/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/591f688d0d9f98350a1d636e6f93ed37563f3ea9765ca6b633490533b0cf32d4'), size_on_disk=11498542, blob_last_accessed=1756926309.2804272, blob_last_modified=1756926310.8714201), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417817/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ccf81aa26021a009a212ef8f8bee71c12f5c8d0a2b97ffdb2c4d86d13c8b8133'), size_on_disk=7521086, blob_last_accessed=1756926340.0802932, blob_last_modified=1756926341.3222878), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381047/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dda6f6ca669a0e5c9d34b548305cbeb6b82f1b70086e06d3604e7e096fd7fb25'), size_on_disk=6799004, blob_last_accessed=1756926776.8777544, blob_last_modified=1756926778.4717455), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417689/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e27d1d153e5b135a09ec5db80bb05ace721fd60ac90f0693ac34ca3160fdfbdd'), size_on_disk=5015020, blob_last_accessed=1756926319.2973835, blob_last_modified=1756926321.220375), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417909/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c5de46a74c16aee32dfb2be89a50dceaa5704c0c550076dcec483d1c21d983af'), size_on_disk=5969725, blob_last_accessed=1756926357.3362184, blob_last_modified=1756926358.5162132), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417634/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/529d984782684991b2eb7be2d345b5a4e8a6e08d3dbe30ad34495d8380464e6e'), size_on_disk=16306189, blob_last_accessed=1756926306.810438, blob_last_modified=1756926310.239423), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417668/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b426513aab4e071a24b7d27100a4da4da8c71c8e9b3e82163808b0ec6a42b8d0'), size_on_disk=6011682, blob_last_accessed=1756926314.5374043, blob_last_modified=1756926315.4354005), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417947/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/50bb9cc74904c9d3b6cc3ec8bc12c7ad1fb89204be6f89a863e703a94d224512'), size_on_disk=4023877, blob_last_accessed=1756926364.0161893, blob_last_modified=1756926364.9561853), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417895/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2daca9b311da1afa40f578c6df7dc4b0039dd4857663c98cd87c665a547b7144'), size_on_disk=11915424, blob_last_accessed=1756926354.7232296, blob_last_modified=1756926355.8262248), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380933/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/07c22e11c56ce5ecb6c22c910c3d3a0d9b54fd341bef0513b4bf42a9c53ec6a0'), size_on_disk=10138164, blob_last_accessed=1756926365.8761814, blob_last_modified=1756926366.7471776), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417789/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6bd6339b995a5730b6b65ec65f680b57195469ebee9f2e15de6e604b069d4b0e'), size_on_disk=9612750, blob_last_accessed=1756926334.6493168, blob_last_modified=1756926335.8183117), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4c942b91ac018330aeb4da512a3e47ae4531a434b6e9933cf3e2bb476a77f9ca'), size_on_disk=6312758, blob_last_accessed=1756926366.1041803, blob_last_modified=1756926367.0141764), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381043/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f6ded8e6496dcfdd499edc382d19da2071f25daebbb250b375b9a2706e9d8211'), size_on_disk=9139336, blob_last_accessed=1756926775.6057615, blob_last_modified=1756926777.1057532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417683/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6131abcd72bd4c6a8627685c4df8a295af50e824f52a5cde156adae7cd694bba'), size_on_disk=15545733, blob_last_accessed=1756926317.382392, blob_last_modified=1756926319.0483847), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417766/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/39da4f16b82cc0b5019d8526f2e3eed49de9760e319f8e839db621bf4c1ab021'), size_on_disk=6482957, blob_last_accessed=1756926331.2123318, blob_last_modified=1756926332.1133277), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417613/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/695d3095619f3a313d25c506b9d0493f171896f1439bcf3bd1cd932d1f6fed5d'), size_on_disk=2812565, blob_last_accessed=1756926301.6434605, blob_last_modified=1756926302.4814568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380936/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5a931bc1181001314c3822051c493ba7a8b87b5bb26b8866783612f2bcf49c62'), size_on_disk=3467608, blob_last_accessed=1756926366.19118, blob_last_modified=1756926367.2261755), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381015/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/345c83c5ab13d230542683014d3d6073ba5a348e3063fea325843981c6c6e7c1'), size_on_disk=3807314, blob_last_accessed=1756926377.67913, blob_last_modified=1756926378.4971266), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381036/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ff76dabe83d2948240dd846fcc2a969380b7bd9f8d2eff2c71c299f0b14134cd'), size_on_disk=2593393, blob_last_accessed=1756926379.8661208, blob_last_modified=1756926380.5611176), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417675/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/79072181ebce12769f2f0a1971977d6691dde1841b53d7b54cf9d4f47f53b3dc'), size_on_disk=6478433, blob_last_accessed=1756926315.763399, blob_last_modified=1756926317.2643924), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417806/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2befcad73b2aa8170ceaf8c249ceba45e87037771e86c9bb4bd7addc5815bbec'), size_on_disk=5276603, blob_last_accessed=1756926337.9863024, blob_last_modified=1756926338.8112986), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380979/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef743a2a6a8fe44b62963b981ef0429676e7854243ab07d450d9c3ab569c7e8e'), size_on_disk=3735979, blob_last_accessed=1756926372.2201538, blob_last_modified=1756926373.6791475), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417656/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/03f8707a323ef7563c8ec2d6352d24c6454d043c4bbbca417680df5c7db208f8'), size_on_disk=10029839, blob_last_accessed=1756926311.588417, blob_last_modified=1756926312.8104117), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380928/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/251ffcb8e06f2ac0db0791065248327a9d1eeef33a07f5c6b25946e04b6c46d8'), size_on_disk=2289693, blob_last_accessed=1756926365.037185, blob_last_modified=1756926366.1961799), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417674/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5970db11c3efe2bba4eb50d07480cb0b46ed74dfc3ddf12d611ecd61f1d5fb68'), size_on_disk=4619941, blob_last_accessed=1756926315.5114, blob_last_modified=1756926317.630391), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417667/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef18f0ec136ef4b0facd217b32604556a7c9f514d75e26d2be17eb8da9b74a14'), size_on_disk=7979245, blob_last_accessed=1756926314.2284057, blob_last_modified=1756926315.734399), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fb5958ec4e10f5c7e19ebc55b24f26012e6d8ccf'), size_on_disk=8004, blob_last_accessed=1756926300.2594666, blob_last_modified=1756926300.3214662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417931/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7e70415ed0dfb1eb42e46789c905ccf2b593dc192d907d939d87acd59cd963e1'), size_on_disk=4991063, blob_last_accessed=1756926361.306201, blob_last_modified=1756926363.1161933), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417825/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/efc4971b63b21d6aee96e17d9e9b2cc2e3fd34608cc3722e36032c594fc03e48'), size_on_disk=8550531, blob_last_accessed=1756926341.8732853, blob_last_modified=1756926345.0782714), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417907/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b24926bd36cf33ea9107043b9a47db8239ad6208745c56002513225e8cb80a6e'), size_on_disk=7594380, blob_last_accessed=1756926356.7332208, blob_last_modified=1756926357.882216), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381068/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/16e4412527449a3cb9d2da129c3309a71b2ec9b33a7a39cb9bb31d41ce5d2e04'), size_on_disk=14909067, blob_last_accessed=1756926780.5687337, blob_last_modified=1756926782.3187242), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417753/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a8969c75211310735ad33d5af57327f2babc86276adc7cefcda254cc9fc9baa5'), size_on_disk=6433095, blob_last_accessed=1756926329.577339, blob_last_modified=1756926330.4023352), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417750/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aedd94fe4b42547108a807242052397b6dfbfda0eb4e56a9f2e267b312251147'), size_on_disk=1531584, blob_last_accessed=1756926329.1973405, blob_last_modified=1756926329.9403372), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381020/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c85f953439e9f6918c7f7c8194bcbbebe0ef8a96070119813025a42bde8c08fe'), size_on_disk=897644, blob_last_accessed=1756926378.3451273, blob_last_modified=1756926378.7061257), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381045/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdc8f9e6447280a7fc713c2a8f2157eb9076a5c01a7eb8437176e9ee30d6d4ab'), size_on_disk=4939421, blob_last_accessed=1756926776.3437574, blob_last_modified=1756926777.2507522), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417844/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ae034bb207abc0c649fb60aae78e798d46647f53b4e0352c6510fb7c8339a234'), size_on_disk=5408445, blob_last_accessed=1756926345.5212696, blob_last_modified=1756926346.4342656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417830/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/21e2dbb49f07cb7c2d7277f5bf1a442e216a2991677c3c4ece5c51316795ea77'), size_on_disk=12760016, blob_last_accessed=1756926342.9432807, blob_last_modified=1756926344.1602755), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417928/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/97e74e01fd641bfef68b6206a3c62ab41b5c28d8ea8369ce4198df1b3387b266'), size_on_disk=2449472, blob_last_accessed=1756926360.9072027, blob_last_modified=1756926361.3542008), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417760/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0500e5835116a61477f2d98554d0dd97e8d932a6b564f709c40c4884f2b49052'), size_on_disk=2234621, blob_last_accessed=1756926330.2873356, blob_last_modified=1756926330.8273335), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417708/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b58038866c334daaf757a086f99c3a5c5962b8563769ee0a3205bcdcbb0144ec'), size_on_disk=250863, blob_last_accessed=1756926323.0003674, blob_last_modified=1756926323.366366), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417884/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ff7d3efa4a231f5130be299d8d0ff67fe4973856e4db18e8a6c53d47ba6b28a7'), size_on_disk=11684836, blob_last_accessed=1756926352.791238, blob_last_modified=1756926354.0932324), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417918/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9f1cf3832969364b6cee1c817d3583a554b765bdbc1ff3f5b29692b7ea98ee7e'), size_on_disk=6936136, blob_last_accessed=1756926358.5802128, blob_last_modified=1756926360.9132028), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417901/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/69432386b347f5b364c02d270b38891358fa2068465354d1ada84d5450ea5bf0'), size_on_disk=9484337, blob_last_accessed=1756926355.6292257, blob_last_modified=1756926357.0582194), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381070/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/32c8f4c4efdbe6e137add61b0e31c27ae6a9eb5e8b463e9e4410a27cc51a57d3'), size_on_disk=29864184, blob_last_accessed=1756926780.7487328, blob_last_modified=1756926783.3167186), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417670/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5e36b63ef1fbb55f821ed2c3d61f0d0bf86cf00846e86ff9dbe6f4d9597f9dc8'), size_on_disk=10881106, blob_last_accessed=1756926314.7704034, blob_last_modified=1756926315.980398), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380973/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/343eb57057b5af866386fb4c02d0c41b888e206f5473ed7b4041200cfb93dbd5'), size_on_disk=1511721, blob_last_accessed=1756926371.3611574, blob_last_modified=1756926372.4931526), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381017/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1d9b2dbb5079eca871182c2dce1d681b943f1362072c18e255a4bd6d1068b840'), size_on_disk=2436549, blob_last_accessed=1756926378.161128, blob_last_modified=1756926378.7931254), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417660/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c394d7b0cfca3f3782dca86380e1a55903111d32efde98173af8a0b726ef5e7d'), size_on_disk=6441490, blob_last_accessed=1756926312.4744132, blob_last_modified=1756926313.4024093), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417840/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7e32bef3c0f7f7520382325aba12f5cf9405c22ebce8554b1d877fe24d2da52b'), size_on_disk=9679209, blob_last_accessed=1756926345.0092719, blob_last_modified=1756926346.0542672), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417882/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ac2bd9710196abc4469d4d78ccbde6ddc67e9ef39ade7d50c32b0d951866e653'), size_on_disk=10076041, blob_last_accessed=1756926352.116241, blob_last_modified=1756926353.223236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417746/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/432442dbdd5578acedcbf6eddc14cfe0d460facc7b0dea857225fbd4e6a4242c'), size_on_disk=2726090, blob_last_accessed=1756926328.6353428, blob_last_modified=1756926329.1983404), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417625/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/96b04f28e74e25bd1502a4ea9aca8047bfa8462e61cced0d726af4a336e1a585'), size_on_disk=17384720, blob_last_accessed=1756926304.4014485, blob_last_modified=1756926306.2074406), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417720/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/961772a081bde1795af5caea9d7562e2b1f889981bd3c3f1c792ad9093a758d7'), size_on_disk=659095, blob_last_accessed=1756926324.1233625, blob_last_modified=1756926324.6243603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380947/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3d6062f97d18f4945c36590355a312df2a16206c26626392cfb44d9613aea49d'), size_on_disk=9201129, blob_last_accessed=1756926368.5321698, blob_last_modified=1756926369.383166), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381063/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1e4361a7d20f8e92e8efc238365e0ed06eb021243864321c93d4638fa21634c4'), size_on_disk=10916323, blob_last_accessed=1756926779.3497405, blob_last_modified=1756926780.685733), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417768/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8bb8e088eb5123049e418c868ab42d94fb48946f934a8313f4cf283e3bfa09ba'), size_on_disk=4124375, blob_last_accessed=1756926331.3153312, blob_last_modified=1756926332.2313273), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417845/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ca2b89837de406b10d72533a3e3d5457b3512b704f7cea395fb0e1b88828393e'), size_on_disk=4648586, blob_last_accessed=1756926345.6932688, blob_last_modified=1756926346.5862648), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417861/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef65c45e0e86b7d04e274c39408b332cc94879c0804f16acf7c537b14bb6498c'), size_on_disk=10436399, blob_last_accessed=1756926348.3832572, blob_last_modified=1756926349.6192517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417894/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/12f262a12783317d135ffc5e1d281a3d43b746a82ed8053c15de824b94bd5ef8'), size_on_disk=6449687, blob_last_accessed=1756926354.4772308, blob_last_modified=1756926355.4742265), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417677/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cfcdac65879c0c26771c1cd1c4a656d4db20e6adb2ed3ad833cf23cd35a9d848'), size_on_disk=13135194, blob_last_accessed=1756926315.9083984, blob_last_modified=1756926318.1283886), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380942/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a958d997c1aff07ec07304485ee818280bc7714dd9c485ea73ef7fda81f2fe63'), size_on_disk=11407010, blob_last_accessed=1756926367.109176, blob_last_modified=1756926368.6211693), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380925/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdf190d30101829153acfd62edd4d689ac65a7d737f0e80978c7d460819b5caa'), size_on_disk=9220100, blob_last_accessed=1756926364.6101868, blob_last_modified=1756926366.1331801), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417854/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d52c8b14e197b179a9b43fbc89bb3205d09d458161ee49ac15a62b7c1f7a39fe'), size_on_disk=6203801, blob_last_accessed=1756926347.311262, blob_last_modified=1756926348.387257), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417717/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/689842dffacf7efa4ba6cd9b4314d195e89f9105eb71675f27019a9e25be63db'), size_on_disk=102777, blob_last_accessed=1756926323.9603631, blob_last_modified=1756926324.5793605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380966/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f27a802d18bf78781f3d168370e0c0e241a130d04a5871d7416442be3ce3e40f'), size_on_disk=4241927, blob_last_accessed=1756926370.3881617, blob_last_modified=1756926371.272158), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380986/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f23d3060df5cb96778fbdf10fff0155c931e1a1e8cc077c7f1582542d54827f5'), size_on_disk=5858046, blob_last_accessed=1756926373.285149, blob_last_modified=1756926374.5971434), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417896/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/71e393229e9071bad31abe5dc3d41bd5d719a5dcb68077827861b4b4e9a49a85'), size_on_disk=6055777, blob_last_accessed=1756926354.7882292, blob_last_modified=1756926355.8442247), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381022/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/980ed07a51dfad581fc2f7bce44031c92445822ba7e994cbfc9b5508cdf97c85'), size_on_disk=2103246, blob_last_accessed=1756926378.5151265, blob_last_modified=1756926378.9441247), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417949/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6123bc7bd312e6e37fabadee561c7dacad32d26bada92c9785c783f94fc50a37'), size_on_disk=1320707, blob_last_accessed=1756926364.4151876, blob_last_modified=1756926365.0891848), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417740/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e91586589c967e7bcbd521989874b21c024da79365d0d9916ffc66b774043e01'), size_on_disk=11875677, blob_last_accessed=1756926326.8903506, blob_last_modified=1756926327.6843472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380963/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c0a30240a0a99347e114928a15a24268495fd37480fde6822f503438bdf3ed48'), size_on_disk=579777, blob_last_accessed=1756926370.2681623, blob_last_modified=1756926370.81716), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417912/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4451877e599fa65e1f00f67b5fcd41fe74719d8bcb19ddacac419fb347c77cf5'), size_on_disk=4943909, blob_last_accessed=1756926357.7152166, blob_last_modified=1756926358.6302128), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417757/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6c9c5dbd0513759ace8599b2542a1f99fe0c30feb318665ebdb55bfb111c8c67'), size_on_disk=5834465, blob_last_accessed=1756926330.011337, blob_last_modified=1756926330.7623336), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381011/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/839fea471c67e08e0b68ae6344432c76da559e67c505c3f6120b20db2f1f753e'), size_on_disk=4378212, blob_last_accessed=1756926377.2971318, blob_last_modified=1756926378.0891285), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381049/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c571bcc5f87a79c7cd38b9614ee83b0d5cc742094e17a7990c829394fca193be'), size_on_disk=3753119, blob_last_accessed=1756926777.145753, blob_last_modified=1756926778.2097468), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417828/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eb4987a8ef94e1691c74c1d828d7d9313da4691b577118a73524def2a90c6d0b'), size_on_disk=4827634, blob_last_accessed=1756926342.3472834, blob_last_modified=1756926343.2782793), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417604/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e6dd3abdf75462f1f5d16f19383b98a8a4c817dc3b13c7cea2b4bea611c40c62'), size_on_disk=961320, blob_last_accessed=1756926300.390466, blob_last_modified=1756926301.3534617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417922/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fba17c65fa210922e57c533f5146b4237677149a5022fb836c1cd52dc89855b2'), size_on_disk=10194446, blob_last_accessed=1756926359.5812085, blob_last_modified=1756926360.610204), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417631/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b4b8c985528935ec46c6c7e78482af1ed26238137fc63b2196c6631e2d3796ab'), size_on_disk=27651513, blob_last_accessed=1756926306.2814403, blob_last_modified=1756926309.1994276), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417692/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/82405dc0a21bd225cc6a9e813e62d4dc0522719abf1e677c954a6ec38763b306'), size_on_disk=4873514, blob_last_accessed=1756926319.6253822, blob_last_modified=1756926320.6443777), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417898/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ced2c80d4f9d2aa28260b885a20b1c90b41d8eb54ee92f418ed592b97b954a9e'), size_on_disk=6422129, blob_last_accessed=1756926355.1262279, blob_last_modified=1756926356.0962236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417864/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/15d259c52e80a2d2d9c20274ef940237c90001d32f2aecb80779335a708222ff'), size_on_disk=3706383, blob_last_accessed=1756926348.5722563, blob_last_modified=1756926350.0962496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417826/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3532f474125bbdc0445e46fe8be83801ca9c079afb1dedd7cd95e25994c50d17'), size_on_disk=11492099, blob_last_accessed=1756926341.9132853, blob_last_modified=1756926343.2502794), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380949/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/65090ef93158269c98bc2e747e505cea9d460f0959d0b2957e58543cd1944163'), size_on_disk=2110872, blob_last_accessed=1756926368.705169, blob_last_modified=1756926369.142167), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417818/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3b30217b127ff88f165762c5054c392d9489b9007ba7ca8b86f18b44f9988e4d'), size_on_disk=8388230, blob_last_accessed=1756926340.2372925, blob_last_modified=1756926341.5632868), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417620/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b973f1a83f111c4e114a40d3731ff24779cafcb7b71523fe8571cf2ed849b316'), size_on_disk=20780203, blob_last_accessed=1756926302.5494566, blob_last_modified=1756926306.7334383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380946/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62fe0084bf7a8f9f484e76366f203e1c68862a211d5d501c08e19b8f28678d6b'), size_on_disk=8090103, blob_last_accessed=1756926367.6871734, blob_last_modified=1756926368.7681687), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417698/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d240b652ebca64784e037cc8ffbda424e2c92f368c5bb9c730930f48f29facc2'), size_on_disk=1855826, blob_last_accessed=1756926321.2993748, blob_last_modified=1756926322.0703714), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380929/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aeddf7cf3aee11ee08c90d305e66fc682e675959c171ecfaa591bc1d9015ebbc'), size_on_disk=6656759, blob_last_accessed=1756926365.1011846, blob_last_modified=1756926366.0181806), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417807/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ebc732f5d06d745bb63af3c7f5966722972fa722ef9d0194d71cf6d9485fda83'), size_on_disk=14049230, blob_last_accessed=1756926338.268301, blob_last_modified=1756926339.5272956), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417637/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ca1a954c7b2aca56529daf9a89249ad4e6ec2373e29ac7c19d2af1533afc4263'), size_on_disk=2776397, blob_last_accessed=1756926307.8804333, blob_last_modified=1756926309.1794276), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417821/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c29db68a3430cf8cb01716d90a92105387777ed1361992eb9501271c35359cfb'), size_on_disk=6597050, blob_last_accessed=1756926341.1692884, blob_last_modified=1756926342.2702837), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417929/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/85b194132215311994e48908799e31357d1094663a232e8170a4edcfa5a11eff'), size_on_disk=9155231, blob_last_accessed=1756926360.9862025, blob_last_modified=1756926361.9231985), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380955/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/369e0f1de51ea197e928618e562613eeed2ed59ce5c1389ce07ff6dd96d391b4'), size_on_disk=3596907, blob_last_accessed=1756926369.4561658, blob_last_modified=1756926370.2661622), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417743/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d4eb93049648498d2fdc1f125574429a4dfdec65e626ad9fa16bdf0d0cf599d8'), size_on_disk=2280534, blob_last_accessed=1756926327.462348, blob_last_modified=1756926329.1343408), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417788/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fd6f44471f1b86568bc059f25e5ccec7bda2b85b9f8be536aeaf2305fe015f5d'), size_on_disk=9112879, blob_last_accessed=1756926334.546317, blob_last_modified=1756926335.971311), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380945/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/51efeee4439e81d920c4d9e860de321d90c72703600044e52215c774e1540650'), size_on_disk=6902367, blob_last_accessed=1756926367.356175, blob_last_modified=1756926368.6011696), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381059/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/59daa71f55c9c0da6f8d56fc2497491477312fbdbcb9e92d4951874f2ed72686'), size_on_disk=13124154, blob_last_accessed=1756926778.926743, blob_last_modified=1756926781.8157268), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380974/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ea9eb19e91fedadd9e46d5d7dc9169995065af5b6b8e823f1667117be9d737d0'), size_on_disk=8889742, blob_last_accessed=1756926371.5481567, blob_last_modified=1756926372.2801535), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417934/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5816750a355a277e532d46ffa5159f8a2ec1ce66686fb12f07ccab4b4f7b3c98'), size_on_disk=7324082, blob_last_accessed=1756926361.995198, blob_last_modified=1756926363.89619), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380985/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2ec986412ef2a9e6e59f4ea3dc278b27ab3975b5025d00bd1cd38fe1867b0fea'), size_on_disk=7208882, blob_last_accessed=1756926373.2251494, blob_last_modified=1756926373.9591463), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417876/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0713329434bfbd55f1998400d9d44c1d8c7fec6cb1b646c0fdb926f1046f9b47'), size_on_disk=11246388, blob_last_accessed=1756926350.971246, blob_last_modified=1756926352.0472412), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380980/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e36f58da696f0827c72fed8647c2a45a0499fdc5d1859386dbf025388f07e16d'), size_on_disk=2288305, blob_last_accessed=1756926372.371153, blob_last_modified=1756926373.2051497), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417869/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8dd08ab28007f90a664a6fbde23298ae2bb0ded917d5d75b1275c41e3eb5f7dd'), size_on_disk=16604892, blob_last_accessed=1756926349.6872516, blob_last_modified=1756926352.7202382), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417649/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6baf4bcd82d9fe55b8dcdc7602c0d39d20954675ef4c5484331ff25dac8ad3f4'), size_on_disk=2672976, blob_last_accessed=1756926310.8234205, blob_last_modified=1756926311.4704177), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417764/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8a30e54d96d5b83981e37a0d8f6cae9e6584819a76a200f2730d9de32e282d02'), size_on_disk=2641764, blob_last_accessed=1756926331.099332, blob_last_modified=1756926332.088328), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417652/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d38c028233981eb275c44995eafa73172388a9e0e704fe278b0cedbab49d429f'), size_on_disk=12947754, blob_last_accessed=1756926311.0344195, blob_last_modified=1756926312.1414146), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417855/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ac2218ccb9f80664ce377351ea1dc84bf3f70b36006a86cffdb761a40ddab791'), size_on_disk=1981158, blob_last_accessed=1756926347.3992615, blob_last_modified=1756926348.3062575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417719/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cc0a365d1d79f0469571216394f0cb211b9096f2a567e0a347a18f0c496bac06'), size_on_disk=2076369, blob_last_accessed=1756926324.010363, blob_last_modified=1756926324.951359), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417903/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3ddfaef9dc57d0f63b8613408aba3af803dabd5ee87a1584edd9391b2b41bec3'), size_on_disk=11218813, blob_last_accessed=1756926355.9432244, blob_last_modified=1756926357.2232187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417709/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/395b1dd171ab9759e06a22580672bef65aac2acd7d548e38098d207c80071d89'), size_on_disk=717131, blob_last_accessed=1756926322.9983675, blob_last_modified=1756926323.6373646), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417870/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ca1bc9825b47ee9ac59e53bc6ada3b1a9e8aba1b57c57c57947afae7926054b'), size_on_disk=10710319, blob_last_accessed=1756926349.7052515, blob_last_modified=1756926350.7552469), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417742/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1a87c492aab479dd0c013fbe80e006f18ba80de751da600b95babcb3f6e88dad'), size_on_disk=2054520, blob_last_accessed=1756926327.3293486, blob_last_modified=1756926328.4213438), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380990/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4e04122d2830db35f2b82b29efaca846fe10d7638c88c3e4d3cf78bf10cf245a'), size_on_disk=2093813, blob_last_accessed=1756926373.9261465, blob_last_modified=1756926375.1611412), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f00bc431128183affc8964f9610f3e9486a043bcea5b31b3ebbcbd9b0070a448'), size_on_disk=6096918, blob_last_accessed=1756926377.3431315, blob_last_modified=1756926378.2571278), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381025/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aea8c4d9f3783191d3e89f4566811f01089f3b0b76b736c5167bda54693b0bc4'), size_on_disk=339954, blob_last_accessed=1756926378.7781255, blob_last_modified=1756926379.125124), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380958/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ef3849004b6c72205ea1b298cf6a586f1114061ca94925229eda191d4666885'), size_on_disk=3288900, blob_last_accessed=1756926369.617165, blob_last_modified=1756926370.303162), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417899/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b4349f94cb0679468e9c28a9439c1a71c88d7288adc3bdad0ca31a2590f1d4e0'), size_on_disk=7910137, blob_last_accessed=1756926355.3832269, blob_last_modified=1756926356.3022227), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380931/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/be287270314fed3dded81e0c24a66d8bd991b219fd6a157f1aeaa1690262e563'), size_on_disk=4324179, blob_last_accessed=1756926365.1611843, blob_last_modified=1756926366.1241803), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417776/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/01c4a97f92cd5e2919759d52d083eb824408a2fd7ade9413a060c6b20d233963'), size_on_disk=2759569, blob_last_accessed=1756926332.8593245, blob_last_modified=1756926333.629321), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417618/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d2ce91297551bc97b4fca46007f80c8846e5f9e9c78d9c1d7ded02fc31b3ef38'), size_on_disk=9239111, blob_last_accessed=1756926302.3654573, blob_last_modified=1756926304.3714485), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417862/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cea0cfbd611f8c6482ec33f8ea32f304a481d1c1225d3dacea432dd359fc976b'), size_on_disk=5244392, blob_last_accessed=1756926348.381257, blob_last_modified=1756926349.6162517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381026/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e41f2d7a1b084a404e7f5e9e003b27f88665a983ec73859f7eb42cd8bfa2bdd3'), size_on_disk=2857045, blob_last_accessed=1756926378.898125, blob_last_modified=1756926379.7281213), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417872/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7d71ca12594ab85797fca8c1b88947cb0f3ece93596f1a4c5adc04c11c0abf68'), size_on_disk=8522283, blob_last_accessed=1756926350.1742494, blob_last_modified=1756926351.180245), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417941/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/30188c510d2d7041e1525e304edc8f25b569a1771cf00844cb27300a052441e8'), size_on_disk=7032333, blob_last_accessed=1756926363.2551925, blob_last_modified=1756926364.2891881), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417680/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aa9e972e7427b1db5556008756651933c8792891339607dee2ee231733072d1c'), size_on_disk=18443288, blob_last_accessed=1756926316.5853953, blob_last_modified=1756926321.4263742), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417738/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c3d82a351044aa8cd5d43979369088ccf2f563022d4f7aa3683ef20923202770'), size_on_disk=19772535, blob_last_accessed=1756926326.3913527, blob_last_modified=1756926328.6913426), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417654/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/67d658902a2b91d0d36345be06dc00c85af1b88ba40c37deaa091705d97b3ffc'), size_on_disk=6323092, blob_last_accessed=1756926311.166419, blob_last_modified=1756926314.4344046), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417946/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef43c84affe53bbdefcd70cb97a4a4d1e483824b8b6f91e3360a11094609f815'), size_on_disk=4853893, blob_last_accessed=1756926364.0141892, blob_last_modified=1756926365.0591848), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417641/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/355d8669b1d4c5576d8aa900582800fdf1819360f0db91d5e0299342b49884fb'), size_on_disk=6322689, blob_last_accessed=1756926309.3414268, blob_last_modified=1756926310.7974205), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417796/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/52d8262071c9c9de8cd8fb52be9e67acf125b8ce67033749fb92989f0d1f9345'), size_on_disk=10184069, blob_last_accessed=1756926336.2803097, blob_last_modified=1756926337.4333048), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417627/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4027ab6ecd21364ca365bbe7192797ed924c817e6edc841d750111b2e3f6f45a'), size_on_disk=21089243, blob_last_accessed=1756926305.0144458, blob_last_modified=1756926309.6264257), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417651/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b95b6be3a7a6a5b4af3bbf15c5a4a6b3893f3030ef6d1dcfe79e6c6554e3d3cb'), size_on_disk=13551797, blob_last_accessed=1756926310.9544199, blob_last_modified=1756926314.6814036), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417630/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/26fdbadf7c1252a8fd73ac85ea7430d942d2d11ddc43ea1b2590aaa008e08337'), size_on_disk=3015575, blob_last_accessed=1756926305.7104428, blob_last_modified=1756926306.7214384), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417802/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ea68aebb727f77d846a145d43ec16fa2496e9c05d49a8791aa131b8e3e536452'), size_on_disk=4881005, blob_last_accessed=1756926337.2223055, blob_last_modified=1756926338.1273017), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381028/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8d40caa48e7b9d32d2a0548732ef91c01ede9d0f4f6d74f045817e818c52c808'), size_on_disk=18141950, blob_last_accessed=1756926378.9691246, blob_last_modified=1756926778.2687466), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417858/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2d7539fcb6297592fe3755ccc88e122441925fbaf9f5600d4daa88639630fd8b'), size_on_disk=1021831, blob_last_accessed=1756926347.8042595, blob_last_modified=1756926348.5072565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417815/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ee8a37cd03dfe154008718f2a0f7fd95b3fdf468f99b1fc73ca515433fa45930'), size_on_disk=6563669, blob_last_accessed=1756926339.6062953, blob_last_modified=1756926341.8062856), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381061/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d44d57e9c03bb6e6e750809eea88657c2960ed251b8acaa1352a07f4cb346a82'), size_on_disk=3789431, blob_last_accessed=1756926779.0527422, blob_last_modified=1756926779.8977375), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380997/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/add73ab9723ef88a47192fa169b89aaf17c16a0841fc9e53c1c5d73cfdc2ad50'), size_on_disk=15536494, blob_last_accessed=1756926375.1291413, blob_last_modified=1756926377.6141305), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380932/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f214422d02adb2b0a1037eb424d8574123cf27d8a5cd1418feb5733b0249f60c'), size_on_disk=6251591, blob_last_accessed=1756926365.6181824, blob_last_modified=1756926366.5551784), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380971/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0147090dd5a74f558b67151503ff635423a8af661b56d1faf06bb50a59adfb3a'), size_on_disk=1276600, blob_last_accessed=1756926370.9411592, blob_last_modified=1756926371.462157), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417836/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4fb964c54f4a37387c7844902d6171011a9a81efff244f365efcb156f630158f'), size_on_disk=3312893, blob_last_accessed=1756926344.2532752, blob_last_modified=1756926345.0702715), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417846/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a3dc440aac6a69d75ec73242a30e59d8183023a2677f1576b5e73d037d6e6654'), size_on_disk=2680468, blob_last_accessed=1756926346.0562673, blob_last_modified=1756926346.7392642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381044/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/89a79f2c5f6258380ff4776af49db6119ac0c04342686705cfde94feebd7c9ca'), size_on_disk=3576881, blob_last_accessed=1756926776.065759, blob_last_modified=1756926778.8987432), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381024/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5182750a0c8af8253cc98ecad8715519463d5c18adbe067488dfef91198c0d80'), size_on_disk=603678, blob_last_accessed=1756926378.7151258, blob_last_modified=1756926379.128124), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417638/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c8cface311cd55cd1542ed94d609da72cd2b3087422c28ad26c60e1d81a3e6bd'), size_on_disk=993699, blob_last_accessed=1756926308.4724307, blob_last_modified=1756926309.2634273), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417663/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cc7010c3ed70c3083d49d88c0f95dbe8f6eff873d4b5848eeae06a22df71a479'), size_on_disk=8938116, blob_last_accessed=1756926313.18441, blob_last_modified=1756926314.5674043), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380962/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/72be01aa42db736ffac8236c23c16edea00bdf5da88c3114fc94d9eb3daa4ee9'), size_on_disk=1460258, blob_last_accessed=1756926370.2361624, blob_last_modified=1756926370.6811604), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417814/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9f70b1469a6d0ad8758aa7a44e754e07ccc401906f8af403a72075d763919535'), size_on_disk=7618977, blob_last_accessed=1756926339.5142956, blob_last_modified=1756926341.0872889), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417676/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/53c35af59283469a22148b2841cb8574faf80d59162a33d269560283c9228af2'), size_on_disk=12006227, blob_last_accessed=1756926315.8083987, blob_last_modified=1756926316.8563943), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417666/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/38008595447ab722c1889d0e19babf74299822ac7733504a4f09e1af1e4ad1ac'), size_on_disk=7712422, blob_last_accessed=1756926313.945407, blob_last_modified=1756926315.073402), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417856/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3dc4e868f31759c9ac3d43f40097f5ed2f0efb6307e3bc1ccbed70c50a16ee4e'), size_on_disk=6126482, blob_last_accessed=1756926347.3832614, blob_last_modified=1756926348.3162575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417718/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3ba23a0e7c5b3a455e95fbf6a22b51687d5330c19207241bdfd189e3776f5ba3'), size_on_disk=3304954, blob_last_accessed=1756926324.0233629, blob_last_modified=1756926324.986359), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417942/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f3a5b1eb0f954ddf66a8dfa1a38465726d2251730d3898865535269a43c7fa4a'), size_on_disk=6911573, blob_last_accessed=1756926363.415192, blob_last_modified=1756926364.3521879), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417695/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d6ae8d39c2949235dd4d82e7a0e56d9f20c20ba5f1b7a445fbba1f0df29c88b7'), size_on_disk=7710772, blob_last_accessed=1756926320.4913783, blob_last_modified=1756926321.959372), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417678/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/825d9bcd10c008b68cc84e92f20608c0d52770ac028bbc4a9219f1cd210ab8c4'), size_on_disk=29320273, blob_last_accessed=1756926316.0503976, blob_last_modified=1756926318.3993874), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380972/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/91a8106ca7ac2babcabd1549072b2b730909e1386f827e732428bff2bb5251cb'), size_on_disk=6712599, blob_last_accessed=1756926371.1721582, blob_last_modified=1756926372.0711544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417888/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/961a35161b04a0e892f46a0a90b6d4776d7fe07ef82fc7016c95b1a885c4274f'), size_on_disk=9651505, blob_last_accessed=1756926353.298236, blob_last_modified=1756926354.65023), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417921/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/05fb4ff0001b88b711eee9f29ae30642d940667f7166121c9782f464057880f5'), size_on_disk=7891276, blob_last_accessed=1756926359.3782094, blob_last_modified=1756926361.2442014), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417799/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cba2fb579c7706a7ff38fad3c7c649000560c1052e681adea986c5cc02c02cfb'), size_on_disk=5688906, blob_last_accessed=1756926336.7133079, blob_last_modified=1756926337.7073035), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417867/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f4783b00d4674df92d84f9a304fb9d143c7b9e4754e3efcb9c072a49a9b9298b'), size_on_disk=8536709, blob_last_accessed=1756926349.3882527, blob_last_modified=1756926350.2882488), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417887/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/58707813ec63af2eb1a72b0c8317b4701b4777df8f828f2e08a0cbb1ccfa534f'), size_on_disk=9080847, blob_last_accessed=1756926353.0712368, blob_last_modified=1756926354.412231), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bd149efcdaa899981b965dc424d16fb66668a7666a059c6bc5043572331394c2'), size_on_disk=4513644, blob_last_accessed=1756926377.1751323, blob_last_modified=1756926378.1161282), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417820/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9d36f1c17151e3203186142abff734c0182377d5287b3f27d00917608501abb3'), size_on_disk=8116588, blob_last_accessed=1756926340.9562893, blob_last_modified=1756926342.0902843), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381033/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5fff567b96a4649d787e93ef8cdca2d8ce977b7f2eb25d6f78e50bc30c798bce'), size_on_disk=1712156, blob_last_accessed=1756926379.7391212, blob_last_modified=1756926380.3661187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417710/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9837ecd3769eaaf407d939ff09d6ae74d832630b27353251942bd262cd70fc35'), size_on_disk=330457, blob_last_accessed=1756926323.098367, blob_last_modified=1756926323.797364), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417767/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8d0afed33cd85298c709656da4b210e3fe87aa4d6822208d744200816d59b95b'), size_on_disk=3739401, blob_last_accessed=1756926331.2333317, blob_last_modified=1756926331.8623288), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380964/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3a63df07de544662fb04d5388f34b0c8068efbc19f464b673d8a860a57b5b422'), size_on_disk=956844, blob_last_accessed=1756926370.333162, blob_last_modified=1756926370.8741596), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417801/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4bbb1b3faac1a4c15eaac8c81260f4f18493d2a082915cbe4c480ce76a94140e'), size_on_disk=4600965, blob_last_accessed=1756926337.090306, blob_last_modified=1756926337.9153025), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417711/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e30dbc09cd2dfb4722d60d0833123542e9c5096f70b97867cd15d466aa7b927a'), size_on_disk=96859, blob_last_accessed=1756926323.1863666, blob_last_modified=1756926323.7823641), CachedFileInfo(file_name='GSE222268_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE222268_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e0f1d5b82fd97798d0c02d992c49c0e83d8dcf82e07e001284e78d9656355481'), size_on_disk=4411, blob_last_accessed=1756926300.1784668, blob_last_modified=1756926300.5344653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417737/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2e1d5b2306dd24257d4ba261baa42b324c9f4f8ca3a1d30e0f920c4f4a5f99ba'), size_on_disk=1342511, blob_last_accessed=1756926326.2713532, blob_last_modified=1756926326.9043505), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380927/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/38e67ea2fef57610ff63c0c298a6db65ae1c0a159f2b1e61940f2e65306b8fa9'), size_on_disk=7297320, blob_last_accessed=1756926364.969185, blob_last_modified=1756926365.7781818), CachedFileInfo(file_name='GSE178430_metadata.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE178430_metadata.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9673758f13ec096ec4a89642bbe34d60975f5f05'), size_on_disk=61, blob_last_accessed=1756926300.2374666, blob_last_modified=1756926300.2994664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417702/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/41123195fa339a4ffabf6d686a284c343d0519089a18b828818c92ec43a26eae'), size_on_disk=6213776, blob_last_accessed=1756926322.0263717, blob_last_modified=1756926323.0323672), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381032/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/335a75b123ae38273f1b1a10f16e9547eec356f68a1d3078926f011d3332e2b5'), size_on_disk=656385, blob_last_accessed=1756926379.6781216, blob_last_modified=1756926775.4077625), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417771/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8fe5513ae86b28ecca3d4c70bdb73097f578dd31b4d08a072960b61550f582fa'), size_on_disk=3970195, blob_last_accessed=1756926331.9313285, blob_last_modified=1756926332.8643246), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417716/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cd98cf97346b62e4f1efcace598c9c273d9ad9b8d8692e35f50b11d46f7ed8a6'), size_on_disk=5100666, blob_last_accessed=1756926323.8513637, blob_last_modified=1756926324.8793592), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417833/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/48792db399d7f0aab6765109e8c01abd61a2da93e5d0bdf7c325bcffe01fe113'), size_on_disk=8139822, blob_last_accessed=1756926343.3262792, blob_last_modified=1756926344.480274), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417786/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdd4770c9b3e18e86f9a46ff924e3a562e37b632d6f0e8ad11627956a3416e09'), size_on_disk=11354740, blob_last_accessed=1756926334.3243182, blob_last_modified=1756926335.4083135), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417900/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/45d5a0bbf39e542f22aa4e36186673d1246533dc513f006312a26286496f6ead'), size_on_disk=11235417, blob_last_accessed=1756926355.535226, blob_last_modified=1756926356.6632211), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4331f53e52f628f99a9695fa70339b08eb69a011cc8a3cbdb08dfa8e6c5d3ddd'), size_on_disk=10779086, blob_last_accessed=1756926376.8381338, blob_last_modified=1756926377.9141293), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417686/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6a42f24b250baf0d88696bc123f01494a400b3f03f2ff2a1fc828f9295721a16'), size_on_disk=12070748, blob_last_accessed=1756926318.2063882, blob_last_modified=1756926319.627382), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380926/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fd708dfb8084d767fde32c52acb58e785083ce7becbd2e160277f31460719346'), size_on_disk=6579161, blob_last_accessed=1756926364.8451858, blob_last_modified=1756926365.5441828), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380981/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/198a0b08846a11e72de25e0e9c44f495ef88c9260de2e036d240715004c04953'), size_on_disk=8535317, blob_last_accessed=1756926372.5771523, blob_last_modified=1756926373.8291469), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417602/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/26ae91a914e9ad095615c88ac3c62c99e28ae2687ccd66007f13ac8fbbffa3fe'), size_on_disk=3717181, blob_last_accessed=1756926448.0367467, blob_last_modified=1756926301.5714607), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417889/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/295f9efb0d78e3a116ce4022a7830297570446e2017a3fcd3b3d423c0c20664b'), size_on_disk=9617979, blob_last_accessed=1756926353.3102357, blob_last_modified=1756926354.7322295), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ad0e80a28e4cd2027893adee5afceab851576ed2637c1a8e94d0af2adae6ace1'), size_on_disk=5088900, blob_last_accessed=1756926375.41114, blob_last_modified=1756926376.3821359), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417804/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/16ee23ae4dbe8a4d968a614f5e6de65028b6edce012231a50eb776d65f949d34'), size_on_disk=2107502, blob_last_accessed=1756926337.7883031, blob_last_modified=1756926338.5083), CachedFileInfo(file_name='GSE209631_metadata.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE209631_metadata.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fe31b19357a717d3d51d1d201620d37fca79b1c8'), size_on_disk=61, blob_last_accessed=1756926300.2214668, blob_last_modified=1756926300.2814665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417735/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/634551506800bc76183ce179c8c8f324cb6f0f577f6cf0b629b74ce4f1e995d5'), size_on_disk=19171195, blob_last_accessed=1756926326.0393543, blob_last_modified=1756926329.4873393), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417793/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8a51bd4d953e697022a85f9b527afd45126988ba4bbb796d71c9e5f6c8fffc44'), size_on_disk=10797388, blob_last_accessed=1756926335.4883132, blob_last_modified=1756926336.7013078), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417650/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c4d103888f768ff3cb0c68f4b747cb8d8aad8058b75174160cfc42b2504407f1'), size_on_disk=1337049, blob_last_accessed=1756926310.89842, blob_last_modified=1756926311.4924176), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417865/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/348bfc1685f79e9ec7f559e96b61b8c43628d611378b6c4ead8bffea44415f1d'), size_on_disk=9233047, blob_last_accessed=1756926348.8982549, blob_last_modified=1756926349.9182506), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417868/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/52e3f224a0147264d4ce477d26f4e11ff0745c328eb3e2ac44209dba1c8c55eb'), size_on_disk=5039768, blob_last_accessed=1756926349.6852515, blob_last_modified=1756926350.6082475), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381046/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a96c1819b1010c40fc4d92418e407cc5b5f48f9e24883d4b4cdf3e02d23b688a'), size_on_disk=3071378, blob_last_accessed=1756926776.6277559, blob_last_modified=1756926777.855749), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381037/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5cef560daffa3a5b1e841a312937eca595ced0bbdb3e6c641b13359a0340fea1'), size_on_disk=2090047, blob_last_accessed=1756926379.8821206, blob_last_modified=1756926380.4171183), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417732/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f99cdb10c12a75e1d020125d68b5c5302263eaef050b5c6b0032e27a0ac9a23c'), size_on_disk=685742, blob_last_accessed=1756926325.7463555, blob_last_modified=1756926326.326353), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380987/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2eb045d9f88b952eed8f8354e77c682bc3003bd2e9eacb0eb0c2df5ca44a34cc'), size_on_disk=4463796, blob_last_accessed=1756926373.559148, blob_last_modified=1756926374.4241443), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381065/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/99cc73bc4c0908da04c01c7c59eff995a478ffc023c31fb970c7bb9789b2d70b'), size_on_disk=14673323, blob_last_accessed=1756926780.0087368, blob_last_modified=1756926781.5847282), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417640/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/411d9146359847b5805450f67cbde4f1cbd51e3c2fae4bbcd6295db7426cb2ca'), size_on_disk=7000528, blob_last_accessed=1756926308.9124289, blob_last_modified=1756926310.0344238), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/64a119c8028083521d3872e2daca1545f2f8753eb8acfc751bf0c6d25803a3f8'), size_on_disk=2003211, blob_last_accessed=1756926376.794134, blob_last_modified=1756926377.2761319), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417696/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2e1bce702520ca07d9037bd4de4d21bb30ef6b7fdf7de9d75ea232acc036a72e'), size_on_disk=31303382, blob_last_accessed=1756926320.7203774, blob_last_modified=1756926777.0657532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417810/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b807197acb33acdd1b70eddedd59dc4d831e271d231d6c9bb6f1281fb4d5813f'), size_on_disk=9808564, blob_last_accessed=1756926338.8082986, blob_last_modified=1756926339.7202947), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417853/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5995033a04f017892b30c025a1527f9b6420bf1e7636c466cdd8d1462cf228c8'), size_on_disk=2186899, blob_last_accessed=1756926346.9012635, blob_last_modified=1756926347.8172596), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417893/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f3b9f2c711cb806350320b93dba528efd6433d0ca10c39e5e2dd6aa89a408bd'), size_on_disk=9896637, blob_last_accessed=1756926354.2372317, blob_last_modified=1756926355.535226), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417800/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8e563a7d64bf06e7fe26c2817271f626273eed4c7dd19db0b9fd8e14c25f73b3'), size_on_disk=9904697, blob_last_accessed=1756926336.7953074, blob_last_modified=1756926338.958298), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417713/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c5d75f4a32cadceee844da0c5ce703483f575fb927990a1615ee37205137d986'), size_on_disk=524444, blob_last_accessed=1756926323.533365, blob_last_modified=1756926324.045363), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381048/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/53cd017f084781da16e69ae01b406ac0d8824a4d991e39b22c428e664a3808f2'), size_on_disk=2181901, blob_last_accessed=1756926776.9907537, blob_last_modified=1756926777.7067497), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380976/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d27d3efdccd104ca208d69fe9d94fe9011cb6212b34739679b1c8f550c5e9df2'), size_on_disk=12889551, blob_last_accessed=1756926371.6141565, blob_last_modified=1756926372.8021512), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417774/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ad00f794fb29d2a609375d63b09170f396f055ae392cd05a31d5f8fb5ede7a33'), size_on_disk=2914273, blob_last_accessed=1756926332.1763275, blob_last_modified=1756926333.217323), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380983/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eec33f1158aa017b073fcb16d6400cd800eb79e8c0b78b5e2f831d7413e4a736'), size_on_disk=4478939, blob_last_accessed=1756926372.869151, blob_last_modified=1756926373.6721475), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417834/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a69c65a4d32399355863fa14d4a1e92ecff2c92498e122e8e77796de16d42948'), size_on_disk=8537233, blob_last_accessed=1756926343.3672788, blob_last_modified=1756926345.2462707), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417926/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/303d44a38fbb646cf211ce1bd03fe2d24bb004f385c63d05ab8e707c5b283f0c'), size_on_disk=10766909, blob_last_accessed=1756926360.6072042, blob_last_modified=1756926361.6751995), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417734/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/10d4c6d9acc5b56370fc32062262ae3871008e20e960c6b352ac27a4e229c905'), size_on_disk=1495194, blob_last_accessed=1756926325.9453547, blob_last_modified=1756926326.745351), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417910/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/694d86a1e469836a53e1ce5dba0d64b77b8f68590a2034e2a1ef5310457dc7fb'), size_on_disk=4494010, blob_last_accessed=1756926357.3112185, blob_last_modified=1756926358.3942137), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417722/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f29055ed874a3a40735526808a6739884efecd8d7c5e7e9eb1fcaa8e495ade3'), size_on_disk=345818, blob_last_accessed=1756926324.5753605, blob_last_modified=1756926325.2483575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417945/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6689ba375c993d00d1f0e716d3bc39604d68df654a67b2518c56f4d54c7f4ca6'), size_on_disk=11289854, blob_last_accessed=1756926364.0191894, blob_last_modified=1756926364.9011855), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417839/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a04fc093d2a4fe873de135165bd62a38a1cb9b29860881ed973022ff128dc6d8'), size_on_disk=6789775, blob_last_accessed=1756926344.5502737, blob_last_modified=1756926346.4242656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417944/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/01e0e606e4e8b2203e6497ffc8c231bce8672a43d83098ba838c8edfdb052c27'), size_on_disk=4563261, blob_last_accessed=1756926363.89319, blob_last_modified=1756926364.6951864), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417681/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6e4ea424e9eab4dc3ea304621992e1db158565a8ce30374980b1084a653a0132'), size_on_disk=7689435, blob_last_accessed=1756926316.934394, blob_last_modified=1756926318.231388), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417779/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/918376b5cb0b63e3e531ac990854e5a7b461e7251f5ed562c5a92cbf6a5a0141'), size_on_disk=9459105, blob_last_accessed=1756926333.1593232, blob_last_modified=1756926334.2033186), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417917/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2118491b5ba6b4af1c0d394eec3139b0258cf3a4e42b4d1a02ab177247852081'), size_on_disk=14019020, blob_last_accessed=1756926358.4622135, blob_last_modified=1756926359.8632073), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381064/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8569620c4d2efb8dd163aa69d8edf8f855b96eba637e754a126a88e6f0c73192'), size_on_disk=20598404, blob_last_accessed=1756926779.800738, blob_last_modified=1756926781.1587305), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417871/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e44cb883893b5e30baa752dfbd773b31795f2072a7ec4f473371f435989cad42'), size_on_disk=6264896, blob_last_accessed=1756926349.9852502, blob_last_modified=1756926350.937246), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/059cc8a8b1f7918ab1b6499d3d9843100b3b37fe6dcd57dfe9ae512c6030cad6'), size_on_disk=2109635, blob_last_accessed=1756926376.7021344, blob_last_modified=1756926377.469131), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417930/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62f17648a07c7675d6a101e3d12efeae247c742e37eaacf96f95e03dceedefde'), size_on_disk=20574235, blob_last_accessed=1756926361.2242014, blob_last_modified=1756926362.5701957), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417731/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/39ec5969bc8f3acceb3aa30b72f0c0101785a36da340f3983ad00d8cc80160a4'), size_on_disk=1557745, blob_last_accessed=1756926325.658356, blob_last_modified=1756926326.2043536), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381055/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0aad0d3724742845da7f0afae0c18ab5c992bec76c02e0ffda057f00d0894d35'), size_on_disk=3355008, blob_last_accessed=1756926778.3377461, blob_last_modified=1756926779.263741), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381058/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9dc97b5892fcc8af593c03fad2040396b9b10626a655319ad40b38acd75e1d7b'), size_on_disk=11534270, blob_last_accessed=1756926778.543745, blob_last_modified=1756926779.7317386), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417880/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b08db0a2399fc4e5b6f59c19b7af05b1294d0fac10b2c336b627234fa828f1be'), size_on_disk=9818886, blob_last_accessed=1756926351.5782433, blob_last_modified=1756926353.246236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380982/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d1e44b0b82ea22fb6c513ce02b49facf5f38b64b2baa91b24942fc6858125e37'), size_on_disk=6977161, blob_last_accessed=1756926372.8241513, blob_last_modified=1756926373.4951482), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417704/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/477a83e4a5eace957a036c5ec65ef4539442c078058c1a44a877763cb850c86d'), size_on_disk=459034, blob_last_accessed=1756926322.1393712, blob_last_modified=1756926322.8993678), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417724/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e5beee12039dfe6b4dae180a31ac0897dea725754554db43e2df9f99da94db58'), size_on_disk=4535402, blob_last_accessed=1756926324.69836, blob_last_modified=1756926325.9703546), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381027/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ba9aba2cc5b9bd9e83599ccba5920f028ac9c6936436dac50f45c606f3abd841'), size_on_disk=6537548, blob_last_accessed=1756926379.0151243, blob_last_modified=1756926775.5467618), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417688/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f21ccaca0f6782b6ce2b70b50c9dc93487b6aaec03db6c30d9cb0c9fc01dda42'), size_on_disk=16023102, blob_last_accessed=1756926318.5463867, blob_last_modified=1756926320.0283804), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417608/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d225f561536fca4d073d2418a32efc3df68e9666a3403c7d8b1ee0a520861322'), size_on_disk=873305, blob_last_accessed=1756926300.603465, blob_last_modified=1756926301.521461), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417897/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/309aab637d037638082524649ef7b0828885a98e786c64af01072d68968b7d66'), size_on_disk=7857749, blob_last_accessed=1756926354.8022292, blob_last_modified=1756926355.9902241), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381051/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ba017d941936d755013757711f5de4661acd187ef79524662d85485ba6588f34'), size_on_disk=8199472, blob_last_accessed=1756926777.322752, blob_last_modified=1756926778.4757454), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417824/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/65d997db1339fc5a8380a61fcf1ab1a6d28fb9142fc7d78c89236dc218b91ea3'), size_on_disk=8218856, blob_last_accessed=1756926341.7612858, blob_last_modified=1756926343.0512803), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417659/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/20f21bb1ba86fdbee1918776793b1ff2275be49933c72eaba90b8be8582f7692'), size_on_disk=15311232, blob_last_accessed=1756926312.2364142, blob_last_modified=1756926313.8474073), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380937/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e31f54581c4a4a47033065033f3f9266913edad654543dd07b20d0ffe54847dd'), size_on_disk=2691574, blob_last_accessed=1756926366.2071798, blob_last_modified=1756926367.1951756), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417644/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0c788a7ba70689a796af823df5cc14504dfdc854617952459367364c4498cdeb'), size_on_disk=949200, blob_last_accessed=1756926309.758425, blob_last_modified=1756926310.6444213), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417745/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ab84f2e8e76311cfe45a4b525b239186e835cab37f1e5222fdc35c286064e5e'), size_on_disk=1270105, blob_last_accessed=1756926328.596343, blob_last_modified=1756926329.1233408), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380989/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3c8b646955209386a1d97882163b7c9816463d2dea68fbed2cc960817ffeb332'), size_on_disk=12022144, blob_last_accessed=1756926373.7761471, blob_last_modified=1756926375.1511412), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/43f12fb4826be4c7a3dd64800fc72cbcab2a9ce5bed7f7b5b9aee974deb7c06d'), size_on_disk=1116877, blob_last_accessed=1756926376.094137, blob_last_modified=1756926376.6491346), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417913/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/35c1a160b767f9f5870216ed33a1dc24804690772a5baee58f623c6dc0632127'), size_on_disk=9135350, blob_last_accessed=1756926357.9462156, blob_last_modified=1756926359.3142097), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380944/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7331bfc92dc902a721083ac4ea027481c90ce204d7dca7c493fec8f284db0155'), size_on_disk=8432479, blob_last_accessed=1756926367.299175, blob_last_modified=1756926368.6141694), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417690/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/283af9b17d776b75f6ff6ad1f426d6d0e0e5aa1f2089a20910edcb6763191b69'), size_on_disk=12265448, blob_last_accessed=1756926319.171384, blob_last_modified=1756926321.4153743), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417739/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1c9e3fcff1dc467673efe70177cb4d678d7eff7b5b2bb3ded247d7a458b466dc'), size_on_disk=21974051, blob_last_accessed=1756926326.8623507, blob_last_modified=1756926328.7393425), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417727/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/414c702fdf3dd97c536f3237d2c38e0ef5ad8d08bb7c991ee252e16d57b201b6'), size_on_disk=2636905, blob_last_accessed=1756926325.0683584, blob_last_modified=1756926325.8093553), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380960/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/60771297311bf96c3213c2a96321062a06067c91d2bba2fce69a7a6d7a0c980e'), size_on_disk=560607, blob_last_accessed=1756926369.7811644, blob_last_modified=1756926370.3121622), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417612/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ee40f7af39e729ee79ef18d6777bbd632b7cc7811041a32d54f4a02b42638464'), size_on_disk=3509045, blob_last_accessed=1756926301.5874608, blob_last_modified=1756926302.6274562), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417881/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bf380975f31e362e8964000688bff364809bbe2e8e3efa9ceaa71253ca6beefb'), size_on_disk=8717095, blob_last_accessed=1756926351.848242, blob_last_modified=1756926352.9732373), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380988/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/27b6e71986cf2b7da20b64dbda0ba47e08753f7184ddd57ed288f11df88d21e1'), size_on_disk=16063760, blob_last_accessed=1756926373.7421472, blob_last_modified=1756926374.932142), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381030/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e5baa8386faa327b26d27d6c8e6b265dddac1d55565e81d724a1980b93d871a3'), size_on_disk=1867415, blob_last_accessed=1756926379.2021236, blob_last_modified=1756926379.6681216), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417714/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d6abeb724a8c8b23217f3465284564da5baffee59ee4e3af582e0a3705ae32c6'), size_on_disk=262443, blob_last_accessed=1756926323.756364, blob_last_modified=1756926324.256362), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417744/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/faf407f0b251cf5f58c8ac7becee4b995327fd9a7d6599c3045fde8a78e1e811'), size_on_disk=2548979, blob_last_accessed=1756926327.7843466, blob_last_modified=1756926328.7443423), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380968/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/549ef4e39a437adb99efecdc9b05fee3ca4b31c878b99f7a4cf1398f405ca049'), size_on_disk=3575926, blob_last_accessed=1756926370.7441602, blob_last_modified=1756926371.5941565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417832/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/268a3b7a91ccc3f3909042a711454c336b1887705de99442bda022bcbc26a01e'), size_on_disk=7655304, blob_last_accessed=1756926343.2332795, blob_last_modified=1756926344.3042748), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380948/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6ac219aa7f5be77c21bde60c80ccad59edf825e2a009d6a4b7fc8bd4d8be83d4'), size_on_disk=6046323, blob_last_accessed=1756926368.6691692, blob_last_modified=1756926369.5471654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380941/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9a23f03fee8d60d752bd0bc1e59e9205abea4f3a78e3272b9a03447f076383d5'), size_on_disk=3514837, blob_last_accessed=1756926366.9851766, blob_last_modified=1756926368.7591689), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417851/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2d8a571fae50598143744a1f13d40cec2ea8c2198e87aae17f96d7c8a00460d0'), size_on_disk=11051527, blob_last_accessed=1756926346.6472647, blob_last_modified=1756926347.6582603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417783/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9603c95097c451e616c54d52d3474b866453409905ea16365d019e77c6ca31b3'), size_on_disk=4176778, blob_last_accessed=1756926333.6983209, blob_last_modified=1756926334.4773176), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417835/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6245fc8d220aad556115d4b831f72ae6a9beac48ed6e2aba811284aa0ccab4f7'), size_on_disk=7888215, blob_last_accessed=1756926343.795277, blob_last_modified=1756926344.942272), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380970/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bdec4a51127eaf09abb5d25d7f3a732afc3b561105fd18ba1df44d04e6dd90a2'), size_on_disk=3545790, blob_last_accessed=1756926370.9301593, blob_last_modified=1756926371.5451567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417811/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/da92b1c1579a2295da085050305367bd02a47fd52a74d1ec2a9f227b5e808b11'), size_on_disk=6127159, blob_last_accessed=1756926338.8782983, blob_last_modified=1756926340.1172931), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380992/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dbc6b1de202e7fb25701c02607facc3e8c332550907ca0b70ba177533a311d6d'), size_on_disk=2551903, blob_last_accessed=1756926374.2281451, blob_last_modified=1756926375.0341415), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417773/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d7980069a740f581dbcfd9416372452203d636d8ad4c9352c3b2d6483cd27379'), size_on_disk=5178664, blob_last_accessed=1756926332.1683276, blob_last_modified=1756926333.411322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380969/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/10410488c74f35a493cef2ffc4a64b28b336be1fb32a2b6dac692e0d7fbd4271'), size_on_disk=1973405, blob_last_accessed=1756926371.041159, blob_last_modified=1756926372.1191542), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381053/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6ca1dc55841bbb6d8d8a83d1a87d00570c1eea7cf1963db8cb04ddd427711fb6'), size_on_disk=10277654, blob_last_accessed=1756926777.9197485, blob_last_modified=1756926779.9187374), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380965/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6b384b53be751cec9056e74c2c8ce9fe2e532cfbf6a38e43bb09ce74ec817f3a'), size_on_disk=12974487, blob_last_accessed=1756926370.3761618, blob_last_modified=1756926371.4361572), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417838/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0dcd4dc268902c8e0845a1ee81938a81a00412014d5b198a41a523cc3450dfc4'), size_on_disk=10178872, blob_last_accessed=1756926344.5452738, blob_last_modified=1756926345.3902702), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381042/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/80a0c18e8d146043e9c95c6a86552d28e28267a489b3e5c6f2a11319cf47b877'), size_on_disk=4477936, blob_last_accessed=1756926775.660761, blob_last_modified=1756926776.9177542), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417765/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bd2b5a1b1593db002ea9c159d4c40d1d8addddbf124207a7b32b0bb43e2e4d72'), size_on_disk=2696437, blob_last_accessed=1756926331.2103317, blob_last_modified=1756926332.046328), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417647/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8c01c928abe00b5afa6a2292820695797391b3671cc110b9026f5fc5cb8564ae'), size_on_disk=2891716, blob_last_accessed=1756926310.1614234, blob_last_modified=1756926310.93942), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417747/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7011e7ff4b64ca9c57bdab93cfcfc52938e7b0574b48417feb4f6185f6c118b9'), size_on_disk=4791771, blob_last_accessed=1756926328.7593424, blob_last_modified=1756926329.9543371), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381069/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aa8f6186814bf973f8b4448a0c4b692c31594e03905c75d5691b982d6ccde97d'), size_on_disk=13705106, blob_last_accessed=1756926780.5807338, blob_last_modified=1756926782.319724), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380940/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4f59a7f9287c427bed27910f08a041e460eff2490c21e2fbdaa5cc9348f5921e'), size_on_disk=6062382, blob_last_accessed=1756926366.8141773, blob_last_modified=1756926368.3051708), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417809/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/486100d34df42ce97476d9753f8d57edb5f53596eaeca2d0cf81f5f95a4f4e12'), size_on_disk=7376702, blob_last_accessed=1756926338.5872996, blob_last_modified=1756926339.9172938), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417891/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cabef83ee373e37d528b127d813f1535faede4b98c5524d83e9906df83558b07'), size_on_disk=12596609, blob_last_accessed=1756926353.9052331, blob_last_modified=1756926355.2992272), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417904/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cf9194917da2f4c6c3f0411256b2604e8c14959373d7431047de2e23cffbb9bf'), size_on_disk=7269877, blob_last_accessed=1756926356.062224, blob_last_modified=1756926358.1762147), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417908/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/128e54326b088977000f8caaba7d09e324281ed7fe0e3e5f2779783f0a82f293'), size_on_disk=3614805, blob_last_accessed=1756926357.1272192, blob_last_modified=1756926358.2542143), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381034/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/112a8e705f7536fa06c630165d3c409f87a5784ad626b891cc2b3919e24bdd14'), size_on_disk=250813, blob_last_accessed=1756926379.795121, blob_last_modified=1756926380.1791193), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417866/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b0424cd7567723443472cb266e090a30e2b64ee6b09bb1b94d8dcfdb9f0b0c43'), size_on_disk=9685580, blob_last_accessed=1756926349.109254, blob_last_modified=1756926350.493248), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417860/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/72e751d246e9b938ce9ab4380e2a1114ecd3c36d5829e3298f391c1edfefae24'), size_on_disk=3996315, blob_last_accessed=1756926347.9312592, blob_last_modified=1756926348.819255), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417729/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8383a0e86c2bfe15c46045980d6768eb4e7e3c4c157ff1ae7ce20144e245ca80'), size_on_disk=3058876, blob_last_accessed=1756926325.3113573, blob_last_modified=1756926326.103354), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417805/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/661f37ed294c22f6c3b02d5c1ea25794f5e33090c246185e891ee82153e2e5f2'), size_on_disk=3071166, blob_last_accessed=1756926337.8643029, blob_last_modified=1756926339.3292964), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417877/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b170370a220db5677b9c6193707d32d1d14b9082ca574b9562c4ac8ea4c72c26'), size_on_disk=10449263, blob_last_accessed=1756926351.0392456, blob_last_modified=1756926352.32624), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417924/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f2053fcaa7f21b1703e3491e167b457c29759b2fe9535f0cb2bfa257a1b86cea'), size_on_disk=6988370, blob_last_accessed=1756926359.940207, blob_last_modified=1756926360.8152032), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417905/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b9d291469b7a414bab157f45bfd3a1f7ffaaf931de25fec9eb18012a18b70939'), size_on_disk=4418829, blob_last_accessed=1756926356.1612234, blob_last_modified=1756926357.2652185), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380998/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6808b8e1fa74b807321826b2c54182a3b6e257f0a223be0a6a94caf9463959e9'), size_on_disk=16847111, blob_last_accessed=1756926375.2231407, blob_last_modified=1756926376.572135), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417932/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f040d4f594fe69143a7c3f04f0498be2a30fb9b5e78d42ec464fee65efcc42dd'), size_on_disk=9231914, blob_last_accessed=1756926361.4262006, blob_last_modified=1756926362.5771956), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417636/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4a3b2c7076107dcc47a87179cecab9e2051350c94e951a7aadc2ff0cf6fdbaca'), size_on_disk=43479381, blob_last_accessed=1756926307.6494343, blob_last_modified=1756926311.4484177), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381067/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/da9c5ab3c0f970f9c2af4c0f5f0e9a95d47769e8096ff6b9d76092becbc8517e'), size_on_disk=9837433, blob_last_accessed=1756926780.2977352, blob_last_modified=1756926781.5777283), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417754/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0d840353f1148a08928dfb3177a0abfe635c406fc77d6d9958743585ade6b872'), size_on_disk=1101891, blob_last_accessed=1756926329.758338, blob_last_modified=1756926330.215336), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381039/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5f95b6de8d0d73589c612313eaed6ce1e6adf734e795719f23df6f54cbbedc69'), size_on_disk=1656495, blob_last_accessed=1756926379.8751206, blob_last_modified=1756926380.283119), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380984/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a15ab5e5a0ead07dc2a530a1e4abb714027a95ce090c7a0eda5ec2bb714d903f'), size_on_disk=8070704, blob_last_accessed=1756926373.2251494, blob_last_modified=1756926374.1601455), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417624/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/28c1de2a4bd6b421cd05c3bb211ffde83e4b830127f106ec7be42ad288e26ef6'), size_on_disk=16758887, blob_last_accessed=1756926303.7884512, blob_last_modified=1756926305.6184433), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417703/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e82b21d919b84770e2d141eaea09aff82ef57c3ff3dbd15c588c1b8cff212c9e'), size_on_disk=1462841, blob_last_accessed=1756926322.0533717, blob_last_modified=1756926322.898368), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380995/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e0cae334f1cb71be0be1e8ceb5fc6ad23d22e4db20cb64b1314c3d080f2bb5f7'), size_on_disk=6315860, blob_last_accessed=1756926374.931142, blob_last_modified=1756926375.7271385), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417787/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/17f33e06b6bd77c0bbd3141b53fc2d9550b0c54e99b18fe79ddad931e3a283a9'), size_on_disk=12355842, blob_last_accessed=1756926334.5003173, blob_last_modified=1756926336.2953095), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417672/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f0741c46f1460c30d4d9b88ca1f7c0ca77a040cf25a34d82fab4c777a921ac26'), size_on_disk=18268758, blob_last_accessed=1756926314.9764023, blob_last_modified=1756926316.5143957), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417936/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8ba89583b8c822b64968df9eca8273d12270429f0f820d6027f13c66504c383d'), size_on_disk=4552381, blob_last_accessed=1756926362.205197, blob_last_modified=1756926363.171193), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380956/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/15047f69259f2cbc2cb08949d07ac6fde505bade6a0e3bb3912b183a48a4b288'), size_on_disk=2282680, blob_last_accessed=1756926369.4771657, blob_last_modified=1756926370.1431627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/225bf60f7c9796682210dc48d07a30d3dabab8ebc13abb7a84834279fa131020'), size_on_disk=5307353, blob_last_accessed=1756926376.2401364, blob_last_modified=1756926377.3751316), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417645/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/761eba472193b23cbf7735946e08aa1c4db5b1abd5125f1aad22ddabbf872f04'), size_on_disk=2080044, blob_last_accessed=1756926309.765425, blob_last_modified=1756926311.5084174), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417712/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/59cbfa83b9abe13f60d6a80b35afe857e3ffd761366133f60505312a2b5a72e2'), size_on_disk=641243, blob_last_accessed=1756926323.2163665, blob_last_modified=1756926323.7173643), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417923/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a32971eb62fe5c4c08298b1bdd97a916d8ef8f5898aee42aaf3fbaa8069e92d1'), size_on_disk=10004280, blob_last_accessed=1756926359.919207, blob_last_modified=1756926362.0851977), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417693/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/98a5478f5bd4feb033ac69510f2bf785c87777377b7b8b6dce24b8f89dbd6a03'), size_on_disk=18580692, blob_last_accessed=1756926319.7013817, blob_last_modified=1756926321.9783719), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417812/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9e8445f413d26051897f0fe3e7566b116a9a55062b78201990de334f17aecb31'), size_on_disk=19905089, blob_last_accessed=1756926339.0272977, blob_last_modified=1756926340.6232908), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417755/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f4ffef985aed69a80417b5179ef9923bd73378a93a97f2dff128b1f7716b4599'), size_on_disk=8437424, blob_last_accessed=1756926329.777338, blob_last_modified=1756926331.2423315), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417772/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a21b204a2da7654dd5bf4e7965a3c1e19159c16052232030787175a830ff0233'), size_on_disk=8204405, blob_last_accessed=1756926332.1323278, blob_last_modified=1756926333.453322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381052/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/652334151cb539b888d9cfcb60d9711d80bd74ec65cfa3799de10b2970ab9c09'), size_on_disk=5788579, blob_last_accessed=1756926777.7767494, blob_last_modified=1756926779.283741), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380999/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/acb2ee4711dab5800afb7ccbea72c8c80268b0a848723e7bd8ecf197decb982d'), size_on_disk=2605563, blob_last_accessed=1756926375.2281408, blob_last_modified=1756926376.1321368), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417751/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9a5733130de69115a731511dac8bf577fc5609a7f9649106ae9a12ffae7011ba'), size_on_disk=1660869, blob_last_accessed=1756926329.2023404, blob_last_modified=1756926329.9043374), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380959/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4693c1876e880fee1ec277fd5ae9127aafa9f40d41a3d5ed482dfcfa8cef55f1'), size_on_disk=873902, blob_last_accessed=1756926369.7061646, blob_last_modified=1756926370.1731627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417749/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8460c24102627d611c8918a1620078ee1bdf9929c44cdca863cd0dbb23f9cf1f'), size_on_disk=4938551, blob_last_accessed=1756926328.814342, blob_last_modified=1756926329.7143383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380957/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5a2362d35ac90fb1926051e6313886a649074b0022b128741a22a5ee24bb095d'), size_on_disk=5388789, blob_last_accessed=1756926369.5311654, blob_last_modified=1756926370.8571596), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417874/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6674c316eab3b03bbbe3502565efff7650445f5e1453933336d74938e42a14bf'), size_on_disk=6201897, blob_last_accessed=1756926350.5772476, blob_last_modified=1756926351.5142436), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417792/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/196cd0cf279f6bc85ca0dbeb1f089721f63eb167644d999b45d8cfe9a1f52acf'), size_on_disk=11982413, blob_last_accessed=1756926335.4593132, blob_last_modified=1756926336.651308), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417778/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/54e8ea03e214967eb4a1c7b50a086c4d7083d2c9c95dd0419e545fb0e8f18422'), size_on_disk=9345257, blob_last_accessed=1756926333.1513233, blob_last_modified=1756926334.0903192), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417933/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4fe347d88754646d39d93dd8a78e857d04d1903f7163a21495038b087d5f6b8a'), size_on_disk=12389872, blob_last_accessed=1756926361.7481992, blob_last_modified=1756926362.9061942), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417726/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/739e474fc54ffee0c7e6300d3abb1f83d33d48c78d137ed848f837fa186b9308'), size_on_disk=2430088, blob_last_accessed=1756926325.0663583, blob_last_modified=1756926325.6803558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380978/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a1d811dc3a4bfd44683ddf8bd0401a71d4941497c5cac8d8040a597fc5d26587'), size_on_disk=5902739, blob_last_accessed=1756926372.160154, blob_last_modified=1756926373.09715), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417878/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d3d105cc45280a5ea88c2bba754c0c5849d0cec7112a0926e9ed0c946e7f5cfd'), size_on_disk=8838404, blob_last_accessed=1756926351.2492447, blob_last_modified=1756926352.7352383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380993/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d568d29486ad3ec6f67f87b78cacb8b7102c591f979ec2bdb17ff75bde789670'), size_on_disk=3031778, blob_last_accessed=1756926374.496144, blob_last_modified=1756926375.2661407), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417892/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7ac6b85018422c74c894467b7b53b1c2dc7c5298fc31654bdea5ca70493e0a87'), size_on_disk=8194725, blob_last_accessed=1756926354.176232, blob_last_modified=1756926354.9712286), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417798/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0ac62fa7573f4b3c756fd9c280be4b97ec0c7c756e8c76307d4fab56d29e1c08'), size_on_disk=10509245, blob_last_accessed=1756926336.5193086, blob_last_modified=1756926337.800303), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417873/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d282d59c3c191bd7b7fa7251108224d0ac3929bc98f96a2c43f283f8a5cd046a'), size_on_disk=7609540, blob_last_accessed=1756926350.3852484, blob_last_modified=1756926351.373244), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417770/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0e7437c90c616b78ccc0963e0388385028b70d66fbe73e0ceb4e30e2d0239816'), size_on_disk=8252889, blob_last_accessed=1756926331.6563299, blob_last_modified=1756926333.0863235), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417603/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdfb1fd9b21341104e6423c684b0ae8a52ed1701eb3e232babb98340f953ea5b'), size_on_disk=4460026, blob_last_accessed=1756926300.366466, blob_last_modified=1756926301.2574623), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417795/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b995a48f28ae3baa145438a39c9fed73fa8689eb31e07ff02a3bc6bc827eb76e'), size_on_disk=8032882, blob_last_accessed=1756926336.1053104, blob_last_modified=1756926336.9993064), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381014/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5c5106dfb24bcfb1436aeb87b8218df873417ca2809aed71ca58e9b48ffacd29'), size_on_disk=1425306, blob_last_accessed=1756926377.5811305, blob_last_modified=1756926378.2571278), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417837/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/04d099809ca78bd87dcb9921398681ac64e827439049a37e97a71ba0828debfb'), size_on_disk=8348990, blob_last_accessed=1756926344.4012744, blob_last_modified=1756926345.6152692), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417785/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/209799965b03585e1143e881d87c5cea80d37e06637d2fc3f7e1ad2eaadfd3bc'), size_on_disk=11557515, blob_last_accessed=1756926334.1813188, blob_last_modified=1756926335.1353147), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417669/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8eaf08a45dbd72787c5250403f3d3079db9902e77527c7ca438abc740fdfb753'), size_on_disk=7376929, blob_last_accessed=1756926314.6724036, blob_last_modified=1756926316.0383978), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417733/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/542261ed8bf189205b611485f1a878f28cae5c90e452ba9cad687eacfdfc2814'), size_on_disk=2645461, blob_last_accessed=1756926325.8883548, blob_last_modified=1756926327.3873484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417819/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a1ba6808d672dbc9c58c26364c5cd9ed0b8a3041e6714844846648d3cd1aa050'), size_on_disk=3244517, blob_last_accessed=1756926340.7022905, blob_last_modified=1756926341.6952863), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417857/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/64f710e0b8a62bb6eddf3508f133b6b82af8bd21dd07f2319af6d07981085b1e'), size_on_disk=11109477, blob_last_accessed=1756926347.471261, blob_last_modified=1756926349.6202517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417841/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cb829be078dd8b533a90c2c548dfb2ce0455026be54c36572337e0a1ddeeb2cf'), size_on_disk=5791736, blob_last_accessed=1756926345.138271, blob_last_modified=1756926345.9902675), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417925/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8cdb783613764a35fd3a73db89463db6876951d443f5651a9a46877a53917f3f'), size_on_disk=912848, blob_last_accessed=1756926360.409205, blob_last_modified=1756926361.1532018), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417916/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2abad8555b89fbb5009e8f2a95e553a5ce579d1841ae4e71eec7b137f3282ef1'), size_on_disk=9236466, blob_last_accessed=1756926358.371214, blob_last_modified=1756926359.0582108), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417850/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c08fca103aa3dd1944dcf5c73b67397263842dc687b4101c8ab2c1a63d50205a'), size_on_disk=1182282, blob_last_accessed=1756926346.5122652, blob_last_modified=1756926347.3082619), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417635/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d2d1c241467bd5c55cf059f793b4236c99d48e59bec853d1052ff6c644e50d1b'), size_on_disk=3044373, blob_last_accessed=1756926307.2014363, blob_last_modified=1756926308.391431), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417606/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f76c81d98337c9e127bf5516af0dd135458fd622c01f2b5c9fd85ce6edfa4c7f'), size_on_disk=9502783, blob_last_accessed=1756926300.5794652, blob_last_modified=1756926301.6394606), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417653/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/59233342d3c3b71fce95d5295cf7c1e5ef3f4254dbc120229cb83b6c8115c759'), size_on_disk=12208357, blob_last_accessed=1756926311.136419, blob_last_modified=1756926312.3904135), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417629/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/29c9c86be94792d797005b106ab00d066634bea44fbf17e81738a00cbffa264b'), size_on_disk=22750780, blob_last_accessed=1756926305.5594435, blob_last_modified=1756926307.5804346), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381066/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ae017c72cf3b91ea3fdd40f63a126f640e7049340baca502ff3228c6ee40a7b4'), size_on_disk=13139272, blob_last_accessed=1756926780.000737, blob_last_modified=1756926782.6217225), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417902/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2d72fc510cd348af7611ca170b7804b228b74bd38e7ff5e437375c1cc926fa95'), size_on_disk=8952511, blob_last_accessed=1756926355.9322243, blob_last_modified=1756926357.2422187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380991/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dd86db1779581d5853d4d6a0fa2aeacdb00f92c832683ad788743f17db625db4'), size_on_disk=2640507, blob_last_accessed=1756926374.030146, blob_last_modified=1756926374.8661423), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381035/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/410edf5a6097a77bcec6880ec2618b4aec4bc10c44607163fe2b27aca0a05117'), size_on_disk=1451911, blob_last_accessed=1756926379.8691208, blob_last_modified=1756926380.5191178), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417657/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62398c518588ad11a939f95736cf0ed01a39890567cd4a8f05e9c9ba1155f9c1'), size_on_disk=10144285, blob_last_accessed=1756926311.5634172, blob_last_modified=1756926313.1174104), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417762/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c8b34d14be6dd44af03432d758f705cf81e86a46831c6deb4b0944248709630a'), size_on_disk=3024339, blob_last_accessed=1756926330.943333, blob_last_modified=1756926331.59933), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417633/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0b38824aeb2fac57eca256e3652539deb4bc221eeb5afc3701a997885f9ad9d0'), size_on_disk=19311587, blob_last_accessed=1756926306.8324378, blob_last_modified=1756926308.847429), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381041/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a53caa1961d9245d82db10989b8a8d601760ccf26f67666d5558937830c51e31'), size_on_disk=6621865, blob_last_accessed=1756926774.784766, blob_last_modified=1756926775.9747593), CachedFileInfo(file_name='GSE209631_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE209631_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/be49eef90682fbd55a5e60ada8b4c1c554e1b5a2ec1209996269db020d3bb074'), size_on_disk=4258, blob_last_accessed=1756926300.173467, blob_last_modified=1756926300.594465), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417697/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/198a3cd31740f347f0308db2914df4fc0fc303b4a9f040fbaf79fb5ca0170e7f'), size_on_disk=5521438, blob_last_accessed=1756926321.4153743, blob_last_modified=1756926322.4743698), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417611/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c82d882f6538d65316dd01e26a82af13e41717952c79e20dae6c3f1d9724ab60'), size_on_disk=3720891, blob_last_accessed=1756926301.4614613, blob_last_modified=1756926302.2704577), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417605/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f689b77eccd8d95d70fb616f4e2aa03a41201aec2a8c3c86849b7b145cb0f796'), size_on_disk=1787545, blob_last_accessed=1756926300.5184655, blob_last_modified=1756926301.74346), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417673/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/535d074f70f4553e24f3171fe5cfbd1821635ce7ca6ef93f8665876de620d6c9'), size_on_disk=6559693, blob_last_accessed=1756926315.1844015, blob_last_modified=1756926317.0733933), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381031/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b4705ac4577c8e7b116d1a1b677967f69412b0e4716e0de7fa7ee97bc9be4d74'), size_on_disk=4551217, blob_last_accessed=1756926379.2931232, blob_last_modified=1756926776.2707577), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417780/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4f1b7c3ded6441c8e906c3494ae63750d25303ee6f8d8e05120dc11ce8d79080'), size_on_disk=9025914, blob_last_accessed=1756926333.2923226, blob_last_modified=1756926334.387318), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417911/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3ca8b1b14564fbc90b44995a61a34d5a67482eb5fd867ce4f9f2196de2b73539'), size_on_disk=3051177, blob_last_accessed=1756926357.404218, blob_last_modified=1756926358.3022141), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417883/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8e23cc97e00adfd244a989f83702f4ce7a998ba7998dd95908a1b4ec256bc054'), size_on_disk=10709227, blob_last_accessed=1756926352.4542394, blob_last_modified=1756926353.675234), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417664/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/91fc5dce8497d1fc5466fb5ac31e44a20b295d8f97825500f256f6830ea2f983'), size_on_disk=8203399, blob_last_accessed=1756926313.1894102, blob_last_modified=1756926314.6624038), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381021/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3e80794742d10b6d120e70c4e4f1d7f77123ddf8aa18499f686b4abc8eadd325'), size_on_disk=1524611, blob_last_accessed=1756926378.410127, blob_last_modified=1756926378.897125), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417621/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/354c654ace9bc123bcd9009c56af518b98f1a547a7ccd79f348d0e533607a41e'), size_on_disk=20457307, blob_last_accessed=1756926302.5744565, blob_last_modified=1756926307.7874336), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380975/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ea018f4882a60f5fb07a4bbae51bd494e44e6df9b215d9da5e405687a567dcbf'), size_on_disk=8369150, blob_last_accessed=1756926371.6051564, blob_last_modified=1756926372.7471516), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380939/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dc6382d9dba7855504a6c7fa2a4adc44f564e25ff5c54eedd082c3cbe0a520a0'), size_on_disk=9235430, blob_last_accessed=1756926366.630178, blob_last_modified=1756926367.5861738), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417781/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9dd7989f90f8762246868ccd7d11c9ce876b2b33099efc3a4c5bf1bc7cbcca80'), size_on_disk=6923944, blob_last_accessed=1756926333.4783218, blob_last_modified=1756926335.3743136), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417784/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fd3dd53cf34f48c235e4f771378f42a5605446bbc62d108a5637a042513fd75d'), size_on_disk=4826642, blob_last_accessed=1756926333.7233207, blob_last_modified=1756926334.576317), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417919/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f5504428a85399cb212c74235abf9cec068dba03136a87ea79eae6d63a3cf430'), size_on_disk=7011211, blob_last_accessed=1756926358.6892123, blob_last_modified=1756926359.8312075), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417927/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8e639025209cff8d34b7e35a5fa371282bb725173225097a39f8f2101a49deed'), size_on_disk=7241057, blob_last_accessed=1756926360.6762037, blob_last_modified=1756926362.1171975), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381000/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/503f854145f3a220aa936072c3e66410d34914fef948992399a4addbfc977186'), size_on_disk=3904727, blob_last_accessed=1756926375.3401403, blob_last_modified=1756926376.5601351), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417748/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/db4c67c001a1f688f70918f0e25d3ddd996392b33b9f1144a14551bc0026d036'), size_on_disk=3246481, blob_last_accessed=1756926328.8123422, blob_last_modified=1756926329.6943383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381056/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/382194ca9758ddc18f059a649b13c5a21ead3a7f1fa5e012065315047a037a58'), size_on_disk=2351268, blob_last_accessed=1756926778.362746, blob_last_modified=1756926778.9827425), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381057/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4e6c617d22a655d32847df8b5334f4fb7236a15b8bf6e30aaabcbb0a4c55782f'), size_on_disk=17494867, blob_last_accessed=1756926778.5417452, blob_last_modified=1756926780.4997342), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417885/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6d5faeda491268a8ec88393883d743aac6cd14d4837698164ca32ca1052da36f'), size_on_disk=7668243, blob_last_accessed=1756926352.806238, blob_last_modified=1756926354.1422322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417661/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c6effb515a20066646b7863cb86e9dc012e14187e432ba7458cd6af18662bfd6'), size_on_disk=22122748, blob_last_accessed=1756926312.9054115, blob_last_modified=1756926315.6813993), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417699/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0c64ecbe6b245299fe04c04239d792fbae0f1b288562fe0c7912a24de56f0ad5'), size_on_disk=5707915, blob_last_accessed=1756926321.6203735, blob_last_modified=1756926322.8483682), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417816/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/86256a1b98c6d44750fd21ac9ea099262f1f0ec03d182b65741d669fb80f289e'), size_on_disk=10618044, blob_last_accessed=1756926339.7852945, blob_last_modified=1756926341.8372855), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417679/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bc48687cfd5c03c1a71787981180295dd3a27cf4d2c46bd26bb68cdf8b35925a'), size_on_disk=17346576, blob_last_accessed=1756926316.1203973, blob_last_modified=1756926317.84439), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417685/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/32edfbf9226b7ef1060ebe3e6986a1803d60408ccda969528cf4a59c5785c722'), size_on_disk=16967633, blob_last_accessed=1756926317.9163895, blob_last_modified=1756926319.5263824), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417852/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/43431a444b2761ed9b6fa4c7e6edfc8da31b64ff933408c517bc771e9f3e94dc'), size_on_disk=6967416, blob_last_accessed=1756926346.8272638, blob_last_modified=1756926347.8462594), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756926300.2174668, blob_last_modified=1756926300.2804666), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417842/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c763016dcbf1b53df840e3ea3c1b45234e2d9e797114000efaf692e2c394d487'), size_on_disk=9967694, blob_last_accessed=1756926345.1472712, blob_last_modified=1756926347.1812623), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417610/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a93c708ade83c1c4521589a51835a1dcc98972ab900e0eefdb8ce34e64394bf4'), size_on_disk=2566580, blob_last_accessed=1756926301.328462, blob_last_modified=1756926302.4814568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417831/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/53d810292e5a513a8ab543fe18783f8d493cedbadc7c97400dc103a4675954df'), size_on_disk=6767934, blob_last_accessed=1756926343.12528, blob_last_modified=1756926344.4612741), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417723/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f397cef2d9c491fb034826727c63b3c224f6ed791c9b4dcf884c63c55c9961c'), size_on_disk=2800893, blob_last_accessed=1756926324.70836, blob_last_modified=1756926325.4453568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417769/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e640018be0b2de04e0ed77c7586c413990876e91348531f43511c20da5fe279a'), size_on_disk=11568261, blob_last_accessed=1756926331.59533, blob_last_modified=1756926333.0913236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417705/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/92b4f5af6793c98b854bcca83c86ac0cb41ec4e40ec23d3ba9c4ff287852c63c'), size_on_disk=332184, blob_last_accessed=1756926322.5923693, blob_last_modified=1756926323.1503668), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417691/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/48de5a2968610e5ab855522d27f535d40e1973c1835ec32fcfa5bcd91a0795dc'), size_on_disk=14481663, blob_last_accessed=1756926319.447383, blob_last_modified=1756926321.2093751), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417948/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d43ea802a452aa9d57004381e1688222e61bd313b107077c1c663555547d1d44'), size_on_disk=1748124, blob_last_accessed=1756926364.3661878, blob_last_modified=1756926365.038185), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417646/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2b3d78fc052de8647b1ba6e83245b5f375b4e6c10952feff795855160458a1d5'), size_on_disk=396843, blob_last_accessed=1756926310.1544235, blob_last_modified=1756926311.0694194), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381050/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f3c3bac37a1d4ec633edf53ba305c970eb3de412fd54dfbaf0cdf89f39574334'), size_on_disk=1518412, blob_last_accessed=1756926777.1687527, blob_last_modified=1756926778.028748), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417728/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2be09708238c50a31b4e9b334c6202c26eaa6a950ebf3de4287651999d45097e'), size_on_disk=3385901, blob_last_accessed=1756926325.0603585, blob_last_modified=1756926325.869355), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4c4a265f9e3e8d373550ea58ca1cde6094c823f090336974fde3e7cee117fd99'), size_on_disk=1770007, blob_last_accessed=1756926376.6771345, blob_last_modified=1756926377.1011326), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417875/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8dc3634dae23cad7f562d15903341a67d3048929bd2198e53f71529b00a3508a'), size_on_disk=11490812, blob_last_accessed=1756926350.6762471, blob_last_modified=1756926351.7832425), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417775/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2ca9ba5e1394191206945f41168d555519fe3963d4c39f251c1ead57f2a323f4'), size_on_disk=1059891, blob_last_accessed=1756926332.3723266, blob_last_modified=1756926332.773325), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417803/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1fd8b542fb1b0e18bc5346c50ad85da440ff6190a987467ec6876ed3ec51a4e8'), size_on_disk=4928668, blob_last_accessed=1756926337.5433042, blob_last_modified=1756926338.7182992), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417741/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/734d5947cb26e877b6ae9e9adaf84575974a4fe2fb464ba11c4320906f17a42a'), size_on_disk=4566889, blob_last_accessed=1756926326.96635, blob_last_modified=1756926328.5653431), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381038/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/415f62b8333a02df8a87ba85e1d0de30a6cc9fdb07f7c8dc02d30f81ed4c14ef'), size_on_disk=2424198, blob_last_accessed=1756926379.8751206, blob_last_modified=1756926380.4921181), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380967/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3abcaaa4db306d580dc27882172ced53d591b8aa69a1805bd2a9eea0448ff941'), size_on_disk=1245882, blob_last_accessed=1756926370.6731606, blob_last_modified=1756926371.1021585), CachedFileInfo(file_name='GSE178430_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE178430_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/be2c4af55a502d7fa22abb28afe6db347950327bde1bc7e97dbb68fdb0c59f3d'), size_on_disk=9398, blob_last_accessed=1756926861.9192877, blob_last_modified=1756926300.5344653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417682/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/83b3100cfd1dc6918855c4a711ff607b36f40971655733855a7df353b4d5fca5'), size_on_disk=14037100, blob_last_accessed=1756926317.1663928, blob_last_modified=1756926319.1013844), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417822/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62cbbb0172158a2c0776000136424032a794757ca9e9f971e5f445deb20e8579'), size_on_disk=6040431, blob_last_accessed=1756926341.3882875, blob_last_modified=1756926342.8302813), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417626/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1981a2a1e321acc5c35bbeb9b61a12636c27f28d6e216de20093afdbee9f8689'), size_on_disk=12046611, blob_last_accessed=1756926304.4464483, blob_last_modified=1756926306.2084405), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380961/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fc4356aaa2a30217145b4aecc4f3fdd2575fcd696a2603fea3d8112998f770c7'), size_on_disk=798599, blob_last_accessed=1756926370.1791627, blob_last_modified=1756926370.589161), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380953/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5b3410430d33f3123b935982be90037f2d0a8e657f6b0b70a7042846d5ddd1b0'), size_on_disk=1192961, blob_last_accessed=1756926368.8401685, blob_last_modified=1756926369.627165), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381071/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cf15be48b47b6c369c6a4290b783b9b22b01d6cb89d04c738c275241548b811b'), size_on_disk=2543373, blob_last_accessed=1756926781.2297301, blob_last_modified=1756926782.309724), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417623/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/562a43ff381a2ca177fb1cc3dbe47fc2cf7cade495bad511ca7ed75aefca7a98'), size_on_disk=19049552, blob_last_accessed=1756926302.696456, blob_last_modified=1756926305.4934437), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417715/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ac5effc2ec07a8fb49bac4d77b7d4129a18ae2180807874ea02d1c252a3b68bd'), size_on_disk=520286, blob_last_accessed=1756926323.8693635, blob_last_modified=1756926324.477361), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417859/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e4c5915df6cb5bb450e1948d8c43bbe031d09ec345688ab51f2d8ea60f3be09f'), size_on_disk=12709203, blob_last_accessed=1756926347.8872592, blob_last_modified=1756926348.9972544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417937/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e432985b09ba72eab54fc008b87b20a718dbae96d920a61b47de7e16d214227c'), size_on_disk=6445266, blob_last_accessed=1756926362.6341953, blob_last_modified=1756926363.6641908), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417763/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/91d8a58ead35fc52f7c2649ce9509a92236a20ca8c26f5f2ef4ba1a38e64afb1'), size_on_disk=230495, blob_last_accessed=1756926330.8943331, blob_last_modified=1756926331.4653306), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380954/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/338886924ec42501a99a4b842e18fdbed28200d52daaf43e00ee601a147e0188'), size_on_disk=4903852, blob_last_accessed=1756926369.2841666, blob_last_modified=1756926370.101163), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417707/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/89192f5007f730a90c823219ea107c799935f5e824963090abad813603160fa8'), size_on_disk=2615872, blob_last_accessed=1756926322.9183679, blob_last_modified=1756926323.9473634), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417622/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/28eed3b67a6206b453d8adb775edf2bea32cfd54ba5b37d51787f67280e0317f'), size_on_disk=20411775, blob_last_accessed=1756926302.674456, blob_last_modified=1756926304.8564465), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417758/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/54f8e8d84e5b8b9457aaa143829fcfd5a4715d0fb184478ce659cd6c6efe7be1'), size_on_disk=4176901, blob_last_accessed=1756926330.0203369, blob_last_modified=1756926331.1383321), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417939/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a0fc7804b026f95809f33087deb75f00beeb9635475b62864d3e58e948361bfa'), size_on_disk=9099632, blob_last_accessed=1756926362.9701939, blob_last_modified=1756926363.9411898), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417658/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b6cf4c024743be14fcd5730f0ddbb960dbcd83d07adf8668639f9a1cdf195368'), size_on_disk=8754108, blob_last_accessed=1756926311.5764172, blob_last_modified=1756926313.1134105), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417829/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/63a9af789279180a07d8e8afe07bb0e9b8aa32c4340e01347a6f7ee809198308'), size_on_disk=5677879, blob_last_accessed=1756926342.904281, blob_last_modified=1756926343.7332773), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417721/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5457a24eab864fb369ae0e6fbdb302876e2a6c65ae83444f18bc01ad08ecfc0c'), size_on_disk=2073045, blob_last_accessed=1756926324.3243616, blob_last_modified=1756926324.9983587), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417759/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b2dad4cf4b03c846adf9d5229aeab340e58034c751bc68b37bb0c8402a7c071b'), size_on_disk=1372312, blob_last_accessed=1756926330.1243365, blob_last_modified=1756926331.1623318), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417782/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d6624acaa7c4ec21ae02d6d1322b8669a148308095e714b41bff868483fe2fa9'), size_on_disk=11742567, blob_last_accessed=1756926333.5273216, blob_last_modified=1756926334.824316), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381029/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6431cec7b7404a0773d8715761e2b161f01ebd8471950e71e17dad8976d83f3c'), size_on_disk=7229691, blob_last_accessed=1756926379.0071244, blob_last_modified=1756926776.565756), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417879/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6714b3f5155dd540962a7541625f45e14e6a93b4c01e913790b70818e50c821b'), size_on_disk=8601872, blob_last_accessed=1756926351.4612439, blob_last_modified=1756926352.8182378), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380994/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7c5f237d9c32233a005e39386360f02d9e1932f9ac267ac3458f96aaf59e01ef'), size_on_disk=2496053, blob_last_accessed=1756926374.8051426, blob_last_modified=1756926375.3471403), CachedFileInfo(file_name='GSE222268_metadata.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE222268_metadata.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4459325dc2deeb9f00d803b89cd8fdc823ed383f'), size_on_disk=61, blob_last_accessed=1756926300.2714665, blob_last_modified=1756926300.3444662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417607/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/623d085888c300fba1d05a9018ec3d4c31912f2b91a872ef34feda3322889b70'), size_on_disk=6809356, blob_last_accessed=1756926300.611465, blob_last_modified=1756926301.75846), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417615/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3b8b3c4c686238c7c642151bbdcb2ba7796de56c1922e558a1e7f08507dc5abb'), size_on_disk=2866472, blob_last_accessed=1756926301.8314598, blob_last_modified=1756926302.5744565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381016/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0e597280417f8f8b5b476bc9d66033afc9412759f5e2f9bd9f3367932cc6f03f'), size_on_disk=820782, blob_last_accessed=1756926377.9791288, blob_last_modified=1756926378.4041271), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417777/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bd953fd8a3ee2a5c90382c1c9adc8091fbca801aa87f07a409b6dd5a9704421f'), size_on_disk=1267333, blob_last_accessed=1756926332.9623241, blob_last_modified=1756926333.6353211), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380930/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/734e9bc8119c7a507e9cf84f9926b1f9d06ccc8b0062a9b4dc36631f6f1bc6d1'), size_on_disk=4604638, blob_last_accessed=1756926365.1301844, blob_last_modified=1756926365.9251812), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417616/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0b0f3c9481fd0e254abd544ba803de0c9da9b3a686451975f981d02fc481148d'), size_on_disk=15058562, blob_last_accessed=1756926301.8254597, blob_last_modified=1756926305.0234458), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417655/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/25d98ba17be9b354b0ec7a0e513a6ff9d4593dd1cadf269f28bcea1ca9c71565'), size_on_disk=5461224, blob_last_accessed=1756926311.5504172, blob_last_modified=1756926312.9474113), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381019/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3023e878b223b00b77d3411a6d20195b85aa7e61a8445fe725ff694c77816053'), size_on_disk=1022843, blob_last_accessed=1756926378.3191273, blob_last_modified=1756926378.864125), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fc6783c3340c869bc3838a9a9d5abb8bca67361feeef12c42b6cf478d73bfd66'), size_on_disk=9020794, blob_last_accessed=1756926366.2571797, blob_last_modified=1756926367.2561753)}), refs=frozenset({'main'}), last_modified=1756926783.3167186)}), last_accessed=1756926861.9192877, last_modified=1756926783.3167186), CachedRepoInfo(repo_id='BrentLab/mahendrawada_2025', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025'), size_on_disk=94317969, nb_files=8, revisions=frozenset({CachedRevisionInfo(commit_hash='3b912489743a1797199beb023a49b14f7e3ba19d', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/3b912489743a1797199beb023a49b14f7e3ba19d'), size_on_disk=1957331, files=frozenset({CachedFileInfo(file_name='chec_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/3b912489743a1797199beb023a49b14f7e3ba19d/chec_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/dd3e967d8c54c6056dc9fd6bdb921c3eed9d10df3a449273bd08faeb1b936765'), size_on_disk=1220601, blob_last_accessed=1759345153.538238, blob_last_modified=1758569549.9902885), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/3b912489743a1797199beb023a49b14f7e3ba19d/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/c8cbc56ef9fad47193120cce2affd35374b8373d'), size_on_disk=18689, blob_last_accessed=1759345153.4962385, blob_last_modified=1758569549.1302917), CachedFileInfo(file_name='features_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/3b912489743a1797199beb023a49b14f7e3ba19d/features_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/618ea89d880c8a1b4c6d05cc3c13a70cbb05784d10bf2b6c4fc954705203096c'), size_on_disk=431436, blob_last_accessed=1759345156.897212, blob_last_modified=1758652945.6395862), CachedFileInfo(file_name='rnaseq_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/3b912489743a1797199beb023a49b14f7e3ba19d/rnaseq_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/4411789f50aab449658c100f2758ceb410022a0a30f956fd5fe41152915580f9'), size_on_disk=286605, blob_last_accessed=1759345153.5952375, blob_last_modified=1758569588.8621461)}), refs=frozenset(), last_modified=1758652945.6395862), CachedRevisionInfo(commit_hash='8bea431be57e21633be4174df291540b1fae212a', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/8bea431be57e21633be4174df291540b1fae212a'), size_on_disk=18603, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/8bea431be57e21633be4174df291540b1fae212a/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/6a2a5a6a8006e386c0e47a2a98151442046d3d41'), size_on_disk=18603, blob_last_accessed=1758568089.249362, blob_last_modified=1758568089.246362)}), refs=frozenset(), last_modified=1758568089.246362), CachedRevisionInfo(commit_hash='874a5dfe4052a1e71b6544a2e5b2c99ad4286c04', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/874a5dfe4052a1e71b6544a2e5b2c99ad4286c04'), size_on_disk=18603, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/874a5dfe4052a1e71b6544a2e5b2c99ad4286c04/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/685c0e56e0c68e20e3c2af8e38267095d686c03c'), size_on_disk=18603, blob_last_accessed=1758569041.2889845, blob_last_modified=1758569041.2879846)}), refs=frozenset(), last_modified=1758569041.2879846), CachedRevisionInfo(commit_hash='af5ac9dc922b7fbd14f460c2fe94b727db1e1245', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/af5ac9dc922b7fbd14f460c2fe94b727db1e1245'), size_on_disk=92323432, files=frozenset({CachedFileInfo(file_name='reprocess_diffcontrol_5prime.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/af5ac9dc922b7fbd14f460c2fe94b727db1e1245/reprocess_diffcontrol_5prime.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/eba528776f8fe8da63c39ee0660527bc35f989ac6a976daf09270860cad6d735'), size_on_disk=92302997, blob_last_accessed=1763665270.5143101, blob_last_modified=1763578870.280984), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/af5ac9dc922b7fbd14f460c2fe94b727db1e1245/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/8f0cf5e3dd2bb5bdc2f8fde970e31a2404481fec'), size_on_disk=20435, blob_last_accessed=1764298314.9501038, blob_last_modified=1763578711.307058)}), refs=frozenset({'main'}), last_modified=1763578870.280984)}), last_accessed=1764298314.9501038, last_modified=1763578870.280984), CachedRepoInfo(repo_id='BrentLab/hughes_2006', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006'), size_on_disk=11969274, nb_files=11, revisions=frozenset({CachedRevisionInfo(commit_hash='51ae7addf429c955a97934a15b242ff8a2ccd653', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/51ae7addf429c955a97934a15b242ff8a2ccd653'), size_on_disk=5701, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/51ae7addf429c955a97934a15b242ff8a2ccd653/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/f799807a7bf81229b0d063e6c939339c1f5c90f4'), size_on_disk=5701, blob_last_accessed=1756855057.8437808, blob_last_modified=1756855057.8417807)}), refs=frozenset(), last_modified=1756855057.8417807), CachedRevisionInfo(commit_hash='0de73d0932e423cfbbfbc1b21d029c96490dc200', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/0de73d0932e423cfbbfbc1b21d029c96490dc200'), size_on_disk=8791, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/0de73d0932e423cfbbfbc1b21d029c96490dc200/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/76efc047ec3c08e362cfcaac776b7e05c02f8a6b'), size_on_disk=8791, blob_last_accessed=1764712744.7980804, blob_last_modified=1764712744.7960804)}), refs=frozenset(), last_modified=1764712744.7960804), CachedRevisionInfo(commit_hash='1009546961df1cf9743a03383826d8a5e010bb38', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/1009546961df1cf9743a03383826d8a5e010bb38'), size_on_disk=17152, files=frozenset({CachedFileInfo(file_name='metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/1009546961df1cf9743a03383826d8a5e010bb38/metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/54173aa9fff98e5b077e2b24e139ad600a6d7cea74a47b0305cb0fbdf1c14d9b'), size_on_disk=8362, blob_last_accessed=1764713263.750364, blob_last_modified=1764713263.741364), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/1009546961df1cf9743a03383826d8a5e010bb38/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/572115c6e031637a3ac876795a6f91d3653bf5e2'), size_on_disk=8790, blob_last_accessed=1764713262.7733643, blob_last_modified=1764713223.5823674)}), refs=frozenset({'main'}), last_modified=1764713263.741364), CachedRevisionInfo(commit_hash='cc20720c4fe7de9151a051aafbd41e6de2b4cd84', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84'), size_on_disk=11937630, files=frozenset({CachedFileInfo(file_name='knockout.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/knockout.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/5e000e2e9401011c9ecc81655c94d55b7f9469ed856216fa21cef2883a904c93'), size_on_disk=5571518, blob_last_accessed=1756856029.2493467, blob_last_modified=1756856029.2143471), CachedFileInfo(file_name='overexpression.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/overexpression.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/9b8cc1feede780efe0e8537555ed1cbbf067e7fa9aa7a7edc74e5a343c64a443'), size_on_disk=6337781, blob_last_accessed=1756856028.536351, blob_last_modified=1756856029.230347), CachedFileInfo(file_name='metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/e27a67137876123c68c229705cfbb8e06ed3ee76a3a868b66d80d624f8d03671'), size_on_disk=7942, blob_last_accessed=1756856028.533351, blob_last_modified=1756856028.9083488), CachedFileInfo(file_name='checksum.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/checksum.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/973b686313e32c96b6e0ef1dbc84020857c073ef'), size_on_disk=159, blob_last_accessed=1756856028.5883508, blob_last_modified=1756856028.6513503), CachedFileInfo(file_name='parse_hughes_2006.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/scripts/parse_hughes_2006.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/0c2645bdc0ab7f6be38a428a0a92752e9d3f88d5'), size_on_disk=9255, blob_last_accessed=1756856028.5803509, blob_last_modified=1756856028.6393504), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/842b551fb3d1ba1e6f1daa35157a0c7c616f7655'), size_on_disk=8514, blob_last_accessed=1756855276.4871445, blob_last_modified=1756855276.4861445), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756856028.69735, blob_last_modified=1756856028.7793496)}), refs=frozenset(), last_modified=1756856029.230347)}), last_accessed=1764713263.750364, last_modified=1764713263.741364), CachedRepoInfo(repo_id='BrentLab/callingcards', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards'), size_on_disk=49606009, nb_files=137, revisions=frozenset({CachedRevisionInfo(commit_hash='9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0'), size_on_disk=49588983, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6140/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/66c2da17957d7e6a8b159d3573879ac779d0094e35150b51e2643f7ddee36e1f'), size_on_disk=332958, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4236548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=MAG001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/30b63d014686a103b5e7fc6571e902fa9c08630a96557cc12ec9dfcf60871306'), size_on_disk=347333, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.559651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR007B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6f0620b102e7cbfadced7d5530545a185036b53118d3e2b7f074c56403d80f4b'), size_on_disk=263185, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7616518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7295/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/19bd07f913e0af3460e6b4b5091971ceed8f4aa510cf18b82ec36dabb67c68b8'), size_on_disk=272619, blob_last_accessed=1763591393.1151721, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6863/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec534c91cae4413d30477014131d39b51c9c236dfbf150874bd21ec3de821613'), size_on_disk=389025, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648269.6376605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7118/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/be8bd409b83526d5671501de6ef2980177955fdcc3a13d5e153d2593493b2a1e'), size_on_disk=346308, blob_last_accessed=1763591392.9281735, blob_last_modified=1758648270.1336627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7297/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4bd4088cbfcccdf96def916bb8fd9d673327d3e57c2a4a3e57299743ce4aca7'), size_on_disk=261344, blob_last_accessed=1763591393.588169, blob_last_modified=1758648270.6516652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5301_5088/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/65ef775934339b80eda59a141cf791a74e1c6f12513d8143307cebe2f11c3738'), size_on_disk=410106, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.9516528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5617/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bc01241e8363373c995a0fa7439aa1e432a60eb8a7805124fff302311f638361'), size_on_disk=351950, blob_last_accessed=1763591393.6541686, blob_last_modified=1758648268.1026535), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7367/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a7778bb24506047963575fa96126157895c7f329996c729c3e7d6070595955c3'), size_on_disk=324361, blob_last_accessed=1763591392.1631787, blob_last_modified=1758648270.628665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4d098c33564f99ec6aed77d585d1f6e871806f441f099790eef4d6478e322af'), size_on_disk=264459, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7486472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6073/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d68ca6411ba47eb63adad81e88fa1341a0ee269897268957ae936dcc9ba7788'), size_on_disk=450031, blob_last_accessed=1763591393.1341722, blob_last_modified=1758648268.3886547), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6061/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73e03845d0fbff1a5931e4f792e2c1965ade02fc622ffc08e05c267ac6cd3231'), size_on_disk=394930, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.447655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7211/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73d7747698fa1168c89f12c105bbe900d5969d27a82ae2302d0720d761fbfdab'), size_on_disk=298482, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.1276627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6770/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/082f14f7b0d5fc47e088efc40ae85a8ec41f99437c66da96ca69af210bce61cc'), size_on_disk=318118, blob_last_accessed=1763591392.8081744, blob_last_modified=1758648269.3776593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/62e9a627f20607bc4887ebc9277131f974ec80ff39ac386775f4b8f2f5cb9d1d'), size_on_disk=346782, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7476518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/11485fd7bb94d0093ce2508d541fea3ce64f934197d8a5f7bb26ecbdb59f8dbb'), size_on_disk=124642, blob_last_accessed=1763591392.3321776, blob_last_modified=1758648267.0666487), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5961/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/49cac5c2df1adf790a9ac0209db6c0945de50b8191d403e8b1ad386ee2dcb35e'), size_on_disk=321368, blob_last_accessed=1763591392.560176, blob_last_modified=1758648268.215654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6532/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3041f19cf0dcb3e0bd9b1178994351a22f3297ccd43c5e291df8bfe0ad8d5d02'), size_on_disk=516437, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.8236568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6506/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/986d9d3b4ed064d66ebaa0f2502b933f1650a20ac4bbbba804e2cde03b9454c0'), size_on_disk=378407, blob_last_accessed=1763591391.97318, blob_last_modified=1758648268.7656565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5614/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1aa702ff57d34ebed961043e0efae064bfdb62f589cbb515311ae8ea37ba8c76'), size_on_disk=429423, blob_last_accessed=1763591393.4671698, blob_last_modified=1758648268.0406532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6764/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec87eef9e2e8cdf3c3a10ead28dcf1bfa76e94e12a42422a46c8559fb18207c2'), size_on_disk=75684, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.4036593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02313d4d85859a7353e56d89b6e14bcec5160849b5c0089ccbecaa1cda5012cf'), size_on_disk=301213, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4356549), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6635/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6c5b7f1fda3a7f5679870f438d13a45558f7f8da0f479c5144d7751bc8f7fbbc'), size_on_disk=420658, blob_last_accessed=1763591391.95018, blob_last_modified=1758648269.3956594), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ef036874e53ee98dfe1b5d84c42749fc40da9279cd573174321ac83201c1651'), size_on_disk=349762, blob_last_accessed=1763591393.0791724, blob_last_modified=1758648267.4966507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=KB001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38c939d54d3ed77a09650d2ccf00a6ed0e20390b8be722f1c3ed7881a5904a60'), size_on_disk=351677, blob_last_accessed=1763591392.9151735, blob_last_modified=1758648267.5566509), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e5b3174998823af40d055f46ca06e7bb44601cbe97b133062cd3e0491dd30ac2'), size_on_disk=323308, blob_last_accessed=1763591393.153172, blob_last_modified=1758648267.2686496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fc9311b65abb851fbbfd8bb0c0765af59069659f5a715aee546a8644b0af4ec2'), size_on_disk=210948, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648267.6766515), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7283/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0dc91bfacca82c5ed23de7757d59f9f808d541d96dd79aab56f0c4252e7aabf3'), size_on_disk=265146, blob_last_accessed=1763591392.7731745, blob_last_modified=1758648270.401664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6374/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e410602f56bc94d9831a2bc8bdb865ee4ebe9862a65b603165b655fbcc4b9914'), size_on_disk=441531, blob_last_accessed=1763591393.1891718, blob_last_modified=1758648268.5356555), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/33d7303ed077f489ee28021daca0bbc859feb1cfd383dfc9a856ea13994801a9'), size_on_disk=331877, blob_last_accessed=1763591393.292171, blob_last_modified=1758648268.2226539), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6545/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/260c4a280f52024cb12d44559a6189641ee02efbaf20168435196efbfc69d741'), size_on_disk=404812, blob_last_accessed=1763591392.3761773, blob_last_modified=1758648268.8256567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR006B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fcc5e886ec7c6363a5ab51b03d3da189ac3e0cde0a0f79d4f0c46043a6b61838'), size_on_disk=253857, blob_last_accessed=1763591392.0501795, blob_last_modified=1758648267.785652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS011/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ea7cb0559b342dd0ec278e7d5846a5ba0c0d867ea04d545f72e0d1c9472a0cc'), size_on_disk=363979, blob_last_accessed=1763591392.9791732, blob_last_modified=1758648267.0116484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6872/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02fabe6e32f52fe6fca923beb0a6cc2898d991304e23a3e75f3ae8a6bcf361c6'), size_on_disk=274362, blob_last_accessed=1763591393.6231687, blob_last_modified=1758648269.6966608), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a76f0849e518faedfe50e6f0c76908a3f6beed34fd0b607603db1859493595d0'), size_on_disk=292312, blob_last_accessed=1763587658.6168654, blob_last_modified=1758648266.7676473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5399/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b65d0aa7ddc98940f91f2ae5f07e1d3f81dda688792552b91fcc39fb4f9ededa'), size_on_disk=256729, blob_last_accessed=1763591392.5471761, blob_last_modified=1758648267.972653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6437/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/556084dbbc174db5450872d6f5e66cc72091380ea32006977dd176e23c105a7f'), size_on_disk=389840, blob_last_accessed=1763591392.714175, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6567/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0e9713a94594dfcbc80134af52449e12ef207ca9149f5a96c43b6cb457b2431c'), size_on_disk=478809, blob_last_accessed=1763591392.6241755, blob_last_modified=1758648268.893657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7370/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ff4dce26afb34b291220c17625be4a28078f9721a8b000ea177c4add3bf1a039'), size_on_disk=241010, blob_last_accessed=1763591393.7701678, blob_last_modified=1758648270.6726654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5a70fa6f22aeda3246d17646cc4ec7c5a46e1993f9981b19d9fd5a8cc99bf11f'), size_on_disk=308858, blob_last_accessed=1763591391.9411802, blob_last_modified=1758648267.0186484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6177/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b91ffef1dfcdf7952df46956d76a4b364aa0d8342453636cf09e6a343607184f'), size_on_disk=387618, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.424655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6959/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c0cb6c2ec2bda19b0c1da5d5fc2229d0b6d6987d75646fa230c0c14d591cf1f1'), size_on_disk=537588, blob_last_accessed=1763591392.851174, blob_last_modified=1758648269.8686616), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6390/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f324cdf73148c8b0fa23456bc4f51cff3032d08ed42e44dd9fcc4815a6d969c'), size_on_disk=560601, blob_last_accessed=1763591392.7391748, blob_last_modified=1758648268.6206558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7151/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9969e4607d6bf42424f5d2ce8f0d16e3d2d89d069cae6d75997e99c88a68dc77'), size_on_disk=275572, blob_last_accessed=1763591393.7231681, blob_last_modified=1758648270.1476629), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4e8332a5db6260f6357f940e62e3e7b622785f386e81cdb9c75b538d318f4a6'), size_on_disk=158978, blob_last_accessed=1763591393.5471692, blob_last_modified=1758648266.7626472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6421/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1e3bacd3bcf1fcb46cfc5f9ab66c2af57e190f3d006031e7ef37864fbd36aaca'), size_on_disk=495452, blob_last_accessed=1763591392.2161784, blob_last_modified=1758648268.6366558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6708/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/35520976736159652e3be8066e77b1371522f63a8b3564d9b0d9f5b1598a144d'), size_on_disk=253955, blob_last_accessed=1763591393.6881683, blob_last_modified=1758648269.1996584), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6551/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c3729692991cdcafe9baa3d2805b3866f72fe9bc89ba22c6d2cdae4d5b5bbf5f'), size_on_disk=545477, blob_last_accessed=1763591393.0611725, blob_last_modified=1758648268.8376567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1fa09a5a6e6e69198501d994050adfcad7ff39a3cc2fe94e7ea177414e1bc968'), size_on_disk=303532, blob_last_accessed=1763591393.7091682, blob_last_modified=1758648267.2776496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ac8c590c19e7b95b9aac571b1394976039f25fdd66e62a68ecb99925433f8e54'), size_on_disk=355706, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0226483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d3e36418bd46acf9537a8f864b1a1361a062d72fec2b093789b88ed655ec117'), size_on_disk=377393, blob_last_accessed=1763591393.0451727, blob_last_modified=1758648267.0136483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dcb33135a9d854d17a6b80348f6bee8becea9ca3808583a034f89c5e4f460c54'), size_on_disk=218643, blob_last_accessed=1763591393.4831698, blob_last_modified=1758648267.9636528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/00e5602b87ac84670a98c081e6520ca744038f4e9b3a72a78cb61f5926be56b1'), size_on_disk=251087, blob_last_accessed=1763591392.5781758, blob_last_modified=1758648267.8286521), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e91fd764a83947c14f35d7a2d8c0d156c0a61a32a1da96273718ef4be720787'), size_on_disk=361855, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.7196517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6527/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/10081f44b1c2f5ad32489da51fc9ac5b5289b54688a7e78725db247a644b7e4d'), size_on_disk=494640, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.882657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6861/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6955b1a1746fe41162ea1d7a098482bbfdb3d0e411c809b52d698e285627ce03'), size_on_disk=385103, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6146603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6689/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/90ea249116dc6a9be86974e1ec0acf79558dfd56a5666be2e05d2107b8c4ee60'), size_on_disk=366787, blob_last_accessed=1763591393.6661685, blob_last_modified=1758648269.1406581), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6021/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eea0d101dd58c9619b41a94009fc0dff8156d2d36d7f2b88bb653e96123a032e'), size_on_disk=439545, blob_last_accessed=1763591393.5531693, blob_last_modified=1758648268.258654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6920/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/45122fcfb3abc3a43631023712c224dc6e00dba566fb2e15deb4ab4501ad2538'), size_on_disk=446998, blob_last_accessed=1763591392.8881738, blob_last_modified=1758648269.6416605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6448/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/182dc134914a5ddb02992ab6a4f1c12d2450aa331820bf7bd95693dc999bcfae'), size_on_disk=390210, blob_last_accessed=1763591392.9591732, blob_last_modified=1758648268.672656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6954/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/aeed3c55d81a4eb0c568486e476211c599ce2bf37f9dff22c7dfca23f5d80495'), size_on_disk=476054, blob_last_accessed=1763591393.011173, blob_last_modified=1758648269.943662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6853/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ed47ecb2aa870fdbf12920fd4486933262c0a24318c9c8c5531e359a3c416c90'), size_on_disk=515114, blob_last_accessed=1763591393.2661712, blob_last_modified=1758648269.4276595), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1c8a8cb23b1ec073827d6f5343a3bf03a9d7a04cf2ae3fd3ea4cc5ac37673b38'), size_on_disk=342987, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.6226559), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6572/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3488eaac5e4b9b3418b909138e6b50e3f9ebaf3e7345a4a10b6ced18b66152e4'), size_on_disk=440579, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9506574), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6055/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/31bb70b4c90654ecb276664f5c914679322b24985ec66d5b396a99f253ebfa17'), size_on_disk=377015, blob_last_accessed=1763591393.5701692, blob_last_modified=1758648268.3076544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6513/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c8f9c31704c2f8732b4668e1e6410034b8adf2ada39ff55935aa0199e920d055'), size_on_disk=507367, blob_last_accessed=1763591392.1451788, blob_last_modified=1758648268.8406568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6965/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9ae4546d58672f0632757c94b595af8b7c40da215611783fdc1a65000075ec98'), size_on_disk=509677, blob_last_accessed=1763591392.2731779, blob_last_modified=1758648269.933662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6354/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/80b129a46defc7b06e472a35935bfaf410bc5e456475b2885feb2c8a4934a81b'), size_on_disk=173225, blob_last_accessed=1763591393.4981697, blob_last_modified=1758648268.4986553), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bd2017e75947ead2c06f9d1936347f7fdcfac7fbff620e0b39077208a02c3bd9'), size_on_disk=303094, blob_last_accessed=1763591393.1271722, blob_last_modified=1758648269.9296618), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6731/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/23ecd71c4e3c1c547295bb756d4654eb413ff288d10118ab286cd21ccd95b666'), size_on_disk=346187, blob_last_accessed=1763591392.1871786, blob_last_modified=1758648269.1906583), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ac08e026f6d48c034f13a49f3531c90b6c070907'), size_on_disk=9519, blob_last_accessed=1763588875.9720669, blob_last_modified=1763588875.9700668), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/530b5650d91dae3bf169f967af8e800611e3e01daeb1811d6fd21c2612ac4ad9'), size_on_disk=315170, blob_last_accessed=1763591393.6121688, blob_last_modified=1758648266.7956474), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5690/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4831e3e037d421a14efbe1da8c369015ec7d01df351bb27f0ef0400c8b94c693'), size_on_disk=311754, blob_last_accessed=1763591392.9021738, blob_last_modified=1758648268.211654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d4c0cbbf6075a8b5732e3cef2ba154d95d21aa0efc7e49d947cceebd8742b30'), size_on_disk=269007, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4106548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7258/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7b1860ba3be7dbf053203fa5690da2002f7841186940231acea6064e70f0bdc8'), size_on_disk=171331, blob_last_accessed=1763591392.6171756, blob_last_modified=1758648270.3746638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6651/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7644af4cc956ea08809ab6c80c65095e3fdbd36e056040c0eaded3773d546359'), size_on_disk=559951, blob_last_accessed=1763591393.6361687, blob_last_modified=1758648269.1856585), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7155/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/521fc1bea6963640645c21cbdb8d1de07793f11213c23ac71d249f3252b32f54'), size_on_disk=304554, blob_last_accessed=1763591393.5841691, blob_last_modified=1758648270.1366627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6924/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5e9b3a94d73c1720e6b05ea55000054905a8672edea7f950736ce3fe191f8faf'), size_on_disk=412939, blob_last_accessed=1763591393.748168, blob_last_modified=1758648269.7066607), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6923/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/454ca6a52d4b305eddc4b6967ef9604be4861269fdcfff419883daed9b2fe48b'), size_on_disk=337423, blob_last_accessed=1763591393.5031695, blob_last_modified=1758648269.6476605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5801/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/12e91a415cc2a149af4c34c3c8b032821ff301e2828dfb9f4636bd6b2913ea3d'), size_on_disk=360485, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.1636536), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7063/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9427d8f8c54e3c0cc9d720958ef15d3859f00cdfb678559e0268387a36a8ef37'), size_on_disk=404051, blob_last_accessed=1763591392.5301762, blob_last_modified=1758648269.8976617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS006b/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7eedae811a5b3dd4389e7e814d35b022bfaf01fc4d2cfb47be6e6786d6d58c64'), size_on_disk=315912, blob_last_accessed=1763591393.2571712, blob_last_modified=1758648266.7546473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bbe701ffd546a2f4359f821262f7b44232bdc328d9d4719bfdaa8f2aedc6d2e9'), size_on_disk=346079, blob_last_accessed=1763591393.0861723, blob_last_modified=1758648267.589651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5986/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f1179c75ac6e91de03b67a73882f3b9c07c8e435d938d3059de489403adde7a'), size_on_disk=328749, blob_last_accessed=1763591392.691175, blob_last_modified=1758648268.228654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7331/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f159a9811b8ac1b5e252b52427979a3bddce2e7131b13f84046b31e47f7fa399'), size_on_disk=242821, blob_last_accessed=1763591393.2501714, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eb61968b5ec5eadd9087e42ead29333f91195dd5b3f9c5a616b41e94ca23c494'), size_on_disk=330245, blob_last_accessed=1763591393.6761684, blob_last_modified=1758648269.710661), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6808/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c71f5371e8c803cfc7fd507d0b3c11f7548db5d7a0fa42c68977169369ea036e'), size_on_disk=422880, blob_last_accessed=1763591392.9491735, blob_last_modified=1758648269.3916593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ab338795b8bbf334650386a123f6e0df41363cb5dcbb778d485ce58804e96b29'), size_on_disk=375506, blob_last_accessed=1763591392.9711733, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7237/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/110a743162ace7792a44dfe48d7afd40bb67c45c722038c981548477ebc5d8fe'), size_on_disk=313207, blob_last_accessed=1763591393.143172, blob_last_modified=1758648270.1426628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7137/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/703fe9f8552adcbdfb5e640509cb62df2fa734b43fe1e98fc1fd342415f823d7'), size_on_disk=515398, blob_last_accessed=1763589053.7698598, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f427ea0f4ded3b6ff841153d8ad87d72a26a964c189246fb6fd5c439bd8b0c1e'), size_on_disk=353623, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.812652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7115/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/205443fd87ce5c793dab86eb75c21dab7dc2ae2edac41d7e6314cfe491bdb01d'), size_on_disk=321850, blob_last_accessed=1763591393.2331715, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6855/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48e48378d24404e926bc1c44b10af60d4b12f82124ff2f80bc0b69b4a0f98740'), size_on_disk=609248, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6556606), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6798/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/899cde55032cd593be0013e1f6ff33fe9a12f4616477b1410d86fae1b2008a09'), size_on_disk=342376, blob_last_accessed=1763591392.7951744, blob_last_modified=1758648269.4006593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6677/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4ee6e1d3e2b9a9bbcc8851048d2b16f4aea6a1e2f752afb9e0bf75a56adf7042'), size_on_disk=322147, blob_last_accessed=1763591392.393177, blob_last_modified=1758648269.0826578), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7243/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cb59e645fff2683db692b4d2f514602ba210271b632cd113853febe941c6485f'), size_on_disk=281696, blob_last_accessed=1763591392.701175, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7269/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5bae329d2318610f627fc562a5163253c7d61e0ef83c53808b220133b2299264'), size_on_disk=203263, blob_last_accessed=1763591392.541176, blob_last_modified=1758648270.4096642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e20f616f6e340b76f6446d2cdffb6cc9feb81297bd0307733f835acf4142cfd'), size_on_disk=269523, blob_last_accessed=1763591392.3461773, blob_last_modified=1758648267.2696495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6443/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4deb4634e8b4bf141a2410e94b2d99e2b31a0e8f037de9a1c6e14696025bffae'), size_on_disk=494775, blob_last_accessed=1763591392.991173, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6640/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48339729112d22aa5aabf411674bfb90a567ec4c7a07ea7930738645a96823ef'), size_on_disk=329042, blob_last_accessed=1763591392.6391754, blob_last_modified=1758648269.090658), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6573/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e435989051765cf2955d8cd733904e8b60e5f3121e1c2fdc987f4e6f71fe3970'), size_on_disk=265022, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9586573), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/d1cd38451bc41ef14dbc1566c4704b004b2c0661f667210c81dba09bec90540f'), size_on_disk=244487, blob_last_accessed=1763591393.6301687, blob_last_modified=1758648267.3246498), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/74cbce50c258bdddb7e42c8fd5b0b2a9da74fe3cf89a9ab5a53ee4fde7c1dee6'), size_on_disk=186142, blob_last_accessed=1763591392.842174, blob_last_modified=1758648266.9966483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7351/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cc708adf186f28db09dfdd4ac43865a90482b2192306949eab01492598cf23e7'), size_on_disk=198070, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.610665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7185/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/14f67668aca8d8f8aa2b8af98835da683a7fc3f22aa09cdcd7f943a37632391e'), size_on_disk=201650, blob_last_accessed=1763591393.2191715, blob_last_modified=1758648270.1076627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7256/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1af21cbdd7f6f6effff15a1b061a48f85132d582f08c8fd42e04ea58bde7ec00'), size_on_disk=206710, blob_last_accessed=1763591392.586176, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5610/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a86512ae1000a994c86e9aebaf30cf9ce1a59160155f3dc1afa6cbde0a1fc47e'), size_on_disk=307382, blob_last_accessed=1763591393.5291693, blob_last_modified=1758648267.994653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6739/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/58b5ed63f2197f406dd3df6a569975d776722193007912718979eaed4d4e5f9c'), size_on_disk=399160, blob_last_accessed=1763591392.6551754, blob_last_modified=1758648269.331659), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/128015caa722289251a45aa3d56cabac6b5946eb4e1be16eb950555fb24ac413'), size_on_disk=172328, blob_last_accessed=1763591393.735168, blob_last_modified=1758648267.2826495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9f7663ccf72b0681ae5866c13500fbde98f35877fd677ac0d49c17447d17d6ce'), size_on_disk=332767, blob_last_accessed=1763591392.1731787, blob_last_modified=1758648266.744647), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5654/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38961219eb66dec31505ebcc5088f16684be9891b96fffa8c9a13cd3f13254d0'), size_on_disk=447782, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.1876538), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6738/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e6d612d16538a06c985f1134f3fdfd367944b2a85cedfb8b0ddad1679004c940'), size_on_disk=333560, blob_last_accessed=1763591392.8651738, blob_last_modified=1758648269.3676593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6b92129c2fb99028b6cab9509cf1a4c40b142109cde34c9a130d375001dcd211'), size_on_disk=267273, blob_last_accessed=1763591392.7811744, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6454/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c35b60861d2fc7a3f4256869e14e629aed07cd2ccce76608578ceb8c6ec423e1'), size_on_disk=498326, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.7226562), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d50648ed5c9f593f830aff661d13b3d34bd14f0bf93b16169df4765c9f47fc8'), size_on_disk=295924, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7426472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7266/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9aea2d6f5606d24e1999d450379b1f8c8b9d80eded9e784fb729faa516a27026'), size_on_disk=324480, blob_last_accessed=1763591392.0401795, blob_last_modified=1758648270.379664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=MAG002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f7f35257d4c1f7fa5812ea31c1de791e08ab5e3a4e7ca42b2bb12b2c64f7ed7'), size_on_disk=490174, blob_last_accessed=1763591392.829174, blob_last_modified=1758648267.583651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dc74862f02ff12b8c906046f1f03953d33f5516091d58397b0f9a0fc05bb4b5c'), size_on_disk=315435, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.5006506), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7276/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4e0d576cc4a6a85be64f2198e5001a9f5b02b506072df6f695cfdfc719cb041'), size_on_disk=204036, blob_last_accessed=1763591392.987173, blob_last_modified=1758648270.4646642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=composite/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1f8adf8fb4992283714df8587126211d54e6a0580b8be88c1d5e911196aed783'), size_on_disk=4814573, blob_last_accessed=1763591392.0561793, blob_last_modified=1758648268.0256531), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7cb5cca74f1bda2f8f1c8ef962e54d34228361eaa9dd8b21e0d1dd7092ff8164'), size_on_disk=229703, blob_last_accessed=1763591392.555176, blob_last_modified=1758648267.0126483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6657/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/95ad2225152f51e2e31c030ff6708aeebccee5e548035d6f43c9cc3554bdfe5e'), size_on_disk=507730, blob_last_accessed=1763591393.163172, blob_last_modified=1758648269.1676583), CachedFileInfo(file_name='annotated_features_meta.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features_meta.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/2be4b693d333698f7ada7ef40ffaf927c0160d7975ebcf6e215cbc4ba1be1604'), size_on_disk=11813, blob_last_accessed=1763587594.724117, blob_last_modified=1758648264.795638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4b2ff192aa4808cb50b1c1b1667060f98505dd63c1aa58c7d8354e41194957e4'), size_on_disk=289767, blob_last_accessed=1763591393.4871697, blob_last_modified=1758648266.7706473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ddd58117dbedfe7e9dc3fd946187e7bd5d88fe8b0f38999c6e4b5660938bb787'), size_on_disk=359739, blob_last_accessed=1763591393.5981688, blob_last_modified=1758648267.2726495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6983/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4ee29f84ab279c520521a6c5a5eed9e5e5581b2e39378bdd898eec67d13654f'), size_on_disk=410123, blob_last_accessed=1763591392.6751752, blob_last_modified=1758648269.8636615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/646c843611bf88c7f5dcd5b1a664d724c3b24b6434cc52d9a54de62ccaf6c542'), size_on_disk=255949, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.4506505), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fa1a44bd33df22ec6b5a8baf3be17e51a11522b5fdd545a38e6e163cc5ae8207'), size_on_disk=241911, blob_last_accessed=1763591392.572176, blob_last_modified=1758648267.5226507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/71a33d11c3ff714ce0730145c740eec4fe88812c6136b8d10b86fa8d4054991f'), size_on_disk=186500, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7286518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7332/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f7f62ee008fa2b5cc688cbd80c888f1bf9da538caa245b63581520d485338a99'), size_on_disk=309595, blob_last_accessed=1763591393.1021724, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c9b19726dc45896e4f86a5acc52d07cc422f895bfaaaddb332aaceedff36560c'), size_on_disk=178793, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0266485), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0230df4f1d3748337e5315bb1d22a0373fdab95f9bc900041b0e34966d868718'), size_on_disk=311116, blob_last_accessed=1763591392.9241736, blob_last_modified=1758648267.2506495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7240/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4460d188b4cd3b362f33acf0f0b5312a267b37f8fb2dab4b2577c6c3bbb1231'), size_on_disk=307039, blob_last_accessed=1763591392.6101756, blob_last_modified=1758648270.403664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e871cf06f596bfe74896b62aa28d6b1e6f2f0b8408630fb146c7e29cfd88fbf0'), size_on_disk=232721, blob_last_accessed=1763591392.7871745, blob_last_modified=1758648267.991653)}), refs=frozenset(), last_modified=1763588875.9700668), CachedRevisionInfo(commit_hash='27becd0ab489579a50c38ba896dd03680c5fd2da', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da'), size_on_disk=49585603, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ab338795b8bbf334650386a123f6e0df41363cb5dcbb778d485ce58804e96b29'), size_on_disk=375506, blob_last_accessed=1763591392.9711733, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6965/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9ae4546d58672f0632757c94b595af8b7c40da215611783fdc1a65000075ec98'), size_on_disk=509677, blob_last_accessed=1763591392.2731779, blob_last_modified=1758648269.933662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e871cf06f596bfe74896b62aa28d6b1e6f2f0b8408630fb146c7e29cfd88fbf0'), size_on_disk=232721, blob_last_accessed=1763591392.7871745, blob_last_modified=1758648267.991653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7331/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f159a9811b8ac1b5e252b52427979a3bddce2e7131b13f84046b31e47f7fa399'), size_on_disk=242821, blob_last_accessed=1763591393.2501714, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6532/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3041f19cf0dcb3e0bd9b1178994351a22f3297ccd43c5e291df8bfe0ad8d5d02'), size_on_disk=516437, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.8236568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6177/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b91ffef1dfcdf7952df46956d76a4b364aa0d8342453636cf09e6a343607184f'), size_on_disk=387618, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.424655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6390/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f324cdf73148c8b0fa23456bc4f51cff3032d08ed42e44dd9fcc4815a6d969c'), size_on_disk=560601, blob_last_accessed=1763591392.7391748, blob_last_modified=1758648268.6206558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6954/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/aeed3c55d81a4eb0c568486e476211c599ce2bf37f9dff22c7dfca23f5d80495'), size_on_disk=476054, blob_last_accessed=1763591393.011173, blob_last_modified=1758648269.943662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ef036874e53ee98dfe1b5d84c42749fc40da9279cd573174321ac83201c1651'), size_on_disk=349762, blob_last_accessed=1763591393.0791724, blob_last_modified=1758648267.4966507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7258/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7b1860ba3be7dbf053203fa5690da2002f7841186940231acea6064e70f0bdc8'), size_on_disk=171331, blob_last_accessed=1763591392.6171756, blob_last_modified=1758648270.3746638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS011/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ea7cb0559b342dd0ec278e7d5846a5ba0c0d867ea04d545f72e0d1c9472a0cc'), size_on_disk=363979, blob_last_accessed=1763591392.9791732, blob_last_modified=1758648267.0116484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6513/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c8f9c31704c2f8732b4668e1e6410034b8adf2ada39ff55935aa0199e920d055'), size_on_disk=507367, blob_last_accessed=1763591392.1451788, blob_last_modified=1758648268.8406568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6506/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/986d9d3b4ed064d66ebaa0f2502b933f1650a20ac4bbbba804e2cde03b9454c0'), size_on_disk=378407, blob_last_accessed=1763591391.97318, blob_last_modified=1758648268.7656565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6573/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e435989051765cf2955d8cd733904e8b60e5f3121e1c2fdc987f4e6f71fe3970'), size_on_disk=265022, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9586573), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6454/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c35b60861d2fc7a3f4256869e14e629aed07cd2ccce76608578ceb8c6ec423e1'), size_on_disk=498326, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.7226562), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6443/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4deb4634e8b4bf141a2410e94b2d99e2b31a0e8f037de9a1c6e14696025bffae'), size_on_disk=494775, blob_last_accessed=1763591392.991173, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7240/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4460d188b4cd3b362f33acf0f0b5312a267b37f8fb2dab4b2577c6c3bbb1231'), size_on_disk=307039, blob_last_accessed=1763591392.6101756, blob_last_modified=1758648270.403664), CachedFileInfo(file_name='annotated_features_meta.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features_meta.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/2be4b693d333698f7ada7ef40ffaf927c0160d7975ebcf6e215cbc4ba1be1604'), size_on_disk=11813, blob_last_accessed=1763587594.724117, blob_last_modified=1758648264.795638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6421/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1e3bacd3bcf1fcb46cfc5f9ab66c2af57e190f3d006031e7ef37864fbd36aaca'), size_on_disk=495452, blob_last_accessed=1763591392.2161784, blob_last_modified=1758648268.6366558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7118/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/be8bd409b83526d5671501de6ef2980177955fdcc3a13d5e153d2593493b2a1e'), size_on_disk=346308, blob_last_accessed=1763591392.9281735, blob_last_modified=1758648270.1336627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4d098c33564f99ec6aed77d585d1f6e871806f441f099790eef4d6478e322af'), size_on_disk=264459, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7486472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6055/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/31bb70b4c90654ecb276664f5c914679322b24985ec66d5b396a99f253ebfa17'), size_on_disk=377015, blob_last_accessed=1763591393.5701692, blob_last_modified=1758648268.3076544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5690/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4831e3e037d421a14efbe1da8c369015ec7d01df351bb27f0ef0400c8b94c693'), size_on_disk=311754, blob_last_accessed=1763591392.9021738, blob_last_modified=1758648268.211654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6731/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/23ecd71c4e3c1c547295bb756d4654eb413ff288d10118ab286cd21ccd95b666'), size_on_disk=346187, blob_last_accessed=1763591392.1871786, blob_last_modified=1758648269.1906583), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7211/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73d7747698fa1168c89f12c105bbe900d5969d27a82ae2302d0720d761fbfdab'), size_on_disk=298482, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.1276627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6708/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/35520976736159652e3be8066e77b1371522f63a8b3564d9b0d9f5b1598a144d'), size_on_disk=253955, blob_last_accessed=1763591393.6881683, blob_last_modified=1758648269.1996584), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6b92129c2fb99028b6cab9509cf1a4c40b142109cde34c9a130d375001dcd211'), size_on_disk=267273, blob_last_accessed=1763591392.7811744, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6551/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c3729692991cdcafe9baa3d2805b3866f72fe9bc89ba22c6d2cdae4d5b5bbf5f'), size_on_disk=545477, blob_last_accessed=1763591393.0611725, blob_last_modified=1758648268.8376567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5617/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bc01241e8363373c995a0fa7439aa1e432a60eb8a7805124fff302311f638361'), size_on_disk=351950, blob_last_accessed=1763591393.6541686, blob_last_modified=1758648268.1026535), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/33d7303ed077f489ee28021daca0bbc859feb1cfd383dfc9a856ea13994801a9'), size_on_disk=331877, blob_last_accessed=1763591393.292171, blob_last_modified=1758648268.2226539), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6657/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/95ad2225152f51e2e31c030ff6708aeebccee5e548035d6f43c9cc3554bdfe5e'), size_on_disk=507730, blob_last_accessed=1763591393.163172, blob_last_modified=1758648269.1676583), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dc74862f02ff12b8c906046f1f03953d33f5516091d58397b0f9a0fc05bb4b5c'), size_on_disk=315435, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.5006506), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/00e5602b87ac84670a98c081e6520ca744038f4e9b3a72a78cb61f5926be56b1'), size_on_disk=251087, blob_last_accessed=1763591392.5781758, blob_last_modified=1758648267.8286521), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e91fd764a83947c14f35d7a2d8c0d156c0a61a32a1da96273718ef4be720787'), size_on_disk=361855, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.7196517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7063/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9427d8f8c54e3c0cc9d720958ef15d3859f00cdfb678559e0268387a36a8ef37'), size_on_disk=404051, blob_last_accessed=1763591392.5301762, blob_last_modified=1758648269.8976617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/11485fd7bb94d0093ce2508d541fea3ce64f934197d8a5f7bb26ecbdb59f8dbb'), size_on_disk=124642, blob_last_accessed=1763591392.3321776, blob_last_modified=1758648267.0666487), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c9b19726dc45896e4f86a5acc52d07cc422f895bfaaaddb332aaceedff36560c'), size_on_disk=178793, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0266485), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9f7663ccf72b0681ae5866c13500fbde98f35877fd677ac0d49c17447d17d6ce'), size_on_disk=332767, blob_last_accessed=1763591392.1731787, blob_last_modified=1758648266.744647), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6923/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/454ca6a52d4b305eddc4b6967ef9604be4861269fdcfff419883daed9b2fe48b'), size_on_disk=337423, blob_last_accessed=1763591393.5031695, blob_last_modified=1758648269.6476605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02313d4d85859a7353e56d89b6e14bcec5160849b5c0089ccbecaa1cda5012cf'), size_on_disk=301213, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4356549), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6527/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/10081f44b1c2f5ad32489da51fc9ac5b5289b54688a7e78725db247a644b7e4d'), size_on_disk=494640, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.882657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7269/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5bae329d2318610f627fc562a5163253c7d61e0ef83c53808b220133b2299264'), size_on_disk=203263, blob_last_accessed=1763591392.541176, blob_last_modified=1758648270.4096642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1c8a8cb23b1ec073827d6f5343a3bf03a9d7a04cf2ae3fd3ea4cc5ac37673b38'), size_on_disk=342987, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.6226559), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6738/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e6d612d16538a06c985f1134f3fdfd367944b2a85cedfb8b0ddad1679004c940'), size_on_disk=333560, blob_last_accessed=1763591392.8651738, blob_last_modified=1758648269.3676593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7370/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ff4dce26afb34b291220c17625be4a28078f9721a8b000ea177c4add3bf1a039'), size_on_disk=241010, blob_last_accessed=1763591393.7701678, blob_last_modified=1758648270.6726654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d3e36418bd46acf9537a8f864b1a1361a062d72fec2b093789b88ed655ec117'), size_on_disk=377393, blob_last_accessed=1763591393.0451727, blob_last_modified=1758648267.0136483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dcb33135a9d854d17a6b80348f6bee8becea9ca3808583a034f89c5e4f460c54'), size_on_disk=218643, blob_last_accessed=1763591393.4831698, blob_last_modified=1758648267.9636528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ddd58117dbedfe7e9dc3fd946187e7bd5d88fe8b0f38999c6e4b5660938bb787'), size_on_disk=359739, blob_last_accessed=1763591393.5981688, blob_last_modified=1758648267.2726495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6448/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/182dc134914a5ddb02992ab6a4f1c12d2450aa331820bf7bd95693dc999bcfae'), size_on_disk=390210, blob_last_accessed=1763591392.9591732, blob_last_modified=1758648268.672656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6739/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/58b5ed63f2197f406dd3df6a569975d776722193007912718979eaed4d4e5f9c'), size_on_disk=399160, blob_last_accessed=1763591392.6551754, blob_last_modified=1758648269.331659), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=KB001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38c939d54d3ed77a09650d2ccf00a6ed0e20390b8be722f1c3ed7881a5904a60'), size_on_disk=351677, blob_last_accessed=1763591392.9151735, blob_last_modified=1758648267.5566509), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5801/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/12e91a415cc2a149af4c34c3c8b032821ff301e2828dfb9f4636bd6b2913ea3d'), size_on_disk=360485, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.1636536), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR006B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fcc5e886ec7c6363a5ab51b03d3da189ac3e0cde0a0f79d4f0c46043a6b61838'), size_on_disk=253857, blob_last_accessed=1763591392.0501795, blob_last_modified=1758648267.785652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6677/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4ee6e1d3e2b9a9bbcc8851048d2b16f4aea6a1e2f752afb9e0bf75a56adf7042'), size_on_disk=322147, blob_last_accessed=1763591392.393177, blob_last_modified=1758648269.0826578), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6140/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/66c2da17957d7e6a8b159d3573879ac779d0094e35150b51e2643f7ddee36e1f'), size_on_disk=332958, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4236548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7237/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/110a743162ace7792a44dfe48d7afd40bb67c45c722038c981548477ebc5d8fe'), size_on_disk=313207, blob_last_accessed=1763591393.143172, blob_last_modified=1758648270.1426628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6798/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/899cde55032cd593be0013e1f6ff33fe9a12f4616477b1410d86fae1b2008a09'), size_on_disk=342376, blob_last_accessed=1763591392.7951744, blob_last_modified=1758648269.4006593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6861/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6955b1a1746fe41162ea1d7a098482bbfdb3d0e411c809b52d698e285627ce03'), size_on_disk=385103, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6146603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6920/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/45122fcfb3abc3a43631023712c224dc6e00dba566fb2e15deb4ab4501ad2538'), size_on_disk=446998, blob_last_accessed=1763591392.8881738, blob_last_modified=1758648269.6416605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/74cbce50c258bdddb7e42c8fd5b0b2a9da74fe3cf89a9ab5a53ee4fde7c1dee6'), size_on_disk=186142, blob_last_accessed=1763591392.842174, blob_last_modified=1758648266.9966483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/128015caa722289251a45aa3d56cabac6b5946eb4e1be16eb950555fb24ac413'), size_on_disk=172328, blob_last_accessed=1763591393.735168, blob_last_modified=1758648267.2826495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0230df4f1d3748337e5315bb1d22a0373fdab95f9bc900041b0e34966d868718'), size_on_disk=311116, blob_last_accessed=1763591392.9241736, blob_last_modified=1758648267.2506495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6635/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6c5b7f1fda3a7f5679870f438d13a45558f7f8da0f479c5144d7751bc8f7fbbc'), size_on_disk=420658, blob_last_accessed=1763591391.95018, blob_last_modified=1758648269.3956594), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6073/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d68ca6411ba47eb63adad81e88fa1341a0ee269897268957ae936dcc9ba7788'), size_on_disk=450031, blob_last_accessed=1763591393.1341722, blob_last_modified=1758648268.3886547), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7332/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f7f62ee008fa2b5cc688cbd80c888f1bf9da538caa245b63581520d485338a99'), size_on_disk=309595, blob_last_accessed=1763591393.1021724, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS006b/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7eedae811a5b3dd4389e7e814d35b022bfaf01fc4d2cfb47be6e6786d6d58c64'), size_on_disk=315912, blob_last_accessed=1763591393.2571712, blob_last_modified=1758648266.7546473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6651/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7644af4cc956ea08809ab6c80c65095e3fdbd36e056040c0eaded3773d546359'), size_on_disk=559951, blob_last_accessed=1763591393.6361687, blob_last_modified=1758648269.1856585), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4e8332a5db6260f6357f940e62e3e7b622785f386e81cdb9c75b538d318f4a6'), size_on_disk=158978, blob_last_accessed=1763591393.5471692, blob_last_modified=1758648266.7626472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6764/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec87eef9e2e8cdf3c3a10ead28dcf1bfa76e94e12a42422a46c8559fb18207c2'), size_on_disk=75684, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.4036593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7295/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/19bd07f913e0af3460e6b4b5091971ceed8f4aa510cf18b82ec36dabb67c68b8'), size_on_disk=272619, blob_last_accessed=1763591393.1151721, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6872/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02fabe6e32f52fe6fca923beb0a6cc2898d991304e23a3e75f3ae8a6bcf361c6'), size_on_disk=274362, blob_last_accessed=1763591393.6231687, blob_last_modified=1758648269.6966608), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=MAG001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/30b63d014686a103b5e7fc6571e902fa9c08630a96557cc12ec9dfcf60871306'), size_on_disk=347333, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.559651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d50648ed5c9f593f830aff661d13b3d34bd14f0bf93b16169df4765c9f47fc8'), size_on_disk=295924, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7426472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6863/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec534c91cae4413d30477014131d39b51c9c236dfbf150874bd21ec3de821613'), size_on_disk=389025, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648269.6376605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR007B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6f0620b102e7cbfadced7d5530545a185036b53118d3e2b7f074c56403d80f4b'), size_on_disk=263185, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7616518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bd2017e75947ead2c06f9d1936347f7fdcfac7fbff620e0b39077208a02c3bd9'), size_on_disk=303094, blob_last_accessed=1763591393.1271722, blob_last_modified=1758648269.9296618), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/530b5650d91dae3bf169f967af8e800611e3e01daeb1811d6fd21c2612ac4ad9'), size_on_disk=315170, blob_last_accessed=1763591393.6121688, blob_last_modified=1758648266.7956474), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bbe701ffd546a2f4359f821262f7b44232bdc328d9d4719bfdaa8f2aedc6d2e9'), size_on_disk=346079, blob_last_accessed=1763591393.0861723, blob_last_modified=1758648267.589651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6640/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48339729112d22aa5aabf411674bfb90a567ec4c7a07ea7930738645a96823ef'), size_on_disk=329042, blob_last_accessed=1763591392.6391754, blob_last_modified=1758648269.090658), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5654/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38961219eb66dec31505ebcc5088f16684be9891b96fffa8c9a13cd3f13254d0'), size_on_disk=447782, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.1876538), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=composite/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1f8adf8fb4992283714df8587126211d54e6a0580b8be88c1d5e911196aed783'), size_on_disk=4814573, blob_last_accessed=1763591392.0561793, blob_last_modified=1758648268.0256531), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/71a33d11c3ff714ce0730145c740eec4fe88812c6136b8d10b86fa8d4054991f'), size_on_disk=186500, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7286518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7243/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cb59e645fff2683db692b4d2f514602ba210271b632cd113853febe941c6485f'), size_on_disk=281696, blob_last_accessed=1763591392.701175, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7185/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/14f67668aca8d8f8aa2b8af98835da683a7fc3f22aa09cdcd7f943a37632391e'), size_on_disk=201650, blob_last_accessed=1763591393.2191715, blob_last_modified=1758648270.1076627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7276/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4e0d576cc4a6a85be64f2198e5001a9f5b02b506072df6f695cfdfc719cb041'), size_on_disk=204036, blob_last_accessed=1763591392.987173, blob_last_modified=1758648270.4646642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7151/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9969e4607d6bf42424f5d2ce8f0d16e3d2d89d069cae6d75997e99c88a68dc77'), size_on_disk=275572, blob_last_accessed=1763591393.7231681, blob_last_modified=1758648270.1476629), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7266/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9aea2d6f5606d24e1999d450379b1f8c8b9d80eded9e784fb729faa516a27026'), size_on_disk=324480, blob_last_accessed=1763591392.0401795, blob_last_modified=1758648270.379664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5399/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b65d0aa7ddc98940f91f2ae5f07e1d3f81dda688792552b91fcc39fb4f9ededa'), size_on_disk=256729, blob_last_accessed=1763591392.5471761, blob_last_modified=1758648267.972653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6354/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/80b129a46defc7b06e472a35935bfaf410bc5e456475b2885feb2c8a4934a81b'), size_on_disk=173225, blob_last_accessed=1763591393.4981697, blob_last_modified=1758648268.4986553), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/d1cd38451bc41ef14dbc1566c4704b004b2c0661f667210c81dba09bec90540f'), size_on_disk=244487, blob_last_accessed=1763591393.6301687, blob_last_modified=1758648267.3246498), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5961/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/49cac5c2df1adf790a9ac0209db6c0945de50b8191d403e8b1ad386ee2dcb35e'), size_on_disk=321368, blob_last_accessed=1763591392.560176, blob_last_modified=1758648268.215654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5986/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f1179c75ac6e91de03b67a73882f3b9c07c8e435d938d3059de489403adde7a'), size_on_disk=328749, blob_last_accessed=1763591392.691175, blob_last_modified=1758648268.228654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7297/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4bd4088cbfcccdf96def916bb8fd9d673327d3e57c2a4a3e57299743ce4aca7'), size_on_disk=261344, blob_last_accessed=1763591393.588169, blob_last_modified=1758648270.6516652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/62e9a627f20607bc4887ebc9277131f974ec80ff39ac386775f4b8f2f5cb9d1d'), size_on_disk=346782, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7476518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7256/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1af21cbdd7f6f6effff15a1b061a48f85132d582f08c8fd42e04ea58bde7ec00'), size_on_disk=206710, blob_last_accessed=1763591392.586176, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7115/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/205443fd87ce5c793dab86eb75c21dab7dc2ae2edac41d7e6314cfe491bdb01d'), size_on_disk=321850, blob_last_accessed=1763591393.2331715, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7cb5cca74f1bda2f8f1c8ef962e54d34228361eaa9dd8b21e0d1dd7092ff8164'), size_on_disk=229703, blob_last_accessed=1763591392.555176, blob_last_modified=1758648267.0126483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5610/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a86512ae1000a994c86e9aebaf30cf9ce1a59160155f3dc1afa6cbde0a1fc47e'), size_on_disk=307382, blob_last_accessed=1763591393.5291693, blob_last_modified=1758648267.994653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1fa09a5a6e6e69198501d994050adfcad7ff39a3cc2fe94e7ea177414e1bc968'), size_on_disk=303532, blob_last_accessed=1763591393.7091682, blob_last_modified=1758648267.2776496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5614/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1aa702ff57d34ebed961043e0efae064bfdb62f589cbb515311ae8ea37ba8c76'), size_on_disk=429423, blob_last_accessed=1763591393.4671698, blob_last_modified=1758648268.0406532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=MAG002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f7f35257d4c1f7fa5812ea31c1de791e08ab5e3a4e7ca42b2bb12b2c64f7ed7'), size_on_disk=490174, blob_last_accessed=1763591392.829174, blob_last_modified=1758648267.583651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fa1a44bd33df22ec6b5a8baf3be17e51a11522b5fdd545a38e6e163cc5ae8207'), size_on_disk=241911, blob_last_accessed=1763591392.572176, blob_last_modified=1758648267.5226507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7137/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/703fe9f8552adcbdfb5e640509cb62df2fa734b43fe1e98fc1fd342415f823d7'), size_on_disk=515398, blob_last_accessed=1763589053.7698598, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6924/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5e9b3a94d73c1720e6b05ea55000054905a8672edea7f950736ce3fe191f8faf'), size_on_disk=412939, blob_last_accessed=1763591393.748168, blob_last_modified=1758648269.7066607), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6061/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73e03845d0fbff1a5931e4f792e2c1965ade02fc622ffc08e05c267ac6cd3231'), size_on_disk=394930, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.447655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6689/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/90ea249116dc6a9be86974e1ec0acf79558dfd56a5666be2e05d2107b8c4ee60'), size_on_disk=366787, blob_last_accessed=1763591393.6661685, blob_last_modified=1758648269.1406581), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6021/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eea0d101dd58c9619b41a94009fc0dff8156d2d36d7f2b88bb653e96123a032e'), size_on_disk=439545, blob_last_accessed=1763591393.5531693, blob_last_modified=1758648268.258654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6545/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/260c4a280f52024cb12d44559a6189641ee02efbaf20168435196efbfc69d741'), size_on_disk=404812, blob_last_accessed=1763591392.3761773, blob_last_modified=1758648268.8256567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e5b3174998823af40d055f46ca06e7bb44601cbe97b133062cd3e0491dd30ac2'), size_on_disk=323308, blob_last_accessed=1763591393.153172, blob_last_modified=1758648267.2686496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6567/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0e9713a94594dfcbc80134af52449e12ef207ca9149f5a96c43b6cb457b2431c'), size_on_disk=478809, blob_last_accessed=1763591392.6241755, blob_last_modified=1758648268.893657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ac8c590c19e7b95b9aac571b1394976039f25fdd66e62a68ecb99925433f8e54'), size_on_disk=355706, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0226483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f427ea0f4ded3b6ff841153d8ad87d72a26a964c189246fb6fd5c439bd8b0c1e'), size_on_disk=353623, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.812652), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a88564c359f73b5ac3f70a4635f33a1ec57f527f'), size_on_disk=6139, blob_last_accessed=1763587733.4135532, blob_last_modified=1758648264.1986353), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6853/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ed47ecb2aa870fdbf12920fd4486933262c0a24318c9c8c5531e359a3c416c90'), size_on_disk=515114, blob_last_accessed=1763591393.2661712, blob_last_modified=1758648269.4276595), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d4c0cbbf6075a8b5732e3cef2ba154d95d21aa0efc7e49d947cceebd8742b30'), size_on_disk=269007, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4106548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6437/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/556084dbbc174db5450872d6f5e66cc72091380ea32006977dd176e23c105a7f'), size_on_disk=389840, blob_last_accessed=1763591392.714175, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7155/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/521fc1bea6963640645c21cbdb8d1de07793f11213c23ac71d249f3252b32f54'), size_on_disk=304554, blob_last_accessed=1763591393.5841691, blob_last_modified=1758648270.1366627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4b2ff192aa4808cb50b1c1b1667060f98505dd63c1aa58c7d8354e41194957e4'), size_on_disk=289767, blob_last_accessed=1763591393.4871697, blob_last_modified=1758648266.7706473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fc9311b65abb851fbbfd8bb0c0765af59069659f5a715aee546a8644b0af4ec2'), size_on_disk=210948, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648267.6766515), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a76f0849e518faedfe50e6f0c76908a3f6beed34fd0b607603db1859493595d0'), size_on_disk=292312, blob_last_accessed=1763587658.6168654, blob_last_modified=1758648266.7676473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7351/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cc708adf186f28db09dfdd4ac43865a90482b2192306949eab01492598cf23e7'), size_on_disk=198070, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.610665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6770/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/082f14f7b0d5fc47e088efc40ae85a8ec41f99437c66da96ca69af210bce61cc'), size_on_disk=318118, blob_last_accessed=1763591392.8081744, blob_last_modified=1758648269.3776593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6983/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4ee29f84ab279c520521a6c5a5eed9e5e5581b2e39378bdd898eec67d13654f'), size_on_disk=410123, blob_last_accessed=1763591392.6751752, blob_last_modified=1758648269.8636615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6959/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c0cb6c2ec2bda19b0c1da5d5fc2229d0b6d6987d75646fa230c0c14d591cf1f1'), size_on_disk=537588, blob_last_accessed=1763591392.851174, blob_last_modified=1758648269.8686616), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6808/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c71f5371e8c803cfc7fd507d0b3c11f7548db5d7a0fa42c68977169369ea036e'), size_on_disk=422880, blob_last_accessed=1763591392.9491735, blob_last_modified=1758648269.3916593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/646c843611bf88c7f5dcd5b1a664d724c3b24b6434cc52d9a54de62ccaf6c542'), size_on_disk=255949, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.4506505), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5301_5088/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/65ef775934339b80eda59a141cf791a74e1c6f12513d8143307cebe2f11c3738'), size_on_disk=410106, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.9516528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e20f616f6e340b76f6446d2cdffb6cc9feb81297bd0307733f835acf4142cfd'), size_on_disk=269523, blob_last_accessed=1763591392.3461773, blob_last_modified=1758648267.2696495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6572/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3488eaac5e4b9b3418b909138e6b50e3f9ebaf3e7345a4a10b6ced18b66152e4'), size_on_disk=440579, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9506574), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eb61968b5ec5eadd9087e42ead29333f91195dd5b3f9c5a616b41e94ca23c494'), size_on_disk=330245, blob_last_accessed=1763591393.6761684, blob_last_modified=1758648269.710661), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6374/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e410602f56bc94d9831a2bc8bdb865ee4ebe9862a65b603165b655fbcc4b9914'), size_on_disk=441531, blob_last_accessed=1763591393.1891718, blob_last_modified=1758648268.5356555), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6855/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48e48378d24404e926bc1c44b10af60d4b12f82124ff2f80bc0b69b4a0f98740'), size_on_disk=609248, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6556606), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7367/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a7778bb24506047963575fa96126157895c7f329996c729c3e7d6070595955c3'), size_on_disk=324361, blob_last_accessed=1763591392.1631787, blob_last_modified=1758648270.628665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7283/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0dc91bfacca82c5ed23de7757d59f9f808d541d96dd79aab56f0c4252e7aabf3'), size_on_disk=265146, blob_last_accessed=1763591392.7731745, blob_last_modified=1758648270.401664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5a70fa6f22aeda3246d17646cc4ec7c5a46e1993f9981b19d9fd5a8cc99bf11f'), size_on_disk=308858, blob_last_accessed=1763591391.9411802, blob_last_modified=1758648267.0186484)}), refs=frozenset(), last_modified=1758648270.6726654), CachedRevisionInfo(commit_hash='6864b2582ce35f92a8dde59851786319c2c494bc', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc'), size_on_disk=49590351, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6443/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4deb4634e8b4bf141a2410e94b2d99e2b31a0e8f037de9a1c6e14696025bffae'), size_on_disk=494775, blob_last_accessed=1763591392.991173, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6863/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec534c91cae4413d30477014131d39b51c9c236dfbf150874bd21ec3de821613'), size_on_disk=389025, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648269.6376605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7295/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/19bd07f913e0af3460e6b4b5091971ceed8f4aa510cf18b82ec36dabb67c68b8'), size_on_disk=272619, blob_last_accessed=1763591393.1151721, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6513/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c8f9c31704c2f8732b4668e1e6410034b8adf2ada39ff55935aa0199e920d055'), size_on_disk=507367, blob_last_accessed=1763591392.1451788, blob_last_modified=1758648268.8406568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7269/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5bae329d2318610f627fc562a5163253c7d61e0ef83c53808b220133b2299264'), size_on_disk=203263, blob_last_accessed=1763591392.541176, blob_last_modified=1758648270.4096642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6965/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9ae4546d58672f0632757c94b595af8b7c40da215611783fdc1a65000075ec98'), size_on_disk=509677, blob_last_accessed=1763591392.2731779, blob_last_modified=1758648269.933662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/71a33d11c3ff714ce0730145c740eec4fe88812c6136b8d10b86fa8d4054991f'), size_on_disk=186500, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7286518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6640/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48339729112d22aa5aabf411674bfb90a567ec4c7a07ea7930738645a96823ef'), size_on_disk=329042, blob_last_accessed=1763591392.6391754, blob_last_modified=1758648269.090658), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7211/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73d7747698fa1168c89f12c105bbe900d5969d27a82ae2302d0720d761fbfdab'), size_on_disk=298482, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.1276627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7237/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/110a743162ace7792a44dfe48d7afd40bb67c45c722038c981548477ebc5d8fe'), size_on_disk=313207, blob_last_accessed=1763591393.143172, blob_last_modified=1758648270.1426628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1c8a8cb23b1ec073827d6f5343a3bf03a9d7a04cf2ae3fd3ea4cc5ac37673b38'), size_on_disk=342987, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.6226559), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9f7663ccf72b0681ae5866c13500fbde98f35877fd677ac0d49c17447d17d6ce'), size_on_disk=332767, blob_last_accessed=1763591392.1731787, blob_last_modified=1758648266.744647), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6545/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/260c4a280f52024cb12d44559a6189641ee02efbaf20168435196efbfc69d741'), size_on_disk=404812, blob_last_accessed=1763591392.3761773, blob_last_modified=1758648268.8256567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6437/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/556084dbbc174db5450872d6f5e66cc72091380ea32006977dd176e23c105a7f'), size_on_disk=389840, blob_last_accessed=1763591392.714175, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6923/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/454ca6a52d4b305eddc4b6967ef9604be4861269fdcfff419883daed9b2fe48b'), size_on_disk=337423, blob_last_accessed=1763591393.5031695, blob_last_modified=1758648269.6476605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5986/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f1179c75ac6e91de03b67a73882f3b9c07c8e435d938d3059de489403adde7a'), size_on_disk=328749, blob_last_accessed=1763591392.691175, blob_last_modified=1758648268.228654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6506/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/986d9d3b4ed064d66ebaa0f2502b933f1650a20ac4bbbba804e2cde03b9454c0'), size_on_disk=378407, blob_last_accessed=1763591391.97318, blob_last_modified=1758648268.7656565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6983/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4ee29f84ab279c520521a6c5a5eed9e5e5581b2e39378bdd898eec67d13654f'), size_on_disk=410123, blob_last_accessed=1763591392.6751752, blob_last_modified=1758648269.8636615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6532/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3041f19cf0dcb3e0bd9b1178994351a22f3297ccd43c5e291df8bfe0ad8d5d02'), size_on_disk=516437, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.8236568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6738/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e6d612d16538a06c985f1134f3fdfd367944b2a85cedfb8b0ddad1679004c940'), size_on_disk=333560, blob_last_accessed=1763591392.8651738, blob_last_modified=1758648269.3676593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS006b/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7eedae811a5b3dd4389e7e814d35b022bfaf01fc4d2cfb47be6e6786d6d58c64'), size_on_disk=315912, blob_last_accessed=1763591393.2571712, blob_last_modified=1758648266.7546473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/128015caa722289251a45aa3d56cabac6b5946eb4e1be16eb950555fb24ac413'), size_on_disk=172328, blob_last_accessed=1763591393.735168, blob_last_modified=1758648267.2826495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0230df4f1d3748337e5315bb1d22a0373fdab95f9bc900041b0e34966d868718'), size_on_disk=311116, blob_last_accessed=1763591392.9241736, blob_last_modified=1758648267.2506495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5a70fa6f22aeda3246d17646cc4ec7c5a46e1993f9981b19d9fd5a8cc99bf11f'), size_on_disk=308858, blob_last_accessed=1763591391.9411802, blob_last_modified=1758648267.0186484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7240/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4460d188b4cd3b362f33acf0f0b5312a267b37f8fb2dab4b2577c6c3bbb1231'), size_on_disk=307039, blob_last_accessed=1763591392.6101756, blob_last_modified=1758648270.403664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6374/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e410602f56bc94d9831a2bc8bdb865ee4ebe9862a65b603165b655fbcc4b9914'), size_on_disk=441531, blob_last_accessed=1763591393.1891718, blob_last_modified=1758648268.5356555), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6770/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/082f14f7b0d5fc47e088efc40ae85a8ec41f99437c66da96ca69af210bce61cc'), size_on_disk=318118, blob_last_accessed=1763591392.8081744, blob_last_modified=1758648269.3776593), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fc266bb938fa146c46d1a7bd5446d5d27b3bf202'), size_on_disk=10887, blob_last_accessed=1763649507.5438576, blob_last_modified=1763649507.5418575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/00e5602b87ac84670a98c081e6520ca744038f4e9b3a72a78cb61f5926be56b1'), size_on_disk=251087, blob_last_accessed=1763591392.5781758, blob_last_modified=1758648267.8286521), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6021/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eea0d101dd58c9619b41a94009fc0dff8156d2d36d7f2b88bb653e96123a032e'), size_on_disk=439545, blob_last_accessed=1763591393.5531693, blob_last_modified=1758648268.258654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6551/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c3729692991cdcafe9baa3d2805b3866f72fe9bc89ba22c6d2cdae4d5b5bbf5f'), size_on_disk=545477, blob_last_accessed=1763591393.0611725, blob_last_modified=1758648268.8376567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5614/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1aa702ff57d34ebed961043e0efae064bfdb62f589cbb515311ae8ea37ba8c76'), size_on_disk=429423, blob_last_accessed=1763591393.4671698, blob_last_modified=1758648268.0406532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02313d4d85859a7353e56d89b6e14bcec5160849b5c0089ccbecaa1cda5012cf'), size_on_disk=301213, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4356549), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6572/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3488eaac5e4b9b3418b909138e6b50e3f9ebaf3e7345a4a10b6ced18b66152e4'), size_on_disk=440579, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9506574), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ddd58117dbedfe7e9dc3fd946187e7bd5d88fe8b0f38999c6e4b5660938bb787'), size_on_disk=359739, blob_last_accessed=1763591393.5981688, blob_last_modified=1758648267.2726495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6073/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d68ca6411ba47eb63adad81e88fa1341a0ee269897268957ae936dcc9ba7788'), size_on_disk=450031, blob_last_accessed=1763591393.1341722, blob_last_modified=1758648268.3886547), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6421/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1e3bacd3bcf1fcb46cfc5f9ab66c2af57e190f3d006031e7ef37864fbd36aaca'), size_on_disk=495452, blob_last_accessed=1763591392.2161784, blob_last_modified=1758648268.6366558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/530b5650d91dae3bf169f967af8e800611e3e01daeb1811d6fd21c2612ac4ad9'), size_on_disk=315170, blob_last_accessed=1763591393.6121688, blob_last_modified=1758648266.7956474), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bd2017e75947ead2c06f9d1936347f7fdcfac7fbff620e0b39077208a02c3bd9'), size_on_disk=303094, blob_last_accessed=1763591393.1271722, blob_last_modified=1758648269.9296618), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c9b19726dc45896e4f86a5acc52d07cc422f895bfaaaddb332aaceedff36560c'), size_on_disk=178793, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0266485), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7063/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9427d8f8c54e3c0cc9d720958ef15d3859f00cdfb678559e0268387a36a8ef37'), size_on_disk=404051, blob_last_accessed=1763591392.5301762, blob_last_modified=1758648269.8976617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6635/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6c5b7f1fda3a7f5679870f438d13a45558f7f8da0f479c5144d7751bc8f7fbbc'), size_on_disk=420658, blob_last_accessed=1763591391.95018, blob_last_modified=1758648269.3956594), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ef036874e53ee98dfe1b5d84c42749fc40da9279cd573174321ac83201c1651'), size_on_disk=349762, blob_last_accessed=1763591393.0791724, blob_last_modified=1758648267.4966507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4e8332a5db6260f6357f940e62e3e7b622785f386e81cdb9c75b538d318f4a6'), size_on_disk=158978, blob_last_accessed=1763591393.5471692, blob_last_modified=1758648266.7626472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6177/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b91ffef1dfcdf7952df46956d76a4b364aa0d8342453636cf09e6a343607184f'), size_on_disk=387618, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.424655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eb61968b5ec5eadd9087e42ead29333f91195dd5b3f9c5a616b41e94ca23c494'), size_on_disk=330245, blob_last_accessed=1763591393.6761684, blob_last_modified=1758648269.710661), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6689/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/90ea249116dc6a9be86974e1ec0acf79558dfd56a5666be2e05d2107b8c4ee60'), size_on_disk=366787, blob_last_accessed=1763591393.6661685, blob_last_modified=1758648269.1406581), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7185/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/14f67668aca8d8f8aa2b8af98835da683a7fc3f22aa09cdcd7f943a37632391e'), size_on_disk=201650, blob_last_accessed=1763591393.2191715, blob_last_modified=1758648270.1076627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7370/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ff4dce26afb34b291220c17625be4a28078f9721a8b000ea177c4add3bf1a039'), size_on_disk=241010, blob_last_accessed=1763591393.7701678, blob_last_modified=1758648270.6726654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5399/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b65d0aa7ddc98940f91f2ae5f07e1d3f81dda688792552b91fcc39fb4f9ededa'), size_on_disk=256729, blob_last_accessed=1763591392.5471761, blob_last_modified=1758648267.972653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6567/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0e9713a94594dfcbc80134af52449e12ef207ca9149f5a96c43b6cb457b2431c'), size_on_disk=478809, blob_last_accessed=1763591392.6241755, blob_last_modified=1758648268.893657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6390/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f324cdf73148c8b0fa23456bc4f51cff3032d08ed42e44dd9fcc4815a6d969c'), size_on_disk=560601, blob_last_accessed=1763591392.7391748, blob_last_modified=1758648268.6206558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6808/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c71f5371e8c803cfc7fd507d0b3c11f7548db5d7a0fa42c68977169369ea036e'), size_on_disk=422880, blob_last_accessed=1763591392.9491735, blob_last_modified=1758648269.3916593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fc9311b65abb851fbbfd8bb0c0765af59069659f5a715aee546a8644b0af4ec2'), size_on_disk=210948, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648267.6766515), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7367/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a7778bb24506047963575fa96126157895c7f329996c729c3e7d6070595955c3'), size_on_disk=324361, blob_last_accessed=1763591392.1631787, blob_last_modified=1758648270.628665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6448/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/182dc134914a5ddb02992ab6a4f1c12d2450aa331820bf7bd95693dc999bcfae'), size_on_disk=390210, blob_last_accessed=1763591392.9591732, blob_last_modified=1758648268.672656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5654/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38961219eb66dec31505ebcc5088f16684be9891b96fffa8c9a13cd3f13254d0'), size_on_disk=447782, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.1876538), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6055/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/31bb70b4c90654ecb276664f5c914679322b24985ec66d5b396a99f253ebfa17'), size_on_disk=377015, blob_last_accessed=1763591393.5701692, blob_last_modified=1758648268.3076544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/74cbce50c258bdddb7e42c8fd5b0b2a9da74fe3cf89a9ab5a53ee4fde7c1dee6'), size_on_disk=186142, blob_last_accessed=1763591392.842174, blob_last_modified=1758648266.9966483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e91fd764a83947c14f35d7a2d8c0d156c0a61a32a1da96273718ef4be720787'), size_on_disk=361855, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.7196517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7cb5cca74f1bda2f8f1c8ef962e54d34228361eaa9dd8b21e0d1dd7092ff8164'), size_on_disk=229703, blob_last_accessed=1763591392.555176, blob_last_modified=1758648267.0126483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ac8c590c19e7b95b9aac571b1394976039f25fdd66e62a68ecb99925433f8e54'), size_on_disk=355706, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0226483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7332/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f7f62ee008fa2b5cc688cbd80c888f1bf9da538caa245b63581520d485338a99'), size_on_disk=309595, blob_last_accessed=1763591393.1021724, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7151/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9969e4607d6bf42424f5d2ce8f0d16e3d2d89d069cae6d75997e99c88a68dc77'), size_on_disk=275572, blob_last_accessed=1763591393.7231681, blob_last_modified=1758648270.1476629), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4d098c33564f99ec6aed77d585d1f6e871806f441f099790eef4d6478e322af'), size_on_disk=264459, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7486472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d3e36418bd46acf9537a8f864b1a1361a062d72fec2b093789b88ed655ec117'), size_on_disk=377393, blob_last_accessed=1763591393.0451727, blob_last_modified=1758648267.0136483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dcb33135a9d854d17a6b80348f6bee8becea9ca3808583a034f89c5e4f460c54'), size_on_disk=218643, blob_last_accessed=1763591393.4831698, blob_last_modified=1758648267.9636528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6140/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/66c2da17957d7e6a8b159d3573879ac779d0094e35150b51e2643f7ddee36e1f'), size_on_disk=332958, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4236548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6573/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e435989051765cf2955d8cd733904e8b60e5f3121e1c2fdc987f4e6f71fe3970'), size_on_disk=265022, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9586573), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6354/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/80b129a46defc7b06e472a35935bfaf410bc5e456475b2885feb2c8a4934a81b'), size_on_disk=173225, blob_last_accessed=1763591393.4981697, blob_last_modified=1758648268.4986553), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f427ea0f4ded3b6ff841153d8ad87d72a26a964c189246fb6fd5c439bd8b0c1e'), size_on_disk=353623, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.812652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6954/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/aeed3c55d81a4eb0c568486e476211c599ce2bf37f9dff22c7dfca23f5d80495'), size_on_disk=476054, blob_last_accessed=1763591393.011173, blob_last_modified=1758648269.943662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6739/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/58b5ed63f2197f406dd3df6a569975d776722193007912718979eaed4d4e5f9c'), size_on_disk=399160, blob_last_accessed=1763591392.6551754, blob_last_modified=1758648269.331659), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6924/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5e9b3a94d73c1720e6b05ea55000054905a8672edea7f950736ce3fe191f8faf'), size_on_disk=412939, blob_last_accessed=1763591393.748168, blob_last_modified=1758648269.7066607), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=composite/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1f8adf8fb4992283714df8587126211d54e6a0580b8be88c1d5e911196aed783'), size_on_disk=4814573, blob_last_accessed=1763591392.0561793, blob_last_modified=1758648268.0256531), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR007B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6f0620b102e7cbfadced7d5530545a185036b53118d3e2b7f074c56403d80f4b'), size_on_disk=263185, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7616518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6764/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec87eef9e2e8cdf3c3a10ead28dcf1bfa76e94e12a42422a46c8559fb18207c2'), size_on_disk=75684, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.4036593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4b2ff192aa4808cb50b1c1b1667060f98505dd63c1aa58c7d8354e41194957e4'), size_on_disk=289767, blob_last_accessed=1763591393.4871697, blob_last_modified=1758648266.7706473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d50648ed5c9f593f830aff661d13b3d34bd14f0bf93b16169df4765c9f47fc8'), size_on_disk=295924, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7426472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7256/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1af21cbdd7f6f6effff15a1b061a48f85132d582f08c8fd42e04ea58bde7ec00'), size_on_disk=206710, blob_last_accessed=1763591392.586176, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5801/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/12e91a415cc2a149af4c34c3c8b032821ff301e2828dfb9f4636bd6b2913ea3d'), size_on_disk=360485, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.1636536), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=MAG002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f7f35257d4c1f7fa5812ea31c1de791e08ab5e3a4e7ca42b2bb12b2c64f7ed7'), size_on_disk=490174, blob_last_accessed=1763591392.829174, blob_last_modified=1758648267.583651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fa1a44bd33df22ec6b5a8baf3be17e51a11522b5fdd545a38e6e163cc5ae8207'), size_on_disk=241911, blob_last_accessed=1763591392.572176, blob_last_modified=1758648267.5226507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dc74862f02ff12b8c906046f1f03953d33f5516091d58397b0f9a0fc05bb4b5c'), size_on_disk=315435, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.5006506), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6731/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/23ecd71c4e3c1c547295bb756d4654eb413ff288d10118ab286cd21ccd95b666'), size_on_disk=346187, blob_last_accessed=1763591392.1871786, blob_last_modified=1758648269.1906583), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6920/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/45122fcfb3abc3a43631023712c224dc6e00dba566fb2e15deb4ab4501ad2538'), size_on_disk=446998, blob_last_accessed=1763591392.8881738, blob_last_modified=1758648269.6416605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6657/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/95ad2225152f51e2e31c030ff6708aeebccee5e548035d6f43c9cc3554bdfe5e'), size_on_disk=507730, blob_last_accessed=1763591393.163172, blob_last_modified=1758648269.1676583), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR006B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fcc5e886ec7c6363a5ab51b03d3da189ac3e0cde0a0f79d4f0c46043a6b61838'), size_on_disk=253857, blob_last_accessed=1763591392.0501795, blob_last_modified=1758648267.785652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7283/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0dc91bfacca82c5ed23de7757d59f9f808d541d96dd79aab56f0c4252e7aabf3'), size_on_disk=265146, blob_last_accessed=1763591392.7731745, blob_last_modified=1758648270.401664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e871cf06f596bfe74896b62aa28d6b1e6f2f0b8408630fb146c7e29cfd88fbf0'), size_on_disk=232721, blob_last_accessed=1763591392.7871745, blob_last_modified=1758648267.991653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS011/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ea7cb0559b342dd0ec278e7d5846a5ba0c0d867ea04d545f72e0d1c9472a0cc'), size_on_disk=363979, blob_last_accessed=1763591392.9791732, blob_last_modified=1758648267.0116484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5301_5088/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/65ef775934339b80eda59a141cf791a74e1c6f12513d8143307cebe2f11c3738'), size_on_disk=410106, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.9516528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7118/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/be8bd409b83526d5671501de6ef2980177955fdcc3a13d5e153d2593493b2a1e'), size_on_disk=346308, blob_last_accessed=1763591392.9281735, blob_last_modified=1758648270.1336627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e20f616f6e340b76f6446d2cdffb6cc9feb81297bd0307733f835acf4142cfd'), size_on_disk=269523, blob_last_accessed=1763591392.3461773, blob_last_modified=1758648267.2696495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7266/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9aea2d6f5606d24e1999d450379b1f8c8b9d80eded9e784fb729faa516a27026'), size_on_disk=324480, blob_last_accessed=1763591392.0401795, blob_last_modified=1758648270.379664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6527/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/10081f44b1c2f5ad32489da51fc9ac5b5289b54688a7e78725db247a644b7e4d'), size_on_disk=494640, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.882657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7297/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4bd4088cbfcccdf96def916bb8fd9d673327d3e57c2a4a3e57299743ce4aca7'), size_on_disk=261344, blob_last_accessed=1763591393.588169, blob_last_modified=1758648270.6516652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/d1cd38451bc41ef14dbc1566c4704b004b2c0661f667210c81dba09bec90540f'), size_on_disk=244487, blob_last_accessed=1763591393.6301687, blob_last_modified=1758648267.3246498), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5610/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a86512ae1000a994c86e9aebaf30cf9ce1a59160155f3dc1afa6cbde0a1fc47e'), size_on_disk=307382, blob_last_accessed=1763591393.5291693, blob_last_modified=1758648267.994653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6061/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73e03845d0fbff1a5931e4f792e2c1965ade02fc622ffc08e05c267ac6cd3231'), size_on_disk=394930, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.447655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5617/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bc01241e8363373c995a0fa7439aa1e432a60eb8a7805124fff302311f638361'), size_on_disk=351950, blob_last_accessed=1763591393.6541686, blob_last_modified=1758648268.1026535), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6651/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7644af4cc956ea08809ab6c80c65095e3fdbd36e056040c0eaded3773d546359'), size_on_disk=559951, blob_last_accessed=1763591393.6361687, blob_last_modified=1758648269.1856585), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/11485fd7bb94d0093ce2508d541fea3ce64f934197d8a5f7bb26ecbdb59f8dbb'), size_on_disk=124642, blob_last_accessed=1763591392.3321776, blob_last_modified=1758648267.0666487), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7115/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/205443fd87ce5c793dab86eb75c21dab7dc2ae2edac41d7e6314cfe491bdb01d'), size_on_disk=321850, blob_last_accessed=1763591393.2331715, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6872/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02fabe6e32f52fe6fca923beb0a6cc2898d991304e23a3e75f3ae8a6bcf361c6'), size_on_disk=274362, blob_last_accessed=1763591393.6231687, blob_last_modified=1758648269.6966608), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7243/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cb59e645fff2683db692b4d2f514602ba210271b632cd113853febe941c6485f'), size_on_disk=281696, blob_last_accessed=1763591392.701175, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/62e9a627f20607bc4887ebc9277131f974ec80ff39ac386775f4b8f2f5cb9d1d'), size_on_disk=346782, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7476518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6855/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48e48378d24404e926bc1c44b10af60d4b12f82124ff2f80bc0b69b4a0f98740'), size_on_disk=609248, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6556606), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e5b3174998823af40d055f46ca06e7bb44601cbe97b133062cd3e0491dd30ac2'), size_on_disk=323308, blob_last_accessed=1763591393.153172, blob_last_modified=1758648267.2686496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=KB001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38c939d54d3ed77a09650d2ccf00a6ed0e20390b8be722f1c3ed7881a5904a60'), size_on_disk=351677, blob_last_accessed=1763591392.9151735, blob_last_modified=1758648267.5566509), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5961/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/49cac5c2df1adf790a9ac0209db6c0945de50b8191d403e8b1ad386ee2dcb35e'), size_on_disk=321368, blob_last_accessed=1763591392.560176, blob_last_modified=1758648268.215654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7258/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7b1860ba3be7dbf053203fa5690da2002f7841186940231acea6064e70f0bdc8'), size_on_disk=171331, blob_last_accessed=1763591392.6171756, blob_last_modified=1758648270.3746638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7331/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f159a9811b8ac1b5e252b52427979a3bddce2e7131b13f84046b31e47f7fa399'), size_on_disk=242821, blob_last_accessed=1763591393.2501714, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a76f0849e518faedfe50e6f0c76908a3f6beed34fd0b607603db1859493595d0'), size_on_disk=292312, blob_last_accessed=1763587658.6168654, blob_last_modified=1758648266.7676473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7137/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/703fe9f8552adcbdfb5e640509cb62df2fa734b43fe1e98fc1fd342415f823d7'), size_on_disk=515398, blob_last_accessed=1763589053.7698598, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bbe701ffd546a2f4359f821262f7b44232bdc328d9d4719bfdaa8f2aedc6d2e9'), size_on_disk=346079, blob_last_accessed=1763591393.0861723, blob_last_modified=1758648267.589651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7276/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4e0d576cc4a6a85be64f2198e5001a9f5b02b506072df6f695cfdfc719cb041'), size_on_disk=204036, blob_last_accessed=1763591392.987173, blob_last_modified=1758648270.4646642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1fa09a5a6e6e69198501d994050adfcad7ff39a3cc2fe94e7ea177414e1bc968'), size_on_disk=303532, blob_last_accessed=1763591393.7091682, blob_last_modified=1758648267.2776496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6708/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/35520976736159652e3be8066e77b1371522f63a8b3564d9b0d9f5b1598a144d'), size_on_disk=253955, blob_last_accessed=1763591393.6881683, blob_last_modified=1758648269.1996584), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ab338795b8bbf334650386a123f6e0df41363cb5dcbb778d485ce58804e96b29'), size_on_disk=375506, blob_last_accessed=1763591392.9711733, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6959/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c0cb6c2ec2bda19b0c1da5d5fc2229d0b6d6987d75646fa230c0c14d591cf1f1'), size_on_disk=537588, blob_last_accessed=1763591392.851174, blob_last_modified=1758648269.8686616), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/33d7303ed077f489ee28021daca0bbc859feb1cfd383dfc9a856ea13994801a9'), size_on_disk=331877, blob_last_accessed=1763591393.292171, blob_last_modified=1758648268.2226539), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7155/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/521fc1bea6963640645c21cbdb8d1de07793f11213c23ac71d249f3252b32f54'), size_on_disk=304554, blob_last_accessed=1763591393.5841691, blob_last_modified=1758648270.1366627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6853/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ed47ecb2aa870fdbf12920fd4486933262c0a24318c9c8c5531e359a3c416c90'), size_on_disk=515114, blob_last_accessed=1763591393.2661712, blob_last_modified=1758648269.4276595), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6454/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c35b60861d2fc7a3f4256869e14e629aed07cd2ccce76608578ceb8c6ec423e1'), size_on_disk=498326, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.7226562), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6798/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/899cde55032cd593be0013e1f6ff33fe9a12f4616477b1410d86fae1b2008a09'), size_on_disk=342376, blob_last_accessed=1763591392.7951744, blob_last_modified=1758648269.4006593), CachedFileInfo(file_name='annotated_features_meta.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features_meta.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/2be4b693d333698f7ada7ef40ffaf927c0160d7975ebcf6e215cbc4ba1be1604'), size_on_disk=11813, blob_last_accessed=1763587594.724117, blob_last_modified=1758648264.795638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=MAG001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/30b63d014686a103b5e7fc6571e902fa9c08630a96557cc12ec9dfcf60871306'), size_on_disk=347333, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.559651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6677/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4ee6e1d3e2b9a9bbcc8851048d2b16f4aea6a1e2f752afb9e0bf75a56adf7042'), size_on_disk=322147, blob_last_accessed=1763591392.393177, blob_last_modified=1758648269.0826578), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5690/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4831e3e037d421a14efbe1da8c369015ec7d01df351bb27f0ef0400c8b94c693'), size_on_disk=311754, blob_last_accessed=1763591392.9021738, blob_last_modified=1758648268.211654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d4c0cbbf6075a8b5732e3cef2ba154d95d21aa0efc7e49d947cceebd8742b30'), size_on_disk=269007, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4106548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/646c843611bf88c7f5dcd5b1a664d724c3b24b6434cc52d9a54de62ccaf6c542'), size_on_disk=255949, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.4506505), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6861/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6955b1a1746fe41162ea1d7a098482bbfdb3d0e411c809b52d698e285627ce03'), size_on_disk=385103, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6146603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7351/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cc708adf186f28db09dfdd4ac43865a90482b2192306949eab01492598cf23e7'), size_on_disk=198070, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.610665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6b92129c2fb99028b6cab9509cf1a4c40b142109cde34c9a130d375001dcd211'), size_on_disk=267273, blob_last_accessed=1763591392.7811744, blob_last_modified=1758648269.9106617)}), refs=frozenset({'main'}), last_modified=1763649507.5418575)}), last_accessed=1763649507.5438576, last_modified=1763649507.5418575), CachedRepoInfo(repo_id='BrentLab/kemmeren_2014', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014'), size_on_disk=319245924, nb_files=5, revisions=frozenset({CachedRevisionInfo(commit_hash='b76f0dfe8477b300000faecf16b7d315fbf59344', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/b76f0dfe8477b300000faecf16b7d315fbf59344'), size_on_disk=319228170, files=frozenset({CachedFileInfo(file_name='kemmeren_2014.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/b76f0dfe8477b300000faecf16b7d315fbf59344/kemmeren_2014.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/3c463017444bd7690959c0d6fff0d0ebe2faa7b916dd9c44e305156f6c98b863'), size_on_disk=319218929, blob_last_accessed=1760552530.221978, blob_last_modified=1756832530.9644527), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/b76f0dfe8477b300000faecf16b7d315fbf59344/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/199dce9d01d3884716dd3a785a85751cb9b1de3e'), size_on_disk=9241, blob_last_accessed=1760552530.0869784, blob_last_modified=1758654056.5908144)}), refs=frozenset(), last_modified=1758654056.5908144), CachedRevisionInfo(commit_hash='95a5f915ad49dfe4af75632861d9da69de2b525f', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/95a5f915ad49dfe4af75632861d9da69de2b525f'), size_on_disk=10286, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/95a5f915ad49dfe4af75632861d9da69de2b525f/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/05a15ab9764763e5d1957023162e13b0068a5697'), size_on_disk=10286, blob_last_accessed=1765901283.9662895, blob_last_modified=1765415815.7076464)}), refs=frozenset({'main'}), last_modified=1765415815.7076464), CachedRevisionInfo(commit_hash='a6c9a954a1def1774b68582f117ad1abc5864a07', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07'), size_on_disk=319226397, files=frozenset({CachedFileInfo(file_name='kemmeren_2014.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07/kemmeren_2014.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/3c463017444bd7690959c0d6fff0d0ebe2faa7b916dd9c44e305156f6c98b863'), size_on_disk=319218929, blob_last_accessed=1760552530.221978, blob_last_modified=1756832530.9644527), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/a29a64128c10ef9691ee773131972c61f720ba07'), size_on_disk=5007, blob_last_accessed=1756832529.5094638, blob_last_modified=1756832529.5404634), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756832529.5094638, blob_last_modified=1756832529.5434635)}), refs=frozenset(), last_modified=1756832530.9644527)}), last_accessed=1765901283.9662895, last_modified=1765415815.7076464), CachedRepoInfo(repo_id='BrentLab/hu_2007_reimand_2010', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010'), size_on_disk=42682732, nb_files=4, revisions=frozenset({CachedRevisionInfo(commit_hash='31ccd35daf785420014a1346a7db064dd306d4f8', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8'), size_on_disk=42682732, files=frozenset({CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756844741.8097973, blob_last_modified=1756844741.8767972), CachedFileInfo(file_name='hu_2007_reimand_2010.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/hu_2007_reimand_2010.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/eb9be5a1f01907a81eb2ab4fbbee36ff8c50186f'), size_on_disk=63, blob_last_accessed=1756844741.7997973, blob_last_modified=1756844741.8557973), CachedFileInfo(file_name='hu_2007_reimand_2010.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/hu_2007_reimand_2010.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/47a15f3e23f8cb50af3217d00c6439d29a7422b5190116e4453a5f78cb22540d'), size_on_disk=42677516, blob_last_accessed=1756844742.1787965, blob_last_modified=1756844742.1477966), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/886f4614dfd1ef33709d462908ae18608c13e8ba'), size_on_disk=2692, blob_last_accessed=1756844742.0937967, blob_last_modified=1756844742.1537964)}), refs=frozenset({'main'}), last_modified=1756844742.1537964)}), last_accessed=1756844742.1787965, last_modified=1756844742.1537964), CachedRepoInfo(repo_id='BrentLab/rossi_2021', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021'), size_on_disk=213734041, nb_files=48, revisions=frozenset({CachedRevisionInfo(commit_hash='dcf529428b66f5efc8c44ea06fb4733c41952e03', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/dcf529428b66f5efc8c44ea06fb4733c41952e03'), size_on_disk=15570962, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/dcf529428b66f5efc8c44ea06fb4733c41952e03/genome_map/accession=SRR11466106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/04bacc0f8c03ee000ba3b69320522d0b1be909686474b6e8a126173a0ceae8c4'), size_on_disk=15547870, blob_last_accessed=1762805142.7999256, blob_last_modified=1756944357.1972551), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/dcf529428b66f5efc8c44ea06fb4733c41952e03/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/192e51c15075f65f15da70558261e4c95bb1392a'), size_on_disk=8396, blob_last_accessed=1762805108.5211468, blob_last_modified=1762805108.5201466), CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/dcf529428b66f5efc8c44ea06fb4733c41952e03/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1762805126.6580298, blob_last_modified=1756944310.9395182)}), refs=frozenset({'main'}), last_modified=1762805108.5201466), CachedRevisionInfo(commit_hash='6b29baae54286d5fbdd1c70f499c03319badad51', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51'), size_on_disk=213707016, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466115/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/c08ce1194dd98cb028a6038944317e70ac1545e32b21dd323eafa57b16611e6e'), size_on_disk=3503431, blob_last_accessed=1757598690.9468212, blob_last_modified=1757104405.9427147), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466143/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/54bd4af6ed8caf52a5810598d96c9b235266542586a692112cb247a209c2649f'), size_on_disk=10801408, blob_last_accessed=1757598691.2688208, blob_last_modified=1757104413.3726768), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466107/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/2f924bc58d82a21b621b051addd1913c77369afaa738171ef0962548fc1a1021'), size_on_disk=5670198, blob_last_accessed=1757598690.8608212, blob_last_modified=1757104405.0417192), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466124/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/39ef32412cae257b724c3b797ea482eeffb5b29afef4246d997ea525a2124054'), size_on_disk=7162800, blob_last_accessed=1757598691.075821, blob_last_modified=1757104409.5966961), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466108/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/56875f81a3cc7c1d40b3042b2c443058eb0f9a2b7e7ebc2e1704c098be612628'), size_on_disk=1765819, blob_last_accessed=1757598690.8238213, blob_last_modified=1757104407.1947083), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466127/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/1565c5175e05c322f160529cb4bb74d5e8468458f150f228d6aadf2b670c3b4a'), size_on_disk=11670434, blob_last_accessed=1757598691.092821, blob_last_modified=1757104411.0776885), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466125/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/0bba21490ea0a6297031b0ff3219fc0158d9bbcf15099906c86a770b1a1312fc'), size_on_disk=2624759, blob_last_accessed=1757598691.081821, blob_last_modified=1757104409.4426968), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466129/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/b290d4c9436756b444d0da1af1229b0a2a6e877fd968c5d01b73efa96e62cff8'), size_on_disk=6955953, blob_last_accessed=1757598691.196821, blob_last_modified=1757104410.78769), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466126/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/34292b90d99b5a5be542c7dd10b11336605330639d92e93b092e567a56fe7d93'), size_on_disk=2850164, blob_last_accessed=1757598691.086821, blob_last_modified=1757104410.0976934), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466144/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/57b3d2c7917bfa29312cc51c921beff914858c61762e28a658eb29d8eaf3348b'), size_on_disk=918271, blob_last_accessed=1757598691.2758207, blob_last_modified=1757104413.554676), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466112/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8f3441964a046d3c148b41f335948a085d26884e1a890e515711a2d1b48a40be'), size_on_disk=4883601, blob_last_accessed=1757598690.9128213, blob_last_modified=1757104408.5027015), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466142/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/3dc044b5cc1b7bc8070d8de4b7b870524b296f26b4f43b22dce2dcbf161d4c74'), size_on_disk=1739520, blob_last_accessed=1757598691.2588208, blob_last_modified=1757104415.5896657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466149/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/ae4d5237fde214a92d3267fbe11f6f1f88569cc7f3f7ccb40fed200871bf33b1'), size_on_disk=10933582, blob_last_accessed=1757598691.3518207, blob_last_modified=1757104415.1316679), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466109/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/6588704c47423e671054df9cb2318c045138b0018f2eefff61ef91a25848eb58'), size_on_disk=5259413, blob_last_accessed=1757598690.8298213, blob_last_modified=1757104404.9987195), CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1762805126.6580298, blob_last_modified=1756944310.9395182), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466141/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/e9efb807ce35fc3e43b1ff0d27b80646c7d137c587abd9d6247393e44d4f30a0'), size_on_disk=986450, blob_last_accessed=1757598691.2628207, blob_last_modified=1757104413.129678), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/04bacc0f8c03ee000ba3b69320522d0b1be909686474b6e8a126173a0ceae8c4'), size_on_disk=15547870, blob_last_accessed=1762805142.7999256, blob_last_modified=1756944357.1972551), CachedFileInfo(file_name='genome_map.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/51978dc2125afb40a41a29672f8ae750308d9a63'), size_on_disk=5311747, blob_last_accessed=1757598690.9468212, blob_last_modified=1757104406.477712), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466118/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/192057e6004dd5cd303e8b1abf54cb72005e211c315665e28eec7b617a84d95a'), size_on_disk=728586, blob_last_accessed=1757598691.0068212, blob_last_modified=1757104407.0917087), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466122/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/6362da9e07f02d4c9dc05f15f638ca1b1bf0de5989da4ee8c6f0768f2453f98f'), size_on_disk=1249990, blob_last_accessed=1757598691.0398211, blob_last_modified=1757104411.9016843), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466150/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/80cd2d325e5d7a50c6ecbf79a052f45c54a93b9423e90735bf8989aada9bb9eb'), size_on_disk=3234303, blob_last_accessed=1757598691.3798206, blob_last_modified=1757104417.837654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466139/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/25c7d3ffbacca76fd998edffe0c762e220f448140019aa2d389facff5117ce48'), size_on_disk=176811, blob_last_accessed=1757598691.2478209, blob_last_modified=1757104412.6266806), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466110/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/d5283e08e898b4f59837f7ff2064f79c8a828994f868c637894f4e07df36daa5'), size_on_disk=13123795, blob_last_accessed=1757598690.8318212, blob_last_modified=1757104407.6787057), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466132/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/cc41509e63473d565341ed36fd6881fecfd537e59c003f4779d4c4ec7b49cb54'), size_on_disk=1906184, blob_last_accessed=1757598691.161821, blob_last_modified=1757104413.4366765), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466131/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/bdce0842b5568e406a799dbe36b4b9645a30fa58eb0a707f24ed3bb3900fbf67'), size_on_disk=11759994, blob_last_accessed=1757598691.157821, blob_last_modified=1757104411.1146884), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466111/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/ba431fc9a05f40581e450429d467226a8e3c49195de355437f9044bb0fdfe524'), size_on_disk=5749460, blob_last_accessed=1757598690.9028213, blob_last_modified=1757104404.9707196), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466114/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/2c07f671cca471d6ad3e10e071cd1b976a89a45ff32920913d8fc36dbeaa1678'), size_on_disk=10992797, blob_last_accessed=1757598690.9498212, blob_last_modified=1757104406.3287127), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466123/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/57b7ca5d419676590538573f295b99db05a952e20cb4c6b2a1618974611d5051'), size_on_disk=3672899, blob_last_accessed=1757598691.073821, blob_last_modified=1757104409.590696), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466135/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/d44d381db0b8c0cb4960796e48d3d16e3d766aff87cbc686789a0c5799ba0a98'), size_on_disk=5204256, blob_last_accessed=1757598691.180821, blob_last_modified=1757104412.4376817), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466113/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/acec3b041a74ab45dbeb03e7f183c2b3d16479aadd64a81f80a74aa42d56368d'), size_on_disk=5866774, blob_last_accessed=1757598690.921821, blob_last_modified=1757104409.0226989), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466134/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/0b23434181eb1fc864e858cfca82d2d8fed1524a8f7dbea3bf576f6f452cf08b'), size_on_disk=12398132, blob_last_accessed=1757598691.169821, blob_last_modified=1757104412.552681), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466140/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/25f98a891820b5a91c11629ed5615d40ffe1fe4a6cba1b2c6642067d76b5be87'), size_on_disk=68174, blob_last_accessed=1757598691.2588208, blob_last_modified=1757104415.6456654), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/09f6dce90ec0e64d8f18614091dd18ad2e86213a'), size_on_disk=4403, blob_last_accessed=1757598690.987821, blob_last_modified=1757104409.9196944), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466136/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/f5e7fd644d30ae0c8e2c72bc341155bfe5df2ce102b68b08ff4d243470026fcc'), size_on_disk=343033, blob_last_accessed=1757598691.1958208, blob_last_modified=1757104411.7116852), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466119/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/0157d9136c2c2bb5640866449adf4bab8c62b3d7f7fb4262981908c77986f89e'), size_on_disk=12516152, blob_last_accessed=1757598691.0068212, blob_last_modified=1757104412.3166823), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466120/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/4220a31e6a7be79341828d41da77ef77fb5a8026980d4445fec18c57b5229c1f'), size_on_disk=3644577, blob_last_accessed=1757598691.034821, blob_last_modified=1757104408.3467023), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466121/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/78d0f377f8a756b28f2f5296932368c9c6b764d2972dc93aa6dd354591d29860'), size_on_disk=7053621, blob_last_accessed=1757598691.0328212, blob_last_modified=1757104408.5097015), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1757598690.9228213, blob_last_modified=1757104403.6017265), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466116/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/587a7b86dffe3d7ade9adeeb94029ea1de54e61fb2c2445dc527979b7f1c24ac'), size_on_disk=5596311, blob_last_accessed=1757598690.988821, blob_last_modified=1757104407.3507075), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466130/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/73db821736e3e47c09da250e369fa06812e02e5abe7a4a3ceab5d1020422b66c'), size_on_disk=1920920, blob_last_accessed=1757598691.1328208, blob_last_modified=1757104410.9306893), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466117/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/49065d4cee74c20f34303405e34e284e29f19082b76fa883a38aeab24cadf674'), size_on_disk=1742958, blob_last_accessed=1757598690.995821, blob_last_modified=1757104411.8276846), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466128/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/f631797d2bc13a580f570637d94a0b9ea37deb2fd7ef92e32c66fd9014260525'), size_on_disk=1059587, blob_last_accessed=1757598691.118821, blob_last_modified=1757104410.6996903), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466133/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/b6d6913e14b52d5d51dab2f254815da89e03cebe75e80356b27cf18e31eb048f'), size_on_disk=5090722, blob_last_accessed=1757598691.169821, blob_last_modified=1757104412.180683)}), refs=frozenset(), last_modified=1757104417.837654), CachedRevisionInfo(commit_hash='6aac54b4b3fbb414561a8b6cc7acf87880c3c3c2', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6aac54b4b3fbb414561a8b6cc7acf87880c3c3c2'), size_on_disk=4665, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6aac54b4b3fbb414561a8b6cc7acf87880c3c3c2/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/14e1e02ad3804c0a4146a52509460e9cbb759d93'), size_on_disk=4665, blob_last_accessed=1758306843.4906857, blob_last_modified=1758156479.1516545)}), refs=frozenset(), last_modified=1758156479.1516545), CachedRevisionInfo(commit_hash='824bf517ff0722a085c86ac7924df0ab1278c8bf', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/824bf517ff0722a085c86ac7924df0ab1278c8bf'), size_on_disk=14696, files=frozenset({CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/824bf517ff0722a085c86ac7924df0ab1278c8bf/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1762805126.6580298, blob_last_modified=1756944310.9395182)}), refs=frozenset(), last_modified=1756944310.9395182), CachedRevisionInfo(commit_hash='3ce77eb2070d7bb43049ba26bb462fded0ff7863', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/3ce77eb2070d7bb43049ba26bb462fded0ff7863'), size_on_disk=15562566, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/3ce77eb2070d7bb43049ba26bb462fded0ff7863/genome_map/accession=SRR11466106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/04bacc0f8c03ee000ba3b69320522d0b1be909686474b6e8a126173a0ceae8c4'), size_on_disk=15547870, blob_last_accessed=1762805142.7999256, blob_last_modified=1756944357.1972551), CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/3ce77eb2070d7bb43049ba26bb462fded0ff7863/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1762805126.6580298, blob_last_modified=1756944310.9395182)}), refs=frozenset(), last_modified=1756944357.1972551), CachedRevisionInfo(commit_hash='1acaa5629922414e8efe23a5d023bd44965824c8', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1acaa5629922414e8efe23a5d023bd44965824c8'), size_on_disk=4683, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1acaa5629922414e8efe23a5d023bd44965824c8/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/bb690d71edd44885609ed660d926f3cf2c4c473e'), size_on_disk=4683, blob_last_accessed=1758306349.7238934, blob_last_modified=1758156991.1235294)}), refs=frozenset(), last_modified=1758156991.1235294), CachedRevisionInfo(commit_hash='22ebbfcf62a7ed665fb8eceaea53b58b45a1709e', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/22ebbfcf62a7ed665fb8eceaea53b58b45a1709e'), size_on_disk=4602, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/22ebbfcf62a7ed665fb8eceaea53b58b45a1709e/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/e03b8d10ba59c549907a309ad8343133b7b902fc'), size_on_disk=4602, blob_last_accessed=1757613581.062093, blob_last_modified=1757613581.0610929)}), refs=frozenset(), last_modified=1757613581.0610929), CachedRevisionInfo(commit_hash='1e07e46fd0b5348243487ac4a573b570a2a3946b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1e07e46fd0b5348243487ac4a573b570a2a3946b'), size_on_disk=4683, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1e07e46fd0b5348243487ac4a573b570a2a3946b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/bb690d71edd44885609ed660d926f3cf2c4c473e'), size_on_disk=4683, blob_last_accessed=1758306349.7238934, blob_last_modified=1758156991.1235294)}), refs=frozenset(), last_modified=1758156991.1235294), CachedRevisionInfo(commit_hash='664d95cdd482d753bc139d26119061c2fa6eaa76', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/664d95cdd482d753bc139d26119061c2fa6eaa76'), size_on_disk=4679, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/664d95cdd482d753bc139d26119061c2fa6eaa76/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/ff6f75db42c0690f0e0285e56c1aa95bc5cf1300'), size_on_disk=4679, blob_last_accessed=1758157110.1705706, blob_last_modified=1758157110.1695707)}), refs=frozenset(), last_modified=1758157110.1695707)}), last_accessed=1762805142.7999256, last_modified=1762805108.5201466), CachedRepoInfo(repo_id='BrentLab/harbison_2004', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004'), size_on_disk=70377892, nb_files=10, revisions=frozenset({CachedRevisionInfo(commit_hash='2916ad316cc0d8a1ce2e7f35d3707b831b203e87', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/2916ad316cc0d8a1ce2e7f35d3707b831b203e87'), size_on_disk=7160, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/2916ad316cc0d8a1ce2e7f35d3707b831b203e87/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/7e3ec66f5479346ca9d082545ff0b6a960fb60d5'), size_on_disk=7160, blob_last_accessed=1758649475.233073, blob_last_modified=1758155946.7669883)}), refs=frozenset(), last_modified=1758155946.7669883), CachedRevisionInfo(commit_hash='073cdeb1b541c28b1a97df9dbc6be6af1d9c23b6', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/073cdeb1b541c28b1a97df9dbc6be6af1d9c23b6'), size_on_disk=5145, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/073cdeb1b541c28b1a97df9dbc6be6af1d9c23b6/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/f7a6cf3dd4477f08e508f38665350e8d11589963'), size_on_disk=5145, blob_last_accessed=1755876214.0380862, blob_last_modified=1755876188.1642818)}), refs=frozenset(), last_modified=1755876188.1642818), CachedRevisionInfo(commit_hash='a0f6f1b23505c4e6a471e65a43a33ff5cced0733', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a0f6f1b23505c4e6a471e65a43a33ff5cced0733'), size_on_disk=25423545, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a0f6f1b23505c4e6a471e65a43a33ff5cced0733/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b5fbd9e98fd8ddadeeb5631e3b6f5055e917c98d'), size_on_disk=7679, blob_last_accessed=1759345152.2162483, blob_last_modified=1758649637.161202), CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a0f6f1b23505c4e6a471e65a43a33ff5cced0733/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/91904b3e84754bd6011cd4b385bbc99d69a899e5c7562f039a121a744ba95a0a'), size_on_disk=25415866, blob_last_accessed=1763588775.480192, blob_last_modified=1758649664.1292322)}), refs=frozenset(), last_modified=1758649664.1292322), CachedRevisionInfo(commit_hash='5eeb06e389a648a36087e20eabad1f961e22dc5a', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/5eeb06e389a648a36087e20eabad1f961e22dc5a'), size_on_disk=44894945, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/5eeb06e389a648a36087e20eabad1f961e22dc5a/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/aa43ea37112a84a60d21ce254c1cc1e433def6dc'), size_on_disk=8550, blob_last_accessed=1765406568.606891, blob_last_modified=1765292615.6690626), CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/5eeb06e389a648a36087e20eabad1f961e22dc5a/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b89f865a471fbf3a8054871f8fe79507f6f4be5c2291dcd19030ec8fd4a5325c'), size_on_disk=44886395, blob_last_accessed=1765901285.6763043, blob_last_modified=1765314558.8410568)}), refs=frozenset(), last_modified=1765314558.8410568), CachedRevisionInfo(commit_hash='4e54b7fafd79829bcd179b508efd11a3dc7182cc', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/4e54b7fafd79829bcd179b508efd11a3dc7182cc'), size_on_disk=44900498, files=frozenset({CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/4e54b7fafd79829bcd179b508efd11a3dc7182cc/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b89f865a471fbf3a8054871f8fe79507f6f4be5c2291dcd19030ec8fd4a5325c'), size_on_disk=44886395, blob_last_accessed=1765901285.6763043, blob_last_modified=1765314558.8410568), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/4e54b7fafd79829bcd179b508efd11a3dc7182cc/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/ef196364c79e1a0ec01b3ab6db8dae798f0f8fd5'), size_on_disk=14103, blob_last_accessed=1765911961.5656407, blob_last_modified=1765819563.7734888)}), refs=frozenset(), last_modified=1765819563.7734888), CachedRevisionInfo(commit_hash='95dace55563820030e6bcac2d4b2a9a386a2369b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/95dace55563820030e6bcac2d4b2a9a386a2369b'), size_on_disk=44900425, files=frozenset({CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/95dace55563820030e6bcac2d4b2a9a386a2369b/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b89f865a471fbf3a8054871f8fe79507f6f4be5c2291dcd19030ec8fd4a5325c'), size_on_disk=44886395, blob_last_accessed=1765901285.6763043, blob_last_modified=1765314558.8410568), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/95dace55563820030e6bcac2d4b2a9a386a2369b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/1cfc9a691644fd13ba4e3c4d141caf8bc8dadc92'), size_on_disk=14030, blob_last_accessed=1765809787.9431984, blob_last_modified=1765415607.8811436)}), refs=frozenset(), last_modified=1765415607.8811436), CachedRevisionInfo(commit_hash='a33c34b373e379dfa9bd4922d281790180bb1217', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a33c34b373e379dfa9bd4922d281790180bb1217'), size_on_disk=44899466, files=frozenset({CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a33c34b373e379dfa9bd4922d281790180bb1217/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b89f865a471fbf3a8054871f8fe79507f6f4be5c2291dcd19030ec8fd4a5325c'), size_on_disk=44886395, blob_last_accessed=1765901285.6763043, blob_last_modified=1765314558.8410568), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a33c34b373e379dfa9bd4922d281790180bb1217/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/3cb3997d01aa1c91d0719f954e1cf207976c8a7d'), size_on_disk=13071, blob_last_accessed=1765916907.5030358, blob_last_modified=1765916907.339037)}), refs=frozenset({'main'}), last_modified=1765916907.339037), CachedRevisionInfo(commit_hash='3ff5f32dfeef3c0b2b7d6cc860b259b0ad095829', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/3ff5f32dfeef3c0b2b7d6cc860b259b0ad095829'), size_on_disk=25421759, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/3ff5f32dfeef3c0b2b7d6cc860b259b0ad095829/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/342343ab9f71e7ce0394c540bed737de39f2cf08'), size_on_disk=5893, blob_last_accessed=1764345374.74204, blob_last_modified=1763587990.060371), CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/3ff5f32dfeef3c0b2b7d6cc860b259b0ad095829/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/91904b3e84754bd6011cd4b385bbc99d69a899e5c7562f039a121a744ba95a0a'), size_on_disk=25415866, blob_last_accessed=1763588775.480192, blob_last_modified=1758649664.1292322)}), refs=frozenset(), last_modified=1763587990.060371)}), last_accessed=1765916907.5030358, last_modified=1765916907.339037), CachedRepoInfo(repo_id='BrentLab/yeast_genome_resources', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources'), size_on_disk=114475, nb_files=7, revisions=frozenset({CachedRevisionInfo(commit_hash='6607f7be76546563db0a68a9365c0a858bb5f3a1', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/6607f7be76546563db0a68a9365c0a858bb5f3a1'), size_on_disk=4495, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/6607f7be76546563db0a68a9365c0a858bb5f3a1/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/a31c4647c502767c22b5fe725ec8de58686a02d4'), size_on_disk=4495, blob_last_accessed=1755813454.8008156, blob_last_modified=1755813454.7998157)}), refs=frozenset(), last_modified=1755813454.7998157), CachedRevisionInfo(commit_hash='7441b9a8c858332e730e7ddb9d255460ae698626', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/7441b9a8c858332e730e7ddb9d255460ae698626'), size_on_disk=87714, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/7441b9a8c858332e730e7ddb9d255460ae698626/features/chr=chrII/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/d64638adb2290412a962abffe0fbd0546ff139b29064bbf313b0a768449454a5'), size_on_disk=82276, blob_last_accessed=1755812630.656903, blob_last_modified=1755812631.0999012), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/7441b9a8c858332e730e7ddb9d255460ae698626/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/e63c2087b2e56dc15e8ac6cf5693c2391127b732'), size_on_disk=5438, blob_last_accessed=1755816785.70087, blob_last_modified=1755816785.6988702)}), refs=frozenset(), last_modified=1755816785.6988702), CachedRevisionInfo(commit_hash='25b47a191e40728efc2437d906e84317f922b36b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/25b47a191e40728efc2437d906e84317f922b36b'), size_on_disk=5426, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/25b47a191e40728efc2437d906e84317f922b36b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/9cdf7b342b249f25f2c2cbe46a2e0a26ded4f692'), size_on_disk=5426, blob_last_accessed=1755815405.3308983, blob_last_modified=1755815405.3298984)}), refs=frozenset(), last_modified=1755815405.3298984), CachedRevisionInfo(commit_hash='aae6e5ea8139e23940d11b4d7e77684c78bb7f6b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/aae6e5ea8139e23940d11b4d7e77684c78bb7f6b'), size_on_disk=4585, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/aae6e5ea8139e23940d11b4d7e77684c78bb7f6b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/4320f8f18620d988a5fc32872a671ace406fd0a2'), size_on_disk=4585, blob_last_accessed=1755814342.1923234, blob_last_modified=1755814342.1913235)}), refs=frozenset(), last_modified=1755814342.1913235), CachedRevisionInfo(commit_hash='a5ecb243ba27ca4b4d6ad1b60cc160c033ca2be6', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/a5ecb243ba27ca4b4d6ad1b60cc160c033ca2be6'), size_on_disk=82276, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/a5ecb243ba27ca4b4d6ad1b60cc160c033ca2be6/features/chr=chrII/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/d64638adb2290412a962abffe0fbd0546ff139b29064bbf313b0a768449454a5'), size_on_disk=82276, blob_last_accessed=1755812630.656903, blob_last_modified=1755812631.0999012)}), refs=frozenset(), last_modified=1755812631.0999012), CachedRevisionInfo(commit_hash='42beb28478e27c5d4c5bb2308b12100e6489ef07', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/42beb28478e27c5d4c5bb2308b12100e6489ef07'), size_on_disk=6125, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/42beb28478e27c5d4c5bb2308b12100e6489ef07/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/ab6cc475062a2767a41b2aaa006faf4d9d2d60d6'), size_on_disk=6125, blob_last_accessed=1759787821.8450553, blob_last_modified=1758155946.5549896)}), refs=frozenset({'main'}), last_modified=1758155946.5549896), CachedRevisionInfo(commit_hash='15fdb72f8c31ae58f3e3ce7c90be279383743a2f', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/15fdb72f8c31ae58f3e3ce7c90be279383743a2f'), size_on_disk=88406, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/15fdb72f8c31ae58f3e3ce7c90be279383743a2f/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/645da8028fd6abe86ee305f76eab78cdf29be364'), size_on_disk=6130, blob_last_accessed=1755819093.2316637, blob_last_modified=1755819093.2306638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/15fdb72f8c31ae58f3e3ce7c90be279383743a2f/features/chr=chrII/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/d64638adb2290412a962abffe0fbd0546ff139b29064bbf313b0a768449454a5'), size_on_disk=82276, blob_last_accessed=1755812630.656903, blob_last_modified=1755812631.0999012)}), refs=frozenset(), last_modified=1755819093.2306638)}), last_accessed=1759787821.8450553, last_modified=1758155946.5549896), CachedRepoInfo(repo_id='BrentLab/hackett_2020', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020'), size_on_disk=817026981, nb_files=22, revisions=frozenset({CachedRevisionInfo(commit_hash='4fef197cd055065207d66ea42aec9746b23f38c8', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/4fef197cd055065207d66ea42aec9746b23f38c8'), size_on_disk=9431, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/4fef197cd055065207d66ea42aec9746b23f38c8/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/0a3330d889673abc635b58a78c221ba5b3e36200'), size_on_disk=9431, blob_last_accessed=1765809790.786152, blob_last_modified=1765650535.7166286)}), refs=frozenset({'main'}), last_modified=1765650535.7166286), CachedRevisionInfo(commit_hash='2c196866d4f057cb5cf0adf1fe35f2c712c61025', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025'), size_on_disk=404565376, files=frozenset({CachedFileInfo(file_name='hackett_2020.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/hackett_2020.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4f47951c65c799b5c74ee3782b49c16cbb038d50'), size_on_disk=55, blob_last_accessed=1757105553.3228161, blob_last_modified=1756843395.5719483), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c3e72ccb1b8deba4bbfd18abe6081de7ec3914d9'), size_on_disk=8879, blob_last_accessed=1757105552.290822, blob_last_modified=1757104206.2667632), CachedFileInfo(file_name='parse_mcisaac_data.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/scripts/parse_mcisaac_data.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0'), size_on_disk=4691, blob_last_accessed=1756843395.7579472, blob_last_modified=1756843395.820947), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1760552530.4189773, blob_last_modified=1756843405.225893), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756843395.5059488, blob_last_modified=1756843395.5679483)}), refs=frozenset(), last_modified=1757104206.2667632), CachedRevisionInfo(commit_hash='1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43'), size_on_disk=2678425, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=45/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/db8595cd20dd7440c890c46025cf0643da8ec6be3e15bc18a8759b3fecdc02af'), size_on_disk=349256, blob_last_accessed=1756145253.7795417, blob_last_modified=1756145254.6225288), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=5/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/ed331d9d2b27a0958b12368c7b5d2d3383b111d3d1c39ed0e77b78561dc78b6a'), size_on_disk=348626, blob_last_accessed=1756145253.7845416, blob_last_modified=1756145254.6395288), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=90/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/ebd06af83cc386d9094ce6b8d795b155c76c47c96da1a458743348264f1638ae'), size_on_disk=349917, blob_last_accessed=1756145253.7845416, blob_last_modified=1756145254.7235274), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=30/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/a85bd6b418d9644d9adaa1269c27f97469a4aaee51af63cf1aa041f62cd8ba2c'), size_on_disk=350044, blob_last_accessed=1756145253.7915416, blob_last_modified=1756145254.6385286), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/45cb0687c3ea72e7b32ad17dd799bc38ad02f80b'), size_on_disk=16034, blob_last_accessed=1756138543.6766906, blob_last_modified=1756138543.6746907), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=10/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4a2eb1051cad96c5b39a014f19b092f042b03700cc1b1a40c2de9e7edd73edf9'), size_on_disk=352201, blob_last_accessed=1756145253.7915416, blob_last_modified=1756145254.6375287), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=15/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/51dfb68ed2518f0dd8ab4d4070d4896b6eca912ecb7e4aa773d857e3096020b5'), size_on_disk=351273, blob_last_accessed=1756145217.972108, blob_last_modified=1756142799.7054222), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=20/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/97adc0bb2b436c325555d82bb7f0ffb0e250d73092fcbf03ac0c00fa2b762e51'), size_on_disk=351379, blob_last_accessed=1756145253.7895415, blob_last_modified=1756145254.6265287), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=0/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/409962e1914ac9e9d631e4ae73e04b167822d0915d8d8d4a87703d4c7281ffb9'), size_on_disk=209695, blob_last_accessed=1756145254.7305272, blob_last_modified=1756145254.608529)}), refs=frozenset(), last_modified=1756145254.7235274), CachedRevisionInfo(commit_hash='60b3ecf6a06e709f0397b327ece5c31016303b5c', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/60b3ecf6a06e709f0397b327ece5c31016303b5c'), size_on_disk=404558833, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/60b3ecf6a06e709f0397b327ece5c31016303b5c/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/6f54100c2bb84a6bc059c17e0954dad0ea451666'), size_on_disk=9543, blob_last_accessed=1760552529.9219792, blob_last_modified=1758155372.1328666), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/60b3ecf6a06e709f0397b327ece5c31016303b5c/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1760552530.4189773, blob_last_modified=1756843405.225893)}), refs=frozenset(), last_modified=1758155372.1328666), CachedRevisionInfo(commit_hash='ca2500351e8aa9fc5b996516cd25a60e8298734d', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/ca2500351e8aa9fc5b996516cd25a60e8298734d'), size_on_disk=6294, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/ca2500351e8aa9fc5b996516cd25a60e8298734d/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/ebb31f16ba92f3c0a9eec2aaf4897b3a7662cfad'), size_on_disk=6294, blob_last_accessed=1764169619.6288424, blob_last_modified=1764169578.8420532)}), refs=frozenset(), last_modified=1764169578.8420532), CachedRevisionInfo(commit_hash='6bea8e8497a12fec83927a941ba912a0ec781822', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/6bea8e8497a12fec83927a941ba912a0ec781822'), size_on_disk=8027, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/6bea8e8497a12fec83927a941ba912a0ec781822/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/159b3e518310d0bf928d14a691e41a77d5540aaa'), size_on_disk=8027, blob_last_accessed=1765299157.242701, blob_last_modified=1765299156.9627051)}), refs=frozenset(), last_modified=1765299156.9627051), CachedRevisionInfo(commit_hash='5b09fb31bac250b0bfc099c1bdb32bc2673e754b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/5b09fb31bac250b0bfc099c1bdb32bc2673e754b'), size_on_disk=409731660, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/5b09fb31bac250b0bfc099c1bdb32bc2673e754b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/dacac6a8023596c4c46dce14e80f861d4e4a7401'), size_on_disk=9583, blob_last_accessed=1765309535.4347303, blob_last_modified=1765308942.1208022), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/5b09fb31bac250b0bfc099c1bdb32bc2673e754b/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/10f544e6c4e4da0b411f1084cbe7f9188a842bc6578b699bdf02f3507aff6592'), size_on_disk=409722077, blob_last_accessed=1765311990.5457778, blob_last_modified=1765311990.540778)}), refs=frozenset(), last_modified=1765311990.540778), CachedRevisionInfo(commit_hash='7192b0e2275ba5aa3ffb3752b87f1c3595f2331a', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a'), size_on_disk=404565656, files=frozenset({CachedFileInfo(file_name='parse_mcisaac_data.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/scripts/parse_mcisaac_data.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0'), size_on_disk=4691, blob_last_accessed=1756843395.7579472, blob_last_modified=1756843395.820947), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/f3899cd97da03a3543db10d76c15ee0442bb136e'), size_on_disk=9159, blob_last_accessed=1758145252.5929751, blob_last_modified=1757541416.40237), CachedFileInfo(file_name='hackett_2020.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/hackett_2020.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4f47951c65c799b5c74ee3782b49c16cbb038d50'), size_on_disk=55, blob_last_accessed=1757105553.3228161, blob_last_modified=1756843395.5719483), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756843395.5059488, blob_last_modified=1756843395.5679483), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1760552530.4189773, blob_last_modified=1756843405.225893)}), refs=frozenset(), last_modified=1757541416.40237), CachedRevisionInfo(commit_hash='b9d39535e175295d9cba23489a49fafebbac5ca0', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0'), size_on_disk=404565563, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/03e579e00c632cdb393c0ffafd9d15b069390e5c'), size_on_disk=9066, blob_last_accessed=1757439136.0598524, blob_last_modified=1757105945.6476595), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756843395.5059488, blob_last_modified=1756843395.5679483), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1760552530.4189773, blob_last_modified=1756843405.225893), CachedFileInfo(file_name='hackett_2020.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/hackett_2020.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4f47951c65c799b5c74ee3782b49c16cbb038d50'), size_on_disk=55, blob_last_accessed=1757105553.3228161, blob_last_modified=1756843395.5719483), CachedFileInfo(file_name='parse_mcisaac_data.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/scripts/parse_mcisaac_data.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0'), size_on_disk=4691, blob_last_accessed=1756843395.7579472, blob_last_modified=1756843395.820947)}), refs=frozenset(), last_modified=1757105945.6476595)}), last_accessed=1765809790.786152, last_modified=1765650535.7166286)}), warnings=[])" ] }, - "execution_count": 76, + "execution_count": 2, "metadata": {}, "output_type": "execute_result" } @@ -122,7 +130,7 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -176,7 +184,7 @@ }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -185,16 +193,16 @@ "text": [ "HuggingFace Cache Status (Case 2):\n", "========================================\n", - "✓ Repository BrentLab/hackett_2020 found in cache\n", - " Size: 407.3M\n", - " Revisions: 5\n", - " Files: 17\n", - " Latest revision: 60b3ecf6\n", - " Last accessed: 1758155372.1328666\n", + "✓ Repository BrentLab/mahendrawada_2025 found in cache\n", + " Size: 94.3M\n", + " Revisions: 4\n", + " Files: 8\n", + " Latest revision: af5ac9dc\n", + " Last accessed: 1763578870.280984\n", "\n", " → Case 2 would succeed: Load from local cache\n", "\n", - "Cache efficiency: Using local files avoids re-downloading 407.3M\n" + "Cache efficiency: Using local files avoids re-downloading 94.3M\n" ] } ], @@ -251,7 +259,7 @@ }, { "cell_type": "code", - "execution_count": 79, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -260,20 +268,20 @@ "text": [ "Current HuggingFace Cache Overview:\n", "========================================\n", - "Total cache size: 4.6G\n", - "Number of repositories: 9\n", + "Total cache size: 5.2G\n", + "Number of repositories: 10\n", "\n", "Largest repositories (top 5):\n", " • BrentLab/barkai_compendium: 3.6G (1 revisions)\n", - " • BrentLab/hackett_2020: 407.3M (5 revisions)\n", - " • BrentLab/kemmeren_2014: 319.2M (1 revisions)\n", - " • BrentLab/rossi_2021: 213.7M (7 revisions)\n", - " • BrentLab/hu_2007_reimand_2010: 42.7M (1 revisions)\n", - " ... and 4 more repositories\n", + " • BrentLab/hackett_2020: 817.0M (9 revisions)\n", + " • BrentLab/kemmeren_2014: 319.2M (3 revisions)\n", + " • BrentLab/rossi_2021: 213.7M (9 revisions)\n", + " • BrentLab/mahendrawada_2025: 94.3M (4 revisions)\n", + " ... and 5 more repositories\n", "\n", - "Total revisions across all repos: 29\n", - "Revisions older than 30 days: 0\n", - "Recent revisions (≤30 days): 29\n" + "Total revisions across all repos: 49\n", + "Revisions older than 30 days: 34\n", + "Recent revisions (≤30 days): 15\n" ] } ], @@ -348,7 +356,7 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -546,7 +554,7 @@ }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -555,22 +563,22 @@ "text": [ "Current HuggingFace Cache Status:\n", "===================================\n", - "Total size: 4.6G\n", - "Number of repositories: 9\n", + "Total size: 5.2G\n", + "Number of repositories: 10\n", "\n", "Repository breakdown:\n", - " • BrentLab/mahendrawada_2025: 2.0M (3 revisions)\n", - " • BrentLab/yeast_genome_resources: 114.5K (7 revisions)\n", " • BrentLab/barkai_compendium: 3.6G (1 revisions)\n", - " • BrentLab/rossi_2021: 213.7M (7 revisions)\n", - " • BrentLab/harbison_2004: 12.3K (2 revisions)\n", - " ... and 4 more repositories\n", + " • BrentLab/mahendrawada_2025: 94.3M (4 revisions)\n", + " • BrentLab/hughes_2006: 12.0M (4 revisions)\n", + " • BrentLab/callingcards: 49.6M (3 revisions)\n", + " • BrentLab/kemmeren_2014: 319.2M (3 revisions)\n", + " ... and 5 more repositories\n", "\n", - "Target repository (BrentLab/hackett_2020) cache info:\n", - " Size: 407.3M\n", - " Revisions: 5\n", - " Latest revision: 60b3ecf6\n", - " Last modified: 1758155372.1328666\n" + "Target repository (BrentLab/mahendrawada_2025) cache info:\n", + " Size: 94.3M\n", + " Revisions: 4\n", + " Latest revision: af5ac9dc\n", + " Last modified: 1763578870.280984\n" ] } ], @@ -621,7 +629,7 @@ }, { "cell_type": "code", - "execution_count": 82, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -636,8 +644,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "INFO:__main__:No old revisions found to delete\n", - "INFO:__main__:Found 0 old revisions. Will free 0.0\n", + "INFO:__main__:Found 34 old revisions. Will free 4.6G\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" ] }, @@ -647,9 +654,20 @@ "text": [ "\n", "Cleanup strategy created:\n", - "Expected space freed: 0.0\n", - "Items to delete: 0\n", - "No old files found for cleanup.\n" + "Expected space freed: 4.6G\n", + "Items to delete: 59\n", + "\n", + "Breakdown of items to delete:\n", + " • Blob files: 39\n", + " • Reference files: 0\n", + " • Repository directories: 4\n", + " • Snapshot directories: 16\n", + "\n", + "Sample blob files to delete:\n", + " • /home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0\n", + " • /home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/51dfb68ed2518f0dd8ab4d4070d4896b6eca912ecb7e4aa773d857e3096020b5\n", + " • /home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/f799807a7bf81229b0d063e6c939339c1f5c90f4\n", + " ... and 36 more blob files\n" ] } ], @@ -698,7 +716,7 @@ }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -713,7 +731,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "INFO:__main__:Selected 14 revisions for deletion. Will free 4.0G\n", + "INFO:__main__:Selected 16 revisions for deletion. Will free 3.6G\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" ] }, @@ -723,8 +741,8 @@ "text": [ "\n", "Size-based cleanup strategy:\n", - "Expected space freed: 4.0G\n", - "Items to delete: 30\n", + "Expected space freed: 3.6G\n", + "Items to delete: 43\n", "\n", "Comparing cleanup strategies for 1GB:\n" ] @@ -733,22 +751,9 @@ "name": "stderr", "output_type": "stream", "text": [ - "INFO:__main__:Selected 14 revisions for deletion. Will free 4.0G\n", - "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " • oldest_first : 4.0G (30 items)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:__main__:Selected 1 revisions for deletion. Will free 3.6G\n", + "INFO:__main__:Selected 16 revisions for deletion. Will free 3.6G\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n", + "INFO:__main__:Selected 3 revisions for deletion. Will free 4.0G\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" ] }, @@ -756,14 +761,15 @@ "name": "stdout", "output_type": "stream", "text": [ - " • largest_first : 3.6G (1 items)\n" + " • oldest_first : 3.6G (43 items)\n", + " • largest_first : 4.0G (6 items)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "INFO:__main__:Selected 14 revisions for deletion. Will free 4.0G\n", + "INFO:__main__:Selected 16 revisions for deletion. Will free 3.6G\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" ] }, @@ -771,7 +777,7 @@ "name": "stdout", "output_type": "stream", "text": [ - " • least_used : 4.0G (30 items)\n" + " • least_used : 3.6G (43 items)\n" ] } ], @@ -822,7 +828,7 @@ }, { "cell_type": "code", - "execution_count": 84, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -837,7 +843,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "INFO:__main__:Found 14 unused revisions. Will free 216.4M\n", + "INFO:__main__:Found 31 unused revisions. Will free 642.9M\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" ] }, @@ -847,38 +853,38 @@ "text": [ "\n", "Revision cleanup strategy:\n", - "Expected space freed: 216.4M\n", - "Items to delete: 75\n", + "Expected space freed: 642.9M\n", + "Items to delete: 118\n", "\n", "Breakdown of cleanup:\n", - " • Blob files: 61\n", + " • Blob files: 87\n", " • Reference files: 0\n", " • Repository directories: 0\n", - " • Snapshot directories: 14\n", + " • Snapshot directories: 31\n", "\n", "Per-repository revision analysis:\n", "\n", - " • BrentLab/mahendrawada_2025:\n", - " Total revisions: 3\n", - " Would keep: 2\n", - " Would delete: 1\n", - " Keep: 8bea431b (modified: 1758155849.9076133)\n", - " Keep: d602c165 (modified: 1757439229.7949307)\n", - " Delete: 66e0cf1f (modified: 1756858618.1383443)\n", - "\n", - " • BrentLab/yeast_genome_resources:\n", - " Total revisions: 7\n", - " Would keep: 2\n", - " Would delete: 5\n", - " Keep: 42beb284 (modified: 1758155946.5549896)\n", - " Keep: 15fdb72f (modified: 1755819093.2306638)\n", - " Delete: 7441b9a8 (modified: 1755816785.6988702)\n", - "\n", " • BrentLab/barkai_compendium:\n", " Total revisions: 1\n", " Would keep: 1\n", " Would delete: 0\n", - " Keep: a987ef37 (modified: 1756926783.3167186)\n" + " Keep: a987ef37 (modified: 1756926783.3167186)\n", + "\n", + " • BrentLab/mahendrawada_2025:\n", + " Total revisions: 4\n", + " Would keep: 2\n", + " Would delete: 2\n", + " Keep: af5ac9dc (modified: 1763578870.280984)\n", + " Keep: 3b912489 (modified: 1758652945.6395862)\n", + " Delete: 874a5dfe (modified: 1758569041.2879846)\n", + "\n", + " • BrentLab/hughes_2006:\n", + " Total revisions: 4\n", + " Would keep: 2\n", + " Would delete: 2\n", + " Keep: 10095469 (modified: 1764713263.741364)\n", + " Keep: 0de73d09 (modified: 1764712744.7960804)\n", + " Delete: cc20720c (modified: 1756856029.230347)\n" ] } ], @@ -935,7 +941,7 @@ }, { "cell_type": "code", - "execution_count": 85, + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -957,12 +963,11 @@ "name": "stderr", "output_type": "stream", "text": [ - "INFO:__main__:No old revisions found to delete\n", - "INFO:__main__:Found 0 old revisions. Will free 0.0\n", + "INFO:__main__:Found 34 old revisions. Will free 4.6G\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n", - "INFO:__main__:Found 14 unused revisions. Will free 216.4M\n", + "INFO:__main__:Found 31 unused revisions. Will free 642.9M\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n", - "INFO:__main__:Automated cleanup complete. Total freed: 206.4MB\n" + "INFO:__main__:Automated cleanup complete. Total freed: 4.9GB\n" ] }, { @@ -971,11 +976,11 @@ "text": [ "\n", "Automated cleanup executed 2 strategies:\n", - " 1. Strategy freed: 0.0\n", - " 2. Strategy freed: 216.4M\n", + " 1. Strategy freed: 4.6G\n", + " 2. Strategy freed: 642.9M\n", "\n", - "Total space that would be freed: 206.4MB\n", - "Cache size after cleanup: 4.1GB\n" + "Total space that would be freed: 4.9GB\n", + "Cache size after cleanup: 0B\n" ] } ], @@ -1024,7 +1029,7 @@ }, { "cell_type": "code", - "execution_count": 86, + "execution_count": 12, "metadata": {}, "outputs": [ { @@ -1037,9 +1042,9 @@ "Demonstrating cache cleanup performance...\n", "\n", "1. Cache scanning performance:\n", - " Time to scan cache: 0.111 seconds\n", - " Repositories found: 9\n", - " Total cache size: 4.6G\n", + " Time to scan cache: 0.095 seconds\n", + " Repositories found: 10\n", + " Total cache size: 5.2G\n", "\n", "2. Cleanup strategy creation performance:\n" ] @@ -1048,8 +1053,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "INFO:__main__:No old revisions found to delete\n", - "INFO:__main__:Found 0 old revisions. Will free 0.0\n", + "INFO:__main__:Found 34 old revisions. Will free 4.6G\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" ] }, @@ -1057,29 +1061,16 @@ "name": "stdout", "output_type": "stream", "text": [ - " Age cleanup strategy: 0.109 seconds\n" + " Age cleanup strategy: 0.099 seconds\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "INFO:__main__:Selected 14 revisions for deletion. Will free 4.0G\n", - "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " Size cleanup strategy: 0.105 seconds\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:__main__:Found 14 unused revisions. Will free 216.4M\n", + "INFO:__main__:Selected 16 revisions for deletion. Will free 3.6G\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n", + "INFO:__main__:Found 31 unused revisions. Will free 642.9M\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" ] }, @@ -1087,10 +1078,11 @@ "name": "stdout", "output_type": "stream", "text": [ - " Revision cleanup strategy: 0.111 seconds\n", + " Size cleanup strategy: 0.097 seconds\n", + " Revision cleanup strategy: 0.096 seconds\n", "\n", "Performance insights:\n", - "• Cache scanning is fast: 0.111s for 9 repos\n", + "• Cache scanning is fast: 0.095s for 10 repos\n", "• Cleanup strategy creation is efficient\n", "• Dry runs allow safe preview of cleanup operations\n", "• Multiple strategies can be compared quickly\n" @@ -1148,7 +1140,7 @@ }, { "cell_type": "code", - "execution_count": 87, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -1219,7 +1211,7 @@ }, { "cell_type": "code", - "execution_count": 88, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -1258,10 +1250,10 @@ " cache_mgr.clean_cache_by_size('10GB', strategy='oldest_first')\n", "\n", "Current Session State:\n", - "Repository: BrentLab/hackett_2020\n", + "Repository: BrentLab/mahendrawada_2025\n", "DuckDB tables: 0\n", - "HF cache size: 4.6G\n", - "Cache repositories: 9\n" + "HF cache size: 5.2G\n", + "Cache repositories: 10\n" ] } ], @@ -1318,7 +1310,7 @@ }, { "cell_type": "code", - "execution_count": 89, + "execution_count": 15, "metadata": {}, "outputs": [ { @@ -1334,7 +1326,7 @@ " • Check repository access permissions\n", "\n", "2. Cache Space and Performance Issues:\n", - " Current cache size: 4.6G\n", + " Current cache size: 5.2G\n", " • Use auto_clean_cache() for automated management\n", " • Monitor cache growth with scan_cache_dir()\n", " • Set appropriate size limits for your system\n", @@ -1351,7 +1343,7 @@ "\n", "Cache Health Check:\n", "✓ DuckDB connection: DuckDB OK\n", - "✓ Cache access: 9 repositories found\n" + "✓ Cache access: 10 repositories found\n" ] }, { @@ -1370,7 +1362,7 @@ "✓ Cache cleanup methods: Working\n", "\n", "Current Status:\n", - "Repository: BrentLab/hackett_2020\n", + "Repository: BrentLab/mahendrawada_2025\n", "Logger configured: True\n", "Cache management ready: ✓\n" ] diff --git a/docs/tutorials/correlation_tutorial.ipynb b/docs/tutorials/correlation_tutorial.ipynb deleted file mode 100644 index 5c3d5d4..0000000 --- a/docs/tutorials/correlation_tutorial.ipynb +++ /dev/null @@ -1,3420 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "fcdcb13a", - "metadata": {}, - "source": [ - "# Hackett 2020 vs Kemmeren Correlation Analysis Tutorial\n", - "\n", - "This tutorial demonstrates how to conduct comprehensive correlation analysis between Hackett 2020 (mcisaac) and Kemmeren datasets using tfbpapi, focusing on regulator-target relationships and temporal dynamics.\n", - "\n", - "## Overview\n", - "\n", - "Correlation analysis evalute how well the effects of the same regulators correlate between these two experimental approaches, and whether timing affects the correlation strength.\n", - "\n", - "### Datasets Used\n", - "- **Perturbation data**: McIsaac (ZEV system), Kemmeren (overexpression)\n", - "\n", - "### Analysis Strategy\n", - "1. Load and filter Hackett 2020 data (time=15, mechanism=\"zev\", restriction=\"p\") and Kemmeren 2014 data\n", - "2. Identify shared regulators between Hackett and Kemmeren\n", - "3. For each shared regulator, calculate Spearman correlations:\n", - " - Hackett effect vs Kemmeren effect\n", - " - Hackett effect vs Kemmeren p-value\n", - "4. Generate scatter plots for specific individual regulator\n", - "5. Create distribution plots (boxplots) across all shared regulators\n", - "6. Analyze temporal dynamics by comparing different timepoints in Hackett data" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "30abaca0", - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chase/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", - " from .autonotebook import tqdm as notebook_tqdm\n" - ] - } - ], - "source": [ - "import pandas as pd\n", - "import numpy as np\n", - "from tfbpapi.HfQueryAPI import HfQueryAPI\n", - "from tfbpapi.datainfo.datacard import DataCard\n", - "from tfbpapi.errors import DataCardError, HfDataFetchError\n", - "from scipy.stats import spearmanr\n", - "import matplotlib.pyplot as plt\n", - "import seaborn as sns\n", - "\n", - "# Configure plotting\n", - "plt.style.use('default')\n", - "sns.set_palette(\"husl\")" - ] - }, - { - "cell_type": "markdown", - "id": "12941f4d", - "metadata": {}, - "source": [ - "## Dataset Exploration with DataCard\n", - "\n", - "Before loading data, let's explore dataset structure and metadata using the DataCard interface." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "c9b9afdf", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Exploring dataset structures...\n", - "\n", - "==================================================\n", - "Exploring BrentLab/hackett_2020\n", - "Dataset types in configs: {'annotated_features'}\n", - "Configurations: 1\n", - "\n", - "Config: hackett_2020\n", - " Type: annotated_features\n", - " Features: 17\n", - " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", - " Target fields: ['target_locus_tag', 'target_symbol']\n", - " Metadata fields: ['regulator_locus_tag', 'regulator_symbol', 'time', 'mechanism', 'restriction', 'date', 'strain']\n", - "\n", - "==================================================\n", - "Exploring BrentLab/kemmeren_2014\n", - "Dataset types in configs: {'annotated_features'}\n", - "Configurations: 1\n", - "\n", - "Config: kemmeren_2014\n", - " Type: annotated_features\n", - " Features: 11\n", - " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", - " Target fields: ['target_locus_tag', 'target_symbol']\n", - " Metadata fields: ['regulator_locus_tag', 'regulator_symbol']\n", - "\n", - "==================================================\n", - "Successfully explored 2 datasets\n" - ] - } - ], - "source": [ - "# Explore dataset structure using DataCard\n", - "print(\"Exploring dataset structures...\")\n", - "\n", - "datasets_to_explore = [\n", - " \"BrentLab/hackett_2020\",\n", - " \"BrentLab/kemmeren_2014\"\n", - "]\n", - "\n", - "dataset_info = {}\n", - "\n", - "for repo_id in datasets_to_explore:\n", - " try:\n", - " print(f\"\\n{'='*50}\")\n", - " print(f\"Exploring {repo_id}\")\n", - "\n", - " # Create DataCard instance\n", - " datacard = DataCard(repo_id)\n", - " card = datacard.dataset_card\n", - "\n", - " # DatasetCard doesn't have dataset_type directly - it's on individual configs\n", - " dataset_types = [config.dataset_type.value for config in card.configs]\n", - " print(f\"Dataset types in configs: {set(dataset_types)}\")\n", - " print(f\"Configurations: {len(card.configs)}\")\n", - "\n", - " # Store dataset info for later use\n", - " dataset_info[repo_id] = {\n", - " 'datacard': datacard,\n", - " 'card': card,\n", - " 'configs': {config.config_name: config for config in card.configs}\n", - " }\n", - "\n", - " # Display configuration details\n", - " for config in card.configs:\n", - " print(f\"\\nConfig: {config.config_name}\")\n", - " print(f\" Type: {config.dataset_type.value}\")\n", - " print(f\" Features: {len(config.dataset_info.features) if config.dataset_info.features else 0}\")\n", - "\n", - " if config.dataset_info.features:\n", - " # Show regulator and target fields if available\n", - " feature_names = [f.name for f in config.dataset_info.features]\n", - " regulator_fields = [f for f in feature_names if 'regulator' in f.lower()]\n", - " target_fields = [f for f in feature_names if 'target' in f.lower()]\n", - "\n", - " if regulator_fields:\n", - " print(f\" Regulator fields: {regulator_fields}\")\n", - " if target_fields:\n", - " print(f\" Target fields: {target_fields}\")\n", - "\n", - " if config.metadata_fields:\n", - " print(f\" Metadata fields: {config.metadata_fields}\")\n", - "\n", - " except (DataCardError, HfDataFetchError) as e:\n", - " print(f\"Error exploring {repo_id}: {e}\")\n", - " continue\n", - " except Exception as e:\n", - " print(f\"Unexpected error exploring {repo_id}: {e}\")\n", - " continue\n", - "\n", - "print(f\"\\n{'='*50}\")\n", - "print(f\"Successfully explored {len(dataset_info)} datasets\")" - ] - }, - { - "cell_type": "markdown", - "id": "cf0265f0", - "metadata": {}, - "source": [ - "## Initialize dataset connections and load data" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "a6b0424f", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Initializing HfQueryAPI connections...\n", - "All API connections initialized\n" - ] - } - ], - "source": [ - "# Initialize dataset connections and load data\n", - "print(\"Initializing HfQueryAPI connections...\")\n", - "\n", - "hackett_2020 = HfQueryAPI(repo_id=\"BrentLab/hackett_2020\")\n", - "kemmeren_2014 = HfQueryAPI(repo_id=\"BrentLab/kemmeren_2014\")\n", - "\n", - "print(\"All API connections initialized\")" - ] - }, - { - "cell_type": "markdown", - "id": "6e1b7269", - "metadata": {}, - "source": [ - "## Get metadata from each dataset to find common regulators" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "baafb468", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Getting metadata from datasets...\n", - "All metadata loaded\n", - "Found 185 common regulators across all datasets\n" - ] - } - ], - "source": [ - "# Get metadata from each dataset to find common regulators\n", - "print(\"Getting metadata from datasets...\")\n", - "\n", - "kemmeren_meta = kemmeren_2014.get_metadata(\"kemmeren_2014\") \n", - "hackett_2020_meta = hackett_2020.get_metadata(\"hackett_2020\")\n", - "\n", - "print(\"All metadata loaded\")\n", - "\n", - "# Get the intersection of common regulators\n", - "common_regulators = (set(hackett_2020_meta.regulator_symbol.unique())\n", - " & set(kemmeren_meta.regulator_symbol.unique()))\n", - "\n", - "print(f\"Found {len(common_regulators)} common regulators across all datasets\")\n", - "\n", - "# Create proper SQL IN clause\n", - "if common_regulators:\n", - " regulator_clause = \"(\" + \", \".join(f\"'{reg}'\" for reg in common_regulators) + \")\"" - ] - }, - { - "cell_type": "markdown", - "id": "2d89d745", - "metadata": {}, - "source": [ - "## Filter the data" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "c888c477", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Applying dataset-specific filters...\n", - "All filters applied\n" - ] - } - ], - "source": [ - "# Apply dataset-specific filters\n", - "print(\"Applying dataset-specific filters...\")\n", - "\n", - "hackett_2020.set_sql_filter(\n", - " \"hackett_2020\",\n", - " f\"\"\"\n", - " time = 15 \n", - " AND mechanism = 'ZEV' \n", - " AND restriction = 'P' \n", - " AND regulator_symbol IN {regulator_clause}\n", - " \"\"\")\n", - "\n", - "print(\"All filters applied\")" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "050e3f4c", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Loading both perturbation datasets...\n", - "Both datasets loaded\n", - "hackett: 145 regulators\n", - "kemmeren: 1487 regulators\n", - "Final intersection: 145 regulators\n", - "Common regulators: ['ACA1', 'ADA2', 'AFT2', 'ARO80', 'ARR1', 'ASH1', 'AZF1', 'BDF2', 'BMH1', 'CAD1']...\n" - ] - } - ], - "source": [ - "# Load both perturbation datasets to find common regulators\n", - "print(\"Loading both perturbation datasets...\")\n", - "\n", - "# Load both datasets\n", - "hackett_data = hackett_2020.query(\"SELECT * FROM hackett_2020\", \"hackett_2020\")\n", - "kemmeren_data = kemmeren_2014.query(\"SELECT * FROM kemmeren_2014\", \"kemmeren_2014\")\n", - "\n", - "print(\"Both datasets loaded\")\n", - "\n", - "# Check what regulators we actually got from each dataset\n", - "actual_regulators = {\n", - " 'hackett': set(hackett_data.regulator_symbol.unique()),\n", - " 'kemmeren': set(kemmeren_data.regulator_symbol.unique()),\n", - "}\n", - "\n", - "for name, regulators in actual_regulators.items():\n", - " print(f\"{name}: {len(regulators)} regulators\")\n", - "\n", - "# Find the intersection - regulators present in BOTH datasets\n", - "final_common = set.intersection(*actual_regulators.values())\n", - "print(f\"Final intersection: {len(final_common)} regulators\")\n", - "\n", - "if final_common:\n", - " final_common_clause = \"(\" + \", \".join(f\"'{reg}'\" for reg in final_common) + \")\"\n", - " print(f\"Common regulators: {sorted(list(final_common))[:10]}...\") # Show first 10\n", - "else:\n", - " print(\"WARNING: No common regulators found!\")\n", - " final_common_clause = \"()\"\n" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "06293141", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Finding common targets...\n", - "Found 6063 common targets\n", - "Loading final datasets with complete filtering...\n", - "Final datasets loaded with complete filtering\n", - "McIsaac: 891,408 rows, 145 regulators\n", - "Kemmeren: 880,730 rows, 145 regulators\n" - ] - } - ], - "source": [ - "# Find common targets across all datasets\n", - "print(\"Finding common targets...\")\n", - "\n", - "common_targets = (set(hackett_data.target_symbol.unique())\n", - " & set(kemmeren_data.target_symbol.unique()))\n", - "\n", - "print(f\"Found {len(common_targets)} common targets\")\n", - "\n", - "if common_targets:\n", - " target_clause = \"(\" + \", \".join(f\"'{tgt}'\" for tgt in common_targets) + \")\"\n", - "\n", - "# Load final datasets with both regulator and target filtering \n", - "print(\"Loading final datasets with complete filtering...\")\n", - "\n", - "\n", - "# Load final datasets with both regulator and target filtering \n", - "hackett_final = hackett_2020.query(f\"\"\"\n", - " SELECT * FROM hackett_2020\n", - " WHERE regulator_symbol IN {final_common_clause} \n", - " AND target_symbol IN {target_clause}\n", - "\"\"\", \"hackett_2020\")\n", - "\n", - "kemmeren_final = kemmeren_2014.query(f\"\"\"\n", - " SELECT * FROM kemmeren_2014\n", - " WHERE regulator_symbol IN {final_common_clause} \n", - " AND target_symbol IN {target_clause}\n", - "\"\"\", \"kemmeren_2014\")\n", - "\n", - "print(\"Final datasets loaded with complete filtering\")\n", - "\n", - "# Print final dataset sizes\n", - "datasets_info = [\n", - " ('McIsaac', hackett_final), ('Kemmeren', kemmeren_final)\n", - "]\n", - "\n", - "for name, data in datasets_info:\n", - " if len(data) > 0:\n", - " regulators = data['regulator_symbol'].nunique() if 'regulator_symbol' in data.columns else 0\n", - " print(f\"{name}: {len(data):,} rows, {regulators} regulators\")\n", - " else:\n", - " print(f\"{name}: No data loaded\")" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "549e73ae", - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "regulator_locus_tag", - "rawType": "object", - "type": "string" - }, - { - "name": "regulator_symbol", - "rawType": "object", - "type": "string" - }, - { - "name": "reporterId", - "rawType": "object", - "type": "string" - }, - { - "name": "target_locus_tag", - "rawType": "object", - "type": "string" - }, - { - "name": "target_symbol", - "rawType": "object", - "type": "string" - }, - { - "name": "M", - "rawType": "float64", - "type": "float" - }, - { - "name": "Madj", - "rawType": "float64", - "type": "float" - }, - { - "name": "A", - "rawType": "float64", - "type": "float" - }, - { - "name": "pval", - "rawType": "float64", - "type": "float" - }, - { - "name": "variable_in_wt", - "rawType": "bool", - "type": "boolean" - }, - { - "name": "multiple_probes", - "rawType": "bool", - "type": "boolean" - } - ], - "ref": "31ae3af5-8283-4e49-9dc6-2dbd59140d0f", - "rows": [ - [ - "0", - "YBL008W", - "HIR1", - "SCAB000002", - "Q0017", - "Q0017", - "-0.039025753", - "-0.0397208098656576", - "5.666642", - "0.591335", - "False", - "False" - ], - [ - "1", - "YBL008W", - "HIR1", - "SCAB000004", - "Q0045", - "COX1", - "-0.10270845", - "-0.104404483712687", - "7.1872175", - "0.278744", - "False", - "False" - ], - [ - "2", - "YBL008W", - "HIR1", - "SCAB000005", - "Q0050", - "AI1", - "-0.09215408", - "-0.101312394230742", - "7.9818127", - "0.789989", - "True", - "False" - ], - [ - "3", - "YBL008W", - "HIR1", - "SCAB000006", - "Q0055", - "AI2", - "-0.11363403", - "-0.123208021645201", - "9.3025734", - "0.695235", - "True", - "False" - ], - [ - "4", - "YBL008W", - "HIR1", - "SCAB000007", - "Q0060", - "AI3", - "-0.0099832871", - "-0.0186756321118991", - "8.3151407", - "0.975177", - "False", - "False" - ], - [ - "5", - "YBL008W", - "HIR1", - "SCAB000008", - "Q0065", - "AI4", - "-0.16157419", - "-0.173001391865694", - "11.104408", - "0.508279", - "True", - "False" - ], - [ - "6", - "YBL008W", - "HIR1", - "SCAB000009", - "Q0070", - "AI5_ALPHA", - "-0.29374796", - "-0.306969229210268", - "10.721268", - "0.127481", - "True", - "False" - ], - [ - "7", - "YBL008W", - "HIR1", - "SCAB000010", - "Q0075", - "AI5_BETA", - "-0.048776282", - "-0.0512431882361536", - "7.3602157", - "0.788852", - "True", - "False" - ], - [ - "8", - "YBL008W", - "HIR1", - "SCAB000011", - "Q0080", - "ATP8", - "-0.18210337", - "-0.187242319447809", - "6.7582501", - "0.227365", - "True", - "False" - ], - [ - "9", - "YBL008W", - "HIR1", - "SCAB000012", - "Q0085", - "ATP6", - "-0.0065067789", - "-0.00701312899803988", - "6.4296849", - "0.9677", - "False", - "False" - ], - [ - "10", - "YBL008W", - "HIR1", - "SCAB000014", - "Q0105", - "COB", - "-0.023712626", - "-0.0241945156649505", - "5.6905146", - "0.819055", - "False", - "False" - ], - [ - "11", - "YBL008W", - "HIR1", - "SCAB000015", - "Q0110", - "BI2", - "-0.10026133", - "-0.102682551857025", - "6.3605368", - "0.538407", - "False", - "False" - ], - [ - "12", - "YBL008W", - "HIR1", - "SCAB000016", - "Q0115", - "BI3", - "-0.053808146", - "-0.0569359410492525", - "6.6718358", - "0.725973", - "False", - "False" - ], - [ - "13", - "YBL008W", - "HIR1", - "SCAB000017", - "Q0120", - "BI4", - "-0.020458971", - "-0.0236853183042711", - "6.9095507", - "0.927896", - "False", - "False" - ], - [ - "14", - "YBL008W", - "HIR1", - "SCAB000018", - "Q0130", - "OLI1", - "-0.043537368", - "-0.0566553964261391", - "12.556363", - "0.890456", - "True", - "False" - ], - [ - "15", - "YBL008W", - "HIR1", - "SCAB000019", - "Q0140", - "VAR1", - "-0.16343272", - "-0.163938440697184", - "6.1672218", - "0.188679", - "True", - "False" - ], - [ - "16", - "YBL008W", - "HIR1", - "SCAB000023", - "Q0160", - "SCEI", - "-0.11115273", - "-0.112545724325415", - "6.40361", - "0.233242", - "False", - "False" - ], - [ - "17", - "YBL008W", - "HIR1", - "SCAB000024", - "Q0182", - "Q0182", - "-0.074097777", - "-0.07502283029592", - "5.8703247", - "0.373117", - "False", - "False" - ], - [ - "18", - "YBL008W", - "HIR1", - "SCAB000025", - "Q0250", - "COX2", - "-0.093127871", - "-0.0967308066694721", - "7.4043715", - "0.275569", - "False", - "False" - ], - [ - "19", - "YBL008W", - "HIR1", - "SCAB000026", - "Q0255", - "Q0255", - "-0.031337077", - "-0.0317708668814822", - "5.6274759", - "0.677195", - "False", - "False" - ], - [ - "20", - "YBL008W", - "HIR1", - "SCAB000027", - "Q0275", - "COX3", - "-0.018126893", - "-0.0174743777233046", - "6.0614997", - "0.861477", - "False", - "False" - ], - [ - "21", - "YBL008W", - "HIR1", - "SCAB000028", - "Q0297", - "Q0297", - "-0.0015863937", - "-0.00165756742970601", - "6.2053847", - "0.99118", - "False", - "False" - ], - [ - "22", - "YBL008W", - "HIR1", - "SCAB000029", - "YAL001C", - "TFC3", - "-0.060650037", - "-0.0567581406799775", - "9.7950907", - "0.415566", - "False", - "False" - ], - [ - "23", - "YBL008W", - "HIR1", - "SCAB000030", - "YAL002W", - "VPS8", - "-0.0074215245", - "-0.00566180001033513", - "8.3969858", - "0.930872", - "False", - "False" - ], - [ - "24", - "YBL008W", - "HIR1", - "SCAB000031", - "YAL003W", - "EFB1", - "-0.029623397", - "-0.03985574724458", - "14.921067", - "0.84173", - "False", - "False" - ], - [ - "25", - "YBL008W", - "HIR1", - "SCAB000032", - "YAL004W", - "YAL004W", - "0.059369123", - "0.0561266464793721", - "6.6928234", - "0.54419", - "False", - "False" - ], - [ - "26", - "YBL008W", - "HIR1", - "SCAB000033", - "YAL005C", - "SSA1", - "0.042630331", - "0.0487909166720867", - "15.675554", - "0.862133", - "False", - "False" - ], - [ - "27", - "YBL008W", - "HIR1", - "SCAB000034", - "YAL007C", - "ERP2", - "-0.060302507", - "-0.0583166518417961", - "11.623729", - "0.346134", - "False", - "False" - ], - [ - "28", - "YBL008W", - "HIR1", - "SCAB000035", - "YAL008W", - "FUN14", - "-0.089632662", - "-0.0758621939217628", - "9.7471534", - "0.251776", - "False", - "False" - ], - [ - "29", - "YBL008W", - "HIR1", - "SCAB000036", - "YAL009W", - "SPO7", - "0.061596497", - "0.059807970651245", - "9.5949268", - "0.337943", - "False", - "False" - ], - [ - "30", - "YBL008W", - "HIR1", - "SCAB000037", - "YAL010C", - "MDM10", - "0.033553389", - "0.0347720388006548", - "7.8775572", - "0.707184", - "False", - "False" - ], - [ - "31", - "YBL008W", - "HIR1", - "SCAB000038", - "YAL011W", - "SWC3", - "-0.053778196", - "-0.0547258310698601", - "6.1043641", - "0.478414", - "False", - "False" - ], - [ - "32", - "YBL008W", - "HIR1", - "SCAB000039", - "YAL012W", - "CYS3", - "-0.067001533", - "-0.0704603092262692", - "12.296266", - "0.589873", - "False", - "False" - ], - [ - "33", - "YBL008W", - "HIR1", - "SCAB000040", - "YAL013W", - "DEP1", - "0.045037505", - "0.0422594728589327", - "8.8906158", - "0.705922", - "False", - "False" - ], - [ - "34", - "YBL008W", - "HIR1", - "SCAB000041", - "YAL014C", - "SYN8", - "-0.0028853882", - "-0.003421632139455", - "10.140393", - "0.984162", - "False", - "False" - ], - [ - "35", - "YBL008W", - "HIR1", - "SCAB000042", - "YAL015C", - "NTG1", - "0.17305066", - "0.178185020644813", - "9.5554939", - "0.010956", - "False", - "False" - ], - [ - "36", - "YBL008W", - "HIR1", - "SCAB000043", - "YAL016W", - "TPD3", - "-0.084858514", - "-0.0803126247904039", - "11.337088", - "0.270546", - "False", - "False" - ], - [ - "37", - "YBL008W", - "HIR1", - "SCAB000044", - "YAL017W", - "PSK1", - "-0.081218414", - "-0.0672602695566325", - "9.9839762", - "0.450345", - "False", - "False" - ], - [ - "38", - "YBL008W", - "HIR1", - "SCAB000045", - "YAL018C", - "LDS1", - "-0.0015791723", - "-0.00059224976381921", - "5.9934205", - "0.990055", - "False", - "False" - ], - [ - "39", - "YBL008W", - "HIR1", - "SCAB000046", - "YAL019W", - "FUN30", - "0.1520406", - "0.148431864200967", - "10.781851", - "0.0493148", - "False", - "False" - ], - [ - "40", - "YBL008W", - "HIR1", - "SCAB000047", - "YAL020C", - "ATS1", - "-0.020495554", - "-0.0238614082255441", - "9.3828017", - "0.836546", - "False", - "False" - ], - [ - "41", - "YBL008W", - "HIR1", - "SCAB000048", - "YAL021C", - "CCR4", - "-0.13337841", - "-0.13693929825126", - "9.4790413", - "0.171857", - "False", - "False" - ], - [ - "42", - "YBL008W", - "HIR1", - "SCAB000049", - "YAL022C", - "FUN26", - "-0.097163876", - "-0.0973296431760643", - "9.893272", - "0.226213", - "False", - "False" - ], - [ - "43", - "YBL008W", - "HIR1", - "SCAB000050", - "YAL023C", - "PMT2", - "-0.027986767", - "-0.0333328913664413", - "9.0967819", - "0.821875", - "False", - "False" - ], - [ - "44", - "YBL008W", - "HIR1", - "SCAB000051", - "YAL024C", - "LTE1", - "-0.13226007", - "-0.135436445273697", - "8.4398933", - "0.045112", - "False", - "False" - ], - [ - "45", - "YBL008W", - "HIR1", - "SCAB000052", - "YAL025C", - "MAK16", - "0.17071987", - "0.159309256880021", - "11.301871", - "0.0211462", - "False", - "False" - ], - [ - "46", - "YBL008W", - "HIR1", - "SCAB000053", - "YAL026C", - "DRS2", - "-0.1904308", - "-0.190251792611949", - "10.91405", - "0.00111783", - "False", - "False" - ], - [ - "47", - "YBL008W", - "HIR1", - "SCAB000054", - "YAL027W", - "SAW1", - "-0.18881836", - "-0.192964020762811", - "8.2938543", - "7.6856e-06", - "False", - "False" - ], - [ - "48", - "YBL008W", - "HIR1", - "SCAB000055", - "YAL028W", - "FRT2", - "-0.053743754", - "-0.0418562759081939", - "7.9800474", - "0.599747", - "False", - "False" - ], - [ - "49", - "YBL008W", - "HIR1", - "SCAB000056", - "YAL029C", - "MYO4", - "-0.025923023", - "-0.0246822755213326", - "10.036795", - "0.842811", - "False", - "False" - ] - ], - "shape": { - "columns": 11, - "rows": 880730 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
regulator_locus_tagregulator_symbolreporterIdtarget_locus_tagtarget_symbolMMadjApvalvariable_in_wtmultiple_probes
0YBL008WHIR1SCAB000002Q0017Q0017-0.039026-0.0397215.6666420.591335FalseFalse
1YBL008WHIR1SCAB000004Q0045COX1-0.102708-0.1044047.1872180.278744FalseFalse
2YBL008WHIR1SCAB000005Q0050AI1-0.092154-0.1013127.9818130.789989TrueFalse
3YBL008WHIR1SCAB000006Q0055AI2-0.113634-0.1232089.3025730.695235TrueFalse
4YBL008WHIR1SCAB000007Q0060AI3-0.009983-0.0186768.3151410.975177FalseFalse
....................................
880725YPL202CAFT2SCAB007301YML099W-AYML099W-A-0.141110-0.1164658.0925130.432310FalseFalse
880726YPL202CAFT2SCAB007304YNL067W-AYNL067W-A0.015283-0.0008496.4725350.969828FalseFalse
880727YPL202CAFT2SCAB007305YNL162W-AYNL162W-A0.1181870.0618369.3640320.696226FalseFalse
880728YPL202CAFT2SCAB007306YNR001W-AYNR001W-A0.0279180.0204606.3517640.944834FalseFalse
880729YPL202CAFT2SCAB007307YNR034W-AEGO4-0.1281200.1119417.6171170.846665FalseFalse
\n", - "

880730 rows × 11 columns

\n", - "
" - ], - "text/plain": [ - " regulator_locus_tag regulator_symbol reporterId target_locus_tag \\\n", - "0 YBL008W HIR1 SCAB000002 Q0017 \n", - "1 YBL008W HIR1 SCAB000004 Q0045 \n", - "2 YBL008W HIR1 SCAB000005 Q0050 \n", - "3 YBL008W HIR1 SCAB000006 Q0055 \n", - "4 YBL008W HIR1 SCAB000007 Q0060 \n", - "... ... ... ... ... \n", - "880725 YPL202C AFT2 SCAB007301 YML099W-A \n", - "880726 YPL202C AFT2 SCAB007304 YNL067W-A \n", - "880727 YPL202C AFT2 SCAB007305 YNL162W-A \n", - "880728 YPL202C AFT2 SCAB007306 YNR001W-A \n", - "880729 YPL202C AFT2 SCAB007307 YNR034W-A \n", - "\n", - " target_symbol M Madj A pval variable_in_wt \\\n", - "0 Q0017 -0.039026 -0.039721 5.666642 0.591335 False \n", - "1 COX1 -0.102708 -0.104404 7.187218 0.278744 False \n", - "2 AI1 -0.092154 -0.101312 7.981813 0.789989 True \n", - "3 AI2 -0.113634 -0.123208 9.302573 0.695235 True \n", - "4 AI3 -0.009983 -0.018676 8.315141 0.975177 False \n", - "... ... ... ... ... ... ... \n", - "880725 YML099W-A -0.141110 -0.116465 8.092513 0.432310 False \n", - "880726 YNL067W-A 0.015283 -0.000849 6.472535 0.969828 False \n", - "880727 YNL162W-A 0.118187 0.061836 9.364032 0.696226 False \n", - "880728 YNR001W-A 0.027918 0.020460 6.351764 0.944834 False \n", - "880729 EGO4 -0.128120 0.111941 7.617117 0.846665 False \n", - "\n", - " multiple_probes \n", - "0 False \n", - "1 False \n", - "2 False \n", - "3 False \n", - "4 False \n", - "... ... \n", - "880725 False \n", - "880726 False \n", - "880727 False \n", - "880728 False \n", - "880729 False \n", - "\n", - "[880730 rows x 11 columns]" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "kemmeren_final" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "d48a21fc", - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "regulator_locus_tag", - "rawType": "object", - "type": "string" - }, - { - "name": "regulator_symbol", - "rawType": "object", - "type": "string" - }, - { - "name": "target_locus_tag", - "rawType": "object", - "type": "string" - }, - { - "name": "target_symbol", - "rawType": "object", - "type": "string" - }, - { - "name": "time", - "rawType": "float64", - "type": "float" - }, - { - "name": "mechanism", - "rawType": "object", - "type": "string" - }, - { - "name": "restriction", - "rawType": "object", - "type": "string" - }, - { - "name": "date", - "rawType": "object", - "type": "string" - }, - { - "name": "strain", - "rawType": "object", - "type": "string" - }, - { - "name": "green_median", - "rawType": "float64", - "type": "float" - }, - { - "name": "red_median", - "rawType": "float64", - "type": "float" - }, - { - "name": "log2_ratio", - "rawType": "float64", - "type": "float" - }, - { - "name": "log2_cleaned_ratio", - "rawType": "float64", - "type": "float" - }, - { - "name": "log2_noise_model", - "rawType": "float64", - "type": "float" - }, - { - "name": "log2_cleaned_ratio_zth2d", - "rawType": "float64", - "type": "float" - }, - { - "name": "log2_selected_timecourses", - "rawType": "float64", - "type": "float" - }, - { - "name": "log2_shrunken_timecourses", - "rawType": "float64", - "type": "float" - } - ], - "ref": "58a78ce1-cf9e-4895-aeda-7a6835616ff2", - "rows": [ - [ - "0", - "YER045C", - "ACA1", - "YNL055C", - "POR1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "91090.295", - "124164.75", - "0.406527130762", - "0.0432783723328", - "0.167308051886", - "0.0", - "0.0", - "0.0" - ], - [ - "1", - "YER045C", - "ACA1", - "YIL114C", - "POR2", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "929.4918", - "662.37395", - "-0.0842615914349", - "-0.0977695029636", - "0.129415342895", - "-0.0", - "0.0", - "0.0" - ], - [ - "2", - "YER045C", - "ACA1", - "YPL188W", - "POS5", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "591.31345", - "734.7885", - "-0.0758081091353", - "-0.0508571587889", - "0.107883665412", - "-0.0", - "0.0", - "0.0" - ], - [ - "3", - "YER045C", - "ACA1", - "YIL160C", - "POT1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "205.71685", - "121.719", - "0.5847627015", - "0.1705942734", - "0.215645578247", - "0.1705942734", - "0.170594273399", - "0.00462968359641295" - ], - [ - "4", - "YER045C", - "ACA1", - "YGL205W", - "POX1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "119.5065", - "87.946405", - "0.179056555936", - "0.0200286797035", - "0.198205486203", - "0.0", - "0.0", - "0.0" - ], - [ - "5", - "YER045C", - "ACA1", - "YMR267W", - "PPA2", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "1275.302", - "1416.7", - "0.180721523462", - "0.0987729763456", - "0.104456428231", - "0.0", - "0.0", - "0.0" - ], - [ - "6", - "YER045C", - "ACA1", - "YHR075C", - "PPE1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "1326.4295", - "1359.741", - "0.390555253563", - "0.266483987284", - "0.131385515049", - "0.0", - "0.0", - "0.0" - ], - [ - "7", - "YER045C", - "ACA1", - "YNR032W", - "PPG1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "1099.959", - "1152.4355", - "-0.214915545431", - "-0.204062063627", - "0.117168825607", - "-0.0", - "0.0", - "0.0" - ], - [ - "8", - "YER045C", - "ACA1", - "YDL134C", - "PPH21", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "7493.293", - "7856.0695", - "-0.0775299490434", - "-0.133124758634", - "0.0927194595661", - "-0.0", - "0.0", - "0.0" - ], - [ - "9", - "YER045C", - "ACA1", - "YDL188C", - "PPH22", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "6011.497", - "4252.663", - "0.0215924976055", - "-0.0847941323527", - "0.126466576504", - "-0.0", - "0.0", - "0.0" - ], - [ - "10", - "YER045C", - "ACA1", - "YDR075W", - "PPH3", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "1084.69", - "953.6436", - "-0.143874021717", - "-0.0282806734827", - "0.115360567164", - "-0.0", - "0.0", - "0.0" - ], - [ - "11", - "YER045C", - "ACA1", - "YDR435C", - "PPM1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "1241.2985", - "1179.9235", - "0.125080147412", - "0.0906497470663", - "0.119276410483", - "0.0", - "0.0", - "0.0" - ], - [ - "12", - "YER045C", - "ACA1", - "YOL141W", - "PPM2", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "6.4157995", - "2.552427", - "-1.48508070136", - "-1.37626086393", - "0.520525479607", - "-0.0", - "0.0", - "0.0" - ], - [ - "13", - "YER045C", - "ACA1", - "YDR452W", - "PPN1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "4925.801", - "5224.871", - "-0.0386115679586", - "-0.121743225934", - "0.105781342704", - "-0.0", - "0.0", - "0.0" - ], - [ - "14", - "YER045C", - "ACA1", - "YPL179W", - "PPQ1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "7831.3035", - "5207.4895", - "-0.235458038146", - "-0.246050278661", - "0.134560096602", - "-0.0", - "0.0", - "0.0" - ], - [ - "15", - "YER045C", - "ACA1", - "YLR014C", - "PPR1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "525.5364", - "558.23195", - "-0.0720257307211", - "0.0530460147321", - "0.120093662406", - "0.0", - "0.0", - "0.0" - ], - [ - "16", - "YER045C", - "ACA1", - "YBR276C", - "PPS1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "2419.3925", - "2788.6315", - "0.0122846283333", - "0.0492935299569", - "0.0963579749912", - "0.0", - "0.0", - "0.0" - ], - [ - "17", - "YER045C", - "ACA1", - "YGR123C", - "PPT1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "1271.969", - "756.6555", - "-0.225361085921", - "0.0143144232829", - "0.143078282691", - "0.0", - "0.0", - "0.0" - ], - [ - "18", - "YER045C", - "ACA1", - "YPL148C", - "PPT2", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "219.84405", - "182.46345", - "0.0613811644696", - "0.0627513730773", - "0.156174422974", - "0.0627513730773", - "0.0627513730772", - "0.0" - ], - [ - "19", - "YER045C", - "ACA1", - "YHR201C", - "PPX1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "3845.679", - "3410.8325", - "-0.154114687167", - "-0.117511741551", - "0.0967597414614", - "-0.0", - "0.0", - "0.0" - ], - [ - "20", - "YER045C", - "ACA1", - "YML016C", - "PPZ1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "1342.195", - "1373.8975", - "-0.0362655421877", - "-0.0738714297714", - "0.101152319324", - "-0.0", - "0.0", - "0.0" - ], - [ - "21", - "YER045C", - "ACA1", - "YDR436W", - "PPZ2", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "1525.9705", - "1543.802", - "0.218383737172", - "0.0563081385542", - "0.113157259395", - "0.0", - "0.0", - "0.0" - ], - [ - "22", - "YER045C", - "ACA1", - "YEL060C", - "PRB1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "5911.781", - "5569.027", - "-0.455707646615", - "-0.571860070431", - "0.207328157539", - "-0.0", - "0.0", - "0.0" - ], - [ - "23", - "YER045C", - "ACA1", - "YMR297W", - "PRC1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "36126.48", - "39761.95", - "0.410772124663", - "0.131407081485", - "0.141859292096", - "0.131407081485", - "0.131407081485", - "0.01127754540884046" - ], - [ - "24", - "YER045C", - "ACA1", - "YCL057W", - "PRD1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "5299.106", - "5666.927", - "0.217251799868", - "0.0958561117769", - "0.118813194578", - "0.0", - "0.0", - "0.0" - ], - [ - "25", - "YER045C", - "ACA1", - "YER012W", - "PRE1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "3579.9625", - "3507.407", - "0.257330221212", - "0.212146176027", - "0.117287337021", - "0.0", - "0.0", - "0.0" - ], - [ - "26", - "YER045C", - "ACA1", - "YOR362C", - "PRE10", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "7441.532", - "9475.323", - "0.122188272669", - "0.0718660920117", - "0.103600609006", - "0.0", - "0.0", - "0.0" - ], - [ - "27", - "YER045C", - "ACA1", - "YPR103W", - "PRE2", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "7716.9675", - "10321.0485", - "0.0640417232106", - "0.0126514481209", - "0.0994120132179", - "0.0", - "0.0", - "0.0" - ], - [ - "28", - "YER045C", - "ACA1", - "YJL001W", - "PRE3", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "8069.2035", - "8403.125", - "-0.0893897513213", - "-0.138440536033", - "0.097284953069", - "-0.0", - "0.0", - "0.0" - ], - [ - "29", - "YER045C", - "ACA1", - "YFR050C", - "PRE4", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "6965.228", - "8014.186", - "0.130369797315", - "0.0550630647991", - "0.101602878222", - "0.0", - "0.0", - "0.0" - ], - [ - "30", - "YER045C", - "ACA1", - "YMR314W", - "PRE5", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "9141.1395", - "10320.41", - "0.137077950724", - "0.122101501008", - "0.0963541759471", - "0.0", - "0.0", - "0.0" - ], - [ - "31", - "YER045C", - "ACA1", - "YOL038W", - "PRE6", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "8436.736", - "10381.487", - "0.107476341446", - "0.0669936382585", - "0.105301291198", - "0.0", - "0.0", - "0.0" - ], - [ - "32", - "YER045C", - "ACA1", - "YBL041W", - "PRE7", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "2173.121", - "1985.315", - "-0.0822820646082", - "-0.0939006623829", - "0.105080316319", - "-0.0", - "0.0", - "0.0" - ], - [ - "33", - "YER045C", - "ACA1", - "YML092C", - "PRE8", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "7177.268", - "6808.069", - "-0.257981612196", - "-0.309732696759", - "0.0980797505299", - "-0.0", - "0.0", - "0.0" - ], - [ - "34", - "YER045C", - "ACA1", - "YGR135W", - "PRE9", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "12547.15", - "15473.24", - "0.0691947071468", - "0.0589345565803", - "0.10341285939", - "0.0", - "0.0", - "0.0" - ], - [ - "35", - "YER045C", - "ACA1", - "YIR008C", - "PRI1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "1434.682", - "1462.7145", - "-0.131154719562", - "-0.124372895682", - "0.0957816864817", - "-0.0", - "0.0", - "0.0" - ], - [ - "36", - "YER045C", - "ACA1", - "YKL045W", - "PRI2", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "2744.2565", - "2751.2355", - "-0.0877778595728", - "0.0103854720443", - "0.109576289525", - "0.0103854720443", - "0.0103854720443", - "0.0" - ], - [ - "37", - "YER045C", - "ACA1", - "YIL095W", - "PRK1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "175.32645", - "213.4852", - "-0.306520099792", - "-0.359214715414", - "0.147309738357", - "-0.0", - "0.0", - "0.0" - ], - [ - "38", - "YER045C", - "ACA1", - "YNL279W", - "PRM1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "151.35995", - "60.88811", - "-0.163721814997", - "-0.155100365723", - "0.184526335745", - "-0.0", - "0.0", - "0.0" - ], - [ - "39", - "YER045C", - "ACA1", - "YJL108C", - "PRM10", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "434.85715", - "487.97225", - "0.197827906609", - "0.270562592171", - "0.155702667029", - "0.270562592171", - "0.270562592171", - "0.14228978173358325" - ], - [ - "40", - "YER045C", - "ACA1", - "YMR278W", - "PRM15", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "831.627", - "1182.736", - "-0.0430126710172", - "-0.14258375941", - "0.134093156636", - "-0.0", - "0.0", - "0.0" - ], - [ - "41", - "YER045C", - "ACA1", - "YIL037C", - "PRM2", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "211.582", - "225.26115", - "0.0187691123028", - "-0.028108956191", - "0.127184486471", - "-0.0", - "0.0", - "0.0" - ], - [ - "42", - "YER045C", - "ACA1", - "YPL192C", - "PRM3", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "10.9251915", - "5.660958", - "-0.591203647306", - "-0.509557179708", - "0.446843040608", - "-0.0", - "0.0", - "0.0" - ], - [ - "43", - "YER045C", - "ACA1", - "YPL156C", - "PRM4", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "817.6399", - "1094.5992", - "-0.114741081918", - "-0.0275536173095", - "0.141289574881", - "-0.0275536173095", - "-0.0275536173095", - "-0.0" - ], - [ - "44", - "YER045C", - "ACA1", - "YIL117C", - "PRM5", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "2402.5815", - "3609.9235", - "0.0528869881727", - "0.0523652440494", - "0.123605948584", - "0.0", - "0.0", - "0.0" - ], - [ - "45", - "YER045C", - "ACA1", - "YML047C", - "PRM6", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "10.5794195", - "2.8140655", - "-0.694608414725", - "-0.553824321969", - "0.643974825346", - "-0.0", - "0.0", - "0.0" - ], - [ - "46", - "YER045C", - "ACA1", - "YDL039C", - "PRM7", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "863.883", - "698.3094", - "-0.117281767333", - "0.0781061133085", - "0.177734867942", - "0.0781061133085", - "0.0781061133085", - "0.0" - ], - [ - "47", - "YER045C", - "ACA1", - "YGL053W", - "PRM8", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "823.95815", - "960.38885", - "0.389527968106", - "0.29532151425", - "0.127558963187", - "0.29532151425", - "0.29532151425", - "0.23594077664534263" - ], - [ - "48", - "YER045C", - "ACA1", - "YAR031W", - "PRM9", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "13.66105", - "16.78304", - "0.197818562658", - "0.201583564502", - "0.397577394418", - "0.0", - "0.0", - "0.0" - ], - [ - "49", - "YER045C", - "ACA1", - "YDR300C", - "PRO1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "803.2018", - "742.33955", - "-0.0789609053406", - "0.0695793804634", - "0.113501268363", - "0.0695793804634", - "0.0695793804634", - "0.0" - ] - ], - "shape": { - "columns": 17, - "rows": 891408 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
regulator_locus_tagregulator_symboltarget_locus_tagtarget_symboltimemechanismrestrictiondatestraingreen_medianred_medianlog2_ratiolog2_cleaned_ratiolog2_noise_modellog2_cleaned_ratio_zth2dlog2_selected_timecourseslog2_shrunken_timecourses
0YER045CACA1YNL055CPOR115.0ZEVP20160229SMY204991090.29500124164.7500000.4065270.0432780.1673080.0000000.0000000.00000
1YER045CACA1YIL114CPOR215.0ZEVP20160229SMY2049929.49180662.373950-0.084262-0.0977700.129415-0.0000000.0000000.00000
2YER045CACA1YPL188WPOS515.0ZEVP20160229SMY2049591.31345734.788500-0.075808-0.0508570.107884-0.0000000.0000000.00000
3YER045CACA1YIL160CPOT115.0ZEVP20160229SMY2049205.71685121.7190000.5847630.1705940.2156460.1705940.1705940.00463
4YER045CACA1YGL205WPOX115.0ZEVP20160229SMY2049119.5065087.9464050.1790570.0200290.1982050.0000000.0000000.00000
......................................................
891403YFL052WZNF1YGL087CMMS215.0ZEVP20161117SMY2245923.69900662.4565000.028204-0.0045660.117553-0.0000000.0000000.00000
891404YFL052WZNF1YKL175WZRT315.0ZEVP20161117SMY22455181.167503997.746500-0.136347-0.0541080.096181-0.0000000.0000000.00000
891405YFL052WZNF1YBR046CZTA115.0ZEVP20161117SMY2245466.54105581.0441000.3423840.0141900.1395310.0000000.0000000.00000
891406YFL052WZNF1YGR285CZUO115.0ZEVP20161117SMY224516498.7350017306.235000-0.143440-0.0572460.090285-0.0000000.0000000.00000
891407YFL052WZNF1YNL241CZWF115.0ZEVP20161117SMY22454058.685004520.9095000.013953-0.0964790.159377-0.0000000.0000000.00000
\n", - "

891408 rows × 17 columns

\n", - "
" - ], - "text/plain": [ - " regulator_locus_tag regulator_symbol target_locus_tag target_symbol \\\n", - "0 YER045C ACA1 YNL055C POR1 \n", - "1 YER045C ACA1 YIL114C POR2 \n", - "2 YER045C ACA1 YPL188W POS5 \n", - "3 YER045C ACA1 YIL160C POT1 \n", - "4 YER045C ACA1 YGL205W POX1 \n", - "... ... ... ... ... \n", - "891403 YFL052W ZNF1 YGL087C MMS2 \n", - "891404 YFL052W ZNF1 YKL175W ZRT3 \n", - "891405 YFL052W ZNF1 YBR046C ZTA1 \n", - "891406 YFL052W ZNF1 YGR285C ZUO1 \n", - "891407 YFL052W ZNF1 YNL241C ZWF1 \n", - "\n", - " time mechanism restriction date strain green_median \\\n", - "0 15.0 ZEV P 20160229 SMY2049 91090.29500 \n", - "1 15.0 ZEV P 20160229 SMY2049 929.49180 \n", - "2 15.0 ZEV P 20160229 SMY2049 591.31345 \n", - "3 15.0 ZEV P 20160229 SMY2049 205.71685 \n", - "4 15.0 ZEV P 20160229 SMY2049 119.50650 \n", - "... ... ... ... ... ... ... \n", - "891403 15.0 ZEV P 20161117 SMY2245 923.69900 \n", - "891404 15.0 ZEV P 20161117 SMY2245 5181.16750 \n", - "891405 15.0 ZEV P 20161117 SMY2245 466.54105 \n", - "891406 15.0 ZEV P 20161117 SMY2245 16498.73500 \n", - "891407 15.0 ZEV P 20161117 SMY2245 4058.68500 \n", - "\n", - " red_median log2_ratio log2_cleaned_ratio log2_noise_model \\\n", - "0 124164.750000 0.406527 0.043278 0.167308 \n", - "1 662.373950 -0.084262 -0.097770 0.129415 \n", - "2 734.788500 -0.075808 -0.050857 0.107884 \n", - "3 121.719000 0.584763 0.170594 0.215646 \n", - "4 87.946405 0.179057 0.020029 0.198205 \n", - "... ... ... ... ... \n", - "891403 662.456500 0.028204 -0.004566 0.117553 \n", - "891404 3997.746500 -0.136347 -0.054108 0.096181 \n", - "891405 581.044100 0.342384 0.014190 0.139531 \n", - "891406 17306.235000 -0.143440 -0.057246 0.090285 \n", - "891407 4520.909500 0.013953 -0.096479 0.159377 \n", - "\n", - " log2_cleaned_ratio_zth2d log2_selected_timecourses \\\n", - "0 0.000000 0.000000 \n", - "1 -0.000000 0.000000 \n", - "2 -0.000000 0.000000 \n", - "3 0.170594 0.170594 \n", - "4 0.000000 0.000000 \n", - "... ... ... \n", - "891403 -0.000000 0.000000 \n", - "891404 -0.000000 0.000000 \n", - "891405 0.000000 0.000000 \n", - "891406 -0.000000 0.000000 \n", - "891407 -0.000000 0.000000 \n", - "\n", - " log2_shrunken_timecourses \n", - "0 0.00000 \n", - "1 0.00000 \n", - "2 0.00000 \n", - "3 0.00463 \n", - "4 0.00000 \n", - "... ... \n", - "891403 0.00000 \n", - "891404 0.00000 \n", - "891405 0.00000 \n", - "891406 0.00000 \n", - "891407 0.00000 \n", - "\n", - "[891408 rows x 17 columns]" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "hackett_final" - ] - }, - { - "cell_type": "markdown", - "id": "81a946c6", - "metadata": {}, - "source": [ - "## Individual regulator correlation analysis and scatter plots\n", - "\n", - "Now we'll analyze correlations for each regulator individually, focusing on the relationship between Hackett effects and Kemmeren effects/p-values.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "75cc1bb9", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'regulator_symbol': 'ACA1', 'n_targets': 6075, 'eff_eff_corr': np.float64(0.01608402414610723), 'eff_eff_pval': np.float64(0.21004245167836072), 'eff_pval_corr': np.float64(-0.017860635301765383), 'eff_pval_pval': np.float64(0.1639456246498038)}\n", - "Correlation analysis function finished!\n" - ] - } - ], - "source": [ - "import numpy as np\n", - "from scipy.stats import spearmanr\n", - "\n", - "# Set up plotting parameters\n", - "plt.rcParams['figure.figsize'] = (12, 5)\n", - "plt.rcParams['font.size'] = 10\n", - "\n", - "# Example regulator symbol\n", - "chosen_regulator_symbol = 'ACA1'\n", - " \n", - "# Filter data for the specific regulator\n", - "hackett_reg = hackett_final[hackett_final['regulator_symbol'] == chosen_regulator_symbol].copy()\n", - "kemmeren_reg = kemmeren_final[kemmeren_final['regulator_symbol'] == chosen_regulator_symbol].copy()\n", - " \n", - "# Check if data was found\n", - "if len(hackett_reg) == 0 or len(kemmeren_reg) == 0:\n", - " print(f\"No data found for regulator {chosen_regulator_symbol}\")\n", - " \n", - "# Merge datasets on target_symbol\n", - "merged = pd.merge(\n", - " hackett_reg[['target_symbol', 'log2_shrunken_timecourses']].rename(columns={'log2_shrunken_timecourses': 'effect_hackett'}),\n", - " kemmeren_reg[['target_symbol', 'Madj', 'pval']].rename(columns={'Madj': 'effect_kemmeren', 'pval': 'pvalue_kemmeren'}),\n", - " on='target_symbol',\n", - " how='inner'\n", - ")\n", - " \n", - "if len(merged) < 3: # Need at least 3 points for correlation\n", - " print(f\"Insufficient data points ({len(merged)}) for regulator {chosen_regulator_symbol}\")\n", - "\n", - " \n", - "# Calculate correlations\n", - "# Remove any NaN values\n", - "clean_data = merged.dropna(subset=['effect_hackett', 'effect_kemmeren', 'pvalue_kemmeren'])\n", - " \n", - "if len(clean_data) < 3:\n", - " print(f\"Insufficient clean data points ({len(clean_data)}) for regulator {chosen_regulator_symbol}\")\n", - " \n", - "# Spearman correlations\n", - "eff_eff_corr, eff_eff_pval = spearmanr(clean_data['effect_hackett'], clean_data['effect_kemmeren'])\n", - "eff_pval_corr, eff_pval_pval = spearmanr(clean_data['effect_hackett'], clean_data['pvalue_kemmeren'])\n", - " \n", - "correlation_results = {\n", - " 'regulator_symbol': chosen_regulator_symbol,\n", - " 'n_targets': len(clean_data),\n", - " 'eff_eff_corr': eff_eff_corr,\n", - " 'eff_eff_pval': eff_eff_pval,\n", - " 'eff_pval_corr': eff_pval_corr,\n", - " 'eff_pval_pval': eff_pval_pval\n", - "}\n", - " \n", - "print(correlation_results)\n", - "print(\"Correlation analysis function finished!\")\n" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "b9324fd2", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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KEJDQ4Wnp6do3ry5WLt2rdi5c6fNA9/c3Fzx7rvvigYNGpjaN3XqVHH37l2rxwshxPz580WDBg2Eu7u71YFrXFyc6Nu3rwgMDBQeHh6iYcOGYvr06Rb9FaLsBr56vV5ERESUOBgVQoht27YJAKJnz55m2w0Gg1i/fr14/PHHRXh4uPDw8BDVqlUTTZs2FS+++KI4ceKEEEKIrVu3lthuI+MslLVr15q2RUZGmmagWPuKjY0t8bxEREREReHY1RzHrhy7EpF9FCGEKHkRBSIieuCBB7Bv3z6kpqaa7vJLREREROSMOHYlInJtGkc3gIjI2SQlJVlsW7NmDfbu3YsePXpw0EtEREREToNjVyKiyokzbYmICqlevTpatWqFe++9F1qtFkeOHMGuXbvg5+eHvXv3olmzZo5uIhERERERAI5diYgqKxZtiYgK+ec//4nvv/8ely5dQkZGBkJCQtCtWzdMnz4dMTExjm4eEREREZEJx65ERJUTi7ZEREREREREREREToRr2hIRERERERERERE5ERZtiYiIiIiIiIiIiJwIi7ZEVGX997//RevWreHn5wdFUfDSSy/ZtI/sk5eXh5kzZ6JBgwbw9PSEoijYuHFjifuIiIiIqhKOTSunCxcuQFEUjBkzxtFNISIXw6ItEbk840CouK969eqZPWbfvn0YPnw40tLS8Nxzz2HGjBno06dPifvKy65du6AoCmbOnFmuz1Naq1atKjHrwgPSBQsWYNasWQgPD8e0adMwY8YM000xittXXmbOnAlFUbBr165yfR4iIiKqmjg2rTjWxqbe3t6IiYnBP/7xDyQnJzu6iURE0twc3QAiorJSv359jBgxwuq+wMBAs+83b94MIQS++OIL3HfffTbvI9VDDz2EBx54wOq+li1bmn3/ww8/wNfXF9u2bYOHh4fN+4iIiIhcGcemFafg2PTmzZvYunUr/v3vf+Pbb7/Fn3/+ierVqzu4hURE9mPRlogqjXvuucfm2QBXr14FAISHh9u1j1Q9evTA66+/btOxV69eRfXq1a0WZYvbR0REROTKODatOIXHpnl5eejduzd27tyJJUuWOP2MYSIia7g8AhFVKcaPesXGxgIAoqKiTB+lMn68ytq+CxcumM6RkJCAp59+GnXr1oWnpyfCwsIwZswYXLx40epzxsfHY8KECYiKioKnpydq1qyJrl27YtWqVQDUj+t369YNADBr1iyzj3cVfN7CVq9eDUVRMHv2bKv7Dx06BEVRMHz4cNO2s2fPYuzYsaa2BAcHo0WLFnjppZcghLA1RpsYlyFISEjAxYsXzT4OWNy+guLi4vDoo4+iRo0a8PT0RIMGDfDWW28hMzPT6nPGxcXh8ccfR2hoKDw9PVGnTh0MHDgQv/76KwCga9eumDVrFgCgW7duRT4vERERUUXg2LR8xqbu7u6YOHEiAODAgQPFHjt+/HgoioK4uDir+xcuXAhFUfCf//zHtG3lypXo378/6tWrBy8vLwQHB5uKxLaqV69ekWPQrl27QlEUi+1CCKxcuRL3338//P39Ua1aNbRt2xYrV660+XmJyHVwpi0RVSn16tXDjBkzsHHjRhw9ehRTpkwxfTytZcuWRe4z/vn777+jd+/eyMjIwCOPPIIGDRrgwoULWLt2LX788Ufs27cP0dHRpuf79ddf0a9fP9y9exe9e/fGE088gTt37uDw4cNYvHgxxowZg65du+LChQv4/PPP0aVLF3Tt2tX0+MIfnSto4MCBeO6557B27Vq8/fbbFvtXr14NABg5ciQAdZZG+/btkZGRgX79+mHYsGHIyMjA2bNnsWzZMnzwwQdwcyu7fxaM/Vi0aBEAmG6YERgYaFpCwdo+o48//hiTJk1CYGAgHn30UdSsWRMHDx7Eu+++i507d2Lnzp1mM3QXL16Ml19+Gd7e3hgwYADq1q2LxMRE/Prrr1i/fj0eeOAB03q7u3fvxujRo00D5eJyJiIiIiovHJuW/9jUWvGzoJEjR2LlypVYs2YNOnfubLXdnp6eGDJkiGnbpEmT0KJFC/To0QMhISFITEzExo0b0aNHD3z77bfo379/qdtdmBACw4cPx3//+180aNAATz31FDw8PLBt2zaMHz8eJ06cwAcffFDmz0tEDiSIiFxcQkKCACDq168vZsyYYfXrxx9/NHvM6NGjBQCRkJBgcb6i9uXm5op69eoJPz8/cejQIbN9e/bsEVqtVjzyyCOmbdnZ2aJ27dpCo9FYPL8QQly+fNn09507dwoAYsaMGXb1fcSIEQKA+P33382263Q6ERoaKmrVqiV0Op0QQogPP/xQABCLFi2yOM+tW7dser7Y2FgBQDz00ENFZn3y5Emzx0RGRorIyEir5ytq399//y3c3NxEixYtRHJystm+uXPnCgDigw8+MG07cuSI0Gg0Ijw83OJ1MxgMIjEx0fT9jBkzBACxc+dOm/pMREREZA+OTSt+bDp37lyz7Xl5eaJ79+4CgJg1a1ax5zAYDKJu3boiKChIZGdnm+3766+/BAAxePBgs+3x8fEW57l69aoIDw8XDRo0MNtuvB5Gjx5ttr24MXKXLl1E4XLNJ598IgCIsWPHitzcXNP2nJwc8eijjwoA4uDBg8X2lYhcC2faElGlcf78edNH3wubMmVKqe+y+8MPP+DChQuYPXs2WrVqZbbvgQceQP/+/bFx40akpaXB398fmzZtQmJiIkaNGmX1uSMiIkrVHkCdGbBmzRqsWbMG7du3N23/+eefcf36dbz00kvQarVmj/H29rY4T3BwsF3Pu2PHDuzYscPqvpYtWyImJsau8xW2YsUK6HQ6LFmyxOLGEa+++ioWLlyI//73v5g6darpeIPBgHfeecfiY2aKonANOCIiIqpwHJtW3Nh0+/btyM7OBgDcunULW7duxdmzZxEVFYUXXnih2Mcal2yYO3cuNm/ejIEDB5r2GWcHF76hXFRUlMV5wsLCMGjQICxZsgQXL15EZGSkXX0oydKlS+Hj44OPPvoI7u7upu0eHh5499138f333+O///0v2rRpU6bPS0SOw6ItEVUavXv3xk8//VRu59+/fz8A4PTp01ZvZnDt2jUYDAacOXMGbdu2xR9//AEA6NWrV7m16aGHHkJYWBi++uorLFy40PQRsjVr1gDI//gZADz66KN44403MGnSJOzYsQN9+vRBly5dzD4yZ6u5c+fafCMyGcast27darU47O7ujlOnTpm+r4isiYiIiOzBsWnFjU0LTijw9PREvXr18I9//ANvvPGGqQBsLaOXXnoJgYGBGDlyJObOnYvVq1ebirYGgwFffvklqlevjocfftjscfHx8Zg7dy5++eUXJCYmIicnx2z/1atXy7Rom5mZib/++gvh4eF47733LPbn5eUBgNn4mIhcH4u2REQ2un37NgBg7dq1xR6XkZEBAEhNTQUA1K5du9zapNVq8dRTT2HBggXYunUr+vXrh/T0dGzcuBH33nsvWrdubTq2Xr162L9/P2bOnIktW7bgm2++AQDExMRg9uzZZut0OZox63fffdem41NTU6EoCsLCwsqzWUREREROg2PTfLZMKLA263nMmDEIDAxE48aN0aZNG2zZsgV37txBUFAQdu3ahStXruD55583m9l67tw5tG/fHmlpaejWrRseffRR+Pv7Q6PRYNeuXdi9e7dFEbe07ty5AyEEEhMTi5y9DeS/1kRUOWgc3QAiIlfh7+8PAPj+++8hhCjyq0uXLgDyb9SQmJhYru0yzlgwzmDYsGEDMjMzzWYyGDVt2hTr16/H7du3sW/fPrz99tu4du0ahg0bhr1795ZrO+1hzDotLa3YrI0CAwMhhEBSUpKjmkxERERUoTg2tY+1bAouqzVy5Ejk5uaaiseFb5xm9O9//xt37tzBqlWrsG3bNixatAizZ8/GzJkz7VoiTKPRQKfTWd1nLLAbGV/rNm3aFPta79y50+bnJyLnx6ItEZGNOnToAADYt2+fTccb1/H6+eefSzzWuLaXXq+3u10tWrRAs2bNsGnTJty9exdr1qwxrc1VFHd3d3Ts2BGzZs3Chx9+CCEEfvjhB7ufu7wYszZ+7K8kFZU1ERERkbPg2LRsPfnkk3Bzc8OaNWuQlZWFb7/9Fvfccw86duxodtz58+cBAP379zfbLoSwq9AcFBSEGzduWBRuMzIycPbsWbNtfn5+aNy4MU6ePImUlBQ7ekVEroxFWyIiG/Xv3x9169bFwoULERcXZ7E/Ly8Pv/76q+n7xx57DBEREVizZg22bt1qcXzBWQ7GtbYuX74s1baRI0ciKysLH374IX755Rd06dIFderUMTvmzz//RFpamsVjr1+/DgDw8vKSeu7y8Pzzz8PNzQ2TJ0/GpUuXLPanpKTg8OHDpu+fffZZaLVavPXWW7h48aLZsUIIXL161fR9abMmIiIicgYcm5atmjVrolevXti7dy8WLVqEtLQ0ixuQATCtVVswWwCYN28ejh8/bvPztWvXDnl5eWbLWwgh8MYbb1hd5uDFF19EZmYmnnnmGav7ExIScOHCBZufn4icH9e0JaJK49y5c1ZvMGD0+uuvl2rw5+npifXr16Nv377o0qULunfvjmbNmkFRFFy8eBF79uxB9erVTTcA8PT0xDfffIM+ffqgb9++6NOnD1q0aIG0tDQcOXIEmZmZpsJjTEwMwsPD8dVXX8HT0xMRERFQFAWTJ09GQEBAiW176qmn8Prrr2PWrFkwGAxWP362evVqrFixAp07d0b9+vXh7++PEydOYMuWLQgODsbYsWNtzqLgHXoLq1WrFp599lmbz2VN06ZNsWzZMjz33HNo1KgRHn74YdSvXx93795FfHw8du/ejTFjxmD58uUAgGbNmmHRokV48cUX0aRJEzz++OOIjIzEtWvXEBcXh379+mHRokUAgG7dukFRFLz55pv4+++/ERAQgMDAwBLvLExERERkD45NK25sWlZGjhyJLVu2YMaMGQBgtWj77LPPIjY2FoMGDcLQoUNRvXp17N+/H4cOHUK/fv2wefNmm57rhRdeQGxsLJ5++mls27YNISEh2LNnD1JSUtCiRQscPXrU7PiJEydi//79+Pzzz7F371706NED4eHhuH79Ok6dOoXff/8dX375pdmSD0Tk4gQRkYtLSEgQAEr8unPnjukxo0ePFgBEQkKCxfmK2yeEEFeuXBFTpkwRDRo0EJ6ensLf3180btxYPP3002LHjh0Wx587d06MHz9eRERECHd3d1GzZk3RtWtX8cUXX5gdt3//ftGlSxfh5+dnanNRbbCmR48eAoDw8vISqampFvv3798vJk6cKJo2bSoCAwOFt7e3aNCggXjhhRfExYsXbXqO2NjYEnNu0aKF2WMiIyNFZGSk1fMVt08IIf744w/xxBNPiPDwcOHu7i5q1KghWrduLV5//XVx8uRJi+N37twpHnnkEREcHCw8PDxERESEGDRokNi7d6/ZcatWrRLNmjUTnp6eAkCxbSAiIiKyB8emqoocm86dO9fmdhUnMzNT+Pv7CwCiU6dORR63c+dOcf/99ws/Pz8RGBgoHn74YfHnn3+KGTNmCABi586dpmON18Po0aMtzvPLL7+IDh06CE9PT1G9enUxcuRIcf36ddGlSxdRVLnm66+/Fj169BBBQUHC3d1d1K5dW3Tt2lUsWLBA3Lx5s7QREJETUYQocCcXIiIiIiIiIiIiInIormlLRERERERERERE5ERYtCUiIiIiIiIiIiJyIizaEhERERERERERETkRFm2JiIiIiIiIiIiInAiLtkREREREREREREROhEVbIiIiIiIiIiIiIifi5ugGOJLBYMDVq1fh5+cHRVEc3RwiIiIikiCEwN27dxEeHg6NpnLOSeC4lYiIiKhysHXsWqWLtlevXkWdOnUc3QwiIiIiKgOXL19GRESEo5tRLjhuJSIiIqpcShq7VumirZ+fHwA1JH9//xKPNxgMSEpKQlhYWKWdxVFemJ08ZieP2cljdvKYnTxmJ6+qZ5eWloY6deqYxnaVkb3jVoDXRWkwO3nMTh6zk8fs5DE7ecxOXlXPztaxqyKEEBXUJqeTlpaGgIAApKam2jz4JSIiIiLnUhXGdFWhj0RERERVga3juqpXzi4FvV6PkydPQq/XO7opLofZyWN28pidPGYnj9nJY3bymB1Zw+tCHrOTx+zkMTt5zE4es5PH7OQxO9uwaGsHIQRSU1NRhScnS2N28pidPGYnj9nJY3bymJ08ZkfW8LqQx+zkMTt5zE4es5PH7OQxO3nMzjYs2hIRERERERERERE5ERZtiYiIiIiIiIiIiJwIi7Z20Gg0iI6OrpJ3tistZieP2cljdvKYnTxmJ4/ZyWN2ZA2vC3nMTh6zk8fs5DE7ecxOHrOTx+xso4gqvIAE78JLRERE5PqqwpiuKvSRiIiIqCqwdVzHkrYd9Ho9jh49yrvbSWB28pidPGYnj9nJY3bymJ08ZkfW8LqQx+zkMTt5zE4es5PH7OQxO3nMzjYs2tpBCIGsrCze3U4Cs5PH7OQxO3nMTh6zk8fs5DE7sobXhTxmJ4/ZyWN28pidPGYnj9nJY3a2YdGWiIiIiIiIiIiIyImwaEtERERERERERETkRFi0tYNWq0VMTAy0Wq2jm+JymJ08ZieP2cljdvKYnTxmJ4/ZkTW8LuQxO3nMTh6zk8fs5DE7ecxOHrOzjSKq8AISvAsvERERkeurCmO6qtBHIiIioqrA1nEdZ9raQafT4cCBA9DpdI5uisthdvKYnTxmJ4/ZyWN28pidPGZH1vC6kMfs5DE7ecxOHrOTx+zkMTt5zM42LNraSa/XO7oJLovZyWN28pidPGYnj9nJY3bymB1Zw+tCHrOTx+zkMTt5zE4es5PH7OQxu5KxaEtERERERERERETkRNwc3YCqRq/X49ChQzhz5gwyMzMd3ZwKo9frcfHiRRw5coQLTdvJmbLz8fFBs2bN0LRpUyiK4tC2EBERuZKZM2di1qxZZtsaNWqEU6dOOahFtklPT8fevXuRlJSEvLw8RzenwjjT+MvVOHt2bm5uqFGjBu6//34EBwc7ujlERERF4o3I7LihgxACWVlZ8Pb2lipYbd68GYsWLcKdO3fg7e2NatWqVanClxCiSvW3rOh0gMEgoNEocHPgr1kMBgPS09ORm5uL0NBQ/POf/8R9993nuAbZoLTv2aqM2cljdvKYnbyqnp0r3KRr5syZWL9+PbZv327aZiwe2UKmj6W5LnQ6HebNm4fNmzcjLy8P/v7+8PDwsOscro5jV0k6HYTBAEWjgUMHr0XIy8tDWloaFEXBAw88gHfeeQfVqlVzdLMA8Gd5aTA7ecxOHrOT51TZJSQA168DoaFAVFSFPKWt4zrn+1fUyckOVrds2YKZM2eiV69eGD58OBo3buz4C7MCFfzdQFXqd2nduQMkJwsYDIBGA9SooSAoyHHtMRgMOHLkCGJjYzF16lQsWrQIHTp0cFyDbFDV/oNZlpidPGYnj9nJY3bOz83NDbVq1arQ55S5LoQQeOutt7Br1y48//zz6NOnD2rWrFkOrXNeHLtKunMHIjkZxsGrUqMGHDp4LUJKSgp++eUXLF68GJMnT8ayZcvg6enp6GYB4M/y0mB28pidPGYnzymy27ABiI0F0tIAf39g7Fhg0CBHt8qERVs76PV6HDx4EG3btoWbHb81NhgMWLp0Kbp3745//etf0Giq5lLCGRkZ8PHxcXQzXEZODpCcrP49ODgDKSk+SE4GqlUDHDWm1Gg0aN26NZo1a4Znn30WK1ascOqirex7lphdaTA7ecxOHrNzDWfPnkV4eDi8vLzQqVMnzJ07F3Xr1rV6bE5ODnJyckzfp6WlAVBnwBrvtKzRaKDRaGAwGGAwGEzHGrfn5ubi0KFDaN26NbRarWm7Xq83K0pqtVooimI674kTJ7B9+3bMmjULDz/8MADzImZRFEWxepyzbbeFEAKZmZnw8fFxqraXpk/l3pYCg9eM4GBUS00Fbt0CvL0BT0+n6lNgYCAGDBiA+vXrY/z48di5cyd69epV7Pup8PvG1vdTwe2A5Y13Cm43LqXXunVreHp6QghhdryiKNBqtRZtLGp7ST8jKqJPBbm5uZVbnwwGAw4dOoRWrVqZLcvhyn2qqNep8L8VlaFPFfU6Gd+z7dq1s/h546p9Kq7tZdknnU6HgwcPmq47h/Tp4kVoVq2CRgjoY2Igrl0DvvgCaNkS2ujocn2dbMVRfQU4fvw4bty4gWHDhlXZgi3ZLy8P0OvVcS4AeHgAWVnqdkdPBHB3d8egQYPw9ttv4/r16wgNDXVsg4iIiJxchw4dsGrVKjRq1AhJSUmYNWsWHnzwQRw/fhx+fn4Wx8+dO9diDVwAOHz4sOmX4CEhIahfvz4SEhJw8+ZN0zERERGIiIjAuXPnkJKSgkOHDkFRFERHR6NmzZo4fvw4srKyTMfHxMQgMDAQhw8fhl6vxzfffAMfHx/07NkTgPqL94J8fHxgMBjMzqEoCnx8fKDX65GdnW3artFoUK1aNeh0OrMitFarhbe3N/Ly8pCbm2va7ubmBi8vL+Tk5Jj9R8nDwwMeHh7Izs42+4+Pp6cn3N3dkZWVZfafMy8vL7i5uSEzM9PsP5be3t7QaDQ29cn4PJWpT+X6Oun18HB3h7uiwKDRIDMgQB3M5uTAU6t1yj7Vr18f9evXx9q1a9GgQYNi309nzpxBamqqabut7yej5s2bw8PDAwcPHjTrU9u2bZGbm4tjx45BCIGUlBQcPXoU7du3R2pqqtm6197e3mjRogWSk5MRHx9v2h4QEIDGjRvj6tWruHLliml7ST8jKqJPBXNv165dufUpLCwMAHDu3DncvXu3UvSpol6no0ePmv6tcHNzqxR9qqjXSQhh+rlTWfoEVMzrlJ2dbTZGcUif7txBdGAganp64njTpshq0wbIyADOnEFM9erl+jpF2bgMA9e0tWNtMONvAuydxfLtt99i7ty5+P3336ts0VYIYZppy4+Y2SYnB7h0CQAEAgPVmbaAgrp1HV+0BYCkpCQ8+uijWLJkCTp16uTo5lgl+54lZlcazE4es5NX1bNzhTVtC0tJSUFkZCQWLlyI8ePHW+y3NtO2Tp06uHXrlqmPJc0mycnJkZpp+9JLL8HDwwMLFiywq0/ONvu0rGbaWuOKfSr3tuTkAJcvAwAyAgNRLTUVCgBERDjdTNuC2xcuXIjffvsN69atc/jMQM605UxbzrR1rdeJM23l++Q0M21fegkagwH62rXVmbaKAixcWO4zbTMyMrimrbPIzs6Gl5dXlS3YkhxPT6BGDfVTZXq9+rOjenXnKNgCMP0npuBv2IiIiMg2gYGBaNiwIc6dO2d1v6enp9U1Nt3c3CwK88b/iBRm/E+U8T/hBbdbYzwmNzcXQUFBUr9oL+oxzrbdHs7Wdqftk5cXUKMGxP8PXhUASvXq6nbJ89tDtu0+Pj7IysoyvYeKez9ZU9L7yd7txves8e/Wji+qjfZur6g+FVRefTIWVgr/vDNyxT4ZlffrZO3fClfvU0W+TsafJZWpT0bl2SdFUayOUSq0T/XrA2PGALGx0J46pa5pO26cul2iT0VtL6pPtmAV0Q5arRZt27Yt8gItTnEDkb/++guDBw9GZGQkvLy8ULt2bfTs2RNLliwpTXOdDtezzZeTk4PXXnsN4eHh8Pb2RocOHbBt2zaL44KCgDp1AF9fH9Spk38fh8TERAwdOhSBgYHw9/dH//79zT4qYPTxxx9jyJAhqFu3LhRFwZgxY4pt1/bt29G9e3cEBATAz88Pbdq0wddff10WXXaI0rxnqzpmJ4/ZyWN28pid60lPT8f58+dNH+ktDxy7lg7HriVLSUnBhAkTEBISAh8fH3QbOBCHkpPh4+sLs8FrEU6ePIk+ffrA19cXwcHBGDlypNnHWY3effddPPbYYwgNDYWiKJg5c2aR59y+fTu6deuGGjVqIDAwEO3bt8fq1auLPN6ZPgXIn+XymJ08ZieP2clzmuwGDQKWLAHmz1f/HDjQse0phEVbOxVcy6ks/Pbbb2jbti2OHj2KZ555BkuXLsXTTz8NjUaDxYsXl+lzOVrBqelV3ZgxY7Bw4UIMHz4cixcvhlarxcMPP4xff/3V4lgPD8Db2wDjjRXT09PRrVs37N69G2+++SZmzZqFw4cPo0uXLrh165bZY9977z388ssvaNKkSYm/2YmNjUWvXr3g7u6OOXPm4P3330fnzp1x+f8/5uaqyvo9W5UwO3nMTh6zk8fsnNu0adOwe/duXLhwAb/99hsGDBgArVaLJ598slyfl2NXeRy7Fs9gMKBfv3748ssv8cILL2D+/Pm4ceMGuvXqhdNXrgAl3BX8ypUr6Ny5M86dO4c5c+Zg2rRp2Lx5M3r27Glx3b711ls4cOAAWrVqVew5//e//6FXr17Izc3FzJkz8e6778Lb2xujRo3Cv//971L3uSLwZ7k8ZieP2cljdvKcJruoKKBjR/VPJ8PlEeyg1+tx7NixMl0v7t1330VAQAAOHDiAwMBAs303btwok+coDzqdDgaDAR4lDMYKysrK4owFAH/88Qe++uorvP/++5g2bRoAYNSoUWjatCleffVV/PbbbxaPKZjdsmXLcPbsWfzxxx9o164dAKBv375o2rQpFixYgDlz5pget3v3btMsW19f3yLbdOHCBUyaNAmTJ0+uVP/hKo/3bFXB7OQxO3nMTh6zc35XrlzBk08+iVu3biEkJAQPPPAA9u/fj5CQkHJ7To5d83HsWvbWr19vWgt28ODBAIChQ4eiYcOGePvtt0v8tNacOXOQkZGBP//8E3Xr1gUAtG/fHj179sSqVaswYcIE07EJCQmoV68ekpOTi33PLF26FGFhYfjll19My4tMnDgRMTExWLVqFV5++eXSdrtc8We5PGYnj9nJY3bymJ1tONPWwc6fP48mTZpYDHoBoGbNmmbfK4qCF154AWvXrkWjRo3g5eWFNm3aIC4uzuKxiYmJGDduHEJDQ+Hp6YkmTZpg5cqVZsfk5ubi7bffRps2bRAQEAAfHx88+OCD2Llzp9lxFy5cgKIo+OCDD7Bo0SLUr18fnp6eOHHiBGbOnAlFUXDmzBmMGDECAQEBCAkJwfTp0yGEwOXLl9G/f38EBASgfv36Fje0kGnDJ598YmpDu3btcODAAZvz/uWXX9C4cWN4eHggICAAY8aMQUpKis2PLwvr16+HVqs1G4h6eXlh/Pjx2LdvX4kzW9evX4927dqZCraAerfGhx56CN98843ZsZGRkTZ95Gv58uXQ6/WYPXs2AHU2bxW+RyEREVGZ++qrr3D16lXk5OTgypUr+Oqrr1C/wJpproJj16o3di3K+vXrERoaioEFPkoaEhKCIUOGYPPmzWY30rNmw4YNeOSRR0wFWwDo0aMHGjZsaDGmrVevnk1tSktLQ1BQkNl60G5ubqhRowa8vb1tOgcREZGzYDnbwSIjI7Fv3z4cP34cTZs2LfH43bt34+uvv8aLL74IT09PLFu2DH369MEff/xhevz169fRsWNH00A5JCQEP/74I8aPH4+0tDS89NJLANRBzaeffoonn3wSzzzzDO7evYvPPvsMvXv3xh9//IGWLVuaPXdsbCyys7MxYcIEeHp6Ijg42LRv2LBhaNy4MebNm4fNmzfjnXfeQXBwMFasWIHu3btj3rx5WL16NV555RW0b98enTt3lmrDl19+ibt372LixIlQFAXz58/HwIEDER8fD3d392Kz++WXX9CrVy/cd999eP7553H69Gl8/PHHSEhIwK5du4otbubk5ODu3bslvj4AUKNGjWL3Hz58GA0bNrS4Q2D79u0BAEeOHEGdOnWsPtZgMODYsWMYN26cxb727dvj559/xt27d+Hn52dTW422b9+OmJgYbNmyBa+88goSExMRFBSESZMmYdasWbyJHhEREQHg2LUqjl2LcvjwYbRu3dpi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HVnD66J0OP6Sx+zk8D0rj9nJY3bymJ08ZmcbFm3JZMeOHRg3bhwaNmyIatWqITo6Gk8//TSSkpJsevzp06fx8ssv47777oOXlxcURcGFCxeKPP5///sfWrduDS8vL9StWxczZsyATqezOC4lJQUTJkxASEgIfHx80K1bNxw6dEi2m8X67bff8MADD6BatWqoVasWXnzxRaSnp5sdk56ejhkzZqBPnz4IDg6GoihYtWpVubSHiIiIiKxLSkrC66+/jm7dusHPzw+KomDXrl12n+frr79Gp06d4OPjg8DAQNx333345ZdfTPuzsrIwfvx4NG3aFAEBAfD19UWLFi2wePFi5OXlWZxv27ZtpvFkUFAQBg8eXOyYuDQ4diUiIqq8WLQlk9deew27du3CgAED8OGHH+KJJ57AN998g1atWuHatWslPn7fvn348MMPcffu3RLXJvnxxx/x+OOPIzAwEEuWLMHjjz+Od955B5MnTzY7zmAwoF+/fvjyyy/xwgsvYP78+bhx4wa6du2Ks2fPlqq/hR05cgQPPfQQMjMzsXDhQjz99NP45JNPMGTIELPjkpOTMXv2bJw8eRItWrQo0zYQERERkW1Onz6N9957D4mJiWjWrJnUOWbOnIknn3wSderUwcKFC/HOO++gefPmSExMNB2TlZWFv//+Gw8//DDmzp2LDz74AC1atMDLL7+M0aNHm53vhx9+QJ8+fZCTk4N58+Zh6tSp2L17Nx544AHcvHmzVP0tjGNXIiKiyo03IrODRqNBSEgINJrKWeteuHAhHnjgAbP+9enTB126dMHSpUvxzjvvFPv4xx57DCkpKfDz88MHH3yAI0eOmO13c8u/3KZNm4bmzZvj559/Nm339/fHnDlzMGXKFMTExAAA1q9fj99++w3r1q3D4MGDAQBDhw5Fw4YNMWPGDHz55Zdl0XUAwJtvvomgoCDs2rUL/v7+AIB69erhmWeewc8//4xevXoBAMLCwpCUlIRatWrh4MGDaNeuXZm1oSgFsyPbVfb3bHlidvKYnTxmJ4/ZkTWV/bpo06YNbt26heDgYKxfv96iWFmS/fv3Y/bs2ViwYAFefvlls31CCOTk5AAAgoODsX//frP9zz77LAICArB06VIsXLgQtWrVAqBOgoiOjsbevXvh4eEBAHj00UfRunVrzJs3DwsWLJDtrgWOXSufyv6e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+/jss8+Qlpbm9Tbedtqmp6fj2LFjTXPsbDdJfX09CgsLkZubC4fDwV0/PszJMAwUFhZi4sSJcDgcYTEnt259nd55B+Yrr2DTtGnI/fRTOK65BrjsstCeUw/vtHV/zUZHR4fFnDx1907bwsJC5OTktLiwUSjPqadep4aGhhY/K8JhTj2509b9s8LdSinU59TR2P05J/e5q3vdhcOcvB1vb041NTW2zl1FRdsVK1bg2WefxRdffIE+ffr4eveg4esJvmVZqK2tRWxsrE/bmYnZdQWzk2N2csxOjtnJMTu53p5dV4u2H3/8MS677DKMGjUKa9as8dqy4M4778T//u//4rvf/S7eeust8TnwokWL8M4772DDhg22d1kAsjn29nXRFcxOziouRm1JCWJTU6FlZgZ6OCGF606O2ckxOzlmJ9fbs+vWnrY33HADDMPAGWecgSuuuAJpaWkt/jcLUJXjX/ziF5KHD2pRUVGBHkLIYnZyzE6O2ckxOzlmJ8fs5Jid3IUXXoi///3vuOyyy3DRRRfho48+aircWpaF66+/Hi+//DKuuOIK/PGPf0RkZKTPz2FZFhYvXoy33noL69at86lg2xVcF3LMTigjA1FDhwKtfj8ke7ju5JidHLOTY3ZyzK5zuuRO27Ztw7Jly1BSUoJnnnkGd911F+644442b+HGMAwUFBS02e5MnWN2csxOjtnJMTs5ZifH7OSYXddNnz4dq1evxu7duzF9+nQcP34cjY2NmDt3Ll5++WX85Cc/wRtvvCEq2ALAzTffjD/84Q/405/+hP79++PIkSM4cuRIi/5p/sZ1Icfs5JidHLOTY3ZyzE6O2ckxO3tEO21/+tOfoqKiAitWrMDkyZNbXLSBiIiIiCgUTZ06Fe+99x5mzpyJadOmISUlBR9//DGWLFmCX/7yl1167BdeeAEAMG3atBbH8/PzMX/+/C49NhERERGFH1HRdvPmzXjkkUdw/fXX+3s8REREREQBc9555+GDDz7ApZdeiq+++grLli3zy1+QCS4jQURERES9mKho21M9uIiIiIiIult2dnabY5GRkYiKisIrr7yCV155pcXnNE3Dli1bemp4RERERNQLaZbgv/3ffPNN3HHHHfj000+Rnp7eHePqEb5ehdeyLBiGAYfD0SuvbtcVzE6O2ckxOzlmJ8fs5JidXG/PztdzutamTZvm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svDQQw+hoaEh0EMLCY899hjOOecc9OnTBwkJCYEeTlB77rnncPrppyMmJgaTJ0/Gv//970APKSRs2LABP/jBDzB48GBomoa333470EMKCUuXLsXEiRPRv39/JCcnY9asWdi5c2eghxUSXnjhBWRnZyMuLg5xcXE4++yz8f777wd6WCHpl7/8JTRNw6233hrooQS9hx9+GJqmtXgbNWpUoIdFQYbnrV3D81bf8NzVdzxvleO5qxzPXf2D562+4bmrb1i0FdqxYwdM08SKFSvw1Vdf4cknn8Rvf/tb3HvvvYEeWkhoaGjA3LlzcdNNNwV6KEHtjTfewG233YaHHnoIhYWFGDduHC655BIcPXo00EMLejU1NRg3bhyee+65QA8lpKxfvx4333wzNm7ciDVr1qCxsREXX3wxampqAj20oJeWloZf/vKX+PLLL1FQUIDp06fj8ssvx1dffRXooYWUTZs2YcWKFcjOzg70UELGmWeeiZKSkqa3zz77LNBDoiDD89au4XmrfTx3leF5qxzPXeV47tp1PG+V4bmrDyzym2XLllkZGRmBHkZIyc/Pt+Lj4wM9jKA1adIk6+abb2762DAMa/DgwdbSpUsDOKrQA8B66623Aj2MkHT06FELgLV+/fpADyUkDRgwwPr9738f6GGEjKqqKuuMM86w1qxZY02dOtW65ZZbAj2koPfQQw9Z48aNC/QwKATxvNV3PG/tHM9du47nrV3Dc9eu4bmrfTxvleG5q2+409aPKioqkJiYGOhhUJhoaGjAl19+iYsuuqjpmK7ruOiii/Cvf/0rgCOj3qSiogIA+L3NR4Zh4PXXX0dNTQ3OPvvsQA8nZNx8882YOXNmi+971Lndu3dj8ODByMzMxH/9139h//79gR4ShQCet5K/8dyVggHPXWV47uo7nrfK8dzVvohADyBc7NmzB8888wyWL18e6KFQmCgvL4dhGEhJSWlxPCUlBTt27AjQqKg3MU0Tt956K84991yMHTs20MMJCf/5z39w9tlno66uDv369cNbb72FMWPGBHpYIeH1119HYWEhNm3aFOihhJTJkydj5cqVGDlyJEpKSvDII4/gvPPOw7Zt29C/f/9AD4+CFM9bqTvw3JUCjeeuvuO5qwzPW+V47uob7rRt5e67727TFLn1W+uTjkOHDmHGjBmYO3curr/++gCNPPAk2RFR8Lr55puxbds2vP7664EeSsgYOXIkNm/ejC+++AI33XQT5s2bh6+//jrQwwp6Bw4cwC233II//vGPiImJCfRwQsqll16KuXPnIjs7G5dccgnee+89nDx5En/+858DPTTqATxvleN5K1H44bmr73ju6juet3YNz119w522rdx+++2YP39+h7fJzMxs+vfhw4dxwQUX4JxzzsGLL77YzaMLbr5mRx0bOHAgHA4HSktLWxwvLS3FoEGDAjQq6i0WLVqEd999Fxs2bEBaWlqghxMyoqKiMHz4cADAhAkTsGnTJjz99NNYsWJFgEcW3L788kscPXoUubm5TccMw8CGDRvw7LPPor6+Hg6HI4AjDB0JCQkYMWIE9uzZE+ihUA/geascz1v9j+euFEg8d5XhuavveN7qXzx37RiLtq0kJSUhKSnJ1m0PHTqECy64ABMmTEB+fj50vXdvXPYlO+pcVFQUJkyYgI8//hizZs0CoP7k5+OPP8aiRYsCOzgKW5ZlYfHixXjrrbewbt06ZGRkBHpIIc00TdTX1wd6GEHvwgsvxH/+858WxxYsWIBRo0bhrrvu4omvD6qrq7F3715ce+21gR4K9QCet8rxvNX/eO5KgcBzV//iuWvneN7qXzx37RiLtkKHDh3CtGnTMGzYMCxfvhxlZWVNn+P/JHdu//79OH78OPbv3w/DMLB582YAwPDhw9GvX7/ADi6I3HbbbZg3bx7y8vIwadIkPPXUU6ipqcGCBQsCPbSgV11d3eJ/65xOJzZv3ozExEQMHTo0gCMLbjfffDP+9Kc/4Z133kH//v1x5MgRAEB8fDxiY2MDPLrgds899+DSSy/F0KFDUVVVhT/96U9Yt24d/vGPfwR6aEGvf//+bXrP9e3bF6eddhp70nXijjvuwA9+8AMMGzYMhw8fxkMPPQSHw4Grr7460EOjIMLz1q7heat9PHeV4XmrHM9d5XjuKsPz1q7huauPLBLJz8+3AHh9o87NmzfPa3Zr164N9NCCzjPPPGMNHTrUioqKsiZNmmRt3Lgx0EMKCWvXrvW6xubNmxfooQW19r6v5efnB3poQW/hwoXWsGHDrKioKCspKcm68MILrQ8//DDQwwpZU6dOtW655ZZADyPoXXnllVZqaqoVFRVlDRkyxLryyiutPXv2BHpYFGR43to1PG/1Dc9dfcfzVjmeu8rx3NV/eN5qH89dfaNZlmV1TzmYiIiIiIiIiIiIiHzVu5tZEREREREREREREQUZFm2JiIiIiIiIiIiIggiLtkRERERERERERERBhEVbIiIiIiIiIiIioiDCoi0RERERERERERFREGHRloiIiIiIiIiIiCiIsGhLREREREREREREFERYtCUiIiIiIiIiIiIKIizaEhH5YNq0aRg7dmygh9Elr776KkaNGoXIyEgkJCQ0HX/iiSeQmZkJh8OB8ePHB2x8vlq3bh00TcO6desCPRQiIiKioMJz1+DDc1cisotFWyIKKStXroSmaSgoKPD6+VA6Mf3666/x8MMP45tvvmnzueeffx4rV660/ViaprX7duONNzbdbseOHZg/fz6ysrLwu9/9Di+++CIA4MMPP8SSJUtw7rnnIj8/H48//nhXp9fGe++9h4cfftj27U3TxCuvvILJkycjMTER/fv3x4gRI3Dddddh48aNfh8fERERkb/x3NU7nrsSEXUuItADICLqrb7++ms88sgjmDZtGk4//fQWn3v++ecxcOBAzJ8/3/bjffe738V1113X5viIESOa/r1u3TqYpomnn34aw4cPbzr+ySefQNd1vPTSS4iKivJ5Lna89957eO6552yf/P785z/Hc889h8svvxz/9V//hYiICOzcuRPvv/8+MjMzMWXKFADA+eefj9ra2m4bNxERERHx3LUzPHclIn9j0ZaIKEyMGDEC11xzTYe3OXr0KAC0+NMy9/HY2NigOXksLS3F888/j+uvv75pR4XbU089hbKysqaPdV1HTExMTw+RiIiIiLqA565ERB1jewQiCnv5+fmYPn06kpOTER0djTFjxuCFF17wetv3338fU6dORf/+/REXF4eJEyfiT3/6U4eP/+GHH6JPnz64+uqr4XK5AKg/5briiiuQmJiImJgY5OXl4W9/+1vTfVauXIm5c+cCAC74/+3dfUzUdRwH8PdxQtKPwyg978KSx012wZgYLWQcHeI1H0ibGnMNq+HDvJS2+EMtdtV0za0FyhgSjlNzY8uITZnGWF63dC5rY8xILdnpsjDPp4tCUrlPfzh+4/ydPGl02vu13Xb3fbrf5/MPH773+9698IJ6HOzrr79GQkICOjs74fF41Pb8/Px7zkNCQgKcTicAYMqUKdDpdHjvvfeg0+ngcrnw119/qe83+Hjb3r17kZWV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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# create scatter plots\n", - "fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6))\n", - "\n", - "# scatter plot1: Hackett effect vs Kemmeren effect\n", - "scatter1 = ax1.scatter(clean_data['effect_hackett'], clean_data['effect_kemmeren'], \n", - " alpha=0.6, s=10, c='blue')\n", - "ax1.set_xlabel('Hackett Effect Size', fontsize=12)\n", - "ax1.set_ylabel('Kemmeren Effect Size', fontsize=12)\n", - "ax1.set_title(f'Regulator: {chosen_regulator_symbol}\\nEffect vs Effect', fontsize=14)\n", - "# Add correlation coefficient text\n", - "ax1.text(0.05, 0.95, f'Spearman ρ = {eff_eff_corr:.3f}\\np = {eff_eff_pval:.3e}', \n", - " transform=ax1.transAxes, fontsize=12, verticalalignment='top',\n", - " bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))\n", - "ax1.grid(True, linestyle='--', alpha=0.7)\n", - "\n", - "# scatter plot2: Hackett effect vs Kemmeren pvalue\n", - "# Set a minimum p-value to ensure that -log10(p) does not exceed the range of the plot\n", - "min_pvalue = 10**(-5) \n", - "\n", - "# copy pvalue column and replace 0 values\n", - "pvalue_for_plot = clean_data['pvalue_kemmeren'].copy()\n", - "pvalue_for_plot[pvalue_for_plot == 0] = min_pvalue\n", - "\n", - "# calculate log_pvalue\n", - "log_pvalue = -np.log10(pvalue_for_plot)\n", - "\n", - "scatter2 = ax2.scatter(clean_data['effect_hackett'], log_pvalue, \n", - " alpha=0.6, s=10, c='red')\n", - "ax2.set_xlabel('Hackett Effect Size', fontsize=12)\n", - "ax2.set_ylabel('-log10(Kemmeren p-value)', fontsize=12)\n", - "ax2.set_title(f'Regulator: {chosen_regulator_symbol}\\nEffect vs P-value', fontsize=14)\n", - "# Add correlation coefficient text\n", - "ax2.text(0.05, 0.95, f'Spearman ρ = {eff_pval_corr:.3f}\\np = {eff_pval_pval:.3e}', \n", - " transform=ax2.transAxes, fontsize=12, verticalalignment='top',\n", - " bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))\n", - "ax2.grid(True, linestyle='--', alpha=0.7)\n", - "\n", - "plt.tight_layout()\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "id": "da5d4268", - "metadata": {}, - "source": [ - "## Make distributions over the shared regulators" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "7db5d124", - "metadata": {}, - "outputs": [], - "source": [ - "from typing import Dict, Any, Optional\n", - "\n", - "# Functional encapsulation\n", - "# Calculate the correlation between the two datasets for a given regulator\n", - "def calculate_regulator_correlation(\n", - " chosen_regulator_symbol: str, \n", - " hackett_final: pd.DataFrame, \n", - " kemmeren_final: pd.DataFrame\n", - ") -> Optional[Dict[str, Any]]:\n", - " \"\"\"\n", - " Parameters:\n", - " chosen_regulator_symbol (str): The regulator symbol to be analyzed\n", - " hackett_final (DataFrame): Filtered Hackett dataset\n", - " kemmeren_final (DataFrame): Kemmeren dataset\n", - "\n", - " Returns:\n", - " dict: Dictionary containing correlation coefficients and other \n", - " statistical information, returns None if insufficient data\n", - " \"\"\"\n", - " # Filter data for the specific regulator\n", - " hackett_reg = hackett_final[hackett_final['regulator_symbol'] == chosen_regulator_symbol].copy()\n", - " kemmeren_reg = kemmeren_final[kemmeren_final['regulator_symbol'] == chosen_regulator_symbol].copy()\n", - " \n", - " # Check if data was found\n", - " if len(hackett_reg) == 0 or len(kemmeren_reg) == 0:\n", - " print(f\"No data found for regulator {chosen_regulator_symbol}\")\n", - " return None\n", - " \n", - " # Merge datasets on target_symbol\n", - " merged = pd.merge(\n", - " hackett_reg[['target_symbol', 'log2_shrunken_timecourses']].rename(\n", - " columns={'log2_shrunken_timecourses': 'effect_hackett'}),\n", - " kemmeren_reg[['target_symbol', 'Madj', 'pval']].rename(\n", - " columns={'Madj': 'effect_kemmeren', 'pval': 'pvalue_kemmeren'}),\n", - " on='target_symbol',\n", - " how='inner'\n", - " )\n", - " \n", - " if len(merged) < 3: # Need at least 3 points for correlation\n", - " print(f\"Insufficient data points ({len(merged)}) for regulator {chosen_regulator_symbol}\")\n", - " return None\n", - " \n", - " # Calculate correlations\n", - " # Remove any NaN values\n", - " clean_data = merged.dropna(subset=['effect_hackett', 'effect_kemmeren', 'pvalue_kemmeren'])\n", - " \n", - " if len(clean_data) < 3:\n", - " print(f\"Insufficient clean data points ({len(clean_data)}) for regulator {chosen_regulator_symbol}\")\n", - " return None\n", - " \n", - " # Spearman correlations\n", - " eff_eff_corr, eff_eff_pval = spearmanr(clean_data['effect_hackett'], clean_data['effect_kemmeren'])\n", - " eff_pval_corr, eff_pval_pval = spearmanr(clean_data['effect_hackett'], clean_data['pvalue_kemmeren'])\n", - " \n", - " correlation_results = {\n", - " 'regulator_symbol': chosen_regulator_symbol,\n", - " 'n_targets': len(clean_data),\n", - " 'eff_eff_corr': eff_eff_corr,\n", - " 'eff_eff_pval': eff_eff_pval,\n", - " 'eff_pval_corr': eff_pval_corr,\n", - " 'eff_pval_pval': eff_pval_pval\n", - " }\n", - " \n", - " return correlation_results" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "f116659e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'regulator_symbol': 'ACA1',\n", - " 'n_targets': 6075,\n", - " 'eff_eff_corr': np.float64(0.01608402414610723),\n", - " 'eff_eff_pval': np.float64(0.21004245167836072),\n", - " 'eff_pval_corr': np.float64(-0.017860635301765383),\n", - " 'eff_pval_pval': np.float64(0.1639456246498038)}" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# test the function\n", - "calculate_regulator_correlation('ACA1', hackett_final, kemmeren_final)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "89b6ad09", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Successfully calculated correlations for 145 regulators\n", - " regulator_symbol n_targets eff_eff_corr eff_eff_pval eff_pval_corr \\\n", - "0 ACA1 6075 0.016084 2.100425e-01 -0.017861 \n", - "1 ADA2 6075 -0.100878 3.243911e-15 -0.031116 \n", - "2 ARO80 6075 -0.037804 3.209219e-03 -0.027915 \n", - "3 AFT2 6075 -0.004887 7.033263e-01 0.034297 \n", - "4 ARR1 6075 -0.010794 4.002741e-01 -0.002523 \n", - "\n", - " eff_pval_pval \n", - "0 0.163946 \n", - "1 0.015293 \n", - "2 0.029576 \n", - "3 0.007507 \n", - "4 0.844150 \n" - ] - } - ], - "source": [ - "\n", - "# Extract unique regulator symbols from the filtered Hackett dataset\n", - "regulator_symbols = hackett_final['regulator_symbol'].unique().tolist()\n", - "\n", - "# Initialize an empty list to store all correlation results\n", - "all_correlation_results = []\n", - "\n", - "# Loop through each regulator symbol and calculate correlations\n", - "for regulator_symbol in regulator_symbols:\n", - " # Calculate correlations for this regulator\n", - " correlation_result = calculate_regulator_correlation(\n", - " regulator_symbol, \n", - " hackett_final, \n", - " kemmeren_final\n", - " )\n", - " \n", - " # Only add to results if we got a valid result (not None)\n", - " if correlation_result is not None:\n", - " all_correlation_results.append(correlation_result)\n", - "\n", - "# Convert the list of dictionaries to a DataFrame for easier analysis\n", - "results_df = pd.DataFrame(all_correlation_results)\n", - "\n", - "print(f\"Successfully calculated correlations for {len(results_df)} regulators\")\n", - "print(results_df.head()) # Display first few rows" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "7df98119", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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n2jYsGGWsbGxsRo3blye6wgDwPWE3NVx5K7O4e65q72TC6pXr6569eppyZIlOnXqlEqVKqWVK1fq4MGD6tevn1Ux+d9//1WDBg2UmpqqZs2aqX379goICJCHh4dWrlypVatWFThnutqpU6dkGIYOHTpk87yzZD3Pubn55pu1Y8cOHTp0SFWrVrXr+IWV25YpUybbByTSf79X06ZNyzWutLS0XAu0AwcO1LvvvquIiAh16NBBYWFhlnV1R48efV3ktm+99ZZuueUWxcXFacyYMRozZox8fX3VtWtXTZo0Kdfzh/ugQAvk08qVKyVJ9evXz7NvdHS04uPjdfHiRW3atEnff/+93n77bT300EMKDw9X48aN7T6urf+U7HH06NEc200mk+XTew8PD0nSpUuXsvXNSpIKKus/mJxiOnLkiFU/ZwoICJDZbNbx48etFp2XLt+EwDCMQjluYciK8/nnn7drkf+sG0QcOnSoQMeNjIy0minpKlnn/8033+i+++7Ls7+zzj83jRs3VmJiopYvX253gbYgr4fc3g9y2pa1n9TU1Dxn7eTk999/1zfffKNWrVrpu+++k6enp2XbunXrLDcrKYhr8T5RvHhxS/K6d+9erVixQu+9957eeustpaen6/3333d43wDgbshdHUfu6hxFKXft3r27nnvuOS1YsEBPPvmk5s+fb2m/0pQpU3Tq1CnNnz9fjz32mNW2p556SqtWrbLreB4eHrpw4YLNbVf/nmc9z/Xq1dPGjRvt2n9OGjdurJUrV2r58uVq3ry5XWNcldv+9ddfio6OtivGqx07dkzTpk1TrVq1tHbtWqsP6Y8cOZJrodte1+J9pFixYho8eLAGDx6sw4cPa9WqVYqLi9O8efN05MgR/fjjjw7vG9eOh6sDAK4nu3bt0oIFC+Tj46P777/f7nFeXl664447NHr0aL399tsyDEPffvutZXtWkcOeT9Pz69dff83Wtm/fPh04cEA1atSwXCKWdXdLW4lQ1iVWV/P09MxXzFl3t/zll1+yJUuGYeiXX36x6udMt912m6T//ki5UlZbYRy3MNSvX18mk0lr1661q//tt98uDw8PrVixwnJpU248PDwK5XfRWRo2bChJdp9/1iVTP/30U559HX0tZt1ZedKkSUpPT8+1b9an8Nf69ZD1vGVdDuaIrMvH2rVrZ1WclWy/10ju/z5xyy23qHfv3lq1apVuuukmLV682Cn7BQB3QO5qzd3/T7oSuat75q4PP/ywihUrpo8++kjp6elauHChKleurDvuuMOqX1bO1LFjR6t2wzBynUV8tVKlSunYsWPZPohIS0uzLA2Rxd/fX9WrV9c///xjdfm7I3r16iVPT0/NnDlTx48fz7VvVm5brVo1+fr66vfff9e5c+ey9XP2721+/yawZc+ePTIMQy1atMh2BVVuua1k//vftX5ewsPD9fDDD+uHH35Q5cqVtWzZsjz/PoF7oEAL2Om3335Tq1atlJGRoZdffjnPS242bdpk8zKFrE/OfH19LW1BQUGSpAMHDjgx4svmzZunP//80/LYMAwNHTpUmZmZ6tWrl6W9atWq8vf31+LFiy2Xi2TFO2bMGJv7DgoK0okTJ+xes6p8+fJq1qyZtm3bptmzZ1ttmzlzpv755x81b9482xpeztCzZ09Jly9TufLnkpKSYvlkNKuPu7v55pvVtWtXrVmzRhMmTLA5M2D9+vWWBCA0NFQPPvig1fpaV7o66QsKCtLBgwcL7wQKqGPHjipfvrwmT55s+cPoShcvXtTq1astjzt06KBy5crpo48+svnp8ZV/2Dn6WmzWrJkefvhh7dy5Uw888ICOHTuWrU9qaqqGDh2qmTNnSrr2r4d+/fqpWLFieuaZZ7R///5s20+fPp3jH7RZstZJu/L5laRt27Zp/PjxNse42/vE8ePH9ffff2drP3XqlDIyMqzemwHgekbump27/Z+UG3JX98xdy5Qpo3vvvVe//fabpk6dqtTU1GwzZKWcc6bXX3/dZh6Sk/r16+vixYtWS3sZhqEhQ4bYXKpg4MCBOnfunJ544gmb2/fu3avExMQ8j1u5cmW9+OKLOnHihNq0aaO9e/dm63P+/HlNnjzZsmSEt7e3Hn74YZ04cSJbXvjDDz/oxx9/VOXKlfM1Ez83sbGx8vf317Bhw7Rt27Zs28+dO5fnxISsn9OaNWus1p09ePCghgwZYnNMft//Cvt5ycjI0Jo1a7K1p6Wl6ezZs/Ly8rJccQD3xhIHwFX+/fdfy38yFy5c0LFjx7Rhwwb99ddf8vT01PDhwzVy5Mg89zN//ny9//77iomJUaVKlRQQEKDt27dryZIlCgoKUmxsrKVv8+bNFR8frwcffFBt2rSRr6+vateurfbt2xf4fFq1aqVGjRqpW7duCgkJ0fLly7Vx40bdcccdeuaZZyz9vL299cwzz2jcuHGqW7euOnbsqDNnzuibb75RkyZNbC683rx5c23cuFFt2rTR3XffLW9vb8XExCgmJibHeGbMmKG77rpLTzzxhL755hvdeuut2rZtmxYvXqyQkBDNmDGjwOdsS0xMjJ555hm98847io6O1oMPPijDMPTll1/q4MGDGjhwYK5x58elS5dyXZe1W7duqlatWoGOMX36dO3cuVMvvvii5s+fr0aNGikwMFAHDhzQxo0btXv3biUlJVk+CZ4+fbr+/vtvjR07VkuWLFHz5s1lGIZ27dqln376SUePHrVcTta8eXMtWLBAnTp10m233SZPT0916NBBtWrVsiu2K19Dtrz88ssFKoL5+PgoPj5ebdq0UZMmTdS8eXPVrFlTJpNJ+/bt06+//qrg4GDLDRB8fHy0YMECtW7dWm3atFHr1q1Vu3ZtpaamasuWLTp37pylMFmtWjWFh4frs88+k4+Pj8qVKyeTyaRnnnlGJUuWzDWuWbNmyTAMffbZZ7rlllt07733qkqVKjIMQ7t379by5ct15swZy6Vw0rV9PURHR2v69Ol6+umnVbVqVbVt21aVKlXSmTNntGfPHq1atUq9evXSe++9l+M+GjRooAYNGmjBggVKSkrSHXfcof3792vx4sVq166d4uPjs41xt/eJQ4cO6bbbblPt2rVVq1YtlS1bVidPntTXX3+tixcvavDgwQ7vGwBcgdyV3LWgbvTcNb+6d++uJUuWWF5Xtgq0Tz31lOLi4vTggw+qa9euCg4O1rp16/THH3+oXbt2+u677+w61oABAxQXF6fHH39cS5cuVUhIiH799VedPn1atWvX1tatW636P/nkk1q3bp3mzp2r3377TS1atFB4eLiOHj2qHTt2aP369frkk08UGRmZ57HHjBmj8+fPa8qUKapataqaN2+u6OhoeXl5ae/evVq2bJlOnjxp9YHIG2+8oVWrVmnMmDFas2aNGjZsqMTERH3xxRcqXry44uLinFYsDAkJ0aeffqouXbqodu3aat26tapVq6aMjAwlJiZq1apVuvPOO3NdXzgsLEwPPvigvvzyS91+++265557dPToUX377be65557cnwfye/7X2E+L+np6WrcuLGqVKmievXqqXz58jp79qy+/fZbHTlyRIMH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w+Xn56eGDRtq6dKldo09dOiQunbtqsDAQAUEBKhjx47as2dPtn4mk8nm1+uvv+7s0wEAAMANws/Pz2qGapbz589bttvj119/VZ8+fdSqVSuNHTs22zEk5Xic3I4xZMgQpaSkWL5yWxMXAAAA+Vdkljjo1auX4uPj9dxzzykqKkpz5sxR27ZttWLFCt111105jjt79qyaNWumlJQUDR06VF5eXpoyZYqaNGmiLVu2KDg42Kp/y5Yt1aNHD6u22267rVDOCQAAAEVfWFiYDh06lK09a1mC8PDwPPexdetWdejQQdHR0YqPj7e6EVjWMbL2efUM2KSkJMt6t7b4+PjYnHkLAAAA5ygSBdoNGzbos88+04QJEzR48GBJUo8ePRQdHa0XX3xRa9asyXHs9OnTtXv3bm3YsEH169eXJLVp00bR0dGaNGmSxo0bZ9W/SpUqeuyxxwrvZAAAAHBDqVOnjlasWKHU1FSrG4WtX7/esj03CQkJat26tcqUKaMlS5bopptusnkMSdq4caNVMfbw4cM6ePCg+vbtW/ATAQAAgEOKxBIH8fHx8vT0tEosfX191adPH61duzbXy7Di4+NVv359S3FWkqpVq6Z77rlHCxYssDkmPT3dcskZAAAAUBCdO3dWZmamZs6caWnLyMhQXFycGjZsaJnxun//fu3YscNq7JEjR3TvvffKw8NDP/74o0JCQmweo0aNGqpWrZpmzpypzMxMS/uMGTNkMpmsbigGAACAa6tIzKDdvHmzqlSpYjXjQJJldsCWLVts3szAbDbrzz//VO/evbNta9CggX766SedOXNG/v7+lvY5c+Zo+vTpMgxD1atX1/Dhw/XII484+YwAAABwo2jYsKG6dOmiIUOG6NixY6pcubLmzp2rxMREzZo1y9KvR48eWrVqlQzDsLS1bt1ae/bs0YsvvqjVq1dr9erVlm2hoaFq2bKl5fGECRPUoUMH3XvvverWrZv+/vtvvfvuu3r88cdVvXr1a3OyAAAAyKZIFGiTkpIs62pdKavt8OHDNsclJycrIyMjz7FVq1aVJN15553q2rWrbrnlFh0+fFjTpk3To48+qpSUFD399NM5xpeRkWF1Q4bU1FT7Tw4AAABF3rx58zRixAjNnz9fp06dUq1atfTtt98qJiYm13Fbt26VJL355pvZtjVp0sSqQHvfffdp4cKFGj16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QNLlG4SNGTNGnTt31i233KLk5GR98skn+vvvvzVu3DjdfPPN1/CMAQAAcKUiUaCNj4+Xp6en+vbta2nz9fVVnz59NHToUB04cMAy88DW2Pr161uKs5JUrVo13XPPPVqwYIGlQLtixQqdPHlS/fr1sxrfv39/ffzxx/ruu+/02GOPFcLZAQAAoKibN2+eRowYofnz5+vUqVOqVauWvv32W8XExOQ67tSpUxoxYoRV26RJkyRJFSpUsBRoa9asqVtvvVUfffSRjh8/Lm9vb9WpU0cLFixQly5dCuekAAAAYJciUaDdvHmzqlSpooCAAKv2Bg0aSJK2bNlis0BrNpv1559/qnfv3tm2NWjQQD/99JPOnDkjf39/bd68WZJ0++23W/WrV6+ePDw8tHnz5vwXaM+fl7y9s7d7eFi3/+8OvjYVpG9GhnTFDAwrJpN05c0g8tP3wgXJbM45Dl9f1/f18bkctyRdvChddcmfU/peunT5yxl9vb0v//yc3dfLS/L0zH/fzMzLz0VOihW7/JXfvmbz5Z+ds/saxuXfYWf09fS8/Fw4u++1et3n1vfq55P3iMvf8x6R/768Rzi/rzu8R7gij8gtniLG19dXEyZM0IQJE3Lss3LlymxtkZGRVjNqc1KvXj0tXry4ICECAACgkBSJAm1SUpLCwsKytWe1HT582Oa45ORkZWRk5Dm2atWqSkpKkqenp8qUKWPVz9vbW8HBwTkeQ7q8hljGFX90WW4A0aPHf3+YXen226WRI/97/NhjOf/RFh0tjR//3+M+faSrbjBhERUlTZ783+N+/aRjx2z3jYiQpk//7/GgQdKBA7b7likjXXH5nV5+Wdq923bfgADp44//ezxypPT337b7+vhI/1tOQtLl89y40XZfSfrmm/++nzxZ+t9lfjZ98cV/xZpp06Tly3Pu+9FHUsmSl7//8ENpyZKc+86adfn5kKR586Svvsq577RpUvnyl79fsED69NOc+06efPnnJ0mLF0txcTn3HTdOqlnz8vc//ii9917OfV95RcqaPb5qlTR1as59X3pJuuuuy9+vXSvltvbyc89J99xz+fs//pBefTXnvk89JbVrd/n7bdukoUNz7hsbKz3wwOXvExKk//u/nPs+/LD0yCOXvz9wQOrfP+e+998vZX1Qc/z45ddRTtq2lZ5++vL3qamXX585ueeey8+F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xwUFBTklSAAAAMBR5LIAAABwZ3YVaIcOHarY2Fh99913kiSTyaSnnnpKTz31VK7jMjMzCx4hAAAAUADksgAAAHBndhVou3fvrgYNGmjlypU6evSoRo0apfvvv5+1uwAAAOD2yGUBAADgzuwq0EpS1apVVbVqVUlSXFycevbsqQ4dOhRaYAAAAICzkMsCAADAXdldoL3S3r17nR0HAAAAcE2QywIAAMCdeDg6MDU1Va+//rpatWql2267TRs2bJAkJScna/Lkyfr333+dFiQAAADgTOSyAAAAcBcOzaA9ePCgmjRpogMHDigqKko7duzQ2bNnJV2+2+3777+vffv26a233nJqsAAAAEBBkcsCAADAnThUoH3hhRd05swZbdmyRWXKlFGZMmWstnfq1EnffvutUwIEAAAAnIlcFgAAAO7EoSUOfvrpJw0cOFC33nqrTCZTtu0VK1bUgQMHChwcAAAA4GzksgAAAHAnDhVo09PTFRISkuP2M2fOOBwQAAAAUJjIZQEAAOBOHCrQ3nrrrfrll19y3L5o0SLddtttDgcFAAAAFBZyWQAAALgThwq0zz33nD777DO98cYbSklJkSSZzWb9+++/6t69u9auXatBgwY5NVAAAADAGchlAQAA4E4cuknYY489pn379mn48OEaNmyYJKl169YyDEMeHh4aN26cOnXq5Mw4AQAAAKcglwUAAIA7cahAK0nDhg1T9+7d9eWXX+rff/+V2WxWpUqV9MADD6hixYrOjBEAAABwKnJZAAAAuAuHC7SSVL58eS7/AgAAwHWJXBYAAADuoEAF2r179+r777/Xvn37JEmRkZFq3bq1brnlFqcEBwAAABQWclkAAAC4A4cLtM8//7zeeustmc1mq3YPDw8999xzmjhxYoGDAwAAAAoDuSwAAADchYcjgyZNmqQpU6bogQce0Nq1a3X69GmdPn1aa9euVefOnTVlyhRNmTLF2bECAAAABUYuCwAAAHfi0AzaDz74QB06dNCCBQus2hs2bKjPPvtM58+f1/vvv8+aXgAAAHA75LIAAABwJw7NoE1MTFSrVq1y3N6qVSslJiY6GhMAAABQaMhlAQAA4E4cKtCWKVNGW7duzXH71q1bFRIS4nBQAAAAQGEhlwUAAIA7cahA26VLF3344Yd6/fXXlZaWZmlPS0vTG2+8oQ8//FAPPfSQ04IE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9bVsd5bffvtNzz77rDp06KCnnnpKmzZtUnR0tIYOHZrrccaPH6+SJUtaviIiIpwWEwAAAPKnsHJZAAAAwBEOFWj79eunn376SW3bttVPP/2kxMREJSYm6scff1S7du20dOlSDRgwIN/7/eWXX+Tn52fX186dOyVJfn5+NteZzVojNr/ry+aHt7e3BgwYoNOnT2vTpk059hsyZIhSUlIsXwcOHCi0mAAAAJC7wsplAQAAAEcUc2RQv379dOzYMb3++uv68ccfrbZ5eXnplVde0dNPP53v/VarVk1xcXF29c1awiAsLExJSUnZtme1hYeH5zuO/MiaDZvbZXA+Pj42Z/kCAADg2iusXBYAAABwhEMFWkkaNWqUBgwYoGXLlmnfvn2SpAoVKqhFixYqXbq0Q/u8+eab1atXr3yNqVOnjn799VeZzWarG4WtX79exYsXV5UqVRyKxV579uyRJIWEhBTqcQAAAOA8hZHLAgAAAI5wuEArSaVLl1a3bt2cFYtDOnfurPj4eC1cuFCdO3eWJJ04cUJffPGF2rdvbzVzNSEhQZIcWlPs+PHj2YqwZ86c0dSpU1W6dGnVq1evAGcBAACAa80dclkAAADA7gJtSkqKHnroIcXExGjo0KE59hs7dqxWr16tL774QjfddJNTgsxN586ddccddyg2Nlbbt29X6dKlNX36dGVmZmr06NFWfe+55x5JUmJioqVt3759mj9/viRp48aNkqQxY8ZIujyLonv37pKkadOmadGiRWrfvr3Kly+vpKQkzZ49W/v379f8+fPl7e1d2KcKAAAAB7lrLgsAAADYXaB99913tWbNGksxMydPPPGE3nzzTU2bNk0vvfRSgQPMi6enp5YsWaIXXnhBb7/9ttLT01W/fn3NmTNHVatWzXP83r17NWLECKu2rMdNmjSxFGgbN26sNWvW6MMPP9TJkydVokQJNWjQQLNnz1bz5s2df2IAAABwGnfNZQEAAAC7C7RfffWVunXrludaq2XKlNHDDz+sL7/88poltaVKldKHH36oDz/8MNd+V86czdK0aVMZhpHnMVq2bKmWLVs6GiIAAABcyJ1zWQAAANzYPPLuctmOHTt0++2329W3bt26+ueffxwOCgAAAHAmclkAAAC4K7sLtPbMMr2S2WzOdzAAAABAYSCXBQAAgLuyu0Bbvnx5bdq0ya6+mzZtUvny5R0OCgAAAHAmclkAAAC4K7sLtO3atdNHH32k3bt359pv9+7d+uijj9SuXbsCBwcAAAA4A7ksAAAA3JXdBdoXX3xRxYsXV5MmTfT555/r0qVLVtsvXbqkzz//XM2aNVPx4sX1wgsvOD1YAAAAwBHksgAAAHBXxeztWKZMGS1ZskT333+/HnnkEfn5+alKlSry9/fXmTNntGvXLqWnp+vmm2/Wd999p9DQ0MKMGwAAALAbuSwAAADcld0FWkmqX7++tm3bpvfee0/ffPON/vnnH6WmpiogIEC1a9dW+/bt9dRTTykwMLCQwgUAAAAcQy4LAAAAd5SvAq0klSxZUi+99JJeeumlwogHAAAAKDTksgAAAHA3dq9BCwAAAAAAAABwLgq0AAAAAAAAAOAiFGgBAAAAAAAAwEUo0AIAAAAAAACAi1CgBQAAAAAAAAAXKebqAAAAAADAnZ28eNHVIQAAUCTwf6ptDhdoMzMz9eOPP2rPnj06deqUDMOw2m4ymTRixIgCBwgAAAA4G7ks7FGiRAl5FSumJSdOuDoUAACKDK9ixVSiRAlXh+FWHCrQbty4UQ8++KAOHjyYLZnNQlILAAAAd0QuC3sFBQVp6LBhSktLc3UoAPJw9OhRzZ8/X927d1doaKirwwGQixIlSigoKMjVYbgVhwq0/fr1U3p6uhYtWqS7775bgYGBTg4LAAAAKBzkssiPoKAg/ogEriOhoaGKiIhwdRgAkC8OFWj//PNPjR07Vu3bt3d2PAAAAEChIpcFAACAO/FwZFC5cuVyvBwMAAAAcGfksgAAAHAnDhVoX3rpJX3wwQdKTU11djwAAABAoSKXBQAAgDtxaImDM2fO6KabblLlypXVrVs3RUREyNPT06qPyWTSoEGDnBIkAAAA4CzksgAAAHAnDhVoBw8e/P/t3X9c1fX9///7OShHQCAQPEKDGAS4xGGizHJz+KuGZP4Y6puNUmZZqav3WqxslfNd5uZ0NV3aDxVbTpY4Q6eyTbdYpKnzxyjUYW+UtMlQEQGVH5MXnz/6cr47bxDhePAc5Ha9XM7lks8fr/N4dS4XfPDweR4v23//6le/anUNSS0AAADcEbksAAAA3IlDBdoTJ044Ow4AAADghiCXBQAAgDtxqEB72223OTsOAAAA4IYglwUAAIA7ceghYQAAAAAAAACA6+fQCVpJ+vjjj7V8+XIdPHhQVVVVMgzDbt5kMqmkpOS6AwQAAACcjVwWAAAA7sKhE7T5+flKTEzU1q1bFRoaquPHjysyMlKhoaH67LPP1Lt3b40YMcLZsQIAAADXjVwWAAAA7sShAu0LL7ygyMhIFRcXKysrS5L07LPP6sMPP9Tu3bv1+eefa+rUqU4NFAAAAHAGclkAAAC4E4cKtAcPHtTMmTPl5+cnDw8PSVJjY6Mk6Wtf+5oeeeQRPf/8886LEgAAAHASclkAAAC4E4cKtD169JCvr68k6ZZbblHPnj115swZ23xkZKSOHDninAgBAAAAJyKXBQAAgDtxqEB7++2369NPP5X0xQMU+vfvr/fee882v23bNvXr1885EQIAAABORC4LAAAAd+JQgXbcuHHKzs7WlStXJElPPvmkNm3apOjoaEVHR2vLli165JFHnBooAAAA4AzksgAAAHAnPRzZ9Pzzz+uJJ56w9eyaPn26PDw89Lvf/U4eHh768Y9/rBkzZjgzTgAAAMApyGUBAADgThwq0Pbs2VN9+vSxG0tPT1d6erpTggIAAAA6C7ksAAAA3IlDLQ4AAAAAAAAAANfPoRO0kvThhx9qzZo1On78uCorK9XU1GQ3bzKZVFhYeN0BAgAAAM5GLgsAAAB34VCB9he/+IUyMzPVq1cvxcbGKjAw0NlxAQAAAJ2CXBYAAADuxKEC7c9//nMNHz5cv//97+Xv7+/smAAAAIBOQy4LAAAAd+JQD9rLly/ru9/9LgktAAAAuhx3y2UvXLigWbNmKTg4WD4+Pho5cqQOHjzY7v1Hjx7Vt771LfXu3VuBgYF64IEHdPbsWbs1paWlMplMrb5++9vfOvuWAAAA0AEOnaAdOXKkPvnkE2fHAgAAAHQ6d8plDcNQSkqKCgsLlZmZqaCgIK1YsUJJSUk6cOCAoqOj29z/+eefa8SIEfL399fLL7+sixcvasmSJfrkk0+0b98+eXp62q1PS0vTuHHj7Mbuuusup98XAAAA2s+hAu3y5ct1zz33aMmSJfre975H3y4AAAB0Ge6Uy27cuFG7d+9WTk6OUlNTJUlTp05VTEyM5s+fr/Xr17e5/+WXX9alS5d04MABhYeHS5ISExM1duxYrV27VrNmzbJbP3jwYKWnp3fOzQAAAMAhDrU4CAsL0yOPPKJnnnnG9lUsPz8/u5e7fGUMAAAA+E/ulMtu3LhRVqtVkydPto0FBwdr6tSp2rx5s+rr69vc/7vf/U733XefrTgrSWPGjFFMTIw2bNjQ6p5Lly6poaHBOTcAAACA6+bQCdoXXnhBCxcu1K233qohQ4ZQjAUAAECX4U657KFDhzR48GCZzfbnJhITE/Xmm2/q2LFjGjhwYKt7//nPf+rMmTMaMmRIi7nExERt3769xfiCBQuUmZkpk8mkhIQELVy4UPfcc49zbgYAAAAOcahA+/rrryslJUW5ubktkkkAAADAnblTLltWVqYRI0a0GA8JCZEknT59+qoF2rKyMru1/3f/+fPnVV9fL4vFIrPZrHvuuUeTJk3SrbfequPHj+sXv/iFkpOTtWXLFqWkpFw1xvr6eruTvNXV1R26RwAAALTNoQJtQ0ODUlJSXJ7QAgAAAB3VWbmsYRjtbh1gsVhkMplUW1sri8XSYr5Xr16SpNra2qteo3nuWvstFovCw8P1xz/+0W7NAw88oDvuuEM//OEP2yzQLlq0SAsWLLj2TQEAAMAhDmWl9913nwoKCpwdCwAAANDpOiuX/eCDD+Tl5dWuV3FxsSTJy8ur1T6zdXV1tvmraZ5zdH9gYKAyMjJUXFyszz///Krr5s2bp6qqKtvr1KlTV10LAACAjnPoBO38+fM1bdo0zZ49WzNnzlR4eLg8PDxarHPlE3EBAACA1nRWLtu/f39lZWW1a21zW4KQkBBbq4L/1DwWGhp6zWtcbX9gYGCrp2v/U1hYmCTp/Pnz+tKXvtTqGovFcs3rAAAAwHEOFWhjY2MlSX//+9/1xhtvXHVdY2OjY1EBAAAAnaSzctl+/fppxowZHdozaNAgFRQUyDAMu5YLe/fulbe3t2JiYq6699Zbb1VwcLD279/fYm7fvn0aNGjQNd//+PHjkqTg4OAOxQ0AAADncahA+8ILL8hkMjk7FgAAAKDTuVMum5qaqo0bN2rTpk1KTU2VJJ07d045OTkaP3683cnVkpISSVJUVJRt7Nvf/rbefvttnTp1ynYa9s9//rOOHTumH/zgB7Z1Z8+ebVGE/ec//6k1a9boq1/9aqsPGgMAAMCN0eEC7b///W9NnjxZgYGBV/0aFAAAAOCO3C2XTU1N1bBhw5SRkaEjR44oKChIK1asUGNjY4sHc40ePVqSVFpaaht79tlnlZOTo5EjR+qJJ57QxYsX9fOf/1wDBw5URkaGbd2PfvQjlZSUaPTo0QoNDVVpaaneeOMNXbp0Sb/85S9vyL0CAACgdR1+SJjZbFZCQoI2bdrUGfEAAAAAncbdclkPDw9t375d06ZN07Jly5SZmamgoCD95S9/sbViaEtYWJj++te/KioqSs8884wWL16scePGaceOHXanb++55x6ZTCa99tprmj17tt58802NGDFCH330kZKSkjrxDgEAAHAtHT5B6+Hhodtuu63Vp8UCAAAA7swdc9mAgACtWrVKq1atanPdf56c/U8DBgzQH//4xzb3pqWlKS0tzdEQAQAA0Ik6fIJWkr7//e/rzTff1Pnz550dDwAAANCpyGUBAADgThx6SFhjY6MsFouioqKUmpqqiIgIeXl52a0xmUx2DyYAAAAA3AG5LAAAANyJQwXap556yvbfq1evbnUNSS1w82poaNDZs2ddHQacrPkz5bO9+QQHB8vT09PVYQBug1wWAAAA7sShAu2JEyecHQeALuTs2bN67bXXXB0GOsmGDRtcHQKcbM6cObr11ltdHQbgNshlAQAA4E4cKtDedtttzo4DQBcSHBysOXPmuDoMAO0UHBzs6hAAt0IuCwAAAHfiUIEWQPfm6enJaTwAAAAAAAAnMDu68eOPP9bDDz+shIQE3X777YqMjLR7RUVFOTPONl24cEGzZs1ScHCwfHx8NHLkSB08ePCa+wzD0Nq1a3X//fcrLCxMPj4+iouL00svvaS6urpW96xevVpf+cpX1KtXL0VHR2v58uXOvh0AAAB0MnfKZQEAANC9OVSgzc/PV2JiorZu3arQ0FAdP35ckZGRCg0N1WeffabevXtrxIgRzo61VYZhKCUlRevXr9fcuXO1ePFinTlzRklJSfr000/b3Hv58mVlZGTo7NmzevTRR/Xqq68qMTFR8+fPV3JyspqamuzWv/HGG3rooYc0YMAALV++XHfddZcef/xx/exnP+vMWwQAAIATuVMuCwAAAJia/m8Vsh1GjBihc+fOac+ePWpoaFDfvn21c+dOjRo1Snv37lVycrJ+85vfKDk5uTNitrNhwwZNmzZNOTk5Sk1NlfTFA4xiYmKUnJys9evXX3VvQ0OD9u/fr7vvvttu/H/+5380f/587dixQ2PGjJEk1dbWKiwsTMOGDdPWrVtta9PT05Wbm6tTp04pICCgXTFXV1fL399fVVVV8vPz6+gtAwAAdAudlTO5Uy7bFZHLAnBHp06d0pIlS/TUU08pLCzM1eEAQIdyJodO0B48eFAzZ86Un5+fPDw8JEmNjY2SpK997Wt65JFH9Pzzzzty6Q7buHGjrFarJk+ebBsLDg7W1KlTtXnzZtXX1191r6enZ4virCRNmjRJknT06FHb2Pvvv6+KigrNnj3bbu2cOXN06dIlbdu27XpvBQAAADeAO+WyAAAAgEMF2h49esjX11eSdMstt6hnz546c+aMbT4yMlJHjhxxToTXcOjQIQ0ePFhms/2tJCYm6vLlyzp27FiHr/mvf/1LkhQUFGT3PpI0ZMgQu7UJCQkym822eQAAALg3d8plAQAAAIcKtLfffrutv6vJZFL//v313nvv2ea3bdumfv36OSfCaygrK1NISEiL8eax06dPd/iaixcvlp+fn93X2srKyuTh4aG+ffvarfX09FSfPn3afJ/6+npVV1fbvQAAAOAa7pTLAgAAAA4VaMeNG6fs7GxduXJFkvTkk09q06ZNio6OVnR0tLZs2aJHHnmkw9c1DEN1dXXtejW3zq2trZXFYmlxrV69etnmO+Lll1/Wzp079dOf/lS33HKLbby2tlaenp6t7unVq1eb77No0SL5+/vbXvTDAQAAcJ3OymUBAAAARzhUoH3++edVWFho69k1ffp0/frXv1ZcXJzi4+O1Zs0aPf300x2+7gcffCAvL692vYqLiyVJXl5erfaZraurs82317vvvqvnnntOM2fO1GOPPWY35+XlpYaGhlb31dXVtfk+8+bNU1VVle116tSpdscEAAAA5+qsXBYAAABwRA9HNvXs2VN9+vSxG0tPT1d6evp1BdO/f39lZWW1a21zC4OQkBCVlZW1mG8eCw0Nbdf1duzYoQcffFApKSl6/fXXW32/xsZGnTlzxq7NQUNDgyoqKtp8H4vF0uopXwAAANx4nZXLAgAAAI5wqEDbrL6+XgcPHtSZM2c0fPhwu4dqOaJfv36aMWNGh/YMGjRIBQUFMgzD7kFhe/fulbe3t2JiYq55jb1792rSpEkaMmSINmzYoB49Wv5vGTRokCRp//79GjdunG18//79MgzDNg8AAICuwdm5LAAAAOAIh1ocSNKyZcsUEhKir3/965o8ebI+/vhjSdK5c+cUFBSkNWvWOC3ItqSmpqq8vFybNm2yjZ07d045OTkaP3683cnVkpISlZSU2O0/evSoUlJSFBERoa1bt161VcGoUaMUGBiolStX2o2vXLlS3t7eSklJceJdAQAAoDO5Sy4LAAAAOHSCNisrS//93/+t//qv/9I999yj733ve7a5oKAgjRo1Sr/97W/txjtLamqqhg0bpoyMDB05ckRBQUFasWKFGhsbtWDBAru1o0ePliSVlpZKkmpqanTvvfeqsrJSmZmZ2rZtm936qKgo3XXXXZK+6EH74osvas6cOZoyZYruvfdeFRQUaN26dVq4cKECAwM7/V4BAABw/dwplwUAAAAcKtAuXbpUEyZM0Pr161VRUdFiPiEhQcuWLbvu4NrDw8ND27dvV2ZmppYtW6ba2loNHTpUa9euVWxsbJt7KyoqbA/seuaZZ1rMT58+3VaglaTZs2erZ8+eWrp0qbZs2aKwsDC98soreuKJJ5x7UwAAAOg07pTLAgAAAA4VaP/3f/9Xjz/++FXnAwMDW012O0tAQIBWrVqlVatWtbmu+eRss4iICDU1NXXovR5++GE9/PDDHQ0RAAAAbsLdclkAAAB0bw71oL3lllt07ty5q84fOXJE/fr1czgoAAAAoLOQywIAAMCdOFSgHTdunN58801duHChxdzhw4f11ltv6f7777/e2AAAAACnI5cFAACAO3GoQPvSSy+psbFRcXFxeu6552QymfT2228rPT1dQ4YMUd++ffXCCy84O1YAAADgupHLAgAAwJ04VKANDQ3VgQMH9K1vfUvvvvuumpqa9M477+j3v/+90tLStGfPHgUFBTk7VgAAAOC6kcsCAADAnTj0kDBJ6tu3r+3BXGfPnpVhGAoODpbZ7FDNFwAAALhhyGUBAADgLq47A21qalJTU5NMJpNMJpMzYgIAAABuCHJZAAAAuJrDBdojR44oNTVVfn5+CgkJUUhIiPz8/JSamqqioiJnxggAAAA4FbksAAAA3IVDLQ4KCgqUnJwswzA0YcIExcTESJKKi4u1ZcsW5eXl6Q9/+IO+8Y1vODVYAAAA4HqRywIAAMCdOFSg/cEPfqC+ffvqr3/9q8LCwuzmTp06pREjRujJJ5/U3/72N6cECQAAADgLuSwAAADciUMtDg4fPqzZs2e3SGglKSwsTI899pgOHz583cEBAAAAzkYuCwAAAHfiUIH2tttuU319/VXnGxoaWk14AQAAAFcj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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Effect-Effect Correlation Summary:\n", - "count 145.000000\n", - "mean -0.058967\n", - "std 0.072261\n", - "min -0.296206\n", - "25% -0.091059\n", - "50% -0.037956\n", - "75% -0.008329\n", - "max 0.042079\n", - "Name: eff_eff_corr, dtype: float64\n", - "\n", - "Effect-Pvalue Correlation Summary:\n", - "count 145.000000\n", - "mean -0.002324\n", - "std 0.047957\n", - "min -0.164093\n", - "25% -0.028492\n", - "50% -0.001771\n", - "75% 0.017656\n", - "max 0.186708\n", - "Name: eff_pval_corr, dtype: float64\n" - ] - } - ], - "source": [ - "# Set up the plotting style and parameters\n", - "plt.style.use('default')\n", - "plt.rcParams['figure.figsize'] = (14, 6)\n", - "plt.rcParams['font.size'] = 12\n", - "\n", - "# Create a figure with two subplots side by side\n", - "fig, (ax1, ax2) = plt.subplots(1, 2)\n", - "\n", - "# First boxplot: Effect-Effect Correlation Distribution\n", - "sns.boxplot(data=results_df, y='eff_eff_corr', ax=ax1, color='lightblue')\n", - "ax1.set_title('Distribution of Effect-Effect Correlations', fontsize=14)\n", - "ax1.set_ylabel('Spearman Correlation Coefficient', fontsize=12)\n", - "# Add a horizontal line at y=0 for reference\n", - "ax1.axhline(y=0, color='r', linestyle='--', alpha=0.7)\n", - "# Add text showing the number of regulators\n", - "ax1.text(0.05, 0.95, f'n = {len(results_df)}', transform=ax1.transAxes, \n", - " fontsize=12, verticalalignment='top',\n", - " bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))\n", - "\n", - "# Second boxplot: Effect-Pvalue Correlation Distribution\n", - "sns.boxplot(data=results_df, y='eff_pval_corr', ax=ax2, color='lightcoral')\n", - "ax2.set_title('Distribution of Effect-Pvalue Correlations', fontsize=14)\n", - "ax2.set_ylabel('Spearman Correlation Coefficient', fontsize=12)\n", - "# Add a horizontal line at y=0 for reference\n", - "ax2.axhline(y=0, color='r', linestyle='--', alpha=0.7)\n", - "# Add text showing the number of regulators\n", - "ax2.text(0.05, 0.95, f'n = {len(results_df)}', transform=ax2.transAxes, \n", - " fontsize=12, verticalalignment='top',\n", - " bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))\n", - "\n", - "# Adjust layout and display the plot\n", - "plt.tight_layout()\n", - "plt.show()\n", - "\n", - "# Optional: Print summary statistics\n", - "print(\"Effect-Effect Correlation Summary:\")\n", - "print(results_df['eff_eff_corr'].describe())\n", - "print(\"\\nEffect-Pvalue Correlation Summary:\")\n", - "print(results_df['eff_pval_corr'].describe())\n" - ] - }, - { - "cell_type": "markdown", - "id": "4becea1e", - "metadata": {}, - "source": [ - "## Check if all final_common regulators have other timepoints" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "id": "2cb70685", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Dataset 'time_0' contains 1037400 rows of data\n", - "Dataset 'time_5' contains 1025050 rows of data\n", - "Dataset 'time_10' contains 1018875 rows of data\n", - "Dataset 'time_15' contains 1012700 rows of data\n", - "Dataset 'time_20' contains 1006525 rows of data\n", - "Dataset 'time_30' contains 1031225 rows of data\n", - "Dataset 'time_45' contains 1018875 rows of data\n", - "Dataset 'time_60' contains 6175 rows of data\n", - "Dataset 'time_90' contains 1018875 rows of data\n", - "Dataset 'time_100' contains 12350 rows of data\n", - "Dataset 'time_120' contains 6175 rows of data\n", - "Dataset 'time_180' contains 6175 rows of data\n", - "Dataset 'time_290' contains 6175 rows of data\n" - ] - } - ], - "source": [ - "hackett_2020_copy = HfQueryAPI(repo_id=\"BrentLab/hackett_2020\")\n", - "\n", - "hackett_2020_copy.set_sql_filter(\n", - " \"hackett_2020\",\n", - " f\"\"\"\n", - " mechanism = 'ZEV' \n", - " AND restriction = 'P' \n", - " \"\"\")\n", - "\n", - "hackett_data_copy = hackett_2020_copy.query(\"SELECT * FROM hackett_2020\", \"hackett_2020\")\n", - "\n", - "# Get all unique time points\n", - "unique_times = hackett_data_copy['time'].unique()\n", - "unique_times.sort() # Sort in chronological order\n", - "\n", - "# Create a dictionary to store datasets split by time point\n", - "time_datasets = {}\n", - "\n", - "# Split data by each time point\n", - "for time_point in unique_times:\n", - " # Filter data for the current time point\n", - " time_data = hackett_data_copy[hackett_data_copy['time'] == time_point].copy()\n", - " \n", - " # Create meaningful key names using time point markers\n", - " key_name = f\"time_{int(time_point)}\" # Convert float to integer for naming\n", - " \n", - " # Store the dataset in the dictionary\n", - " time_datasets[key_name] = time_data\n", - " \n", - " # Print information about each dataset\n", - " print(f\"Dataset '{key_name}' contains {len(time_data)} rows of data\")" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "id": "8654baec", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Time 0: Contains 145/145 regulators (100.0%) - All present? True\n", - "Time 5: Contains 144/145 regulators (99.3%) - All present? False\n", - "Time 10: Contains 144/145 regulators (99.3%) - All present? False\n", - "Time 15: Contains 145/145 regulators (100.0%) - All present? True\n", - "Time 20: Contains 142/145 regulators (97.9%) - All present? False\n", - "Time 30: Contains 144/145 regulators (99.3%) - All present? False\n", - "Time 45: Contains 143/145 regulators (98.6%) - All present? False\n", - "Time 60: Contains 1/145 regulators (0.7%) - All present? False\n", - "Time 90: Contains 143/145 regulators (98.6%) - All present? False\n", - "Time 100: Contains 2/145 regulators (1.4%) - All present? False\n", - "Time 120: Contains 1/145 regulators (0.7%) - All present? False\n", - "Time 180: Contains 1/145 regulators (0.7%) - All present? False\n", - "Time 290: Contains 1/145 regulators (0.7%) - All present? False\n" - ] - } - ], - "source": [ - "# Check the percentage of final_common regulators present in each time-split dataset\n", - "\n", - "# Initialize a list to store the results\n", - "coverage_results = []\n", - "\n", - "# Get the total number of regulators in final_common for percentage calculation\n", - "total_regulators = len(final_common)\n", - "\n", - "# Iterate through each time-split dataset\n", - "for time_key, dataset in time_datasets.items():\n", - " # Extract the time point from the key name\n", - " time_point = time_key.replace('time_', '')\n", - " \n", - " # Get unique regulators in this time dataset\n", - " regulators_in_dataset = set(dataset['regulator_symbol'].unique())\n", - " \n", - " # Find the intersection with final_common regulators\n", - " common_regulators = regulators_in_dataset.intersection(set(final_common))\n", - " \n", - " # Calculate the percentage of final_common regulators present\n", - " percentage_present = (len(common_regulators) / total_regulators) * 100\n", - " \n", - " # Check if all final_common regulators are present\n", - " contains_all = len(common_regulators) == total_regulators\n", - " \n", - " # Store the result\n", - " coverage_results.append({\n", - " 'time_point': time_point,\n", - " 'contains_all_shared_regulators': contains_all,\n", - " 'percentage_present': percentage_present,\n", - " 'regulators_present': len(common_regulators),\n", - " 'total_regulators': total_regulators\n", - " })\n", - " \n", - " # Print the result for this time point\n", - " print(f\"Time {time_point}: Contains {len(common_regulators)}/{total_regulators} regulators ({percentage_present:.1f}%) - All present? {contains_all}\")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "tfbpapi-py3.11", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.9" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/docs/tutorials/datacard_tutorial.ipynb b/docs/tutorials/datacard_tutorial.ipynb index 055f8fe..e3e8ba8 100644 --- a/docs/tutorials/datacard_tutorial.ipynb +++ b/docs/tutorials/datacard_tutorial.ipynb @@ -25,17 +25,9 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chase/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", - " from .autonotebook import tqdm as notebook_tqdm\n" - ] - }, { "name": "stdout", "output_type": "stream", @@ -45,7 +37,7 @@ } ], "source": [ - "from tfbpapi.datainfo import DataCard\n", + "from tfbpapi.datacard import DataCard\n", "\n", "card = DataCard('BrentLab/harbison_2004')\n", "\n", @@ -63,7 +55,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -81,7 +73,7 @@ "num_configs : 1\n", "dataset_types : ['annotated_features']\n", "total_files : 7\n", - "last_modified : 2025-12-11T01:11:35+00:00\n", + "last_modified : 2025-12-16T20:28:09+00:00\n", "has_default_config : True\n" ] } @@ -98,7 +90,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -138,7 +130,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -172,34 +164,18 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Main Configuration Details:\n", - "========================================\n", - "Config name: harbison_2004\n", - "Dataset type: annotated_features\n", - "Number of features: 7\n", - "\n", - "Features:\n", - " • condition [experimental_condition]\n", - " Environmental condition of the experiment. Nearly all of the 204 regulators have a YPD condition, and some have others in addition.\n", - " • regulator_locus_tag [regulator_identifier]\n", - " Systematic gene name (ORF identifier) of the ChIPd transcription factor\n", - " • regulator_symbol [regulator_identifier]\n", - " Standard gene symbol of the ChIPd transcription factor\n", - " • target_locus_tag [target_identifier]\n", - " Systematic gene name (ORF identifier) of the target gene measured\n", - " • target_symbol [target_identifier]\n", - " Standard gene symbol of the target gene measured\n", - " • effect [quantitative_measure]\n", - " The chip channel ratio (effect size)\n", - " • pvalue [quantitative_measure]\n", - " pvalue of the chip channel ratio (effect)\n" + "ename": "AttributeError", + "evalue": "'DataCard' object has no attribute 'explore_config'", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mAttributeError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[6]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;66;03m# Explore the main data configuration in detail\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m config_info = \u001b[43mcard\u001b[49m\u001b[43m.\u001b[49m\u001b[43mexplore_config\u001b[49m(\u001b[33m'\u001b[39m\u001b[33mharbison_2004\u001b[39m\u001b[33m'\u001b[39m)\n\u001b[32m 4\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33mMain Configuration Details:\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 5\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33m=\u001b[39m\u001b[33m\"\u001b[39m * \u001b[32m40\u001b[39m)\n", + "\u001b[31mAttributeError\u001b[39m: 'DataCard' object has no attribute 'explore_config'" ] } ], @@ -246,7 +222,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -286,7 +262,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -326,7 +302,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -350,7 +326,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -394,7 +370,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -447,7 +423,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -508,7 +484,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -549,7 +525,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -577,7 +553,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -633,7 +609,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "metadata": {}, "outputs": [ { diff --git a/docs/tutorials/hfqueryapi_tutorial.ipynb b/docs/tutorials/hfqueryapi_tutorial.ipynb deleted file mode 100644 index 70c3ebb..0000000 --- a/docs/tutorials/hfqueryapi_tutorial.ipynb +++ /dev/null @@ -1,2214 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# HfQueryAPI Tutorial: Metadata-Driven Data Exploration\n", - "\n", - "This tutorial demonstrates how to use the HfQueryAPI for efficient metadata-driven exploration and querying of Hugging Face datasets, using the Hackett 2020 transcription factor overexpression dataset as an example.\n", - "\n", - "## Overview\n", - "\n", - "The HfQueryAPI provides a streamlined workflow for:\n", - "1. **Exploring metadata** to understand dataset structure\n", - "2. **Setting filters** based on metadata values\n", - "3. **Querying data** with automatic filter application\n", - "4. **Efficient SQL-based filtering** on large datasets\n", - "\n", - "## Dataset: Hackett 2020\n", - "\n", - "The `BrentLab/hackett_2020` dataset contains transcription factor overexpression data with embedded metadata fields including:\n", - "- `regulator_locus_tag` & `regulator_symbol`: Transcription factor identifiers\n", - "- `time`: Time point (0, 15, 30, 90 minutes)\n", - "- `mechanism`: Experimental mechanism (ZEV)\n", - "- `restriction`: Restriction enzyme treatment (P, NP)\n", - "- `date`, `strain`: Experimental metadata\n", - "\n", - "Let's start exploring!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Initialize HfQueryAPI and Getting Metadata\n", - "\n", - "First, we'll initialize the API client for the Hackett 2020 dataset:" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Initialized HfQueryAPI for BrentLab/hackett_2020\n", - "Repository type: dataset\n", - "Available configurations: ['hackett_2020']\n" - ] - } - ], - "source": [ - "from tfbpapi.HfQueryAPI import HfQueryAPI\n", - "import pandas as pd\n", - "\n", - "# Initialize the API client\n", - "api = HfQueryAPI(repo_id=\"BrentLab/hackett_2020\", repo_type=\"dataset\")\n", - "\n", - "print(f\"Initialized HfQueryAPI for {api.repo_id}\")\n", - "print(f\"Repository type: {api.repo_type}\")\n", - "print(f\"Available configurations: {[c.config_name for c in api.configs]}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "metadata = api.get_metadata(\"hackett_2020\")" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Experimental Conditions Summary\n", - "==================================================\n", - "\n", - "⏱️ Time Points:\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Time Point (min)", - "rawType": "float64", - "type": "float" - }, - { - "name": "Frequency", - "rawType": "int64", - "type": "integer" - }, - { - "name": "% of Records", - "rawType": "float64", - "type": "float" - } - ], - "ref": "537d2492-acbe-4e3d-a2cc-3c1129ec2ed1", - "rows": [ - [ - "0", - "0.0", - "217", - "12.8" - ], - [ - "1", - "2.5", - "8", - "0.5" - ], - [ - "2", - "5.0", - "212", - "12.5" - ], - [ - "3", - "7.5", - "2", - "0.1" - ], - [ - "4", - "8.0", - "2", - "0.1" - ], - [ - "5", - "10.0", - "187", - "11.0" - ], - [ - "6", - "12.5", - "2", - "0.1" - ], - [ - "7", - "15.0", - "212", - "12.5" - ], - [ - "8", - "18.0", - "1", - "0.1" - ], - [ - "9", - "20.0", - "184", - "10.9" - ], - [ - "10", - "30.0", - "216", - "12.8" - ], - [ - "11", - "45.0", - "213", - "12.6" - ], - [ - "12", - "60.0", - "20", - "1.2" - ], - [ - "13", - "90.0", - "212", - "12.5" - ], - [ - "14", - "100.0", - "2", - "0.1" - ], - [ - "15", - "120.0", - "1", - "0.1" - ], - [ - "16", - "180.0", - "1", - "0.1" - ], - [ - "17", - "290.0", - "1", - "0.1" - ] - ], - "shape": { - "columns": 3, - "rows": 18 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Time Point (min)Frequency% of Records
00.021712.8
12.580.5
25.021212.5
37.520.1
48.020.1
510.018711.0
612.520.1
715.021212.5
818.010.1
920.018410.9
1030.021612.8
1145.021312.6
1260.0201.2
1390.021212.5
14100.020.1
15120.010.1
16180.010.1
17290.010.1
\n", - "
" - ], - "text/plain": [ - " Time Point (min) Frequency % of Records\n", - "0 0.0 217 12.8\n", - "1 2.5 8 0.5\n", - "2 5.0 212 12.5\n", - "3 7.5 2 0.1\n", - "4 8.0 2 0.1\n", - "5 10.0 187 11.0\n", - "6 12.5 2 0.1\n", - "7 15.0 212 12.5\n", - "8 18.0 1 0.1\n", - "9 20.0 184 10.9\n", - "10 30.0 216 12.8\n", - "11 45.0 213 12.6\n", - "12 60.0 20 1.2\n", - "13 90.0 212 12.5\n", - "14 100.0 2 0.1\n", - "15 120.0 1 0.1\n", - "16 180.0 1 0.1\n", - "17 290.0 1 0.1" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Experimental Design Overview:\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Condition Type", - "rawType": "object", - "type": "string" - }, - { - "name": "Unique Values", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Most Common", - "rawType": "object", - "type": "string" - } - ], - "ref": "fc6ebeb5-026f-49dc-ab9a-89995ff14cb9", - "rows": [ - [ - "0", - "Mechanisms", - "2", - "ZEV" - ], - [ - "1", - "Restriction Treatments", - "3", - "P" - ], - [ - "2", - "Strains", - "215", - "SMY2207" - ], - [ - "3", - "Experimental Dates", - "23", - "20150101 to 20161117" - ] - ], - "shape": { - "columns": 3, - "rows": 4 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Condition TypeUnique ValuesMost Common
0Mechanisms2ZEV
1Restriction Treatments3P
2Strains215SMY2207
3Experimental Dates2320150101 to 20161117
\n", - "
" - ], - "text/plain": [ - " Condition Type Unique Values Most Common\n", - "0 Mechanisms 2 ZEV\n", - "1 Restriction Treatments 3 P\n", - "2 Strains 215 SMY2207\n", - "3 Experimental Dates 23 20150101 to 20161117" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Create summary tables for experimental conditions\n", - "print(\"Experimental Conditions Summary\")\n", - "print(\"=\" * 50)\n", - "\n", - "# Time points summary\n", - "time_summary = pd.DataFrame({\n", - " 'Time Point (min)': metadata['time'].value_counts().sort_index().index,\n", - " 'Frequency': metadata['time'].value_counts().sort_index().values,\n", - "})\n", - "time_summary['% of Records'] = (time_summary['Frequency'] / time_summary['Frequency'].sum() * 100).round(1)\n", - "\n", - "print(\"\\n⏱️ Time Points:\")\n", - "display(time_summary)\n", - "\n", - "# Experimental conditions table\n", - "conditions_summary = pd.DataFrame({\n", - " 'Condition Type': ['Mechanisms', 'Restriction Treatments', 'Strains', 'Experimental Dates'],\n", - " 'Unique Values': [\n", - " metadata['mechanism'].nunique(),\n", - " metadata['restriction'].nunique(), \n", - " metadata['strain'].nunique(),\n", - " metadata['date'].nunique()\n", - " ],\n", - " 'Most Common': [\n", - " metadata['mechanism'].mode().iloc[0],\n", - " metadata['restriction'].mode().iloc[0],\n", - " metadata['strain'].mode().iloc[0],\n", - " f\"{metadata['date'].min()} to {metadata['date'].max()}\"\n", - " ]\n", - "})\n", - "\n", - "print(\"\\nExperimental Design Overview:\")\n", - "display(conditions_summary)" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Transcription Factor Analysis\n", - "==================================================\n", - "\n", - "Dataset Overview:\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Metric", - "rawType": "object", - "type": "string" - }, - { - "name": "Value", - "rawType": "object", - "type": "string" - } - ], - "ref": "bd31a290-891e-4027-8987-337b87e04e47", - "rows": [ - [ - "0", - "Total Unique Transcription Factors", - "203" - ] - ], - "shape": { - "columns": 2, - "rows": 1 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
MetricValue
0Total Unique Transcription Factors203
\n", - "
" - ], - "text/plain": [ - " Metric Value\n", - "0 Total Unique Transcription Factors 203" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Create comprehensive transcription factor analysis tables\n", - "print(\"Transcription Factor Analysis\")\n", - "print(\"=\" * 50)\n", - "\n", - "print(f\"\\nDataset Overview:\")\n", - "tf_overview = pd.DataFrame({\n", - " 'Metric': [\n", - " 'Total Unique Transcription Factors',\n", - " ],\n", - " 'Value': [\n", - " f\"{metadata['regulator_locus_tag'].nunique():,}\",\n", - " ]\n", - "})\n", - "display(tf_overview)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Setting Simple Filters" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Filter Analysis\n", - "========================================\n", - "\n", - "Applied Filters:\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Filter Component", - "rawType": "object", - "type": "string" - }, - { - "name": "Value", - "rawType": "object", - "type": "string" - } - ], - "ref": "6c3fe59a-d2b0-49aa-b067-c472c07db1e0", - "rows": [ - [ - "0", - "Time Point", - "15 minutes" - ], - [ - "1", - "Mechanism", - "ZEV" - ], - [ - "2", - "Restriction", - "P (Restriction)" - ], - [ - "3", - "SQL Filter", - "time = 15 AND mechanism = 'ZEV' AND restriction = 'P'" - ] - ], - "shape": { - "columns": 2, - "rows": 4 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Filter ComponentValue
0Time Point15 minutes
1MechanismZEV
2RestrictionP (Restriction)
3SQL Filtertime = 15 AND mechanism = 'ZEV' AND restrictio...
\n", - "
" - ], - "text/plain": [ - " Filter Component Value\n", - "0 Time Point 15 minutes\n", - "1 Mechanism ZEV\n", - "2 Restriction P (Restriction)\n", - "3 SQL Filter time = 15 AND mechanism = 'ZEV' AND restrictio..." - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Top Transcription Factors in Filtered Dataset:\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Locus Tag", - "rawType": "object", - "type": "string" - }, - { - "name": "Symbol", - "rawType": "object", - "type": "string" - }, - { - "name": "Records in Subset", - "rawType": "int64", - "type": "integer" - } - ], - "ref": "183800f8-03b3-4fcb-846a-7585ea8533b5", - "rows": [ - [ - "0", - "YPL016W", - "SWI1", - "18525" - ], - [ - "1", - "YEL009C", - "GCN4", - "12350" - ], - [ - "2", - "YPL133C", - "RDS2", - "12350" - ], - [ - "3", - "Z3EV", - "Z3EV", - "12350" - ], - [ - "4", - "YBL008W", - "HIR1", - "6175" - ], - [ - "5", - "YBL021C", - "HAP3", - "6175" - ], - [ - "6", - "YBL025W", - "RRN10", - "6175" - ], - [ - "7", - "YBR239C", - "ERT1", - "6175" - ] - ], - "shape": { - "columns": 3, - "rows": 8 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Locus TagSymbolRecords in Subset
0YPL016WSWI118525
1YEL009CGCN412350
2YPL133CRDS212350
3Z3EVZ3EV12350
4YBL008WHIR16175
5YBL021CHAP36175
6YBL025WRRN106175
7YBR239CERT16175
\n", - "
" - ], - "text/plain": [ - " Locus Tag Symbol Records in Subset\n", - "0 YPL016W SWI1 18525\n", - "1 YEL009C GCN4 12350\n", - "2 YPL133C RDS2 12350\n", - "3 Z3EV Z3EV 12350\n", - "4 YBL008W HIR1 6175\n", - "5 YBL021C HAP3 6175\n", - "6 YBL025W RRN10 6175\n", - "7 YBR239C ERT1 6175" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Set filters for 15-minute timepoint with ZEV mechanism and P restriction\n", - "api.set_filter(\"hackett_2020\", time=15, mechanism=\"ZEV\", restriction=\"P\")\n", - "\n", - "# Create comprehensive filter analysis\n", - "print(\"Filter Analysis\")\n", - "print(\"=\" * 40)\n", - "\n", - "# Show current filter\n", - "current_filter = api.get_current_filter(\"hackett_2020\")\n", - "filter_info = pd.DataFrame({\n", - " 'Filter Component': ['Time Point', 'Mechanism', 'Restriction', 'SQL Filter'],\n", - " 'Value': ['15 minutes', 'ZEV', 'P (Restriction)', current_filter]\n", - "})\n", - "\n", - "print(\"\\nApplied Filters:\")\n", - "display(filter_info)\n", - "\n", - "# Analyze filter impact\n", - "filtered_metadata = metadata[\n", - " (metadata['time'] == 15) & \n", - " (metadata['mechanism'] == 'ZEV') & \n", - " (metadata['restriction'] == 'P')\n", - "]\n", - "\n", - "# Show top TFs in filtered data\n", - "filtered_tf_summary = filtered_metadata.groupby(['regulator_locus_tag', 'regulator_symbol'])['count'].sum().sort_values(ascending=False).head(8)\n", - "tf_filtered_df = pd.DataFrame({\n", - " 'Locus Tag': [idx[0] for idx in filtered_tf_summary.index],\n", - " 'Symbol': [idx[1] for idx in filtered_tf_summary.index],\n", - " 'Records in Subset': filtered_tf_summary.values,\n", - "})\n", - "\n", - "print(\"\\nTop Transcription Factors in Filtered Dataset:\")\n", - "display(tf_filtered_df)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Query Data with Automatic Filter Application\n", - "\n", - "Now when we query the data, our filters will be automatically applied:" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Setting Complex SQL Filters\n", - "\n", - "For more sophisticated filtering, we can use the `set_sql_filter()` method with full SQL expressions:" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Complex SQL Filter Analysis\n", - "==================================================\n", - "\n", - "Applied Complex Filter:\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Filter Component", - "rawType": "object", - "type": "string" - }, - { - "name": "Value", - "rawType": "object", - "type": "string" - } - ], - "ref": "1063532f-04e7-453f-9fac-2261490f4701", - "rows": [ - [ - "0", - "Time Points", - "15, 30 minutes" - ], - [ - "1", - "Mechanism", - "ZEV (overexpression)" - ], - [ - "2", - "Restriction", - "P (restriction enzyme)" - ], - [ - "3", - "Selected TFs", - "YER040W (GLN3), YER028C (RSF2), YPL016W (SWI1)" - ], - [ - "4", - "Complete SQL Filter", - "time IN (15, 30) AND \n mechanism = 'ZEV' AND \n restriction = 'P' AND \n regulator_locus_tag IN ('YER040W', 'YER028C', 'YPL016W')" - ] - ], - "shape": { - "columns": 2, - "rows": 5 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Filter ComponentValue
0Time Points15, 30 minutes
1MechanismZEV (overexpression)
2RestrictionP (restriction enzyme)
3Selected TFsYER040W (GLN3), YER028C (RSF2), YPL016W (SWI1)
4Complete SQL Filtertime IN (15, 30) AND \\n mechanism = 'ZEV' A...
\n", - "
" - ], - "text/plain": [ - " Filter Component Value\n", - "0 Time Points 15, 30 minutes\n", - "1 Mechanism ZEV (overexpression)\n", - "2 Restriction P (restriction enzyme)\n", - "3 Selected TFs YER040W (GLN3), YER028C (RSF2), YPL016W (SWI1)\n", - "4 Complete SQL Filter time IN (15, 30) AND \\n mechanism = 'ZEV' A..." - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Set a complex filter for multiple time points and specific transcription factors\n", - "api.set_sql_filter(\"hackett_2020\", \"\"\"\n", - " time IN (15, 30) AND \n", - " mechanism = 'ZEV' AND \n", - " restriction = 'P' AND \n", - " regulator_locus_tag IN ('YER040W', 'YER028C', 'YPL016W')\n", - "\"\"\")\n", - "\n", - "print(\"Complex SQL Filter Analysis\")\n", - "print(\"=\" * 50)\n", - "\n", - "# Show filter details\n", - "filter_details = pd.DataFrame({\n", - " 'Filter Component': [\n", - " 'Time Points',\n", - " 'Mechanism', \n", - " 'Restriction',\n", - " 'Selected TFs',\n", - " 'Complete SQL Filter'\n", - " ],\n", - " 'Value': [\n", - " '15, 30 minutes',\n", - " 'ZEV (overexpression)',\n", - " 'P (restriction enzyme)',\n", - " 'YER040W (GLN3), YER028C (RSF2), YPL016W (SWI1)',\n", - " api.get_current_filter('hackett_2020')\n", - " ]\n", - "})\n", - "\n", - "print(\"\\nApplied Complex Filter:\")\n", - "display(filter_details)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [], - "source": [ - "## Query with the complex filter" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Time Course Comparison for Selected TFs:\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Locus Tag", - "rawType": "object", - "type": "string" - }, - { - "name": "Symbol", - "rawType": "object", - "type": "string" - }, - { - "name": "Time (min)", - "rawType": "float64", - "type": "float" - }, - { - "name": "Target Count", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Avg Response", - "rawType": "float64", - "type": "float" - }, - { - "name": "Strong Responders", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Max |Response|", - "rawType": "float64", - "type": "float" - }, - { - "name": "% Strong", - "rawType": "float64", - "type": "float" - } - ], - "ref": "fbfefef3-454e-4318-99ae-3d43d79d3800", - "rows": [ - [ - "0", - "YER028C", - "MIG3", - "15.0", - "6175", - "-0.01", - "99", - "5.894", - "1.6" - ], - [ - "1", - "YER028C", - "MIG3", - "30.0", - "6175", - "-0.028", - "246", - "5.516", - "4.0" - ], - [ - "2", - "YER040W", - "GLN3", - "15.0", - "6175", - "0.018", - "81", - "7.923", - "1.3" - ], - [ - "3", - "YER040W", - "GLN3", - "30.0", - "6175", - "0.042", - "631", - "10.459", - "10.2" - ], - [ - "4", - "YPL016W", - "SWI1", - "15.0", - "18525", - "0.001", - "431", - "6.216", - "2.3" - ], - [ - "5", - "YPL016W", - "SWI1", - "30.0", - "18525", - "0.033", - "762", - "6.753", - "4.1" - ] - ], - "shape": { - "columns": 8, - "rows": 6 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Locus TagSymbolTime (min)Target CountAvg ResponseStrong RespondersMax |Response|% Strong
0YER028CMIG315.06175-0.010995.8941.6
1YER028CMIG330.06175-0.0282465.5164.0
2YER040WGLN315.061750.018817.9231.3
3YER040WGLN330.061750.04263110.45910.2
4YPL016WSWI115.0185250.0014316.2162.3
5YPL016WSWI130.0185250.0337626.7534.1
\n", - "
" - ], - "text/plain": [ - " Locus Tag Symbol Time (min) Target Count Avg Response Strong Responders \\\n", - "0 YER028C MIG3 15.0 6175 -0.010 99 \n", - "1 YER028C MIG3 30.0 6175 -0.028 246 \n", - "2 YER040W GLN3 15.0 6175 0.018 81 \n", - "3 YER040W GLN3 30.0 6175 0.042 631 \n", - "4 YPL016W SWI1 15.0 18525 0.001 431 \n", - "5 YPL016W SWI1 30.0 18525 0.033 762 \n", - "\n", - " Max |Response| % Strong \n", - "0 5.894 1.6 \n", - "1 5.516 4.0 \n", - "2 7.923 1.3 \n", - "3 10.459 10.2 \n", - "4 6.216 2.3 \n", - "5 6.753 4.1 " - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Overall TF Performance Summary:\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Locus Tag", - "rawType": "object", - "type": "string" - }, - { - "name": "Symbol", - "rawType": "object", - "type": "string" - }, - { - "name": "Total Targets", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Total Strong", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Avg Response", - "rawType": "float64", - "type": "float" - }, - { - "name": "% Strong Overall", - "rawType": "float64", - "type": "float" - } - ], - "ref": "ca80d3c1-4fec-4613-9f73-12ba5359ed44", - "rows": [ - [ - "0", - "YER028C", - "MIG3", - "12350", - "345", - "-0.019", - "2.8" - ], - [ - "1", - "YER040W", - "GLN3", - "12350", - "712", - "0.03", - "5.8" - ], - [ - "2", - "YPL016W", - "SWI1", - "37050", - "1193", - "0.017", - "3.2" - ] - ], - "shape": { - "columns": 6, - "rows": 3 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Locus TagSymbolTotal TargetsTotal StrongAvg Response% Strong Overall
0YER028CMIG312350345-0.0192.8
1YER040WGLN3123507120.0305.8
2YPL016WSWI13705011930.0173.2
\n", - "
" - ], - "text/plain": [ - " Locus Tag Symbol Total Targets Total Strong Avg Response \\\n", - "0 YER028C MIG3 12350 345 -0.019 \n", - "1 YER040W GLN3 12350 712 0.030 \n", - "2 YPL016W SWI1 37050 1193 0.017 \n", - "\n", - " % Strong Overall \n", - "0 2.8 \n", - "1 5.8 \n", - "2 3.2 " - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "\n", - "time_comparison = api.query(\"\"\"\n", - " SELECT \n", - " regulator_locus_tag,\n", - " regulator_symbol,\n", - " time,\n", - " COUNT(*) as target_count,\n", - " ROUND(AVG(log2_shrunken_timecourses), 3) as avg_response,\n", - " COUNT(CASE WHEN ABS(log2_shrunken_timecourses) > 0.5 THEN 1 END) as strong_responders,\n", - " ROUND(MAX(ABS(log2_shrunken_timecourses)), 3) as max_abs_response\n", - " FROM hackett_2020 \n", - " GROUP BY regulator_locus_tag, regulator_symbol, time\n", - " ORDER BY regulator_locus_tag, time\n", - "\"\"\", \"hackett_2020\")\n", - "\n", - "# Format display\n", - "time_comparison_display = time_comparison.copy()\n", - "time_comparison_display.columns = ['Locus Tag', 'Symbol', 'Time (min)', 'Target Count', 'Avg Response', 'Strong Responders', 'Max |Response|']\n", - "time_comparison_display['% Strong'] = (time_comparison_display['Strong Responders'] / time_comparison_display['Target Count'] * 100).round(1)\n", - "\n", - "print(\"\\nTime Course Comparison for Selected TFs:\")\n", - "display(time_comparison_display)\n", - "\n", - "# Summary analysis\n", - "tf_summary = time_comparison.groupby(['regulator_locus_tag', 'regulator_symbol']).agg({\n", - " 'target_count': 'sum',\n", - " 'strong_responders': 'sum',\n", - " 'avg_response': 'mean'\n", - "}).reset_index()\n", - "\n", - "tf_summary['total_%_strong'] = (tf_summary['strong_responders'] / tf_summary['target_count'] * 100).round(1)\n", - "tf_summary_display = tf_summary.copy()\n", - "tf_summary_display.columns = ['Locus Tag', 'Symbol', 'Total Targets', 'Total Strong', 'Avg Response', '% Strong Overall']\n", - "\n", - "print(\"\\nOverall TF Performance Summary:\")\n", - "display(tf_summary_display)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Filter Management\n", - "\n", - "The filtering system provides full control over active filters:" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "🔧 Filter Management Demonstration\n", - "==================================================\n", - "\n", - "Current filter:\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Status", - "rawType": "object", - "type": "string" - }, - { - "name": "SQL WHERE clause", - "rawType": "object", - "type": "string" - } - ], - "ref": "e93f9433-b31e-49db-b98d-5c5b4116e1d6", - "rows": [ - [ - "0", - "Active Filter", - "time IN (15, 30) AND \n mechanism = 'ZEV' AND \n restriction = 'P' AND \n regulator_locus_tag IN ('YER040W', 'YER028C', 'YPL016W')" - ] - ], - "shape": { - "columns": 2, - "rows": 1 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
StatusSQL WHERE clause
0Active Filtertime IN (15, 30) AND \\n mechanism = 'ZEV' A...
\n", - "
" - ], - "text/plain": [ - " Status SQL WHERE clause\n", - "0 Active Filter time IN (15, 30) AND \\n mechanism = 'ZEV' A..." - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "After clearing filters:\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Status", - "rawType": "object", - "type": "string" - }, - { - "name": "SQL WHERE clause", - "rawType": "object", - "type": "string" - } - ], - "ref": "7ca71950-6a31-404e-baa4-d096e56c4991", - "rows": [ - [ - "0", - "After Clearing", - "None" - ] - ], - "shape": { - "columns": 2, - "rows": 1 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
StatusSQL WHERE clause
0After ClearingNone
\n", - "
" - ], - "text/plain": [ - " Status SQL WHERE clause\n", - "0 After Clearing None" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "📊 Dataset Size Comparison:\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Dataset State", - "rawType": "object", - "type": "string" - }, - { - "name": "Total Records", - "rawType": "object", - "type": "string" - }, - { - "name": "Percentage", - "rawType": "object", - "type": "string" - } - ], - "ref": "d30810e1-4013-4929-a14a-522ee08c48fe", - "rows": [ - [ - "0", - "Unfiltered (Full Dataset)", - "10,454,275", - "100.0%" - ], - [ - "1", - "Filtered (time=15, ZEV, P)", - "1,012,700", - "9.7%" - ], - [ - "2", - "Data Reduction", - "9,441,575", - "90.3%" - ] - ], - "shape": { - "columns": 3, - "rows": 3 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Dataset StateTotal RecordsPercentage
0Unfiltered (Full Dataset)10,454,275100.0%
1Filtered (time=15, ZEV, P)1,012,7009.7%
2Data Reduction9,441,57590.3%
\n", - "
" - ], - "text/plain": [ - " Dataset State Total Records Percentage\n", - "0 Unfiltered (Full Dataset) 10,454,275 100.0%\n", - "1 Filtered (time=15, ZEV, P) 1,012,700 9.7%\n", - "2 Data Reduction 9,441,575 90.3%" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Complete HfQueryAPI Workflow:\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Step", - "rawType": "object", - "type": "string" - }, - { - "name": "Method", - "rawType": "object", - "type": "string" - }, - { - "name": "Purpose", - "rawType": "object", - "type": "string" - } - ], - "ref": "f8e9e25a-c9d1-479d-b664-f70bac590d68", - "rows": [ - [ - "0", - "1. Explore Metadata", - "api.get_metadata()", - "Understand dataset structure" - ], - [ - "1", - "2. Set Simple Filters", - "api.set_filter(config, **kwargs)", - "Filter by metadata values" - ], - [ - "2", - "3. Set Complex SQL Filters", - "api.set_sql_filter(config, sql)", - "Complex multi-condition filtering" - ], - [ - "3", - "4. Query with Auto-Apply", - "api.query(sql, config)", - "Analyze with automatic filtering" - ], - [ - "4", - "5. Clear/Manage Filters", - "api.clear_filter() / get_current_filter()", - "Reset or inspect current state" - ] - ], - "shape": { - "columns": 3, - "rows": 5 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
StepMethodPurpose
01. Explore Metadataapi.get_metadata()Understand dataset structure
12. Set Simple Filtersapi.set_filter(config, **kwargs)Filter by metadata values
23. Set Complex SQL Filtersapi.set_sql_filter(config, sql)Complex multi-condition filtering
34. Query with Auto-Applyapi.query(sql, config)Analyze with automatic filtering
45. Clear/Manage Filtersapi.clear_filter() / get_current_filter()Reset or inspect current state
\n", - "
" - ], - "text/plain": [ - " Step Method \\\n", - "0 1. Explore Metadata api.get_metadata() \n", - "1 2. Set Simple Filters api.set_filter(config, **kwargs) \n", - "2 3. Set Complex SQL Filters api.set_sql_filter(config, sql) \n", - "3 4. Query with Auto-Apply api.query(sql, config) \n", - "4 5. Clear/Manage Filters api.clear_filter() / get_current_filter() \n", - "\n", - " Purpose \n", - "0 Understand dataset structure \n", - "1 Filter by metadata values \n", - "2 Complex multi-condition filtering \n", - "3 Analyze with automatic filtering \n", - "4 Reset or inspect current state " - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Demonstrate filter management capabilities\n", - "print(\"🔧 Filter Management Demonstration\")\n", - "print(\"=\" * 50)\n", - "\n", - "# Show current filter\n", - "current_filter = api.get_current_filter('hackett_2020')\n", - "print(f\"\\nCurrent filter:\")\n", - "current_filter_df = pd.DataFrame({\n", - " 'Status': ['Active Filter'],\n", - " 'SQL WHERE clause': [current_filter if current_filter else 'None']\n", - "})\n", - "display(current_filter_df)\n", - "\n", - "# Clear all filters and show impact\n", - "api.clear_filter(\"hackett_2020\")\n", - "print(f\"\\nAfter clearing filters:\")\n", - "cleared_filter_df = pd.DataFrame({\n", - " 'Status': ['After Clearing'],\n", - " 'SQL WHERE clause': [api.get_current_filter('hackett_2020') or 'None']\n", - "})\n", - "display(cleared_filter_df)\n", - "\n", - "# Query unfiltered vs filtered data comparison\n", - "total_records = api.query(\"SELECT COUNT(*) as total FROM hackett_2020\", \"hackett_2020\")\n", - "\n", - "# Set filters again for comparison\n", - "api.set_filter(\"hackett_2020\", time=15, mechanism=\"ZEV\", restriction=\"P\")\n", - "filtered_records = api.query(\"SELECT COUNT(*) as total FROM hackett_2020\", \"hackett_2020\")\n", - "\n", - "# Create comprehensive comparison table\n", - "comparison_results = pd.DataFrame({\n", - " 'Dataset State': [\n", - " 'Unfiltered (Full Dataset)',\n", - " 'Filtered (time=15, ZEV, P)',\n", - " 'Data Reduction'\n", - " ],\n", - " 'Total Records': [\n", - " f\"{total_records.iloc[0]['total']:,}\",\n", - " f\"{filtered_records.iloc[0]['total']:,}\",\n", - " f\"{total_records.iloc[0]['total'] - filtered_records.iloc[0]['total']:,}\"\n", - " ],\n", - " 'Percentage': [\n", - " '100.0%',\n", - " f\"{(filtered_records.iloc[0]['total'] / total_records.iloc[0]['total'] * 100):.1f}%\",\n", - " f\"{((total_records.iloc[0]['total'] - filtered_records.iloc[0]['total']) / total_records.iloc[0]['total'] * 100):.1f}%\"\n", - " ]\n", - "})\n", - "\n", - "print(\"\\n📊 Dataset Size Comparison:\")\n", - "display(comparison_results)\n", - "\n", - "# Show filter workflow summary\n", - "workflow_summary = pd.DataFrame({\n", - " 'Step': [\n", - " '1. Explore Metadata',\n", - " '2. Set Simple Filters', \n", - " '3. Set Complex SQL Filters',\n", - " '4. Query with Auto-Apply',\n", - " '5. Clear/Manage Filters'\n", - " ],\n", - " 'Method': [\n", - " 'api.get_metadata()',\n", - " 'api.set_filter(config, **kwargs)',\n", - " 'api.set_sql_filter(config, sql)',\n", - " 'api.query(sql, config)',\n", - " 'api.clear_filter() / get_current_filter()'\n", - " ],\n", - " 'Purpose': [\n", - " 'Understand dataset structure',\n", - " 'Filter by metadata values',\n", - " 'Complex multi-condition filtering',\n", - " 'Analyze with automatic filtering',\n", - " 'Reset or inspect current state'\n", - " ]\n", - "})\n", - "\n", - "print(\"\\nComplete HfQueryAPI Workflow:\")\n", - "display(workflow_summary)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "tfbpapi-py3.11", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.9" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/tutorials/metadata_builder_tutorial.ipynb b/docs/tutorials/metadata_builder_tutorial.ipynb deleted file mode 100644 index 774f413..0000000 --- a/docs/tutorials/metadata_builder_tutorial.ipynb +++ /dev/null @@ -1,938 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# MetadataBuilder Tutorial\n", - "\n", - "This tutorial demonstrates how to use the `MetadataBuilder` to create standardized metadata tables across heterogeneous datasets using optional alias mappings.\n", - "\n", - "## Overview\n", - "\n", - "The `MetadataBuilder` solves a common problem when working with multiple genomics datasets: **factor levels are named inconsistently across datasets**. For example, carbon sources might be labeled as \"D-glucose\", \"dextrose\", \"glucose\", etc.\n", - "\n", - "**Key difference from filtering**: This system does NOT filter or exclude any data. Instead, it normalizes factor level names to create standardized metadata views. ALL data is included, just with standardized names.\n", - "\n", - "You specify:\n", - "\n", - "1. **Factor aliases** (optional): Mappings to normalize factor level names (e.g., `glucose: [\"D-glucose\", \"dextrose\"]`)\n", - "2. **Property mappings**: Where to find each property in each dataset (e.g., repo-level vs field-level)\n", - "\n", - "## Key Features\n", - "\n", - "- **No filtering**: ALL data is included with normalized names\n", - "- **Optional aliases**: If no alias is configured, original values pass through unchanged\n", - "- **Case-insensitive matching**: \"D-glucose\" matches \"d-glucose\"\n", - "- **Three output modes**:\n", - " - `conditions`: Just normalized metadata (no data retrieval)\n", - " - `samples`: Sample-level metadata (one row per sample_id)\n", - " - `full_data`: All measurements with normalized metadata\n", - "- **External configuration**: No hardcoded dataset logic in your analysis code" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Setup" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chase/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", - " from .autonotebook import tqdm as notebook_tqdm\n" - ] - } - ], - "source": [ - "from pathlib import Path\n", - "import yaml\n", - "import pandas as pd\n", - "from tfbpapi.datainfo.metadata_builder import MetadataBuilder, normalize_value\n", - "from tfbpapi.datainfo import DataCard" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Understanding Dataset Structures\n", - "\n", - "Before normalizing, let's examine how experimental conditions are structured in two representative datasets.\n", - "\n", - "### Repo-Level Conditions: Kemmeren 2014\n", - "\n", - "All samples in this dataset share the same experimental conditions defined at the repository level." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Kemmeren 2014 - Repo-Level Experimental Conditions\n", - "============================================================\n", - "Temperature: 30°C\n", - "Media: synthetic_complete\n", - "Carbon source: ['D-glucose']\n", - "\n", - "All samples in this dataset share these conditions.\n" - ] - } - ], - "source": [ - "# Load Kemmeren 2014 datacard\n", - "kemmeren_card = DataCard(\"BrentLab/kemmeren_2014\")\n", - "\n", - "# Get repo-level experimental conditions\n", - "exp_conds = kemmeren_card.get_experimental_conditions(\"kemmeren_2014\")\n", - "\n", - "print(\"Kemmeren 2014 - Repo-Level Experimental Conditions\")\n", - "print(\"=\" * 60)\n", - "print(f\"Temperature: {exp_conds.get('temperature_celsius')}°C\")\n", - "print(f\"Media: {exp_conds.get('media', {}).get('name')}\")\n", - "\n", - "carbon_source = exp_conds.get('media', {}).get('carbon_source', [])\n", - "if carbon_source:\n", - " compounds = [cs.get('compound') for cs in carbon_source]\n", - " print(f\"Carbon source: {compounds}\")\n", - "\n", - "print(\"\\nAll samples in this dataset share these conditions.\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Field-Level Conditions: Harbison 2004\n", - "\n", - "This dataset has different experimental conditions for different samples, defined in the `condition` field's definitions." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Field: condition\n", - "Levels: 14\n", - "\n", - "YPD:\n", - " media.name: YPD\n", - " media.carbon_source: [{'compound': 'D-glucose', 'concentration_percent': 2}]\n", - "\n", - "SM:\n", - " media.name: synthetic_complete\n", - " media.carbon_source: None\n", - "\n", - "RAPA:\n", - " media.name: YPD\n", - " media.carbon_source: [{'compound': 'D-glucose', 'concentration_percent': 2}]\n", - "\n", - "H2O2Hi:\n", - " media.name: YPD\n", - " media.carbon_source: [{'compound': 'D-glucose', 'concentration_percent': 2}]\n", - "\n", - "H2O2Lo:\n", - " media.name: YPD\n", - " media.carbon_source: [{'compound': 'D-glucose', 'concentration_percent': 2}]\n", - "\n", - "Acid:\n", - " media.name: YPD\n", - " media.carbon_source: [{'compound': 'D-glucose', 'concentration_percent': 2}]\n", - "\n", - "... and 8 more levels\n" - ] - } - ], - "source": [ - "# Load Harbison 2004 datacard\n", - "harbison_card = DataCard(\"BrentLab/harbison_2004\")\n", - "\n", - "# Get a summary of condition field levels\n", - "print(harbison_card.summarize_field_levels(\n", - " \"harbison_2004\",\n", - " \"condition\",\n", - " properties=[\"media.name\", \"media.carbon_source\"],\n", - " max_levels=6\n", - "))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Example 8: Extracting Properties from Field Definitions\n", - "\n", - "The MetadataBuilder can extract properties from field definitions and add them as new columns. This is useful for creating analysis-ready metadata tables.\n", - "\n", - "For harbison_2004, the base metadata has `condition` as a field, but we want to extract:\n", - "- `growth_media` from `media.name`\n", - "- `carbon_source` from `media.carbon_source.compound`\n", - "\n", - "And normalize carbon source names using aliases." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Fetching 1 files: 100%|██████████| 1/1 [00:00<00:00, 2.88it/s]\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "sample_id", - "rawType": "int32", - "type": "integer" - }, - { - "name": "regulator_locus_tag", - "rawType": "object", - "type": "string" - }, - { - "name": "regulator_symbol", - "rawType": "object", - "type": "string" - }, - { - "name": "condition", - "rawType": "object", - "type": "string" - }, - { - "name": "carbon_source", - "rawType": "object", - "type": "unknown" - }, - { - "name": "growth_media", - "rawType": "object", - "type": "string" - } - ], - "ref": "95a0f05b-86e0-4ac9-bda0-b91b5f5e4de5", - "rows": [ - [ - "0", - "1", - "YSC0017", - "MATA1", - "YPD", - "glucose", - "YPD" - ], - [ - "1", - "2", - "YAL051W", - "OAF1", - "YPD", - "glucose", - "YPD" - ], - [ - "2", - "3", - "YBL005W", - "PDR3", - "YPD", - "glucose", - "YPD" - ], - [ - "3", - "4", - "YBL008W", - "HIR1", - "YPD", - "glucose", - "YPD" - ], - [ - "4", - "5", - "YBL021C", - "HAP3", - "YPD", - "glucose", - "YPD" - ], - [ - "5", - "6", - "YBL054W", - "TOD6", - "YPD", - "glucose", - "YPD" - ], - [ - "6", - "7", - "YBL103C", - "RTG3", - "H2O2Hi", - "glucose", - "YPD" - ], - [ - "7", - "8", - "YBL103C", - "RTG3", - "H2O2Lo", - "glucose", - "YPD" - ], - [ - "8", - "9", - "YBL103C", - "RTG3", - "RAPA", - "glucose", - "YPD" - ], - [ - "9", - "10", - "YBL103C", - "RTG3", - "SM", - null, - "synthetic_complete" - ], - [ - "10", - "11", - "YBL103C", - "RTG3", - "YPD", - "glucose", - "YPD" - ], - [ - "11", - "12", - "YBR033W", - "EDS1", - "YPD", - "glucose", - "YPD" - ], - [ - "12", - "13", - "YBR049C", - "REB1", - "H2O2Hi", - "glucose", - "YPD" - ], - [ - "13", - "14", - "YBR049C", - "REB1", - "H2O2Lo", - "glucose", - "YPD" - ], - [ - "14", - "15", - "YBR049C", - "REB1", - "YPD", - "glucose", - "YPD" - ], - [ - "15", - "16", - "YBR083W", - "TEC1", - "Alpha", - "glucose", - "YPD" - ], - [ - "16", - "17", - "YBR083W", - "TEC1", - "BUT14", - "glucose", - "YPD" - ], - [ - "17", - "18", - "YBR083W", - "TEC1", - "YPD", - "glucose", - "YPD" - ], - [ - "18", - "19", - "YBR150C", - "TBS1", - "YPD", - "glucose", - "YPD" - ], - [ - "19", - "20", - "YBR182C", - "SMP1", - "YPD", - "glucose", - "YPD" - ], - [ - "20", - "21", - "YBR239C", - "ERT1", - "YPD", - "glucose", - "YPD" - ], - [ - "21", - "22", - "YBR240C", - "THI2", - "Thi-", - null, - "synthetic_complete_minus_thiamine" - ], - [ - "22", - "23", - "YBR240C", - "THI2", - "YPD", - "glucose", - "YPD" - ], - [ - "23", - "24", - "YBR267W", - "REI1", - "YPD", - "glucose", - "YPD" - ], - [ - "24", - "25", - "YBR297W", - "MAL33", - "H2O2Hi", - "glucose", - "YPD" - ], - [ - "25", - "26", - "YBR297W", - "MAL33", - "H2O2Lo", - "glucose", - "YPD" - ], - [ - "26", - "27", - "YBR297W", - "MAL33", - "YPD", - "glucose", - "YPD" - ], - [ - "27", - "28", - "YCR018C", - "SRD1", - "YPD", - "glucose", - "YPD" - ], - [ - "28", - "29", - "YCR106W", - "RDS1", - "H2O2Hi", - "glucose", - "YPD" - ], - [ - "29", - "30", - "YCR106W", - "RDS1", - "YPD", - "glucose", - "YPD" - ], - [ - "30", - "31", - "YDL020C", - "RPN4", - "H2O2Hi", - "glucose", - "YPD" - ], - [ - "31", - "32", - "YDL020C", - "RPN4", - "H2O2Lo", - "glucose", - "YPD" - ], - [ - "32", - "33", - "YDL020C", - "RPN4", - "YPD", - "glucose", - "YPD" - ], - [ - "33", - "34", - "YDL048C", - "STP4", - "YPD", - "glucose", - "YPD" - ], - [ - "34", - "35", - "YDL056W", - "MBP1", - "H2O2Hi", - "glucose", - "YPD" - ], - [ - "35", - "36", - "YDL056W", - "MBP1", - "H2O2Lo", - "glucose", - "YPD" - ], - [ - "36", - "37", - "YDL056W", - "MBP1", - "YPD", - "glucose", - "YPD" - ], - [ - "37", - "38", - "YDL106C", - "PHO2", - "H2O2Hi", - "glucose", - "YPD" - ], - [ - "38", - "39", - "YDL106C", - "PHO2", - "H2O2Lo", - "glucose", - "YPD" - ], - [ - "39", - "40", - "YDL106C", - "PHO2", - "Pi-", - null, - "synthetic_complete_minus_phosphate" - ], - [ - "40", - "41", - "YDL106C", - "PHO2", - "SM", - null, - "synthetic_complete" - ], - [ - "41", - "42", - "YDL106C", - "PHO2", - "YPD", - "glucose", - "YPD" - ], - [ - "42", - "43", - "YDL166C", - "FAP7", - "YPD", - "glucose", - "YPD" - ], - [ - "43", - "44", - "YDL170W", - "UGA3", - "RAPA", - "glucose", - "YPD" - ], - [ - "44", - "45", - "YDL170W", - "UGA3", - "SM", - null, - "synthetic_complete" - ], - [ - "45", - "46", - "YDL170W", - "UGA3", - "YPD", - "glucose", - "YPD" - ], - [ - "46", - "47", - "YDR009W", - "GAL3", - "YPD", - "glucose", - "YPD" - ], - [ - "47", - "48", - "YDR026C", - "NSI1", - "YPD", - "glucose", - "YPD" - ], - [ - "48", - "49", - "YDR043C", - "NRG1", - "H2O2Hi", - "glucose", - "YPD" - ], - [ - "49", - "50", - "YDR043C", - "NRG1", - "H2O2Lo", - "glucose", - "YPD" - ] - ], - "shape": { - "columns": 6, - "rows": 352 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sample_idregulator_locus_tagregulator_symbolconditioncarbon_sourcegrowth_media
01YSC0017MATA1YPDglucoseYPD
12YAL051WOAF1YPDglucoseYPD
23YBL005WPDR3YPDglucoseYPD
34YBL008WHIR1YPDglucoseYPD
45YBL021CHAP3YPDglucoseYPD
.....................
347348YPR104CFHL1YPDglucoseYPD
348349YPR196WYPR196WYPDglucoseYPD
349350YPR199CARR1H2O2HiglucoseYPD
350351YPR199CARR1YPDglucoseYPD
351352YKL043WPHD1BUT14glucoseYPD
\n", - "

352 rows × 6 columns

\n", - "
" - ], - "text/plain": [ - " sample_id regulator_locus_tag regulator_symbol condition carbon_source \\\n", - "0 1 YSC0017 MATA1 YPD glucose \n", - "1 2 YAL051W OAF1 YPD glucose \n", - "2 3 YBL005W PDR3 YPD glucose \n", - "3 4 YBL008W HIR1 YPD glucose \n", - "4 5 YBL021C HAP3 YPD glucose \n", - ".. ... ... ... ... ... \n", - "347 348 YPR104C FHL1 YPD glucose \n", - "348 349 YPR196W YPR196W YPD glucose \n", - "349 350 YPR199C ARR1 H2O2Hi glucose \n", - "350 351 YPR199C ARR1 YPD glucose \n", - "351 352 YKL043W PHD1 BUT14 glucose \n", - "\n", - " growth_media \n", - "0 YPD \n", - "1 YPD \n", - "2 YPD \n", - "3 YPD \n", - "4 YPD \n", - ".. ... \n", - "347 YPD \n", - "348 YPD \n", - "349 YPD \n", - "350 YPD \n", - "351 YPD \n", - "\n", - "[352 rows x 6 columns]" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Configuration with property extraction and aliases\n", - "extraction_config = {\n", - " \"factor_aliases\": {\n", - " \"carbon_source\": {\n", - " \"glucose\": [\"D-glucose\", \"Glucose\", \"dextrose\", \"Dextrose\"],\n", - " \"galactose\": [\"D-galactose\", \"Galactose\"],\n", - " \"raffinose\": [\"D-raffinose\", \"Raffinose\"]\n", - " }\n", - " },\n", - " \"BrentLab/harbison_2004\": {\n", - " \"dataset\": {\n", - " \"harbison_2004\": {\n", - " \"growth_media\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"media.name\"\n", - " },\n", - " \"carbon_source\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"media.carbon_source.compound\"\n", - " }\n", - " }\n", - " }\n", - " }\n", - "}\n", - "\n", - "config_path_extraction = Path(\"/tmp/metadata_extraction.yaml\")\n", - "with open(config_path_extraction, 'w') as f:\n", - " yaml.dump(extraction_config, f)\n", - "\n", - "# Build metadata with extraction\n", - "builder_extraction = MetadataBuilder(config_path_extraction)\n", - "results_extraction = builder_extraction.build_metadata(\n", - " repos=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", - " mode=\"samples\"\n", - ")\n", - "\n", - "df_extracted = results_extraction[\"BrentLab/harbison_2004\"][\"data\"]\n", - "\n", - "df_extracted\n", - "\n", - "# print(f\"Columns: {list(df_extracted.columns)}\")\n", - "# print(f\"\\nShape: {df_extracted.shape}\")\n", - "# print(\"\\nSample data showing base + extracted columns:\")\n", - "# print(df_extracted[['sample_id', 'condition', 'growth_media', 'carbon_source']].head(10))" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "tfbpapi-py3.11", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.9" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/tutorials/metadata_manager_tutorial.ipynb b/docs/tutorials/metadata_manager_tutorial.ipynb deleted file mode 100644 index 891df65..0000000 --- a/docs/tutorials/metadata_manager_tutorial.ipynb +++ /dev/null @@ -1,586 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# MetadataManager Tutorial: Enriching Data with Condition Metadata\n", - "\n", - "This tutorial demonstrates how to create enriched metadata views by combining:\n", - "1. Actual data fields from HuggingFace datasets (via HfQueryAPI)\n", - "2. Condition metadata from DataCard definitions\n", - "\n", - "We'll use the **BrentLab/harbison_2004** ChIP-chip dataset, which has 14 experimental conditions with detailed metadata about media composition, temperature, and other environmental factors.\n", - "\n", - "## Why Enrich Metadata?\n", - "\n", - "The raw data contains a `condition` field with values like \"YPD\", \"HEAT\", \"GAL\". But to analyze data by media type or carbon source, we need to:\n", - "- Extract nested information from condition definitions\n", - "- Create queryable columns for filtering and grouping\n", - "- Join this enriched metadata with the actual measurements\n", - "\n", - "This tutorial teaches you how to:\n", - "1. Use DataCard to explore nested condition metadata\n", - "2. Manually construct SQL CASE expressions to enrich your data\n", - "3. Create metadata views that combine data with DataCard information" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 1. Setup and Imports" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [], - "source": [ - "from tfbpapi.datainfo import DataCard\n", - "from tfbpapi import HfQueryAPI\n", - "import duckdb\n", - "import pandas as pd\n", - "import json\n", - "\n", - "pd.set_option('display.max_columns', None)\n", - "pd.set_option('display.width', None)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2. Explore Condition Metadata with DataCard\n", - "\n", - "First, let's load the DataCard and explore what condition metadata is available." - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Dataset type: DatasetType.ANNOTATED_FEATURES\n", - "Number of features: 7\n", - "\n", - "Feature names:\n", - " - condition ({'class_label': {'names': ['YPD', 'SM', 'RAPA', 'H2O2Hi', 'H2O2Lo', 'Acid', 'Alpha', 'BUT14', 'BUT90', 'Thi-', 'GAL', 'HEAT', 'Pi-', 'RAFF']}})\n", - " - regulator_locus_tag (string)\n", - " - regulator_symbol (string)\n", - " - target_locus_tag (string)\n", - " - target_symbol (string)\n", - " - effect (float64)\n", - " - pvalue (float64)\n" - ] - } - ], - "source": [ - "# Load DataCard\n", - "card = DataCard('BrentLab/harbison_2004')\n", - "\n", - "# Get the config to understand the data structure\n", - "config = card.get_config('harbison_2004')\n", - "print(f\"Dataset type: {config.dataset_type}\")\n", - "print(f\"Number of features: {len(config.dataset_info.features)}\")\n", - "print(f\"\\nFeature names:\")\n", - "for feature in config.dataset_info.features:\n", - " print(f\" - {feature.name} ({feature.dtype})\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Extract Media Specifications Using get_field_attribute()\n", - "\n", - "The new `get_field_attribute()` method makes it easy to extract nested specifications from field definitions. Let's use it to explore the media specifications for each condition." - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [ - { - "ename": "AttributeError", - "evalue": "'DataCard' object has no attribute 'get_field_attribute'", - "output_type": "error", - "traceback": [ - "\u001b[31m---------------------------------------------------------------------------\u001b[39m", - "\u001b[31mAttributeError\u001b[39m Traceback (most recent call last)", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[34]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;66;03m# Extract media specifications for all conditions\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m media_specs = \u001b[43mcard\u001b[49m\u001b[43m.\u001b[49m\u001b[43mget_field_attribute\u001b[49m(\u001b[33m'\u001b[39m\u001b[33mharbison_2004\u001b[39m\u001b[33m'\u001b[39m, \u001b[33m'\u001b[39m\u001b[33mcondition\u001b[39m\u001b[33m'\u001b[39m, \u001b[33m'\u001b[39m\u001b[33mmedia\u001b[39m\u001b[33m'\u001b[39m)\n\u001b[32m 4\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mFound media specifications for \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mlen\u001b[39m(media_specs)\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m conditions\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m\"\u001b[39m)\n\u001b[32m 6\u001b[39m \u001b[38;5;66;03m# Display them in a readable format\u001b[39;00m\n", - "\u001b[31mAttributeError\u001b[39m: 'DataCard' object has no attribute 'get_field_attribute'" - ] - } - ], - "source": [ - "# Extract media specifications for all conditions\n", - "media_specs = card.get_field_attribute('harbison_2004', 'condition', 'media')\n", - "\n", - "print(f\"Found media specifications for {len(media_specs)} conditions\\n\")\n", - "\n", - "# Display them in a readable format\n", - "for condition in sorted(media_specs.keys()):\n", - " print(f\"\\n{condition}:\")\n", - " print(json.dumps(media_specs[condition], indent=2))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Understanding the Nested Structure\n", - "\n", - "Notice that each media specification contains:\n", - "- `name`: The media name (e.g., \"YPD\", \"synthetic_complete\")\n", - "- `carbon_source`: Either a list of dicts with compound/concentration, or \"unspecified\"\n", - "- `nitrogen_source`: Either a list of dicts with compound/concentration, or \"unspecified\"\n", - "- Sometimes `additives`: Additional compounds like butanol\n", - "\n", - "To create queryable metadata columns, we need to flatten these nested structures." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 3. Load Data with HfQueryAPI\n", - "\n", - "Now let's load the actual data using HfQueryAPI. We'll create a shared DuckDB connection that we can use for building our enriched metadata view." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Create shared DuckDB connection\n", - "conn = duckdb.connect(':memory:')\n", - "\n", - "# Initialize HfQueryAPI with shared connection\n", - "api = HfQueryAPI('BrentLab/harbison_2004', duckdb_conn=conn)\n", - "\n", - "# Load the harbison_2004 config data\n", - "api.load_config('harbison_2004')\n", - "\n", - "print(\"Data loaded successfully!\")\n", - "print(\"\\nAvailable views in DuckDB:\")\n", - "views = conn.execute(\"SHOW TABLES\").fetchall()\n", - "for view in views:\n", - " print(f\" - {view[0]}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Examine the Base Data\n", - "\n", - "Let's look at the structure of the base data to see what fields are available." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Query the base view\n", - "base_df = conn.execute(\"\"\"\n", - " SELECT *\n", - " FROM harbison_2004_train\n", - " LIMIT 5\n", - "\"\"\").df()\n", - "\n", - "print(f\"Base data shape: {base_df.shape}\")\n", - "print(f\"\\nColumn names: {list(base_df.columns)}\")\n", - "print(\"\\nSample rows:\")\n", - "display(base_df)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Check unique conditions in the data\n", - "conditions_df = conn.execute(\"\"\"\n", - " SELECT DISTINCT condition\n", - " FROM harbison_2004_train\n", - " ORDER BY condition\n", - "\"\"\").df()\n", - "\n", - "print(f\"Unique conditions in data: {len(conditions_df)}\")\n", - "print(conditions_df['condition'].tolist())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 4. Flatten Media Specifications\n", - "\n", - "Before we can build SQL CASE expressions, we need to flatten the nested media specifications into simple strings. We'll create helper functions to extract:\n", - "- Media name\n", - "- Carbon source (as comma-separated compound names)\n", - "- Nitrogen source (as comma-separated compound names)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def flatten_carbon_source(media_spec):\n", - " \"\"\"Extract carbon source as comma-separated string.\"\"\"\n", - " if media_spec == 'unspecified':\n", - " return 'unspecified'\n", - " \n", - " carbon = media_spec.get('carbon_source', 'unspecified')\n", - " if carbon == 'unspecified':\n", - " return 'unspecified'\n", - " \n", - " if isinstance(carbon, list):\n", - " compounds = [c['compound'] for c in carbon]\n", - " return ', '.join(compounds)\n", - " \n", - " return str(carbon)\n", - "\n", - "def flatten_nitrogen_source(media_spec):\n", - " \"\"\"Extract nitrogen source as comma-separated string.\"\"\"\n", - " if media_spec == 'unspecified':\n", - " return 'unspecified'\n", - " \n", - " nitrogen = media_spec.get('nitrogen_source', 'unspecified')\n", - " if nitrogen == 'unspecified':\n", - " return 'unspecified'\n", - " \n", - " if isinstance(nitrogen, list):\n", - " compounds = [n['compound'] for n in nitrogen]\n", - " return ', '.join(compounds)\n", - " \n", - " return str(nitrogen)\n", - "\n", - "# Create flattened mapping for all conditions\n", - "flattened_media = {}\n", - "for condition, media_spec in media_specs.items():\n", - " flattened_media[condition] = {\n", - " 'media_name': media_spec.get('name', 'unspecified') if media_spec != 'unspecified' else 'unspecified',\n", - " 'carbon_source': flatten_carbon_source(media_spec),\n", - " 'nitrogen_source': flatten_nitrogen_source(media_spec)\n", - " }\n", - "\n", - "# Display flattened mapping\n", - "print(\"Flattened media specifications:\\n\")\n", - "for condition in sorted(flattened_media.keys()):\n", - " print(f\"{condition}:\")\n", - " for key, value in flattened_media[condition].items():\n", - " print(f\" {key}: {value}\")\n", - " print()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 5. Build SQL CASE Expressions Manually\n", - "\n", - "Now we'll construct SQL CASE expressions to map each condition value to its media specifications. This is the core of creating enriched metadata." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Build CASE expression for media_name\n", - "media_name_cases = []\n", - "for condition, specs in sorted(flattened_media.items()):\n", - " media_name_cases.append(f\" WHEN '{condition}' THEN '{specs['media_name']}'\")\n", - "media_name_sql = \"\\n\".join(media_name_cases)\n", - "\n", - "# Build CASE expression for carbon_source\n", - "carbon_source_cases = []\n", - "for condition, specs in sorted(flattened_media.items()):\n", - " carbon_source_cases.append(f\" WHEN '{condition}' THEN '{specs['carbon_source']}'\")\n", - "carbon_source_sql = \"\\n\".join(carbon_source_cases)\n", - "\n", - "# Build CASE expression for nitrogen_source\n", - "nitrogen_source_cases = []\n", - "for condition, specs in sorted(flattened_media.items()):\n", - " nitrogen_source_cases.append(f\" WHEN '{condition}' THEN '{specs['nitrogen_source']}'\")\n", - "nitrogen_source_sql = \"\\n\".join(nitrogen_source_cases)\n", - "\n", - "# Construct the complete CREATE VIEW SQL\n", - "create_view_sql = f\"\"\"\n", - "CREATE OR REPLACE VIEW harbison_2004_enriched_metadata AS\n", - "SELECT\n", - " sample_id,\n", - " regulator_locus_tag,\n", - " regulator_symbol,\n", - " condition,\n", - " CASE condition\n", - "{media_name_sql}\n", - " END AS media_name,\n", - " CASE condition\n", - "{carbon_source_sql}\n", - " END AS carbon_source,\n", - " CASE condition\n", - "{nitrogen_source_sql}\n", - " END AS nitrogen_source\n", - "FROM harbison_2004_train\n", - "\"\"\"\n", - "\n", - "print(\"Generated SQL:\")\n", - "print(\"=\"*80)\n", - "print(create_view_sql)\n", - "print(\"=\"*80)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 6. Execute SQL and Create Enriched Metadata View\n", - "\n", - "Now let's execute the SQL to create our enriched metadata view." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Execute the CREATE VIEW statement\n", - "conn.execute(create_view_sql)\n", - "\n", - "print(\"Enriched metadata view created successfully!\")\n", - "print(\"\\nAvailable views:\")\n", - "views = conn.execute(\"SHOW TABLES\").fetchall()\n", - "for view in views:\n", - " print(f\" - {view[0]}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Query the Enriched Metadata View\n", - "\n", - "Let's examine the enriched metadata to see the results." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Query enriched metadata\n", - "enriched_df = conn.execute(\"\"\"\n", - " SELECT *\n", - " FROM harbison_2004_enriched_metadata\n", - " LIMIT 10\n", - "\"\"\").df()\n", - "\n", - "print(f\"Enriched metadata shape: {enriched_df.shape}\")\n", - "print(\"\\nSample rows:\")\n", - "display(enriched_df)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 7. Practical Examples: Querying with Enriched Metadata\n", - "\n", - "Now that we have enriched metadata, we can perform analyses that wouldn't be possible with just the raw condition values." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Example 1: Find All Experiments with D-glucose as Carbon Source" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "glucose_df = conn.execute(\"\"\"\n", - " SELECT DISTINCT\n", - " condition,\n", - " media_name,\n", - " carbon_source,\n", - " nitrogen_source,\n", - " COUNT(*) as sample_count\n", - " FROM harbison_2004_enriched_metadata\n", - " WHERE carbon_source LIKE '%D-glucose%'\n", - " GROUP BY condition, media_name, carbon_source, nitrogen_source\n", - " ORDER BY condition\n", - "\"\"\").df()\n", - "\n", - "print(f\"Found {len(glucose_df)} condition(s) with D-glucose\")\n", - "display(glucose_df)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Example 2: Compare Rich Media vs Synthetic Media Experiments" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "media_comparison_df = conn.execute(\"\"\"\n", - " SELECT\n", - " CASE\n", - " WHEN media_name LIKE '%YPD%' OR media_name LIKE '%yeast_extract%'\n", - " THEN 'Rich Media'\n", - " WHEN media_name LIKE '%synthetic%'\n", - " THEN 'Synthetic Media'\n", - " ELSE 'Other'\n", - " END AS media_type,\n", - " COUNT(DISTINCT condition) as num_conditions,\n", - " COUNT(DISTINCT sample_id) as num_samples,\n", - " COUNT(DISTINCT regulator_symbol) as num_regulators\n", - " FROM harbison_2004_enriched_metadata\n", - " GROUP BY media_type\n", - " ORDER BY media_type\n", - "\"\"\").df()\n", - "\n", - "print(\"Media type comparison:\")\n", - "display(media_comparison_df)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Example 3: Analyze Carbon Source Diversity" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "carbon_diversity_df = conn.execute(\"\"\"\n", - " SELECT\n", - " carbon_source,\n", - " COUNT(DISTINCT condition) as num_conditions,\n", - " COUNT(DISTINCT sample_id) as num_samples,\n", - " STRING_AGG(DISTINCT condition, ', ' ORDER BY condition) as conditions\n", - " FROM harbison_2004_enriched_metadata\n", - " GROUP BY carbon_source\n", - " ORDER BY num_conditions DESC\n", - "\"\"\").df()\n", - "\n", - "print(\"Carbon source diversity:\")\n", - "display(carbon_diversity_df)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Example 4: Find Regulators Tested Across Multiple Carbon Sources" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "regulator_diversity_df = conn.execute(\"\"\"\n", - " SELECT\n", - " regulator_symbol,\n", - " regulator_locus_tag,\n", - " COUNT(DISTINCT carbon_source) as num_carbon_sources,\n", - " COUNT(DISTINCT condition) as num_conditions,\n", - " STRING_AGG(DISTINCT carbon_source, ', ') as carbon_sources_tested\n", - " FROM harbison_2004_enriched_metadata\n", - " GROUP BY regulator_symbol, regulator_locus_tag\n", - " HAVING COUNT(DISTINCT carbon_source) > 1\n", - " ORDER BY num_carbon_sources DESC\n", - " LIMIT 20\n", - "\"\"\").df()\n", - "\n", - "print(f\"Found {len(regulator_diversity_df)} regulators tested across multiple carbon sources\")\n", - "print(\"\\nTop 20 regulators by carbon source diversity:\")\n", - "display(regulator_diversity_df)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 8. Summary and Key Takeaways\n", - "\n", - "In this tutorial, we learned how to:\n", - "\n", - "1. **Explore nested metadata** using DataCard's `get_field_attribute()` method\n", - "2. **Flatten nested structures** into queryable strings\n", - "3. **Build SQL CASE expressions** manually to map condition values to metadata attributes\n", - "4. **Create enriched metadata views** that combine data fields with DataCard information\n", - "5. **Query by metadata attributes** that aren't present in the raw data\n", - "\n", - "### When to Use This Approach\n", - "\n", - "Use this pattern when:\n", - "- You need to analyze data by attributes that are defined in the DataCard but not in the data itself\n", - "- You want full control over the SQL being generated\n", - "- You're working with a single dataset and need custom metadata columns\n", - "\n", - "### Next Steps\n", - "\n", - "- Explore other attributes like `temperature_celsius` or `cultivation_method`\n", - "- Combine multiple attributes in your enriched views\n", - "- Use this pattern with other datasets in the BrentLab collection\n", - "- Build reusable helper functions for your specific analysis needs" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.0" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/tutorials/my_metadata_config.yaml b/docs/tutorials/my_metadata_config.yaml deleted file mode 100644 index 6338159..0000000 --- a/docs/tutorials/my_metadata_config.yaml +++ /dev/null @@ -1,39 +0,0 @@ -BrentLab/harbison_2004: - nitrogen_source: - path: media.nitrogen_source.name - dataset: - harbison_2004: - phosphate_source: - path: media.phosphate_source.compound - carbon_source: - field: condition - path: media.carbon_source.compound - temperature_celsius: - field: condition - path: temperature_celsius - -BrentLab/kemmeren_2014: - dataset: - kemmeren_2014: - carbon_source: - path: media.carbon_source.compound - temperature_celsius: - path: temperature_celsius - -factor_aliases: - carbon_source: - galactose: - - D-galactose - # should not have to include the actual name of the column -- that can be inferred - - Galactose - glucose: - - D-glucose - - dextrose - - glucose - raffinose: - - D-raffinose - - raffinose - -description: - carbon_source: - media: \ No newline at end of file diff --git a/docs/tutorials/passing_callingcards.csv b/docs/tutorials/passing_callingcards.csv deleted file mode 100644 index acaea06..0000000 --- a/docs/tutorials/passing_callingcards.csv +++ /dev/null @@ -1,82 +0,0 @@ -id,uploader,modifier,source_name,regulator_symbol,regulator_locus_tag,condition,data_usable,preferred_replicate,background_name,promoterset,batch,lab,assay,source_orig_id,upload_date,modified_date,file,single_binding,composite_binding,promoter,background,fileformat -8573,admin,admin,brent_nf_cc,SOK2,YMR016C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.335275-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8573.csv.gz,NA,1328,4,1,5 -8574,admin,admin,brent_nf_cc,RDR1,YOR380W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.373096-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8574.csv.gz,NA,1329,4,1,5 -8575,admin,admin,brent_nf_cc,PGD1,YGL025C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.674607-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8575.csv.gz,NA,1330,4,1,5 -8576,admin,admin,brent_nf_cc,TOG1,YER184C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.701098-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8576.csv.gz,NA,1331,4,1,5 -8577,admin,admin,brent_nf_cc,MET4,YNL103W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.787380-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8577.csv.gz,NA,1332,4,1,5 -8578,admin,admin,brent_nf_cc,CST6,YIL036W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.794787-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8578.csv.gz,NA,1333,4,1,5 -8579,admin,admin,brent_nf_cc,YOX1,YML027W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.813536-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8579.csv.gz,NA,1334,4,1,5 -8580,admin,admin,brent_nf_cc,RPD3,YNL330C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.866654-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8580.csv.gz,NA,1336,4,1,5 -8581,admin,admin,brent_nf_cc,MSN1,YOL116W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.844507-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8581.csv.gz,NA,1335,4,1,5 -8582,admin,admin,brent_nf_cc,HAP3,YBL021C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.918717-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8582.csv.gz,NA,1338,4,1,5 -8583,admin,admin,brent_nf_cc,SKN7,YHR206W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.924313-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8583.csv.gz,NA,1337,4,1,5 -8584,admin,admin,brent_nf_cc,SMP1,YBR182C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.984147-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8584.csv.gz,NA,1339,4,1,5 -8585,admin,admin,brent_nf_cc,MBP1,YDL056W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.068717-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8585.csv.gz,NA,1340,4,1,5 -8586,admin,admin,brent_nf_cc,RTG3,YBL103C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.078145-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8586.csv.gz,NA,1341,4,1,5 -8587,admin,admin,brent_nf_cc,DAT1,YML113W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.146444-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8587.csv.gz,NA,1342,4,1,5 -8588,admin,admin,brent_nf_cc,GZF3,YJL110C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.294324-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8588.csv.gz,NA,1344,4,1,5 -8589,admin,admin,brent_nf_cc,RDS2,YPL133C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.280370-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8589.csv.gz,NA,1343,4,1,5 -8590,admin,admin,brent_nf_cc,PDR3,YBL005W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.343977-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8590.csv.gz,NA,1346,4,1,5 -8591,admin,admin,brent_nf_cc,RFX1,YLR176C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.304403-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8591.csv.gz,NA,1345,4,1,5 -8592,admin,admin,brent_nf_cc,MET31,YPL038W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.521624-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8592.csv.gz,NA,1347,4,1,5 -8593,admin,admin,brent_nf_cc,INO4,YOL108C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:44.341119-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8593.csv.gz,NA,1349,4,1,5 -8594,admin,admin,brent_nf_cc,RGT1,YKL038W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:44.301633-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8594.csv.gz,NA,1348,4,1,5 -8595,admin,admin,brent_nf_cc,STP2,YHR006W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:44.653232-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8595.csv.gz,NA,1350,4,1,5 -8596,admin,admin,brent_nf_cc,AZF1,YOR113W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.090708-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8596.csv.gz,NA,1352,4,1,5 -8597,admin,admin,brent_nf_cc,ZAP1,YJL056C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.085289-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8597.csv.gz,NA,1351,4,1,5 -8598,admin,admin,brent_nf_cc,MOT3,YMR070W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.171772-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8598.csv.gz,NA,1353,4,1,5 -8599,admin,admin,brent_nf_cc,VHR1,YIL056W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.215135-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8599.csv.gz,NA,1354,4,1,5 -8600,admin,admin,brent_nf_cc,URC2,YDR520C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.286065-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8600.csv.gz,NA,1355,4,1,5 -8601,admin,admin,brent_nf_cc,INO2,YDR123C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.339489-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8601.csv.gz,NA,1356,4,1,5 -8602,admin,admin,brent_nf_cc,FKH2,YNL068C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.395206-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8602.csv.gz,NA,1357,4,1,5 -8603,admin,admin,brent_nf_cc,CHA4,YLR098C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.445223-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8603.csv.gz,NA,1360,4,1,5 -8604,admin,admin,brent_nf_cc,TYE7,YOR344C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.446764-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8604.csv.gz,NA,1359,4,1,5 -8605,admin,admin,brent_nf_cc,OAF1,YAL051W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.447196-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8605.csv.gz,NA,1358,4,1,5 -8606,admin,admin,brent_nf_cc,MET28,YIR017C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.551152-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8606.csv.gz,NA,1361,4,1,5 -8607,admin,admin,brent_nf_cc,AFT1,YGL071W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.825510-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8607.csv.gz,NA,1362,4,1,5 -8608,admin,admin,brent_nf_cc,GAT2,YMR136W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.897217-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8608.csv.gz,NA,1363,4,1,5 -8609,admin,admin,brent_nf_cc,PHO2,YDL106C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:46.029628-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8609.csv.gz,NA,1366,4,1,5 -8610,admin,admin,brent_nf_cc,DAL80,YKR034W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.927940-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8610.csv.gz,NA,1364,4,1,5 -8611,admin,admin,brent_nf_cc,UME1,YPL139C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.951004-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8611.csv.gz,NA,1365,4,1,5 -8612,admin,admin,brent_nf_cc,RLM1,YPL089C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:46.219541-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8612.csv.gz,NA,1367,4,1,5 -8613,admin,admin,brent_nf_cc,HAP5,YOR358W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:46.366453-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8613.csv.gz,NA,1368,4,1,5 -8614,admin,admin,brent_nf_cc,TUP1,YCR084C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:46.441695-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8614.csv.gz,NA,1369,4,1,5 -8615,admin,admin,brent_nf_cc,IXR1,YKL032C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:46.605683-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8615.csv.gz,NA,1370,4,1,5 -8616,admin,admin,brent_nf_cc,CYC8,YBR112C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.115289-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8616.csv.gz,NA,1371,4,1,5 -8617,admin,admin,brent_nf_cc,UPC2,YDR213W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.185506-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8617.csv.gz,NA,1372,4,1,5 -8618,admin,admin,brent_nf_cc,ARO80,YDR421W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.300436-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8618.csv.gz,NA,1373,4,1,5 -8619,admin,admin,brent_nf_cc,YHP1,YDR451C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.432083-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8619.csv.gz,NA,1374,4,1,5 -8620,admin,admin,brent_nf_cc,GAL11,YOL051W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.457453-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8620.csv.gz,NA,1375,4,1,5 -8621,admin,admin,brent_nf_cc,MED2,YDL005C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.457928-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8621.csv.gz,NA,1376,4,1,5 -8622,admin,admin,brent_nf_cc,GIS1,YDR096W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.495815-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8622.csv.gz,NA,1377,4,1,5 -8623,admin,admin,brent_nf_cc,STP1,YDR463W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.533662-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8623.csv.gz,NA,1379,4,1,5 -8624,admin,admin,brent_nf_cc,RTG1,YOL067C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.529096-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8624.csv.gz,NA,1378,4,1,5 -8625,admin,admin,brent_nf_cc,HAP2,YGL237C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.546475-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8625.csv.gz,NA,1380,4,1,5 -8626,admin,admin,brent_nf_cc,MSN2,YMR037C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.876943-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8626.csv.gz,NA,1381,4,1,5 -8627,admin,admin,brent_nf_cc,LEU3,YLR451W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:48.130584-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8627.csv.gz,NA,1382,4,1,5 -8628,admin,admin,brent_nf_cc,GAL4,YPL248C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:48.383735-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8628.csv.gz,NA,1383,4,1,5 -8629,admin,admin,brent_nf_cc,GCN4,YEL009C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:48.661669-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8629.csv.gz,NA,1384,4,1,5 -8630,admin,admin,brent_nf_cc,CRZ1,YNL027W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:48.724198-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8630.csv.gz,NA,1385,4,1,5 -8631,admin,admin,brent_nf_cc,STE12,YHR084W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:48.827903-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8631.csv.gz,NA,1386,4,1,5 -8632,admin,admin,brent_nf_cc,STP3,YLR375W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.012518-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8632.csv.gz,NA,1387,4,1,5 -8633,admin,admin,brent_nf_cc,PHD1,YKL043W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.172299-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8633.csv.gz,NA,1388,4,1,5 -8634,admin,admin,brent_nf_cc,RPN4,YDL020C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.219434-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8634.csv.gz,NA,1389,4,1,5 -8635,admin,admin,brent_nf_cc,ASH1,YKL185W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.252413-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8635.csv.gz,NA,1390,4,1,5 -8636,admin,admin,brent_nf_cc,TEA1,YOR337W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.600060-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8636.csv.gz,NA,1391,4,1,5 -8637,admin,admin,brent_nf_cc,SUM1,YDR310C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.893152-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8637.csv.gz,NA,1392,4,1,5 -8638,admin,admin,brent_nf_cc,NRG1,YDR043C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.991998-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8638.csv.gz,NA,1394,4,1,5 -8639,admin,admin,brent_nf_cc,TEC1,YBR083W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.962020-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8639.csv.gz,NA,1393,4,1,5 -8640,admin,admin,brent_nf_cc,CSE2,YNR010W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:50.509234-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8640.csv.gz,NA,1395,4,1,5 -8641,admin,admin,brent_nf_cc,ROX1,YPR065W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:50.797488-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8641.csv.gz,NA,1397,4,1,5 -8642,admin,admin,brent_nf_cc,FKH1,YIL131C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:50.782743-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8642.csv.gz,NA,1396,4,1,5 -8643,admin,admin,brent_nf_cc,GAL80,YML051W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:50.868439-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8643.csv.gz,NA,1398,4,1,5 -8644,admin,admin,brent_nf_cc,SKO1,YNL167C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:51.214395-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8644.csv.gz,NA,1399,4,1,5 -8645,admin,admin,brent_nf_cc,PDR1,YGL013C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:51.527347-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8645.csv.gz,NA,1400,4,1,5 -8646,admin,admin,brent_nf_cc,CIN5,YOR028C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:51.544390-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8646.csv.gz,NA,1401,4,1,5 -8647,admin,admin,brent_nf_cc,PPR1,YLR014C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:51.974579-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8647.csv.gz,NA,1402,4,1,5 -8648,admin,admin,brent_nf_cc,HAL9,YOL089C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:52.067382-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8648.csv.gz,NA,1403,4,1,5 -8649,admin,admin,brent_nf_cc,CBF1,YJR060W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:52.302853-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8649.csv.gz,NA,1404,4,1,5 -8650,admin,admin,brent_nf_cc,USV1,YPL230W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:52.353098-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8650.csv.gz,NA,1405,4,1,5 -8651,admin,admin,brent_nf_cc,RPH1,YER169W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:53.152828-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8651.csv.gz,NA,1406,4,1,5 -8652,admin,admin,brent_nf_cc,ERT1,YBR239C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:53.548696-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8652.csv.gz,NA,1407,4,1,5 -8653,admin,admin,brent_nf_cc,MET32,YDR253C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:54.027532-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8653.csv.gz,NA,1408,4,1,5 diff --git a/docs/tutorials/rank_response_tutorial.ipynb b/docs/tutorials/rank_response_tutorial.ipynb deleted file mode 100644 index 2e85f20..0000000 --- a/docs/tutorials/rank_response_tutorial.ipynb +++ /dev/null @@ -1,968 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Rank-Response Analysis Tutorial\n", - "\n", - "This tutorial demonstrates how to conduct comprehensive rank-response analysis using tfbpapi to compare binding data with perturbation responses across multiple datasets.\n", - "\n", - "## Overview\n", - "\n", - "Rank-response analysis evaluates whether targets with stronger binding evidence are more likely to show transcriptional responses upon regulator perturbation. This approach helps validate functional relevance of binding data.\n", - "\n", - "### Datasets Used\n", - "- **Binding data**: Calling Cards, ChEC-seq, ChIP-chip\n", - "- **Perturbation data**: McIsaac (ZEV system), Kemmeren (overexpression), Mahendrawada (RNA-seq)\n", - "\n", - "### Analysis Strategy\n", - "1. Load and filter datasets to common regulators/targets\n", - "2. Rank targets by binding strength for each regulator\n", - "3. Label targets as responsive/non-responsive based on perturbation data\n", - "4. Perform binned analysis to assess rank-response relationship\n", - "5. Compare results across different data combinations" - ] - }, - { - "cell_type": "code", - "execution_count": 61, - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "from tfbpapi.HfQueryAPI import HfQueryAPI\n", - "from tfbpapi.RankResponseAnalysis import RankResponseAnalyzer\n", - "from tfbpapi.datainfo.datacard import DataCard\n", - "from tfbpapi.errors import DataCardError, HfDataFetchError\n", - "import matplotlib.pyplot as plt\n", - "import seaborn as sns\n", - "\n", - "# Configure plotting\n", - "plt.style.use('default')\n", - "sns.set_palette(\"husl\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Dataset Exploration with DataCard\n", - "\n", - "Before loading data, let's explore dataset structure and metadata using the DataCard interface." - ] - }, - { - "cell_type": "code", - "execution_count": 62, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Exploring dataset structures...\n", - "\n", - "==================================================\n", - "Exploring BrentLab/callingcards\n", - "Dataset types in configs: {'genome_map', 'annotated_features', 'metadata'}\n", - "Configurations: 4\n", - "\n", - "Config: annotated_features\n", - " Type: annotated_features\n", - " Features: 13\n", - " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", - " Target fields: ['target_locus_tag', 'target_symbol']\n", - "\n", - "Config: genome_map\n", - " Type: genome_map\n", - " Features: 7\n", - "\n", - "Config: annotated_features_meta\n", - " Type: metadata\n", - " Features: 9\n", - " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", - "\n", - "Config: genome_map_meta\n", - " Type: metadata\n", - " Features: 7\n", - " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", - "\n", - "==================================================\n", - "Exploring BrentLab/mahendrawada_2025\n", - "Dataset types in configs: {'genome_map', 'annotated_features', 'metadata', 'genomic_features'}\n", - "Configurations: 6\n", - "\n", - "Config: genomic_features\n", - " Type: genomic_features\n", - " Features: 24\n", - "\n", - "Config: mahendrawada_2025_metadata\n", - " Type: metadata\n", - " Features: 7\n", - " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", - "\n", - "Config: chec_seq_genome_map\n", - " Type: genome_map\n", - " Features: 3\n", - "\n", - "Config: mahendrawada_chec_seq\n", - " Type: annotated_features\n", - " Features: 6\n", - " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", - " Target fields: ['target_locus_tag', 'target_symbol']\n", - " Metadata fields: ['regulator_locus_tag', 'regulator_symbol']\n", - "\n", - "Config: reprocessed_chec_seq\n", - " Type: annotated_features\n", - " Features: 6\n", - " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", - " Target fields: ['target_locus_tag', 'target_symbol']\n", - "\n", - "Config: rna_seq\n", - " Type: annotated_features\n", - " Features: 5\n", - " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", - " Target fields: ['target_locus_tag', 'target_symbol']\n", - " Metadata fields: ['regulator_locus_tag', 'regulator_symbol']\n", - "\n", - "==================================================\n", - "Exploring BrentLab/harbison_2004\n", - "Dataset types in configs: {'annotated_features'}\n", - "Configurations: 1\n", - "\n", - "Config: harbison_2004\n", - " Type: annotated_features\n", - " Features: 7\n", - " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", - " Target fields: ['target_locus_tag', 'target_symbol']\n", - " Metadata fields: ['regulator_locus_tag', 'regulator_symbol', 'condition']\n", - "\n", - "==================================================\n", - "Exploring BrentLab/hackett_2020\n", - "Dataset types in configs: {'annotated_features'}\n", - "Configurations: 1\n", - "\n", - "Config: hackett_2020\n", - " Type: annotated_features\n", - " Features: 17\n", - " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", - " Target fields: ['target_locus_tag', 'target_symbol']\n", - " Metadata fields: ['regulator_locus_tag', 'regulator_symbol', 'time', 'mechanism', 'restriction', 'date', 'strain']\n", - "\n", - "==================================================\n", - "Exploring BrentLab/kemmeren_2014\n", - "Dataset types in configs: {'annotated_features'}\n", - "Configurations: 1\n", - "\n", - "Config: kemmeren_2014\n", - " Type: annotated_features\n", - " Features: 11\n", - " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", - " Target fields: ['target_locus_tag', 'target_symbol']\n", - " Metadata fields: ['regulator_locus_tag', 'regulator_symbol']\n", - "\n", - "==================================================\n", - "Successfully explored 5 datasets\n" - ] - } - ], - "source": [ - "# Explore dataset structure using DataCard\n", - "print(\"Exploring dataset structures...\")\n", - "\n", - "datasets_to_explore = [\n", - " \"BrentLab/callingcards\",\n", - " \"BrentLab/mahendrawada_2025\", \n", - " \"BrentLab/harbison_2004\",\n", - " \"BrentLab/hackett_2020\",\n", - " \"BrentLab/kemmeren_2014\"\n", - "]\n", - "\n", - "dataset_info = {}\n", - "\n", - "for repo_id in datasets_to_explore:\n", - " try:\n", - " print(f\"\\n{'='*50}\")\n", - " print(f\"Exploring {repo_id}\")\n", - "\n", - " # Create DataCard instance\n", - " datacard = DataCard(repo_id)\n", - " card = datacard.dataset_card\n", - "\n", - " # DatasetCard doesn't have dataset_type directly - it's on individual configs\n", - " dataset_types = [config.dataset_type.value for config in card.configs]\n", - " print(f\"Dataset types in configs: {set(dataset_types)}\")\n", - " print(f\"Configurations: {len(card.configs)}\")\n", - "\n", - " # Store dataset info for later use\n", - " dataset_info[repo_id] = {\n", - " 'datacard': datacard,\n", - " 'card': card,\n", - " 'configs': {config.config_name: config for config in card.configs}\n", - " }\n", - "\n", - " # Display configuration details\n", - " for config in card.configs:\n", - " print(f\"\\nConfig: {config.config_name}\")\n", - " print(f\" Type: {config.dataset_type.value}\")\n", - " print(f\" Features: {len(config.dataset_info.features) if config.dataset_info.features else 0}\")\n", - "\n", - " if config.dataset_info.features:\n", - " # Show regulator and target fields if available\n", - " feature_names = [f.name for f in config.dataset_info.features]\n", - " regulator_fields = [f for f in feature_names if 'regulator' in f.lower()]\n", - " target_fields = [f for f in feature_names if 'target' in f.lower()]\n", - "\n", - " if regulator_fields:\n", - " print(f\" Regulator fields: {regulator_fields}\")\n", - " if target_fields:\n", - " print(f\" Target fields: {target_fields}\")\n", - "\n", - " if config.metadata_fields:\n", - " print(f\" Metadata fields: {config.metadata_fields}\")\n", - "\n", - " except (DataCardError, HfDataFetchError) as e:\n", - " print(f\"Error exploring {repo_id}: {e}\")\n", - " continue\n", - " except Exception as e:\n", - " print(f\"Unexpected error exploring {repo_id}: {e}\")\n", - " continue\n", - "\n", - "print(f\"\\n{'='*50}\")\n", - "print(f\"Successfully explored {len(dataset_info)} datasets\")" - ] - }, - { - "cell_type": "code", - "execution_count": 63, - "metadata": {}, - "outputs": [], - "source": [ - "## Initialize dataset connections and load data\n" - ] - }, - { - "cell_type": "code", - "execution_count": 64, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Initializing HfQueryAPI connections...\n", - "All API connections initialized\n" - ] - } - ], - "source": [ - "\n", - "print(\"Initializing HfQueryAPI connections...\")\n", - "\n", - "mahendrawada_2025 = HfQueryAPI(repo_id=\"BrentLab/mahendrawada_2025\")\n", - "hackett_2020 = HfQueryAPI(repo_id=\"BrentLab/hackett_2020\")\n", - "callingcards = HfQueryAPI(repo_id=\"BrentLab/callingcards\")\n", - "harbison_2004 = HfQueryAPI(repo_id=\"BrentLab/harbison_2004\") \n", - "kemmeren_2014 = HfQueryAPI(repo_id=\"BrentLab/kemmeren_2014\")\n", - "\n", - "print(\"All API connections initialized\")" - ] - }, - { - "cell_type": "code", - "execution_count": 65, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Getting metadata from datasets...\n", - "All metadata loaded\n", - "Found 81 common regulators across all datasets\n" - ] - } - ], - "source": [ - "# Get metadata from each dataset to find common regulators\n", - "print(\"Getting metadata from datasets...\")\n", - "\n", - "harbison_meta = harbison_2004.get_metadata(\"harbison_2004\")\n", - "kemmeren_meta = kemmeren_2014.get_metadata(\"kemmeren_2014\") \n", - "callingcards_meta = callingcards.get_metadata(\"annotated_features\")\n", - "mahendrawada_checseq_meta = mahendrawada_2025.get_metadata(\"mahendrawada_chec_seq\")\n", - "mahendrawada_rnaseq_meta = mahendrawada_2025.get_metadata(\"rna_seq\")\n", - "hackett_2020_meta = hackett_2020.get_metadata(\"hackett_2020\")\n", - "\n", - "print(\"All metadata loaded\")\n", - "\n", - "# Get the intersection of common regulators\n", - "common_regulators = (set(mahendrawada_rnaseq_meta.regulator_locus_tag.unique())\n", - " & set(mahendrawada_checseq_meta.regulator_locus_tag.unique())\n", - " & set(hackett_2020_meta.regulator_locus_tag.unique())\n", - " & set(callingcards_meta.regulator_locus_tag.unique())\n", - " & set(harbison_meta.regulator_locus_tag.unique())\n", - " & set(kemmeren_meta.regulator_locus_tag.unique()))\n", - "\n", - "print(f\"Found {len(common_regulators)} common regulators across all datasets\")\n", - "\n", - "# Create proper SQL IN clause\n", - "if common_regulators:\n", - " regulator_clause = \"(\" + \", \".join(f\"'{reg}'\" for reg in common_regulators) + \")\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Filter the data" - ] - }, - { - "cell_type": "code", - "execution_count": 66, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Loading calling cards quality filter...\n", - "Found 81 passing calling cards experiments\n", - "Applying dataset-specific filters...\n", - "All filters applied\n" - ] - } - ], - "source": [ - "# Load and filter calling cards data with passing experiments\n", - "print(\"Loading calling cards quality filter...\")\n", - "\n", - "passing_cc = pd.read_csv(\"./passing_callingcards.csv\")\n", - "db_ids_as_str = [str(id) for id in passing_cc.id.unique()]\n", - "\n", - "# filter the callingcards_data on the db_id column using passing_cc.id\n", - "db_ids = callingcards_meta[callingcards_meta.db_id.isin(db_ids_as_str)].id\n", - "cc_ids_clause = \"(\" + \", \".join(f\"'{db_id}'\" for db_id in db_ids) + \")\"\n", - "\n", - "print(f\"Found {len(db_ids)} passing calling cards experiments\")\n", - "\n", - "# Apply dataset-specific filters\n", - "print(\"Applying dataset-specific filters...\")\n", - "\n", - "hackett_2020.set_sql_filter(\n", - " \"hackett_2020\",\n", - " f\"\"\"\n", - " time = 15 \n", - " AND mechanism = 'ZEV' \n", - " AND restriction = 'P' \n", - " AND regulator_locus_tag IN {regulator_clause}\n", - " \"\"\")\n", - "\n", - "mahendrawada_2025.set_sql_filter(\n", - " \"mahendrawada_chec_seq\",\n", - " f\"\"\"\n", - " regulator_locus_tag IN {regulator_clause}\n", - " \"\"\")\n", - "\n", - "mahendrawada_2025.set_sql_filter(\n", - " \"rna_seq\",\n", - " f\"\"\"\n", - " regulator_locus_tag IN {regulator_clause}\n", - " \"\"\")\n", - "\n", - "callingcards.set_sql_filter(\n", - " \"annotated_features\",\n", - " f\"\"\"\n", - " regulator_locus_tag IN {regulator_clause} \n", - " AND id IN {cc_ids_clause}\n", - " \"\"\")\n", - "\n", - "harbison_2004.set_sql_filter(\n", - " \"harbison_2004\",\n", - " f\"\"\"\n", - " regulator_locus_tag IN {regulator_clause} \n", - " AND condition = 'YPD'\n", - " \"\"\")\n", - "\n", - "print(\"All filters applied\")" - ] - }, - { - "cell_type": "code", - "execution_count": 67, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Loading initial datasets...\n", - "Initial datasets loaded\n", - "mcisaac: 61 regulators\n", - "mahendrawada_chec: 81 regulators\n", - "callingcards: 46 regulators\n", - "harbison: 81 regulators\n", - "Final intersection: 33 regulators\n" - ] - } - ], - "source": [ - "# Load initial datasets to determine final common regulators and targets\n", - "print(\"Loading initial datasets...\")\n", - "\n", - "mcisaac = hackett_2020.query(\"SELECT * FROM hackett_2020\", \"hackett_2020\")\n", - "mahendrawada_chec = mahendrawada_2025.query(\"SELECT * FROM mahendrawada_chec_seq\", \"mahendrawada_chec_seq\") \n", - "mahendrawada_perturb = mahendrawada_2025.query(\"SELECT * FROM rna_seq\", \"rna_seq\")\n", - "callingcards_data = callingcards.query(\"SELECT * FROM annotated_features\", \"annotated_features\")\n", - "harbison_data = harbison_2004.query(\"SELECT * FROM harbison_2004\", \"harbison_2004\")\n", - "mahendrawada_features = mahendrawada_2025.query(\"SELECT * FROM genomic_features\", \"genomic_features\")\n", - "\n", - "print(\"Initial datasets loaded\")\n", - "\n", - "# After running your current queries, check what you actually got\n", - "actual_regulators = {\n", - " 'mcisaac': set(mcisaac.regulator_locus_tag.unique()),\n", - " 'mahendrawada_chec': set(mahendrawada_chec.regulator_locus_tag.unique()),\n", - " 'callingcards': set(callingcards_data.regulator_locus_tag.unique()),\n", - " 'harbison': set(harbison_data.regulator_locus_tag.unique())\n", - "}\n", - "\n", - "for name, regulators in actual_regulators.items():\n", - " print(f\"{name}: {len(regulators)} regulators\")\n", - "\n", - "final_common = set.intersection(*actual_regulators.values())\n", - "print(f\"Final intersection: {len(final_common)} regulators\")\n", - "\n", - "final_common_clause = \"(\" + \", \".join(f\"'{reg}'\" for reg in final_common) + \")\"" - ] - }, - { - "cell_type": "code", - "execution_count": 68, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Finding common targets...\n", - "Found 5525 common targets\n", - "Loading final datasets with complete filtering...\n", - "Final datasets loaded with complete filtering\n", - "McIsaac: 187,884 rows, 33 regulators\n", - "Kemmeren: 178,332 rows, 33 regulators\n", - "Mahendrawada RNA-seq: 4,411 rows, 33 regulators\n", - "Calling Cards: 182,325 rows, 33 regulators\n", - "Mahendrawada ChEC: 22,478 rows, 33 regulators\n", - "Harbison: 182,952 rows, 33 regulators\n" - ] - } - ], - "source": [ - "# Find common targets across all datasets\n", - "print(\"Finding common targets...\")\n", - "\n", - "common_targets = (set(mcisaac.target_locus_tag.unique())\n", - " & set(mahendrawada_features.locus_tag.unique())\n", - " & set(callingcards_data.target_locus_tag.unique())\n", - " & set(harbison_data.target_locus_tag.unique()))\n", - "\n", - "print(f\"Found {len(common_targets)} common targets\")\n", - "\n", - "if common_targets:\n", - " target_clause = \"(\" + \", \".join(f\"'{tgt}'\" for tgt in common_targets) + \")\"\n", - "\n", - "# Load final datasets with both regulator and target filtering \n", - "print(\"Loading final datasets with complete filtering...\")\n", - "\n", - "mcisaac = hackett_2020.query(f\"\"\"\n", - " SELECT *\n", - " FROM hackett_2020\n", - " WHERE regulator_locus_tag in {final_common_clause} \n", - " AND target_locus_tag IN {target_clause}\n", - " \"\"\", \"hackett_2020\")\n", - "\n", - "mahendrawada_perturb = mahendrawada_2025.query(f\"\"\"\n", - " SELECT *\n", - " FROM rna_seq\n", - " WHERE regulator_locus_tag IN {final_common_clause} \n", - " AND target_locus_tag IN {target_clause}\n", - " \"\"\", \"rna_seq\")\n", - "\n", - "kemmeren = kemmeren_2014.query(f\"\"\"\n", - " SELECT * FROM kemmeren_2014 WHERE regulator_locus_tag IN {final_common_clause} \n", - " AND target_locus_tag IN {target_clause} \n", - " AND variable_in_wt = 0\n", - " \"\"\",\n", - " \"kemmeren_2014\")\n", - "\n", - "mahendrawada_chec = mahendrawada_2025.query(f\"SELECT * FROM mahendrawada_chec_seq WHERE regulator_locus_tag IN {final_common_clause} AND target_locus_tag IN {target_clause}\", \"mahendrawada_chec_seq\")\n", - "\n", - "callingcards_data = callingcards.query(f\"SELECT * FROM annotated_features WHERE regulator_locus_tag IN {final_common_clause} AND target_locus_tag IN {target_clause}\",\n", - " \"annotated_features\")\n", - "\n", - "harbison = harbison_2004.query(f\"SELECT * FROM harbison_2004 WHERE regulator_locus_tag IN {final_common_clause} AND target_locus_tag IN {target_clause}\",\n", - " \"harbison_2004\")\n", - "\n", - "print(\"Final datasets loaded with complete filtering\")\n", - "\n", - "# Print final dataset sizes\n", - "datasets_info = [\n", - " ('McIsaac', mcisaac), ('Kemmeren', kemmeren), ('Mahendrawada RNA-seq', mahendrawada_perturb),\n", - " ('Calling Cards', callingcards_data), ('Mahendrawada ChEC', mahendrawada_chec), ('Harbison', harbison)\n", - "]\n", - "\n", - "for name, data in datasets_info:\n", - " if len(data) > 0:\n", - " regulators = data['regulator_locus_tag'].nunique() if 'regulator_locus_tag' in data.columns else 0\n", - " print(f\"{name}: {len(data):,} rows, {regulators} regulators\")\n", - " else:\n", - " print(f\"{name}: No data loaded\")" - ] - }, - { - "cell_type": "code", - "execution_count": 69, - "metadata": {}, - "outputs": [], - "source": [ - "## 2. Rank Binding Data by Strength" - ] - }, - { - "cell_type": "code", - "execution_count": 70, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Binding datasets ranked by strength\n" - ] - } - ], - "source": [ - "\n", - "# Rank binding datasets by strength (stronger binding = lower rank)\n", - "callingcards_ranked = (callingcards_data\n", - " .sort_values(['regulator_locus_tag', 'poisson_pval', 'callingcards_enrichment'],\n", - " ascending=[True, True, False], # Lower p-value, higher enrichment = stronger\n", - " kind='stable')\n", - " .reset_index(drop=True)\n", - " )\n", - "\n", - "mahendrawada_chec_ranked = (mahendrawada_chec\n", - " .sort_values(['regulator_locus_tag', 'peak_score'],\n", - " ascending=[True, False], # Higher peak score = stronger\n", - " kind='stable')\n", - " .reset_index(drop=True)\n", - " )\n", - "\n", - "harbison_ranked = (harbison\n", - " .sort_values(['regulator_locus_tag', 'pvalue', 'effect'],\n", - " ascending=[True, True, False], # Lower p-value, higher effect = stronger\n", - " kind='stable')\n", - " .reset_index(drop=True)\n", - " )\n", - "\n", - "print(\"Binding datasets ranked by strength\")" - ] - }, - { - "cell_type": "code", - "execution_count": 71, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Labeling functions defined\n" - ] - } - ], - "source": [ - "\n", - "def label_by_mcisaac(df):\n", - " \"\"\"Label targets as responsive based on McIsaac perturbation data.\"\"\"\n", - " out = df.merge(\n", - " mcisaac[['regulator_locus_tag', 'target_locus_tag', 'log2_shrunken_timecourses']],\n", - " on=['regulator_locus_tag', 'target_locus_tag'],\n", - " how='left',\n", - " )\n", - "\n", - " out['is_responsive'] = (\n", - " out.get('log2_shrunken_timecourses', pd.Series(0)).fillna(0).abs() > 0)\n", - "\n", - " return out\n", - "\n", - "def label_by_kemmeren(df):\n", - " \"\"\"Label targets as responsive based on Kemmeren perturbation data.\"\"\" \n", - " out = df.merge(\n", - " kemmeren[['regulator_locus_tag', 'target_locus_tag', 'Madj', 'pval']],\n", - " on=['regulator_locus_tag', 'target_locus_tag'],\n", - " how='left',\n", - " )\n", - "\n", - " out['is_responsive'] = (\n", - " out.get('pval', pd.Series(1)).fillna(1) <= 0.05)\n", - "\n", - " return out\n", - "\n", - "def label_by_mahendrawada_rnaseq(df):\n", - " \"\"\"Label targets as responsive based on Mahendrawada RNA-seq data.\"\"\"\n", - " out = df.merge(\n", - " mahendrawada_perturb[['regulator_locus_tag', 'target_locus_tag', 'log2fc']],\n", - " on=['regulator_locus_tag', 'target_locus_tag'],\n", - " how='left',\n", - " )\n", - "\n", - " out['is_responsive'] = ~out.log2fc.isna()\n", - "\n", - " return out\n", - "\n", - "print(\"Labeling functions defined\")" - ] - }, - { - "cell_type": "code", - "execution_count": 72, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Analysis functions defined\n" - ] - } - ], - "source": [ - "\n", - "def get_rr_results(df):\n", - " \"\"\"\n", - " Perform rank-response analysis for each regulator in the dataset.\n", - "\n", - " :param df: DataFrame with ranked binding data and responsive labels\n", - "\n", - " :returns: DataFrame with bin-level results for all regulators\n", - " \"\"\"\n", - " res_dict = {}\n", - "\n", - " # For each regulator separately\n", - " for regulator in df['regulator_locus_tag'].unique():\n", - " regulator_data = df[\n", - " df['regulator_locus_tag'] == regulator\n", - " ]\n", - "\n", - " try:\n", - " analyzer = RankResponseAnalyzer(\n", - " data=regulator_data[['target_locus_tag', 'is_responsive']],\n", - " target_col='target_locus_tag',\n", - " responsive_col='is_responsive',\n", - " bin_size=5 # Small bin size for detailed analysis\n", - " )\n", - "\n", - " results = analyzer.get_bin_summary()\n", - " res_dict[regulator] = results\n", - "\n", - " except Exception as e:\n", - " print(f\"Warning: Failed to analyze {regulator}: {e}\")\n", - " continue\n", - "\n", - " if not res_dict:\n", - " return pd.DataFrame()\n", - "\n", - " return (pd.concat(res_dict, names=['regulator_locus_tag', 'original_index'])\n", - " .reset_index(level=0)\n", - " .reset_index(drop=True))\n", - "\n", - "def combine_rr_results(ranked_df, binding_source):\n", - " \"\"\"\n", - " Generate rank-response results for all perturbation sources and combine filtered results.\n", - "\n", - " :param ranked_df: DataFrame with ranked binding data\n", - " :param binding_source: String identifier for the binding data source\n", - "\n", - " :returns: Combined DataFrame with bin==5 results from all perturbation sources\n", - " \"\"\"\n", - " print(f\"Analyzing {binding_source} binding data...\")\n", - "\n", - " # Generate results for each perturbation source\n", - " cc_mcisaac_rr = get_rr_results(label_by_mcisaac(ranked_df))\n", - " cc_kem_rr = get_rr_results(label_by_kemmeren(ranked_df))\n", - " cc_mahendrawada_rr = get_rr_results(label_by_mahendrawada_rnaseq(ranked_df))\n", - "\n", - " # Add source columns to each result\n", - " cc_mcisaac_rr['binding_source'] = binding_source\n", - " cc_mcisaac_rr['perturbation_source'] = 'mcisaac'\n", - "\n", - " cc_kem_rr['binding_source'] = binding_source\n", - " cc_kem_rr['perturbation_source'] = 'kemmeren'\n", - "\n", - " cc_mahendrawada_rr['binding_source'] = binding_source\n", - " cc_mahendrawada_rr['perturbation_source'] = 'mahendrawada_rnaseq'\n", - "\n", - " # Filter to bin==5 (strongest binding bin) and combine\n", - " bin5_results = [cc_mcisaac_rr[cc_mcisaac_rr.bin == 5],\n", - " cc_kem_rr[cc_kem_rr.bin == 5],\n", - " cc_mahendrawada_rr[cc_mahendrawada_rr.bin == 5]]\n", - "\n", - " combined = pd.concat(bin5_results, ignore_index=True)\n", - "\n", - " # Find regulators that have observations from all 3 perturbation sources\n", - " regulator_counts = combined.groupby('regulator_locus_tag')['perturbation_source'].nunique()\n", - " complete_regulators = regulator_counts[regulator_counts == 3].index\n", - "\n", - " # Filter to only keep rows for regulators with all 3 perturbation sources\n", - " final_result = combined[combined.regulator_locus_tag.isin(complete_regulators)]\n", - "\n", - " print(f\"Analysis complete: {len(complete_regulators)} regulators with complete data\")\n", - "\n", - " return final_result\n", - "\n", - "print(\"Analysis functions defined\")" - ] - }, - { - "cell_type": "code", - "execution_count": 73, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Binding data ranked by strength\n" - ] - } - ], - "source": [ - "def rank_by_binding_strength(df, binding_cols, ascending_order):\n", - " \"\"\"\n", - " Rank targets by binding strength within each regulator group.\n", - "\n", - " :param df: DataFrame with binding data\n", - " :param binding_cols: List of columns to sort by\n", - " :param ascending_order: List of boolean values for sort order\n", - " \"\"\"\n", - " return (df\n", - " .sort_values(['regulator_locus_tag'] + binding_cols, \n", - " ascending=[True] + ascending_order, \n", - " kind='stable')\n", - " .reset_index(drop=True))\n", - "\n", - "# Rank binding datasets\n", - "callingcards_ranked = rank_by_binding_strength(\n", - " callingcards_data,\n", - " ['poisson_pval', 'callingcards_enrichment'],\n", - " [True, False] # Lower p-value and higher enrichment = stronger binding\n", - ")\n", - "\n", - "mahendrawada_chec_ranked = rank_by_binding_strength(\n", - " mahendrawada_chec,\n", - " ['peak_score'],\n", - " [False] # Higher peak score = stronger binding\n", - ")\n", - "\n", - "harbison_ranked = rank_by_binding_strength(\n", - " harbison_data,\n", - " ['pvalue', 'effect'],\n", - " [True, False] # Lower p-value and higher effect = stronger binding\n", - ")\n", - "\n", - "print(\"Binding data ranked by strength\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Rank by binding and label each target as responsive or non-responsive" - ] - }, - { - "cell_type": "code", - "execution_count": 74, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Executing comprehensive rank-response analysis...\n", - "This will analyze each binding dataset against all perturbation sources...\n", - "Analyzing callingcards binding data...\n", - "Analysis complete: 33 regulators with complete data\n", - "Analyzing mahendrawada_chec binding data...\n", - "Analysis complete: 33 regulators with complete data\n", - "Analyzing harbison binding data...\n", - "Analysis complete: 81 regulators with complete data\n", - "\n", - "==================================================\n", - "Individual dataset results:\n", - "Calling Cards: 99 result rows\n", - "ChEC-seq: 99 result rows\n", - "Harbison: 243 result rows\n" - ] - } - ], - "source": [ - "# Run analysis for all available binding datasets\n", - "print(\"Executing comprehensive rank-response analysis...\")\n", - "print(\"This will analyze each binding dataset against all perturbation sources...\")\n", - "\n", - "# Execute analysis for each binding dataset\n", - "cc_rr_res = combine_rr_results(callingcards_ranked, \"callingcards\")\n", - "chec_rr_res = combine_rr_results(mahendrawada_chec_ranked, \"mahendrawada_chec\") \n", - "harb_rr_res = combine_rr_results(harbison_ranked, \"harbison\")\n", - "\n", - "print(f\"\\n{'='*50}\")\n", - "print(\"Individual dataset results:\")\n", - "print(f\"Calling Cards: {len(cc_rr_res)} result rows\")\n", - "print(f\"ChEC-seq: {len(chec_rr_res)} result rows\") \n", - "print(f\"Harbison: {len(harb_rr_res)} result rows\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Visualize rank-response relationships" - ] - }, - { - "cell_type": "code", - "execution_count": 75, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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7O3tBSMjI6Fhw4ZCbGysYGVlJfTr1++LxyOn/flUVsxPnz6Vm67s99LkyZMFR0dHQUdHR+4zosqxPH/+vODr6yuYmpoKxsbGQoMGDYSTJ08qLHv58mXB29tbMDQ0FBwdHYXJkycLK1euVPhsElH+Yk7PnJ45PXP63GRdIz79Z2hoKHh4eAhLlizJ9nP4ad6ZU26SXT7+4cMHYfDgwYKVlZVgYmIi+Pn5Cffv31dYpyq5fHZ554ULFwQvLy/BwMBAKF68uDB9+nRhwYIFAgDh8ePHuR6P//77T2jTpo1gYWEhmJubCx06dBAePnxYIPudnS995j7f7y9d03KKWxA+5rKDBg0SbGxsBIlEIpcnP336VGjXrp1gbGwsFC1aVOjbt69w9epVhc9FTp9VQfi/z+u///4r1K5dWzA0NBScnZ2FRYsWyZWTyWTCtGnTBGdnZ7Fe9vfffwv+/v6Cs7OzXNmTJ0+KOf6nxzO7PD89PV2YOHGi4OrqKujp6QlOTk7C6NGjhZSUFLlyqpxr2WFOT0TaSCIIHFknrwICArB582a19MtJRMp59uwZihUrhvHjx+f6+CsRqebVq1coWrQopkyZgjFjxhR2OEREBYY5PVHBY06fs+DgYCxbtgzJyck5DpBMRET0vfqmx8wgom9PVFQUMjMz0a1bt8IOhUhrffjwQWFaVr+8Pj4+BRsMERERfXeY03/0eU72/PlzrFmzBvXq1WNDBhERUTa++TEziOjbcOjQIVy/fh1Tp05F69at4eLiUtghEWmtjRs3IioqCs2bN4epqSmOHz+O9evX48cff0TdunULOzwiIiL6RjGnl1e7dm34+PigfPnyePLkCVauXIk3b97waRUiIqIcsDGDiLTCpEmTcPLkSdStWxcLFy4s7HCItFqVKlWgq6uLWbNm4c2bN+Kg4FOmTCns0IiIiOgbxpxeXvPmzbF582ZERERAIpGgWrVqWLlyJerXr1/YoREREWkkjplBREREREREREREREQajWNmEBERERERERERERGRRmNjBhERERERERERERERabTvbswMmUyGhw8fokiRIpBIJIUdDhERERUgQRDw9u1bODg4QEeH93QQaSvm9ERERN8v5vRE36/vrjHj4cOHcHJyKuwwiIiIqBDdv38fxYsXL+wwiCiPmNMTERERc3qi789315hRpEgRAB8veGZmZoUcDRERERWkN2/ewMnJScwHiEg7MacnIiL6fjGnJ/p+fXeNGVmPoZuZmbHiQ0RE9J1itzRE2o05PRERETGnJ/r+sGM5IiIiIiIiIiIiIiLSaGzMICIiIiIiIiIiIiIijcbGDCIiIiIiIiIiIiIi0mjf3ZgZRERERJogMzMT6enphR0GaQF9fX3o6PAeJCIiIiJNw5ye6OupUt9hYwYRERFRARIEAY8fP8arV68KOxTSEjo6OnB1dYW+vn5hh0JEREREYE5PpE6q1HfYmEFERERUgLIqPba2tjA2NoZEIinskEiDyWQyPHz4EI8ePUKJEiV4vhARERFpAOb0ROqhan2HjRlEREREBSQzM1Os9FhZWRV2OKQlbGxs8PDhQ2RkZEBPT6+wwyEiIiL6rjGnJ1IvVeo77HyXiIiIqIBk9adrbGxcyJGQNsl63DozM7OQIyEiIiIi5vRE6qVKfYeNGUREREQFjI+hkyp4vhARERFpHuZoROqhymeJjRlERERERERERERERKTR2JhBRERE9I1KSEiARCLBxYsXv2o9EyZMgIeHh/g6ICAArVu3/qp1ahIfHx8EBwerfb2fHzciIiIiou9ZftYjvqU6irrqcdlxcXFBWFiY2tdbUAq1MePo0aPw8/ODg4MDJBIJtm/f/sVloqOjUa1aNRgYGKB06dKIiorK9ziJiIiINE1AQAAkEon4z8rKCk2bNsXly5fFMk5OTnj06BEqVaqk1m3Pnz+/QHKwrH3s16+fwrwBAwZAIpEgICBA6fVFR0dDIpHg1atX6guSmNMTERERFbJP6wb6+vooXbo0Jk2ahIyMjK9er6Y1EOT0Q39B1VGyjvPp06flpqempsLKygoSiQTR0dFKr08Tj7EmK9TGjHfv3sHd3R2LFy9Wqnx8fDxatGiBBg0a4OLFiwgODkbv3r2xb9++fI6UiIiISPM0bdoUjx49wqNHj3Dw4EHo6uqiZcuW4nypVAp7e3vo6uqqdbvm5uawsLBQ6zpz4uTkhA0bNuDDhw/itJSUFPzxxx8oUaJEgcRAuWNOT0RERFT4suoGt2/fxvDhwzFhwgTMnj07T+vKzMyETCZTW2zqXl92CrqOEhkZKTdt27ZtMDU1LZDtf88KtTGjWbNmmDJlCtq0aaNU+aVLl8LV1RVz585F+fLlMXDgQLRv3x6//fZbPkdKREREpHkMDAxgb28Pe3t7eHh4YNSoUbh//z6ePn0KQPGupawnEw4ePIjq1avD2NgYderUwc2bN+XWO2PGDNjZ2aFIkSLo1asXUlJS5OZ/fveQj48PBg8ejJEjR8LS0hL29vaYMGGC3DI3btxAvXr1YGhoiAoVKuDAgQNK3cVfrVo1ODk5YevWreK0rVu3okSJEqhatapcWZlMhunTp8PV1RVGRkZwd3fH5s2bxWPRoEEDAEDRokUVnuqQyWS5xp+YmIhWrVrB1NQUZmZm6NixI548eaLScftWMacnIiIiKnxZdQNnZ2f0798fjRs3xo4dOwB8fGogJCQEjo6OMDExQa1ateSeHoiKioKFhQV27NiBChUqwMDAAD179sTq1avx119/iU8jREdHZ/u088WLFyGRSJCQkJDj+hITE8XyEydOhI2NDczMzNCvXz+kpaWJ8/bu3Yt69erBwsICVlZWaNmyJe7cuSPOd3V1BQBUrVoVEokEPj4+ABTrKKmpqRg8eDBsbW1haGiIevXq4ezZs+J8ZetG2fH391e44WrVqlXw9/dXKHv//n107NgRFhYWsLS0RKtWrcTjNGHChGyPcZa7d++iQYMGMDY2hru7O06dOiW37i1btqBixYowMDCAi4sL5s6dKzc/KSkJfn5+MDIygqurK9atW/fFfdN06r1NL5+dOnUKjRs3lpvm6+ubax/HqampSE1NFV+/efMmv8LLUUpKCu7fv5+v23BycoKhoWG+boOIiIg0V3JyMtauXYvSpUvDysoq17JjxozB3LlzYWNjg379+qFnz544ceIEAODPP//EhAkTsHjxYtSrVw9r1qzBggULULJkyVzXuXr1agwbNgwxMTE4deoUAgICULduXTRp0gSZmZlo3bo1SpQogZiYGLx9+xbDhw9Xet969uyJyMhIdO3aFcDHikKPHj0UHt+ePn061q5di6VLl8LNzQ1Hjx7FL7/8AhsbG9SrVw9btmxBu3btcPPmTZiZmcHIyEip+GUymdiQceTIEWRkZGDAgAH4+eefxRjyety+R8zps8d8noiIiNTJyMgIz58/BwAMHDgQ169fx4YNG+Dg4IBt27ahadOmuHLlCtzc3AAA79+/x8yZM7FixQpYWVmhWLFi+PDhA968eSM+hWBpaYmTJ08qtf3P12drawsAOHjwIAwNDREdHY2EhAT06NEDVlZWmDp1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I7LMmDED7dq1Q7du3VCtWjXExcVh3759KFq0qNKxfolEIoG1tbX4tMjnjI2NcfToUZQoUQJt27ZF+fLlxW6psuo12R1jZVSrVg1//vknNmzYgEqVKmH8+PGYNGkSAgICxDKRkZFwcHCAt7c32rZti8DAQNja2n71fhcmifBpB2ffgTdv3sDc3ByvX7/OtTJcWG7fvo2goCCEh4ezf10iIiI1K+w8ICUlBfHx8XB1dVXpR/xv0YkTJ1CvXj3ExcWhVKlShR2ORuN5o6iwP8tfwpyeiIgo/xR2HsDcjEi9VPlMadUA4ERERESknbZt2wZTU1O4ubkhLi4OQ4YMQd26ddmQQUREREREREphYwYRERER5bu3b9/if//7HxITE2FtbY3GjRur1LcuERERERERfd/YmEFERERE+a579+5yg0ATERERERERqaJQBwAnIiIiIiIiIiIiIiL6EjZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFG0y3sAIiIiIgISEpKwuvXrwtse+bm5rC1tS2w7WWJiopCcHAwXr16BQCYMGECtm/fjosXLwIAAgIC8OrVK2zfvr3AY8tvLi4uCA4ORnBwcGGHQkRERET54HvJ6bN8y7k7aSY2ZhAREREVsqSkJPTs0QOpaWkFtk0DfX2sioxUqfLz+PFjTJ06Fbt27cKDBw9ga2sLDw8PBAcHo1GjRmqJa/78+RAEQS3r+pKC2B8iIiIi+j5oS06vTgWZuxMBbMwgIiIiKnSvX79Galoa+pWtBgfjIvm+vYfv32LpzfN4/fq10hWfhIQE1K1bFxYWFpg9ezYqV66M9PR07Nu3DwMGDMCNGzfUEpu5ubla1vMl+bU/mZmZkEgk0NFhb65ERERE3xNtyOnVraByd6IsrGURERERaQgH4yJwMbXI9395qVwFBQVBIpHgzJkzaNeuHcqUKYOKFSti2LBhOH36tFhu3rx5qFy5MkxMTODk5ISgoCAkJycrvZ2AgAC0bt1afO3j44PBgwdj5MiRsLS0hL29PSZMmCC3zI0bN1CvXj0YGhqiQoUKOHDgACQSSa6Pu6trf6KiomBhYYEdO3agQoUKMDAwQGJiIpKSkuDn5wcjIyO4urpi3bp1ctsXBAET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zHD06FGEhYXhzZs3cHZ2xty5c9GsWTMAwMqVKxEYGIhq1arByckJ06ZNQ0hIiLi8Mrl3mTJl8M8//+DXX39FzZo1YWRkhFq1aqFz5875fixIM0gEVUZA/Aa8efMG5ubmeP36NczMzAo7HAW3b99GUFAQwsPD4ebmVtjhEBERfVMKOw9ISUlBfHw8XF1d5X5oT0pKQs8ePZD6FeNKqMpAXx+rIiNha2tbYNssKCdOnEC9evUQFxeHUqVKFXY4Xy2n8+Z7Vtif5S9hTk9ERJR/CjsPYE5PpF6q1Hf4ZAYRERFRIbO1tcWqyEi8fv26wLZpbm7+zVR6tm3bBlNTU7i5uSEuLg5DhgxB3bp1v4mGDCIiIiLSDszpifIfGzOIiIiINICtrS0rInn09u1b/O9//0NiYiKsra3RuHFjzJ07t7DDIiIiIqLvDHN6ovzFxgwiIiIi0mrdu3fP13E5iIiIiIiIqPDpFHYAREREREREREREREREuWFjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNN3CDoCIiIiIgKSkJLx+/brAtmdubg5bW1u1r1cikWDbtm1o3bq12tdNRERERKTJND2n9/HxgYeHB8LCwvIvKKJ8xMYMIiIiokKWlJSEnj0CkJqWXmDbNNDXw6rIKJUbNB4/foypU6di165dePDgAWxtbeHh4YHg4GA0atToi8snJCTA1dU123mnTp3CDz/8AABIS0tDWFgY1q1bh9u3b8PY2Bhly5ZF79698csvv0BPT0+luImIiIiI8pM25fRE2oqNGURERESF7PXr10hNS0cDDwFFTfN/ey+TgcMX0/H69WuVKj4JCQmoW7cuLCwsMHv2bFSuXBnp6enYt28fBgwYgBs3bii9rgMHDqBixYpy06ysrAB8bMjw9fXFpUuXMHnyZNStWxdmZmY4ffo05syZg6pVq8LDw0PpbRERERER5Tdtyem/Venp6bzh6TvAMTOIiIiINERRU8DaPP//5bVyFRQUBIlEgjNnzqBdu3YoU6YMKlasiGHDhuH06dNiuWfPnqFNmzYwNjaGm5sbduzYobAuKysr2Nvby/3LqnyEhYXh6NGjOHjwIAYMGAAPDw+ULFkSXbp0QUxMDNzc3HKM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b/VcZX6Xd9p67FiPIyLKX4q3PRWgYcOGYfny5Vi9ejViY2PRv39/vHv3Dj169AAAdO/eXW4wQT8/PyxZsgQbNmxAfHw89u/fj3HjxsHPz0+jBnwizSWVShEYGIiYmBiEhobi+vXreP/+Pa5fv47Q0FDExMQgMDCQ5xMVOn19fbRt2xYvX75Ely5dsGvXLjx79gy7du1Cly5d8PLlS7Rt25aDxn1DeH36Pujp6QH4+GMukbLS0tIAQGM//8zpiYiyx/wu77T12LEe932RyWSFHQLRN0GVm7cKdcyMn3/+GU+fPsX48ePx+PFjeHh4YO/eveIAgomJiXKt72PHjoVEIsHYsWPx4MED2NjYwM/PD1OnTi2sXSAt5OXlhXHjxiEiIgJDhgwRp9vb22PcuHHw8vIqxOiI/k9gYCAAYOvWrQgLCxOnS6VSdOjQQZxP3w5en759UqkUFhYWSEpKAgAYGxtDIpEUclSkyWQyGZ4+fQpjY2OFgUQ1BXN6IqKcMb/LO209dqzHffv09fWho6ODhw8fwsbGBvr6+szpifJIEAQ8ffoUEolEvPkvN4VeIxo4cGCOj6BHR0fLvdbV1UVoaChCQ0MLIDL6lnl5eaFOnTq4evUqnj9/DisrK1SqVEnj7uogCgwMREBAAHbu3ImHDx/CwcEBfn5+vJPnG8br07cvaxyBrAYNoi/R0dFBiRIlNLqSzJyeiChnzO/yTluPHetx3zYdHR24urri0aNHHMydSA0kEgmKFy+u1LW90BsziAqLVCqFu7t7YYdB9EX6+vpo165dYYdBBYjXp2+bRCJBsWLFYGtri/T09MIOh7RA1t1/RESkvZjf5Z22HjvW475t+vr6KFGiBDIyMpCZmVnY4RBpNT09PaUbqdmYQURERFQIpFKpxt9VSERERERE2cvqFkeZrnGISD14ixcREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUb7qsaMlJQUdcVBRERERESFgDk9ERERERFpA5UbM2QyGSZPngxHR0eYmpri7t27AIBx48Zh5cqVag+QiIiIiIjUizk9ERERERFpG5UbM6ZMmYKoqCjMmjUL+vr64vRKlSphxYoVag2OiIiIiIjUjzk9ERERERFpG5UbM37//XdERESga9eukEql4nR3d3fcuHFDrcEREREREZH6MacnIiIiIiJto3JjxoMHD1C6dGmF6TKZDOnp6WoJioiIiIiI8g9zeiIiIiIi0jYqN2ZUqFABx44dU5i+efNmVK1aVS1BERERERFR/mFOT0RERERE2kZX1QXGjx8Pf39/PHjwADKZDFu3bsXNmzfx+++/4++//86PGImIiIiISI2Y0xMRERERkbZR+cmMVq1aYefOnThw4ABMTEwwfvx4xMbGYufOnWjSpEl+xEhERERERGrEnJ6IiIiIiLSNyk9mAICXlxf279+v7liI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kxP27duHoUOHokqVKnB0dMSQIUPwv//9T9XdoO9cYGAgAgICsHPnTjx8+BAODg7w8/PjXQakcXiufh0vLy/UqVMHV69exfPnz2FlZYVKlSrxbs18wHOVSLswpydtx+94ou+DtuaYjJuIKH+o3Jgxbdo0vH37FgAwdepUdO/eHf3794ebmxtWrlypcgADBw7EwIEDs50XHR2tMK127do4ffq0ytsh+py+vj7atWtX2GEQfRHP1a8jlUrh7u5e2GF8F3iuEmkP5vT0LeB3PNH3QVtzTMZNRKR+KjdmVK9eXfzb1tYWe/fuVWtARERERESUv5jTExERERGRtlF5APCcnD9/Hi1btlTX6oiIiIiIqIAxpyciIiIiIk2lUmPGvn37EBISgl9//RV3794FANy4cQOtW7dGjRo1IJPJ8iVIIiIiIiJSD+b0RERERESkjZTuZmrlypXo06cPLC0t8fLlS6xYsQLz5s3DoEGD8PPPP+Pq1asoX758fsZKRERERERfgTk9ERERERFpK6WfzJg/fz5mzpyJZ8+e4c8//8SzZ88QHh6OK1euYOnSpaz0EBERERFpOOb0RERERESkrZRuzLhz5w46dOgAAGjbti10dXUxe/ZsFC9ePN+CIyIiIiIi9WFOT0RERERE2krpxowPHz7A2NgYACCRSGBgYIBixYrlW2BERERERKRezOmJiIiIiEhbKT1mBgCsWLECpqamAICMjAxERUXB2tparszgwYPVFx0REREREakVc3oiIiIiItJGSjdmlChRAsuXLxdf29vbY82aNXJlJBIJKz5ERERERBqKOT0REREREWkrpRszEhIS8jEMIiIiIiLKb8zpiYiIiIhIWyk9ZgYREREREREREREREVFhYGMGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUbLU2PGnTt3MHbsWHTu3BlJSUkAgD179uDatWtqDY6IiIiIiPIHc3oiIiIiItImKjdmHDlyBJUrV0ZMTAy2bt2K5ORkAMClS5cQGhqq9gCJiIiIiEi9mNMTEREREZG2UbkxY9SoUZgyZQr2798PfX19cXrDhg1x+vRptQZHRERERETqx5yeiIiIiIi0jcqNGVeuXEGbNm0Uptva2uLZs2dqCYqIiIiIiPIPc3oiIiIiItI2KjdmWFhY4NGjRwrTL1y4AEdHR7UERURERERE+Yc5PRERERERaRuVGzM6deqE//3vf3j8+DEkEglkMhlOnDiBkJAQdO/ePT9iJCIiIiIiNWJOT0RERERE2kblxoxp06ahXLlycHJyQnJyMipUqID69eujTp06GDt2bH7ESEREREREasScnoiIiIiItI2uqgvo6+tj+fLlGD9+PK5cuYLk5GRUrVoVbm5u+REfERERERGpGXN6IiIiIiLSNio3ZmRxcnKCk5MTMjMzceXKFbx8+RJFixZVZ2xERERERJSPmNMTEREREZG2ULmbqeDgYKxcuRIAkJmZCW9vb1SrVg1OTk6Ijo5Wd3xERERERKRmzOmJiIiIiEjbqNyYsXnzZri7uwMAdu7cibt37+LGjRsYOnQoxowZo/YAiYiIiIhIvZjTExERERGRtlG5MePZs2ewt7cHAOzevRsdO3ZEmTJl0LNnT1y5ckXtARIRERERkXoxpyciIiIiIm2jcmOGnZ0drl+/jszMTOzduxdNmjQBALx//x5SqVTtARIRERERkXoxpyciIiIiIm2j8gDgPXr0QMeOHVGsWDFIJBI0btwYABATE4Ny5cqpPUAiIiIiIlIv5vRERERERKRtVG7MmDBhAipVqoT79++jQ4cOMDAwAABIpVKMGjVK7QESEREREZF6MacnIiIiIiJto3JjBgC0b99eYZq/v/9XB0NERERERAWDOT0REREREWmTPDVmHDx4EAcPHkRSUhJkMpncvFWrVqklMCIiIiIiyj/M6YmIiIiISJuo3JgxceJETJo0CdWrVxf72CUiIiIiIu3BnJ6IiIiIiLSNyo0ZS5cuRVRUFLp165Yf8RARERERUT5jTk9ERERERNpGR9UF0tLSUKdOnfyIhYiIiIiICgBzeiIiIiIi0jYqN2b07t0bf/zxR37EQkREREREBYA5PRERERERaRuVu5lKSUlBREQEDhw4gCpVqkBPT09u/rx589QWHBERERERqR9zeiIiIiIi0jYqN2ZcvnwZHh4eAICrV6/KzePAgUREREREmo85PRERERERaRuVGzMOHz6cH3EQEREREVEBYU5PRERERETaRuUxMz7133//4b///lNXLEREREREVMCY0xMRERERkTZQuTFDJpNh0qRJMDc3h7OzM5ydnWFhYYHJkydDJpPlR4xERERERKRGzOmJiIiIiEjbqNzN1JgxY7By5UrMmDEDdevWBQAcP34cEyZMQEpKCqZOnar2IImIiIiISH2Y0xMRERERkbZRuTFj9erVWLFiBX766SdxWpUqVeDo6IigoCBWfIiIiIiINBxzeiIiIiIi0jYqdzP14sULlCtXTmF6uXLl8OLFC7UERURERERE+Yc5PRERERERaRuVGzPc3d2xaNEihemLFi2Cu7u7WoIiIiIiIqL8w5yeiIiIiIi0jcrdTM2aNQstWrTAgQMHULt2bQDAqVOncP/+fezevVvtARIRERERkXoxpyciIiIiIm2j8pMZ3t7euHXrFtq0aYNXr17h1atXaNu2LW7evAkvL6/8iJGIiIiIiNSIOT0REREREWkblZ/MAAAHBwcOCkhEREREpMWY0xMRERERkTbJU2PGy5cvsXLlSsTGxgIAKlSogB49esDS0lKtwRERERERUf5gTk9ERERERNpE5W6mjh49ChcXFyxYsAAvX77Ey5cvsWDBAri6uuLo0aP5ESMREREREakRc3oiIiIiItI2Kj+ZMWDAAPz8889YsmQJpFIpACAzMxNBQUEYMGAArly5ovYgiYiIiIhIfZjTExERERGRtlH5yYy4uDgMHz5crPQAgFQqxbBhwxAXF6fW4IiIiIiISP2Y0xMRERERkbZRuTGjWrVqYr+6n4qNjYW7u7tagiIiIiIiovzDnJ6IiIiIiLSNyt1MDR48GEOGDEFcXBx++OEHAMDp06exePFizJgxA5cvXxbLVqlSRX2REhERERGRWjCnJyIiIiIibaNyY0bnzp0BACNHjsx2nkQigSAIkEgkyMzM/PoIiYiIiIhIrZjTExERERGRtlG5MSM+Pj4/4iAiIiIiogLCnJ6IiIiIiLSNyo0Zzs7O+REHEREREREVEOb0RERERESkbVQeAHz16tXYtWuX+HrkyJGwsLBAnTp1cO/ePbUGR0RERERE6secnoiIiIiItI3KjRnTpk2DkZERAODUqVNYtGgRZs2aBWtrawwdOlTtARIRERERkXoxpyciIiIiIm2jcjdT9+/fR+nSpQEA27dvR/v27REYGIi6devCx8dH3fEREREREZGaMacnIiIiIiJto/KTGaampnj+/DkA4J9//kGTJk0AAIaGhvjw4UOegli8eDFcXFxgaGiIWrVq4cyZM0ott2HDBkgkErRu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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Summary Statistics by Method Combination:\n", - "============================================================\n", - " Count Mean Median Std Min Max\n", - "binding_source perturbation_source \n", - "Calling Cards kemmeren 33 0.309 0.4 0.265 0.0 0.8\n", - " mahendrawada_rnaseq 33 0.103 0.0 0.167 0.0 0.6\n", - " mcisaac 33 0.297 0.2 0.292 0.0 1.0\n", - "ChEC-seq kemmeren 33 0.261 0.2 0.247 0.0 0.8\n", - " mahendrawada_rnaseq 33 0.091 0.0 0.181 0.0 0.8\n", - " mcisaac 33 0.255 0.2 0.284 0.0 0.8\n", - "Harbison kemmeren 81 0.074 0.0 0.177 0.0 0.8\n", - " mahendrawada_rnaseq 81 0.020 0.0 0.098 0.0 0.6\n", - " mcisaac 81 0.064 0.0 0.158 0.0 0.8\n", - "\n", - "Overall Performance by Binding Method:\n", - "========================================\n", - " Count Mean Median Std\n", - "binding_source \n", - "Calling Cards 99 0.236 0.2 0.262\n", - "ChEC-seq 99 0.202 0.2 0.252\n", - "Harbison 243 0.053 0.0 0.149\n" - ] - } - ], - "source": [ - "\n", - "# Combine all rank-response results for comparison\n", - "combined_results = []\n", - "\n", - "if len(cc_rr_res) > 0:\n", - " cc_rr_res['binding_source'] = 'Calling Cards'\n", - " combined_results.append(cc_rr_res)\n", - "\n", - "if len(chec_rr_res) > 0:\n", - " chec_rr_res['binding_source'] = 'ChEC-seq' \n", - " combined_results.append(chec_rr_res)\n", - "\n", - "if len(harb_rr_res) > 0:\n", - " harb_rr_res['binding_source'] = 'Harbison'\n", - " combined_results.append(harb_rr_res)\n", - "\n", - "if combined_results:\n", - " all_rr_data = pd.concat(combined_results, ignore_index=True)\n", - "\n", - " # Create comparative boxplot visualizations\n", - " fig, axes = plt.subplots(1, 2, figsize=(16, 6))\n", - "\n", - " # 1. Combined boxplot - binding methods grouped by perturbation source\n", - " sns.boxplot(data=all_rr_data, x='perturbation_source', y='response_rate', \n", - " hue='binding_source', ax=axes[0])\n", - " axes[0].set_title('Response Rate Performance by Perturbation and Binding Method')\n", - " axes[0].set_xlabel('Perturbation Source')\n", - " axes[0].set_ylabel('Response Rate')\n", - " axes[0].legend(title='Binding Method', bbox_to_anchor=(1.05, 1), loc='upper left')\n", - " axes[0].tick_params(axis='x', rotation=45)\n", - "\n", - " # 2. Faceted view - binding methods grouped by binding source \n", - " sns.boxplot(data=all_rr_data, x='binding_source', y='response_rate',\n", - " hue='perturbation_source', ax=axes[1])\n", - " axes[1].set_title('Response Rate Performance by Binding and Perturbation Method')\n", - " axes[1].set_xlabel('Binding Source')\n", - " axes[1].set_ylabel('Response Rate')\n", - " axes[1].legend(title='Perturbation Method', bbox_to_anchor=(1.05, 1), loc='upper left')\n", - " axes[1].tick_params(axis='x', rotation=45)\n", - "\n", - " plt.tight_layout()\n", - " plt.show()\n", - "\n", - " # Summary statistics table\n", - " print(\"Summary Statistics by Method Combination:\")\n", - " print(\"=\"*60)\n", - " summary_stats = all_rr_data.groupby(['binding_source', 'perturbation_source']).agg({\n", - " 'response_rate': ['count', 'mean', 'median', 'std', 'min', 'max']\n", - " }).round(3)\n", - " summary_stats.columns = ['Count', 'Mean', 'Median', 'Std', 'Min', 'Max']\n", - " print(summary_stats)\n", - "\n", - " # Overall performance by binding method\n", - " print(f\"\\nOverall Performance by Binding Method:\")\n", - " print(\"=\"*40)\n", - " binding_performance = all_rr_data.groupby('binding_source').agg({\n", - " 'response_rate': ['count', 'mean', 'median', 'std']\n", - " }).round(3)\n", - " binding_performance.columns = ['Count', 'Mean', 'Median', 'Std']\n", - " print(binding_performance)\n", - "\n", - "else:\n", - " print(\"No results available for comparison visualization\")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "tfbpapi-py3.11", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.9" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/tutorials/sample_manager_tutorial.ipynb b/docs/tutorials/sample_manager_tutorial.ipynb deleted file mode 100644 index 1c73ef2..0000000 --- a/docs/tutorials/sample_manager_tutorial.ipynb +++ /dev/null @@ -1,814 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# SampleManager Tutorial: Node-Based Filtering Across Heterogeneous Datasets\n", - "\n", - "This tutorial introduces `SampleManager`, a flexible node-based system for filtering samples across multiple datasets with varying experimental condition structures.\n", - "\n", - "## Why SampleManager?\n", - "\n", - "Traditional table-based approaches struggle with heterogeneous metadata:\n", - "- Different datasets structure conditions differently (e.g., `media.carbon_source` vs `environmental_conditions.media.carbon_source`)\n", - "- Missing fields and optional properties vary by dataset\n", - "- Multi-level hierarchies (repo/config/field) require complex joins\n", - "\n", - "**SampleManager** solves this by:\n", - "- Representing each sample as a node with dynamically discovered properties\n", - "- Supporting flexible MongoDB-style queries across heterogeneous structures\n", - "- Enabling cross-dataset filtering and set operations\n", - "- Respecting hierarchical overrides (field > config > repo)\n", - "\n", - "## Key Concepts\n", - "\n", - "- **SampleNode**: A sample with flattened properties (experimental conditions + metadata)\n", - "- **ActiveSet**: A filtered collection of sample IDs supporting set operations\n", - "- **Query Language**: MongoDB-style filters with operators like `$contains`, `$gte`, `$or`\n", - "- **Property Discovery**: No hardcoded schemas - properties are discovered from the data" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 1. Setup and Basic Usage" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "SampleManager(0 datasets, 0 samples)\n" - ] - } - ], - "source": [ - "from tfbpapi.datainfo.sample_manager import SampleManager, SampleNode\n", - "from tfbpapi.datainfo import DataCard\n", - "import json\n", - "\n", - "# Initialize manager\n", - "manager = SampleManager()\n", - "print(manager)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2. Understanding Property Discovery\n", - "\n", - "Let's explore how SampleManager discovers properties from a DataCard without hardcoded schemas." - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Repo-level conditions:\n", - "{}\n" - ] - } - ], - "source": [ - "# Load a DataCard to see its experimental conditions structure\n", - "card = DataCard('BrentLab/harbison_2004')\n", - "\n", - "# Get repo-level conditions\n", - "repo_conditions = card.get_experimental_conditions('harbison_2004')\n", - "print(\"Repo-level conditions:\")\n", - "print(json.dumps(repo_conditions, indent=2, default=str))" - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Field-level definition for 'YPD':\n", - "{\n", - " \"description\": \"Rich media baseline condition\",\n", - " \"temperature_celsius\": 30,\n", - " \"cultivation_method\": \"unspecified\",\n", - " \"growth_phase_at_harvest\": {\n", - " \"od600\": 0.8\n", - " },\n", - " \"media\": {\n", - " \"name\": \"YPD\",\n", - " \"carbon_source\": [\n", - " {\n", - " \"compound\": \"D-glucose\",\n", - " \"concentration_percent\": 2\n", - " }\n", - " ],\n", - " \"nitrogen_source\": [\n", - " {\n", - " \"compound\": \"yeast_extract\",\n", - " \"concentration_percent\": 1\n", - " },\n", - " {\n", - " \"compound\": \"peptone\",\n", - " \"concentration_percent\": 2\n", - " }\n", - " ]\n", - " }\n", - "}\n" - ] - } - ], - "source": [ - "# Get field-level condition definitions\n", - "field_defs = card.get_field_definitions('harbison_2004', 'condition')\n", - "\n", - "# Show one condition definition\n", - "print(\"\\nField-level definition for 'YPD':\")\n", - "print(json.dumps(field_defs.get('YPD', {}), indent=2, default=str))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### How Properties Are Flattened\n", - "\n", - "SampleManager recursively flattens nested structures using dot notation:\n", - "\n", - "```python\n", - "# Original nested structure:\n", - "{\n", - " \"environmental_conditions\": {\n", - " \"media\": {\n", - " \"name\": \"YPD\",\n", - " \"carbon_source\": [{\"compound\": \"D-glucose\", \"concentration_percent\": 2}]\n", - " },\n", - " \"temperature_celsius\": 30\n", - " }\n", - "}\n", - "\n", - "# Becomes flattened properties:\n", - "{\n", - " \"environmental_conditions.media.name\": \"YPD\",\n", - " \"environmental_conditions.media.carbon_source\": \"D-glucose\", # Simple string\n", - " \"_environmental_conditions.media.carbon_source_structured\": [{...}], # Full structure\n", - " \"environmental_conditions.temperature_celsius\": 30\n", - "}\n", - "```\n", - "\n", - "**Key Points:**\n", - "- Nested dicts become dot-notation keys\n", - "- Lists of dicts (compounds) get both simple and `_structured` versions\n", - "- No hardcoded field names - discovers whatever exists\n", - "- Different datasets can have different structures" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 3. Creating Sample Nodes Manually\n", - "\n", - "Before we load from DataCard, let's manually create nodes to understand the structure." - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Flattened properties:\n", - " environmental_conditions.cultivation_method: liquid_culture (from repo)\n", - " environmental_conditions.media.carbon_source: D-glucose (from field)\n", - " environmental_conditions.media.name: YPD (from field)\n", - " environmental_conditions.media.nitrogen_source: yeast_extract, peptone (from field)\n", - " environmental_conditions.temperature_celsius: 30 (from repo)\n", - " strain_background: BY4741 (from repo)\n" - ] - } - ], - "source": [ - "from tfbpapi.datainfo.sample_manager import SampleNode, ConditionFlattener\n", - "\n", - "# Simulate experimental conditions from different levels\n", - "repo_conditions = {\n", - " \"environmental_conditions\": {\n", - " \"temperature_celsius\": 30,\n", - " \"cultivation_method\": \"liquid_culture\"\n", - " },\n", - " \"strain_background\": \"BY4741\"\n", - "}\n", - "\n", - "field_conditions = {\n", - " \"environmental_conditions\": {\n", - " \"media\": {\n", - " \"name\": \"YPD\",\n", - " \"carbon_source\": [\n", - " {\"compound\": \"D-glucose\", \"concentration_percent\": 2}\n", - " ],\n", - " \"nitrogen_source\": [\n", - " {\"compound\": \"yeast_extract\", \"concentration_percent\": 1},\n", - " {\"compound\": \"peptone\", \"concentration_percent\": 2}\n", - " ]\n", - " }\n", - " }\n", - "}\n", - "\n", - "# Flatten conditions\n", - "properties, sources = ConditionFlattener.flatten_conditions(\n", - " repo_conditions, None, field_conditions\n", - ")\n", - "\n", - "print(\"Flattened properties:\")\n", - "for key, value in sorted(properties.items()):\n", - " source = sources.get(key, 'unknown')\n", - " # Skip structured versions for cleaner output\n", - " if not key.startswith('_'):\n", - " print(f\" {key}: {value} (from {source})\")" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Node: SampleNode(BrentLab/harbison_2004:harbison_2004:sample_001, 8 properties)\n", - "Global ID: BrentLab/harbison_2004:harbison_2004:sample_001\n", - "\n", - "Sample properties:\n", - " Temperature: 30\n", - " Carbon source: D-glucose\n" - ] - } - ], - "source": [ - "# Create a sample node\n", - "node = SampleNode(\n", - " sample_id=\"sample_001\",\n", - " repo_id=\"BrentLab/harbison_2004\",\n", - " config_name=\"harbison_2004\",\n", - " properties=properties,\n", - " property_sources=sources\n", - ")\n", - "\n", - "print(f\"Node: {node}\")\n", - "print(f\"Global ID: {node.global_id()}\")\n", - "print(f\"\\nSample properties:\")\n", - "print(f\" Temperature: {node.get_property('environmental_conditions.temperature_celsius')}\")\n", - "print(f\" Carbon source: {node.get_property('environmental_conditions.media.carbon_source')}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 4. Filtering with the Query Language\n", - "\n", - "SampleManager uses MongoDB-style queries for flexible filtering." - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Query 1 matches: True\n", - "Query 2 matches: True\n", - "Query 3 matches: True\n", - "Query 4 matches: True\n" - ] - } - ], - "source": [ - "from tfbpapi.datainfo.sample_manager import SampleFilter\n", - "\n", - "# Simple equality\n", - "query1 = {\"environmental_conditions.temperature_celsius\": 30}\n", - "print(f\"Query 1 matches: {SampleFilter.matches(node, query1)}\")\n", - "\n", - "# Contains check (case-insensitive)\n", - "query2 = {\"environmental_conditions.media.carbon_source\": {\"$contains\": \"glucose\"}}\n", - "print(f\"Query 2 matches: {SampleFilter.matches(node, query2)}\")\n", - "\n", - "# Numeric comparison\n", - "query3 = {\"environmental_conditions.temperature_celsius\": {\"$gte\": 25, \"$lte\": 35}}\n", - "print(f\"Query 3 matches: {SampleFilter.matches(node, query3)}\")\n", - "\n", - "# Logical OR\n", - "query4 = {\n", - " \"$or\": [\n", - " {\"environmental_conditions.media.carbon_source\": {\"$contains\": \"galactose\"}},\n", - " {\"environmental_conditions.media.carbon_source\": {\"$contains\": \"glucose\"}}\n", - " ]\n", - "}\n", - "print(f\"Query 4 matches: {SampleFilter.matches(node, query4)}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Available Query Operators\n", - "\n", - "| Operator | Description | Example |\n", - "|----------|-------------|----------|\n", - "| `$eq` | Equal (default) | `{\"temp\": 30}` or `{\"temp\": {\"$eq\": 30}}` |\n", - "| `$ne` | Not equal | `{\"temp\": {\"$ne\": 30}}` |\n", - "| `$gt` | Greater than | `{\"temp\": {\"$gt\": 30}}` |\n", - "| `$gte` | Greater than or equal | `{\"temp\": {\"$gte\": 30}}` |\n", - "| `$lt` | Less than | `{\"temp\": {\"$lt\": 30}}` |\n", - "| `$lte` | Less than or equal | `{\"temp\": {\"$lte\": 30}}` |\n", - "| `$in` | In list | `{\"strain\": {\"$in\": [\"BY4741\", \"W303\"]}}` |\n", - "| `$nin` | Not in list | `{\"strain\": {\"$nin\": [\"BY4741\"]}}` |\n", - "| `$contains` | String contains (case-insensitive) | `{\"carbon_source\": {\"$contains\": \"glucose\"}}` |\n", - "| `$exists` | Field exists | `{\"temperature\": {\"$exists\": true}}` |\n", - "| `$and` | Logical AND | `{\"$and\": [{...}, {...}]}` |\n", - "| `$or` | Logical OR | `{\"$or\": [{...}, {...}]}` |" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 5. Working with ActiveSets\n", - "\n", - "ActiveSets represent filtered collections of samples and support set operations." - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Set 1: ActiveSet(name=glucose_samples, size=3)\n", - "Set 2: ActiveSet(name=heat_stress_samples, size=3)\n" - ] - } - ], - "source": [ - "from tfbpapi.datainfo.sample_manager import ActiveSet\n", - "\n", - "# Create some example active sets\n", - "set1 = ActiveSet(\n", - " sample_ids={\"BrentLab:harbison_2004:sample_001\", \"BrentLab:harbison_2004:sample_002\", \"BrentLab:harbison_2004:sample_003\"},\n", - " name=\"glucose_samples\",\n", - " description=\"Samples grown on glucose\"\n", - ")\n", - "\n", - "set2 = ActiveSet(\n", - " sample_ids={\"BrentLab:harbison_2004:sample_002\", \"BrentLab:harbison_2004:sample_003\", \"BrentLab:harbison_2004:sample_004\"},\n", - " name=\"heat_stress_samples\",\n", - " description=\"Samples with heat stress\"\n", - ")\n", - "\n", - "print(f\"Set 1: {set1}\")\n", - "print(f\"Set 2: {set2}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Union: ActiveSet(name=glucose_or_heat, size=4)\n", - " Sample IDs: ['BrentLab:harbison_2004:sample_001', 'BrentLab:harbison_2004:sample_002', 'BrentLab:harbison_2004:sample_003', 'BrentLab:harbison_2004:sample_004']\n", - "\n", - "Intersection: ActiveSet(name=glucose_and_heat, size=2)\n", - " Sample IDs: ['BrentLab:harbison_2004:sample_002', 'BrentLab:harbison_2004:sample_003']\n", - "\n", - "Difference: ActiveSet(name=glucose_no_heat, size=1)\n", - " Sample IDs: ['BrentLab:harbison_2004:sample_001']\n" - ] - } - ], - "source": [ - "# Set operations\n", - "\n", - "# Union - samples in either set\n", - "union = set1.union(set2, name=\"glucose_or_heat\")\n", - "print(f\"Union: {union}\")\n", - "print(f\" Sample IDs: {union.to_sample_ids()}\")\n", - "\n", - "# Intersection - samples in both sets\n", - "intersection = set1.intersection(set2, name=\"glucose_and_heat\")\n", - "print(f\"\\nIntersection: {intersection}\")\n", - "print(f\" Sample IDs: {intersection.to_sample_ids()}\")\n", - "\n", - "# Difference - samples in set1 but not set2\n", - "difference = set1.difference(set2, name=\"glucose_no_heat\")\n", - "print(f\"\\nDifference: {difference}\")\n", - "print(f\" Sample IDs: {difference.to_sample_ids()}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 6. Real-World Example: Exploring Harbison 2004\n", - "\n", - "Now let's work with real data. Note that we're manually creating nodes here since the `load_from_datacard` method needs to be implemented. This demonstrates the intended workflow." - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Found 14 condition definitions\n", - "Conditions: ['Acid', 'Alpha', 'BUT14', 'BUT90', 'GAL', 'H2O2Hi', 'H2O2Lo', 'HEAT', 'Pi-', 'RAFF', 'RAPA', 'SM', 'Thi-', 'YPD']\n" - ] - } - ], - "source": [ - "# For this tutorial, we'll manually create nodes from DataCard information\n", - "# In production, you would use: manager.load_from_datacard(\"BrentLab/harbison_2004\", \"harbison_2004\")\n", - "\n", - "from tfbpapi.datainfo import DataCard\n", - "\n", - "card = DataCard('BrentLab/harbison_2004')\n", - "\n", - "# Get repo-level and field-level conditions\n", - "repo_conds = card.get_experimental_conditions('harbison_2004')\n", - "field_defs = card.get_field_definitions('harbison_2004', 'condition')\n", - "\n", - "print(f\"Found {len(field_defs)} condition definitions\")\n", - "print(f\"Conditions: {sorted(field_defs.keys())}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Loaded 14 sample nodes\n", - "SampleManager(1 datasets, 14 samples)\n" - ] - } - ], - "source": [ - "# Manually create nodes for each condition (simulating samples)\n", - "# This demonstrates what load_from_datacard will do automatically\n", - "\n", - "demo_manager = SampleManager()\n", - "\n", - "for condition_name, condition_def in field_defs.items():\n", - " # Flatten conditions\n", - " properties, sources = ConditionFlattener.flatten_conditions(\n", - " repo_conds, None, condition_def\n", - " )\n", - " \n", - " # Create node (using condition name as sample_id for demo)\n", - " node = SampleNode(\n", - " sample_id=condition_name,\n", - " repo_id=\"BrentLab/harbison_2004\",\n", - " config_name=\"harbison_2004\",\n", - " properties=properties,\n", - " property_sources=sources\n", - " )\n", - " \n", - " # Add to manager's collection\n", - " demo_manager._collection.add_node(node)\n", - "\n", - "print(f\"\\nLoaded {demo_manager._collection.count_total_nodes()} sample nodes\")\n", - "print(demo_manager)" - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Properties available in YPD sample:\n", - " cultivation_method: unspecified\n", - " description: Rich media baseline condition\n", - " growth_phase_at_harvest.od600: 0.8\n", - " media.carbon_source: D-glucose\n", - " media.name: YPD\n", - " media.nitrogen_source: yeast_extract, peptone\n", - " temperature_celsius: 30\n" - ] - } - ], - "source": [ - "# Explore what properties are available\n", - "sample_node = demo_manager.get_sample(\"BrentLab/harbison_2004:harbison_2004:YPD\")\n", - "\n", - "print(\"Properties available in YPD sample:\")\n", - "for key in sorted(sample_node.properties.keys()):\n", - " if not key.startswith('_'): # Skip structured versions\n", - " print(f\" {key}: {sample_node.properties[key]}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 7. Cross-Condition Filtering\n", - "\n", - "Now we can filter samples based on their experimental conditions." - ] - }, - { - "cell_type": "code", - "execution_count": 46, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Found 0 conditions with D-glucose\n", - "Conditions: []\n" - ] - } - ], - "source": [ - "# Find all samples with D-glucose as carbon source\n", - "glucose_samples = demo_manager.filter_all({\n", - " \"environmental_conditions.media.carbon_source\": {\"$contains\": \"D-glucose\"}\n", - "}, name=\"glucose_conditions\")\n", - "\n", - "print(f\"Found {len(glucose_samples)} conditions with D-glucose\")\n", - "print(f\"Conditions: {[sid.split(':')[-1] for sid in glucose_samples.to_sample_ids()]}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Found 0 conditions with alternative carbon sources\n", - "Conditions: []\n" - ] - } - ], - "source": [ - "# Find samples with alternative carbon sources\n", - "alt_carbon = demo_manager.filter_all({\n", - " \"$or\": [\n", - " {\"environmental_conditions.media.carbon_source\": {\"$contains\": \"galactose\"}},\n", - " {\"environmental_conditions.media.carbon_source\": {\"$contains\": \"raffinose\"}}\n", - " ]\n", - "}, name=\"alternative_carbon\")\n", - "\n", - "print(f\"\\nFound {len(alt_carbon)} conditions with alternative carbon sources\")\n", - "print(f\"Conditions: {[sid.split(':')[-1] for sid in alt_carbon.to_sample_ids()]}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Found 0 conditions with additives\n", - "Conditions: []\n" - ] - } - ], - "source": [ - "# Find samples with media additives (like butanol)\n", - "with_additives = demo_manager.filter_all({\n", - " \"environmental_conditions.media.additives\": {\"$contains\": \"butanol\"}\n", - "}, name=\"additive_conditions\")\n", - "\n", - "print(f\"\\nFound {len(with_additives)} conditions with additives\")\n", - "print(f\"Conditions: {[sid.split(':')[-1] for sid in with_additives.to_sample_ids()]}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 8. Property Distribution Analysis" - ] - }, - { - "cell_type": "code", - "execution_count": 49, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Carbon source distribution:\n", - " missing: 14 samples\n" - ] - } - ], - "source": [ - "# Analyze carbon source distribution\n", - "carbon_dist = demo_manager.get_property_distribution(\n", - " \"environmental_conditions.media.carbon_source\"\n", - ")\n", - "\n", - "print(\"Carbon source distribution:\")\n", - "for value, count in sorted(carbon_dist.items(), key=lambda x: x[1], reverse=True):\n", - " print(f\" {value}: {count} samples\")" - ] - }, - { - "cell_type": "code", - "execution_count": 50, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Media type distribution:\n", - " missing: 14 samples\n" - ] - } - ], - "source": [ - "# Analyze media names\n", - "media_dist = demo_manager.get_property_distribution(\n", - " \"environmental_conditions.media.name\"\n", - ")\n", - "\n", - "print(\"\\nMedia type distribution:\")\n", - "for value, count in sorted(media_dist.items(), key=lambda x: x[1], reverse=True):\n", - " print(f\" {value}: {count} samples\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 9. Summary Information" - ] - }, - { - "cell_type": "code", - "execution_count": 51, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Summary of loaded datasets:\n", - " repo_id config_name sample_count \\\n", - "0 BrentLab/harbison_2004 harbison_2004 14 \n", - "\n", - " properties \n", - "0 [description, temperature_celsius, cultivation... \n" - ] - } - ], - "source": [ - "# Get summary of loaded data\n", - "summary = demo_manager.get_summary()\n", - "print(\"\\nSummary of loaded datasets:\")\n", - "print(summary)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 10. Key Takeaways\n", - "\n", - "### Advantages of Node-Based Approach\n", - "\n", - "1. **Flexible Schema**: No hardcoded field names - discovers whatever exists in the data\n", - "2. **Heterogeneity Handling**: Different datasets can have different structures (e.g., `media.carbon_source` vs `environmental_conditions.media.carbon_source`)\n", - "3. **Cross-Dataset Queries**: Filter samples across multiple datasets with varying structures\n", - "4. **Set Operations**: Build complex filters incrementally using union/intersection/difference\n", - "5. **Property Discovery**: Automatically flattens nested structures using dot notation\n", - "6. **Dual Representation**: Compound lists get both simple strings and structured versions\n", - "\n", - "### How to Query Different Property Paths\n", - "\n", - "Since different datasets structure their conditions differently, you need to use the actual property path:\n", - "\n", - "```python\n", - "# Dataset A (harbison_2004): media nested under environmental_conditions\n", - "manager.filter_all({\"environmental_conditions.media.carbon_source\": {\"$contains\": \"glucose\"}})\n", - "\n", - "# Dataset B (hypothetical): media at top level \n", - "manager.filter_all({\"media.carbon_source\": {\"$contains\": \"glucose\"}})\n", - "\n", - "# To query across both, use $or:\n", - "manager.filter_all({\n", - " \"$or\": [\n", - " {\"environmental_conditions.media.carbon_source\": {\"$contains\": \"glucose\"}},\n", - " {\"media.carbon_source\": {\"$contains\": \"glucose\"}}\n", - " ]\n", - "})\n", - "```\n", - "\n", - "### Future: External Property Mappings\n", - "\n", - "For cross-dataset harmonization, you can provide external YAML mappings:\n", - "\n", - "```yaml\n", - "# property_mappings.yaml\n", - "carbon_source:\n", - " paths:\n", - " - environmental_conditions.media.carbon_source\n", - " - media.carbon_source\n", - " value_aliases:\n", - " D-glucose: [glucose, dextrose, D-dextrose]\n", - "```\n", - "\n", - "This would enable canonical queries like `{\"carbon_source\": {\"$contains\": \"glucose\"}}` to work across all datasets." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Next Steps\n", - "\n", - "- Implement `load_from_datacard()` to automatically load samples from HuggingFace\n", - "- Implement `load_from_duckdb()` for integration with HfQueryAPI\n", - "- Implement `export_to_duckdb()` to convert ActiveSets back to SQL views\n", - "- Add external property mapping support via YAML configuration\n", - "- Build UI for interactive filtering and set operations" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "tfbpapi-py3.11", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.9" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/tutorials/virtual_db_tutorial.ipynb b/docs/tutorials/virtual_db_tutorial.ipynb new file mode 100644 index 0000000..14c613e --- /dev/null +++ b/docs/tutorials/virtual_db_tutorial.ipynb @@ -0,0 +1,721 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# VirtualDB Tutorial: Unified Cross-Dataset Queries\n", + "\n", + "The `VirtualDB` class provides a unified query interface across heterogeneous datasets with different experimental condition structures and terminologies. Each dataset defines conditions in its own way, with properties at different hierarchy levels and using different naming conventions. VirtualDB uses external YAML configuration to:\n", + "\n", + "- Map varying structures to a common schema\n", + "- Normalize factor level names (e.g., \"D-glucose\", \"dextrose\", \"glu\" all become \"glucose\")\n", + "- Enable cross-dataset queries with standardized field names and values\n", + "\n", + "In this tutorial, we'll explore how to use VirtualDB to query and compare data across multiple datasets." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1. Introduction: Why VirtualDB?\n", + "\n", + "### The Problem\n", + "\n", + "When working with multiple genomic datasets, you'll encounter:\n", + "\n", + "1. **Inconsistent terminology**: One dataset uses \"D-glucose\", another uses \"glucose\", yet another uses \"glu\"\n", + "2. **Different hierarchies**: Conditions stored at repo-level vs config-level vs field-level\n", + "3. **Varying structures**: Different nesting patterns for the same logical information\n", + "\n", + "### The Solution\n", + "\n", + "VirtualDB abstracts these differences:\n", + "\n", + "- **Common schema**: Query all datasets using standardized field names\n", + "- **Automatic normalization**: \"D-glucose\" and \"glucose\" both match `carbon_source=\"glucose\"`\n", + "- **Unified interface**: Single query retrieves data from multiple datasets" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2. Basic Setup\n", + "\n", + "VirtualDB requires a YAML configuration file that defines:\n", + "- Which datasets to include\n", + "- How to map their fields to common names\n", + "- How to normalize varying terminologies" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chase/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Configuration saved to: /tmp/tmpf7f_eml_/vdb_config.yaml\n" + ] + } + ], + "source": [ + "from tfbpapi.virtual_db import VirtualDB\n", + "import pandas as pd\n", + "\n", + "# For this tutorial, we'll create a sample configuration\n", + "# In practice, you'd load this from a YAML file\n", + "config_yaml = \"\"\"\n", + "repositories:\n", + " BrentLab/harbison_2004:\n", + " dataset:\n", + " harbison_2004:\n", + " carbon_source:\n", + " field: condition\n", + " path: media.carbon_source.compound\n", + " temperature_celsius:\n", + " field: condition\n", + " path: temperature_celsius\n", + " environmental_condition:\n", + " field: condition\n", + "\n", + " BrentLab/kemmeren_2014:\n", + " dataset:\n", + " kemmeren_2014:\n", + " carbon_source:\n", + " path: media.carbon_source.compound\n", + " temperature_celsius:\n", + " path: temperature_celsius\n", + "\n", + "factor_aliases:\n", + " carbon_source:\n", + " glucose: [D-glucose, dextrose, glu]\n", + " galactose: [D-galactose, gal]\n", + " raffinose: [D-raffinose]\n", + "\n", + "missing_value_labels:\n", + " carbon_source: \"unspecified\"\n", + "\n", + "description:\n", + " carbon_source: The carbon source provided during growth\n", + " temperature_celsius: Growth temperature in degrees Celsius\n", + " environmental_condition: Named environmental condition\n", + "\"\"\"\n", + "\n", + "# Save config to temporary file\n", + "import tempfile\n", + "from pathlib import Path\n", + "\n", + "temp_config = Path(tempfile.mkdtemp()) / \"vdb_config.yaml\"\n", + "temp_config.write_text(config_yaml)\n", + "\n", + "print(f\"Configuration saved to: {temp_config}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "ename": "ValidationError", + "evalue": "1 validation error for MetadataConfig\n Value error, Invalid configuration for repository 'repositories': 1 validation error for RepositoryConfig\n Value error, Invalid repo-wide property 'BrentLab/harbison_2004': 1 validation error for PropertyMapping\npath\n Field required [type=missing, input_value={'dataset': {'harbison_20...'field': 'condition'}}}}, input_type=dict]\n For further information visit https://errors.pydantic.dev/2.12/v/missing [type=value_error, input_value={'BrentLab/harbison_2004'...emperature_celsius'}}}}}, input_type=dict]\n For further information visit https://errors.pydantic.dev/2.12/v/value_error [type=value_error, input_value={'repositories': {'BrentL...vironmental condition'}}, input_type=dict]\n For further information visit https://errors.pydantic.dev/2.12/v/value_error", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mValidationError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[2]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;66;03m# Initialize VirtualDB with the configuration\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m vdb = \u001b[43mVirtualDB\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mstr\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mtemp_config\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 4\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33mVirtualDB initialized successfully!\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 5\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mConfigured repositories: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mlen\u001b[39m(vdb.config.repositories)\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/code/tfbp/tfbpapi/tfbpapi/virtual_db.py:191\u001b[39m, in \u001b[36mVirtualDB.__init__\u001b[39m\u001b[34m(self, config_path, token)\u001b[39m\n\u001b[32m 181\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__init__\u001b[39m(\u001b[38;5;28mself\u001b[39m, config_path: Path | \u001b[38;5;28mstr\u001b[39m, token: \u001b[38;5;28mstr\u001b[39m | \u001b[38;5;28;01mNone\u001b[39;00m = \u001b[38;5;28;01mNone\u001b[39;00m):\n\u001b[32m 182\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m 183\u001b[39m \u001b[33;03m Initialize VirtualDB with configuration and optional auth token.\u001b[39;00m\n\u001b[32m 184\u001b[39m \n\u001b[32m (...)\u001b[39m\u001b[32m 189\u001b[39m \n\u001b[32m 190\u001b[39m \u001b[33;03m \"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m191\u001b[39m \u001b[38;5;28mself\u001b[39m.config = \u001b[43mMetadataConfig\u001b[49m\u001b[43m.\u001b[49m\u001b[43mfrom_yaml\u001b[49m\u001b[43m(\u001b[49m\u001b[43mconfig_path\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 192\u001b[39m \u001b[38;5;28mself\u001b[39m.token = token\n\u001b[32m 193\u001b[39m \u001b[38;5;28mself\u001b[39m.cache: \u001b[38;5;28mdict\u001b[39m[\u001b[38;5;28mtuple\u001b[39m[\u001b[38;5;28mstr\u001b[39m, \u001b[38;5;28mstr\u001b[39m], pd.DataFrame] = {}\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/code/tfbp/tfbpapi/tfbpapi/models.py:531\u001b[39m, in \u001b[36mMetadataConfig.from_yaml\u001b[39m\u001b[34m(cls, path)\u001b[39m\n\u001b[32m 528\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(data, \u001b[38;5;28mdict\u001b[39m):\n\u001b[32m 529\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[33m\"\u001b[39m\u001b[33mConfiguration must be a YAML dict\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m--> \u001b[39m\u001b[32m531\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mcls\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mmodel_validate\u001b[49m\u001b[43m(\u001b[49m\u001b[43mdata\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/pydantic/main.py:716\u001b[39m, in \u001b[36mBaseModel.model_validate\u001b[39m\u001b[34m(cls, obj, strict, extra, from_attributes, context, by_alias, by_name)\u001b[39m\n\u001b[32m 710\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m by_alias \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mFalse\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m by_name \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mTrue\u001b[39;00m:\n\u001b[32m 711\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m PydanticUserError(\n\u001b[32m 712\u001b[39m \u001b[33m'\u001b[39m\u001b[33mAt least one of `by_alias` or `by_name` must be set to True.\u001b[39m\u001b[33m'\u001b[39m,\n\u001b[32m 713\u001b[39m code=\u001b[33m'\u001b[39m\u001b[33mvalidate-by-alias-and-name-false\u001b[39m\u001b[33m'\u001b[39m,\n\u001b[32m 714\u001b[39m )\n\u001b[32m--> \u001b[39m\u001b[32m716\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mcls\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m__pydantic_validator__\u001b[49m\u001b[43m.\u001b[49m\u001b[43mvalidate_python\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 717\u001b[39m \u001b[43m \u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 718\u001b[39m \u001b[43m \u001b[49m\u001b[43mstrict\u001b[49m\u001b[43m=\u001b[49m\u001b[43mstrict\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 719\u001b[39m \u001b[43m \u001b[49m\u001b[43mextra\u001b[49m\u001b[43m=\u001b[49m\u001b[43mextra\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 720\u001b[39m \u001b[43m \u001b[49m\u001b[43mfrom_attributes\u001b[49m\u001b[43m=\u001b[49m\u001b[43mfrom_attributes\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 721\u001b[39m \u001b[43m \u001b[49m\u001b[43mcontext\u001b[49m\u001b[43m=\u001b[49m\u001b[43mcontext\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 722\u001b[39m \u001b[43m \u001b[49m\u001b[43mby_alias\u001b[49m\u001b[43m=\u001b[49m\u001b[43mby_alias\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 723\u001b[39m \u001b[43m \u001b[49m\u001b[43mby_name\u001b[49m\u001b[43m=\u001b[49m\u001b[43mby_name\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 724\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[31mValidationError\u001b[39m: 1 validation error for MetadataConfig\n Value error, Invalid configuration for repository 'repositories': 1 validation error for RepositoryConfig\n Value error, Invalid repo-wide property 'BrentLab/harbison_2004': 1 validation error for PropertyMapping\npath\n Field required [type=missing, input_value={'dataset': {'harbison_20...'field': 'condition'}}}}, input_type=dict]\n For further information visit https://errors.pydantic.dev/2.12/v/missing [type=value_error, input_value={'BrentLab/harbison_2004'...emperature_celsius'}}}}}, input_type=dict]\n For further information visit https://errors.pydantic.dev/2.12/v/value_error [type=value_error, input_value={'repositories': {'BrentL...vironmental condition'}}, input_type=dict]\n For further information visit https://errors.pydantic.dev/2.12/v/value_error" + ] + } + ], + "source": [ + "# Initialize VirtualDB with the configuration\n", + "vdb = VirtualDB(str(temp_config))\n", + "\n", + "print(\"VirtualDB initialized successfully!\")\n", + "print(f\"Configured repositories: {len(vdb.config.repositories)}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3. Schema Discovery\n", + "\n", + "Before querying, let's explore what fields are available." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Get all fields defined in any dataset\n", + "all_fields = vdb.get_fields()\n", + "\n", + "print(\"All available fields:\")\n", + "for field in sorted(all_fields):\n", + " print(f\" - {field}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Get fields present in ALL datasets (common fields)\n", + "common_fields = vdb.get_common_fields()\n", + "\n", + "print(\"Common fields (present in all datasets):\")\n", + "for field in sorted(common_fields):\n", + " print(f\" - {field}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Get fields for a specific dataset\n", + "harbison_fields = vdb.get_fields(\"BrentLab/harbison_2004\", \"harbison_2004\")\n", + "\n", + "print(\"Fields in harbison_2004:\")\n", + "for field in sorted(harbison_fields):\n", + " print(f\" - {field}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Discovering Valid Values\n", + "\n", + "VirtualDB can tell you what values exist for each field." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Get all unique values for a field (normalized)\n", + "carbon_sources = vdb.get_unique_values(\"carbon_source\")\n", + "\n", + "print(\"Unique carbon sources (normalized):\")\n", + "for source in sorted(carbon_sources):\n", + " print(f\" - {source}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Get values broken down by dataset\n", + "carbon_by_dataset = vdb.get_unique_values(\"carbon_source\", by_dataset=True)\n", + "\n", + "print(\"Carbon sources by dataset:\")\n", + "for dataset, sources in carbon_by_dataset.items():\n", + " print(f\"\\n{dataset}:\")\n", + " for source in sorted(sources):\n", + " print(f\" - {source}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4. Simple Queries\n", + "\n", + "Now let's start querying data. The `query()` method is the primary interface." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Basic Query: All Samples with Glucose\n", + "\n", + "By default, queries return sample-level data (one row per sample) with all configured fields." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Query all datasets for samples grown on glucose\n", + "glucose_samples = vdb.query(filters={\"carbon_source\": \"glucose\"})\n", + "\n", + "print(f\"Found {len(glucose_samples)} samples with glucose\")\n", + "print(f\"\\nColumns: {list(glucose_samples.columns)}\")\n", + "print(f\"\\nFirst few rows:\")\n", + "glucose_samples.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Query Specific Datasets\n", + "\n", + "Limit your query to specific datasets using the `datasets` parameter." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Query only harbison_2004\n", + "harbison_glucose = vdb.query(\n", + " filters={\"carbon_source\": \"glucose\"},\n", + " datasets=[(\"BrentLab/harbison_2004\", \"harbison_2004\")]\n", + ")\n", + "\n", + "print(f\"Found {len(harbison_glucose)} samples from harbison_2004\")\n", + "harbison_glucose.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Select Specific Fields\n", + "\n", + "Return only the fields you need with the `fields` parameter." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Get just sample_id, carbon_source, and temperature\n", + "minimal_data = vdb.query(\n", + " filters={\"carbon_source\": \"glucose\"},\n", + " fields=[\"sample_id\", \"carbon_source\", \"temperature_celsius\"]\n", + ")\n", + "\n", + "print(f\"Columns: {list(minimal_data.columns)}\")\n", + "minimal_data.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 5. Advanced Queries\n", + "\n", + "VirtualDB supports more sophisticated query patterns." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Multiple Filter Conditions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Samples with glucose at 30C\n", + "glucose_30c = vdb.query(\n", + " filters={\n", + " \"carbon_source\": \"glucose\",\n", + " \"temperature_celsius\": 30\n", + " }\n", + ")\n", + "\n", + "print(f\"Found {len(glucose_30c)} samples with glucose at 30C\")\n", + "glucose_30c.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Numeric Range Queries" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Samples at temperature >= 30C\n", + "warm_samples = vdb.query(\n", + " filters={\"temperature_celsius\": (\">=\", 30)}\n", + ")\n", + "\n", + "print(f\"Found {len(warm_samples)} samples at >= 30C\")\n", + "\n", + "# Samples between 28C and 32C\n", + "moderate_temp = vdb.query(\n", + " filters={\"temperature_celsius\": (\"between\", 28, 32)}\n", + ")\n", + "\n", + "print(f\"Found {len(moderate_temp)} samples between 28-32C\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Factor Alias Expansion\n", + "\n", + "When you query for a normalized value, VirtualDB automatically expands to all original aliases." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Query for \"galactose\" matches \"D-galactose\", \"gal\", and \"galactose\"\n", + "galactose_samples = vdb.query(filters={\"carbon_source\": \"galactose\"})\n", + "\n", + "print(f\"Found {len(galactose_samples)} galactose samples\")\n", + "print(\"\\nThis query internally expanded to:\")\n", + "print(\" WHERE carbon_source IN ('D-galactose', 'gal', 'galactose')\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Complete Data Retrieval\n", + "\n", + "By default, `query()` returns sample-level metadata (one row per sample). \n", + "Set `complete=True` to get all measurements (many rows per sample)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Get complete data with measurements\n", + "complete_data = vdb.query(\n", + " filters={\"carbon_source\": \"glucose\"},\n", + " datasets=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", + " complete=True\n", + ")\n", + "\n", + "print(f\"Complete data: {len(complete_data)} rows\")\n", + "print(f\"Columns: {list(complete_data.columns)}\")\n", + "print(\"\\nFirst few measurements:\")\n", + "complete_data.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# You can combine complete=True with field selection\n", + "# Get just the binding data columns\n", + "binding_data = vdb.query(\n", + " filters={\"carbon_source\": \"glucose\"},\n", + " datasets=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", + " fields=[\"sample_id\", \"regulator_symbol\", \"target_symbol\", \"effect\", \"pvalue\"],\n", + " complete=True\n", + ")\n", + "\n", + "print(f\"Binding data: {len(binding_data)} measurements\")\n", + "binding_data.head(10)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 6. Understanding the Configuration\n", + "\n", + "Let's examine the configuration structure in more detail." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Property Mapping Levels\n", + "\n", + "Properties can be extracted at three hierarchy levels:\n", + "\n", + "1. **Repository-wide**: Common to all datasets\n", + "```yaml\n", + "repositories:\n", + " BrentLab/some_repo:\n", + " nitrogen_source: # Applies to all datasets in this repo\n", + " path: media.nitrogen_source.name\n", + "```\n", + "\n", + "2. **Dataset-specific**: Specific to one config\n", + "```yaml\n", + " dataset:\n", + " config_name:\n", + " phosphate_source: # Only for this dataset\n", + " path: media.phosphate_source.compound\n", + "```\n", + "\n", + "3. **Field-level**: Varies per sample\n", + "```yaml\n", + " carbon_source:\n", + " field: condition # Extract from 'condition' field\n", + " path: media.carbon_source.compound\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Field Aliases (Column Renaming)\n", + "\n", + "When you specify just a `field` without a `path`, it creates a column alias:\n", + "\n", + "```yaml\n", + "environmental_condition:\n", + " field: condition # Renames 'condition' to 'environmental_condition'\n", + "```\n", + "\n", + "This is useful for:\n", + "- Standardizing field names across datasets\n", + "- Enabling normalization of the field values" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Factor Aliases (Value Normalization)\n", + "\n", + "The `factor_aliases` section maps varying terminologies to standard values:\n", + "\n", + "```yaml\n", + "factor_aliases:\n", + " carbon_source:\n", + " glucose: [D-glucose, dextrose, glu]\n", + " galactose: [D-galactose, gal]\n", + "```\n", + "\n", + "This means:\n", + "- Querying for `\"glucose\"` matches `\"D-glucose\"`, `\"dextrose\"`, and `\"glu\"`\n", + "- All returned values are normalized to the canonical form (`\"glucose\"`)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 7. Real-World Example: Cross-Dataset Comparison\n", + "\n", + "Let's do a practical analysis comparing experiments across datasets." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Compare number of samples by carbon source across datasets\n", + "\n", + "# Get all samples\n", + "all_samples = vdb.query()\n", + "\n", + "# Count by dataset and carbon source\n", + "summary = all_samples.groupby(['_repo_id', '_config_name', 'carbon_source']).size()\n", + "summary = summary.reset_index(name='num_samples')\n", + "\n", + "print(\"Sample counts by dataset and carbon source:\")\n", + "print(summary.to_string(index=False))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Compare glucose experiments at different temperatures\n", + "\n", + "glucose_by_temp = vdb.query(\n", + " filters={\"carbon_source\": \"glucose\"},\n", + " fields=[\"sample_id\", \"temperature_celsius\", \"environmental_condition\"]\n", + ")\n", + "\n", + "# Count samples by temperature\n", + "temp_counts = glucose_by_temp['temperature_celsius'].value_counts().sort_index()\n", + "\n", + "print(\"Glucose samples by temperature:\")\n", + "for temp, count in temp_counts.items():\n", + " print(f\" {temp}C: {count} samples\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Get binding data for a specific regulator across datasets\n", + "\n", + "# Query for FHL1 binding in glucose conditions\n", + "fhl1_binding = vdb.query(\n", + " filters={\n", + " \"carbon_source\": \"glucose\",\n", + " \"regulator_symbol\": \"FHL1\"\n", + " },\n", + " fields=[\"sample_id\", \"regulator_symbol\", \"target_symbol\", \"effect\", \"pvalue\"],\n", + " complete=True\n", + ")\n", + "\n", + "print(f\"Found {len(fhl1_binding)} FHL1 binding measurements in glucose\")\n", + "\n", + "# Find significant targets (p < 0.001)\n", + "significant = fhl1_binding[fhl1_binding['pvalue'] < 0.001]\n", + "print(f\"Significant targets: {len(significant)}\")\n", + "\n", + "# Top 10 by effect size\n", + "top_targets = significant.nlargest(10, 'effect')[['target_symbol', 'effect', 'pvalue']]\n", + "print(\"\\nTop 10 targets by effect size:\")\n", + "print(top_targets.to_string(index=False))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 8. Best Practices\n", + "\n", + "### 1. Use Field Selection\n", + "\n", + "Only request the fields you need - it's more efficient:\n", + "\n", + "```python\n", + "# Good: Only get what you need\n", + "df = vdb.query(\n", + " filters={\"carbon_source\": \"glucose\"},\n", + " fields=[\"sample_id\", \"temperature_celsius\"]\n", + ")\n", + "\n", + "# Less efficient: Get all fields\n", + "df = vdb.query(filters={\"carbon_source\": \"glucose\"})\n", + "```\n", + "\n", + "### 2. Filter Before Complete Data\n", + "\n", + "Use filters to reduce the dataset before getting complete data:\n", + "\n", + "```python\n", + "# Good: Filter first\n", + "df = vdb.query(\n", + " filters={\"carbon_source\": \"glucose\", \"temperature_celsius\": 30},\n", + " complete=True\n", + ")\n", + "\n", + "# Less efficient: Get all data then filter in pandas\n", + "df = vdb.query(complete=True)\n", + "df = df[(df['carbon_source'] == 'glucose') & (df['temperature_celsius'] == 30)]\n", + "```\n", + "\n", + "### 3. Use Normalized Values\n", + "\n", + "Always query using the normalized (canonical) values from your config:\n", + "\n", + "```python\n", + "# Good: Use canonical value\n", + "df = vdb.query(filters={\"carbon_source\": \"glucose\"})\n", + "\n", + "# Might miss data: Using original value\n", + "df = vdb.query(filters={\"carbon_source\": \"D-glucose\"}) # Only matches exact string\n", + "```\n", + "\n", + "### 4. Check Available Values First\n", + "\n", + "Use `get_unique_values()` to discover what values exist:\n", + "\n", + "```python\n", + "# See what carbon sources are available\n", + "sources = vdb.get_unique_values(\"carbon_source\")\n", + "print(sources)\n", + "\n", + "# Then query for specific ones\n", + "df = vdb.query(filters={\"carbon_source\": \"galactose\"})\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "VirtualDB provides a powerful abstraction for querying heterogeneous genomic datasets:\n", + "\n", + "1. **Schema Discovery**: Explore available fields and values with `get_fields()` and `get_unique_values()`\n", + "2. **Unified Queries**: Query across datasets using standardized field names\n", + "3. **Automatic Normalization**: Varying terminologies automatically mapped to common values\n", + "4. **Flexible Filtering**: Support for exact matches, ranges, and complex conditions\n", + "5. **Sample or Complete Data**: Choose between metadata-only or full measurements\n", + "\n", + "For more details on the configuration format and design principles, see the [Virtual Database Design](../virtual_database_concepts.md) documentation." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "tfbpapi-py3.11", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/virtual_database_concepts.md b/docs/virtual_database_concepts.md new file mode 100644 index 0000000..093502a --- /dev/null +++ b/docs/virtual_database_concepts.md @@ -0,0 +1,295 @@ +# Virtual Database + +VirtualDB provides a unified query interface across heterogeneous datasets with +different experimental condition structures and terminologies. Each dataset +defines experimental conditions in its own way, with properties stored at +different hierarchy levels (repository, dataset, or field) and using different +naming conventions. VirtualDB uses an external YAML configuration to map these +varying structures to a common schema, normalize factor level names (e.g., +"D-glucose", "dextrose", "glu" all become "glucose"), and enable cross-dataset +queries with standardized field names and values. + +## Configuration Structure + +A configuration file defines the virtual database schema with four sections: + +```yaml +# ===== Repository Configurations ===== +repositories: + # Each repository defines a "table" in the virtual database + BrentLab/harbison_2004: + # Repository-wide properties (apply to all datasets in this repository) + nitrogen_source: + path: media.nitrogen_source.name + + dataset: + # Each dataset gets its own view with standardized fields + harbison_2004: + # Dataset-specific properties (constant for all samples) + phosphate_source: + path: media.phosphate_source.compound + + # Field-level properties (vary per sample) + carbon_source: + field: condition + path: media.carbon_source.compound + + # Field without path (column alias with normalization) + environmental_condition: + field: condition + + BrentLab/kemmeren_2014: + dataset: + kemmeren_2014: + # Same logical fields, different physical paths + carbon_source: + path: media.carbon_source.compound + temperature_celsius: + path: temperature_celsius + +# ===== Normalization Rules ===== +# Map varying terminologies to standardized values +factor_aliases: + carbon_source: + glucose: [D-glucose, glu, dextrose] + galactose: [D-galactose, gal] + +# Handle missing values with defaults +missing_value_labels: + carbon_source: "unspecified" + +# ===== Documentation ===== +description: + carbon_source: The carbon source provided to the cells during growth +``` + +### Property Hierarchy + +Properties are extracted at three hierarchy levels: + +1. **Repository-wide**: Common to all datasets in a repository + - Paths relative to repository-level `experimental_conditions` + - Example: `path: media.nitrogen_source.name` + +2. **Dataset-specific**: Specific to one dataset configuration + - Paths relative to config-level `experimental_conditions` + - Example: `path: media.phosphate_source.compound` + +3. **Field-level**: Vary per sample, defined in field definitions + - `field` specifies which field to extract from + - `path` relative to field definitions (not `experimental_conditions`) + - Example: `field: condition, path: media.carbon_source.compound` + +**Special case**: Field without path creates a column alias +- `field: condition` (no path) to renames `condition` column, enables normalization + +### Path Resolution + +Paths use dot notation to navigate nested structures: + +**Repository/Dataset-level** (automatically prepends `experimental_conditions.`): +- `path: temperature_celsius` to `experimental_conditions.temperature_celsius` +- `path: media.carbon_source.compound` to + `experimental_conditions.media.carbon_source.compound` + +**Field-level** (paths relative to field definitions): +- `field: condition, path: media.carbon_source.compound` to looks in field +`condition`'s definitions to navigates to `media.carbon_source.compound` + +## VirtualDB Structure + +VirtualDB maintains a collection of dataset-specific metadata tables, one per +configured dataset. Each table has the same structure (standardized schema) but +contains data specific to that dataset. + +### Internal Structure + +When materialized (or conceptually if not materialized), VirtualDB contains: + +```python +{ + ("BrentLab/harbison_2004", "harbison_2004"): DataFrame( + # Columns: sample_id, carbon_source, temperature_celsius, nitrogen_source, ... + # Values: Normalized according to factor_aliases + # Example rows: + # sample_id carbon_source temperature_celsius nitrogen_source + # harbison_001 glucose 30 yeast nitrogen base + # harbison_002 galactose 30 yeast nitrogen base + ), + + ("BrentLab/kemmeren_2014", "kemmeren_2014"): DataFrame( + # Columns: sample_id, carbon_source, temperature_celsius, ... + # Note: Different physical source paths, same logical schema + # Example rows: + # sample_id carbon_source temperature_celsius + # kemmeren_001 glucose 30 + # kemmeren_002 raffinose 30 + ) +} +``` + +### View Materialization + +By default, VirtualDB computes views on-demand. For performance, views can be +materialized (cached): + +```python +# Cache all views for faster subsequent queries +vdb.materialize_views() + +# Cache specific datasets +vdb.materialize([("BrentLab/harbison_2004", "harbison_2004")]) + +# Invalidate cache (e.g., after data updates) +vdb.invalidate_cache() +vdb.invalidate_cache([("BrentLab/harbison_2004", "harbison_2004")]) +``` + +Materialized views are stored locally and reused for queries until invalidated. + +## VirtualDB Interface + +### Schema Discovery + +**List all queryable fields**: +```python +from tfbpapi.virtual_db import VirtualDB + +vdb = VirtualDB("config.yaml") + +# All fields defined in any dataset +fields = vdb.get_fields() +# ["carbon_source", "temperature_celsius", "nitrogen_source", "phosphate_source", ...] + +# Fields present in ALL datasets (common fields) +common = vdb.get_common_fields() +# ["carbon_source", "temperature_celsius"] + +# Fields for specific dataset +dataset_fields = vdb.get_fields("BrentLab/harbison_2004", "harbison_2004") +# ["carbon_source", "temperature_celsius", "nitrogen_source", "phosphate_source"] +``` + +**Discover valid values for fields**: +```python +# Unique values across all datasets (normalized) +values = vdb.get_unique_values("carbon_source") +# ["glucose", "galactose", "raffinose", "unspecified"] + +# Values broken down by dataset +values_by_dataset = vdb.get_unique_values("carbon_source", by_dataset=True) +# { +# "BrentLab/harbison_2004": ["glucose", "galactose"], +# "BrentLab/kemmeren_2014": ["glucose", "raffinose"] +# } +``` + +### Querying Data + +The `query()` method is the primary interface for retrieving data from VirtualDB. + +**Basic usage** (sample-level, all fields): +```python +# Query across all configured datasets +# Returns one row per sample with all configured fields +df = vdb.query(filters={"carbon_source": "glucose"}) +# DataFrame: sample_id, carbon_source, temperature_celsius, nitrogen_source, ... +``` + +**Query specific datasets**: +```python +# Limit query to specific datasets +df = vdb.query( + filters={"carbon_source": "glucose", "temperature_celsius": 30}, + datasets=[("BrentLab/harbison_2004", "harbison_2004")] +) +``` + +**Select specific fields**: +```python +# Return only specified fields +df = vdb.query( + filters={"carbon_source": "glucose"}, + fields=["sample_id", "carbon_source", "temperature_celsius"] +) +# DataFrame: sample_id, carbon_source, temperature_celsius +``` + +**Complete data** (measurement-level): +```python +# Set complete=True to get all measurements, not just sample-level +# Returns many rows per sample (one per target/feature/coordinate) +df = vdb.query( + filters={"carbon_source": "glucose"}, + complete=True +) +# DataFrame: sample_id, target, value, carbon_source, temperature_celsius, ... +# For annotated_features: target-level data for all matching samples +# For genome_map: coordinate-level data for all matching samples + +# Can combine with field selection +df = vdb.query( + filters={"carbon_source": "glucose"}, + fields=["sample_id", "target", "effect"], + complete=True +) +# DataFrame: sample_id, target, effect +``` + +### Factor Alias Expansion + +When querying with aliased values, VirtualDB automatically expands to all +original values: + +```python +# User queries for normalized value +df = vdb.query(filters={"carbon_source": "galactose"}) + +# Internally expands to all aliases +# WHERE carbon_source IN ('D-galactose', 'gal', 'galactose') +``` + +This ensures all samples are retrieved regardless of original terminology. + +### Numeric Field Filtering + +Numeric fields support exact matching and range queries: + +```python +# Exact match +df = vdb.query(filters={"temperature_celsius": 30}) + +# Range queries +df = vdb.query(filters={"temperature_celsius": (">=", 28)}) +df = vdb.query(filters={"temperature_celsius": ("between", 28, 32)}) + +# Missing value labels (from missing_value_labels config) +df = vdb.query(filters={"temperature_celsius": "room"}) +# Matches samples where temperature is None/missing +``` + +## Design Principles + +### Virtual Views by Default + +Views are computed on-demand unless explicitly materialized, but optionally cached, +This provides: + +- Reduces setup time when a new instance instantiates MetadataBuilder/VirtualDB +- No storage overhead for casual queries + +### External Configuration as Schema + +The YAML configuration serves as a **database schema definition**: + +- Defines what fields exist (logical schema) +- Maps to physical data structures (via paths) +- Specifies normalization rules (via aliases) +- Documents field semantics (via descriptions) + +This separation enables: + +- Schema updates without code changes + +See: +- [DataCard Documentation](huggingface_datacard.md) diff --git a/docs/virtual_db.md b/docs/virtual_db.md new file mode 100644 index 0000000..bec3ab4 --- /dev/null +++ b/docs/virtual_db.md @@ -0,0 +1,16 @@ +# VirtualDB + +::: tfbpapi.virtual_db.VirtualDB + options: + show_root_heading: true + show_source: true + +## Helper Functions + +::: tfbpapi.virtual_db.get_nested_value + options: + show_root_heading: true + +::: tfbpapi.virtual_db.normalize_value + options: + show_root_heading: true diff --git a/example_filter_config.yaml b/example_filter_config.yaml deleted file mode 100644 index a2c66ca..0000000 --- a/example_filter_config.yaml +++ /dev/null @@ -1,75 +0,0 @@ -# Example filter configuration for DatasetFilterResolver -# -# This file demonstrates how to specify filters and dataset-specific -# property mappings for heterogeneous datasets. -# -# New format: repositories as top-level keys (not nested under dataset_mappings) -# -# Usage: -# from tfbpapi.datainfo.filter_resolver import DatasetFilterResolver -# resolver = DatasetFilterResolver("example_filter_config.yaml") -# results = resolver.resolve_filters([("BrentLab/harbison_2004", "harbison_2004")]) - -# Filters specify acceptable values for each property -# Multiple values = OR logic (match any) -# Multiple properties = AND logic (match all) -filters: - carbon_source: - - "D-glucose" - - "D-galactose" - - temperature_celsius: - - 30 - - 37 - -# Repository configurations specify where to find each property -# Top-level keys are repository IDs (e.g., "BrentLab/harbison_2004") - -# FIELD-LEVEL EXAMPLE: Harbison 2004 -# Properties are in field definitions (condition field) -BrentLab/harbison_2004: - dataset: - harbison_2004: - carbon_source: - field: condition # Look in the 'condition' field's definitions - path: media.carbon_source - temperature_celsius: - field: condition - path: temperature_celsius - -# REPO-WIDE + DATASET-SPECIFIC EXAMPLE: Kemmeren 2014 -# temperature_celsius is repo-wide (applies to all datasets) -# carbon_source is dataset-specific -BrentLab/kemmeren_2014: - # Repo-wide property (applies to all datasets in this repository) - temperature_celsius: - path: temperature_celsius - - # Dataset-specific properties - dataset: - kemmeren_2014: - carbon_source: - path: media.carbon_source - -# CONFIGURATION STRUCTURE EXPLAINED: -# -# Repo-wide properties: -# - Properties directly under repository ID -# - Apply to ALL datasets in that repository -# - Path is relative to experimental_conditions -# - Example: temperature_celsius: path: temperature_celsius -# -> looks in experimental_conditions.temperature_celsius -# -# Dataset-specific properties: -# - Properties under dataset.{dataset_name} -# - Override repo-wide for that specific dataset -# - Can use 'field' to specify field-level definitions -# - Example with field: carbon_source: field: condition, path: media.carbon_source -# -> looks in condition field definitions -# - Example without field: carbon_source: path: media.carbon_source -# -> looks in experimental_conditions.media.carbon_source -# -# Property name aliasing: -# - The filter property name (e.g., "temperature") can differ from the datacard property name -# - Example: temperature: path: temperature_celsius -# Filter uses "temperature", datacard has "temperature_celsius" diff --git a/mkdocs.yml b/mkdocs.yml index a751e9f..0635060 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -149,20 +149,23 @@ extra_css: nav: - Home: index.md - Tutorials: - - "DataCard: Exploring Datasets": tutorials/datacard_tutorial.ipynb - - "Cache Management": tutorials/cache_manager_tutorial.ipynb - - "Querying the Datasets": tutorials/hfqueryapi_tutorial.ipynb - - "Rank Response Analysis": tutorials/rank_response_tutorial.ipynb + - "Getting Started": + - "DataCard: Exploring Datasets": tutorials/datacard_tutorial.ipynb + - "Cache Management": tutorials/cache_manager_tutorial.ipynb + - "Querying Data": + - "VirtualDB: Unified Cross-Dataset Queries": tutorials/virtual_db_tutorial.ipynb + - Concepts: + - "Virtual Database Design": virtual_database_concepts.md - API Reference: - - Core Components: - - HfQueryAPI: HfQueryAPI.md - - HfRankResponse: HfRankResponse.md - - IncrementalAnalysisDB: IncrementalAnalysisDB.md - - Cache Management: - - HfCacheManager: HfCacheManager.md - - Dataset Information: - - DataInfo Package: datainfo.md + - Core: + - VirtualDB: virtual_db.md + - DataCard: datacard.md + - HfCacheManager: hf_cache_manager.md + - Models and Configuration: + - Pydantic Models: models.md + - Fetchers: fetchers.md - Error Handling: - Custom Exceptions: errors.md - HuggingFace Configuration: - HuggingFace Dataset Card Format: huggingface_datacard.md + - BrentLab Collection: brentlab_yeastresources_collection.md diff --git a/tfbpapi/HfQueryAPI.py b/tfbpapi/HfQueryAPI.py deleted file mode 100644 index 02ae684..0000000 --- a/tfbpapi/HfQueryAPI.py +++ /dev/null @@ -1,739 +0,0 @@ -import logging -import re -from pathlib import Path -from typing import Literal - -import duckdb -import pandas as pd - -from .constants import CACHE_DIR, SQL_FILTER_KEYWORDS -from .errors import InvalidFilterFieldError -from .HfCacheManager import HfCacheManager - - -class HfQueryAPI(HfCacheManager): - """Minimal Hugging Face API client focused on metadata retrieval.""" - - def __init__( - self, - repo_id: str, - repo_type: Literal["model", "dataset", "space"] = "dataset", - token: str | None = None, - cache_dir: str | Path | None = None, - duckdb_conn: duckdb.DuckDBPyConnection = duckdb.connect(":memory:"), - ): - """ - Initialize the minimal HF Query API client. - - :param repo_id: Repository identifier (e.g., "user/dataset") - :param repo_type: Type of repository ("dataset", "model", "space") - :param token: HuggingFace token for authentication - :param cache_dir: HF cache_dir for downloads - - """ - self._duckdb_conn = duckdb_conn - - # Initialize parent with minimal setup - super().__init__( - repo_id=repo_id, - duckdb_conn=self._duckdb_conn, - token=token, - logger=logging.getLogger(self.__class__.__name__), - ) - - # Store basic configuration - self.repo_type = repo_type - self.cache_dir = Path(cache_dir) if cache_dir is not None else CACHE_DIR - - # Filter storage system - # dict structure: - # {config_name: "SQL WHERE clause", ...} - self._table_filters: dict[str, str] = {} - - @property - def cache_dir(self) -> Path: - return self._cache_dir - - @cache_dir.setter - def cache_dir(self, value: str | Path) -> None: - """Set the cache directory for huggingface_hub downloads.""" - path = Path(value) - if not path.exists(): - raise FileNotFoundError(f"Cache directory {path} does not exist") - self._cache_dir = path - - def _get_explicit_metadata(self, config, table_name: str) -> pd.DataFrame: - """Helper function to handle explicit metadata configurations.""" - sql = f"SELECT * FROM {table_name}" - return self.duckdb_conn.execute(sql).fetchdf() - - def _get_embedded_metadata(self, config, table_name: str) -> pd.DataFrame: - """Helper function to handle embedded metadata configurations.""" - if config.metadata_fields is None: - raise ValueError(f"Config {config.config_name} has no metadata fields") - fields = ", ".join(config.metadata_fields) - where_clauses = " AND ".join( - [f"{field} IS NOT NULL" for field in config.metadata_fields] - ) - sql = f""" - SELECT DISTINCT {fields}, COUNT(*) as count - FROM {table_name} - WHERE {where_clauses} - GROUP BY {fields} - ORDER BY count DESC - """ - return self.duckdb_conn.execute(sql).fetchdf() - - def _validate_metadata_fields( - self, config_name: str, field_names: list[str] - ) -> None: - """ - Validate that field names exist in the config's metadata columns. - - :param config_name: Configuration name to validate against - :param field_names: List of field names to validate - :raises InvalidFilterFieldError: If any fields don't exist in metadata - - """ - if not field_names: - return - - try: - metadata_df = self.get_metadata(config_name) - if metadata_df.empty: - raise InvalidFilterFieldError( - config_name=config_name, - invalid_fields=field_names, - available_fields=[], - ) - - available_fields = list(metadata_df.columns) - invalid_fields = [ - field for field in field_names if field not in available_fields - ] - - if invalid_fields: - raise InvalidFilterFieldError( - config_name=config_name, - invalid_fields=invalid_fields, - available_fields=available_fields, - ) - except Exception as e: - if isinstance(e, InvalidFilterFieldError): - raise - # If metadata retrieval fails for other reasons, log warning but allow - self.logger.warning( - f"Could not validate filter fields for {config_name}: {e}" - ) - - def _extract_fields_from_sql(self, sql_where: str) -> list[str]: - """ - Extract potential field names from SQL WHERE clause. - - Uses a more robust approach to identify column references while avoiding string - literals used as values. - - :param sql_where: SQL WHERE clause (without 'WHERE' keyword) - :return: List of potential field names found in the SQL - - """ - if not sql_where.strip(): - return [] - - field_names = set() - - # Tokenize the SQL to better understand context - # This regex splits on key tokens while preserving them - tokens = re.findall( - r""" - \bIN\s*\([^)]+\)| # IN clauses with content - \bBETWEEN\s+\S+\s+AND\s+\S+| # BETWEEN clauses - (?:'[^']*')|(?:"[^"]*")| # Quoted strings - \b(?:AND|OR|NOT|IS|NULL|LIKE|BETWEEN|IN)\b| # SQL keywords - [=!<>]+| # Comparison operators - [(),]| # Delimiters - \b[a-zA-Z_][a-zA-Z0-9_]*\b| # Identifiers - \S+ # Other tokens - """, - sql_where, - re.VERBOSE | re.IGNORECASE, - ) - - # Track the context to determine if an identifier is a field name or value - i = 0 - while i < len(tokens): - token = tokens[i].strip() - if not token: - i += 1 - continue - - # Skip IN clauses entirely - they contain values, not field names - if re.match(r"\bIN\s*\(", token, re.IGNORECASE): - i += 1 - continue - - # Skip BETWEEN clauses entirely - they contain values, not field names - if re.match(r"\bBETWEEN\b", token, re.IGNORECASE): - i += 1 - continue - - # Handle quoted strings - could be identifiers or values depending on context - if token.startswith(("'", '"')): - # Extract the content inside quotes - quoted_content = token[1:-1] - - # Find next significant token to determine context - next_significant_token = None - for j in range(i + 1, len(tokens)): - next_token = tokens[j].strip() - if next_token and next_token not in [" ", "\n", "\t"]: - next_significant_token = next_token - break - - # Check if this quoted string is a field name based on context - is_quoted_field = False - - # Check what comes after this quoted string - if next_significant_token: - # If followed by comparison operators or SQL keywords, it's a field name - if ( - next_significant_token - in ["=", "!=", "<>", "<", ">", "<=", ">="] - or next_significant_token.upper() in ["IS", "LIKE", "NOT"] - or re.match( - r"\bBETWEEN\b", next_significant_token, re.IGNORECASE - ) - or re.match(r"\bIN\s*\(", next_significant_token, re.IGNORECASE) - ): - is_quoted_field = True - - # Also check what comes before this quoted string - if not is_quoted_field and i > 0: - # Find the previous significant token - prev_significant_token = None - for j in range(i - 1, -1, -1): - prev_token = tokens[j].strip() - if prev_token and prev_token not in [" ", "\n", "\t"]: - prev_significant_token = prev_token - break - - # If preceded by a comparison operator, could be a field name - # But we need to be very careful not to treat string literals as field names - if prev_significant_token and prev_significant_token in [ - "=", - "!=", - "<>", - "<", - ">", - "<=", - ">=", - ]: - # Only treat as field name if it looks like a database identifier - # AND doesn't look like a typical string value - if self._looks_like_identifier( - quoted_content - ) and self._looks_like_database_identifier(quoted_content): - is_quoted_field = True - - if is_quoted_field: - field_names.add(quoted_content) - - i += 1 - continue - - # Skip SQL keywords and operators - if token.upper() in SQL_FILTER_KEYWORDS or token in [ - "=", - "!=", - "<>", - "<", - ">", - "<=", - ">=", - "(", - ")", - ",", - ]: - i += 1 - continue - - # Skip numeric literals - if re.match(r"^-?\d+(\.\d+)?$", token): - i += 1 - continue - - # Check if this looks like an identifier (field name) - if re.match(r"^[a-zA-Z_][a-zA-Z0-9_]*$", token): - # Check the context - if the next non-whitespace token is a comparison operator, - # then this is likely a field name - next_significant_token = None - for j in range(i + 1, len(tokens)): - next_token = tokens[j].strip() - if next_token and next_token not in [" ", "\n", "\t"]: - next_significant_token = next_token - break - - # Check if followed by a comparison operator or SQL keyword that indicates a field - is_field = False - - if next_significant_token: - # Direct comparison operators - if next_significant_token in [ - "=", - "!=", - "<>", - "<", - ">", - "<=", - ">=", - ]: - is_field = True - # SQL keywords that follow field names - elif next_significant_token.upper() in ["IS", "LIKE", "NOT"]: - is_field = True - # BETWEEN clause (could be just 'BETWEEN' or 'BETWEEN ... AND ...') - elif next_significant_token.upper() == "BETWEEN" or re.match( - r"\bBETWEEN\b", next_significant_token, re.IGNORECASE - ): - is_field = True - # IN clause (could be just 'IN' or 'IN (...)') - elif next_significant_token.upper() == "IN" or re.match( - r"\bIN\s*\(", next_significant_token, re.IGNORECASE - ): - is_field = True - - # If not a field yet, check other contexts - if not is_field and i > 0: - # Find the previous significant token - prev_significant_token = None - for j in range(i - 1, -1, -1): - prev_token = tokens[j].strip() - if prev_token and prev_token not in [" ", "\n", "\t"]: - prev_significant_token = prev_token - break - - # Case 1: After AND/OR and before an operator (original logic) - if ( - prev_significant_token - and prev_significant_token.upper() in ["AND", "OR"] - and next_significant_token - ): - # Same checks as above - if next_significant_token in [ - "=", - "!=", - "<>", - "<", - ">", - "<=", - ">=", - ]: - is_field = True - elif next_significant_token.upper() in ["IS", "LIKE", "NOT"]: - is_field = True - elif next_significant_token.upper() == "BETWEEN" or re.match( - r"\bBETWEEN\b", next_significant_token, re.IGNORECASE - ): - is_field = True - elif next_significant_token.upper() == "IN" or re.match( - r"\bIN\s*\(", next_significant_token, re.IGNORECASE - ): - is_field = True - - # Case 2: After a comparison operator (second operand) - elif prev_significant_token and prev_significant_token in [ - "=", - "!=", - "<>", - "<", - ">", - "<=", - ">=", - ]: - # But exclude function names (identifiers followed by '(') - if next_significant_token != "(": - is_field = True - - # Case 3: After opening parenthesis (function parameter) - elif prev_significant_token == "(": - is_field = True - - if is_field: - field_names.add(token) - - i += 1 - - return list(field_names) - - def _looks_like_identifier(self, content: str) -> bool: - """ - Determine if quoted content looks like an identifier rather than a string - literal. - - :param content: The content inside quotes - :return: True if it looks like an identifier, False if it looks like a string - literal - - """ - if not content: - return False - - # Basic identifier pattern: starts with letter/underscore, contains only alphanumeric/underscore - if re.match(r"^[a-zA-Z_][a-zA-Z0-9_]*$", content): - return True - - # Extended identifier pattern: could contain spaces if it's a column name like "quoted field" - # but not if it contains many special characters or looks like natural language - if " " in content: - # If it contains spaces, it should still look identifier-like - # Allow simple cases like "quoted field" but not "this is a long string value" - words = content.split() - if len(words) <= 3 and all( - re.match(r"^[a-zA-Z_][a-zA-Z0-9_]*$", word) for word in words - ): - return True - return False - - return False - - def _looks_like_database_identifier(self, content: str) -> bool: - """ - Determine if content looks like a database identifier (field/table name). - - This is more strict than _looks_like_identifier and helps distinguish between - quoted identifiers like "field_name" and string values like "value1". - - :param content: The content to check - :return: True if it looks like a database identifier - - """ - if not content: - return False - - # Database identifiers typically: - # 1. Don't start with numbers (field names rarely start with numbers) - # 2. Often contain underscores or descriptive words - # 3. Don't look like simple values - - # Reject if starts with a number (like "value1", "123abc") - if content[0].isdigit(): - return False - - # Handle short simple values that could be literals or field names - if len(content) <= 6 and re.match(r"^[a-z]+\d*$", content.lower()): - # Allow common field name prefixes - field_prefixes = ["field", "col", "column", "attr", "prop"] - if any(content.lower().startswith(prefix) for prefix in field_prefixes): - return True # It's a valid field name like "field1", "col2" - else: - return False # It's likely a simple value like "value", "test" - - # Accept if it contains underscore (common in field names) - if "_" in content: - return True - - # Accept if it has multiple words (like "quoted field") - if " " in content: - return True - - # Accept if it's a longer descriptive name - if len(content) > 8: - return True - - # Reject otherwise (likely a simple value) - return False - - def get_metadata( - self, config_name: str, refresh_cache: bool = False - ) -> pd.DataFrame: - """ - Retrieve metadata as a DataFrame with actual metadata values for a specific - config. - - Supports three types of metadata retrieval: - 1. Direct metadata configs: config_name is itself a metadata config - 2. Embedded metadata: config_name has metadata_fields defined - 3. Applied metadata: config_name appears in another metadata config's applies_to list - - For explicit metadata configs (types 1 & 3), returns all rows from metadata table. - For embedded metadata (type 2), returns distinct combinations of metadata fields. - - :param config_name: Specific config name to retrieve metadata for - :param refresh_cache: If True, force refresh from remote instead of using cache - :return: DataFrame with metadata values for the specified config - :raises ValueError: If config_name has no associated metadata - :raises RuntimeError: If data loading fails for the config - - """ - # Get metadata relationships for this config - relationships = self.get_metadata_relationships(refresh_cache=refresh_cache) - - relevant_relationships = None - - # First priority: data_config matches (config_name is a data config with metadata) - data_config_matches = [r for r in relationships if r.data_config == config_name] - - if data_config_matches: - relevant_relationships = data_config_matches - else: - # Second priority: metadata_config matches (config_name is itself a metadata config) - metadata_config_matches = [ - r for r in relationships if r.metadata_config == config_name - ] - relevant_relationships = metadata_config_matches - - if not relevant_relationships: - # Check what configs are available for helpful error message - all_data_configs = {r.data_config for r in relationships} - all_metadata_configs = {r.metadata_config for r in relationships} - all_available = sorted(all_data_configs | all_metadata_configs) - - if not all_available: - return pd.DataFrame() - - raise ValueError( - f"Config '{config_name}' not found. " - f"Available configs with metadata: {all_available}" - ) - - # Get the config object to process - # For explicit relationships, use the metadata config - # For embedded relationships, use the data config - relationship = relevant_relationships[0] # Use first relationship found - - if relationship.relationship_type == "explicit": - # Find the metadata config - if relationship.metadata_config == config_name: - # config_name is itself a metadata config - config = self.get_config(config_name) - else: - # config_name is a data config with metadata applied to it - config = self.get_config(relationship.metadata_config) - else: # embedded - # config_name is a data config with embedded metadata - config = self.get_config(config_name) - - if not config: - raise ValueError(f"Could not find config object for '{config_name}'") - - # Process the single configuration - config_result = self._get_metadata_for_config( - config, force_refresh=refresh_cache - ) - - if not config_result.get("success", False): - raise RuntimeError(f"Failed to load data for config {config.config_name}") - - table_name = config_result.get("table_name") - if not table_name: - raise RuntimeError(f"No table name for config {config.config_name}") - - try: - if relationship.relationship_type == "explicit": - return self._get_explicit_metadata(config, table_name) - else: # embedded - return self._get_embedded_metadata(config, table_name) - except Exception as e: - self.logger.error(f"Error querying metadata for {config.config_name}: {e}") - raise - - def set_filter(self, config_name: str, **kwargs) -> None: - """ - Set simple filters using keyword arguments. - - Converts keyword arguments to SQL WHERE clause and stores - for automatic application. Validates that all filter fields - exist in the config's metadata columns. - - :param config_name: Configuration name to apply filters to - :param kwargs: Filter conditions as keyword arguments - (e.g., time=15, mechanism="ZEV") - :raises InvalidFilterFieldError: If any filter field doesn't exist - in the metadata columns - - Example: - api.set_filter("hackett_2020", time=15, mechanism="ZEV", restriction="P") - # Equivalent to: WHERE time = 15 AND mechanism = 'ZEV' AND restriction = 'P' - - """ - if not kwargs: - # If no kwargs provided, clear the filter - self.clear_filter(config_name) - return - - # Validate that all filter fields exist in metadata columns - self._validate_metadata_fields(config_name, list(kwargs.keys())) - - # Convert kwargs to SQL WHERE clause - conditions = [] - for key, value in kwargs.items(): - if isinstance(value, str): - # String values need quotes - conditions.append(f"{key} = '{value}'") - elif value is None: - # Handle NULL values - conditions.append(f"{key} IS NULL") - else: - # Numeric/boolean values - conditions.append(f"{key} = {value}") - - where_clause = " AND ".join(conditions) - self._table_filters[config_name] = where_clause - self.logger.info(f"Set filter for {config_name}: {where_clause}") - - def set_sql_filter( - self, config_name: str, sql_where: str, validate_fields: bool = True - ) -> None: - """ - Set complex filters using SQL WHERE clause. - - Stores raw SQL WHERE clause for automatic application to queries. - Validates that field references in the SQL exist in metadata columns - unless validation is disabled. - - :param config_name: Configuration name to apply filters to - :param sql_where: SQL WHERE clause (without the 'WHERE' keyword) - :param validate_fields: Whether to validate field names (default: True) - :raises InvalidFilterFieldError: If any field references don't exist - in the metadata columns (when validate_fields=True) - - Example: - api.set_sql_filter("hackett_2020", "time IN (15, 30) AND mechanism = 'ZEV'") - # To skip validation for complex SQL: - api.set_sql_filter("hackett_2020", "complex_expression(...)", validate_fields=False) - - """ - if not sql_where.strip(): - self.clear_filter(config_name) - return - - # Validate fields if requested - if validate_fields: - extracted_fields = self._extract_fields_from_sql(sql_where) - self._validate_metadata_fields(config_name, extracted_fields) - - self._table_filters[config_name] = sql_where.strip() - self.logger.info(f"Set SQL filter for {config_name}: {sql_where}") - - def clear_filter(self, config_name: str) -> None: - """ - Remove all filters for the specified configuration. - - :param config_name: Configuration name to clear filters for - - """ - if config_name in self._table_filters: - del self._table_filters[config_name] - self.logger.info(f"Cleared filter for {config_name}") - - def get_current_filter(self, config_name: str) -> str | None: - """ - Get the current filter for the specified configuration. - - :param config_name: Configuration name to get filter for - :return: Current SQL WHERE clause or None if no filter set - - """ - return self._table_filters.get(config_name) - - def query( - self, sql: str, config_name: str, refresh_cache: bool = False - ) -> pd.DataFrame: - """ - Execute SQL query with automatic filter application. - - Loads the specified configuration, applies any stored filters, - and executes the query. - - :param sql: SQL query to execute - :param config_name: Configuration name to query (table will be loaded if needed) - :param refresh_cache: If True, force refresh from remote instead of using cache - :return: DataFrame with query results - :raises ValueError: If config_name not found or query fails - - Example: - api.set_filter("hackett_2020", time=15, mechanism="ZEV") - df = api.query("SELECT regulator_locus_tag, target_locus_tag - FROM hackett_2020", "hackett_2020") - # Automatically applies: WHERE time = 15 AND mechanism = 'ZEV' - - """ - # Validate config exists - if config_name not in [c.config_name for c in self.configs]: - available_configs = [c.config_name for c in self.configs] - raise ValueError( - f"Config '{config_name}' not found. " - f"Available configs: {available_configs}" - ) - - # Load the configuration data - config = self.get_config(config_name) - if not config: - raise ValueError(f"Could not retrieve config '{config_name}'") - - config_result = self._get_metadata_for_config( - config, force_refresh=refresh_cache - ) - if not config_result.get("success", False): - raise ValueError( - f"Failed to load data for config '{config_name}': " - f"{config_result.get('message', 'Unknown error')}" - ) - - table_name = config_result.get("table_name") - if not table_name: - raise ValueError(f"No table available for config '{config_name}'") - - # Replace config name with actual table name in SQL for user convenience - sql_with_table = sql.replace(config_name, table_name) - - # Apply stored filters - final_sql = self._apply_filter_to_sql(sql_with_table, config_name) - - try: - return self.duckdb_conn.execute(final_sql).fetchdf() - except Exception as e: - self.logger.error(f"Query execution failed: {e}") - self.logger.error(f"Final SQL: {final_sql}") - raise ValueError(f"Query execution failed: {e}") from e - - def _apply_filter_to_sql(self, sql: str, config_name: str) -> str: - """ - Apply stored filters to SQL query. - - Modifies the SQL query to include stored WHERE clause filters. - - :param sql: Original SQL query - :param config_name: Configuration name to get filters for - :return: Modified SQL query with filters applied - - """ - if config_name not in self._table_filters: - return sql - - filter_clause = self._table_filters[config_name] - sql_upper = sql.upper() - - if "WHERE" in sql_upper: - # SQL already has WHERE clause, append with AND - return f"{sql} AND ({filter_clause})" - else: - # Add WHERE clause - # Find the position to insert WHERE (before ORDER BY, GROUP BY, LIMIT, etc.) - insert_keywords = ["ORDER BY", "GROUP BY", "HAVING", "LIMIT", "OFFSET"] - insert_position = len(sql) - - for keyword in insert_keywords: - pos = sql_upper.find(keyword) - if pos != -1 and pos < insert_position: - insert_position = pos - - if insert_position == len(sql): - # No special clauses, append WHERE at the end - return f"{sql} WHERE {filter_clause}" - else: - # Insert WHERE before the special clause - return ( - f"{sql[:insert_position].rstrip()} " - f"WHERE {filter_clause} {sql[insert_position:]}" - ) diff --git a/tfbpapi/HfRankResponse.py b/tfbpapi/HfRankResponse.py deleted file mode 100644 index 67a9c82..0000000 --- a/tfbpapi/HfRankResponse.py +++ /dev/null @@ -1,482 +0,0 @@ -import logging - -import duckdb -import pandas as pd - -from .IncrementalAnalysisDB import IncrementalAnalysisDB - - -class HfRankResponse: - """ - A class to provide an API to compute and analyze "rank response", which is defined - as the cumulative number of responsive targets (e.g., genes) binned by their binding - rank score for each regulator sample pair of binding and perturbation response data. - - Handles multiple dataset comparisons and stores all results in a shared database. - - """ - - def __init__(self, db: IncrementalAnalysisDB): - """ - Initialize RankResponse analyzer with database connection. - - :param db: IncrementalAnalysisDB instance for storing results - - """ - self.db = db - self.logger = logging.getLogger(self.__class__.__name__) - - def compute( - self, - ranking_api, - response_api, - ranking_table: str, - response_table: str, - ranking_score_column: str, - response_column: str, - comparison_id: str | None = None, - regulator_column: str = "regulator_locus_tag", - target_column: str = "target_locus_tag", - bin_size: int = 5, - force_recompute: bool = False, - responsive_condition: str | None = None, - ) -> pd.DataFrame: - """ - Compute rank response for a specific dataset comparison. - - :param ranking_api: API instance for ranking/binding data - :param response_api: API instance for response/perturbation data - :param ranking_table: Name of ranking table in ranking_api - :param response_table: Name of response table in response_api - :param ranking_score_column: Column name for ranking scores - :param response_column: Column name for response values - :param comparison_id: Unique identifier for this comparison (auto-generated if - None) - :param regulator_column: Column name for regulator identifiers - :param target_column: Column name for target identifiers - :param bin_size: Size of ranking bins - :param force_recompute: Whether to recompute existing results - :param responsive_condition: SQL condition to define responsive (default: IS NOT - NULL) - :return: DataFrame with computed results - - """ - # Generate comparison ID if not provided - if comparison_id is None: - comparison_id = f"{ranking_table}_vs_{response_table}" - - table_name = f"rank_response_{comparison_id}" - - # Get all regulators from ranking data - ranking_api._ensure_dataset_loaded(ranking_table) - all_regulators = ranking_api.query( - f"SELECT DISTINCT {regulator_column} FROM {ranking_table}" - )[regulator_column].tolist() - - # Check which regulators already have results - if not force_recompute and self.db.table_exists(table_name): - existing_regulators = set( - self.db.query(f"SELECT DISTINCT {regulator_column} FROM {table_name}")[ - regulator_column - ].tolist() - ) - - new_regulators = [ - reg for reg in all_regulators if reg not in existing_regulators - ] - self.logger.info( - f"Found {len(existing_regulators)} existing regulators, " - f"{len(new_regulators)} new ones" - ) - else: - new_regulators = all_regulators - self.logger.info(f"Computing analysis for {len(new_regulators)} regulators") - - if not new_regulators: - self.logger.info("No new regulators to analyze") - return self.db.query( - f"SELECT * FROM {table_name} ORDER BY " - f"{regulator_column}, {target_column}" - ) - - # Apply filters to focus on new regulators - # For analytical queries, escape and format the values safely - escaped_values = [ - f"'{reg.replace(chr(39), chr(39)+chr(39))}'" for reg in new_regulators - ] - regulator_filter = f"{regulator_column} IN ({', '.join(escaped_values)})" - - # Temporarily add filters for new regulators only - original_ranking_filter = ranking_api.get_table_filter(ranking_table) - original_response_filter = response_api.get_table_filter(response_table) - - new_ranking_filter = regulator_filter - new_response_filter = regulator_filter - - if original_ranking_filter: - new_ranking_filter = f"({original_ranking_filter}) AND ({regulator_filter})" - if original_response_filter: - new_response_filter = ( - f"({original_response_filter}) AND ({regulator_filter})" - ) - - ranking_api.set_table_filter(ranking_table, new_ranking_filter) - response_api.set_table_filter(response_table, new_response_filter) - - try: - # Load filtered data from both APIs - ranking_api._ensure_dataset_loaded(ranking_table) - response_api._ensure_dataset_loaded(response_table) - - ranking_conn = ranking_api._duckdb_conn - response_conn = response_api._duckdb_conn - - # Create temporary connection for analysis - temp_conn = duckdb.connect(":memory:") - - try: - # Get filtered data - ranking_df = ranking_conn.execute( - f"SELECT * FROM {ranking_table}" - ).fetchdf() - response_df = response_conn.execute( - f"SELECT * FROM {response_table}" - ).fetchdf() - - temp_conn.register("ranking_data", ranking_df) - temp_conn.register("response_data", response_df) - - # Execute intermediate analysis SQL - # Set default responsive condition if not provided - if responsive_condition is None: - responsive_condition = f"b.{response_column} IS NOT NULL" - else: - # Replace column references in the condition - responsive_condition = responsive_condition.replace( - response_column, f"b.{response_column}" - ) - - intermediate_sql = f""" - WITH binned_data AS ( - SELECT - a.{regulator_column}, - a.{target_column}, - a.{ranking_score_column}, - b.{response_column}, - CASE WHEN {responsive_condition} THEN 1 ELSE 0 END - AS responsive, - CEILING(ROW_NUMBER() OVER ( - PARTITION BY a.{regulator_column} - ORDER BY a.{regulator_column}, a.{target_column} - ) / {bin_size}.0) * {bin_size} AS bin_label - FROM ranking_data AS a - LEFT JOIN response_data AS b - ON a.{regulator_column} = b.{regulator_column} - AND a.{target_column} = b.{target_column} - ) - SELECT - {regulator_column}, - {target_column}, - {ranking_score_column}, - {response_column}, - responsive, - bin_label, - SUM(responsive) OVER ( - PARTITION BY {regulator_column} - ORDER BY bin_label - RANGE UNBOUNDED PRECEDING - ) AS cumulative_responsive - FROM binned_data - ORDER BY {regulator_column}, bin_label, {target_column} - """ - - new_results = temp_conn.execute(intermediate_sql).fetchdf() - - finally: - temp_conn.close() - - # Save new intermediate results - if len(new_results) > 0: - self.db.append_results( - new_results, - table_name, - analysis_type="response_rate_intermediate", - parameters={ - "ranking_table": ranking_table, - "response_table": response_table, - "ranking_score_column": ranking_score_column, - "response_column": response_column, - "bin_size": bin_size, - "result_type": "intermediate", - }, - description=( - f"Added intermediate data for {len(new_regulators)} " - "new regulators" - ), - deduplicate_on=[regulator_column, target_column], - ) - - self.logger.info( - f"Saved {len(new_results)} intermediate records to database" - ) - - # Return complete results from database - return self.db.query( - f"SELECT * FROM {table_name} ORDER BY {regulator_column}, bin_label, " - f"{target_column}" - ) - - finally: - # Restore original filters - if original_ranking_filter: - ranking_api.set_table_filter(ranking_table, original_ranking_filter) - else: - ranking_api.remove_table_filter(ranking_table) - - if original_response_filter: - response_api.set_table_filter(response_table, original_response_filter) - else: - response_api.remove_table_filter(response_table) - - def get_comparisons(self) -> list[str]: - """ - Get list of all computed comparisons. - - :return: List of comparison identifiers - - """ - tables = self.db.list_tables() - rank_response_tables = [ - table - for table in tables - if table.startswith("rank_response_") and table != "rank_response_metadata" - ] - return [table.replace("rank_response_", "") for table in rank_response_tables] - - def get_bin_summary( - self, - comparison_id: str, - regulator_column: str = "regulator_locus_tag", - bin_size: int = 5, - regulators_filter: list[str] | None = None, - ) -> pd.DataFrame: - """ - Generate bin-level summary for a specific comparison. - - :param comparison_id: Identifier for the comparison to summarize - :param regulator_column: Column name for regulator identifiers - :param bin_size: Bin size used in analysis - :param regulators_filter: Optional list of regulators to include - :return: DataFrame with bin summary results - - """ - intermediate_table_name = f"rank_response_{comparison_id}" - - if not self.db.table_exists(intermediate_table_name): - raise ValueError( - f"Intermediate table '{intermediate_table_name}' does not exist. " - "Run compute() first." - ) - - # Build WHERE clause for regulator filter - where_clause = "" - if regulators_filter: - # For analytical queries, escape and format the values safely - escaped_values = [ - f"'{reg.replace(chr(39), chr(39)+chr(39))}'" - for reg in regulators_filter - ] - where_clause = f"WHERE {regulator_column} IN ({', '.join(escaped_values)})" - - # Generate summary from intermediate data - summary_sql = f""" - SELECT - {regulator_column}, - bin_label, - COUNT(*) as records_in_bin, - SUM(responsive) as responsive_in_bin, - MAX(cumulative_responsive) as cumulative_responsive, - MAX(cumulative_responsive) / bin_label as response_rate - FROM {intermediate_table_name} - {where_clause} - GROUP BY {regulator_column}, bin_label - ORDER BY {regulator_column}, bin_label - """ - - summary_results = self.db.query(summary_sql) - - self.logger.info( - f"Generated summary for {len(summary_results)} regulator-bin combinations" - ) - return summary_results - - def get_regulator_summary( - self, - comparison_id: str, - regulator_column: str = "regulator_locus_tag", - max_bin_label: int | None = None, - ) -> pd.DataFrame: - """ - Generate regulator-level performance summary for a comparison. - - :param comparison_id: Identifier for the comparison - :param regulator_column: Column name for regulator identifiers - :param max_bin_label: Maximum bin label to consider (e.g., 20 for top 20 - targets) - :return: DataFrame with regulator-level summary statistics - - """ - intermediate_table_name = f"rank_response_{comparison_id}" - - if not self.db.table_exists(intermediate_table_name): - raise ValueError( - f"Intermediate table '{intermediate_table_name}' does not exist." - ) - - where_clause = "" - if max_bin_label: - where_clause = f"WHERE bin_label <= {max_bin_label}" - - regulator_summary_sql = f""" - SELECT - {regulator_column}, - COUNT(*) as total_targets, - SUM(responsive) as total_responsive, - COUNT(DISTINCT bin_label) as num_bins, - MAX(cumulative_responsive) as max_cumulative_responsive, - MAX(bin_label) as max_bin_label, - MAX(cumulative_responsive) / MAX(bin_label) - as overall_response_rate, - AVG(CASE WHEN bin_label <= 5 THEN responsive ELSE NULL END) - as top5_response_rate, - AVG(CASE WHEN bin_label <= 10 THEN responsive ELSE NULL END) - as top10_response_rate, - AVG(CASE WHEN bin_label <= 20 THEN responsive ELSE NULL END) - as top20_response_rate - FROM {intermediate_table_name} - {where_clause} - GROUP BY {regulator_column} - ORDER BY overall_response_rate DESC - """ - - return self.db.query(regulator_summary_sql) - - def summarize( - self, - comparison_id: str, - summary_type: str = "bin", - regulator_column: str = "regulator_locus_tag", - bin_size: int = 5, - regulators_filter: list[str] | None = None, - max_bin_label: int | None = None, - ) -> pd.DataFrame: - """ - Generate summary for a specific comparison. - - :param comparison_id: Identifier for the comparison to summarize - :param summary_type: Type of summary ('bin' or 'regulator') - :param regulator_column: Column name for regulator identifiers - :param bin_size: Bin size used in analysis - :param regulators_filter: Optional list of regulators to include - :param max_bin_label: Maximum bin label to consider (for regulator summaries) - :return: DataFrame with summary results - - """ - if summary_type == "bin": - return self.get_bin_summary( - comparison_id=comparison_id, - regulator_column=regulator_column, - bin_size=bin_size, - regulators_filter=regulators_filter, - ) - elif summary_type == "regulator": - return self.get_regulator_summary( - comparison_id=comparison_id, - regulator_column=regulator_column, - max_bin_label=max_bin_label, - ) - else: - raise ValueError(f"Unknown summary type: {summary_type}") - - def query(self, sql: str) -> pd.DataFrame: - """ - Execute custom SQL query on the database. - - :param sql: SQL query to execute - :return: DataFrame with query results - - """ - return self.db.query(sql) - - def get_comparison_data( - self, - comparison_id: str, - regulator_filter: list[str] | None = None, - limit: int | None = None, - ) -> pd.DataFrame: - """ - Get raw data for a specific comparison. - - :param comparison_id: Identifier for the comparison - :param regulator_filter: Optional list of regulators to filter - :param limit: Optional limit on number of records - :return: DataFrame with raw comparison data - - """ - table_name = f"rank_response_{comparison_id}" - - if not self.db.table_exists(table_name): - raise ValueError(f"No results found for comparison '{comparison_id}'") - - filters = {} - if regulator_filter: - filters["regulator_locus_tag"] = regulator_filter - - return self.db.get_results(table_name, filters=filters, limit=limit) - - def compare_across_datasets( - self, - comparison_ids: list[str], - regulator_column: str = "regulator_locus_tag", - metric_columns: list[str] = ["overall_response_rate", "top10_response_rate"], - ) -> pd.DataFrame: - """ - Compare regulator performance across multiple dataset comparisons. - - :param comparison_ids: List of comparison identifiers to compare - :param regulator_column: Column name for regulator identifiers - :param metric_columns: Performance metrics to compare - :return: DataFrame with cross-comparison results - - """ - comparison_data = [] - - for comp_id in comparison_ids: - summary = self.summarize(comp_id, summary_type="regulator") - summary["comparison_id"] = comp_id - comparison_data.append( - summary[[regulator_column, "comparison_id"] + metric_columns] - ) - - if not comparison_data: - return pd.DataFrame() - - # Combine all comparisons - combined = pd.concat(comparison_data, ignore_index=True) - - # Pivot to have comparisons as columns - result_dfs = [] - for metric in metric_columns: - pivot = combined.pivot( - index=regulator_column, columns="comparison_id", values=metric - ) - pivot.columns = [f"{metric}_{col}" for col in pivot.columns] - result_dfs.append(pivot) - - if len(result_dfs) == 1: - return result_dfs[0].reset_index() - else: - final_result = result_dfs[0] - for df in result_dfs[1:]: - final_result = final_result.join(df) - return final_result.reset_index() diff --git a/tfbpapi/IncrementalAnalysisDB.py b/tfbpapi/IncrementalAnalysisDB.py deleted file mode 100644 index 9e8d53e..0000000 --- a/tfbpapi/IncrementalAnalysisDB.py +++ /dev/null @@ -1,340 +0,0 @@ -import logging -from pathlib import Path -from typing import Any - -import duckdb -import pandas as pd - - -class IncrementalAnalysisDB: - """ - Class for managing incremental analysis results in DuckDB. - - Supports appending new results, updating existing ones, and maintaining analysis - metadata for tracking what's been computed. - - """ - - def __init__(self, db_path: str): - """ - Initialize connection to persistent DuckDB database. - - :param db_path: Path to the DuckDB database file - - """ - self.db_path = db_path - Path(db_path).parent.mkdir(parents=True, exist_ok=True) - self.conn = duckdb.connect(db_path) - self.logger = logging.getLogger(__name__) - - # Create metadata table to track analyses - self._ensure_metadata_table() - - def _ensure_metadata_table(self): - """Create metadata table if it doesn't exist.""" - self.conn.execute( - """ - CREATE TABLE IF NOT EXISTS analysis_metadata ( - table_name VARCHAR PRIMARY KEY, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - total_records INTEGER, - analysis_type VARCHAR, - parameters JSON, - description TEXT - ) - """ - ) - - def append_results( - self, - new_results: pd.DataFrame, - table_name: str, - analysis_type: str = "response_rate", - parameters: dict | None = None, - description: str | None = None, - deduplicate_on: list[str] | None = None, - ) -> int: - """ - Append new analysis results to an existing table. - - :param new_results: DataFrame with new results to append - :param table_name: Name of the target table - :param analysis_type: Type of analysis for metadata - :param parameters: Parameters used in the analysis - :param description: Description of the analysis - :param deduplicate_on: Column names to deduplicate on - :return: Number of records added - - """ - if new_results.empty: - self._update_metadata(table_name, 0, analysis_type, parameters, description) - return 0 - - # Handle deduplication if specified - if deduplicate_on and self.table_exists(table_name): - existing_data = self.get_results(table_name) - if not existing_data.empty: - # Remove duplicates based on specified columns - merged = pd.merge( - new_results, - existing_data[deduplicate_on], - on=deduplicate_on, - how="left", - indicator=True, - ) - new_results = merged[merged["_merge"] == "left_only"].drop( - "_merge", axis=1 - ) - - # Insert new data - if not new_results.empty: - self.conn.register("new_data", new_results) - if self.table_exists(table_name): - self.conn.execute(f"INSERT INTO {table_name} SELECT * FROM new_data") - else: - self.conn.execute( - f"CREATE TABLE {table_name} AS SELECT * FROM new_data" - ) - self.conn.unregister("new_data") - - records_added = len(new_results) - self._update_metadata( - table_name, records_added, analysis_type, parameters, description - ) - - return records_added - - def update_results( - self, updated_data: pd.DataFrame, table_name: str, key_columns: list[str] - ) -> int: - """ - Update existing records in a table. - - :param updated_data: DataFrame with updated values - :param table_name: Name of the target table - :param key_columns: Columns to match records on - :return: Number of records updated - - """ - if not self.table_exists(table_name) or updated_data.empty: - return 0 - - records_updated = 0 - self.conn.register("update_data", updated_data) - - # Build SET clause for non-key columns - non_key_columns = [ - col for col in updated_data.columns if col not in key_columns - ] - set_clause = ", ".join( - [f"{col} = update_data.{col}" for col in non_key_columns] - ) - - # Build WHERE clause for key columns - where_clause = " AND ".join( - [f"{table_name}.{col} = update_data.{col}" for col in key_columns] - ) - - update_query = f""" - UPDATE {table_name} - SET {set_clause} - FROM update_data - WHERE {where_clause} - """ - - self.conn.execute(update_query) - records_updated = len(updated_data) - - self.conn.unregister("update_data") - self._update_metadata_timestamp(table_name) - - return records_updated - - def query(self, sql: str) -> pd.DataFrame: - """ - Execute a SQL query and return results as DataFrame. - - :param sql: SQL query to execute - :return: DataFrame with query results - - """ - return self.conn.execute(sql).fetchdf() - - def get_results( - self, - table_name: str, - filters: dict[str, Any] | None = None, - limit: int | None = None, - ) -> pd.DataFrame: - """ - Retrieve results from a table. - - :param table_name: Name of the table to query - :param filters: Optional filters to apply - :param limit: Optional limit on number of records - :return: DataFrame with results - - """ - if not self.table_exists(table_name): - raise ValueError(f"Table {table_name} does not exist") - - query = f"SELECT * FROM {table_name}" - - if filters: - where_conditions = [] - for column, values in filters.items(): - if isinstance(values, list): - values_str = ", ".join( - [f"'{v}'" if isinstance(v, str) else str(v) for v in values] - ) - where_conditions.append(f"{column} IN ({values_str})") - else: - if isinstance(values, str): - where_conditions.append(f"{column} = '{values}'") - else: - where_conditions.append(f"{column} = {values}") - - if where_conditions: - query += " WHERE " + " AND ".join(where_conditions) - - if limit: - query += f" LIMIT {limit}" - - return self.conn.execute(query).fetchdf() - - def table_exists(self, table_name: str) -> bool: - """Check if a table exists in the database.""" - result = self.conn.execute( - """ - SELECT table_name FROM information_schema.tables - WHERE table_name = ? AND table_schema = 'main' - """, - [table_name], - ).fetchall() - return len(result) > 0 - - def drop_table(self, table_name: str) -> None: - """Drop a table and its metadata.""" - if self.table_exists(table_name): - self.conn.execute(f"DROP TABLE {table_name}") - self.conn.execute( - "DELETE FROM analysis_metadata WHERE table_name = ?", [table_name] - ) - - def get_table_info(self, table_name: str) -> dict[str, Any]: - """Get metadata information about a table.""" - if not self.table_exists(table_name): - raise ValueError(f"Table {table_name} does not exist") - - result = self.conn.execute( - """ - SELECT * FROM analysis_metadata WHERE table_name = ? - """, - [table_name], - ).fetchdf() - - if result.empty: - raise ValueError(f"No metadata found for table {table_name}") - - return result.iloc[0].to_dict() - - def list_tables(self) -> list[str]: - """List all tables in the database.""" - result = self.conn.execute( - """ - SELECT table_name FROM information_schema.tables - WHERE table_schema = 'main' - """ - ).fetchall() - return [row[0] for row in result] - - def get_table_schema(self, table_name: str) -> list[dict[str, str]]: - """Get schema information for a table.""" - if not self.table_exists(table_name): - raise ValueError(f"Table {table_name} does not exist") - - result = self.conn.execute(f"DESCRIBE {table_name}").fetchall() - # TODO: fix the mypy ignore/typing - return [ - { - "column_name": row[0], - "column_type": row[1], - "null": row[2], - "key": row[3] if len(row) > 3 else None, # type: ignore - "default": row[4] if len(row) > 4 else None, # type: ignore - "extra": row[5] if len(row) > 5 else None, # type: ignore - } - for row in result - ] - - def close(self) -> None: - """Close the database connection.""" - if hasattr(self, "conn"): - self.conn.close() - - def __enter__(self): - """Context manager entry.""" - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - """Context manager exit.""" - self.close() - - def _update_metadata( - self, - table_name: str, - records_added: int, - analysis_type: str, - parameters: dict | None, - description: str | None, - ) -> None: - """Update or insert metadata for a table.""" - import json - - # Check if metadata exists - existing = self.conn.execute( - """ - SELECT total_records FROM analysis_metadata WHERE table_name = ? - """, - [table_name], - ).fetchall() - - if existing: - # Update existing metadata - new_total = existing[0][0] + records_added - self.conn.execute( - """ - UPDATE analysis_metadata - SET last_updated = CURRENT_TIMESTAMP, total_records = ? - WHERE table_name = ? - """, - [new_total, table_name], - ) - else: - # Insert new metadata - self.conn.execute( - """ - INSERT INTO analysis_metadata - (table_name, total_records, analysis_type, parameters, description) - VALUES (?, ?, ?, ?, ?) - """, - [ - table_name, - records_added, - analysis_type, - json.dumps(parameters) if parameters else None, - description, - ], - ) - - def _update_metadata_timestamp(self, table_name: str) -> None: - """Update the last_updated timestamp for a table.""" - self.conn.execute( - """ - UPDATE analysis_metadata - SET last_updated = CURRENT_TIMESTAMP - WHERE table_name = ? - """, - [table_name], - ) diff --git a/tfbpapi/RankResponseAnalysis.py b/tfbpapi/RankResponseAnalysis.py deleted file mode 100644 index 036e08a..0000000 --- a/tfbpapi/RankResponseAnalysis.py +++ /dev/null @@ -1,277 +0,0 @@ -""" -Simplified rank-response analysis for pre-arranged binding and perturbation data. - -This module provides a streamlined approach to rank-response analysis where: -1. Binding data is already ranked by strength (rank 1 = strongest binding) -2. Perturbation data provides a simple responsive TRUE/FALSE column -3. Analysis focuses on binning and statistical calculations - -""" - -import logging -from typing import Any, Dict, Optional, Tuple - -import numpy as np -import pandas as pd - - -class RankResponseAnalyzer: - """ - Simplified rank-response analyzer for pre-arranged data. - - Takes a DataFrame with target identifiers (pre-sorted by binding strength) and - responsive boolean values, then performs binning analysis. - - """ - - def __init__( - self, - data: pd.DataFrame, - target_col: str, - responsive_col: str, - bin_size: int = 100, - ): - """ - Initialize the rank-response analyzer. - - :param data: DataFrame with target identifiers and responsive booleans - :param target_col: Name of column containing target identifiers - :param responsive_col: Name of column containing TRUE/FALSE responsive values - :param bin_size: Number of targets per bin for analysis - :raises ValueError: If data validation fails - - """ - self.logger = logging.getLogger(self.__class__.__name__) - - # Store parameters - self.target_col = target_col - self.responsive_col = responsive_col - self.bin_size = bin_size - - # Validate and store data - self.data = self._validate_data(data) - self.n_targets = len(self.data) - self.n_bins = (self.n_targets + bin_size - 1) // bin_size # Ceiling division - - # Calculate overall statistics - self.total_responsive = self.data[responsive_col].sum() - self.overall_response_rate = self.total_responsive / self.n_targets - - self.logger.info( - f"Initialized RankResponseAnalyzer: {self.n_targets} targets, " - f"{self.total_responsive} responsive ({self.overall_response_rate:.1%}), " - f"{self.n_bins} bins of size {bin_size}" - ) - - def _validate_data(self, data: pd.DataFrame) -> pd.DataFrame: - """Validate input data and return cleaned version.""" - if not isinstance(data, pd.DataFrame): - raise ValueError("Data must be a pandas DataFrame") - - if len(data) == 0: - raise ValueError("Data cannot be empty") - - # Check required columns exist - if self.target_col not in data.columns: - raise ValueError(f"Target column '{self.target_col}' not found in data") - if self.responsive_col not in data.columns: - raise ValueError( - f"Responsive column '{self.responsive_col}' not found in data" - ) - - # Extract just the required columns - clean_data = data[[self.target_col, self.responsive_col]].copy() - - # Check for missing values - if clean_data[self.target_col].isna().any(): - raise ValueError( - f"Target column '{self.target_col}' contains missing values" - ) - if clean_data[self.responsive_col].isna().any(): - raise ValueError( - f"Responsive column '{self.responsive_col}' contains missing values" - ) - - # Validate responsive column is boolean-like - unique_values = set(clean_data[self.responsive_col].unique()) - valid_boolean_sets = [ - {True, False}, - {1, 0}, - {1.0, 0.0}, - {"TRUE", "FALSE"}, - {"True", "False"}, - {"true", "false"}, - {"T", "F"}, - ] - - # Allow subsets (e.g., only True values, only False values) - is_valid_boolean = any( - unique_values.issubset(valid_set) for valid_set in valid_boolean_sets - ) - - if not is_valid_boolean: - raise ValueError( - f"Responsive column '{self.responsive_col}' must contain boolean-like values. " - f"Found: {unique_values}" - ) - - # Convert to standard boolean - clean_data[self.responsive_col] = clean_data[self.responsive_col].astype(bool) - - # Reset index to ensure proper ranking (rank 1 = index 0) - clean_data = clean_data.reset_index(drop=True) - - self.logger.debug( - f"Validated data: {len(clean_data)} rows, {unique_values} -> boolean" - ) - - return clean_data - - def create_bins(self) -> pd.DataFrame: - """ - Create bins from the ranked data. - - :return: DataFrame with bin assignments for each target - - """ - bins_data = self.data.copy() - bins_data["rank"] = range(1, len(bins_data) + 1) - bins_data["bin"] = ((bins_data["rank"] - 1) // self.bin_size) + 1 - - return bins_data - - def calculate_bin_stats(self) -> pd.DataFrame: - """ - Calculate statistics for each bin. - - :return: DataFrame with bin-level statistics - - """ - bins_data = self.create_bins() - - bin_stats = [] - for bin_num in range(1, self.n_bins + 1): - bin_data = bins_data[bins_data["bin"] == bin_num] - - n_targets_in_bin = len(bin_data) - n_responsive_in_bin = bin_data[self.responsive_col].sum() - response_rate = ( - n_responsive_in_bin / n_targets_in_bin if n_targets_in_bin > 0 else 0 - ) - - # Calculate rank range for this bin - min_rank = bin_data["rank"].min() - max_rank = bin_data["rank"].max() - - bin_stats.append( - { - "bin": bin_num, - "min_rank": min_rank, - "max_rank": max_rank, - "n_targets": n_targets_in_bin, - "n_responsive": n_responsive_in_bin, - "response_rate": response_rate, - "enrichment_vs_overall": ( - response_rate / self.overall_response_rate - if self.overall_response_rate > 0 - else np.nan - ), - } - ) - - return pd.DataFrame(bin_stats) - - def get_bin_summary(self) -> pd.DataFrame: - """Get comprehensive bin-level summary statistics.""" - return self.calculate_bin_stats() - - def calculate_enrichment(self, reference_rate: float | None = None) -> pd.DataFrame: - """ - Calculate enrichment scores for each bin. - - :param reference_rate: Reference response rate for enrichment calculation. If - None, uses overall response rate. - :return: DataFrame with enrichment calculations - - """ - if reference_rate is None: - reference_rate = self.overall_response_rate - - if reference_rate <= 0: - raise ValueError( - "Reference rate must be greater than 0 for enrichment calculation" - ) - - bin_stats = self.calculate_bin_stats() - bin_stats["enrichment_vs_reference"] = ( - bin_stats["response_rate"] / reference_rate - ) - bin_stats["reference_rate"] = reference_rate - - return bin_stats - - def get_rank_response_curve(self, window_size: int | None = None) -> pd.DataFrame: - """ - Get data for plotting rank vs response rate curve with sliding window. - - :param window_size: Size of sliding window for smoothing. If None, uses - bin_size. - :return: DataFrame with rank positions and smoothed response rates - - """ - if window_size is None: - window_size = self.bin_size - - bins_data = self.create_bins() - curve_data = [] - - for i in range(len(bins_data)): - # Define window around current position - start_idx = max(0, i - window_size // 2) - end_idx = min(len(bins_data), i + window_size // 2 + 1) - - window_data = bins_data.iloc[start_idx:end_idx] - window_response_rate = window_data[self.responsive_col].mean() - - curve_data.append( - { - "rank": bins_data.iloc[i]["rank"], - "target": bins_data.iloc[i][self.target_col], - "responsive": bins_data.iloc[i][self.responsive_col], - "smoothed_response_rate": window_response_rate, - "window_size": len(window_data), - } - ) - - return pd.DataFrame(curve_data) - - def get_summary_stats(self) -> dict[str, Any]: - """Get overall summary statistics.""" - return { - "n_targets": self.n_targets, - "n_responsive": self.total_responsive, - "overall_response_rate": self.overall_response_rate, - "n_bins": self.n_bins, - "bin_size": self.bin_size, - "targets_per_bin_avg": self.n_targets / self.n_bins, - } - - def to_dataframe(self) -> pd.DataFrame: - """Export full results as a comprehensive DataFrame.""" - bins_data = self.create_bins() - bin_stats = self.calculate_bin_stats() - - # Merge bin statistics back to individual target data - result = bins_data.merge(bin_stats, on="bin", suffixes=("", "_bin")) - - return result - - def __repr__(self) -> str: - """String representation of the analyzer.""" - return ( - f"RankResponseAnalyzer(" - f"targets={self.n_targets}, " - f"responsive={self.total_responsive}, " - f"rate={self.overall_response_rate:.1%}, " - f"bins={self.n_bins})" - ) diff --git a/tfbpapi/__init__.py b/tfbpapi/__init__.py index e69de29..f9db664 100644 --- a/tfbpapi/__init__.py +++ b/tfbpapi/__init__.py @@ -0,0 +1,33 @@ +from .datacard import DataCard +from .fetchers import HfDataCardFetcher, HfRepoStructureFetcher, HfSizeInfoFetcher +from .hf_cache_manager import HfCacheManager +from .models import ( + DatasetCard, + DatasetConfig, + DatasetType, + ExtractedMetadata, + FeatureInfo, + MetadataConfig, + MetadataRelationship, + PropertyMapping, + RepositoryConfig, +) +from .virtual_db import VirtualDB + +__all__ = [ + "DataCard", + "HfCacheManager", + "HfDataCardFetcher", + "HfRepoStructureFetcher", + "HfSizeInfoFetcher", + "MetadataConfig", + "PropertyMapping", + "RepositoryConfig", + "VirtualDB", + "DatasetCard", + "DatasetConfig", + "DatasetType", + "ExtractedMetadata", + "FeatureInfo", + "MetadataRelationship", +] diff --git a/tfbpapi/constants.py b/tfbpapi/constants.py index 62a4227..749678f 100644 --- a/tfbpapi/constants.py +++ b/tfbpapi/constants.py @@ -9,47 +9,3 @@ def get_hf_token() -> str | None: """Get HuggingFace token from environment variable.""" return os.getenv("HF_TOKEN") - - -SQL_FILTER_KEYWORDS = sql_keywords = { - "AND", - "OR", - "NOT", - "IN", - "IS", - "NULL", - "TRUE", - "FALSE", - "LIKE", - "BETWEEN", - "EXISTS", - "ALL", - "ANY", - "SOME", - "CASE", - "WHEN", - "THEN", - "ELSE", - "END", - "CAST", - "AS", - "SELECT", - "FROM", - "WHERE", - "GROUP", - "ORDER", - "BY", - "HAVING", - "LIMIT", - "OFFSET", - "DISTINCT", - "COUNT", - "SUM", - "AVG", - "MIN", - "MAX", - "UPPER", - "LOWER", - "SUBSTR", - "LENGTH", -} diff --git a/tfbpapi/datainfo/datacard.py b/tfbpapi/datacard.py similarity index 59% rename from tfbpapi/datainfo/datacard.py rename to tfbpapi/datacard.py index 370a677..bc4cf72 100644 --- a/tfbpapi/datainfo/datacard.py +++ b/tfbpapi/datacard.py @@ -21,9 +21,9 @@ from pydantic import ValidationError -from ..errors import DataCardError, DataCardValidationError, HfDataFetchError -from .fetchers import HfDataCardFetcher, HfRepoStructureFetcher, HfSizeInfoFetcher -from .models import ( +from tfbpapi.errors import DataCardError, DataCardValidationError, HfDataFetchError +from tfbpapi.fetchers import HfDataCardFetcher, HfRepoStructureFetcher, HfSizeInfoFetcher +from tfbpapi.models import ( DatasetCard, DatasetConfig, DatasetType, @@ -159,49 +159,6 @@ def get_config(self, config_name: str) -> DatasetConfig | None: """Get a specific configuration by name.""" return self.dataset_card.get_config_by_name(config_name) - def get_configs_by_type( - self, dataset_type: DatasetType | str - ) -> list[DatasetConfig]: - """Get configurations by dataset type.""" - if isinstance(dataset_type, str): - dataset_type = DatasetType(dataset_type) - return self.dataset_card.get_configs_by_type(dataset_type) - - def get_card_metadata(self) -> dict[str, Any]: - """ - Get all top-level metadata fields from the dataset card. - - Returns all fields stored in model_extra (e.g., license, tags, pretty_name, - etc.) as a dict. This gives direct access to the raw YAML structure at the card - level. - - :return: Dict of all extra metadata fields - - """ - if self.dataset_card.model_extra: - return dict(self.dataset_card.model_extra) - return {} - - def get_config_metadata(self, config_name: str) -> dict[str, Any]: - """ - Get all extra metadata fields from a specific config. - - Returns all fields stored in the config's model_extra (e.g., - experimental_conditions, custom fields, etc.) as a dict. - - :param config_name: Configuration name - :return: Dict of all extra metadata fields for this config - :raises DataCardError: If config not found - - """ - config = self.get_config(config_name) - if not config: - raise DataCardError(f"Configuration '{config_name}' not found") - - if config.model_extra: - return dict(config.model_extra) - return {} - def get_features(self, config_name: str) -> list[FeatureInfo]: """ Get all feature definitions for a configuration. @@ -259,75 +216,6 @@ def get_features_by_role( return by_role - def get_field_values(self, config_name: str, field_name: str) -> set[str]: - """ - Get all unique values for a specific field in a configuration. - - :param config_name: Configuration name - :param field_name: Field name to extract values from - :return: Set of unique values - :raises DataCardError: If config or field not found - - """ - config = self.get_config(config_name) - if not config: - raise DataCardError(f"Configuration '{config_name}' not found") - - # Check if field exists in the config - field_names = [f.name for f in config.dataset_info.features] - if field_name not in field_names: - raise DataCardError( - f"Field '{field_name}' not found in config '{config_name}'" - ) - - return self._extract_field_values(config, field_name) - - def _extract_field_values(self, config: DatasetConfig, field_name: str) -> set[str]: - """Extract unique values for a field from partition structure only.""" - values = set() - - # Check cache first - cache_key = f"{config.config_name}:{field_name}" - if cache_key in self._metadata_cache: - cached_metadata = self._metadata_cache[cache_key] - for meta in cached_metadata: - if meta.field_name == field_name: - values.update(meta.values) - return values - - try: - # For partitioned datasets, extract from file structure - if ( - config.dataset_info.partitioning - and config.dataset_info.partitioning.enabled - ): - partition_values = self._extract_partition_values(config, field_name) - if partition_values: - values.update(partition_values) - # Cache the result - self._metadata_cache[cache_key] = [ - ExtractedMetadata( - config_name=config.config_name, - field_name=field_name, - values=values, - extraction_method="partition_structure", - ) - ] - return values - - # For non-partitioned fields, we can no longer query parquet files - self.logger.debug( - f"Cannot extract values for {field_name} in {config.config_name}: " - "field is not partitioned and parquet querying is not supported" - ) - - except Exception as e: - self.logger.warning(f"Failed to extract values for {field_name}: {e}") - # Return empty set on failure instead of raising - # This maintains backward compatibility - - return values - def _extract_partition_values( self, config: DatasetConfig, field_name: str ) -> set[str]: @@ -423,67 +311,6 @@ def get_repository_info(self) -> dict[str, Any]: "has_default_config": self.dataset_card.get_default_config() is not None, } - def explore_config( - self, config_name: str, include_extra: bool = True - ) -> dict[str, Any]: - """ - Get detailed information about a specific configuration. - - Returns a comprehensive dict with config structure including features, data - files, partitioning, and optionally all extra metadata fields. - - :param config_name: Configuration name - :param include_extra: If True, include all fields from model_extra - :return: Dict with config details - :raises DataCardError: If config not found - - """ - config = self.get_config(config_name) - if not config: - raise DataCardError(f"Configuration '{config_name}' not found") - - info: dict[str, Any] = { - "config_name": config.config_name, - "description": config.description, - "dataset_type": config.dataset_type.value, - "is_default": config.default, - "num_features": len(config.dataset_info.features), - "features": [ - { - "name": f.name, - "dtype": f.dtype, - "description": f.description, - "role": f.role, - "has_definitions": f.definitions is not None, - } - for f in config.dataset_info.features - ], - "data_files": [ - {"split": df.split, "path": df.path} for df in config.data_files - ], - } - - # Add partitioning info if present - if config.dataset_info.partitioning: - info["partitioning"] = { - "enabled": config.dataset_info.partitioning.enabled, - "partition_by": config.dataset_info.partitioning.partition_by, - "path_template": config.dataset_info.partitioning.path_template, - } - - # Add metadata-specific fields - if config.applies_to: - info["applies_to"] = config.applies_to - - if config.metadata_fields: - info["metadata_fields"] = config.metadata_fields - - # Add all extra fields from model_extra - if include_extra and config.model_extra: - info["extra_fields"] = dict(config.model_extra) - - return info - def extract_metadata_schema(self, config_name: str) -> dict[str, Any]: """ Extract complete metadata schema for planning metadata table structure. @@ -565,52 +392,6 @@ def extract_metadata_schema(self, config_name: str) -> dict[str, Any]: return schema - def get_condition_levels( - self, config_name: str, field_name: str - ) -> dict[str, Any] | list[str]: - """ - Get factor levels for an experimental condition field. - - Returns definitions if available (structured dict with descriptions), otherwise - queries distinct values from the parquet file. - - :param config_name: Configuration name - :param field_name: Experimental condition field name - :return: Dict of definitions if available, otherwise list of distinct values - :raises DataCardError: If config or field not found, or field is not an - experimental condition - - """ - config = self.get_config(config_name) - if not config: - raise DataCardError(f"Configuration '{config_name}' not found") - - # Find the feature and verify it's an experimental condition - feature = None - for f in config.dataset_info.features: - if f.name == field_name: - feature = f - break - - if not feature: - raise DataCardError( - f"Field '{field_name}' not found in config '{config_name}'" - ) - - if feature.role != "experimental_condition": - raise DataCardError( - f"Field '{field_name}' is not an experimental condition " - f"(role={feature.role})" - ) - - # If field has definitions, return those - if feature.definitions: - return feature.definitions - - # Otherwise, query distinct values from parquet file - values = self.get_field_values(config_name, field_name) - return sorted(list(values)) - def get_experimental_conditions( self, config_name: str | None = None ) -> dict[str, Any]: @@ -724,168 +505,6 @@ def get_field_definitions( # Return definitions if present, otherwise empty dict return feature.definitions if feature.definitions else {} - def get_field_attribute( - self, config_name: str, field_name: str, attribute: str - ) -> dict[str, Any]: - """ - Extract a specific attribute from field definitions. - - This is useful for exploring nested attributes in condition definitions, - such as media composition, temperature parameters, or growth phases. - - :param config_name: Configuration name - :param field_name: Field with definitions (e.g., 'condition') - :param attribute: Attribute to extract (e.g., 'media', 'temperature_celsius') - :return: Dict mapping field values to their attribute specifications. - Returns 'unspecified' if attribute doesn't exist for a value. - - Example: - >>> card = DataCard('BrentLab/harbison_2004') - >>> media = card.get_field_attribute('harbison_2004', 'condition', 'media') - >>> print(media['YPD']) - {'name': 'YPD', 'carbon_source': [...], 'nitrogen_source': [...]} - - """ - # Get all field definitions - definitions = self.get_field_definitions(config_name, field_name) - - # Extract attribute for each definition - result = {} - for field_value, definition in definitions.items(): - if attribute in definition: - result[field_value] = definition[attribute] - else: - result[field_value] = "unspecified" - - return result - - def list_experimental_condition_fields(self, config_name: str) -> list[str]: - """ - List all fields with role=experimental_condition in a config. - - These are fields that contain per-sample experimental condition variation. - They represent the field-level (third level) of the experimental conditions - hierarchy. - - Fields with this role typically have `definitions` that map each value to - its detailed experimental specification. Use `get_field_definitions()` to - access these definitions. - - :param config_name: Configuration name - :return: List of field names with experimental_condition role - :raises DataCardError: If config not found - - Example: - >>> # Find all condition fields - >>> cond_fields = card.list_experimental_condition_fields('config_name') - >>> # ['condition', 'treatment', 'time_point'] - >>> - >>> # Then get definitions for each - >>> for field in cond_fields: - ... defs = card.get_field_definitions('config_name', field) - ... print(f"{field}: {len(defs)} levels") - - """ - config = self.get_config(config_name) - if not config: - raise DataCardError(f"Configuration '{config_name}' not found") - - return [ - f.name - for f in config.dataset_info.features - if f.role == "experimental_condition" - ] - - def summarize_field_levels( - self, - config_name: str, - field_name: str, - properties: list[str] | None = None, - max_levels: int | None = None, - ) -> str: - """ - Get a human-readable summary of field levels and their key properties. - - Provides a formatted summary showing each level (value) and selected properties - from its definition. Useful for quickly exploring experimental condition - structures without writing loops. - - :param config_name: Configuration name - :param field_name: Field name to summarize - :param properties: Optional list of property paths to display (e.g., ["media.name", "temperature_celsius"]). - If None, shows all top-level keys. - :param max_levels: Optional limit on number of levels to show - :return: Formatted string summary - :raises DataCardError: If config or field not found - - Example: - >>> card = DataCard("BrentLab/harbison_2004") - >>> # Show summary with specific properties - >>> print(card.summarize_field_levels( - ... "harbison_2004", - ... "condition", - ... properties=["media.carbon_source.compound", "temperature_celsius"] - ... )) - Field: condition - Levels: 14 - - YPD: - media.carbon_source.compound: ['D-glucose'] - temperature_celsius: 30 - - GAL: - media.carbon_source.compound: ['D-galactose'] - temperature_celsius: 30 - ... - - """ - from tfbpapi.datainfo.metadata_builder import get_nested_value - - # Get definitions - definitions = self.get_field_definitions(config_name, field_name) - - if not definitions: - return f"Field '{field_name}' has no definitions" - - lines = [ - f"Field: {field_name}", - f"Levels: {len(definitions)}", - "", - ] - - # Determine how many levels to show - levels_to_show = list(definitions.keys()) - if max_levels: - levels_to_show = levels_to_show[:max_levels] - - for level_name in levels_to_show: - definition = definitions[level_name] - lines.append(f"{level_name}:") - - if properties: - # Show only specified properties - for prop_path in properties: - value = get_nested_value(definition, prop_path) - lines.append(f" {prop_path}: {value}") - else: - # Show all top-level keys - if isinstance(definition, dict): - for key in definition.keys(): - value = definition[key] - # Truncate long values - value_str = str(value) - if len(value_str) > 80: - value_str = value_str[:77] + "..." - lines.append(f" {key}: {value_str}") - - lines.append("") - - if max_levels and len(definitions) > max_levels: - remaining = len(definitions) - max_levels - lines.append(f"... and {remaining} more levels") - - return "\n".join(lines) - def summary(self) -> str: """Get a human-readable summary of the dataset.""" card = self.dataset_card diff --git a/tfbpapi/datainfo/__init__.py b/tfbpapi/datainfo/__init__.py deleted file mode 100644 index c1643f6..0000000 --- a/tfbpapi/datainfo/__init__.py +++ /dev/null @@ -1,31 +0,0 @@ -from .datacard import DataCard -from .fetchers import HfDataCardFetcher, HfRepoStructureFetcher, HfSizeInfoFetcher -from .metadata_builder import MetadataBuilder -from .metadata_config_models import MetadataConfig, PropertyMapping, RepositoryConfig -from .metadata_manager import MetadataManager -from .models import ( - DatasetCard, - DatasetConfig, - DatasetType, - ExtractedMetadata, - FeatureInfo, - MetadataRelationship, -) - -__all__ = [ - "DataCard", - "HfDataCardFetcher", - "HfRepoStructureFetcher", - "HfSizeInfoFetcher", - "MetadataBuilder", - "MetadataConfig", - "PropertyMapping", - "RepositoryConfig", - "MetadataManager", - "DatasetCard", - "DatasetConfig", - "DatasetType", - "ExtractedMetadata", - "FeatureInfo", - "MetadataRelationship", -] diff --git a/tfbpapi/datainfo/metadata_builder.py b/tfbpapi/datainfo/metadata_builder.py deleted file mode 100644 index 112c46a..0000000 --- a/tfbpapi/datainfo/metadata_builder.py +++ /dev/null @@ -1,619 +0,0 @@ -""" -Metadata builder for creating standardized tables across heterogeneous datasets. - -This module provides tools for building standardized metadata views by normalizing -factor levels across datasets with varying experimental condition structures. Users -specify optional alias mappings in external YAML files to standardize factor level -names (e.g., "D-glucose" -> "glucose"). - -Key Components: -- MetadataBuilder: Main class for building normalized metadata across datasets -- normalize_value(): Function for normalizing values using optional alias mappings -- Three output modes: conditions, samples, full_data -- External YAML configuration for aliases and property mappings - -Example Configuration: - ```yaml - factor_aliases: - carbon_source: - glucose: ["D-glucose", "dextrose"] - galactose: ["D-galactose", "Galactose"] - - BrentLab/harbison_2004: - dataset: - harbison_2004: - carbon_source: - field: condition - path: media.carbon_source - ``` - -Example Usage: - >>> builder = MetadataBuilder("metadata_config.yaml") - >>> results = builder.build_metadata( - ... repos=[("BrentLab/harbison_2004", "harbison_2004")], - ... mode="conditions" - ... ) - -""" - -from __future__ import annotations - -from pathlib import Path -from typing import Any, Optional - -import pandas as pd - -from tfbpapi.datainfo import DataCard -from tfbpapi.datainfo.metadata_config_models import MetadataConfig, PropertyMapping -from tfbpapi.errors import DataCardError -from tfbpapi.HfQueryAPI import HfQueryAPI - - -def get_nested_value(data: dict, path: str) -> Any: - """ - Navigate nested dict/list using dot notation. - - Handles missing intermediate keys gracefully by returning None. - Supports extracting properties from lists of dicts. - - :param data: Dictionary to navigate - :param path: Dot-separated path (e.g., "media.carbon_source.compound") - :return: Value at path or None if not found - - Examples: - # Simple nested dict - get_nested_value({"media": {"name": "YPD"}}, "media.name") - -> "YPD" - - # List of dicts - extract property from each item - get_nested_value( - {"media": {"carbon_source": [{"compound": "glucose"}, {"compound": "galactose"}]}}, - "media.carbon_source.compound" - ) - -> ["glucose", "galactose"] - - # List of dicts - get the list itself - get_nested_value( - {"media": {"carbon_source": [{"compound": "glucose"}]}}, - "media.carbon_source" - ) - -> [{"compound": "glucose"}] - - """ - if not isinstance(data, dict): - return None - - keys = path.split(".") - current = data - - for i, key in enumerate(keys): - if isinstance(current, dict): - if key not in current: - return None - current = current[key] - elif isinstance(current, list): - # If current is a list and we have more keys, extract property from each item - if i < len(keys): - # Extract the remaining path from each list item - remaining_path = ".".join(keys[i:]) - results = [] - for item in current: - if isinstance(item, dict): - val = get_nested_value(item, remaining_path) - if val is not None: - results.append(val) - return results if results else None - else: - return None - - return current - - - - -def normalize_value(actual_value: Any, aliases: dict[str, list[Any]] | None) -> str: - """ - Normalize a value using optional alias mappings (case-insensitive). - - Returns the alias name if a match is found, otherwise returns the - original value as a string. This enables standardizing factor level - names across heterogeneous datasets. - - :param actual_value: The value from the data to normalize - :param aliases: Optional dict mapping alias names to lists of actual values. - Example: {"glucose": ["D-glucose", "dextrose"]} - :return: Alias name if match found, otherwise str(actual_value) - - Examples: - # With aliases - exact match - normalize_value("D-glucose", {"glucose": ["D-glucose", "dextrose"]}) - -> "glucose" - - # With aliases - case-insensitive match - normalize_value("DEXTROSE", {"glucose": ["D-glucose", "dextrose"]}) - -> "glucose" - - # No alias match - pass through - normalize_value("maltose", {"glucose": ["D-glucose"]}) - -> "maltose" - - # No aliases provided - pass through - normalize_value("D-glucose", None) - -> "D-glucose" - - # Numeric value - normalize_value(30, {"thirty": [30, "30"]}) - -> "thirty" - - """ - if aliases is None: - return str(actual_value) - - # Convert to string for comparison (case-insensitive) - actual_str = str(actual_value).lower() - - # Check each alias mapping - for alias_name, actual_values in aliases.items(): - for val in actual_values: - if str(val).lower() == actual_str: - return alias_name - - # No match found - pass through original value - return str(actual_value) - - -class MetadataBuilder: - """ - Build standardized metadata tables across heterogeneous datasets. - - This class creates metadata views with normalized factor level names using - optional alias mappings. Unlike a filtering system, this includes ALL data - and simply normalizes the factor level names for standardization. - - Configuration specifies: - 1. Optional factor aliases (for normalizing factor level names) - 2. Repository and dataset-specific property paths (where to find each property) - - Configuration structure: - factor_aliases: # Optional - property_name: - alias_name: [actual_value1, actual_value2] - - BrentLab/repo_name: - # Repo-wide properties (apply to all datasets in this repository) - # Paths are relative to experimental_conditions at the repository level - property1: - path: media.name - - # Dataset-specific section - dataset: - dataset_name: - # Dataset-specific properties (apply only to this dataset) - # Paths are relative to experimental_conditions at the config level - property2: - path: temperature_celsius - - # Field-level properties (per-sample variation) - # Paths are relative to field definitions (NOT experimental_conditions) - property3: - field: condition - path: media.carbon_source - - Path Resolution: - - Repo-wide & dataset-specific: Paths automatically - prepended with "experimental_conditions." - Example: path "media.name" resolves to experimental_conditions.media.name - - - Field-level: Paths used directly on field definitions (no prepending) - Example: field "condition", path "media.carbon_source" - looks in condition field's definitions for media.carbon_source - - Three output modes: - - conditions: Extract normalized metadata (no data retrieval) - - samples: Sample-level metadata (one row per sample_id) - - full_data: All measurements with metadata - - Attributes: - config: Validated MetadataConfig instance - factor_aliases: Optional alias mappings for normalization - - """ - - def __init__(self, config_path: Path | str): - """ - Initialize metadata builder with external configuration. - - :param config_path: Path to YAML configuration file - :raises FileNotFoundError: If config file doesn't exist - :raises ValueError: If configuration is invalid - - """ - self.config = MetadataConfig.from_yaml(config_path) - self.factor_aliases = self.config.factor_aliases - - def _get_property_mappings( - self, repo_id: str, config_name: str - ) -> dict[str, PropertyMapping]: - """ - Get property mappings for a specific repo/dataset combination. - - Merges repo-wide and dataset-specific mappings, with dataset-specific taking - precedence. - - :param repo_id: Repository ID - :param config_name: Dataset/config name - :return: Dict mapping property names to PropertyMapping objects - - """ - return self.config.get_property_mappings(repo_id, config_name) - - def build_metadata( - self, - repos: list[tuple[str, str]], - mode: str = "conditions", - token: str | None = None, - ) -> dict[str, Any]: - """ - Build metadata tables across datasets with normalized factor levels. - - Note: ALL repositories are processed - no filtering/exclusion occurs. - Factor aliases are used to normalize factor level names, not to filter. - - :param repos: List of (repo_id, config_name) tuples to process - :param mode: Output mode - "conditions", "samples", or "full_data" - :param token: Optional HuggingFace token for private repos - :return: Dict mapping repo_id to metadata results - :raises ValueError: If mode is invalid - - """ - if mode not in ["conditions", "samples", "full_data"]: - raise ValueError( - f"Invalid mode: {mode}. Must be 'conditions', " - "'samples', or 'full_data'" - ) - - results = {} - - for repo_id, config_name in repos: - try: - # Load DataCard - card = DataCard(repo_id, token=token) - - # Check if this repository has configuration - if repo_id not in self.config.repositories: - results[repo_id] = { - "error": f"No property mappings defined for {repo_id}" - } - continue - - # Extract and normalize metadata for ALL data - metadata = self._extract_metadata(card, repo_id, config_name) - - # Build result based on mode - result = {"metadata": metadata} - - if mode == "conditions": - # Mode 0: Just metadata, no data - pass - elif mode == "samples": - # Mode 1: Sample-level metadata - result["data"] = self._get_sample_metadata( - repo_id, config_name, metadata, token - ) - elif mode == "full_data": - # Mode 2: Full data with measurements - result["data"] = self._get_full_data( - repo_id, config_name, metadata, token - ) - - results[repo_id] = result - - except Exception as e: - results[repo_id] = {"error": f"Error processing dataset: {str(e)}"} - - return results - - def _extract_metadata( - self, - card: DataCard, - repo_id: str, - config_name: str, - ) -> dict[str, Any]: - """ - Extract and normalize metadata from datacard. - - Extracts ALL metadata with normalized factor level names. - No filtering occurs - all data is included. - - :param card: DataCard instance - :param repo_id: Repository ID - :param config_name: Configuration name - :return: Dict with normalized metadata - - """ - metadata = {} - property_mappings = self._get_property_mappings(repo_id, config_name) - - # Extract repo/config level metadata - repo_metadata = self._extract_repo_level(card, config_name, property_mappings) - metadata.update(repo_metadata) - - # Extract field-level metadata - field_metadata = self._extract_field_level(card, config_name, property_mappings) - if field_metadata: - metadata["field_values"] = field_metadata - - return metadata - - def _extract_repo_level( - self, - card: DataCard, - config_name: str, - property_mappings: dict[str, PropertyMapping], - ) -> dict[str, list[str]]: - """ - Extract and normalize repo/config-level metadata. - - :param card: DataCard instance - :param config_name: Configuration name - :param property_mappings: Property mappings for this repo/dataset - :return: Dict mapping property names to normalized values - - """ - metadata = {} - - # Get repo and config level conditions - try: - conditions = card.get_experimental_conditions(config_name) - except DataCardError: - conditions = {} - - if not conditions: - return metadata - - # Extract each mapped property - for prop_name, mapping in property_mappings.items(): - # Skip field-level mappings (handled separately) - if mapping.field is not None: - continue - - # Get path for this property - path = mapping.path - full_path = f"experimental_conditions.{path}" - - # Get value at this path - value = get_nested_value(conditions, full_path) - - if value is None: - continue - - # Ensure value is a list for consistent processing - actual_values = [value] if not isinstance(value, list) else value - - # Normalize using aliases (if configured) - aliases = self.factor_aliases.get(prop_name) - normalized_values = [normalize_value(v, aliases) for v in actual_values] - - metadata[prop_name] = normalized_values - - return metadata - - def _extract_field_level( - self, - card: DataCard, - config_name: str, - property_mappings: dict[str, PropertyMapping], - ) -> dict[str, dict[str, Any]]: - """ - Extract and normalize field-level metadata. - - Returns metadata for ALL field values (no filtering). - - :param card: DataCard instance - :param config_name: Configuration name - :param property_mappings: Property mappings for this repo/dataset - :return: Dict mapping field values to their normalized metadata - - """ - field_metadata: dict[str, dict[str, Any]] = {} - - # Group property mappings by field - field_mappings: dict[str, dict[str, str]] = {} - for prop_name, mapping in property_mappings.items(): - if mapping.field is not None: - field_name = mapping.field - if field_name not in field_mappings: - field_mappings[field_name] = {} - field_mappings[field_name][prop_name] = mapping.path - - # Process each field that has mappings - for field_name, prop_paths in field_mappings.items(): - # Get field definitions - definitions = card.get_field_definitions(config_name, field_name) - if not definitions: - continue - - # Extract metadata for ALL field values (no filtering!) - for field_value, definition in definitions.items(): - if field_value not in field_metadata: - field_metadata[field_value] = {} - - for prop_name, path in prop_paths.items(): - # Get value at path - value = get_nested_value(definition, path) - - if value is None: - continue - - # Ensure value is a list for consistent processing - actual_values = [value] if not isinstance(value, list) else value - - # Normalize using aliases (if configured) - aliases = self.factor_aliases.get(prop_name) - normalized_values = [ - normalize_value(v, aliases) for v in actual_values - ] - - field_metadata[field_value][prop_name] = normalized_values - - return field_metadata - - def _get_sample_metadata( - self, - repo_id: str, - config_name: str, - metadata: dict[str, Any], - token: str | None = None, - ) -> pd.DataFrame: - """ - Get sample-level metadata (Mode 1). - - Returns one row per sample_id with metadata columns. Includes ALL samples (no - filtering). Selects base metadata_fields columns plus adds extracted columns - from field-level property mappings. - - :param repo_id: Repository ID - :param config_name: Configuration name - :param metadata: Extracted metadata dict with field_values - :param token: Optional HuggingFace token - :return: DataFrame with sample metadata including extracted columns - - """ - try: - # Load DataCard to get metadata_fields - card = DataCard(repo_id, token=token) - config = card.get_config(config_name) - - # Initialize query API - api = HfQueryAPI(repo_id, token=token) - - # Determine which columns to select - if config and hasattr(config, 'metadata_fields') and config.metadata_fields: - # Select only metadata fields - columns = ", ".join(config.metadata_fields) - # Add sample_id if not already in metadata_fields - if "sample_id" not in config.metadata_fields: - columns = f"sample_id, {columns}" - sql = f""" - SELECT DISTINCT {columns} - FROM {config_name} - """ - else: - # No metadata_fields specified, select all - sql = f""" - SELECT DISTINCT * - FROM {config_name} - """ - - df = api.query(sql, config_name) - - # For sample-level, we want one row per sample_id - if "sample_id" in df.columns: - df_samples = df.groupby("sample_id").first().reset_index() - else: - # No sample_id column, return distinct rows - df_samples = df - - # Add extracted columns from field-level metadata - if "field_values" in metadata: - df_samples = self._add_extracted_columns(df_samples, metadata["field_values"]) - - return df_samples - - except Exception as e: - return pd.DataFrame( - {"error": [str(e)], "note": ["Failed to retrieve sample metadata"]} - ) - - def _add_extracted_columns( - self, - df: pd.DataFrame, - field_values: dict[str, dict[str, Any]] - ) -> pd.DataFrame: - """ - Add columns extracted from field-level property mappings. - - For each field that has property mappings, creates new columns by looking up - the field value in field_values and extracting the mapped properties. - - :param df: DataFrame with base metadata - :param field_values: Dict mapping field values to their extracted properties - :return: DataFrame with additional extracted columns - - """ - # Group properties by source field - # field_values is like: {"YPD": {"growth_media": ["YPD"], "carbon_source": ["glucose"]}} - - # We need to determine which field each row's value comes from - # For harbison_2004, the field is "condition" - - # Find fields that have definitions (these are the ones we can map) - for field_value, properties in field_values.items(): - # Each property becomes a new column - for prop_name, prop_values in properties.items(): - # Initialize column if it doesn't exist - if prop_name not in df.columns: - df[prop_name] = None - - # For each row where the field matches this field_value, set the property - # We need to find which column contains field_value - for col in df.columns: - if col in [prop_name, "sample_id"]: - continue - # Check if this column contains field_value - mask = df[col] == field_value - if mask.any(): - # Set the property value for matching rows - # Take first value from list (normalized values are lists) - value = prop_values[0] if prop_values else None - df.loc[mask, prop_name] = value - - return df - - def _get_full_data( - self, - repo_id: str, - config_name: str, - metadata: dict[str, Any], - token: str | None = None, - ) -> pd.DataFrame: - """ - Get full data with all measurements (Mode 2). - - Returns many rows per sample_id (one per measured feature/target). Includes ALL - data (no filtering) and ALL columns (both metadata and measurements). - - Unlike samples mode which respects metadata_fields, this mode returns the complete - dataset including quantitative measurements. - - :param repo_id: Repository ID - :param config_name: Configuration name - :param metadata: Extracted metadata dict - :param token: Optional HuggingFace token - :return: DataFrame with full data (all columns) - - """ - # Initialize query API - api = HfQueryAPI(repo_id, token=token) - - # Query for all data (no WHERE clause filtering) - sql = f""" - SELECT * - FROM {config_name} - """ - - try: - return api.query(sql, config_name) - except Exception as e: - return pd.DataFrame( - {"error": [str(e)], "note": ["Failed to retrieve full data"]} - ) - - def __repr__(self) -> str: - """String representation.""" - n_props = len(self.factor_aliases) - n_repos = len(self.config.repositories) - return ( - f"MetadataBuilder({n_props} properties with aliases, " - f"{n_repos} repositories configured)" - ) diff --git a/tfbpapi/datainfo/metadata_config_models.py b/tfbpapi/datainfo/metadata_config_models.py deleted file mode 100644 index 2cd4ded..0000000 --- a/tfbpapi/datainfo/metadata_config_models.py +++ /dev/null @@ -1,312 +0,0 @@ -""" -Pydantic models for metadata normalization configuration. - -This module defines the schema for MetadataBuilder configuration files, providing -validation for factor alias mappings and repository configurations. - -""" - -from __future__ import annotations - -from pathlib import Path -from typing import Any, Dict, List, Optional - -import yaml # type: ignore[import-untyped] -from pydantic import BaseModel, Field, field_validator, model_validator - - -class PropertyMapping(BaseModel): - """ - Mapping specification for a single property. - - Attributes: - path: Dot-notation path to the property value. - For repo/config-level: relative to experimental_conditions - For field-level: relative to field definitions - field: Optional field name for field-level properties. - When specified, looks in this field's definitions. - When omitted, looks in repo/config-level experimental_conditions. - - Examples: - Field-level property: - PropertyMapping(field="condition", path="media.carbon_source") - - Repo/config-level property: - PropertyMapping(path="temperature_celsius") - - """ - - field: str | None = Field(None, description="Field name for field-level properties") - path: str = Field(..., min_length=1, description="Dot-notation path to property") - - @field_validator("path") - @classmethod - def validate_path(cls, v: str) -> str: - """Ensure path is not just whitespace.""" - if not v.strip(): - raise ValueError("path cannot be empty or whitespace") - return v.strip() - - @field_validator("field") - @classmethod - def validate_field(cls, v: str | None) -> str | None: - """Ensure field is not empty string if provided.""" - if v is not None and not v.strip(): - raise ValueError("field cannot be empty or whitespace") - return v.strip() if v else None - - -class RepositoryConfig(BaseModel): - """ - Configuration for a single repository. Eg BrentLab/harbison_2004. - - Attributes: - properties: Repo-wide property mappings that apply to all datasets - dataset: Dataset-specific property mappings (override repo-wide) - - Example: - ```python - config = RepositoryConfig( - properties={ - "temperature_celsius": PropertyMapping(path="temperature_celsius") - }, - dataset={ - "dataset_name": { - "carbon_source": PropertyMapping( - field="condition", - path="media.carbon_source" - ) - } - } - ) - ``` - - """ - - properties: dict[str, PropertyMapping] = Field( - default_factory=dict, description="Repo-wide property mappings" - ) - dataset: dict[str, dict[str, PropertyMapping]] | None = Field( - None, description="Dataset-specific property mappings" - ) - - @model_validator(mode="before") - @classmethod - def parse_structure(cls, data: Any) -> Any: - """Parse raw dict structure into typed PropertyMapping objects.""" - if not isinstance(data, dict): - return data - - # Extract and parse dataset section - dataset_section = data.get("dataset") - parsed_datasets: dict[str, dict[str, PropertyMapping]] | None = None - - if dataset_section: - if not isinstance(dataset_section, dict): - raise ValueError("'dataset' key must contain a dict") - - parsed_datasets = {} - for dataset_name, properties in dataset_section.items(): - if not isinstance(properties, dict): - raise ValueError( - f"Dataset '{dataset_name}' must contain a dict of properties" - ) - - # Parse each property mapping into PropertyMapping object - parsed_datasets[dataset_name] = {} - for prop_name, mapping in properties.items(): - try: - parsed_datasets[dataset_name][prop_name] = ( - PropertyMapping.model_validate(mapping) - ) - except Exception as e: - raise ValueError( - f"Invalid property '{prop_name}' in dataset " - f"'{dataset_name}': {e}" - ) from e - - # Parse repo-wide properties (all keys except 'dataset') - parsed_properties = {} - for key, value in data.items(): - if key == "dataset": - continue - - try: - parsed_properties[key] = PropertyMapping.model_validate(value) - except Exception as e: - raise ValueError(f"Invalid repo-wide property '{key}': {e}") from e - - return {"properties": parsed_properties, "dataset": parsed_datasets} - - -class MetadataConfig(BaseModel): - """ - Configuration for building standardized metadata tables. - - Specifies optional alias mappings for normalizing factor levels across - heterogeneous datasets, plus property path mappings for each repository. - - Attributes: - factor_aliases: Optional mappings of standardized names to actual values. - Example: {"carbon_source": {"glucose": ["D-glucose", "dextrose"]}} - repositories: Dict mapping repository IDs to their configurations - - Example: - ```yaml - factor_aliases: - carbon_source: - glucose: ["D-glucose", "dextrose"] - galactose: ["D-galactose", "Galactose"] - - BrentLab/harbison_2004: - dataset: - harbison_2004: - carbon_source: - field: condition - path: media.carbon_source - - BrentLab/kemmeren_2014: - temperature: - path: temperature_celsius - dataset: - kemmeren_2014: - carbon_source: - path: media.carbon_source - ``` - - """ - - factor_aliases: dict[str, dict[str, list[Any]]] = Field( - default_factory=dict, - description="Optional alias mappings for normalizing factor levels", - ) - repositories: dict[str, RepositoryConfig] = Field( - ..., description="Repository configurations keyed by repo ID" - ) - - @field_validator("factor_aliases") - @classmethod - def validate_factor_aliases( - cls, v: dict[str, dict[str, list[Any]]] - ) -> dict[str, dict[str, list[Any]]]: - """Validate factor alias structure.""" - # Empty is OK - aliases are optional - if not v: - return v - - for prop_name, aliases in v.items(): - if not isinstance(aliases, dict): - raise ValueError( - f"Property '{prop_name}' aliases must be a dict, " - f"got {type(aliases).__name__}" - ) - - # Validate each alias mapping - for alias_name, actual_values in aliases.items(): - if not isinstance(actual_values, list): - raise ValueError( - f"Alias '{alias_name}' for '{prop_name}' must map " - f"to a list of values" - ) - if not actual_values: - raise ValueError( - f"Alias '{alias_name}' for '{prop_name}' cannot " - f"have empty value list" - ) - for val in actual_values: - if not isinstance(val, (str, int, float, bool)): - raise ValueError( - f"Alias '{alias_name}' for '{prop_name}' contains " - f"invalid value type: {type(val).__name__}" - ) - - return v - - @model_validator(mode="before") - @classmethod - def parse_repositories(cls, data: Any) -> Any: - """Parse repository configurations from top-level keys.""" - if not isinstance(data, dict): - return data - - # Extract repositories (all keys except 'factor_aliases') - repositories = {} - for key, value in data.items(): - if key != "factor_aliases": - try: - repositories[key] = RepositoryConfig.model_validate(value) - except Exception as e: - raise ValueError( - f"Invalid configuration for repository '{key}': {e}" - ) from e - - if not repositories: - raise ValueError( - "Configuration must have at least one repository configuration" - ) - - return { - "factor_aliases": data.get("factor_aliases", {}), - "repositories": repositories, - } - - @classmethod - def from_yaml(cls, path: Path | str) -> MetadataConfig: - """ - Load and validate configuration from YAML file. - - :param path: Path to YAML configuration file - :return: Validated MetadataConfig instance - :raises FileNotFoundError: If file doesn't exist - :raises ValueError: If configuration is invalid - - """ - path = Path(path) - - if not path.exists(): - raise FileNotFoundError(f"Configuration file not found: {path}") - - with open(path) as f: - data = yaml.safe_load(f) - - if not isinstance(data, dict): - raise ValueError("Configuration must be a YAML dict") - - return cls.model_validate(data) - - def get_repository_config(self, repo_id: str) -> RepositoryConfig | None: - """ - Get configuration for a specific repository. - - :param repo_id: Repository ID (e.g., "BrentLab/harbison_2004") - :return: RepositoryConfig instance or None if not found - - """ - return self.repositories.get(repo_id) - - def get_property_mappings( - self, repo_id: str, config_name: str - ) -> dict[str, PropertyMapping]: - """ - Get merged property mappings for a repo/dataset combination. - - Merges repo-wide and dataset-specific mappings, with dataset-specific taking - precedence. - - :param repo_id: Repository ID - :param config_name: Dataset/config name - :return: Dict mapping property names to PropertyMapping objects - - """ - repo_config = self.get_repository_config(repo_id) - if not repo_config: - return {} - - # Start with repo-wide properties - mappings: dict[str, PropertyMapping] = dict(repo_config.properties) - - # Override with dataset-specific properties - if repo_config.dataset and config_name in repo_config.dataset: - mappings.update(repo_config.dataset[config_name]) - - return mappings diff --git a/tfbpapi/datainfo/metadata_manager.py b/tfbpapi/datainfo/metadata_manager.py deleted file mode 100644 index d20b05a..0000000 --- a/tfbpapi/datainfo/metadata_manager.py +++ /dev/null @@ -1,282 +0,0 @@ -""" -MetadataManager for creating metadata table views from DataCard information. - -This module provides the MetadataManager class for generating SQL commands that create -metadata views from HuggingFace dataset information extracted via DataCard. The manager -enables users to: - -- Specify which columns to include in metadata views -- Add constant columns from top-level or config-level experimental conditions -- Flatten nested condition definitions into queryable columns -- Generate SQL CREATE VIEW statements - -The design follows a user-driven workflow: -1. User explores DataCard to understand available fields and conditions -2. User downloads data via HfCacheManager/HfQueryAPI (creates base views) -3. User specifies column selection for metadata view -4. MetadataManager generates and executes SQL to create the view - -""" - -from pathlib import Path -from typing import Any - -import duckdb -import pandas as pd - -# Separator conventions for formatting compound metadata values -COMPONENT_SEPARATORS = { - "type_value": ":", # Separates type from value (e.g., "type:value") - "value_conc": "@", # Separates value from concentration (e.g., "compound@0.5%") - "components": ";", # Separates multiple components (e.g., "comp1;comp2;comp3") - "types": "|", # Separates component types (e.g., "type1|type2|type3") -} - - -class MetadataManager: - """ - Manager for creating metadata table views from DataCard information. - - MetadataManager provides a flexible interface for generating SQL views that combine - data fields with experimental condition metadata. Users specify which columns to - include and how to handle conditions at different hierarchy levels. - - Example: - >>> # Step 1: Explore schema with DataCard - >>> card = DataCard("BrentLab/harbison_2004") - >>> schema = card.extract_metadata_schema("harbison_2004") - >>> - >>> # Step 2: Create metadata view - >>> mgr = MetadataManager() - >>> view = mgr.create_metadata_view( - ... base_view="harbison_2004_train", - ... view_name="harbison_2004_metadata", - ... include_fields=["regulator_locus_tag", "target_locus_tag", "condition"], - ... constant_columns={ - ... "temperature_celsius": 30, - ... "strain_background": "BY4741" - ... } - ... ) - - """ - - def __init__( - self, - cache_dir: Path | None = None, - cache: bool = False, - duckdb_conn: duckdb.DuckDBPyConnection | None = None, - ): - """ - Initialize MetadataManager. - - :param cache_dir: Optional directory for caching metadata views - :param cache: Whether to enable persistent caching (not yet implemented) - :param duckdb_conn: Optional DuckDB connection to use. If None, creates new in- - memory connection. Pass a shared connection to work with views created by - HfQueryAPI or other tools. - - """ - self._conn = ( - duckdb_conn if duckdb_conn is not None else duckdb.connect(":memory:") - ) - self._cache_dir = cache_dir - self._cache_enabled = cache - self._registered_datasets: dict[str, Any] = {} - self._view_names: dict[tuple[str, str], str] = {} - - def _sanitize_view_name(self, repo_id: str, config_name: str) -> str: - """ - Convert repo_id and config_name to valid SQL identifier. - - Replaces special characters (/, -, spaces) with underscores and appends - '_metadata' suffix. - - :param repo_id: Repository ID (e.g., "BrentLab/harbison_2004") - :param config_name: Configuration name (e.g., "harbison_2004") - :return: Sanitized view name - (e.g., "BrentLab_harbison_2004_harbison_2004_metadata") - - Example: - >>> mgr = MetadataManager() - >>> mgr._sanitize_view_name("BrentLab/dataset-name", "config_name") - 'BrentLab_dataset_name_config_name_metadata' - - """ - sanitized = f"{repo_id}_{config_name}" - sanitized = sanitized.replace("/", "_").replace("-", "_").replace(" ", "_") - return f"{sanitized}_metadata" - - def _flatten_condition_definition(self, definition: dict) -> dict: - """ - Flatten nested condition definition dict into flat key-value pairs. - - Extracts common experimental condition fields: - - media.name -> growth_media - - temperature_celsius -> temperature_celsius - - cultivation_method -> cultivation_method - - strain_background -> strain_background - - :param definition: Nested definition dict from field.definitions[value] - :return: Flat dict with standardized keys - - Example: - >>> mgr = MetadataManager() - >>> definition = { - ... "media": {"name": "YPD"}, - ... "temperature_celsius": 30 - ... } - >>> mgr._flatten_condition_definition(definition) - {'growth_media': 'YPD', 'temperature_celsius': 30} - - """ - result: dict[str, Any] = {} - if not definition: - return result - - # Extract media name as growth_media - if "media" in definition and isinstance(definition["media"], dict): - if "name" in definition["media"]: - result["growth_media"] = definition["media"]["name"] - - # Extract other top-level condition fields - for key in ["temperature_celsius", "cultivation_method", "strain_background"]: - if key in definition: - result[key] = definition[key] - - return result - - def _flatten_experimental_conditions(self, exp_conds: Any) -> dict: - """ - Extract attributes from ExperimentalConditions object or dict. - - Handles both Pydantic model objects and plain dicts. Extracts: - - temperature_celsius - - cultivation_method - - strain_background - - media.name (as growth_media) - - :param exp_conds: ExperimentalConditions object or dict - :return: Flat dict with condition values - - Example: - >>> mgr = MetadataManager() - >>> class MockConditions: - ... temperature_celsius = 30 - ... strain_background = "BY4741" - >>> mgr._flatten_experimental_conditions(MockConditions()) - {'temperature_celsius': 30, 'strain_background': 'BY4741'} - - """ - result: dict[str, Any] = {} - if exp_conds is None: - return result - - # Handle both objects (with attributes) and dicts (with keys) - for attr in ["temperature_celsius", "cultivation_method", "strain_background"]: - if hasattr(exp_conds, attr): - val = getattr(exp_conds, attr, None) - else: - val = exp_conds.get(attr) if isinstance(exp_conds, dict) else None - - if val is not None: - result[attr] = val - - # Extract media.name if present - if hasattr(exp_conds, "media"): - media = getattr(exp_conds, "media", None) - else: - media = exp_conds.get("media") if isinstance(exp_conds, dict) else None - - if media: - if hasattr(media, "name"): - name = getattr(media, "name", None) - else: - name = media.get("name") if isinstance(media, dict) else None - - if name: - result["growth_media"] = name - - return result - - def create_metadata_view( - self, - base_view: str, - view_name: str, - include_fields: list[str], - constant_columns: dict[str, Any] | None = None, - ) -> str: - """ - Create metadata view from base view with user-specified columns. - - Generates SQL CREATE OR REPLACE VIEW statement that selects specified fields - from an existing base view and adds constant columns as literals. - - :param base_view: Name of existing DuckDB view (created by HfCacheManager) - :param view_name: Name for the new metadata view - :param include_fields: List of field names to select from base view - :param constant_columns: Optional dict of {column_name: value} - to add as constants - :return: Name of created view - - Example: - >>> mgr = MetadataManager() - >>> view = mgr.create_metadata_view( - ... base_view="harbison_2004_train", - ... view_name="harbison_2004_metadata", - ... include_fields=["regulator_locus_tag", "target_locus_tag"], - ... constant_columns={"temperature_celsius": 30} - ... ) - >>> view - 'harbison_2004_metadata' - - """ - # Build SELECT clause with included fields - select_parts = include_fields.copy() - - # Add constant columns as literals - if constant_columns: - for col_name, col_value in constant_columns.items(): - if isinstance(col_value, str): - select_parts.append(f"'{col_value}' AS {col_name}") - else: - select_parts.append(f"{col_value} AS {col_name}") - - select_clause = ",\n ".join(select_parts) - - # Generate SQL - sql = f"""\ - CREATE OR REPLACE VIEW {view_name} AS - SELECT {select_clause} - FROM {base_view} -""" - - # Execute - self._conn.execute(sql) - - return view_name - - def get_active_configs(self) -> list[tuple[str, str]]: - """ - Get list of registered (repo_id, config_name) tuples. - - :return: List of active config tuples (empty for minimal implementation) - - Note: - This is a placeholder method for future multi-dataset support. - Currently returns empty list. - - """ - return [] - - def get_summary(self) -> pd.DataFrame: - """ - Get summary DataFrame of registered datasets. - - :return: Empty DataFrame for minimal implementation - - Note: - This is a placeholder method for future multi-dataset support. - Currently returns empty DataFrame. - - """ - return pd.DataFrame() diff --git a/tfbpapi/datainfo/models.py b/tfbpapi/datainfo/models.py deleted file mode 100644 index 0e3230b..0000000 --- a/tfbpapi/datainfo/models.py +++ /dev/null @@ -1,222 +0,0 @@ -""" -Pydantic models for dataset card validation. - -These models provide minimal structure for parsing HuggingFace dataset cards while -remaining flexible enough to accommodate diverse experimental systems. Most fields use -extra="allow" to accept domain-specific additions without requiring code changes. - -""" - -from enum import Enum -from typing import Any - -from pydantic import BaseModel, ConfigDict, Field, field_validator - - -class DatasetType(str, Enum): - """Supported dataset types.""" - - GENOMIC_FEATURES = "genomic_features" - ANNOTATED_FEATURES = "annotated_features" - GENOME_MAP = "genome_map" - METADATA = "metadata" - QC_DATA = "qc_data" - - -class FeatureInfo(BaseModel): - """ - Information about a dataset feature/column. - - Minimal required fields with flexible dtype handling. - - """ - - name: str = Field(..., description="Column name in the data") - dtype: str | dict[str, Any] = Field( - ..., - description="Data type (string, int64, float64, etc.) or class_label dict", - ) - description: str = Field(..., description="Description of the field") - role: str | None = Field( - default=None, - description="Optional semantic role. 'experimental_condition' has special behavior.", - ) - definitions: dict[str, Any] | None = Field( - default=None, - description="For experimental_condition fields: definitions per value", - ) - - -class PartitioningInfo(BaseModel): - """Partitioning configuration for datasets.""" - - enabled: bool = Field(default=False, description="Whether partitioning is enabled") - partition_by: list[str] | None = Field( - default=None, description="Partition column names" - ) - path_template: str | None = Field( - default=None, description="Path template for partitioned files" - ) - - -class DatasetInfo(BaseModel): - """Dataset structure information.""" - - features: list[FeatureInfo] = Field(..., description="Feature definitions") - partitioning: PartitioningInfo | None = Field( - default=None, description="Partitioning configuration" - ) - - -class DataFileInfo(BaseModel): - """Information about data files.""" - - split: str = Field(default="train", description="Dataset split name") - path: str = Field(..., description="Path to data file(s)") - - -class DatasetConfig(BaseModel): - """ - Configuration for a dataset within a repository. - - Uses extra="allow" to accept arbitrary experimental_conditions and other fields. - - """ - - config_name: str = Field(..., description="Unique configuration identifier") - description: str = Field(..., description="Human-readable description") - dataset_type: DatasetType = Field(..., description="Type of dataset") - default: bool = Field( - default=False, description="Whether this is the default config" - ) - applies_to: list[str] | None = Field( - default=None, description="Configs this metadata applies to" - ) - metadata_fields: list[str] | None = Field( - default=None, description="Fields for embedded metadata extraction" - ) - data_files: list[DataFileInfo] = Field(..., description="Data file information") - dataset_info: DatasetInfo = Field(..., description="Dataset structure information") - - model_config = ConfigDict(extra="allow") - - @field_validator("applies_to") - @classmethod - def applies_to_only_for_metadata(cls, v, info): - """Validate that applies_to is only used for metadata or qc_data configs.""" - if v is not None: - dataset_type = info.data.get("dataset_type") - if dataset_type not in (DatasetType.METADATA, DatasetType.QC_DATA): - raise ValueError( - "applies_to field is only valid " - "for metadata and qc_data dataset types" - ) - return v - - @field_validator("metadata_fields") - @classmethod - def metadata_fields_validation(cls, v): - """Validate metadata_fields usage.""" - if v is not None and len(v) == 0: - raise ValueError("metadata_fields cannot be empty list, use None instead") - return v - - -class DatasetCard(BaseModel): - """ - Complete dataset card model. - - Uses extra="allow" to accept arbitrary top-level metadata and - experimental_conditions. - - """ - - configs: list[DatasetConfig] = Field(..., description="Dataset configurations") - - model_config = ConfigDict(extra="allow") - - @field_validator("configs") - @classmethod - def configs_not_empty(cls, v): - """Ensure at least one config is present.""" - if not v: - raise ValueError("At least one dataset configuration is required") - return v - - @field_validator("configs") - @classmethod - def unique_config_names(cls, v): - """Ensure config names are unique.""" - names = [config.config_name for config in v] - if len(names) != len(set(names)): - raise ValueError("Configuration names must be unique") - return v - - @field_validator("configs") - @classmethod - def at_most_one_default(cls, v): - """Ensure at most one config is marked as default.""" - defaults = [config for config in v if config.default] - if len(defaults) > 1: - raise ValueError("At most one configuration can be marked as default") - return v - - def get_config_by_name(self, name: str) -> DatasetConfig | None: - """Get a configuration by name.""" - for config in self.configs: - if config.config_name == name: - return config - return None - - def get_configs_by_type(self, dataset_type: DatasetType) -> list[DatasetConfig]: - """Get all configurations of a specific type.""" - return [ - config for config in self.configs if config.dataset_type == dataset_type - ] - - def get_default_config(self) -> DatasetConfig | None: - """Get the default configuration if one exists.""" - defaults = [config for config in self.configs if config.default] - return defaults[0] if defaults else None - - def get_data_configs(self) -> list[DatasetConfig]: - """Get all non-metadata configurations.""" - return [ - config - for config in self.configs - if config.dataset_type != DatasetType.METADATA - ] - - def get_metadata_configs(self) -> list[DatasetConfig]: - """Get all metadata configurations.""" - return [ - config - for config in self.configs - if config.dataset_type == DatasetType.METADATA - ] - - -class ExtractedMetadata(BaseModel): - """Metadata extracted from datasets.""" - - config_name: str = Field(..., description="Source configuration name") - field_name: str = Field( - ..., description="Field name the metadata was extracted from" - ) - values: set[str] = Field(..., description="Unique values found") - extraction_method: str = Field(..., description="How the metadata was extracted") - - model_config = ConfigDict( - # Allow sets in JSON serialization - json_encoders={set: list} - ) - - -class MetadataRelationship(BaseModel): - """Relationship between a data config and its metadata.""" - - data_config: str = Field(..., description="Data configuration name") - metadata_config: str = Field(..., description="Metadata configuration name") - relationship_type: str = Field( - ..., description="Type of relationship (explicit, embedded)" - ) diff --git a/tfbpapi/datainfo/sample_manager.py b/tfbpapi/datainfo/sample_manager.py deleted file mode 100644 index 271a0ee..0000000 --- a/tfbpapi/datainfo/sample_manager.py +++ /dev/null @@ -1,774 +0,0 @@ -""" -Node-based sample representation for flexible filtering across heterogeneous datasets. - -This module provides a lightweight, NoSQL-inspired approach to managing samples from -multiple datasets with varying experimental condition structures. Each sample is -represented as a node with flattened properties, enabling flexible filtering across -datasets with different metadata schemas. - -Key Components: -- SampleNode: Represents a single sample with flattened properties -- SampleNodeCollection: In-memory storage with efficient indexing -- ActiveSet: Filtered collection of samples supporting set operations -- SampleFilter: MongoDB-style query language for filtering -- ConditionFlattener: Handles heterogeneous experimental condition structures -- SampleManager: Main API for loading, filtering, and managing samples - -Example Usage: - >>> manager = SampleManager() - >>> manager.load_from_datacard("BrentLab/harbison_2004", "harbison_2004") - >>> active = manager.filter_all({"carbon_source": {"$contains": "glucose"}}) - >>> print(f"Found {len(active)} glucose-grown samples") -""" - -from __future__ import annotations - -from dataclasses import dataclass, field -from datetime import datetime -from pathlib import Path -from typing import Any, Iterator - -import duckdb -import pandas as pd - - -def get_nested_value(data: dict, path: str) -> Any: - """ - Navigate nested dict using dot notation. - - Handles missing intermediate keys gracefully by returning None. - - :param data: Dictionary to navigate - :param path: Dot-separated path (e.g., "environmental_conditions.media.name") - :return: Value at path or None if not found - """ - if not isinstance(data, dict): - return None - - keys = path.split(".") - current = data - - for key in keys: - if not isinstance(current, dict) or key not in current: - return None - current = current[key] - - return current - - -def flatten_compound_list(compounds: list[dict] | str | None) -> str: - """ - Flatten compound list to comma-separated string. - - Handles various representations: - - List of dicts: Extract compound names - - String "unspecified": Return as-is - - None or empty list: Return "unspecified" - - :param compounds: Compound list or string - :return: Comma-separated compound names or "unspecified" - """ - if compounds is None or compounds == "unspecified": - return "unspecified" - - if isinstance(compounds, str): - return compounds - - if isinstance(compounds, list): - if not compounds: - return "unspecified" - compound_names = [ - c.get("compound", "") for c in compounds if isinstance(c, dict) - ] - return ", ".join(compound_names) if compound_names else "unspecified" - - return "unspecified" - - -@dataclass -class SampleNode: - """ - Represents a single sample with flattened experimental condition metadata. - - A sample is uniquely identified by (repo_id, config_name, sample_id) and contains - flattened properties from the 3-level experimental conditions hierarchy plus - selected metadata field values. - - Attributes: - sample_id: Unique identifier within config - repo_id: Dataset repository (e.g., "BrentLab/harbison_2004") - config_name: Configuration name (e.g., "harbison_2004") - properties: Flattened experimental condition properties - metadata_fields: Selected data fields (regulator, target, etc.) - property_sources: Tracks which level each property came from (repo/config/field/missing) - """ - - sample_id: str - repo_id: str - config_name: str - properties: dict[str, Any] = field(default_factory=dict) - metadata_fields: dict[str, Any] = field(default_factory=dict) - property_sources: dict[str, str] = field(default_factory=dict) - - def global_id(self) -> str: - """ - Generate unique identifier across all datasets. - - Format: {repo_id}:{config_name}:{sample_id} - - :return: Global sample ID - """ - return f"{self.repo_id}:{self.config_name}:{self.sample_id}" - - def get_property(self, key: str, default: Any = None) -> Any: - """ - Get property value with default fallback. - - :param key: Property name - :param default: Default value if property not found - :return: Property value or default - """ - return self.properties.get(key, default) - - def __repr__(self) -> str: - """String representation showing global ID and property count.""" - return f"SampleNode({self.global_id()}, {len(self.properties)} properties)" - - -class ConditionFlattener: - """ - Flattens experimental conditions from 3-level hierarchy into node properties. - - Handles heterogeneity in experimental condition structures across datasets: - - Variable nesting depths - - Missing/optional fields - - Compound lists - - Multi-level hierarchical overrides - - Resolution order: repo-level -> config-level -> field-level (field overrides all) - - This flattener dynamically discovers all properties without hardcoded schemas. - It recursively flattens nested structures using dot notation for keys. - """ - - @staticmethod - def _flatten_dict(d: dict, parent_key: str = "", sep: str = ".") -> dict[str, Any]: - """ - Recursively flatten nested dict using dot notation. - - :param d: Dictionary to flatten - :param parent_key: Parent key for recursion - :param sep: Separator for nested keys - :return: Flattened dict with dot-notation keys - """ - items = [] - for k, v in d.items(): - new_key = f"{parent_key}{sep}{k}" if parent_key else k - - if isinstance(v, dict): - # Recursively flatten nested dicts - items.extend( - ConditionFlattener._flatten_dict(v, new_key, sep=sep).items() - ) - elif isinstance(v, list) and v and isinstance(v[0], dict): - # Handle list of dicts (like compound lists) - # Store both flattened string and structured version - items.append((new_key, flatten_compound_list(v))) - items.append((f"_{new_key}_structured", v)) - else: - # Store primitive values as-is - items.append((new_key, v)) - - return dict(items) - - @classmethod - def flatten_conditions( - cls, - repo_conditions: dict | None, - config_conditions: dict | None, - field_conditions: dict | None, - ) -> tuple[dict[str, Any], dict[str, str]]: - """ - Flatten 3-level hierarchy into properties dict. - - Dynamically discovers and flattens all properties without hardcoded schemas. - Nested structures are flattened using dot notation (e.g., "media.carbon_source"). - - :param repo_conditions: Top-level experimental_conditions - :param config_conditions: Config-level experimental_conditions - :param field_conditions: Field-level condition definition - :return: (properties, property_sources) tuple - """ - properties = {} - sources = {} - - # Flatten each level - levels = [ - (repo_conditions, "repo"), - (config_conditions, "config"), - (field_conditions, "field"), - ] - - for conditions, level_name in levels: - if conditions is None: - continue - - # Flatten this level - flattened = cls._flatten_dict(conditions) - - # Merge into properties (later levels override earlier) - for key, value in flattened.items(): - properties[key] = value - sources[key] = level_name - - return properties, sources - - -class SampleNodeCollection: - """ - In-memory collection of sample nodes with efficient indexing. - - Storage strategy: - - Primary index: {(repo_id, config_name): {sample_id: SampleNode}} - - Enables fast dataset-level and cross-dataset operations - - Memory-efficient for typical workloads (1K-100K samples) - - Attributes: - _nodes: Two-level dict storing all nodes - """ - - def __init__(self): - """Initialize empty collection.""" - self._nodes: dict[tuple[str, str], dict[str, SampleNode]] = {} - - def add_node(self, node: SampleNode): - """ - Add sample node to collection. - - :param node: SampleNode to add - """ - key = (node.repo_id, node.config_name) - if key not in self._nodes: - self._nodes[key] = {} - self._nodes[key][node.sample_id] = node - - def get_node( - self, repo_id: str, config_name: str, sample_id: str - ) -> SampleNode | None: - """ - Get specific node. - - :param repo_id: Repository ID - :param config_name: Config name - :param sample_id: Sample ID - :return: SampleNode or None if not found - """ - key = (repo_id, config_name) - if key not in self._nodes: - return None - return self._nodes[key].get(sample_id) - - def get_node_by_global_id(self, global_id: str) -> SampleNode | None: - """ - Get node by global ID. - - :param global_id: Global ID in format {repo_id}:{config_name}:{sample_id} - :return: SampleNode or None if not found - """ - parts = global_id.split(":", 2) - if len(parts) != 3: - return None - return self.get_node(parts[0], parts[1], parts[2]) - - def iter_dataset_nodes( - self, repo_id: str, config_name: str - ) -> Iterator[SampleNode]: - """ - Iterate over nodes in specific dataset. - - :param repo_id: Repository ID - :param config_name: Config name - :return: Iterator over SampleNodes - """ - key = (repo_id, config_name) - if key in self._nodes: - yield from self._nodes[key].values() - - def iter_all_nodes(self) -> Iterator[SampleNode]: - """ - Iterate over all nodes in collection. - - :return: Iterator over all SampleNodes - """ - for dataset_nodes in self._nodes.values(): - yield from dataset_nodes.values() - - def get_dataset_keys(self) -> list[tuple[str, str]]: - """ - Get list of loaded (repo_id, config_name) pairs. - - :return: List of dataset keys - """ - return list(self._nodes.keys()) - - def count_total_nodes(self) -> int: - """ - Count total nodes in collection. - - :return: Total number of nodes - """ - return sum(len(nodes) for nodes in self._nodes.values()) - - def count_dataset_nodes(self, repo_id: str, config_name: str) -> int: - """ - Count nodes in specific dataset. - - :param repo_id: Repository ID - :param config_name: Config name - :return: Number of nodes in dataset - """ - key = (repo_id, config_name) - return len(self._nodes.get(key, {})) - - -@dataclass -class ActiveSet: - """ - Represents a collection of active samples with provenance tracking. - - ActiveSet supports set operations (union, intersection, difference) and - maintains metadata about how the set was created. This enables building - complex filters incrementally and tracking analysis provenance. - - Attributes: - sample_ids: Set of global sample IDs - name: Optional name for this set - description: Optional description - source_filter: Filter query that created this set - created_at: Timestamp - parent_set: ID of parent set (for tracking lineage) - """ - - sample_ids: set[str] = field(default_factory=set) - name: str | None = None - description: str | None = None - source_filter: dict | None = None - created_at: datetime = field(default_factory=datetime.now) - parent_set: str | None = None - - def __len__(self) -> int: - """Return number of samples in set.""" - return len(self.sample_ids) - - def union(self, other: ActiveSet, name: str | None = None) -> ActiveSet: - """ - Create new set with samples from both sets. - - :param other: Another ActiveSet - :param name: Optional name for new set - :return: New ActiveSet with union - """ - return ActiveSet( - sample_ids=self.sample_ids | other.sample_ids, - name=name or f"{self.name}_union_{other.name}", - description=f"Union of {self.name} and {other.name}", - ) - - def intersection(self, other: ActiveSet, name: str | None = None) -> ActiveSet: - """ - Create new set with samples in both sets. - - :param other: Another ActiveSet - :param name: Optional name for new set - :return: New ActiveSet with intersection - """ - return ActiveSet( - sample_ids=self.sample_ids & other.sample_ids, - name=name or f"{self.name}_intersect_{other.name}", - description=f"Intersection of {self.name} and {other.name}", - ) - - def difference(self, other: ActiveSet, name: str | None = None) -> ActiveSet: - """ - Create new set with samples in this set but not other. - - :param other: Another ActiveSet - :param name: Optional name for new set - :return: New ActiveSet with difference - """ - return ActiveSet( - sample_ids=self.sample_ids - other.sample_ids, - name=name or f"{self.name}_minus_{other.name}", - description=f"Samples in {self.name} but not {other.name}", - ) - - def to_sample_ids(self) -> list[str]: - """ - Export as list of global sample IDs. - - :return: Sorted list of global IDs - """ - return sorted(self.sample_ids) - - def __repr__(self) -> str: - """String representation showing name and size.""" - return f"ActiveSet(name={self.name}, size={len(self)})" - - -class SampleFilter: - """ - Evaluate filter expressions against sample nodes. - - Implements MongoDB-style query language for filtering heterogeneous sample data. - - Supported operators: - - $eq, $ne: Equality/inequality (default is $eq) - - $gt, $gte, $lt, $lte: Numeric comparisons - - $in, $nin: List membership - - $contains: String/list containment (case-insensitive) - - $exists: Field presence check - - $and, $or: Logical operators - - Example queries: - {"temperature_celsius": 30} # Simple equality - {"carbon_source": {"$contains": "glucose"}} # Contains check - {"$and": [{"temp": {"$gte": 25}}, {"temp": {"$lte": 35}}]} # Range - """ - - @staticmethod - def _matches_operator(value: Any, operator: str, target: Any) -> bool: - """ - Check if value matches operator condition. - - :param value: Actual property value - :param operator: Operator string (e.g., "$eq", "$contains") - :param target: Target value to compare against - :return: True if condition matches - """ - # Handle None values - if value is None: - if operator == "$exists": - return not target # $exists: false matches None - return operator == "$eq" and target is None - - # Equality operators - if operator == "$eq": - return value == target - if operator == "$ne": - return value != target - - # Numeric comparisons - if operator in ["$gt", "$gte", "$lt", "$lte"]: - try: - value_num = ( - float(value) if not isinstance(value, (int, float)) else value - ) - target_num = ( - float(target) if not isinstance(target, (int, float)) else target - ) - if operator == "$gt": - return value_num > target_num - if operator == "$gte": - return value_num >= target_num - if operator == "$lt": - return value_num < target_num - if operator == "$lte": - return value_num <= target_num - except (ValueError, TypeError): - return False - - # List membership - if operator == "$in": - return value in target if isinstance(target, (list, set, tuple)) else False - if operator == "$nin": - return ( - value not in target if isinstance(target, (list, set, tuple)) else True - ) - - # Contains check (case-insensitive for strings) - if operator == "$contains": - if isinstance(value, str) and isinstance(target, str): - return target.lower() in value.lower() - if isinstance(value, (list, tuple)): - return target in value - return False - - # Existence check - if operator == "$exists": - return bool(target) # $exists: true matches any non-None value - - return False - - @classmethod - def _matches_condition(cls, node: SampleNode, field: str, condition: Any) -> bool: - """ - Check if node matches a single field condition. - - :param node: SampleNode to check - :param field: Property name - :param condition: Condition value or operator dict - :return: True if matches - """ - # Get value from node - value = node.get_property(field) - - # Simple equality check - if not isinstance(condition, dict): - return value == condition - - # Operator-based checks - for operator, target in condition.items(): - if not cls._matches_operator(value, operator, target): - return False - - return True - - @classmethod - def matches(cls, node: SampleNode, query: dict) -> bool: - """ - Check if node matches filter query. - - :param node: SampleNode to check - :param query: Filter query dict - :return: True if node matches all conditions - """ - # Handle logical operators - if "$and" in query: - return all(cls.matches(node, sub_query) for sub_query in query["$and"]) - - if "$or" in query: - return any(cls.matches(node, sub_query) for sub_query in query["$or"]) - - # Check all field conditions (implicit AND) - for field, condition in query.items(): - if field.startswith("$"): # Skip logical operators - continue - if not cls._matches_condition(node, field, condition): - return False - - return True - - @classmethod - def filter_nodes( - cls, nodes: Iterator[SampleNode], query: dict - ) -> Iterator[SampleNode]: - """ - Filter nodes matching query (generator for memory efficiency). - - :param nodes: Iterator of SampleNodes - :param query: Filter query dict - :return: Iterator of matching nodes - """ - for node in nodes: - if cls.matches(node, query): - yield node - - -class SampleManager: - """ - Main interface for node-based sample management. - - SampleManager provides a flexible, NoSQL-inspired approach to managing samples - from multiple heterogeneous datasets. It replaces the table-based MetadataManager - with a node-based system that handles varying experimental condition structures. - - Key Features: - - Load samples from DataCard or DuckDB - - Filter using MongoDB-style queries - - Create and manage ActiveSets - - Export to DuckDB for SQL analysis - - Handle multi-dataset scenarios - - Example: - >>> manager = SampleManager() - >>> manager.load_from_datacard("BrentLab/harbison_2004", "harbison_2004") - >>> active = manager.filter_all({"carbon_source": {"$contains": "glucose"}}) - >>> print(f"Found {len(active)} samples") - """ - - def __init__( - self, - duckdb_conn: duckdb.DuckDBPyConnection | None = None, - cache_dir: Path | None = None, - ): - """ - Initialize SampleManager. - - :param duckdb_conn: Optional shared DuckDB connection for integration with HfQueryAPI - :param cache_dir: Optional cache directory for DataCard fetches - """ - self._collection = SampleNodeCollection() - self._active_sets: dict[str, ActiveSet] = {} - self._duckdb_conn = ( - duckdb_conn if duckdb_conn is not None else duckdb.connect(":memory:") - ) - self._cache_dir = cache_dir - self._flattener = ConditionFlattener() - - def get_active_configs(self) -> list[tuple[str, str]]: - """ - Get list of loaded (repo_id, config_name) pairs. - - :return: List of dataset keys - """ - return self._collection.get_dataset_keys() - - def get_summary(self) -> pd.DataFrame: - """ - Get summary of loaded samples. - - Returns DataFrame with columns: - - repo_id: Repository ID - - config_name: Configuration name - - sample_count: Number of samples - - properties: Common properties available - - :return: Summary DataFrame - """ - rows = [] - for repo_id, config_name in self.get_active_configs(): - count = self._collection.count_dataset_nodes(repo_id, config_name) - # Sample first node to get property names - first_node = next( - self._collection.iter_dataset_nodes(repo_id, config_name), None - ) - properties = list(first_node.properties.keys()) if first_node else [] - - rows.append( - { - "repo_id": repo_id, - "config_name": config_name, - "sample_count": count, - "properties": properties, - } - ) - - return pd.DataFrame(rows) - - def filter_all(self, query: dict, name: str | None = None) -> ActiveSet: - """ - Filter across all loaded samples. - - :param query: Filter query dict (MongoDB-style) - :param name: Optional name for ActiveSet - :return: ActiveSet with matching samples - """ - matching_nodes = SampleFilter.filter_nodes( - self._collection.iter_all_nodes(), query - ) - sample_ids = {node.global_id() for node in matching_nodes} - - return ActiveSet( - sample_ids=sample_ids, - name=name or "filtered_samples", - source_filter=query, - ) - - def filter_dataset( - self, - repo_id: str, - config_name: str, - query: dict, - name: str | None = None, - ) -> ActiveSet: - """ - Filter samples within specific dataset. - - :param repo_id: Repository ID - :param config_name: Config name - :param query: Filter query dict - :param name: Optional name for ActiveSet - :return: ActiveSet with matching samples - """ - matching_nodes = SampleFilter.filter_nodes( - self._collection.iter_dataset_nodes(repo_id, config_name), query - ) - sample_ids = {node.global_id() for node in matching_nodes} - - return ActiveSet( - sample_ids=sample_ids, - name=name or f"{config_name}_filtered", - source_filter=query, - ) - - def get_sample(self, global_id: str) -> SampleNode | None: - """ - Retrieve specific sample by global ID. - - :param global_id: Global ID in format {repo_id}:{config_name}:{sample_id} - :return: SampleNode or None if not found - """ - return self._collection.get_node_by_global_id(global_id) - - def get_samples_by_ids(self, sample_ids: list[str]) -> list[SampleNode]: - """ - Batch retrieve samples. - - :param sample_ids: List of global IDs - :return: List of SampleNodes (may be shorter if some not found) - """ - nodes = [] - for global_id in sample_ids: - node = self.get_sample(global_id) - if node is not None: - nodes.append(node) - return nodes - - def save_active_set(self, name: str, active_set: ActiveSet): - """ - Save named active set for later use. - - :param name: Name to save under - :param active_set: ActiveSet to save - """ - self._active_sets[name] = active_set - - def get_active_set(self, name: str) -> ActiveSet | None: - """ - Retrieve saved active set. - - :param name: Name of saved set - :return: ActiveSet or None if not found - """ - return self._active_sets.get(name) - - def list_active_sets(self) -> list[str]: - """ - List all saved active set names. - - :return: List of set names - """ - return list(self._active_sets.keys()) - - def get_property_distribution( - self, - property_name: str, - dataset_filter: tuple[str, str] | None = None, - ) -> dict[Any, int]: - """ - Get value distribution for a property. - - :param property_name: Property name to analyze - :param dataset_filter: Optional (repo_id, config_name) to limit to specific dataset - :return: Dict mapping values to counts - """ - distribution: dict[Any, int] = {} - - if dataset_filter: - nodes = self._collection.iter_dataset_nodes( - dataset_filter[0], dataset_filter[1] - ) - else: - nodes = self._collection.iter_all_nodes() - - for node in nodes: - value = node.get_property(property_name, "missing") - distribution[value] = distribution.get(value, 0) + 1 - - return distribution - - def __repr__(self) -> str: - """String representation showing loaded datasets and sample count.""" - total = self._collection.count_total_nodes() - datasets = len(self.get_active_configs()) - return f"SampleManager({datasets} datasets, {total} samples)" diff --git a/tfbpapi/errors.py b/tfbpapi/errors.py index 4fc751e..cbacc92 100644 --- a/tfbpapi/errors.py +++ b/tfbpapi/errors.py @@ -3,65 +3,7 @@ from typing import Any -class DatasetError(Exception): - """Base exception for all dataset-related errors.""" - - def __init__(self, message: str, details: dict[str, Any] | None = None): - super().__init__(message) - self.details = details or {} - - def __str__(self) -> str: - base_msg = super().__str__() - if self.details: - detail_str = ", ".join(f"{k}={v}" for k, v in self.details.items()) - return f"{base_msg} (Details: {detail_str})" - return base_msg - - -class RepoTooLargeError(DatasetError): - """Raised when repository exceeds auto-download threshold.""" - - def __init__(self, repo_id: str, size_mb: float, threshold_mb: float): - message = f"Repository '{repo_id}' is too large for " - f"auto-download: {size_mb:.2f}MB exceeds {threshold_mb}MB threshold" - super().__init__( - message, - details={ - "repo_id": repo_id, - "actual_size_mb": size_mb, - "threshold_mb": threshold_mb, - }, - ) - self.repo_id = repo_id - self.size_mb = size_mb - self.threshold_mb = threshold_mb - - -class DataCardParsingError(DatasetError): - """Raised when dataset card parsing fails.""" - - def __init__( - self, - message: str, - repo_id: str | None = None, - config_name: str | None = None, - original_error: Exception | None = None, - ): - details: dict[str, Any] = {} - if repo_id: - details["repo_id"] = repo_id - if config_name: - details["config_name"] = config_name - if original_error: - details["original_error"] = str(original_error) - - super().__init__(message, details) - self.repo_id = repo_id - self.config_name = config_name - self.original_error = original_error - - -class HfDataFetchError(DatasetError): +class HfDataFetchError(Exception): """Raised when HuggingFace API requests fail.""" def __init__( @@ -71,106 +13,13 @@ def __init__( status_code: int | None = None, endpoint: str | None = None, ): - details: dict[str, Any] = {} - if repo_id: - details["repo_id"] = repo_id - if status_code: - details["status_code"] = status_code - if endpoint: - details["endpoint"] = endpoint - - super().__init__(message, details) + super().__init__(message) self.repo_id = repo_id self.status_code = status_code self.endpoint = endpoint -class TableNotFoundError(DatasetError): - """Raised when requested table doesn't exist.""" - - def __init__(self, table_name: str, available_tables: list | None = None): - available_str = ( - f"Available tables: {available_tables}" - if available_tables - else "No tables available" - ) - message = f"Table '{table_name}' not found. {available_str}" - - super().__init__( - message, - details={ - "requested_table": table_name, - "available_tables": available_tables or [], - }, - ) - self.table_name = table_name - self.available_tables = available_tables or [] - - -class MissingDatasetTypeError(DatasetError): - """Raised when dataset_type field is missing from config.""" - - def __init__(self, config_name: str, available_fields: list | None = None): - fields_str = f"Available fields: {available_fields}" if available_fields else "" - message = ( - f"Missing 'dataset_type' field in config '{config_name}'. {fields_str}" - ) - - super().__init__( - message, - details={ - "config_name": config_name, - "available_fields": available_fields or [], - }, - ) - self.config_name = config_name - self.available_fields = available_fields or [] - - -class InvalidDatasetTypeError(DatasetError): - """Raised when dataset_type value is not recognized.""" - - def __init__(self, invalid_type: str, valid_types: list | None = None): - valid_str = f"Valid types: {valid_types}" if valid_types else "" - message = f"Invalid dataset type '{invalid_type}'. {valid_str}" - - super().__init__( - message, - details={"invalid_type": invalid_type, "valid_types": valid_types or []}, - ) - self.invalid_type = invalid_type - self.valid_types = valid_types or [] - - -class ConfigNotFoundError(DatasetError): - """Raised when a requested config doesn't exist.""" - - def __init__( - self, - config_name: str, - repo_id: str | None = None, - available_configs: list | None = None, - ): - repo_str = f" in repository '{repo_id}'" if repo_id else "" - available_str = ( - f"Available configs: {available_configs}" if available_configs else "" - ) - message = f"Config '{config_name}' not found{repo_str}. {available_str}" - - super().__init__( - message, - details={ - "config_name": config_name, - "repo_id": repo_id, - "available_configs": available_configs or [], - }, - ) - self.config_name = config_name - self.repo_id = repo_id - self.available_configs = available_configs or [] - - -class DataCardError(DatasetError): +class DataCardError(Exception): """Base exception for DataCard operations.""" pass @@ -185,67 +34,6 @@ def __init__( repo_id: str | None = None, validation_errors: list | None = None, ): - details: dict[str, Any] = {} - if repo_id: - details["repo_id"] = repo_id - if validation_errors: - details["validation_errors"] = validation_errors - - super().__init__(message, details) + super().__init__(message) self.repo_id = repo_id self.validation_errors = validation_errors or [] - - -class DataCardMetadataError(DataCardError): - """Exception raised when metadata extraction fails.""" - - def __init__( - self, - message: str, - config_name: str | None = None, - field_name: str | None = None, - ): - details: dict[str, Any] = {} - if config_name: - details["config_name"] = config_name - if field_name: - details["field_name"] = field_name - - super().__init__(message, details) - self.config_name = config_name - self.field_name = field_name - - -class InvalidFilterFieldError(DatasetError): - """Raised when filter fields don't exist in metadata columns.""" - - def __init__( - self, - config_name: str, - invalid_fields: list[str], - available_fields: list[str] | None = None, - ): - invalid_str = ", ".join(f"'{field}'" for field in invalid_fields) - available_str = ( - f"Available fields: {sorted(available_fields)}" - if available_fields - else "No fields available" - ) - message = ( - f"Invalid filter field(s) {invalid_str} for config '{config_name}'. " - f"{available_str}" - ) - - super().__init__( - message, - details={ - "config_name": config_name, - "invalid_fields": invalid_fields, - "available_fields": ( - sorted(available_fields) if available_fields else [] - ), - }, - ) - self.config_name = config_name - self.invalid_fields = invalid_fields - self.available_fields = sorted(available_fields) if available_fields else [] diff --git a/tfbpapi/datainfo/fetchers.py b/tfbpapi/fetchers.py similarity index 98% rename from tfbpapi/datainfo/fetchers.py rename to tfbpapi/fetchers.py index 30cc72a..c8d978f 100644 --- a/tfbpapi/datainfo/fetchers.py +++ b/tfbpapi/fetchers.py @@ -8,8 +8,8 @@ from huggingface_hub import DatasetCard, repo_info from requests import HTTPError -from ..constants import get_hf_token -from ..errors import HfDataFetchError +from tfbpapi.constants import get_hf_token +from tfbpapi.errors import HfDataFetchError class HfDataCardFetcher: diff --git a/tfbpapi/HfCacheManager.py b/tfbpapi/hf_cache_manager.py similarity index 88% rename from tfbpapi/HfCacheManager.py rename to tfbpapi/hf_cache_manager.py index 4d2e38e..0a4378c 100644 --- a/tfbpapi/HfCacheManager.py +++ b/tfbpapi/hf_cache_manager.py @@ -7,7 +7,7 @@ from huggingface_hub import scan_cache_dir, try_to_load_from_cache from huggingface_hub.utils import DeleteCacheStrategy -from .datainfo.datacard import DataCard +from tfbpapi.datacard import DataCard class HfCacheManager(DataCard): @@ -518,3 +518,67 @@ def _format_bytes(self, bytes_size: int) -> str: return f"{size:.1f}{unit}" size /= 1024.0 return f"{size:.1f}TB" + + def query( + self, sql: str, config_name: str, refresh_cache: bool = False + ) -> Any: + """ + Execute SQL query against a specific dataset configuration. + + Loads the specified configuration and executes the SQL query. + Automatically replaces the config name in the SQL with the actual + table name for user convenience. + + :param sql: SQL query to execute + :param config_name: Configuration name to query (table will be loaded + if needed) + :param refresh_cache: If True, force refresh from remote instead of + using cache + :return: DataFrame with query results + :raises ValueError: If config_name not found or query fails + + Example: + mgr = HfCacheManager("BrentLab/harbison_2004", duckdb.connect()) + df = mgr.query( + "SELECT DISTINCT sample_id FROM harbison_2004", + "harbison_2004" + ) + """ + # Validate config exists + if config_name not in [c.config_name for c in self.configs]: + available_configs = [c.config_name for c in self.configs] + raise ValueError( + f"Config '{config_name}' not found. " + f"Available configs: {available_configs}" + ) + + # Load the configuration data + config = self.get_config(config_name) + if not config: + raise ValueError(f"Could not retrieve config '{config_name}'") + + config_result = self._get_metadata_for_config( + config, force_refresh=refresh_cache + ) + if not config_result.get("success", False): + raise ValueError( + f"Failed to load data for config '{config_name}': " + f"{config_result.get('message', 'Unknown error')}" + ) + + table_name = config_result.get("table_name") + if not table_name: + raise ValueError(f"No table available for config '{config_name}'") + + # Replace config name with actual table name in SQL for user convenience + modified_sql = sql.replace(config_name, table_name) + + # Execute query + try: + result = self.duckdb_conn.execute(modified_sql).fetchdf() + self.logger.debug(f"Query executed successfully on {config_name}") + return result + except Exception as e: + self.logger.error(f"Query execution failed: {e}") + self.logger.error(f"SQL: {modified_sql}") + raise ValueError(f"Query execution failed: {e}") from e diff --git a/tfbpapi/models.py b/tfbpapi/models.py new file mode 100644 index 0000000..a1489f7 --- /dev/null +++ b/tfbpapi/models.py @@ -0,0 +1,584 @@ +""" +Pydantic models for dataset card validation and metadata configuration. + +These models provide minimal structure for parsing HuggingFace dataset cards while +remaining flexible enough to accommodate diverse experimental systems. Most fields use +extra="allow" to accept domain-specific additions without requiring code changes. + +Also includes models for VirtualDB metadata normalization configuration. + +""" + +from enum import Enum +from pathlib import Path +from typing import Any + +import yaml # type: ignore[import-untyped] +from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator + + +class DatasetType(str, Enum): + """Supported dataset types.""" + + GENOMIC_FEATURES = "genomic_features" + ANNOTATED_FEATURES = "annotated_features" + GENOME_MAP = "genome_map" + METADATA = "metadata" + QC_DATA = "qc_data" + + +class FeatureInfo(BaseModel): + """ + Information about a dataset feature/column. + + Minimal required fields with flexible dtype handling. + + """ + + name: str = Field(..., description="Column name in the data") + dtype: str | dict[str, Any] = Field( + ..., + description="Data type (string, int64, float64, etc.) or class_label dict", + ) + description: str = Field(..., description="Description of the field") + role: str | None = Field( + default=None, + description="Optional semantic role. 'experimental_condition' has special behavior.", + ) + definitions: dict[str, Any] | None = Field( + default=None, + description="For experimental_condition fields: definitions per value", + ) + + +class PartitioningInfo(BaseModel): + """Partitioning configuration for datasets.""" + + enabled: bool = Field(default=False, description="Whether partitioning is enabled") + partition_by: list[str] | None = Field( + default=None, description="Partition column names" + ) + path_template: str | None = Field( + default=None, description="Path template for partitioned files" + ) + + +class DatasetInfo(BaseModel): + """Dataset structure information.""" + + features: list[FeatureInfo] = Field(..., description="Feature definitions") + partitioning: PartitioningInfo | None = Field( + default=None, description="Partitioning configuration" + ) + + +class DataFileInfo(BaseModel): + """Information about data files.""" + + split: str = Field(default="train", description="Dataset split name") + path: str = Field(..., description="Path to data file(s)") + + +class DatasetConfig(BaseModel): + """ + Configuration for a dataset within a repository. + + Uses extra="allow" to accept arbitrary experimental_conditions and other fields. + + """ + + config_name: str = Field(..., description="Unique configuration identifier") + description: str = Field(..., description="Human-readable description") + dataset_type: DatasetType = Field(..., description="Type of dataset") + default: bool = Field( + default=False, description="Whether this is the default config" + ) + applies_to: list[str] | None = Field( + default=None, description="Configs this metadata applies to" + ) + metadata_fields: list[str] | None = Field( + default=None, description="Fields for embedded metadata extraction" + ) + data_files: list[DataFileInfo] = Field(..., description="Data file information") + dataset_info: DatasetInfo = Field(..., description="Dataset structure information") + + model_config = ConfigDict(extra="allow") + + @field_validator("applies_to") + @classmethod + def applies_to_only_for_metadata(cls, v, info): + """Validate that applies_to is only used for metadata or qc_data configs.""" + if v is not None: + dataset_type = info.data.get("dataset_type") + if dataset_type not in (DatasetType.METADATA, DatasetType.QC_DATA): + raise ValueError( + "applies_to field is only valid " + "for metadata and qc_data dataset types" + ) + return v + + @field_validator("metadata_fields") + @classmethod + def metadata_fields_validation(cls, v): + """Validate metadata_fields usage.""" + if v is not None and len(v) == 0: + raise ValueError("metadata_fields cannot be empty list, use None instead") + return v + + +class DatasetCard(BaseModel): + """ + Complete dataset card model. + + Uses extra="allow" to accept arbitrary top-level metadata and + experimental_conditions. + + """ + + configs: list[DatasetConfig] = Field(..., description="Dataset configurations") + + model_config = ConfigDict(extra="allow") + + @field_validator("configs") + @classmethod + def configs_not_empty(cls, v): + """Ensure at least one config is present.""" + if not v: + raise ValueError("At least one dataset configuration is required") + return v + + @field_validator("configs") + @classmethod + def unique_config_names(cls, v): + """Ensure config names are unique.""" + names = [config.config_name for config in v] + if len(names) != len(set(names)): + raise ValueError("Configuration names must be unique") + return v + + @field_validator("configs") + @classmethod + def at_most_one_default(cls, v): + """Ensure at most one config is marked as default.""" + defaults = [config for config in v if config.default] + if len(defaults) > 1: + raise ValueError("At most one configuration can be marked as default") + return v + + def get_config_by_name(self, name: str) -> DatasetConfig | None: + """Get a configuration by name.""" + for config in self.configs: + if config.config_name == name: + return config + return None + + def get_configs_by_type(self, dataset_type: DatasetType) -> list[DatasetConfig]: + """Get all configurations of a specific type.""" + return [ + config for config in self.configs if config.dataset_type == dataset_type + ] + + def get_default_config(self) -> DatasetConfig | None: + """Get the default configuration if one exists.""" + defaults = [config for config in self.configs if config.default] + return defaults[0] if defaults else None + + def get_data_configs(self) -> list[DatasetConfig]: + """Get all non-metadata configurations.""" + return [ + config + for config in self.configs + if config.dataset_type != DatasetType.METADATA + ] + + def get_metadata_configs(self) -> list[DatasetConfig]: + """Get all metadata configurations.""" + return [ + config + for config in self.configs + if config.dataset_type == DatasetType.METADATA + ] + + +class ExtractedMetadata(BaseModel): + """Metadata extracted from datasets.""" + + config_name: str = Field(..., description="Source configuration name") + field_name: str = Field( + ..., description="Field name the metadata was extracted from" + ) + values: set[str] = Field(..., description="Unique values found") + extraction_method: str = Field(..., description="How the metadata was extracted") + + model_config = ConfigDict( + # Allow sets in JSON serialization + json_encoders={set: list} + ) + + +class MetadataRelationship(BaseModel): + """Relationship between a data config and its metadata.""" + + data_config: str = Field(..., description="Data configuration name") + metadata_config: str = Field(..., description="Metadata configuration name") + relationship_type: str = Field( + ..., description="Type of relationship (explicit, embedded)" + ) + + +# ============================================================================ +# VirtualDB Metadata Configuration Models +# ============================================================================ + + +class PropertyMapping(BaseModel): + """ + Mapping specification for a single property. + + Attributes: + path: Optional dot-notation path to the property value. + For repo/config-level: relative to experimental_conditions + For field-level: relative to field definitions + When omitted with field specified, creates a column alias. + field: Optional field name for field-level properties. + When specified, looks in this field's definitions. + When omitted, looks in repo/config-level experimental_conditions. + + Examples: + Field-level property with path: + PropertyMapping(field="condition", path="media.carbon_source") + + Repo/config-level property: + PropertyMapping(path="temperature_celsius") + + Field-level column alias (no path): + PropertyMapping(field="condition") + + """ + + field: str | None = Field(None, description="Field name for field-level properties") + path: str | None = Field(None, description="Dot-notation path to property") + + @field_validator("path") + @classmethod + def validate_path(cls, v: str | None) -> str | None: + """Ensure path is not just whitespace if provided.""" + if v is not None and not v.strip(): + raise ValueError("path cannot be empty or whitespace") + return v.strip() if v else None + + @field_validator("field") + @classmethod + def validate_field(cls, v: str | None) -> str | None: + """Ensure field is not empty string if provided.""" + if v is not None and not v.strip(): + raise ValueError("field cannot be empty or whitespace") + return v.strip() if v else None + + @model_validator(mode="after") + def validate_at_least_one_specified(self) -> "PropertyMapping": + """Ensure at least field or path is specified.""" + if self.field is None and self.path is None: + raise ValueError("At least one of 'field' or 'path' must be specified") + return self + + +class RepositoryConfig(BaseModel): + """ + Configuration for a single repository. Eg BrentLab/harbison_2004. + + Attributes: + properties: Repo-wide property mappings that apply to all datasets + dataset: Dataset-specific property mappings (override repo-wide) + + Example: + ```python + config = RepositoryConfig( + properties={ + "temperature_celsius": PropertyMapping(path="temperature_celsius") + }, + dataset={ + "dataset_name": { + "carbon_source": PropertyMapping( + field="condition", + path="media.carbon_source" + ) + } + } + ) + ``` + + """ + + properties: dict[str, PropertyMapping] = Field( + default_factory=dict, description="Repo-wide property mappings" + ) + dataset: dict[str, dict[str, PropertyMapping]] | None = Field( + None, description="Dataset-specific property mappings" + ) + + @model_validator(mode="before") + @classmethod + def parse_structure(cls, data: Any) -> Any: + """Parse raw dict structure into typed PropertyMapping objects.""" + if not isinstance(data, dict): + return data + + # Extract and parse dataset section + dataset_section = data.get("dataset") + parsed_datasets: dict[str, dict[str, PropertyMapping]] | None = None + + if dataset_section: + if not isinstance(dataset_section, dict): + raise ValueError("'dataset' key must contain a dict") + + parsed_datasets = {} + for dataset_name, properties in dataset_section.items(): + if not isinstance(properties, dict): + raise ValueError( + f"Dataset '{dataset_name}' must contain a dict of properties" + ) + + # Parse each property mapping into PropertyMapping object + parsed_datasets[dataset_name] = {} + for prop_name, mapping in properties.items(): + try: + parsed_datasets[dataset_name][prop_name] = ( + PropertyMapping.model_validate(mapping) + ) + except Exception as e: + raise ValueError( + f"Invalid property '{prop_name}' in dataset " + f"'{dataset_name}': {e}" + ) from e + + # Parse repo-wide properties (all keys except 'dataset') + parsed_properties = {} + for key, value in data.items(): + if key == "dataset": + continue + + try: + parsed_properties[key] = PropertyMapping.model_validate(value) + except Exception as e: + raise ValueError(f"Invalid repo-wide property '{key}': {e}") from e + + return {"properties": parsed_properties, "dataset": parsed_datasets} + + +class MetadataConfig(BaseModel): + """ + Configuration for building standardized metadata tables. + + Specifies optional alias mappings for normalizing factor levels across + heterogeneous datasets, plus property path mappings for each repository. + + Attributes: + factor_aliases: Optional mappings of standardized names to actual values. + Example: {"carbon_source": {"glucose": ["D-glucose", "dextrose"]}} + missing_value_labels: Labels for missing values by property name + description: Human-readable descriptions for each property + repositories: Dict mapping repository IDs to their configurations + + Example: + ```yaml + repositories: + BrentLab/harbison_2004: + dataset: + harbison_2004: + carbon_source: + field: condition + path: media.carbon_source + + BrentLab/kemmeren_2014: + temperature: + path: temperature_celsius + dataset: + kemmeren_2014: + carbon_source: + path: media.carbon_source + + factor_aliases: + carbon_source: + glucose: ["D-glucose", "dextrose"] + galactose: ["D-galactose", "Galactose"] + + missing_value_labels: + carbon_source: "unspecified" + + description: + carbon_source: "Carbon source in growth media" + ``` + + """ + + factor_aliases: dict[str, dict[str, list[Any]]] = Field( + default_factory=dict, + description="Optional alias mappings for normalizing factor levels", + ) + missing_value_labels: dict[str, str] = Field( + default_factory=dict, + description="Labels for missing values by property name", + ) + description: dict[str, str] = Field( + default_factory=dict, + description="Human-readable descriptions for each property", + ) + repositories: dict[str, RepositoryConfig] = Field( + ..., description="Repository configurations keyed by repo ID" + ) + + @field_validator("missing_value_labels", mode="before") + @classmethod + def validate_missing_value_labels(cls, v: Any) -> dict[str, str]: + """Validate missing value labels structure, filtering out None values.""" + if not v: + return {} + if not isinstance(v, dict): + raise ValueError("missing_value_labels must be a dict") + # Filter out None values that may come from empty YAML values + return {k: val for k, val in v.items() if val is not None} + + @field_validator("description", mode="before") + @classmethod + def validate_description(cls, v: Any) -> dict[str, str]: + """Validate description structure, filtering out None values.""" + if not v: + return {} + if not isinstance(v, dict): + raise ValueError("description must be a dict") + # Filter out None values that may come from empty YAML values + return {k: val for k, val in v.items() if val is not None} + + @field_validator("factor_aliases") + @classmethod + def validate_factor_aliases( + cls, v: dict[str, dict[str, list[Any]]] + ) -> dict[str, dict[str, list[Any]]]: + """Validate factor alias structure.""" + # Empty is OK - aliases are optional + if not v: + return v + + for prop_name, aliases in v.items(): + if not isinstance(aliases, dict): + raise ValueError( + f"Property '{prop_name}' aliases must be a dict, " + f"got {type(aliases).__name__}" + ) + + # Validate each alias mapping + for alias_name, actual_values in aliases.items(): + if not isinstance(actual_values, list): + raise ValueError( + f"Alias '{alias_name}' for '{prop_name}' must map " + f"to a list of values" + ) + if not actual_values: + raise ValueError( + f"Alias '{alias_name}' for '{prop_name}' cannot " + f"have empty value list" + ) + for val in actual_values: + if not isinstance(val, (str, int, float, bool)): + raise ValueError( + f"Alias '{alias_name}' for '{prop_name}' contains " + f"invalid value type: {type(val).__name__}" + ) + + return v + + @model_validator(mode="before") + @classmethod + def parse_repositories(cls, data: Any) -> Any: + """Parse repository configurations from 'repositories' key.""" + if not isinstance(data, dict): + return data + + # Extract repositories from 'repositories' key + repositories_data = data.get("repositories", {}) + + if not repositories_data: + raise ValueError( + "Configuration must have a 'repositories' key with at least one repository" + ) + + if not isinstance(repositories_data, dict): + raise ValueError("'repositories' key must contain a dict") + + repositories = {} + for repo_id, repo_config in repositories_data.items(): + try: + repositories[repo_id] = RepositoryConfig.model_validate(repo_config) + except Exception as e: + raise ValueError( + f"Invalid configuration for repository '{repo_id}': {e}" + ) from e + + return { + "factor_aliases": data.get("factor_aliases", {}), + "missing_value_labels": data.get("missing_value_labels", {}), + "description": data.get("description", {}), + "repositories": repositories, + } + + @classmethod + def from_yaml(cls, path: Path | str) -> "MetadataConfig": + """ + Load and validate configuration from YAML file. + + :param path: Path to YAML configuration file + :return: Validated MetadataConfig instance + :raises FileNotFoundError: If file doesn't exist + :raises ValueError: If configuration is invalid + + """ + path = Path(path) + + if not path.exists(): + raise FileNotFoundError(f"Configuration file not found: {path}") + + with open(path) as f: + data = yaml.safe_load(f) + + if not isinstance(data, dict): + raise ValueError("Configuration must be a YAML dict") + + return cls.model_validate(data) + + def get_repository_config(self, repo_id: str) -> RepositoryConfig | None: + """ + Get configuration for a specific repository. + + :param repo_id: Repository ID (e.g., "BrentLab/harbison_2004") + :return: RepositoryConfig instance or None if not found + + """ + return self.repositories.get(repo_id) + + def get_property_mappings( + self, repo_id: str, config_name: str + ) -> dict[str, PropertyMapping]: + """ + Get merged property mappings for a repo/dataset combination. + + Merges repo-wide and dataset-specific mappings, with dataset-specific taking + precedence. + + :param repo_id: Repository ID + :param config_name: Dataset/config name + :return: Dict mapping property names to PropertyMapping objects + + """ + repo_config = self.get_repository_config(repo_id) + if not repo_config: + return {} + + # Start with repo-wide properties + mappings: dict[str, PropertyMapping] = dict(repo_config.properties) + + # Override with dataset-specific properties + if repo_config.dataset and config_name in repo_config.dataset: + mappings.update(repo_config.dataset[config_name]) + + return mappings diff --git a/tfbpapi/tests/conftest.py b/tfbpapi/tests/conftest.py index 47b7b94..55c1082 100644 --- a/tfbpapi/tests/conftest.py +++ b/tfbpapi/tests/conftest.py @@ -24,3 +24,1442 @@ def mock_scan_cache_dir(mock_cache_info): """Mock scan_cache_dir to return our pickled cache data.""" with patch("huggingface_hub.scan_cache_dir", return_value=mock_cache_info): yield mock_cache_info + + +# ============================================================================ +# Datainfo Fixtures (merged from tests/datainfo/conftest.py) +# ============================================================================ + + +@pytest.fixture +def sample_dataset_card_data(): + """Sample dataset card data for testing.""" + return { + "license": "mit", + "language": ["en"], + "tags": ["biology", "genomics", "yeast"], + "pretty_name": "Test Genomics Dataset", + "size_categories": ["100K log2(1.7) & " + "pval < 0.05). Note that " + "there is a slight " + "difference when " + "calculating from the data " + "provided here, I believe " + "due to a difference in " + "the way the targets are " + "parsed and filtered (some " + "ORFs that have since been " + "removed from the " + "annotations are removed). " + "I didn't investigate this " + "closely, though.", + "role": "experimental_condition", + }, + { + "name": "profile_first_published", + "dtype": "string", + "description": "citation or reference " + "indicating where this " + "expression profile was " + "first published", + "role": "experimental_condition", + }, + { + "name": "chase_notes", + "dtype": "string", + "description": "notes added during data " + "curation and parsing", + }, + ] + }, + } + ], + } diff --git a/tfbpapi/tests/datainfo/__init__.py b/tfbpapi/tests/datainfo/__init__.py deleted file mode 100644 index e38de55..0000000 --- a/tfbpapi/tests/datainfo/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Tests for the datainfo package.""" diff --git a/tfbpapi/tests/datainfo/conftest.py b/tfbpapi/tests/datainfo/conftest.py deleted file mode 100644 index 2b459ff..0000000 --- a/tfbpapi/tests/datainfo/conftest.py +++ /dev/null @@ -1,1434 +0,0 @@ -"""Shared fixtures and test data for datainfo tests.""" - -from unittest.mock import Mock - -import pytest - - -@pytest.fixture -def sample_dataset_card_data(): - """Sample dataset card data for testing.""" - return { - "license": "mit", - "language": ["en"], - "tags": ["biology", "genomics", "yeast"], - "pretty_name": "Test Genomics Dataset", - "size_categories": ["100K log2(1.7) & " - "pval < 0.05). Note that " - "there is a slight " - "difference when " - "calculating from the data " - "provided here, I believe " - "due to a difference in " - "the way the targets are " - "parsed and filtered (some " - "ORFs that have since been " - "removed from the " - "annotations are removed). " - "I didn't investigate this " - "closely, though.", - "role": "experimental_condition", - }, - { - "name": "profile_first_published", - "dtype": "string", - "description": "citation or reference " - "indicating where this " - "expression profile was " - "first published", - "role": "experimental_condition", - }, - { - "name": "chase_notes", - "dtype": "string", - "description": "notes added during data " - "curation and parsing", - }, - ] - }, - } - ], - } diff --git a/tfbpapi/tests/datainfo/test_datacard.py b/tfbpapi/tests/datainfo/test_datacard.py deleted file mode 100644 index 1e061ee..0000000 --- a/tfbpapi/tests/datainfo/test_datacard.py +++ /dev/null @@ -1,775 +0,0 @@ -"""Tests for the DataCard class.""" - -from unittest.mock import Mock, patch - -import pytest - -from tfbpapi.datainfo import DataCard -from tfbpapi.datainfo.models import DatasetType -from tfbpapi.errors import DataCardError, DataCardValidationError, HfDataFetchError - - -class TestDataCard: - """Test suite for DataCard class.""" - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_init( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - test_token, - ): - """Test DataCard initialization.""" - datacard = DataCard(test_repo_id, token=test_token) - - assert datacard.repo_id == test_repo_id - assert datacard.token == test_token - assert datacard._dataset_card is None - assert datacard._metadata_cache == {} - - # Check that fetchers were initialized - mock_card_fetcher.assert_called_once_with(token=test_token) - mock_structure_fetcher.assert_called_once_with(token=test_token) - mock_size_fetcher.assert_called_once_with(token=test_token) - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_init_without_token( - self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id - ): - """Test DataCard initialization without token.""" - datacard = DataCard(test_repo_id) - - assert datacard.repo_id == test_repo_id - assert datacard.token is None - - # Check that fetchers were initialized without token - mock_card_fetcher.assert_called_once_with(token=None) - mock_structure_fetcher.assert_called_once_with(token=None) - mock_size_fetcher.assert_called_once_with(token=None) - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_load_and_validate_card_success( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - ): - """Test successful card loading and validation.""" - # Setup mock - mock_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_fetcher_instance - mock_fetcher_instance.fetch.return_value = sample_dataset_card_data - - datacard = DataCard(test_repo_id) - - # Access dataset_card property to trigger loading - card = datacard.dataset_card - - assert card is not None - assert len(card.configs) == 4 - assert card.pretty_name == "Test Genomics Dataset" - mock_fetcher_instance.fetch.assert_called_once_with(test_repo_id) - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_load_card_no_data( - self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id - ): - """Test handling when no dataset card is found.""" - mock_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_fetcher_instance - mock_fetcher_instance.fetch.return_value = {} - - datacard = DataCard(test_repo_id) - - with pytest.raises(DataCardValidationError, match="No dataset card found"): - _ = datacard.dataset_card - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_load_card_validation_error( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - invalid_dataset_card_data, - ): - """Test handling of validation errors.""" - mock_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_fetcher_instance - mock_fetcher_instance.fetch.return_value = invalid_dataset_card_data - - datacard = DataCard(test_repo_id) - - with pytest.raises( - DataCardValidationError, match="Dataset card validation failed" - ): - _ = datacard.dataset_card - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_load_card_fetch_error( - self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id - ): - """Test handling of fetch errors.""" - mock_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_fetcher_instance - mock_fetcher_instance.fetch.side_effect = HfDataFetchError("Fetch failed") - - datacard = DataCard(test_repo_id) - - with pytest.raises(DataCardError, match="Failed to fetch dataset card"): - _ = datacard.dataset_card - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_configs_property( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - ): - """Test getting all configurations via property.""" - mock_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_fetcher_instance - mock_fetcher_instance.fetch.return_value = sample_dataset_card_data - - datacard = DataCard(test_repo_id) - configs = datacard.configs - - assert len(configs) == 4 - config_names = [config.config_name for config in configs] - assert "genomic_features" in config_names - assert "binding_data" in config_names - assert "genome_map_data" in config_names - assert "experiment_metadata" in config_names - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_get_config_by_name( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - ): - """Test getting a specific configuration by name.""" - mock_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_fetcher_instance - mock_fetcher_instance.fetch.return_value = sample_dataset_card_data - - datacard = DataCard(test_repo_id) - - config = datacard.get_config("binding_data") - assert config is not None - assert config.config_name == "binding_data" - assert config.dataset_type == DatasetType.ANNOTATED_FEATURES - - # Test non-existent config - assert datacard.get_config("nonexistent") is None - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_get_configs_by_type( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - ): - """Test getting configurations by dataset type.""" - mock_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_fetcher_instance - mock_fetcher_instance.fetch.return_value = sample_dataset_card_data - - datacard = DataCard(test_repo_id) - - # Test with enum - genomic_configs = datacard.get_configs_by_type(DatasetType.GENOMIC_FEATURES) - assert len(genomic_configs) == 1 - assert genomic_configs[0].config_name == "genomic_features" - - # Test with string - metadata_configs = datacard.get_configs_by_type("metadata") - assert len(metadata_configs) == 1 - assert metadata_configs[0].config_name == "experiment_metadata" - - # Test with genome_map type - genome_map_configs = datacard.get_configs_by_type("genome_map") - assert len(genome_map_configs) == 1 - assert genome_map_configs[0].config_name == "genome_map_data" - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_get_field_values_success( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - ): - """Test getting field values for a specific config and field.""" - mock_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_fetcher_instance - mock_fetcher_instance.fetch.return_value = sample_dataset_card_data - - datacard = DataCard(test_repo_id) - - # Test existing field - values = datacard.get_field_values("binding_data", "regulator_symbol") - # Since _extract_field_values returns empty set by default, we expect empty set - assert isinstance(values, set) - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_get_field_values_config_not_found( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - ): - """Test error when config not found.""" - mock_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_fetcher_instance - mock_fetcher_instance.fetch.return_value = sample_dataset_card_data - - datacard = DataCard(test_repo_id) - - with pytest.raises( - DataCardError, match="Configuration 'nonexistent' not found" - ): - datacard.get_field_values("nonexistent", "some_field") - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_get_field_values_field_not_found( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - ): - """Test error when field not found.""" - mock_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_fetcher_instance - mock_fetcher_instance.fetch.return_value = sample_dataset_card_data - - datacard = DataCard(test_repo_id) - - with pytest.raises(DataCardError, match="Field 'nonexistent' not found"): - datacard.get_field_values("binding_data", "nonexistent") - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_get_metadata_relationships( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - ): - """Test getting metadata relationships.""" - mock_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_fetcher_instance - mock_fetcher_instance.fetch.return_value = sample_dataset_card_data - - datacard = DataCard(test_repo_id) - - relationships = datacard.get_metadata_relationships() - - # Should have explicit relationship between binding_data and experiment_metadata - explicit_rels = [r for r in relationships if r.relationship_type == "explicit"] - assert len(explicit_rels) == 1 - assert explicit_rels[0].data_config == "binding_data" - assert explicit_rels[0].metadata_config == "experiment_metadata" - - # Should have embedded relationship for binding_data (has metadata_fields) - embedded_rels = [r for r in relationships if r.relationship_type == "embedded"] - assert len(embedded_rels) == 1 - assert embedded_rels[0].data_config == "binding_data" - assert embedded_rels[0].metadata_config == "binding_data_embedded" - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_get_repository_info_success( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - sample_repo_structure, - ): - """Test getting repository information.""" - mock_card_fetcher_instance = Mock() - mock_structure_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_card_fetcher_instance - mock_structure_fetcher.return_value = mock_structure_fetcher_instance - - mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data - mock_structure_fetcher_instance.fetch.return_value = sample_repo_structure - - datacard = DataCard(test_repo_id) - - info = datacard.get_repository_info() - - assert info["repo_id"] == test_repo_id - assert info["pretty_name"] == "Test Genomics Dataset" - assert info["license"] == "mit" - assert info["num_configs"] == 4 - assert "genomic_features" in info["dataset_types"] - assert "annotated_features" in info["dataset_types"] - assert "genome_map" in info["dataset_types"] - assert "metadata" in info["dataset_types"] - assert info["total_files"] == 5 - assert info["last_modified"] == "2023-12-01T10:30:00Z" - assert info["has_default_config"] is True - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_get_repository_info_fetch_error( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - ): - """Test getting repository info when structure fetch fails.""" - mock_card_fetcher_instance = Mock() - mock_structure_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_card_fetcher_instance - mock_structure_fetcher.return_value = mock_structure_fetcher_instance - - mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data - mock_structure_fetcher_instance.fetch.side_effect = HfDataFetchError( - "Structure fetch failed" - ) - - datacard = DataCard(test_repo_id) - - info = datacard.get_repository_info() - - assert info["repo_id"] == test_repo_id - assert info["total_files"] is None - assert info["last_modified"] is None - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_explore_config( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - ): - """Test exploring a specific configuration.""" - mock_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_fetcher_instance - mock_fetcher_instance.fetch.return_value = sample_dataset_card_data - - datacard = DataCard(test_repo_id) - - # Test regular config - config_info = datacard.explore_config("binding_data") - - assert config_info["config_name"] == "binding_data" - assert config_info["description"] == "Transcription factor binding measurements" - assert config_info["dataset_type"] == "annotated_features" - assert config_info["is_default"] is False - assert config_info["num_features"] == 4 - assert len(config_info["features"]) == 4 - assert config_info["metadata_fields"] == [ - "regulator_symbol", - "experimental_condition", - ] - - # Test config with partitioning - partitioned_config_info = datacard.explore_config("genome_map_data") - assert "partitioning" in partitioned_config_info - assert partitioned_config_info["partitioning"]["enabled"] is True - assert partitioned_config_info["partitioning"]["partition_by"] == [ - "regulator", - "experiment", - ] - - # Test metadata config with applies_to - metadata_config_info = datacard.explore_config("experiment_metadata") - assert metadata_config_info["applies_to"] == ["binding_data"] - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_explore_config_not_found( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - ): - """Test exploring a non-existent configuration.""" - mock_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_fetcher_instance - mock_fetcher_instance.fetch.return_value = sample_dataset_card_data - - datacard = DataCard(test_repo_id) - - with pytest.raises( - DataCardError, match="Configuration 'nonexistent' not found" - ): - datacard.explore_config("nonexistent") - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_summary( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - sample_repo_structure, - ): - """Test getting a summary of the dataset.""" - mock_card_fetcher_instance = Mock() - mock_structure_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_card_fetcher_instance - mock_structure_fetcher.return_value = mock_structure_fetcher_instance - - mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data - mock_structure_fetcher_instance.fetch.return_value = sample_repo_structure - - datacard = DataCard(test_repo_id) - - summary = datacard.summary() - - assert "Dataset: Test Genomics Dataset" in summary - assert f"Repository: {test_repo_id}" in summary - assert "License: mit" in summary - assert "Configurations: 4" in summary - assert "genomic_features" in summary - assert "binding_data" in summary - assert "genome_map_data" in summary - assert "experiment_metadata" in summary - assert "(default)" in summary # genomic_features is marked as default - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_extract_partition_values( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - ): - """Test extracting partition values.""" - mock_card_fetcher_instance = Mock() - mock_structure_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_card_fetcher_instance - mock_structure_fetcher.return_value = mock_structure_fetcher_instance - - mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data - mock_structure_fetcher_instance.get_partition_values.return_value = [ - "TF1", - "TF2", - "TF3", - ] - - datacard = DataCard(test_repo_id) - - # Get the genome_map_data config which has partitioning enabled - config = datacard.get_config("genome_map_data") - assert config is not None - assert config.dataset_info.partitioning.enabled is True - - values = datacard._extract_partition_values(config, "regulator") - assert values == {"TF1", "TF2", "TF3"} - mock_structure_fetcher_instance.get_partition_values.assert_called_once_with( - test_repo_id, "regulator" - ) - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_extract_partition_values_no_partitioning( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - ): - """Test extracting partition values when partitioning is disabled.""" - mock_card_fetcher_instance = Mock() - mock_structure_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_card_fetcher_instance - mock_structure_fetcher.return_value = mock_structure_fetcher_instance - - mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data - - datacard = DataCard(test_repo_id) - - # Get a config without partitioning - config = datacard.get_config("genomic_features") - assert config is not None - assert config.dataset_info.partitioning is None - - values = datacard._extract_partition_values(config, "some_field") - assert values == set() - mock_structure_fetcher_instance.get_partition_values.assert_not_called() - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_extract_partition_values_field_not_in_partitions( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - ): - """Test extracting partition values when field is not a partition column.""" - mock_card_fetcher_instance = Mock() - mock_structure_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_card_fetcher_instance - mock_structure_fetcher.return_value = mock_structure_fetcher_instance - - mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data - - datacard = DataCard(test_repo_id) - - # Get the genome_map_data config which has partitioning enabled - config = datacard.get_config("genome_map_data") - assert config is not None - - # Try to extract values for a field that's not in partition_by - values = datacard._extract_partition_values(config, "not_a_partition_field") - assert values == set() - mock_structure_fetcher_instance.get_partition_values.assert_not_called() - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_extract_partition_values_fetch_error( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - ): - """Test extracting partition values when fetch fails.""" - mock_card_fetcher_instance = Mock() - mock_structure_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_card_fetcher_instance - mock_structure_fetcher.return_value = mock_structure_fetcher_instance - - mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data - mock_structure_fetcher_instance.get_partition_values.side_effect = ( - HfDataFetchError("Fetch failed") - ) - - datacard = DataCard(test_repo_id) - - config = datacard.get_config("genome_map_data") - values = datacard._extract_partition_values(config, "regulator") - - # Should return empty set on error - assert values == set() - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_get_field_attribute( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - ): - """Test extracting specific attributes from field definitions.""" - # Create sample card data with condition definitions - card_data = { - "configs": [ - { - "config_name": "test_config", - "description": "Test configuration", - "dataset_type": "annotated_features", - "data_files": [{"split": "train", "path": "test.parquet"}], - "dataset_info": { - "features": [ - { - "name": "condition", - "dtype": "string", - "description": "Experimental condition", - "role": "experimental_condition", - "definitions": { - "YPD": { - "media": { - "name": "YPD", - "carbon_source": [ - { - "compound": "D-glucose", - "concentration_percent": 2, - } - ], - "nitrogen_source": [ - { - "compound": "yeast_extract", - "concentration_percent": 1, - }, - { - "compound": "peptone", - "concentration_percent": 2, - }, - ], - }, - "temperature_celsius": 30, - }, - "HEAT": { - "media": { - "name": "YPD", - "carbon_source": [ - { - "compound": "D-glucose", - "concentration_percent": 2, - } - ], - }, - "temperature_celsius": 37, - }, - "SM": { - "media": { - "name": "synthetic_complete", - "carbon_source": "unspecified", - "nitrogen_source": "unspecified", - } - }, - }, - } - ] - }, - } - ] - } - - mock_card_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_card_fetcher_instance - mock_card_fetcher_instance.fetch.return_value = card_data - - datacard = DataCard(test_repo_id) - - # Test extracting media attribute - media_specs = datacard.get_field_attribute( - "test_config", "condition", "media" - ) - - assert "YPD" in media_specs - assert "HEAT" in media_specs - assert "SM" in media_specs - - # Check YPD media specification - assert media_specs["YPD"]["name"] == "YPD" - assert len(media_specs["YPD"]["carbon_source"]) == 1 - assert media_specs["YPD"]["carbon_source"][0]["compound"] == "D-glucose" - assert len(media_specs["YPD"]["nitrogen_source"]) == 2 - - # Check HEAT media specification - assert media_specs["HEAT"]["name"] == "YPD" - assert len(media_specs["HEAT"]["carbon_source"]) == 1 - - # Check SM media specification - assert media_specs["SM"]["name"] == "synthetic_complete" - assert media_specs["SM"]["carbon_source"] == "unspecified" - - # Test extracting temperature attribute - temp_specs = datacard.get_field_attribute( - "test_config", "condition", "temperature_celsius" - ) - - assert temp_specs["YPD"] == 30 - assert temp_specs["HEAT"] == 37 - assert temp_specs["SM"] == "unspecified" # SM doesn't have temperature - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_get_field_attribute_invalid_config( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - minimal_dataset_card_data, - ): - """Test get_field_attribute with invalid config name.""" - mock_card_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_card_fetcher_instance - mock_card_fetcher_instance.fetch.return_value = minimal_dataset_card_data - - datacard = DataCard(test_repo_id) - - with pytest.raises(DataCardError, match="Configuration 'invalid' not found"): - datacard.get_field_attribute("invalid", "condition", "media") - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_get_field_attribute_invalid_field( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - minimal_dataset_card_data, - ): - """Test get_field_attribute with invalid field name.""" - mock_card_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_card_fetcher_instance - mock_card_fetcher_instance.fetch.return_value = minimal_dataset_card_data - - datacard = DataCard(test_repo_id) - - with pytest.raises( - DataCardError, match="Field 'invalid_field' not found in config" - ): - datacard.get_field_attribute("test_config", "invalid_field", "media") diff --git a/tfbpapi/tests/datainfo/test_metadata_builder.py b/tfbpapi/tests/datainfo/test_metadata_builder.py deleted file mode 100644 index cee3b71..0000000 --- a/tfbpapi/tests/datainfo/test_metadata_builder.py +++ /dev/null @@ -1,375 +0,0 @@ -""" -Tests for MetadataBuilder. - -Tests normalization logic, metadata extraction, and builder functionality. - -""" - -from pathlib import Path - -import pytest -import yaml - -from tfbpapi.datainfo.metadata_builder import ( - MetadataBuilder, - get_nested_value, - normalize_value, -) - - -class TestGetNestedValue: - """Test get_nested_value function.""" - - def test_simple_path(self): - """Test simple one-level path.""" - data = {"temperature": 30} - assert get_nested_value(data, "temperature") == 30 - - def test_nested_path(self): - """Test multi-level nested path.""" - data = {"media": {"carbon_source": "D-glucose"}} - assert get_nested_value(data, "media.carbon_source") == "D-glucose" - - def test_deeply_nested_path(self): - """Test deeply nested path.""" - data = {"level1": {"level2": {"level3": {"value": 42}}}} - assert get_nested_value(data, "level1.level2.level3.value") == 42 - - def test_missing_key(self): - """Test that missing keys return None.""" - data = {"temperature": 30} - assert get_nested_value(data, "pressure") is None - - def test_missing_intermediate_key(self): - """Test that missing intermediate keys return None.""" - data = {"media": {"temperature": 30}} - assert get_nested_value(data, "media.carbon_source.compound") is None - - def test_non_dict_input(self): - """Test that non-dict input returns None.""" - assert get_nested_value("not a dict", "path") is None - assert get_nested_value(None, "path") is None - - -class TestGetNestedValueListExtraction: - """Test get_nested_value list extraction functionality.""" - - def test_extract_from_list_of_dicts(self): - """Test extracting property from list of dicts.""" - data = { - "media": { - "carbon_source": [ - {"compound": "D-glucose", "concentration_percent": 2}, - {"compound": "D-galactose", "concentration_percent": 1}, - ] - } - } - result = get_nested_value(data, "media.carbon_source.compound") - assert result == ["D-glucose", "D-galactose"] - - def test_extract_concentration_from_list(self): - """Test extracting numeric property from list of dicts.""" - data = { - "media": { - "carbon_source": [ - {"compound": "D-glucose", "concentration_percent": 2}, - {"compound": "D-galactose", "concentration_percent": 1}, - ] - } - } - result = get_nested_value(data, "media.carbon_source.concentration_percent") - assert result == [2, 1] - - def test_get_list_itself(self): - """Test getting the list without extracting a property.""" - data = { - "media": { - "carbon_source": [ - {"compound": "D-glucose"}, - {"compound": "D-galactose"}, - ] - } - } - result = get_nested_value(data, "media.carbon_source") - expected = [ - {"compound": "D-glucose"}, - {"compound": "D-galactose"}, - ] - assert result == expected - - def test_extract_from_single_item_list(self): - """Test extracting from list with single item.""" - data = { - "media": { - "carbon_source": [{"compound": "D-glucose"}] - } - } - result = get_nested_value(data, "media.carbon_source.compound") - assert result == ["D-glucose"] - - def test_extract_missing_property_from_list(self): - """Test extracting non-existent property from list items.""" - data = { - "media": { - "carbon_source": [ - {"compound": "D-glucose"}, - {"compound": "D-galactose"}, - ] - } - } - result = get_nested_value(data, "media.carbon_source.missing_key") - assert result is None - - def test_nested_list_extraction(self): - """Test extracting from nested structures with lists.""" - data = { - "level1": { - "level2": [ - {"level3": {"value": "a"}}, - {"level3": {"value": "b"}}, - ] - } - } - result = get_nested_value(data, "level1.level2.level3.value") - assert result == ["a", "b"] - - -class TestNormalizeValue: - """Test normalize_value function.""" - - def test_normalize_with_alias_match(self): - """Test normalization when value matches an alias.""" - aliases = {"glucose": ["D-glucose", "dextrose"]} - assert normalize_value("D-glucose", aliases) == "glucose" - assert normalize_value("dextrose", aliases) == "glucose" - - def test_normalize_case_insensitive(self): - """Test that matching is case-insensitive.""" - aliases = {"glucose": ["D-glucose"]} - assert normalize_value("d-glucose", aliases) == "glucose" - assert normalize_value("D-GLUCOSE", aliases) == "glucose" - assert normalize_value("D-Glucose", aliases) == "glucose" - - def test_normalize_no_match_passthrough(self): - """Test pass-through when no alias matches.""" - aliases = {"glucose": ["D-glucose"]} - assert normalize_value("maltose", aliases) == "maltose" - assert normalize_value("galactose", aliases) == "galactose" - - def test_normalize_no_aliases(self): - """Test pass-through when no aliases provided.""" - assert normalize_value("D-glucose", None) == "D-glucose" - assert normalize_value("maltose", None) == "maltose" - - def test_normalize_empty_aliases(self): - """Test pass-through when empty aliases dict.""" - assert normalize_value("D-glucose", {}) == "D-glucose" - - def test_normalize_numeric_values(self): - """Test normalization with numeric actual values.""" - aliases = {"thirty": [30, "30"]} - assert normalize_value(30, aliases) == "thirty" - assert normalize_value("30", aliases) == "thirty" - - def test_normalize_numeric_passthrough(self): - """Test that unmatched numeric values pass through as strings.""" - aliases = {"thirty": [30]} - assert normalize_value(37, aliases) == "37" - - def test_normalize_boolean_values(self): - """Test normalization with boolean values.""" - aliases = {"present": [True, "true", "True"]} - assert normalize_value(True, aliases) == "present" - # Note: bool to str conversion makes this "True" - assert normalize_value("true", aliases) == "present" - - def test_normalize_multiple_aliases(self): - """Test with multiple alias mappings.""" - aliases = { - "glucose": ["D-glucose", "dextrose"], - "galactose": ["D-galactose", "Galactose"], - } - assert normalize_value("D-glucose", aliases) == "glucose" - assert normalize_value("DEXTROSE", aliases) == "glucose" - assert normalize_value("d-galactose", aliases) == "galactose" - assert normalize_value("Galactose", aliases) == "galactose" - # No match - assert normalize_value("maltose", aliases) == "maltose" - - -@pytest.fixture -def write_config(tmp_path): - """Fixture to write YAML config files.""" - - def _write(config_data): - config_path = tmp_path / "config.yaml" - with open(config_path, "w") as f: - yaml.dump(config_data, f) - return config_path - - return _write - - -class TestMetadataBuilder: - """Test MetadataBuilder class.""" - - def test_init_with_aliases(self, write_config): - """Test initialization with factor aliases.""" - config = { - "factor_aliases": {"carbon_source": {"glucose": ["D-glucose", "dextrose"]}}, - "BrentLab/test": { - "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} - }, - } - config_file = write_config(config) - builder = MetadataBuilder(config_file) - - assert len(builder.factor_aliases) == 1 - assert "carbon_source" in builder.factor_aliases - assert "glucose" in builder.factor_aliases["carbon_source"] - - def test_init_without_aliases(self, write_config): - """Test initialization without factor aliases.""" - config = { - "BrentLab/test": { - "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} - } - } - config_file = write_config(config) - builder = MetadataBuilder(config_file) - - assert builder.factor_aliases == {} - - def test_invalid_mode(self, write_config): - """Test that invalid mode raises ValueError.""" - config = { - "BrentLab/test": { - "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} - } - } - config_file = write_config(config) - builder = MetadataBuilder(config_file) - - with pytest.raises(ValueError) as exc_info: - builder.build_metadata( - repos=[("BrentLab/test", "test")], mode="invalid_mode" - ) - assert "Invalid mode" in str(exc_info.value) - - def test_build_metadata_missing_repo_config(self, write_config): - """Test handling of missing repository configuration.""" - config = { - "BrentLab/test": { - "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} - } - } - config_file = write_config(config) - builder = MetadataBuilder(config_file) - - results = builder.build_metadata( - repos=[("BrentLab/missing", "dataset")], mode="conditions" - ) - - assert "BrentLab/missing" in results - assert "error" in results["BrentLab/missing"] - assert "No property mappings" in results["BrentLab/missing"]["error"] - - def test_repr(self, write_config): - """Test string representation.""" - config = { - "factor_aliases": { - "carbon_source": {"glucose": ["D-glucose"]}, - "temperature": {"thirty": [30]}, - }, - "BrentLab/test1": { - "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} - }, - "BrentLab/test2": { - "dataset": {"test": {"temperature": {"path": "temperature_celsius"}}} - }, - } - config_file = write_config(config) - builder = MetadataBuilder(config_file) - - repr_str = repr(builder) - assert "MetadataBuilder" in repr_str - assert "2 properties" in repr_str - assert "2 repositories" in repr_str - - def test_repr_no_aliases(self, write_config): - """Test string representation with no aliases.""" - config = { - "BrentLab/test": { - "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} - } - } - config_file = write_config(config) - builder = MetadataBuilder(config_file) - - repr_str = repr(builder) - assert "MetadataBuilder" in repr_str - assert "0 properties" in repr_str - assert "1 repositories" in repr_str - - -class TestMetadataBuilderIntegration: - """Integration tests with real datacards (if available).""" - - def test_build_metadata_conditions_mode(self, write_config): - """Test building metadata in conditions mode.""" - # This is a minimal test that doesn't require actual datacards - # In practice, you'd use real datacards from HuggingFace - config = { - "factor_aliases": {"carbon_source": {"glucose": ["D-glucose"]}}, - "BrentLab/test": { - "dataset": { - "test": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source", - } - } - } - }, - } - config_file = write_config(config) - builder = MetadataBuilder(config_file) - - # This would fail without a real datacard, but tests the structure - # In actual usage, you'd mock DataCard or use real repos - # For now, just verify the method exists and can be called - assert hasattr(builder, "build_metadata") - assert callable(builder.build_metadata) - - def test_get_property_mappings(self, write_config): - """Test _get_property_mappings method.""" - config = { - "BrentLab/test": { - "temperature": {"path": "temperature_celsius"}, - "dataset": { - "test_dataset": {"carbon_source": {"path": "media.carbon_source"}} - }, - } - } - config_file = write_config(config) - builder = MetadataBuilder(config_file) - - mappings = builder._get_property_mappings("BrentLab/test", "test_dataset") - - assert "temperature" in mappings - assert "carbon_source" in mappings - assert mappings["temperature"].path == "temperature_celsius" - assert mappings["carbon_source"].path == "media.carbon_source" - - def test_get_property_mappings_missing_repo(self, write_config): - """Test _get_property_mappings with missing repository.""" - config = { - "BrentLab/test": { - "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} - } - } - config_file = write_config(config) - builder = MetadataBuilder(config_file) - - mappings = builder._get_property_mappings("BrentLab/missing", "dataset") - assert mappings == {} diff --git a/tfbpapi/tests/datainfo/test_metadata_manager.py b/tfbpapi/tests/datainfo/test_metadata_manager.py deleted file mode 100644 index ad03c5a..0000000 --- a/tfbpapi/tests/datainfo/test_metadata_manager.py +++ /dev/null @@ -1,243 +0,0 @@ -"""Tests for MetadataManager.""" - -import pandas as pd -import pytest - -from tfbpapi.datainfo import DataCard, MetadataManager - - -class TestMetadataManagerBasic: - """Basic tests for MetadataManager instantiation and structure.""" - - def test_instantiation(self): - """Test MetadataManager can be instantiated.""" - mgr = MetadataManager() - assert mgr is not None - assert hasattr(mgr, "_conn") - assert hasattr(mgr, "_registered_datasets") - assert hasattr(mgr, "_view_names") - - def test_instantiation_with_cache(self): - """Test MetadataManager with cache enabled.""" - from pathlib import Path - - cache_dir = Path("/tmp/metadata_cache") - mgr = MetadataManager(cache_dir=cache_dir, cache=True) - assert mgr._cache_dir == cache_dir - assert mgr._cache_enabled is True - - def test_get_active_configs_empty(self): - """Test get_active_configs with no registered datasets.""" - mgr = MetadataManager() - configs = mgr.get_active_configs() - assert configs == [] - - def test_get_summary_empty(self): - """Test get_summary with no registered datasets.""" - mgr = MetadataManager() - summary = mgr.get_summary() - assert isinstance(summary, pd.DataFrame) - assert len(summary) == 0 - - -class TestDataCardExtractMetadataSchema: - """Tests for DataCard.extract_metadata_schema method.""" - - def test_extract_metadata_schema_structure(self): - """Test that extract_metadata_schema returns correct structure.""" - # Note: This test uses a mock since we need a real datacard - # In actual testing, we'd use a real HuggingFace dataset - # For now, we just test the structure - - # Mock test - verify the method exists - from tfbpapi.datainfo.datacard import DataCard - - assert hasattr(DataCard, "extract_metadata_schema") - - -class TestMetadataManagerHelpers: - """Tests for MetadataManager helper methods.""" - - def test_sanitize_view_name(self): - """Test view name sanitization.""" - mgr = MetadataManager() - view_name = mgr._sanitize_view_name("BrentLab/dataset-name", "config_name") - assert view_name == "BrentLab_dataset_name_config_name_metadata" - assert " " not in view_name - assert "/" not in view_name - assert "-" not in view_name - - def test_flatten_condition_definition_empty(self): - """Test flattening empty definition.""" - mgr = MetadataManager() - result = mgr._flatten_condition_definition({}) - assert isinstance(result, dict) - assert len(result) == 0 - - def test_flatten_condition_definition_with_media(self): - """Test flattening definition with media name.""" - mgr = MetadataManager() - definition = { - "media": { - "name": "YPD", - } - } - result = mgr._flatten_condition_definition(definition) - assert result["growth_media"] == "YPD" - - -class TestComponentSeparators: - """Tests for separator conventions.""" - - def test_separator_constants(self): - """Test that separator constants are defined.""" - from tfbpapi.datainfo.metadata_manager import COMPONENT_SEPARATORS - - assert COMPONENT_SEPARATORS["type_value"] == ":" - assert COMPONENT_SEPARATORS["value_conc"] == "@" - assert COMPONENT_SEPARATORS["components"] == ";" - assert COMPONENT_SEPARATORS["types"] == "|" - - -class TestThreeLevelConditionHierarchy: - """Tests for three-level experimental condition hierarchy support.""" - - def test_flatten_experimental_conditions_empty(self): - """Test flattening empty ExperimentalConditions.""" - mgr = MetadataManager() - - # Create a simple mock object with no conditions - class MockExpConditions: - strain_background = None - - result = mgr._flatten_experimental_conditions(MockExpConditions()) - assert isinstance(result, dict) - assert len(result) == 0 - - def test_flatten_experimental_conditions_with_temperature(self): - """Test flattening conditions with temperature.""" - mgr = MetadataManager() - - class MockExpConditions: - temperature_celsius = 30 - strain_background = None - - result = mgr._flatten_experimental_conditions(MockExpConditions()) - assert result["temperature_celsius"] == 30 - - def test_flatten_experimental_conditions_with_cultivation_method(self): - """Test flattening conditions with cultivation method.""" - mgr = MetadataManager() - - class MockExpConditions: - temperature_celsius = None - cultivation_method = "chemostat" - strain_background = None - - result = mgr._flatten_experimental_conditions(MockExpConditions()) - assert result["cultivation_method"] == "chemostat" - - def test_flatten_experimental_conditions_with_media(self): - """Test flattening conditions with media information.""" - mgr = MetadataManager() - - class MockMedia: - name = "minimal" - - class MockExpConditions: - media = MockMedia() - strain_background = None - - result = mgr._flatten_experimental_conditions(MockExpConditions()) - assert result["growth_media"] == "minimal" - - def test_flatten_experimental_conditions_with_strain_background(self): - """Test flattening conditions with strain background.""" - mgr = MetadataManager() - - class MockExpConditions: - strain_background = "BY4741" - - result = mgr._flatten_experimental_conditions(MockExpConditions()) - assert result["strain_background"] == "BY4741" - - def test_datacard_extract_schema_includes_top_level_conditions(self): - """Test that extract_metadata_schema includes top_level_conditions.""" - # This test needs to use real datacards with experimental conditions - # For now, we verify the keys exist in the schema - from unittest.mock import MagicMock, Mock - - # Create a mock DataCard with experimental conditions - mock_card = Mock() - mock_card.repo_id = "test/repo" - - # Mock dataset card with experimental conditions - mock_dataset_card = Mock() - mock_dataset_card.experimental_conditions = Mock() # Non-None - mock_card.dataset_card = mock_dataset_card - - # Mock config with no config-level conditions - mock_config = Mock() - mock_config.experimental_conditions = None - mock_config.dataset_info = Mock() - mock_config.dataset_info.features = [] - - mock_card.get_config = Mock(return_value=mock_config) - - # Call the method via the actual DataCard class - from tfbpapi.datainfo.datacard import DataCard - - schema = DataCard.extract_metadata_schema(mock_card, "test_config") - - # Verify top_level_conditions is in schema - assert "top_level_conditions" in schema - assert "config_level_conditions" in schema - - def test_datacard_extract_schema_includes_config_level_conditions(self): - """Test that extract_metadata_schema includes config_level_conditions.""" - from unittest.mock import Mock - - # Create a mock DataCard - mock_card = Mock() - mock_card.repo_id = "test/repo" - - # Mock dataset card with NO repo-level conditions - mock_dataset_card = Mock() - mock_dataset_card.experimental_conditions = None - mock_card.dataset_card = mock_dataset_card - - # Mock config WITH config-level conditions - mock_config = Mock() - mock_config.experimental_conditions = Mock() # Non-None - mock_config.dataset_info = Mock() - mock_config.dataset_info.features = [] - - mock_card.get_config = Mock(return_value=mock_config) - - # Call the method - from tfbpapi.datainfo.datacard import DataCard - - schema = DataCard.extract_metadata_schema(mock_card, "test_config") - - # Verify config_level_conditions is populated - assert schema["config_level_conditions"] is not None - - -# Integration test placeholder -class TestMetadataManagerIntegration: - """Integration tests for MetadataManager (require real HF datasets).""" - - @pytest.mark.skip(reason="Requires real HuggingFace dataset access") - def test_register_real_dataset(self): - """Test registering a real HuggingFace dataset.""" - # This would test with a real dataset like: - # mgr = MetadataManager() - # mgr.register("BrentLab/some_real_dataset") - # assert len(mgr.get_active_configs()) > 0 - pass - - @pytest.mark.skip(reason="Requires real HuggingFace dataset access") - def test_query_across_datasets(self): - """Test querying across multiple datasets.""" - # This would test cross-dataset queries - pass diff --git a/tfbpapi/tests/datainfo/example_datacards.py b/tfbpapi/tests/example_datacards.py similarity index 100% rename from tfbpapi/tests/datainfo/example_datacards.py rename to tfbpapi/tests/example_datacards.py diff --git a/tfbpapi/tests/datainfo/huggingface_collection_datacards.txt b/tfbpapi/tests/huggingface_collection_datacards.txt similarity index 100% rename from tfbpapi/tests/datainfo/huggingface_collection_datacards.txt rename to tfbpapi/tests/huggingface_collection_datacards.txt diff --git a/tfbpapi/tests/test_HfQueryAPI.py b/tfbpapi/tests/test_HfQueryAPI.py deleted file mode 100644 index 02ad783..0000000 --- a/tfbpapi/tests/test_HfQueryAPI.py +++ /dev/null @@ -1,859 +0,0 @@ -"""Comprehensive tests for HfQueryAPI class.""" - -import logging -from unittest.mock import MagicMock, Mock, patch - -import duckdb -import pandas as pd -import pytest - -from tfbpapi.datainfo.models import MetadataRelationship -from tfbpapi.errors import InvalidFilterFieldError -from tfbpapi.HfQueryAPI import HfQueryAPI - - -class TestHfQueryAPIInit: - """Test HfQueryAPI initialization.""" - - def test_init_basic(self): - """Test basic initialization.""" - conn = duckdb.connect(":memory:") - repo_id = "test/repo" - - with patch("tfbpapi.HfQueryAPI.HfCacheManager.__init__", return_value=None): - api = HfQueryAPI(repo_id, duckdb_conn=conn) - # Manually set properties that would be set by parent - api.repo_id = repo_id - api.duckdb_conn = conn - api._duckdb_conn = conn - api.logger = logging.getLogger("HfQueryAPI") - - assert api.repo_id == repo_id - assert api.duckdb_conn == conn - assert api._duckdb_conn == conn - - def test_init_with_all_params(self): - """Test initialization with all parameters.""" - import tempfile - - conn = duckdb.connect(":memory:") - repo_id = "test/repo" - token = "test_token" - - with tempfile.TemporaryDirectory() as cache_dir: - with patch("tfbpapi.HfQueryAPI.HfCacheManager.__init__", return_value=None): - api = HfQueryAPI( - repo_id=repo_id, - repo_type="model", - token=token, - cache_dir=cache_dir, - duckdb_conn=conn, - ) - # Manually set properties that would be set by parent - api.repo_id = repo_id - api.duckdb_conn = conn - api._duckdb_conn = conn - - assert api.repo_id == repo_id - assert api.duckdb_conn == conn - assert api._duckdb_conn == conn - assert api.repo_type == "model" - - -class TestHfQueryAPIHelpers: - """Test HfQueryAPI helper methods.""" - - @pytest.fixture - def mock_api(self): - """Create a mock HfQueryAPI instance.""" - conn = duckdb.connect(":memory:") - with patch("tfbpapi.HfQueryAPI.HfCacheManager.__init__", return_value=None): - api = HfQueryAPI("test/repo", duckdb_conn=conn) - api.duckdb_conn = conn - api._duckdb_conn = conn - api.logger = logging.getLogger("test") - return api - - def test_get_explicit_metadata(self, mock_api): - """Test _get_explicit_metadata helper.""" - # Mock config and return value - mock_config = Mock() - mock_config.config_name = "test_config" - - # Create mock DataFrame - expected_df = pd.DataFrame({"field1": [1, 2], "field2": ["a", "b"]}) - - # Replace the duckdb_conn with a mock - mock_conn = Mock() - mock_result = Mock() - mock_result.fetchdf.return_value = expected_df - mock_conn.execute.return_value = mock_result - mock_api.duckdb_conn = mock_conn - - result = mock_api._get_explicit_metadata(mock_config, "test_table") - - # Verify SQL was executed correctly - mock_conn.execute.assert_called_once_with("SELECT * FROM test_table") - pd.testing.assert_frame_equal(result, expected_df) - - def test_get_embedded_metadata(self, mock_api): - """Test _get_embedded_metadata helper.""" - # Mock config with metadata fields - mock_config = Mock() - mock_config.config_name = "test_config" - mock_config.metadata_fields = ["time", "mechanism"] - - expected_df = pd.DataFrame( - {"time": [15, 30], "mechanism": ["ZEV", "ZREV"], "count": [100, 50]} - ) - - # Replace the duckdb_conn with a mock - mock_conn = Mock() - mock_result = Mock() - mock_result.fetchdf.return_value = expected_df - mock_conn.execute.return_value = mock_result - mock_api.duckdb_conn = mock_conn - - result = mock_api._get_embedded_metadata(mock_config, "test_table") - - # Verify correct SQL was generated - expected_sql = """ - SELECT DISTINCT time, mechanism, COUNT(*) as count - FROM test_table - WHERE time IS NOT NULL AND mechanism IS NOT NULL - GROUP BY time, mechanism - ORDER BY count DESC - """ - mock_conn.execute.assert_called_once() - actual_sql = mock_conn.execute.call_args[0][0] - # Normalize whitespace for comparison - assert " ".join(actual_sql.split()) == " ".join(expected_sql.split()) - pd.testing.assert_frame_equal(result, expected_df) - - def test_get_embedded_metadata_no_fields(self, mock_api): - """Test _get_embedded_metadata with no metadata fields.""" - mock_config = Mock() - mock_config.config_name = "test_config" - mock_config.metadata_fields = None - - with pytest.raises(ValueError, match="has no metadata fields"): - mock_api._get_embedded_metadata(mock_config, "test_table") - - def test_extract_fields_from_sql_simple(self, mock_api): - """Test _extract_fields_from_sql with simple SQL.""" - sql = "time = 15 AND mechanism = 'ZEV'" - fields = mock_api._extract_fields_from_sql(sql) - - assert "time" in fields - assert "mechanism" in fields - # Note: The current regex may pick up quoted strings as identifiers - # This is a limitation we accept for simplicity - assert "15" not in fields # Should not include numeric literals - - def test_extract_fields_from_sql_complex(self, mock_api): - """Test _extract_fields_from_sql with complex SQL.""" - sql = "field1 IN (1, 2, 3) AND field2 IS NOT NULL AND field3 LIKE '%test%'" - fields = mock_api._extract_fields_from_sql(sql) - - assert "field1" in fields - assert "field2" in fields - assert "field3" in fields - assert "NULL" not in fields # SQL keyword should be excluded - assert "LIKE" not in fields # SQL keyword should be excluded - - def test_extract_fields_from_sql_quoted(self, mock_api): - """Test _extract_fields_from_sql with quoted identifiers.""" - sql = "\"quoted_field\" = 1 AND 'another_field' > 5" - fields = mock_api._extract_fields_from_sql(sql) - - assert "quoted_field" in fields - assert "another_field" in fields - - def test_validate_metadata_fields_success(self, mock_api): - """Test _validate_metadata_fields with valid fields.""" - # Mock get_metadata to return DataFrame with expected columns - metadata_df = pd.DataFrame( - {"time": [15, 30], "mechanism": ["ZEV", "ZREV"], "restriction": ["P", "A"]} - ) - - with patch.object(mock_api, "get_metadata", return_value=metadata_df): - # Should not raise any exception - mock_api._validate_metadata_fields("test_config", ["time", "mechanism"]) - - def test_validate_metadata_fields_invalid(self, mock_api): - """Test _validate_metadata_fields with invalid fields.""" - metadata_df = pd.DataFrame({"time": [15, 30], "mechanism": ["ZEV", "ZREV"]}) - - with patch.object(mock_api, "get_metadata", return_value=metadata_df): - with pytest.raises(InvalidFilterFieldError) as exc_info: - mock_api._validate_metadata_fields( - "test_config", ["invalid_field", "time"] - ) - - error = exc_info.value - assert "invalid_field" in error.invalid_fields - assert "time" not in error.invalid_fields - assert "time" in error.available_fields - assert error.config_name == "test_config" - - def test_validate_metadata_fields_empty_metadata(self, mock_api): - """Test _validate_metadata_fields with empty metadata.""" - empty_df = pd.DataFrame() - - with patch.object(mock_api, "get_metadata", return_value=empty_df): - with pytest.raises(InvalidFilterFieldError) as exc_info: - mock_api._validate_metadata_fields("test_config", ["any_field"]) - - error = exc_info.value - assert error.invalid_fields == ["any_field"] - assert error.available_fields == [] - - def test_validate_metadata_fields_empty_list(self, mock_api): - """Test _validate_metadata_fields with empty field list.""" - # Should not call get_metadata or raise any exception - with patch.object(mock_api, "get_metadata") as mock_get_metadata: - mock_api._validate_metadata_fields("test_config", []) - mock_get_metadata.assert_not_called() - - -class TestHfQueryAPIMainMethods: - """Test HfQueryAPI main methods.""" - - @pytest.fixture - def mock_api(self): - """Create a mock HfQueryAPI instance.""" - conn = duckdb.connect(":memory:") - with patch("tfbpapi.HfQueryAPI.HfCacheManager.__init__", return_value=None): - api = HfQueryAPI("test/repo", duckdb_conn=conn) - # Set up all necessary attributes - api.repo_id = "test/repo" - api.duckdb_conn = conn - api._duckdb_conn = conn - api.logger = logging.getLogger("test") - api._table_filters = {} - - # Set up the internal dataset card attribute - api._dataset_card = Mock() - return api - - def test_get_metadata_explicit_config(self, mock_api): - """Test get_metadata with explicit metadata config.""" - # Setup mock configurations - explicit_config = Mock() - explicit_config.config_name = "metadata_config" - explicit_config.applies_to = None # This config doesn't apply to others - - # Mock the metadata relationship - relationship = MetadataRelationship( - data_config="some_data", - metadata_config="metadata_config", - relationship_type="explicit", - ) - - # Mock the config loading and table setup - mock_config_result = {"success": True, "table_name": "test_metadata_table"} - - expected_df = pd.DataFrame( - {"sample_id": ["sample1", "sample2"], "condition": ["ctrl", "treatment"]} - ) - - with ( - patch.object( - mock_api, "get_metadata_relationships", return_value=[relationship] - ), - patch.object(mock_api, "get_config", return_value=explicit_config), - patch.object( - mock_api, "_get_metadata_for_config", return_value=mock_config_result - ), - patch.object(mock_api, "_get_explicit_metadata", return_value=expected_df), - ): - - result = mock_api.get_metadata("metadata_config") - - mock_api._get_explicit_metadata.assert_called_once_with( - explicit_config, "test_metadata_table" - ) - pd.testing.assert_frame_equal(result, expected_df) - - def test_get_metadata_embedded_config(self, mock_api): - """Test get_metadata with embedded metadata config.""" - # Setup mock configurations - embedded_config = Mock() - embedded_config.config_name = "data_config" - embedded_config.metadata_fields = ["time", "mechanism"] - - # Mock the metadata relationship - relationship = MetadataRelationship( - data_config="data_config", - metadata_config="data_config_embedded", - relationship_type="embedded", - ) - mock_config_result = {"success": True, "table_name": "test_data_table"} - - expected_df = pd.DataFrame( - {"time": [15, 30], "mechanism": ["ZEV", "ZREV"], "count": [100, 50]} - ) - - with ( - patch.object( - mock_api, "get_metadata_relationships", return_value=[relationship] - ), - patch.object(mock_api, "get_config", return_value=embedded_config), - patch.object( - mock_api, "_get_metadata_for_config", return_value=mock_config_result - ), - patch.object(mock_api, "_get_embedded_metadata", return_value=expected_df), - ): - - result = mock_api.get_metadata("data_config") - - mock_api._get_embedded_metadata.assert_called_once_with( - embedded_config, "test_data_table" - ) - pd.testing.assert_frame_equal(result, expected_df) - - def test_get_metadata_applied_config(self, mock_api): - """Test get_metadata with config that has metadata applied to it.""" - # Setup a metadata config that applies to another config - metadata_config = Mock() - metadata_config.config_name = "experiment_metadata" - metadata_config.applies_to = ["data_config", "other_data_config"] - - # Mock the metadata relationship - relationship = MetadataRelationship( - data_config="data_config", - metadata_config="experiment_metadata", - relationship_type="explicit", - ) - mock_config_result = {"success": True, "table_name": "test_metadata_table"} - - expected_df = pd.DataFrame( - {"experiment_id": ["exp1", "exp2"], "condition": ["ctrl", "treatment"]} - ) - - with ( - patch.object( - mock_api, "get_metadata_relationships", return_value=[relationship] - ), - patch.object(mock_api, "get_config", return_value=metadata_config), - patch.object( - mock_api, "_get_metadata_for_config", return_value=mock_config_result - ), - patch.object(mock_api, "_get_explicit_metadata", return_value=expected_df), - ): - - # Request metadata for a config that appears in applies_to - result = mock_api.get_metadata("data_config") - - # Should return the metadata from the config that applies to it - mock_api._get_explicit_metadata.assert_called_once_with( - metadata_config, "test_metadata_table" - ) - pd.testing.assert_frame_equal(result, expected_df) - - def test_get_metadata_config_not_found(self, mock_api): - """Test get_metadata with non-existent config when other configs exist.""" - # Setup a relationship for a different config - relationship = MetadataRelationship( - data_config="other_data", - metadata_config="other_config", - relationship_type="explicit", - ) - with patch.object( - mock_api, "get_metadata_relationships", return_value=[relationship] - ): - with pytest.raises(ValueError, match="Config 'nonexistent' not found"): - mock_api.get_metadata("nonexistent") - - def test_get_metadata_no_metadata_sources(self, mock_api): - """Test get_metadata when no metadata sources are available.""" - with patch.object(mock_api, "get_metadata_relationships", return_value=[]): - result = mock_api.get_metadata("any_config") - assert result.empty - - def test_get_metadata_load_failure(self, mock_api): - """Test get_metadata when config loading fails.""" - config = Mock() - config.config_name = "test_config" - config.applies_to = None - - # Mock the metadata relationship - relationship = MetadataRelationship( - data_config="some_data", - metadata_config="test_config", - relationship_type="explicit", - ) - mock_config_result = {"success": False} - - with ( - patch.object( - mock_api, "get_metadata_relationships", return_value=[relationship] - ), - patch.object(mock_api, "get_config", return_value=config), - patch.object( - mock_api, "_get_metadata_for_config", return_value=mock_config_result - ), - ): - with pytest.raises(RuntimeError, match="Failed to load data for config"): - mock_api.get_metadata("test_config") - - def test_set_filter_valid_fields(self, mock_api): - """Test set_filter with valid field names.""" - with patch.object(mock_api, "_validate_metadata_fields") as mock_validate: - mock_api.set_filter("test_config", time=15, mechanism="ZEV") - - mock_validate.assert_called_once_with("test_config", ["time", "mechanism"]) - assert ( - mock_api._table_filters["test_config"] - == "time = 15 AND mechanism = 'ZEV'" - ) - - def test_set_filter_clear_on_empty(self, mock_api): - """Test set_filter clears filter when no kwargs provided.""" - # Set an initial filter - mock_api._table_filters["test_config"] = "existing_filter" - - with patch.object(mock_api, "clear_filter") as mock_clear: - mock_api.set_filter("test_config") - mock_clear.assert_called_once_with("test_config") - - def test_set_filter_various_types(self, mock_api): - """Test set_filter with different value types.""" - with patch.object(mock_api, "_validate_metadata_fields"): - mock_api.set_filter( - "test_config", - string_field="text", - numeric_field=42, - null_field=None, - bool_field=True, - ) - - expected = ( - "string_field = 'text' AND numeric_field = 42 AND " - "null_field IS NULL AND bool_field = True" - ) - assert mock_api._table_filters["test_config"] == expected - - def test_set_sql_filter_with_validation(self, mock_api): - """Test set_sql_filter with field validation enabled.""" - sql_where = "time IN (15, 30) AND mechanism = 'ZEV'" - - with ( - patch.object( - mock_api, "_extract_fields_from_sql", return_value=["time", "mechanism"] - ) as mock_extract, - patch.object(mock_api, "_validate_metadata_fields") as mock_validate, - ): - - mock_api.set_sql_filter("test_config", sql_where) - - mock_extract.assert_called_once_with(sql_where) - mock_validate.assert_called_once_with("test_config", ["time", "mechanism"]) - assert mock_api._table_filters["test_config"] == sql_where - - def test_set_sql_filter_without_validation(self, mock_api): - """Test set_sql_filter with field validation disabled.""" - sql_where = "complex_function(field1, field2) > 0" - - with ( - patch.object(mock_api, "_extract_fields_from_sql") as mock_extract, - patch.object(mock_api, "_validate_metadata_fields") as mock_validate, - ): - - mock_api.set_sql_filter("test_config", sql_where, validate_fields=False) - - mock_extract.assert_not_called() - mock_validate.assert_not_called() - assert mock_api._table_filters["test_config"] == sql_where - - def test_set_sql_filter_clear_on_empty(self, mock_api): - """Test set_sql_filter clears filter when empty SQL provided.""" - mock_api._table_filters["test_config"] = "existing_filter" - - with patch.object(mock_api, "clear_filter") as mock_clear: - mock_api.set_sql_filter("test_config", "") - mock_clear.assert_called_once_with("test_config") - - def test_clear_filter(self, mock_api): - """Test clear_filter removes stored filter.""" - mock_api._table_filters["test_config"] = "some_filter" - - mock_api.clear_filter("test_config") - - assert "test_config" not in mock_api._table_filters - - def test_clear_filter_nonexistent(self, mock_api): - """Test clear_filter with non-existent config.""" - # Should not raise an error - mock_api.clear_filter("nonexistent_config") - - def test_get_current_filter_exists(self, mock_api): - """Test get_current_filter returns existing filter.""" - expected_filter = "time = 15" - mock_api._table_filters["test_config"] = expected_filter - - result = mock_api.get_current_filter("test_config") - assert result == expected_filter - - def test_get_current_filter_not_exists(self, mock_api): - """Test get_current_filter returns None for non-existent filter.""" - result = mock_api.get_current_filter("nonexistent_config") - assert result is None - - -class TestHfQueryAPIErrorHandling: - """Test HfQueryAPI error handling and edge cases.""" - - @pytest.fixture - def mock_api(self): - """Create a mock HfQueryAPI instance.""" - conn = duckdb.connect(":memory:") - with patch("tfbpapi.HfQueryAPI.HfCacheManager.__init__", return_value=None): - api = HfQueryAPI("test/repo", duckdb_conn=conn) - # Set up all necessary attributes - api.repo_id = "test/repo" - api.duckdb_conn = conn - api._duckdb_conn = conn - api.logger = logging.getLogger("test") - api._table_filters = {} - - # Set up the internal dataset card attribute - api._dataset_card = Mock() - return api - - def test_set_filter_validation_error_propagates(self, mock_api): - """Test that InvalidFilterFieldError from validation propagates.""" - error = InvalidFilterFieldError( - config_name="test_config", - invalid_fields=["invalid_field"], - available_fields=["valid_field"], - ) - - with patch.object(mock_api, "_validate_metadata_fields", side_effect=error): - with pytest.raises(InvalidFilterFieldError) as exc_info: - mock_api.set_filter("test_config", invalid_field="value") - - assert exc_info.value.config_name == "test_config" - assert "invalid_field" in exc_info.value.invalid_fields - - def test_set_sql_filter_validation_error_propagates(self, mock_api): - """Test that InvalidFilterFieldError from SQL validation propagates.""" - error = InvalidFilterFieldError( - config_name="test_config", - invalid_fields=["nonexistent"], - available_fields=["time", "mechanism"], - ) - - with ( - patch.object( - mock_api, "_extract_fields_from_sql", return_value=["nonexistent"] - ), - patch.object(mock_api, "_validate_metadata_fields", side_effect=error), - ): - - with pytest.raises(InvalidFilterFieldError) as exc_info: - mock_api.set_sql_filter("test_config", "nonexistent = 1") - - assert exc_info.value.config_name == "test_config" - - def test_get_metadata_query_error_propagates(self, mock_api): - """Test that query errors in get_metadata propagate.""" - config = Mock() - config.config_name = "test_config" - config.applies_to = None - - # Mock the metadata relationship - relationship = MetadataRelationship( - data_config="some_data", - metadata_config="test_config", - relationship_type="explicit", - ) - - mock_config_result = {"success": True, "table_name": "test_table"} - - with ( - patch.object( - mock_api, "get_metadata_relationships", return_value=[relationship] - ), - patch.object(mock_api, "get_config", return_value=config), - patch.object( - mock_api, "_get_metadata_for_config", return_value=mock_config_result - ), - patch.object( - mock_api, - "_get_explicit_metadata", - side_effect=Exception("Query failed"), - ), - ): - - with pytest.raises(Exception, match="Query failed"): - mock_api.get_metadata("test_config") - - def test_validate_metadata_fields_get_metadata_error_logged(self, mock_api): - """Test that non-InvalidFilterFieldError exceptions in validation are logged.""" - with ( - patch.object( - mock_api, "get_metadata", side_effect=Exception("Network error") - ), - patch.object(mock_api.logger, "warning") as mock_warning, - ): - - # Should not raise, but should log warning - mock_api._validate_metadata_fields("test_config", ["field1"]) - - mock_warning.assert_called_once() - assert "Could not validate filter fields" in mock_warning.call_args[0][0] - - def test_extract_fields_edge_cases(self, mock_api): - """Test _extract_fields_from_sql with various edge cases.""" - # Empty string - assert mock_api._extract_fields_from_sql("") == [] - - # Only SQL keywords - fields = mock_api._extract_fields_from_sql("AND OR NOT NULL") - assert len(fields) == 0 - - # Mixed quotes and operators - fields = mock_api._extract_fields_from_sql( - '"field1" >= "field2" AND field3 <= field4' - ) - assert "field1" in fields - assert "field2" in fields - assert "field3" in fields - assert "field4" in fields - - # Function calls should be excluded - fields = mock_api._extract_fields_from_sql( - "UPPER(field1) = 'VALUE' AND field2 > MAX(field3)" - ) - assert "field1" in fields - assert "field2" in fields - assert "field3" in fields - assert "UPPER" not in fields - assert "MAX" not in fields - - # Numeric literals should be excluded - fields = mock_api._extract_fields_from_sql("field1 = 123.45 AND field2 = -67") - assert "field1" in fields - assert "field2" in fields - assert "123" not in fields - assert "45" not in fields - assert "67" not in fields - - def test_extract_fields_complex_sql(self, mock_api): - """Test _extract_fields_from_sql with complex SQL patterns.""" - complex_sql = """ - (field1 IN (1, 2, 3) OR field2 IS NOT NULL) - AND field3 LIKE '%pattern%' - AND "quoted field" BETWEEN 'start' AND 'end' - AND field4 > COALESCE(field5, 0) - """ - - fields = mock_api._extract_fields_from_sql(complex_sql) - - expected_fields = [ - "field1", - "field2", - "field3", - "quoted field", - "field4", - "field5", - ] - for field in expected_fields: - assert field in fields, f"Field '{field}' should be extracted from SQL" - - # These should not be extracted (SQL keywords and function names) - unwanted = ["COALESCE", "LIKE", "BETWEEN", "NULL"] - for unwanted_item in unwanted: - assert ( - unwanted_item not in fields - ), f"'{unwanted_item}' should not be extracted" - - # String literals should not be extracted - string_literals = ["start", "end", "pattern"] - for literal in string_literals: - assert ( - literal not in fields - ), f"String literal '{literal}' should not be extracted" - - def test_extract_fields_in_clause_with_quoted_values(self, mock_api): - """Test _extract_fields_from_sql with IN clause containing quoted values.""" - # This is the exact pattern from the user's error case - gene_ids = ["YNL199C", "YDL106C", "YLR098C", "YNR009W", "YLR176C"] - regulator_clause = "(" + ", ".join(f"'{gene_id}'" for gene_id in gene_ids) + ")" - - sql = f""" - time = 15 - AND mechanism = 'ZEV' - AND restriction = 'P' - AND regulator_locus_tag IN {regulator_clause} - """ - - fields = mock_api._extract_fields_from_sql(sql) - - # Should extract field names - expected_fields = ["time", "mechanism", "restriction", "regulator_locus_tag"] - for field in expected_fields: - assert field in fields, f"Field '{field}' should be extracted from SQL" - - # Should NOT extract string literals or gene IDs - unwanted_values = ["ZEV", "P"] + gene_ids - for value in unwanted_values: - assert ( - value not in fields - ), f"String literal '{value}' should not be extracted as field" - - # Should NOT extract numeric literals - assert "15" not in fields - - def test_extract_fields_various_comparison_operators(self, mock_api): - """Test _extract_fields_from_sql with various comparison operators and string - values.""" - sql = """ - field1 = 'value1' AND field2 != 'value2' - AND field3 <> 'value3' AND field4 > 'value4' - AND field5 <= 'value5' AND field6 >= 'value6' - AND field7 LIKE 'pattern%' AND field8 NOT LIKE '%other%' - """ - - fields = mock_api._extract_fields_from_sql(sql) - - # Should extract field names - expected_fields = [ - "field1", - "field2", - "field3", - "field4", - "field5", - "field6", - "field7", - "field8", - ] - for field in expected_fields: - assert field in fields, f"Field '{field}' should be extracted from SQL" - - # Should NOT extract string values - unwanted_values = [ - "value1", - "value2", - "value3", - "value4", - "value5", - "value6", - "pattern", - "other", - ] - for value in unwanted_values: - assert ( - value not in fields - ), f"String literal '{value}' should not be extracted as field" - - def test_extract_fields_between_clause(self, mock_api): - """Test _extract_fields_from_sql with BETWEEN clause containing string - values.""" - sql = ( - "field1 BETWEEN 'start_value' AND 'end_value' AND field2 BETWEEN 10 AND 20" - ) - - fields = mock_api._extract_fields_from_sql(sql) - - # Should extract field names - assert "field1" in fields - assert "field2" in fields - - # Should NOT extract BETWEEN values - assert "start_value" not in fields - assert "end_value" not in fields - assert "10" not in fields - assert "20" not in fields - - def test_get_metadata_table_name_missing(self, mock_api): - """Test get_metadata when table_name is missing from config result.""" - config = Mock() - config.config_name = "test_config" - config.applies_to = None - - # Mock the metadata relationship - relationship = MetadataRelationship( - data_config="some_data", - metadata_config="test_config", - relationship_type="explicit", - ) - # Success but no table name - mock_config_result = {"success": True, "table_name": None} - - with ( - patch.object( - mock_api, "get_metadata_relationships", return_value=[relationship] - ), - patch.object(mock_api, "get_config", return_value=config), - patch.object( - mock_api, "_get_metadata_for_config", return_value=mock_config_result - ), - ): - with pytest.raises(RuntimeError, match="No table name for config"): - mock_api.get_metadata("test_config") - - def test_filter_methods_whitespace_handling(self, mock_api): - """Test that filter methods handle whitespace correctly.""" - # set_sql_filter should strip whitespace - with ( - patch.object(mock_api, "_extract_fields_from_sql", return_value=[]), - patch.object(mock_api, "_validate_metadata_fields"), - ): - - mock_api.set_sql_filter("test_config", " field = 1 ") - assert mock_api._table_filters["test_config"] == "field = 1" - - # Empty/whitespace-only SQL should clear filter - with patch.object(mock_api, "clear_filter") as mock_clear: - mock_api.set_sql_filter("test_config", " ") - mock_clear.assert_called_once_with("test_config") - - -class TestInvalidFilterFieldError: - """Test the InvalidFilterFieldError exception.""" - - def test_error_message_formatting(self): - """Test that error message is formatted correctly.""" - error = InvalidFilterFieldError( - config_name="test_config", - invalid_fields=["field1", "field2"], - available_fields=["valid1", "valid2", "valid3"], - ) - - message = str(error) - assert "test_config" in message - assert "'field1'" in message - assert "'field2'" in message - assert "Available fields:" in message - assert "valid1" in message - - def test_error_with_no_available_fields(self): - """Test error message when no fields are available.""" - error = InvalidFilterFieldError( - config_name="empty_config", - invalid_fields=["any_field"], - available_fields=[], - ) - - message = str(error) - assert "No fields available" in message - - def test_error_attributes(self): - """Test that error attributes are set correctly.""" - invalid_fields = ["bad1", "bad2"] - available_fields = ["good1", "good2"] - - error = InvalidFilterFieldError( - config_name="test_config", - invalid_fields=invalid_fields, - available_fields=available_fields, - ) - - assert error.config_name == "test_config" - assert error.invalid_fields == invalid_fields - assert error.available_fields == sorted(available_fields) - assert error.details["config_name"] == "test_config" - assert error.details["invalid_fields"] == invalid_fields - assert error.details["available_fields"] == sorted(available_fields) diff --git a/tfbpapi/tests/test_HfRankResponse.py b/tfbpapi/tests/test_HfRankResponse.py deleted file mode 100644 index 922af97..0000000 --- a/tfbpapi/tests/test_HfRankResponse.py +++ /dev/null @@ -1,505 +0,0 @@ -import tempfile -from pathlib import Path -from unittest.mock import MagicMock, patch - -import pandas as pd -import pytest - -from tfbpapi.HfRankResponse import HfRankResponse -from tfbpapi.IncrementalAnalysisDB import IncrementalAnalysisDB - - -@pytest.fixture -def temp_db_path(): - """Create temporary database path for testing.""" - with tempfile.TemporaryDirectory() as temp_dir: - db_path = Path(temp_dir) / "test.db" - yield str(db_path) - - -@pytest.fixture -def analysis_db(temp_db_path): - """Create IncrementalAnalysisDB instance for testing.""" - return IncrementalAnalysisDB(temp_db_path) - - -@pytest.fixture -def rank_response(analysis_db): - """Create HfRankResponse instance for testing.""" - return HfRankResponse(analysis_db) - - -@pytest.fixture -def mock_ranking_api(): - """Create mock ranking API.""" - api = MagicMock() - api.get_table_filter.return_value = None - api.query.return_value = pd.DataFrame( - { - "regulator_locus_tag": ["REG1", "REG2", "REG3"], - } - ) - api._duckdb_conn.execute.return_value.fetchdf.return_value = pd.DataFrame( - { - "regulator_locus_tag": ["REG1", "REG1", "REG2", "REG2", "REG3"], - "target_locus_tag": ["TGT1", "TGT2", "TGT1", "TGT3", "TGT1"], - "binding_score": [10.5, 8.2, 9.1, 7.8, 6.5], - } - ) - return api - - -@pytest.fixture -def mock_response_api(): - """Create mock response API.""" - api = MagicMock() - api.get_table_filter.return_value = None - api._duckdb_conn.execute.return_value.fetchdf.return_value = pd.DataFrame( - { - "regulator_locus_tag": ["REG1", "REG1", "REG2"], - "target_locus_tag": ["TGT1", "TGT2", "TGT1"], - "log2fc": [2.1, -1.5, 1.8], - } - ) - return api - - -@pytest.fixture -def sample_computed_data(): - """Create sample computed rank response data.""" - return pd.DataFrame( - { - "regulator_locus_tag": ["REG1", "REG1", "REG1", "REG2", "REG2", "REG3"], - "target_locus_tag": ["TGT1", "TGT2", "TGT3", "TGT1", "TGT2", "TGT1"], - "binding_score": [10.5, 8.2, 7.1, 9.1, 7.8, 6.5], - "log2fc": [2.1, -1.5, None, 1.8, None, None], - "responsive": [1, 1, 0, 1, 0, 0], - "bin_label": [5, 5, 10, 5, 10, 5], - "cumulative_responsive": [2, 2, 2, 1, 1, 0], - } - ) - - -class TestHfRankResponse: - - def test_init(self, analysis_db): - """Test HfRankResponse initialization.""" - rr = HfRankResponse(analysis_db) - assert rr.db == analysis_db - assert rr.logger is not None - - def test_get_comparisons_empty(self, rank_response): - """Test getting comparisons when none exist.""" - comparisons = rank_response.get_comparisons() - assert comparisons == [] - - def test_get_comparisons_with_data(self, rank_response, sample_computed_data): - """Test getting comparisons with existing data.""" - # Add some test data - rank_response.db.append_results( - sample_computed_data, "rank_response_test_comparison_1" - ) - rank_response.db.append_results( - sample_computed_data, "rank_response_test_comparison_2" - ) - - comparisons = rank_response.get_comparisons() - assert "test_comparison_1" in comparisons - assert "test_comparison_2" in comparisons - assert len(comparisons) == 2 - - @patch("tfbpapi.HfRankResponse.duckdb.connect") - def test_compute_new_comparison( - self, - mock_duckdb_connect, - rank_response, - mock_ranking_api, - mock_response_api, - sample_computed_data, - ): - """Test computing a new comparison.""" - # Mock the temporary connection - mock_temp_conn = MagicMock() - mock_duckdb_connect.return_value = mock_temp_conn - mock_temp_conn.execute.return_value.fetchdf.return_value = sample_computed_data - - result = rank_response.compute( - ranking_api=mock_ranking_api, - response_api=mock_response_api, - ranking_table="binding_data", - response_table="expression_data", - ranking_score_column="binding_score", - response_column="log2fc", - comparison_id="test_comp", - bin_size=5, - ) - - # Verify APIs were called correctly - mock_ranking_api._ensure_dataset_loaded.assert_called() - mock_response_api._ensure_dataset_loaded.assert_called() - - # Verify data was stored - assert rank_response.db.table_exists("rank_response_test_comp") - - # Verify result - assert not result.empty - assert "regulator_locus_tag" in result.columns - - def test_compute_auto_generated_comparison_id( - self, rank_response, mock_ranking_api, mock_response_api - ): - """Test compute with auto-generated comparison ID.""" - with patch("tfbpapi.HfRankResponse.duckdb.connect") as mock_duckdb: - mock_temp_conn = MagicMock() - mock_duckdb.return_value = mock_temp_conn - mock_temp_conn.execute.return_value.fetchdf.return_value = pd.DataFrame( - { - "regulator_locus_tag": ["REG1"], - "target_locus_tag": ["TGT1"], - "binding_score": [10.0], - "log2fc": [2.0], - "responsive": [1], - "bin_label": [5], - "cumulative_responsive": [1], - } - ) - - rank_response.compute( - ranking_api=mock_ranking_api, - response_api=mock_response_api, - ranking_table="binding_table", - response_table="expression_table", - ranking_score_column="binding_score", - response_column="log2fc", - ) - - # Should create table with auto-generated ID - assert rank_response.db.table_exists( - "rank_response_binding_table_vs_expression_table" - ) - - def test_compute_incremental_update( - self, rank_response, mock_ranking_api, mock_response_api, sample_computed_data - ): - """Test incremental computation with existing data.""" - # First, add some existing data - rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") - - # Mock APIs to return new regulators - mock_ranking_api.query.return_value = pd.DataFrame( - { - "regulator_locus_tag": ["REG1", "REG2", "REG3", "REG4"], # REG4 is new - } - ) - - with patch("tfbpapi.HfRankResponse.duckdb.connect") as mock_duckdb: - mock_temp_conn = MagicMock() - mock_duckdb.return_value = mock_temp_conn - # Return data for new regulator only - mock_temp_conn.execute.return_value.fetchdf.return_value = pd.DataFrame( - { - "regulator_locus_tag": ["REG4"], - "target_locus_tag": ["TGT1"], - "binding_score": [5.0], - "log2fc": [1.0], - "responsive": [1], - "bin_label": [5], - "cumulative_responsive": [1], - } - ) - - result = rank_response.compute( - ranking_api=mock_ranking_api, - response_api=mock_response_api, - ranking_table="binding_data", - response_table="expression_data", - ranking_score_column="binding_score", - response_column="log2fc", - comparison_id="test_comp", - ) - - # Should have data for all regulators now - regulators = set(result["regulator_locus_tag"].unique()) - assert "REG1" in regulators - assert "REG4" in regulators - - def test_get_bin_summary(self, rank_response, sample_computed_data): - """Test generating bin-level summary.""" - # Add test data - rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") - - summary = rank_response.get_bin_summary("test_comp") - - assert not summary.empty - assert "regulator_locus_tag" in summary.columns - assert "bin_label" in summary.columns - assert "records_in_bin" in summary.columns - assert "responsive_in_bin" in summary.columns - assert "cumulative_responsive" in summary.columns - assert "response_rate" in summary.columns - - def test_get_bin_summary_with_filter(self, rank_response, sample_computed_data): - """Test bin summary with regulator filter.""" - rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") - - summary = rank_response.get_bin_summary( - "test_comp", regulators_filter=["REG1", "REG2"] - ) - - assert not summary.empty - regulators = set(summary["regulator_locus_tag"].unique()) - assert regulators.issubset({"REG1", "REG2"}) - assert "REG3" not in regulators - - def test_get_regulator_summary(self, rank_response, sample_computed_data): - """Test generating regulator-level summary.""" - rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") - - summary = rank_response.get_regulator_summary("test_comp") - - assert not summary.empty - assert "regulator_locus_tag" in summary.columns - assert "total_targets" in summary.columns - assert "total_responsive" in summary.columns - assert "overall_response_rate" in summary.columns - assert "top5_response_rate" in summary.columns - assert "top10_response_rate" in summary.columns - assert "top20_response_rate" in summary.columns - - def test_get_regulator_summary_with_max_bin( - self, rank_response, sample_computed_data - ): - """Test regulator summary with max bin limit.""" - rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") - - summary = rank_response.get_regulator_summary("test_comp", max_bin_label=5) - - assert not summary.empty - # Should only include data from bins <= 5 - - def test_summarize_bin_type(self, rank_response, sample_computed_data): - """Test summarize method with bin type.""" - rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") - - summary = rank_response.summarize("test_comp", summary_type="bin") - - assert not summary.empty - assert "bin_label" in summary.columns - - def test_summarize_regulator_type(self, rank_response, sample_computed_data): - """Test summarize method with regulator type.""" - rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") - - summary = rank_response.summarize("test_comp", summary_type="regulator") - - assert not summary.empty - assert "overall_response_rate" in summary.columns - - def test_summarize_invalid_type(self, rank_response, sample_computed_data): - """Test summarize with invalid summary type.""" - rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") - - with pytest.raises(ValueError, match="Unknown summary type"): - rank_response.summarize("test_comp", summary_type="invalid") - - def test_query_method(self, rank_response, sample_computed_data): - """Test direct SQL query method.""" - rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") - - result = rank_response.query( - "SELECT COUNT(*) as count FROM rank_response_test_comp" - ) - - assert not result.empty - assert result.iloc[0]["count"] == len(sample_computed_data) - - def test_get_comparison_data(self, rank_response, sample_computed_data): - """Test getting raw comparison data.""" - rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") - - data = rank_response.get_comparison_data("test_comp") - - assert not data.empty - assert len(data) == len(sample_computed_data) - - def test_get_comparison_data_with_filter(self, rank_response, sample_computed_data): - """Test getting comparison data with regulator filter.""" - rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") - - data = rank_response.get_comparison_data("test_comp", regulator_filter=["REG1"]) - - assert not data.empty - assert all(data["regulator_locus_tag"] == "REG1") - - def test_get_comparison_data_with_limit(self, rank_response, sample_computed_data): - """Test getting comparison data with limit.""" - rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") - - data = rank_response.get_comparison_data("test_comp", limit=3) - - assert len(data) == 3 - - def test_compare_across_datasets(self, rank_response, sample_computed_data): - """Test comparing across multiple datasets.""" - # Add data for multiple comparisons - rank_response.db.append_results(sample_computed_data, "rank_response_comp1") - rank_response.db.append_results(sample_computed_data, "rank_response_comp2") - - comparison = rank_response.compare_across_datasets(["comp1", "comp2"]) - - assert not comparison.empty - assert "regulator_locus_tag" in comparison.columns - # Should have columns for each comparison and metric - assert any("overall_response_rate_" in col for col in comparison.columns) - - def test_compare_across_datasets_empty(self, rank_response): - """Test comparing across datasets with no data.""" - comparison = rank_response.compare_across_datasets([]) - - assert comparison.empty - - def test_compare_across_datasets_custom_metrics( - self, rank_response, sample_computed_data - ): - """Test comparing with custom metric columns.""" - rank_response.db.append_results(sample_computed_data, "rank_response_comp1") - - comparison = rank_response.compare_across_datasets( - ["comp1"], metric_columns=["top5_response_rate"] - ) - - assert not comparison.empty - assert any("top5_response_rate_" in col for col in comparison.columns) - - def test_nonexistent_comparison_error(self, rank_response): - """Test error handling for nonexistent comparisons.""" - with pytest.raises(ValueError, match="No results found"): - rank_response.get_comparison_data("nonexistent") - - with pytest.raises(ValueError, match="does not exist"): - rank_response.get_bin_summary("nonexistent") - - with pytest.raises(ValueError, match="does not exist"): - rank_response.get_regulator_summary("nonexistent") - - def test_compute_with_existing_filters( - self, rank_response, mock_ranking_api, mock_response_api - ): - """Test compute when APIs already have filters set.""" - # Set existing filters - mock_ranking_api.get_table_filter.return_value = "existing_filter = 'value'" - mock_response_api.get_table_filter.return_value = "another_filter = 'value'" - - with patch("tfbpapi.HfRankResponse.duckdb.connect") as mock_duckdb: - mock_temp_conn = MagicMock() - mock_duckdb.return_value = mock_temp_conn - mock_temp_conn.execute.return_value.fetchdf.return_value = pd.DataFrame( - { - "regulator_locus_tag": ["REG1"], - "target_locus_tag": ["TGT1"], - "binding_score": [10.0], - "log2fc": [2.0], - "responsive": [1], - "bin_label": [5], - "cumulative_responsive": [1], - } - ) - - rank_response.compute( - ranking_api=mock_ranking_api, - response_api=mock_response_api, - ranking_table="binding_data", - response_table="expression_data", - ranking_score_column="binding_score", - response_column="log2fc", - comparison_id="with_filters", - ) - - # Verify filters were combined with AND - calls = mock_ranking_api.set_table_filter.call_args_list - assert len(calls) > 0 - combined_filter = calls[0][0][1] # Second argument of first call - assert "existing_filter = 'value'" in combined_filter - assert "AND" in combined_filter - - @patch("tfbpapi.HfRankResponse.logging.getLogger") - def test_logging_setup(self, mock_get_logger, analysis_db): - """Test that logging is properly configured.""" - mock_logger = MagicMock() - mock_get_logger.return_value = mock_logger - - rr = HfRankResponse(analysis_db) - - mock_get_logger.assert_called_once_with("tfbpapi.HfRankResponse") - assert rr.logger == mock_logger - - def test_compute_with_custom_responsive_condition( - self, rank_response, mock_ranking_api, mock_response_api - ): - """Test compute with custom responsive condition.""" - with patch("tfbpapi.HfRankResponse.duckdb.connect") as mock_duckdb: - mock_temp_conn = MagicMock() - mock_duckdb.return_value = mock_temp_conn - mock_temp_conn.execute.return_value.fetchdf.return_value = pd.DataFrame( - { - "regulator_locus_tag": ["REG1"], - "target_locus_tag": ["TGT1"], - "binding_score": [10.0], - "log2fc": [2.0], - "responsive": [1], - "bin_label": [5], - "cumulative_responsive": [1], - } - ) - - rank_response.compute( - ranking_api=mock_ranking_api, - response_api=mock_response_api, - ranking_table="binding_data", - response_table="expression_data", - ranking_score_column="binding_score", - response_column="log2fc", - comparison_id="custom_responsive", - responsive_condition="log2fc IS NOT NULL AND log2fc != 0", - ) - - # Verify the SQL contains the custom responsive condition - sql_calls = mock_temp_conn.execute.call_args_list - assert len(sql_calls) > 0 - executed_sql = sql_calls[0][0][0] - assert "b.log2fc IS NOT NULL AND b.log2fc != 0" in executed_sql - - def test_compute_with_default_responsive_condition( - self, rank_response, mock_ranking_api, mock_response_api - ): - """Test compute with default responsive condition (IS NOT NULL).""" - with patch("tfbpapi.HfRankResponse.duckdb.connect") as mock_duckdb: - mock_temp_conn = MagicMock() - mock_duckdb.return_value = mock_temp_conn - mock_temp_conn.execute.return_value.fetchdf.return_value = pd.DataFrame( - { - "regulator_locus_tag": ["REG1"], - "target_locus_tag": ["TGT1"], - "binding_score": [10.0], - "log2fc": [2.0], - "responsive": [1], - "bin_label": [5], - "cumulative_responsive": [1], - } - ) - - rank_response.compute( - ranking_api=mock_ranking_api, - response_api=mock_response_api, - ranking_table="binding_data", - response_table="expression_data", - ranking_score_column="binding_score", - response_column="log2fc", - comparison_id="default_responsive", - ) - - # Verify the SQL contains the default responsive condition - sql_calls = mock_temp_conn.execute.call_args_list - assert len(sql_calls) > 0 - executed_sql = sql_calls[0][0][0] - assert "b.log2fc IS NOT NULL" in executed_sql diff --git a/tfbpapi/tests/test_IncrementalAnalysisDB.py b/tfbpapi/tests/test_IncrementalAnalysisDB.py deleted file mode 100644 index 0fbb1e1..0000000 --- a/tfbpapi/tests/test_IncrementalAnalysisDB.py +++ /dev/null @@ -1,341 +0,0 @@ -import json -import tempfile -from pathlib import Path -from unittest.mock import MagicMock, patch - -import pandas as pd -import pytest - -from tfbpapi.IncrementalAnalysisDB import IncrementalAnalysisDB - - -@pytest.fixture -def temp_db_path(): - """Create temporary database path for testing.""" - with tempfile.TemporaryDirectory() as temp_dir: - db_path = Path(temp_dir) / "test.db" - yield str(db_path) - - -@pytest.fixture -def sample_dataframe(): - """Create sample DataFrame for testing.""" - return pd.DataFrame( - {"id": [1, 2, 3], "name": ["A", "B", "C"], "value": [10.5, 20.3, 15.7]} - ) - - -@pytest.fixture -def analysis_db(temp_db_path): - """Create IncrementalAnalysisDB instance for testing.""" - return IncrementalAnalysisDB(temp_db_path) - - -class TestIncrementalAnalysisDB: - - def test_init_creates_database_and_metadata_table(self, temp_db_path): - """Test that initialization creates the database file and metadata table.""" - db = IncrementalAnalysisDB(temp_db_path) - - # Check database file exists - assert Path(temp_db_path).exists() - - # Check metadata table exists - result = db.conn.execute( - """ - SELECT table_name FROM information_schema.tables - WHERE table_name='analysis_metadata' AND table_schema='main' - """ - ).fetchall() - assert len(result) == 1 - - db.conn.close() - - def test_init_creates_parent_directories(self): - """Test that initialization creates parent directories if they don't exist.""" - with tempfile.TemporaryDirectory() as temp_dir: - nested_path = Path(temp_dir) / "nested" / "path" / "test.db" - db = IncrementalAnalysisDB(str(nested_path)) - - assert nested_path.parent.exists() - assert nested_path.exists() - db.conn.close() - - def test_append_results_new_table(self, analysis_db, sample_dataframe): - """Test appending results to a new table.""" - records_added = analysis_db.append_results( - new_results=sample_dataframe, - table_name="test_table", - analysis_type="test_analysis", - parameters={"param1": "value1"}, - description="Test description", - ) - - assert records_added == 3 - - # Check data was inserted - result = analysis_db.conn.execute("SELECT * FROM test_table").fetchdf() - pd.testing.assert_frame_equal(result, sample_dataframe) - - # Check metadata was inserted - metadata = analysis_db.conn.execute( - """ - SELECT * FROM analysis_metadata WHERE table_name = 'test_table' - """ - ).fetchdf() - - assert len(metadata) == 1 - assert metadata.iloc[0]["analysis_type"] == "test_analysis" - assert metadata.iloc[0]["total_records"] == 3 - assert json.loads(metadata.iloc[0]["parameters"]) == {"param1": "value1"} - assert metadata.iloc[0]["description"] == "Test description" - - def test_append_results_existing_table(self, analysis_db, sample_dataframe): - """Test appending results to an existing table.""" - # First append - analysis_db.append_results(sample_dataframe, "test_table") - - # Second append with new data - new_data = pd.DataFrame( - {"id": [4, 5], "name": ["D", "E"], "value": [25.1, 30.9]} - ) - - records_added = analysis_db.append_results(new_data, "test_table") - assert records_added == 2 - - # Check total records - result = analysis_db.conn.execute( - "SELECT COUNT(*) as count FROM test_table" - ).fetchdf() - assert result.iloc[0]["count"] == 5 - - # Check metadata updated - metadata = analysis_db.conn.execute( - """ - SELECT total_records FROM analysis_metadata WHERE table_name = 'test_table' - """ - ).fetchdf() - assert metadata.iloc[0]["total_records"] == 5 - - def test_append_results_with_deduplication(self, analysis_db): - """Test appending results with deduplication.""" - initial_data = pd.DataFrame( - {"id": [1, 2, 3], "name": ["A", "B", "C"], "value": [10.5, 20.3, 15.7]} - ) - - analysis_db.append_results(initial_data, "test_table", deduplicate_on=["id"]) - - # Append data with some duplicates - new_data = pd.DataFrame( - { - "id": [2, 3, 4], # 2 and 3 are duplicates - "name": ["B2", "C2", "D"], - "value": [20.3, 15.7, 25.1], - } - ) - - records_added = analysis_db.append_results( - new_data, "test_table", deduplicate_on=["id"] - ) - - # Only record with id=4 should be added - assert records_added == 1 - - # Check total records - result = analysis_db.conn.execute( - "SELECT COUNT(*) as count FROM test_table" - ).fetchdf() - assert result.iloc[0]["count"] == 4 - - def test_update_results(self, analysis_db, sample_dataframe): - """Test updating existing results.""" - # First insert data - analysis_db.append_results(sample_dataframe, "test_table") - - # Update data - updated_data = pd.DataFrame( - {"id": [1, 2], "name": ["A_updated", "B_updated"], "value": [100.5, 200.3]} - ) - - records_updated = analysis_db.update_results( - updated_data, "test_table", key_columns=["id"] - ) - - assert records_updated == 2 - - # Check data was updated - result = analysis_db.conn.execute( - """ - SELECT * FROM test_table WHERE id IN (1, 2) ORDER BY id - """ - ).fetchdf() - - assert result.iloc[0]["name"] == "A_updated" - assert result.iloc[1]["name"] == "B_updated" - - def test_get_results(self, analysis_db, sample_dataframe): - """Test retrieving results.""" - analysis_db.append_results(sample_dataframe, "test_table") - - # Get all results - result = analysis_db.get_results("test_table") - pd.testing.assert_frame_equal(result, sample_dataframe) - - # Get results with filter - filtered_result = analysis_db.get_results("test_table", filters={"id": [1, 2]}) - - expected = sample_dataframe[sample_dataframe["id"].isin([1, 2])] - pd.testing.assert_frame_equal(filtered_result.reset_index(drop=True), expected) - - def test_get_results_with_limit(self, analysis_db, sample_dataframe): - """Test retrieving results with limit.""" - analysis_db.append_results(sample_dataframe, "test_table") - - result = analysis_db.get_results("test_table", limit=2) - assert len(result) == 2 - - def test_query_method(self, analysis_db, sample_dataframe): - """Test direct SQL query execution.""" - analysis_db.append_results(sample_dataframe, "test_table") - - # Test basic query - result = analysis_db.query("SELECT * FROM test_table") - assert len(result) == len(sample_dataframe) - pd.testing.assert_frame_equal(result, sample_dataframe) - - # Test query with WHERE clause - result = analysis_db.query("SELECT * FROM test_table WHERE id = 1") - assert len(result) == 1 - assert result.iloc[0]["id"] == 1 - - # Test query with aggregation - result = analysis_db.query("SELECT COUNT(*) as count FROM test_table") - assert result.iloc[0]["count"] == len(sample_dataframe) - - # Test query with complex SQL - result = analysis_db.query( - """ - SELECT name, AVG(value) as avg_value - FROM test_table - GROUP BY name - ORDER BY name - """ - ) - assert len(result) == 3 # Should have 3 distinct names - assert "avg_value" in result.columns - - def test_table_exists(self, analysis_db, sample_dataframe): - """Test checking if table exists.""" - assert not analysis_db.table_exists("test_table") - - analysis_db.append_results(sample_dataframe, "test_table") - assert analysis_db.table_exists("test_table") - - def test_drop_table(self, analysis_db, sample_dataframe): - """Test dropping a table.""" - analysis_db.append_results(sample_dataframe, "test_table") - assert analysis_db.table_exists("test_table") - - analysis_db.drop_table("test_table") - assert not analysis_db.table_exists("test_table") - - # Check metadata was also removed - metadata = analysis_db.conn.execute( - """ - SELECT * FROM analysis_metadata WHERE table_name = 'test_table' - """ - ).fetchdf() - assert len(metadata) == 0 - - def test_get_table_info(self, analysis_db, sample_dataframe): - """Test getting table information.""" - analysis_db.append_results( - sample_dataframe, - "test_table", - analysis_type="test_analysis", - parameters={"param1": "value1"}, - description="Test description", - ) - - info = analysis_db.get_table_info("test_table") - - assert info["table_name"] == "test_table" - assert info["total_records"] == 3 - assert info["analysis_type"] == "test_analysis" - assert json.loads(info["parameters"]) == {"param1": "value1"} - assert info["description"] == "Test description" - - def test_list_tables(self, analysis_db, sample_dataframe): - """Test listing all tables.""" - # Initially should be empty (except metadata table) - tables = analysis_db.list_tables() - assert "analysis_metadata" in tables - - # Add some tables - analysis_db.append_results(sample_dataframe, "table1") - analysis_db.append_results(sample_dataframe, "table2") - - tables = analysis_db.list_tables() - assert "table1" in tables - assert "table2" in tables - assert "analysis_metadata" in tables - - def test_get_table_schema(self, analysis_db, sample_dataframe): - """Test getting table schema.""" - analysis_db.append_results(sample_dataframe, "test_table") - - schema = analysis_db.get_table_schema("test_table") - - # Should have columns from sample dataframe - column_names = [col["column_name"] for col in schema] - assert "id" in column_names - assert "name" in column_names - assert "value" in column_names - - def test_close_connection(self, analysis_db): - """Test closing database connection.""" - analysis_db.close() - - # Connection should be closed - with pytest.raises(Exception): - analysis_db.conn.execute("SELECT 1") - - def test_context_manager(self, temp_db_path, sample_dataframe): - """Test using IncrementalAnalysisDB as context manager.""" - with IncrementalAnalysisDB(temp_db_path) as db: - db.append_results(sample_dataframe, "test_table") - assert db.table_exists("test_table") - - # Connection should be closed after context exit - with pytest.raises(Exception): - db.conn.execute("SELECT 1") - - @patch("tfbpapi.IncrementalAnalysisDB.logging.getLogger") - def test_logging_setup(self, mock_get_logger, temp_db_path): - """Test that logging is properly configured.""" - mock_logger = MagicMock() - mock_get_logger.return_value = mock_logger - - db = IncrementalAnalysisDB(temp_db_path) - - mock_get_logger.assert_called_once_with("tfbpapi.IncrementalAnalysisDB") - assert db.logger == mock_logger - db.conn.close() - - def test_error_handling_nonexistent_table(self, analysis_db): - """Test error handling for operations on nonexistent tables.""" - with pytest.raises(Exception): - analysis_db.get_results("nonexistent_table") - - with pytest.raises(Exception): - analysis_db.get_table_info("nonexistent_table") - - def test_empty_dataframe_append(self, analysis_db): - """Test appending empty DataFrame.""" - empty_df = pd.DataFrame() - - records_added = analysis_db.append_results(empty_df, "empty_table") - assert records_added == 0 - - # Table should not be created for empty DataFrame - assert not analysis_db.table_exists("empty_table") diff --git a/tfbpapi/tests/test_datacard.py b/tfbpapi/tests/test_datacard.py new file mode 100644 index 0000000..01b2c0b --- /dev/null +++ b/tfbpapi/tests/test_datacard.py @@ -0,0 +1,449 @@ +"""Tests for the DataCard class.""" + +from unittest.mock import Mock, patch + +import pytest + +from tfbpapi import DataCard +from tfbpapi.errors import DataCardError, DataCardValidationError, HfDataFetchError +from tfbpapi.models import DatasetType + + +class TestDataCard: + """Test suite for DataCard class.""" + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_init( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + test_token, + ): + """Test DataCard initialization.""" + datacard = DataCard(test_repo_id, token=test_token) + + assert datacard.repo_id == test_repo_id + assert datacard.token == test_token + assert datacard._dataset_card is None + assert datacard._metadata_cache == {} + + # Check that fetchers were initialized + mock_card_fetcher.assert_called_once_with(token=test_token) + mock_structure_fetcher.assert_called_once_with(token=test_token) + mock_size_fetcher.assert_called_once_with(token=test_token) + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_init_without_token( + self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id + ): + """Test DataCard initialization without token.""" + datacard = DataCard(test_repo_id) + + assert datacard.repo_id == test_repo_id + assert datacard.token is None + + # Check that fetchers were initialized without token + mock_card_fetcher.assert_called_once_with(token=None) + mock_structure_fetcher.assert_called_once_with(token=None) + mock_size_fetcher.assert_called_once_with(token=None) + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_load_and_validate_card_success( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): + """Test successful card loading and validation.""" + # Setup mock + mock_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_fetcher_instance + mock_fetcher_instance.fetch.return_value = sample_dataset_card_data + + datacard = DataCard(test_repo_id) + + # Access dataset_card property to trigger loading + card = datacard.dataset_card + + assert card is not None + assert len(card.configs) == 4 + assert card.pretty_name == "Test Genomics Dataset" + mock_fetcher_instance.fetch.assert_called_once_with(test_repo_id) + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_load_card_no_data( + self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id + ): + """Test handling when no dataset card is found.""" + mock_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_fetcher_instance + mock_fetcher_instance.fetch.return_value = {} + + datacard = DataCard(test_repo_id) + + with pytest.raises(DataCardValidationError, match="No dataset card found"): + _ = datacard.dataset_card + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_load_card_validation_error( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + invalid_dataset_card_data, + ): + """Test handling of validation errors.""" + mock_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_fetcher_instance + mock_fetcher_instance.fetch.return_value = invalid_dataset_card_data + + datacard = DataCard(test_repo_id) + + with pytest.raises( + DataCardValidationError, match="Dataset card validation failed" + ): + _ = datacard.dataset_card + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_load_card_fetch_error( + self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id + ): + """Test handling of fetch errors.""" + mock_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_fetcher_instance + mock_fetcher_instance.fetch.side_effect = HfDataFetchError("Fetch failed") + + datacard = DataCard(test_repo_id) + + with pytest.raises(DataCardError, match="Failed to fetch dataset card"): + _ = datacard.dataset_card + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_configs_property( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): + """Test getting all configurations via property.""" + mock_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_fetcher_instance + mock_fetcher_instance.fetch.return_value = sample_dataset_card_data + + datacard = DataCard(test_repo_id) + configs = datacard.configs + + assert len(configs) == 4 + config_names = [config.config_name for config in configs] + assert "genomic_features" in config_names + assert "binding_data" in config_names + assert "genome_map_data" in config_names + assert "experiment_metadata" in config_names + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_get_config_by_name( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): + """Test getting a specific configuration by name.""" + mock_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_fetcher_instance + mock_fetcher_instance.fetch.return_value = sample_dataset_card_data + + datacard = DataCard(test_repo_id) + + config = datacard.get_config("binding_data") + assert config is not None + assert config.config_name == "binding_data" + assert config.dataset_type == DatasetType.ANNOTATED_FEATURES + + # Test non-existent config + assert datacard.get_config("nonexistent") is None + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_get_metadata_relationships( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): + """Test getting metadata relationships.""" + mock_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_fetcher_instance + mock_fetcher_instance.fetch.return_value = sample_dataset_card_data + + datacard = DataCard(test_repo_id) + + relationships = datacard.get_metadata_relationships() + + # Should have explicit relationship between binding_data and experiment_metadata + explicit_rels = [r for r in relationships if r.relationship_type == "explicit"] + assert len(explicit_rels) == 1 + assert explicit_rels[0].data_config == "binding_data" + assert explicit_rels[0].metadata_config == "experiment_metadata" + + # Should have embedded relationship for binding_data (has metadata_fields) + embedded_rels = [r for r in relationships if r.relationship_type == "embedded"] + assert len(embedded_rels) == 1 + assert embedded_rels[0].data_config == "binding_data" + assert embedded_rels[0].metadata_config == "binding_data_embedded" + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_get_repository_info_success( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + sample_repo_structure, + ): + """Test getting repository information.""" + mock_card_fetcher_instance = Mock() + mock_structure_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_card_fetcher_instance + mock_structure_fetcher.return_value = mock_structure_fetcher_instance + + mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data + mock_structure_fetcher_instance.fetch.return_value = sample_repo_structure + + datacard = DataCard(test_repo_id) + + info = datacard.get_repository_info() + + assert info["repo_id"] == test_repo_id + assert info["pretty_name"] == "Test Genomics Dataset" + assert info["license"] == "mit" + assert info["num_configs"] == 4 + assert "genomic_features" in info["dataset_types"] + assert "annotated_features" in info["dataset_types"] + assert "genome_map" in info["dataset_types"] + assert "metadata" in info["dataset_types"] + assert info["total_files"] == 5 + assert info["last_modified"] == "2023-12-01T10:30:00Z" + assert info["has_default_config"] is True + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_get_repository_info_fetch_error( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): + """Test getting repository info when structure fetch fails.""" + mock_card_fetcher_instance = Mock() + mock_structure_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_card_fetcher_instance + mock_structure_fetcher.return_value = mock_structure_fetcher_instance + + mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data + mock_structure_fetcher_instance.fetch.side_effect = HfDataFetchError( + "Structure fetch failed" + ) + + datacard = DataCard(test_repo_id) + + info = datacard.get_repository_info() + + assert info["repo_id"] == test_repo_id + assert info["total_files"] is None + assert info["last_modified"] is None + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_summary( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + sample_repo_structure, + ): + """Test getting a summary of the dataset.""" + mock_card_fetcher_instance = Mock() + mock_structure_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_card_fetcher_instance + mock_structure_fetcher.return_value = mock_structure_fetcher_instance + + mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data + mock_structure_fetcher_instance.fetch.return_value = sample_repo_structure + + datacard = DataCard(test_repo_id) + + summary = datacard.summary() + + assert "Dataset: Test Genomics Dataset" in summary + assert f"Repository: {test_repo_id}" in summary + assert "License: mit" in summary + assert "Configurations: 4" in summary + assert "genomic_features" in summary + assert "binding_data" in summary + assert "genome_map_data" in summary + assert "experiment_metadata" in summary + assert "(default)" in summary # genomic_features is marked as default + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_extract_partition_values( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): + """Test extracting partition values.""" + mock_card_fetcher_instance = Mock() + mock_structure_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_card_fetcher_instance + mock_structure_fetcher.return_value = mock_structure_fetcher_instance + + mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data + mock_structure_fetcher_instance.get_partition_values.return_value = [ + "TF1", + "TF2", + "TF3", + ] + + datacard = DataCard(test_repo_id) + + # Get the genome_map_data config which has partitioning enabled + config = datacard.get_config("genome_map_data") + assert config is not None + assert config.dataset_info.partitioning.enabled is True + + values = datacard._extract_partition_values(config, "regulator") + assert values == {"TF1", "TF2", "TF3"} + mock_structure_fetcher_instance.get_partition_values.assert_called_once_with( + test_repo_id, "regulator" + ) + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_extract_partition_values_no_partitioning( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): + """Test extracting partition values when partitioning is disabled.""" + mock_card_fetcher_instance = Mock() + mock_structure_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_card_fetcher_instance + mock_structure_fetcher.return_value = mock_structure_fetcher_instance + + mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data + + datacard = DataCard(test_repo_id) + + # Get a config without partitioning + config = datacard.get_config("genomic_features") + assert config is not None + assert config.dataset_info.partitioning is None + + values = datacard._extract_partition_values(config, "some_field") + assert values == set() + mock_structure_fetcher_instance.get_partition_values.assert_not_called() + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_extract_partition_values_field_not_in_partitions( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): + """Test extracting partition values when field is not a partition column.""" + mock_card_fetcher_instance = Mock() + mock_structure_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_card_fetcher_instance + mock_structure_fetcher.return_value = mock_structure_fetcher_instance + + mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data + + datacard = DataCard(test_repo_id) + + # Get the genome_map_data config which has partitioning enabled + config = datacard.get_config("genome_map_data") + assert config is not None + + # Try to extract values for a field that's not in partition_by + values = datacard._extract_partition_values(config, "not_a_partition_field") + assert values == set() + mock_structure_fetcher_instance.get_partition_values.assert_not_called() + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_extract_partition_values_fetch_error( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): + """Test extracting partition values when fetch fails.""" + mock_card_fetcher_instance = Mock() + mock_structure_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_card_fetcher_instance + mock_structure_fetcher.return_value = mock_structure_fetcher_instance + + mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data + mock_structure_fetcher_instance.get_partition_values.side_effect = ( + HfDataFetchError("Fetch failed") + ) + + datacard = DataCard(test_repo_id) + + config = datacard.get_config("genome_map_data") + values = datacard._extract_partition_values(config, "regulator") + + # Should return empty set on error + assert values == set() diff --git a/tfbpapi/tests/datainfo/test_datacard_parsing.py b/tfbpapi/tests/test_datacard_parsing.py similarity index 98% rename from tfbpapi/tests/datainfo/test_datacard_parsing.py rename to tfbpapi/tests/test_datacard_parsing.py index c7a35d0..5d2210e 100644 --- a/tfbpapi/tests/datainfo/test_datacard_parsing.py +++ b/tfbpapi/tests/test_datacard_parsing.py @@ -2,8 +2,8 @@ import yaml -from tfbpapi.datainfo.models import DatasetCard -from tfbpapi.tests.datainfo.example_datacards import ( +from tfbpapi.models import DatasetCard +from tfbpapi.tests.example_datacards import ( EXAMPLE_1_SIMPLE_TOPLEVEL, EXAMPLE_2_COMPLEX_FIELD_DEFINITIONS, EXAMPLE_3_PARTITIONED_WITH_METADATA, diff --git a/tfbpapi/tests/datainfo/test_fetchers.py b/tfbpapi/tests/test_fetchers.py similarity index 93% rename from tfbpapi/tests/datainfo/test_fetchers.py rename to tfbpapi/tests/test_fetchers.py index a0a04fe..ac350f5 100644 --- a/tfbpapi/tests/datainfo/test_fetchers.py +++ b/tfbpapi/tests/test_fetchers.py @@ -6,7 +6,7 @@ import requests from requests import HTTPError -from tfbpapi.datainfo.fetchers import ( +from tfbpapi.fetchers import ( HfDataCardFetcher, HfRepoStructureFetcher, HfSizeInfoFetcher, @@ -34,7 +34,7 @@ def test_init_with_env_token(self, test_token): fetcher = HfDataCardFetcher() assert fetcher.token == test_token - @patch("tfbpapi.datainfo.fetchers.DatasetCard") + @patch("tfbpapi.fetchers.DatasetCard") def test_fetch_success( self, mock_dataset_card, test_repo_id, sample_dataset_card_data ): @@ -52,7 +52,7 @@ def test_fetch_success( test_repo_id, repo_type="dataset", token="test_token" ) - @patch("tfbpapi.datainfo.fetchers.DatasetCard") + @patch("tfbpapi.fetchers.DatasetCard") def test_fetch_no_data_section(self, mock_dataset_card, test_repo_id): """Test fetch when dataset card has no data section.""" # Setup mock with no data @@ -65,7 +65,7 @@ def test_fetch_no_data_section(self, mock_dataset_card, test_repo_id): assert result == {} - @patch("tfbpapi.datainfo.fetchers.DatasetCard") + @patch("tfbpapi.fetchers.DatasetCard") def test_fetch_exception(self, mock_dataset_card, test_repo_id): """Test fetch when DatasetCard.load raises exception.""" mock_dataset_card.load.side_effect = Exception("API Error") @@ -77,7 +77,7 @@ def test_fetch_exception(self, mock_dataset_card, test_repo_id): def test_fetch_different_repo_types(self, sample_dataset_card_data): """Test fetch with different repository types.""" - with patch("tfbpapi.datainfo.fetchers.DatasetCard") as mock_dataset_card: + with patch("tfbpapi.fetchers.DatasetCard") as mock_dataset_card: mock_card = Mock() mock_card.data.to_dict.return_value = sample_dataset_card_data mock_dataset_card.load.return_value = mock_card @@ -122,7 +122,7 @@ def test_build_headers_without_token(self): assert headers["User-Agent"] == "TFBP-API/1.0" assert "Authorization" not in headers - @patch("tfbpapi.datainfo.fetchers.requests.get") + @patch("tfbpapi.fetchers.requests.get") def test_fetch_success(self, mock_get, test_repo_id, sample_size_info): """Test successful size info fetch.""" # Setup mock response @@ -142,7 +142,7 @@ def test_fetch_success(self, mock_get, test_repo_id, sample_size_info): assert call_args[1]["headers"]["Authorization"] == "Bearer test_token" assert call_args[1]["timeout"] == 30 - @patch("tfbpapi.datainfo.fetchers.requests.get") + @patch("tfbpapi.fetchers.requests.get") def test_fetch_404_error(self, mock_get, test_repo_id): """Test fetch with 404 error.""" # Setup mock 404 response @@ -156,7 +156,7 @@ def test_fetch_404_error(self, mock_get, test_repo_id): with pytest.raises(HfDataFetchError, match="Dataset .* not found"): fetcher.fetch(test_repo_id) - @patch("tfbpapi.datainfo.fetchers.requests.get") + @patch("tfbpapi.fetchers.requests.get") def test_fetch_403_error(self, mock_get, test_repo_id): """Test fetch with 403 error.""" # Setup mock 403 response @@ -172,7 +172,7 @@ def test_fetch_403_error(self, mock_get, test_repo_id): ): fetcher.fetch(test_repo_id) - @patch("tfbpapi.datainfo.fetchers.requests.get") + @patch("tfbpapi.fetchers.requests.get") def test_fetch_other_http_error(self, mock_get, test_repo_id): """Test fetch with other HTTP error.""" # Setup mock 500 response @@ -186,7 +186,7 @@ def test_fetch_other_http_error(self, mock_get, test_repo_id): with pytest.raises(HfDataFetchError, match="HTTP error fetching size"): fetcher.fetch(test_repo_id) - @patch("tfbpapi.datainfo.fetchers.requests.get") + @patch("tfbpapi.fetchers.requests.get") def test_fetch_request_exception(self, mock_get, test_repo_id): """Test fetch with request exception.""" mock_get.side_effect = requests.RequestException("Network error") @@ -196,7 +196,7 @@ def test_fetch_request_exception(self, mock_get, test_repo_id): with pytest.raises(HfDataFetchError, match="Request failed fetching size"): fetcher.fetch(test_repo_id) - @patch("tfbpapi.datainfo.fetchers.requests.get") + @patch("tfbpapi.fetchers.requests.get") def test_fetch_json_decode_error(self, mock_get, test_repo_id): """Test fetch with JSON decode error.""" # Setup mock response with invalid JSON @@ -219,7 +219,7 @@ def test_init(self, test_token): assert fetcher.token == test_token assert fetcher._cached_structure == {} - @patch("tfbpapi.datainfo.fetchers.repo_info") + @patch("tfbpapi.fetchers.repo_info") def test_fetch_success(self, mock_repo_info, test_repo_id, sample_repo_structure): """Test successful repository structure fetch.""" # Setup mock repo info @@ -249,7 +249,7 @@ def test_fetch_success(self, mock_repo_info, test_repo_id, sample_repo_structure repo_id=test_repo_id, repo_type="dataset", token="test_token" ) - @patch("tfbpapi.datainfo.fetchers.repo_info") + @patch("tfbpapi.fetchers.repo_info") def test_fetch_with_caching(self, mock_repo_info, test_repo_id): """Test fetch with caching behavior.""" # Setup mock @@ -273,7 +273,7 @@ def test_fetch_with_caching(self, mock_repo_info, test_repo_id): result3 = fetcher.fetch(test_repo_id, force_refresh=True) assert mock_repo_info.call_count == 2 - @patch("tfbpapi.datainfo.fetchers.repo_info") + @patch("tfbpapi.fetchers.repo_info") def test_fetch_siblings_none(self, mock_repo_info, test_repo_id): """Test fetch when siblings is None.""" # Setup mock with None siblings @@ -289,7 +289,7 @@ def test_fetch_siblings_none(self, mock_repo_info, test_repo_id): assert result["files"] == [] assert result["partitions"] == {} - @patch("tfbpapi.datainfo.fetchers.repo_info") + @patch("tfbpapi.fetchers.repo_info") def test_fetch_exception(self, mock_repo_info, test_repo_id): """Test fetch when repo_info raises exception.""" mock_repo_info.side_effect = Exception("API Error") @@ -326,7 +326,7 @@ def test_extract_partition_info(self): # partitions dict should remain unchanged assert len(partitions) == 2 - @patch("tfbpapi.datainfo.fetchers.repo_info") + @patch("tfbpapi.fetchers.repo_info") def test_get_partition_values_success(self, mock_repo_info, test_repo_id): """Test getting partition values for a specific column.""" # Setup mock with partitioned files @@ -344,7 +344,7 @@ def test_get_partition_values_success(self, mock_repo_info, test_repo_id): assert values == ["TF1", "TF2", "TF3"] # Should be sorted - @patch("tfbpapi.datainfo.fetchers.repo_info") + @patch("tfbpapi.fetchers.repo_info") def test_get_partition_values_no_partitions(self, mock_repo_info, test_repo_id): """Test getting partition values when no partitions exist.""" # Setup mock with no partitioned files @@ -360,7 +360,7 @@ def test_get_partition_values_no_partitions(self, mock_repo_info, test_repo_id): assert values == [] - @patch("tfbpapi.datainfo.fetchers.repo_info") + @patch("tfbpapi.fetchers.repo_info") def test_get_dataset_files_all(self, mock_repo_info, test_repo_id): """Test getting all dataset files.""" # Setup mock @@ -384,7 +384,7 @@ def test_get_dataset_files_all(self, mock_repo_info, test_repo_id): assert files[1]["size"] == 2000 assert files[1]["is_lfs"] is True - @patch("tfbpapi.datainfo.fetchers.repo_info") + @patch("tfbpapi.fetchers.repo_info") def test_get_dataset_files_with_pattern(self, mock_repo_info, test_repo_id): """Test getting dataset files with path pattern filter.""" # Setup mock diff --git a/tfbpapi/tests/test_HfCacheManager.py b/tfbpapi/tests/test_hf_cache_manager.py similarity index 94% rename from tfbpapi/tests/test_HfCacheManager.py rename to tfbpapi/tests/test_hf_cache_manager.py index e044150..94fb568 100644 --- a/tfbpapi/tests/test_HfCacheManager.py +++ b/tfbpapi/tests/test_hf_cache_manager.py @@ -7,8 +7,8 @@ import duckdb import pytest -from tfbpapi.datainfo.models import DatasetType -from tfbpapi.HfCacheManager import HfCacheManager +from tfbpapi.hf_cache_manager import HfCacheManager +from tfbpapi.models import DatasetType class TestHfCacheManagerInit: @@ -20,7 +20,7 @@ def test_init_basic(self): repo_id = "test/repo" with patch( - "tfbpapi.HfCacheManager.DataCard.__init__", return_value=None + "tfbpapi.hf_cache_manager.DataCard.__init__", return_value=None ) as mock_datacard_init: cache_manager = HfCacheManager(repo_id, conn) # Manually set the properties that would normally @@ -43,7 +43,7 @@ def test_init_with_token_and_logger(self): logger = logging.getLogger("test_logger") with patch( - "tfbpapi.HfCacheManager.DataCard.__init__", return_value=None + "tfbpapi.hf_cache_manager.DataCard.__init__", return_value=None ) as mock_datacard_init: cache_manager = HfCacheManager(repo_id, conn, token=token, logger=logger) # Manually set the properties that would @@ -69,7 +69,7 @@ def test_datacard_inheritance(self): token = "test_token" with patch( - "tfbpapi.HfCacheManager.DataCard.__init__", return_value=None + "tfbpapi.hf_cache_manager.DataCard.__init__", return_value=None ) as mock_datacard_init: cache_manager = HfCacheManager(repo_id, conn, token=token) @@ -78,13 +78,12 @@ def test_datacard_inheritance(self): # Should have DataCard methods available (they exist on the class) assert hasattr(cache_manager, "get_config") - assert hasattr(cache_manager, "get_configs_by_type") class TestHfCacheManagerDuckDBOperations: """Test DuckDB operations that are still part of HfCacheManager.""" - @patch("tfbpapi.HfCacheManager.DataCard.__init__", return_value=None) + @patch("tfbpapi.hf_cache_manager.DataCard.__init__", return_value=None) def test_create_duckdb_table_from_files_single_file( self, mock_datacard_init, tmpdir ): @@ -106,7 +105,7 @@ def test_create_duckdb_table_from_files_single_file( assert "CREATE OR REPLACE VIEW test_table" in sql_call assert str(parquet_file) in sql_call - @patch("tfbpapi.HfCacheManager.DataCard.__init__", return_value=None) + @patch("tfbpapi.hf_cache_manager.DataCard.__init__", return_value=None) def test_create_duckdb_table_from_files_multiple_files( self, mock_datacard_init, tmpdir ): @@ -137,7 +136,7 @@ class TestHfCacheManagerCacheManagement: def setup_method(self): """Set up test fixtures.""" - with patch("tfbpapi.HfCacheManager.DataCard.__init__", return_value=None): + with patch("tfbpapi.hf_cache_manager.DataCard.__init__", return_value=None): self.conn = duckdb.connect(":memory:") self.repo_id = "test/repo" self.cache_manager = HfCacheManager(self.repo_id, self.conn) @@ -160,7 +159,7 @@ def test_format_bytes(self): assert self.cache_manager._format_bytes(1024**3) == "1.0GB" assert self.cache_manager._format_bytes(1024**4) == "1.0TB" - @patch("tfbpapi.HfCacheManager.scan_cache_dir") + @patch("tfbpapi.hf_cache_manager.scan_cache_dir") def test_clean_cache_by_age(self, mock_scan_cache_dir): """Test age-based cache cleaning.""" # Setup mock cache info @@ -184,7 +183,7 @@ def test_clean_cache_by_age(self, mock_scan_cache_dir): assert result == mock_delete_strategy mock_cache_info.delete_revisions.assert_called_once_with("abc123") - @patch("tfbpapi.HfCacheManager.scan_cache_dir") + @patch("tfbpapi.hf_cache_manager.scan_cache_dir") def test_clean_cache_by_age_no_old_revisions(self, mock_scan_cache_dir): """Test age-based cleaning when no old revisions exist.""" mock_cache_info = Mock() @@ -208,7 +207,7 @@ def test_clean_cache_by_age_no_old_revisions(self, mock_scan_cache_dir): assert result == mock_delete_strategy mock_cache_info.delete_revisions.assert_called_once_with() - @patch("tfbpapi.HfCacheManager.scan_cache_dir") + @patch("tfbpapi.hf_cache_manager.scan_cache_dir") def test_clean_cache_by_size(self, mock_scan_cache_dir): """Test size-based cache cleaning.""" # Setup mock cache info @@ -238,7 +237,7 @@ def test_clean_cache_by_size(self, mock_scan_cache_dir): assert result == mock_delete_strategy mock_cache_info.delete_revisions.assert_called_once() - @patch("tfbpapi.HfCacheManager.scan_cache_dir") + @patch("tfbpapi.hf_cache_manager.scan_cache_dir") def test_clean_cache_by_size_already_under_target(self, mock_scan_cache_dir): """Test size-based cleaning when already under target.""" mock_cache_info = Mock() @@ -258,7 +257,7 @@ def test_clean_cache_by_size_already_under_target(self, mock_scan_cache_dir): assert result == mock_delete_strategy - @patch("tfbpapi.HfCacheManager.scan_cache_dir") + @patch("tfbpapi.hf_cache_manager.scan_cache_dir") def test_clean_unused_revisions(self, mock_scan_cache_dir): """Test cleaning unused revisions.""" # Setup mock with multiple revisions @@ -292,7 +291,7 @@ def test_clean_unused_revisions(self, mock_scan_cache_dir): # Should delete oldest revision (ghi789) mock_cache_info.delete_revisions.assert_called_once_with("ghi789") - @patch("tfbpapi.HfCacheManager.scan_cache_dir") + @patch("tfbpapi.hf_cache_manager.scan_cache_dir") def test_auto_clean_cache(self, mock_scan_cache_dir): """Test automated cache cleaning.""" mock_cache_info = Mock() @@ -336,7 +335,7 @@ class TestHfCacheManagerErrorHandling: def setup_method(self): """Set up test fixtures.""" - with patch("tfbpapi.HfCacheManager.DataCard.__init__", return_value=None): + with patch("tfbpapi.hf_cache_manager.DataCard.__init__", return_value=None): self.conn = duckdb.connect(":memory:") self.repo_id = "test/repo" self.cache_manager = HfCacheManager(self.repo_id, self.conn) @@ -346,7 +345,7 @@ def test_parse_size_string_invalid_input(self): with pytest.raises(ValueError): self.cache_manager._parse_size_string("invalid") - @patch("tfbpapi.HfCacheManager.scan_cache_dir") + @patch("tfbpapi.hf_cache_manager.scan_cache_dir") def test_clean_cache_invalid_strategy(self, mock_scan_cache_dir): """Test error handling for invalid cleanup strategy.""" mock_cache_info = Mock() @@ -367,7 +366,7 @@ class TestHfCacheManagerIntegration: def setup_method(self): """Set up test fixtures.""" - with patch("tfbpapi.HfCacheManager.DataCard.__init__", return_value=None): + with patch("tfbpapi.hf_cache_manager.DataCard.__init__", return_value=None): self.conn = duckdb.connect(":memory:") self.repo_id = "test/repo" self.cache_manager = HfCacheManager(self.repo_id, self.conn) diff --git a/tfbpapi/tests/datainfo/test_metadata_config_models.py b/tfbpapi/tests/test_metadata_config_models.py similarity index 75% rename from tfbpapi/tests/datainfo/test_metadata_config_models.py rename to tfbpapi/tests/test_metadata_config_models.py index 9a82598..09d6523 100644 --- a/tfbpapi/tests/datainfo/test_metadata_config_models.py +++ b/tfbpapi/tests/test_metadata_config_models.py @@ -11,7 +11,7 @@ import yaml from pydantic import ValidationError -from tfbpapi.datainfo.metadata_config_models import ( +from tfbpapi.models import ( MetadataConfig, PropertyMapping, RepositoryConfig, @@ -37,8 +37,7 @@ def test_invalid_empty_path(self): """Test that empty path is rejected.""" with pytest.raises(ValidationError) as exc_info: PropertyMapping(path="") - # Pydantic catches this with min_length=1 before our custom validator - assert "at least 1 character" in str(exc_info.value) + assert "path cannot be empty" in str(exc_info.value) def test_invalid_whitespace_path(self): """Test that whitespace-only path is rejected.""" @@ -57,6 +56,18 @@ def test_path_whitespace_stripped(self): mapping = PropertyMapping(path=" media.carbon_source ") assert mapping.path == "media.carbon_source" + def test_valid_field_only_mapping(self): + """Test valid field-only mapping (column alias).""" + mapping = PropertyMapping(field="condition") + assert mapping.field == "condition" + assert mapping.path is None + + def test_invalid_neither_field_nor_path(self): + """Test that at least one of field or path is required.""" + with pytest.raises(ValidationError) as exc_info: + PropertyMapping() + assert "At least one of 'field' or 'path' must be specified" in str(exc_info.value) + class TestRepositoryConfig: """Tests for RepositoryConfig model.""" @@ -91,27 +102,24 @@ def test_invalid_dataset_not_dict(self): RepositoryConfig.model_validate(config_data) assert "'dataset' key must contain a dict" in str(exc_info.value) - def test_invalid_property_missing_path(self): - """Test that properties must have 'path' field.""" + def test_valid_field_only_property(self): + """Test that field-only properties are valid (column aliases).""" config_data = { "dataset": {"dataset1": {"carbon_source": {"field": "condition"}}} } - with pytest.raises(ValidationError) as exc_info: - RepositoryConfig.model_validate(config_data) - # Pydantic's Field required error comes before our custom validation - assert "Field required" in str( - exc_info.value - ) or "missing required 'path' field" in str(exc_info.value) + config = RepositoryConfig.model_validate(config_data) + assert config.dataset is not None + assert "dataset1" in config.dataset + assert config.dataset["dataset1"]["carbon_source"].field == "condition" + assert config.dataset["dataset1"]["carbon_source"].path is None - def test_invalid_repo_wide_property_missing_path(self): - """Test that repo-wide properties must have 'path' field.""" - config_data = {"temperature_celsius": {"field": "something"}} - with pytest.raises(ValidationError) as exc_info: - RepositoryConfig.model_validate(config_data) - # Pydantic's Field required error comes before our custom validation - assert "Field required" in str( - exc_info.value - ) or "missing required 'path' field" in str(exc_info.value) + def test_valid_repo_wide_field_only_property(self): + """Test that repo-wide field-only properties are valid.""" + config_data = {"environmental_condition": {"field": "condition"}} + config = RepositoryConfig.model_validate(config_data) + assert "environmental_condition" in config.properties + assert config.properties["environmental_condition"].field == "condition" + assert config.properties["environmental_condition"].path is None class TestMetadataConfig: @@ -126,8 +134,10 @@ def test_valid_config_with_aliases(self, tmp_path): "galactose": ["D-galactose", "Galactose"], } }, - "BrentLab/test": { - "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + "repositories": { + "BrentLab/test": { + "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + } }, } @@ -146,8 +156,10 @@ def test_valid_config_with_aliases(self, tmp_path): def test_valid_config_without_aliases(self, tmp_path): """Test that factor_aliases is optional.""" config_data = { - "BrentLab/test": { - "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + "repositories": { + "BrentLab/test": { + "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + } } } @@ -162,8 +174,10 @@ def test_valid_config_empty_aliases(self, tmp_path): """Test that empty factor_aliases dict is allowed.""" config_data = { "factor_aliases": {}, - "BrentLab/test": { - "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + "repositories": { + "BrentLab/test": { + "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + } }, } @@ -180,7 +194,9 @@ def test_invalid_alias_not_dict(self): "factor_aliases": { "carbon_source": ["D-glucose"] # Should be dict, not list }, - "BrentLab/test": {"dataset": {"test": {"prop": {"path": "path"}}}}, + "repositories": { + "BrentLab/test": {"dataset": {"test": {"prop": {"path": "path"}}}} + }, } with pytest.raises(ValidationError) as exc_info: @@ -196,7 +212,9 @@ def test_invalid_alias_value_not_list(self): "factor_aliases": { "carbon_source": {"glucose": "D-glucose"} # Should be list, not string }, - "BrentLab/test": {"dataset": {"test": {"prop": {"path": "path"}}}}, + "repositories": { + "BrentLab/test": {"dataset": {"test": {"prop": {"path": "path"}}}} + }, } with pytest.raises(ValidationError) as exc_info: @@ -210,7 +228,9 @@ def test_invalid_alias_empty_list(self): """Test that alias value lists cannot be empty.""" config_data = { "factor_aliases": {"carbon_source": {"glucose": []}}, - "BrentLab/test": {"dataset": {"test": {"prop": {"path": "path"}}}}, + "repositories": { + "BrentLab/test": {"dataset": {"test": {"prop": {"path": "path"}}}} + }, } with pytest.raises(ValidationError) as exc_info: @@ -226,8 +246,10 @@ def test_aliases_allow_numeric_values(self): "thirty_seven": [37, 37.0], # Integer and float } }, - "BrentLab/test": { - "dataset": {"test": {"temperature": {"path": "temperature_celsius"}}} + "repositories": { + "BrentLab/test": { + "dataset": {"test": {"temperature": {"path": "temperature_celsius"}}} + } }, } @@ -243,18 +265,20 @@ def test_invalid_no_repositories(self): config_data = {"factor_aliases": {"carbon_source": {"glucose": ["D-glucose"]}}} with pytest.raises(ValidationError) as exc_info: MetadataConfig.model_validate(config_data) - assert "at least one repository configuration" in str(exc_info.value) + assert "at least one repository" in str(exc_info.value) def test_get_repository_config(self, tmp_path): """Test get_repository_config method.""" config_data = { "factor_aliases": {"carbon_source": {"glucose": ["D-glucose"]}}, - "BrentLab/harbison_2004": { - "dataset": { - "harbison_2004": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source", + "repositories": { + "BrentLab/harbison_2004": { + "dataset": { + "harbison_2004": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source", + } } } } @@ -282,11 +306,13 @@ def test_get_property_mappings(self, tmp_path): "carbon_source": {"glucose": ["D-glucose"]}, "temperature": {"thirty": [30]}, }, - "BrentLab/kemmeren_2014": { - "temperature": {"path": "temperature_celsius"}, # Repo-wide - "dataset": { - "kemmeren_2014": {"carbon_source": {"path": "media.carbon_source"}} - }, + "repositories": { + "BrentLab/kemmeren_2014": { + "temperature": {"path": "temperature_celsius"}, # Repo-wide + "dataset": { + "kemmeren_2014": {"carbon_source": {"path": "media.carbon_source"}} + }, + } }, } @@ -310,13 +336,15 @@ def test_get_property_mappings(self, tmp_path): def test_dataset_specific_overrides_repo_wide(self, tmp_path): """Test that dataset-specific mappings override repo-wide.""" config_data = { - "BrentLab/test": { - "carbon_source": {"path": "repo.level.path"}, # Repo-wide - "dataset": { - "test_dataset": { - "carbon_source": {"path": "dataset.level.path"} # Override - } - }, + "repositories": { + "BrentLab/test": { + "carbon_source": {"path": "repo.level.path"}, # Repo-wide + "dataset": { + "test_dataset": { + "carbon_source": {"path": "dataset.level.path"} # Override + } + }, + } }, } @@ -353,12 +381,14 @@ def test_nested_alias_property_names(self, tmp_path): "carbon_source.concentration_percent": {"two_percent": [2]}, "carbon_source.specifications": {"no_aa": ["without_amino_acids"]}, }, - "BrentLab/test": { - "dataset": { - "test": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source", + "repositories": { + "BrentLab/test": { + "dataset": { + "test": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source", + } } } } diff --git a/tfbpapi/tests/datainfo/test_models.py b/tfbpapi/tests/test_models.py similarity index 99% rename from tfbpapi/tests/datainfo/test_models.py rename to tfbpapi/tests/test_models.py index d4701e4..bf52f75 100644 --- a/tfbpapi/tests/datainfo/test_models.py +++ b/tfbpapi/tests/test_models.py @@ -8,7 +8,7 @@ import pytest from pydantic import ValidationError -from tfbpapi.datainfo.models import ( +from tfbpapi.models import ( DataFileInfo, DatasetCard, DatasetConfig, diff --git a/tfbpapi/tests/datainfo/test_real_datacards.py b/tfbpapi/tests/test_real_datacards.py similarity index 99% rename from tfbpapi/tests/datainfo/test_real_datacards.py rename to tfbpapi/tests/test_real_datacards.py index 0220797..cd07626 100644 --- a/tfbpapi/tests/datainfo/test_real_datacards.py +++ b/tfbpapi/tests/test_real_datacards.py @@ -11,7 +11,7 @@ import pytest import yaml -from tfbpapi.datainfo.models import DatasetCard +from tfbpapi.models import DatasetCard # Real datacard YAML strings from the collection BARKAI_COMPENDIUM = """ diff --git a/tfbpapi/tests/test_virtual_db.py b/tfbpapi/tests/test_virtual_db.py new file mode 100644 index 0000000..3728e3e --- /dev/null +++ b/tfbpapi/tests/test_virtual_db.py @@ -0,0 +1,507 @@ +""" +Tests for VirtualDB unified query interface. + +Tests configuration loading, schema discovery, querying, filtering, and caching. + +""" + +from pathlib import Path +import tempfile +import pytest +import pandas as pd +import yaml + +from tfbpapi.virtual_db import VirtualDB, get_nested_value, normalize_value + + +class TestHelperFunctions: + """Tests for helper functions.""" + + def test_get_nested_value_simple(self): + """Test simple nested dict navigation.""" + data = {"media": {"name": "YPD"}} + result = get_nested_value(data, "media.name") + assert result == "YPD" + + def test_get_nested_value_missing_key(self): + """Test that missing keys return None.""" + data = {"media": {"name": "YPD"}} + result = get_nested_value(data, "media.carbon_source") + assert result is None + + def test_get_nested_value_list_extraction(self): + """Test extracting property from list of dicts.""" + data = { + "media": { + "carbon_source": [ + {"compound": "glucose"}, + {"compound": "galactose"} + ] + } + } + result = get_nested_value(data, "media.carbon_source.compound") + assert result == ["glucose", "galactose"] + + def test_get_nested_value_non_dict(self): + """Test that non-dict input returns None.""" + result = get_nested_value("not a dict", "path") + assert result is None + + def test_normalize_value_exact_match(self): + """Test exact alias match.""" + aliases = {"glucose": ["D-glucose", "dextrose"]} + result = normalize_value("D-glucose", aliases) + assert result == "glucose" + + def test_normalize_value_case_insensitive(self): + """Test case-insensitive matching.""" + aliases = {"glucose": ["D-glucose", "dextrose"]} + result = normalize_value("DEXTROSE", aliases) + assert result == "glucose" + + def test_normalize_value_no_match(self): + """Test pass-through when no alias matches.""" + aliases = {"glucose": ["D-glucose"]} + result = normalize_value("maltose", aliases) + assert result == "maltose" + + def test_normalize_value_no_aliases(self): + """Test pass-through when no aliases provided.""" + result = normalize_value("D-glucose", None) + assert result == "D-glucose" + + def test_normalize_value_missing_value_label(self): + """Test missing value handling.""" + result = normalize_value(None, None, "unspecified") + assert result == "unspecified" + + def test_normalize_value_missing_value_no_label(self): + """Test missing value without label.""" + result = normalize_value(None, None) + assert result == "None" + + +class TestVirtualDBConfig: + """Tests for VirtualDB configuration loading.""" + + def create_test_config(self, **overrides): + """Helper to create test configuration file.""" + config = { + "factor_aliases": { + "carbon_source": { + "glucose": ["D-glucose", "dextrose"], + "galactose": ["D-galactose", "Galactose"] + } + }, + "missing_value_labels": { + "carbon_source": "unspecified" + }, + "description": { + "carbon_source": "Carbon source in growth media" + }, + "repositories": { + "BrentLab/test_repo": { + "temperature_celsius": {"path": "temperature_celsius"}, + "dataset": { + "test_dataset": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source.compound" + } + } + } + } + } + } + config.update(overrides) + return config + + def test_init_with_valid_config(self): + """Test VirtualDB initialization with valid config.""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + yaml.dump(self.create_test_config(), f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + assert vdb.config is not None + assert vdb.token is None + assert len(vdb.cache) == 0 + finally: + Path(config_path).unlink() + + def test_init_with_token(self): + """Test VirtualDB initialization with HF token.""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + yaml.dump(self.create_test_config(), f) + config_path = f.name + + try: + vdb = VirtualDB(config_path, token="test_token") + assert vdb.token == "test_token" + finally: + Path(config_path).unlink() + + def test_init_missing_config_file(self): + """Test error when config file doesn't exist.""" + with pytest.raises(FileNotFoundError): + VirtualDB("/nonexistent/path.yaml") + + def test_repr(self): + """Test string representation.""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + yaml.dump(self.create_test_config(), f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + repr_str = repr(vdb) + assert "VirtualDB" in repr_str + assert "1 repositories" in repr_str + assert "1 datasets configured" in repr_str + assert "0 views cached" in repr_str + finally: + Path(config_path).unlink() + + +class TestSchemaDiscovery: + """Tests for schema discovery methods.""" + + def create_multi_dataset_config(self): + """Create config with multiple datasets.""" + return { + "factor_aliases": {}, + "repositories": { + "BrentLab/repo1": { + "temperature_celsius": {"path": "temperature_celsius"}, + "dataset": { + "dataset1": { + "carbon_source": {"field": "condition", "path": "media.carbon_source"} + } + } + }, + "BrentLab/repo2": { + "nitrogen_source": {"path": "media.nitrogen_source"}, + "dataset": { + "dataset2": { + "carbon_source": {"path": "media.carbon_source"}, + "temperature_celsius": {"path": "temperature_celsius"} + } + } + } + } + } + + def test_get_fields_all_datasets(self): + """Test getting all fields across all datasets.""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + yaml.dump(self.create_multi_dataset_config(), f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + fields = vdb.get_fields() + assert "carbon_source" in fields + assert "temperature_celsius" in fields + assert "nitrogen_source" in fields + assert fields == sorted(fields) # Should be sorted + finally: + Path(config_path).unlink() + + def test_get_fields_specific_dataset(self): + """Test getting fields for specific dataset.""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + yaml.dump(self.create_multi_dataset_config(), f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + fields = vdb.get_fields("BrentLab/repo1", "dataset1") + assert "carbon_source" in fields + assert "temperature_celsius" in fields + # nitrogen_source is in repo2, not repo1 + assert "nitrogen_source" not in fields + finally: + Path(config_path).unlink() + + def test_get_fields_invalid_partial_args(self): + """Test error when only one of repo_id/config_name provided.""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + yaml.dump(self.create_multi_dataset_config(), f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + with pytest.raises(ValueError, match="Both repo_id and config_name"): + vdb.get_fields(repo_id="BrentLab/repo1") + finally: + Path(config_path).unlink() + + def test_get_common_fields(self): + """Test getting fields common to all datasets.""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + yaml.dump(self.create_multi_dataset_config(), f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + common = vdb.get_common_fields() + # Both datasets have carbon_source and temperature_celsius + assert "carbon_source" in common + assert "temperature_celsius" in common + # nitrogen_source is only in repo2 + assert "nitrogen_source" not in common + finally: + Path(config_path).unlink() + + def test_get_common_fields_empty_config(self): + """Test getting common fields with no repositories.""" + config = {"factor_aliases": {}} + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + # This will fail validation (needs at least one repo) + # So we skip this test + pass + + +class TestCaching: + """Tests for view materialization and caching.""" + + def create_simple_config(self): + """Create simple config for testing.""" + return { + "factor_aliases": {}, + "repositories": { + "BrentLab/test_repo": { + "dataset": { + "test_dataset": { + "carbon_source": {"path": "media.carbon_source"} + } + } + } + } + } + + def test_invalidate_cache_all(self): + """Test invalidating all cache.""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + yaml.dump(self.create_simple_config(), f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + # Manually add to cache + vdb.cache[("BrentLab/test_repo", "test_dataset")] = pd.DataFrame() + assert len(vdb.cache) == 1 + + vdb.invalidate_cache() + assert len(vdb.cache) == 0 + finally: + Path(config_path).unlink() + + def test_invalidate_cache_specific(self): + """Test invalidating specific dataset cache.""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + yaml.dump(self.create_simple_config(), f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + # Add multiple entries to cache + vdb.cache[("BrentLab/test_repo", "test_dataset")] = pd.DataFrame() + vdb.cache[("BrentLab/other_repo", "other_dataset")] = pd.DataFrame() + assert len(vdb.cache) == 2 + + vdb.invalidate_cache([("BrentLab/test_repo", "test_dataset")]) + assert len(vdb.cache) == 1 + assert ("BrentLab/other_repo", "other_dataset") in vdb.cache + finally: + Path(config_path).unlink() + + +class TestFiltering: + """Tests for filter application logic.""" + + def test_apply_filters_exact_match(self): + """Test exact value matching in filters.""" + df = pd.DataFrame({ + "sample_id": ["s1", "s2", "s3"], + "carbon_source": ["glucose", "galactose", "glucose"] + }) + + # Create minimal VirtualDB instance + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + config = { + "repositories": { + "BrentLab/test": { + "dataset": { + "test": {"carbon_source": {"path": "media.carbon_source"}} + } + } + } + } + yaml.dump(config, f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + filtered = vdb._apply_filters( + df, {"carbon_source": "glucose"}, "BrentLab/test", "test" + ) + assert len(filtered) == 2 + assert all(filtered["carbon_source"] == "glucose") + finally: + Path(config_path).unlink() + + def test_apply_filters_numeric_range(self): + """Test numeric range filtering.""" + df = pd.DataFrame({ + "sample_id": ["s1", "s2", "s3"], + "temperature_celsius": [25, 30, 37] + }) + + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + config = { + "repositories": { + "BrentLab/test": { + "dataset": { + "test": {"temperature_celsius": {"path": "temperature_celsius"}} + } + } + } + } + yaml.dump(config, f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + + # Test >= operator + filtered = vdb._apply_filters( + df, {"temperature_celsius": (">=", 30)}, "BrentLab/test", "test" + ) + assert len(filtered) == 2 + assert all(filtered["temperature_celsius"] >= 30) + + # Test between operator + filtered = vdb._apply_filters( + df, {"temperature_celsius": ("between", 28, 32)}, "BrentLab/test", "test" + ) + assert len(filtered) == 1 + assert filtered.iloc[0]["temperature_celsius"] == 30 + finally: + Path(config_path).unlink() + + def test_apply_filters_with_alias_expansion(self): + """Test filter with alias expansion.""" + df = pd.DataFrame({ + "sample_id": ["s1", "s2", "s3"], + "carbon_source": ["glucose", "D-glucose", "galactose"] + }) + + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + config = { + "factor_aliases": { + "carbon_source": { + "glucose": ["D-glucose", "dextrose", "glucose"] + } + }, + "repositories": { + "BrentLab/test": { + "dataset": { + "test": {"carbon_source": {"path": "media.carbon_source"}} + } + } + } + } + yaml.dump(config, f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + filtered = vdb._apply_filters( + df, {"carbon_source": "glucose"}, "BrentLab/test", "test" + ) + # Should match both "glucose" and "D-glucose" due to alias expansion + assert len(filtered) == 2 + finally: + Path(config_path).unlink() + + +class TestExtraction: + """Tests for metadata extraction methods.""" + + def test_add_field_metadata(self): + """Test adding field-level metadata to DataFrame.""" + df = pd.DataFrame({ + "sample_id": ["s1", "s2"], + "condition": ["YPD", "YPG"] + }) + + field_metadata = { + "YPD": { + "carbon_source": ["glucose"], + "growth_media": ["YPD"] + }, + "YPG": { + "carbon_source": ["glycerol"], + "growth_media": ["YPG"] + } + } + + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + config = { + "repositories": { + "BrentLab/test": { + "dataset": { + "test": {"carbon_source": {"path": "media.carbon_source"}} + } + } + } + } + yaml.dump(config, f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + result = vdb._add_field_metadata(df, field_metadata) + + assert "carbon_source" in result.columns + assert "growth_media" in result.columns + assert result.loc[result["condition"] == "YPD", "carbon_source"].iloc[0] == "glucose" + assert result.loc[result["condition"] == "YPG", "carbon_source"].iloc[0] == "glycerol" + finally: + Path(config_path).unlink() + + +class TestQuery: + """Tests for query method - requires mocking HfQueryAPI.""" + + def test_query_empty_result(self): + """Test query with no matching datasets.""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + config = { + "repositories": { + "BrentLab/test": { + "dataset": { + "test": {"carbon_source": {"path": "media.carbon_source"}} + } + } + } + } + yaml.dump(config, f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + # Query with non-configured dataset should return empty + result = vdb.query(datasets=[("BrentLab/other", "other")]) + assert isinstance(result, pd.DataFrame) + assert result.empty + finally: + Path(config_path).unlink() + + +# Note: Full integration tests with real HuggingFace datasets would go here +# but are excluded as they require network access and specific test datasets. +# These tests cover the core logic and would be supplemented with integration +# tests using the actual sample config and real datasets like harbison_2004. diff --git a/tfbpapi/virtual_db.py b/tfbpapi/virtual_db.py new file mode 100644 index 0000000..245bf18 --- /dev/null +++ b/tfbpapi/virtual_db.py @@ -0,0 +1,796 @@ +""" +VirtualDB provides a unified query interface across heterogeneous datasets. + +This module enables cross-dataset queries with standardized field names and values, +mapping varying experimental condition structures to a common schema through external +YAML configuration. + +Key Components: +- VirtualDB: Main interface for unified cross-dataset queries +- Helper functions: get_nested_value(), normalize_value() for metadata extraction +- Configuration-driven schema via models.MetadataConfig + +Example Usage: + >>> from tfbpapi.datainfo import VirtualDB + >>> vdb = VirtualDB("config.yaml") + >>> + >>> # Discover available fields + >>> fields = vdb.get_fields() + >>> print(fields) # ["carbon_source", "temperature_celsius", ...] + >>> + >>> # Query across datasets + >>> df = vdb.query( + ... filters={"carbon_source": "glucose", "temperature_celsius": 30}, + ... fields=["sample_id", "carbon_source", "temperature_celsius"] + ... ) + >>> + >>> # Get complete data with measurements + >>> df = vdb.query( + ... filters={"carbon_source": "glucose"}, + ... complete=True + ... ) + +""" + +from __future__ import annotations + +from pathlib import Path +from typing import Any + +import duckdb +import pandas as pd + +from tfbpapi.datacard import DataCard +from tfbpapi.errors import DataCardError +from tfbpapi.hf_cache_manager import HfCacheManager +from tfbpapi.models import MetadataConfig, PropertyMapping + + +def get_nested_value(data: dict, path: str) -> Any: + """ + Navigate nested dict/list using dot notation. + + Handles missing intermediate keys gracefully by returning None. + Supports extracting properties from lists of dicts. + + :param data: Dictionary to navigate + :param path: Dot-separated path (e.g., "media.carbon_source.compound") + :return: Value at path or None if not found + + Examples: + Simple nested dict: + get_nested_value({"media": {"name": "YPD"}}, "media.name") + Returns: "YPD" + + List of dicts - extract property from each item: + get_nested_value( + {"media": {"carbon_source": [{"compound": "glucose"}, {"compound": "galactose"}]}}, + "media.carbon_source.compound" + ) + Returns: ["glucose", "galactose"] + + """ + if not isinstance(data, dict): + return None + + keys = path.split(".") + current = data + + for i, key in enumerate(keys): + if isinstance(current, dict): + if key not in current: + return None + current = current[key] + elif isinstance(current, list): + # If current is a list and we have more keys, extract property from each item + if i < len(keys): + # Extract the remaining path from each list item + remaining_path = ".".join(keys[i:]) + results = [] + for item in current: + if isinstance(item, dict): + val = get_nested_value(item, remaining_path) + if val is not None: + results.append(val) + return results if results else None + else: + return None + + return current + + +def normalize_value( + actual_value: Any, + aliases: dict[str, list[Any]] | None, + missing_value_label: str | None = None, +) -> str: + """ + Normalize a value using optional alias mappings (case-insensitive). + + Returns the alias name if a match is found, otherwise returns the + original value as a string. Handles missing values by returning + the configured missing_value_label. + + :param actual_value: The value from the data to normalize + :param aliases: Optional dict mapping alias names to lists of actual values. + Example: {"glucose": ["D-glucose", "dextrose"]} + :param missing_value_label: Label to use for None/missing values + :return: Alias name if match found, missing_value_label if None, + otherwise str(actual_value) + + Examples: + With aliases - exact match: + normalize_value("D-glucose", {"glucose": ["D-glucose", "dextrose"]}) + Returns: "glucose" + + With aliases - case-insensitive match: + normalize_value("DEXTROSE", {"glucose": ["D-glucose", "dextrose"]}) + Returns: "glucose" + + Missing value: + normalize_value(None, None, "unspecified") + Returns: "unspecified" + + No alias match - pass through: + normalize_value("maltose", {"glucose": ["D-glucose"]}) + Returns: "maltose" + + """ + # Handle None/missing values + if actual_value is None: + return missing_value_label if missing_value_label else "None" + + if aliases is None: + return str(actual_value) + + # Convert to string for comparison (case-insensitive) + actual_str = str(actual_value).lower() + + # Check each alias mapping + for alias_name, actual_values in aliases.items(): + for val in actual_values: + if str(val).lower() == actual_str: + return alias_name + + # No match found - pass through original value + return str(actual_value) + + +class VirtualDB: + """ + Unified query interface across heterogeneous datasets. + + VirtualDB provides a virtual database layer over multiple HuggingFace datasets, + allowing cross-dataset queries with standardized field names and normalized values. + Each configured dataset becomes a view with a common schema defined by external + YAML configuration. + + The YAML configuration specifies: + 1. Property mappings: How to extract each field from dataset structures + 2. Factor aliases: Normalize varying terminologies to standard values + 3. Missing value labels: Handle missing data consistently + 4. Descriptions: Document each field's semantics + + Attributes: + config: MetadataConfig instance with all configuration + token: Optional HuggingFace token for private datasets + cache: Dict mapping (repo_id, config_name) to cached DataFrame views + + """ + + def __init__(self, config_path: Path | str, token: str | None = None): + """ + Initialize VirtualDB with configuration and optional auth token. + + :param config_path: Path to YAML configuration file + :param token: Optional HuggingFace token for private datasets + :raises FileNotFoundError: If config file doesn't exist + :raises ValueError: If configuration is invalid + + """ + self.config = MetadataConfig.from_yaml(config_path) + self.token = token + self.cache: dict[tuple[str, str], pd.DataFrame] = {} + + def get_fields( + self, repo_id: str | None = None, config_name: str | None = None + ) -> list[str]: + """ + Get list of queryable fields. + + :param repo_id: Optional repository ID to filter to specific dataset + :param config_name: Optional config name (required if repo_id provided) + :return: List of field names + + Examples: + All fields across all datasets: + fields = vdb.get_fields() + + Fields for specific dataset: + fields = vdb.get_fields("BrentLab/harbison_2004", "harbison_2004") + + """ + if repo_id is not None and config_name is not None: + # Get fields for specific dataset + mappings = self.config.get_property_mappings(repo_id, config_name) + return sorted(mappings.keys()) + + if repo_id is not None or config_name is not None: + raise ValueError("Both repo_id and config_name must be provided, or neither") + + # Get all fields across all datasets + all_fields: set[str] = set() + for repo_id, repo_config in self.config.repositories.items(): + # Add repo-wide fields + all_fields.update(repo_config.properties.keys()) + # Add dataset-specific fields + if repo_config.dataset: + for dataset_props in repo_config.dataset.values(): + all_fields.update(dataset_props.keys()) + + return sorted(all_fields) + + def get_common_fields(self) -> list[str]: + """ + Get fields present in ALL configured datasets. + + :return: List of field names common to all datasets + + Example: + common = vdb.get_common_fields() + # ["carbon_source", "temperature_celsius"] + + """ + if not self.config.repositories: + return [] + + # Get field sets for each dataset + dataset_fields: list[set[str]] = [] + for repo_id, repo_config in self.config.repositories.items(): + if repo_config.dataset: + for config_name in repo_config.dataset.keys(): + mappings = self.config.get_property_mappings(repo_id, config_name) + dataset_fields.append(set(mappings.keys())) + + if not dataset_fields: + return [] + + # Return intersection + common = set.intersection(*dataset_fields) + return sorted(common) + + def get_unique_values( + self, field: str, by_dataset: bool = False + ) -> list[str] | dict[str, list[str]]: + """ + Get unique values for a field across datasets (with normalization). + + :param field: Field name to get values for + :param by_dataset: If True, return dict keyed by dataset identifier + :return: List of unique normalized values, or dict if by_dataset=True + + Examples: + All unique values: + values = vdb.get_unique_values("carbon_source") + # ["glucose", "galactose", "raffinose"] + + Values by dataset: + values = vdb.get_unique_values("carbon_source", by_dataset=True) + # {"BrentLab/harbison_2004": ["glucose", "galactose"], + # "BrentLab/kemmeren_2014": ["glucose", "raffinose"]} + + """ + if by_dataset: + result: dict[str, list[str]] = {} + else: + all_values: set[str] = set() + + # Query each dataset that has this field + for repo_id, repo_config in self.config.repositories.items(): + if repo_config.dataset: + for config_name in repo_config.dataset.keys(): + mappings = self.config.get_property_mappings(repo_id, config_name) + if field not in mappings: + continue + + # Build metadata table for this dataset + metadata_df = self._build_metadata_table(repo_id, config_name) + if metadata_df.empty or field not in metadata_df.columns: + continue + + # Get unique values (already normalized) + unique_vals = metadata_df[field].dropna().unique().tolist() + + if by_dataset: + dataset_key = f"{repo_id}/{config_name}" + result[dataset_key] = sorted(unique_vals) + else: + all_values.update(unique_vals) + + if by_dataset: + return result + else: + return sorted(all_values) + + def query( + self, + filters: dict[str, Any] | None = None, + datasets: list[tuple[str, str]] | None = None, + fields: list[str] | None = None, + complete: bool = False, + ) -> pd.DataFrame: + """ + Query VirtualDB with optional filters and field selection. + + :param filters: Dict of field:value pairs to filter on + :param datasets: List of (repo_id, config_name) tuples to query (None = all) + :param fields: List of field names to return (None = all) + :param complete: If True, return measurement-level data; if False, sample-level + :return: DataFrame with query results + + Examples: + Basic query across all datasets: + df = vdb.query(filters={"carbon_source": "glucose"}) + + Query specific datasets with field selection: + df = vdb.query( + filters={"carbon_source": "glucose", "temperature_celsius": 30}, + datasets=[("BrentLab/harbison_2004", "harbison_2004")], + fields=["sample_id", "carbon_source", "temperature_celsius"] + ) + + Complete data with measurements: + df = vdb.query( + filters={"carbon_source": "glucose"}, + complete=True + ) + + """ + # Determine which datasets to query + if datasets is None: + # Query all configured datasets + datasets = [] + for repo_id, repo_config in self.config.repositories.items(): + if repo_config.dataset: + for config_name in repo_config.dataset.keys(): + datasets.append((repo_id, config_name)) + + if not datasets: + return pd.DataFrame() + + # Query each dataset + results: list[pd.DataFrame] = [] + for repo_id, config_name in datasets: + # Build metadata table + metadata_df = self._build_metadata_table(repo_id, config_name) + if metadata_df.empty: + continue + + # Apply filters + if filters: + metadata_df = self._apply_filters(metadata_df, filters, repo_id, config_name) + + # If complete=True, join with full data + if complete: + sample_ids = metadata_df["sample_id"].tolist() + if sample_ids: + full_df = self._get_complete_data( + repo_id, config_name, sample_ids, metadata_df + ) + if not full_df.empty: + metadata_df = full_df + + # Select requested fields + if fields: + # Keep sample_id and any dataset identifier columns + keep_cols = ["sample_id"] + if "dataset_id" in metadata_df.columns: + keep_cols.append("dataset_id") + # Add requested fields that exist + for field in fields: + if field in metadata_df.columns and field not in keep_cols: + keep_cols.append(field) + metadata_df = metadata_df[keep_cols] + + # Add dataset identifier + if "dataset_id" not in metadata_df.columns: + metadata_df["dataset_id"] = f"{repo_id}/{config_name}" + + results.append(metadata_df) + + if not results: + return pd.DataFrame() + + # Concatenate results, filling NaN for missing columns + return pd.concat(results, ignore_index=True, sort=False) + + def materialize_views( + self, datasets: list[tuple[str, str]] | None = None + ) -> None: + """ + Build and cache metadata DataFrames for faster subsequent queries. + + :param datasets: List of (repo_id, config_name) tuples to materialize + (None = materialize all) + + Example: + vdb.materialize_views() # Cache all datasets + vdb.materialize_views([("BrentLab/harbison_2004", "harbison_2004")]) + + """ + if datasets is None: + # Materialize all configured datasets + datasets = [] + for repo_id, repo_config in self.config.repositories.items(): + if repo_config.dataset: + for config_name in repo_config.dataset.keys(): + datasets.append((repo_id, config_name)) + + for repo_id, config_name in datasets: + # Build and cache + self._build_metadata_table(repo_id, config_name, use_cache=False) + + def invalidate_cache( + self, datasets: list[tuple[str, str]] | None = None + ) -> None: + """ + Clear cached metadata DataFrames. + + :param datasets: List of (repo_id, config_name) tuples to invalidate + (None = invalidate all) + + Example: + vdb.invalidate_cache() # Clear all cache + vdb.invalidate_cache([("BrentLab/harbison_2004", "harbison_2004")]) + + """ + if datasets is None: + self.cache.clear() + else: + for dataset_key in datasets: + if dataset_key in self.cache: + del self.cache[dataset_key] + + def _build_metadata_table( + self, repo_id: str, config_name: str, use_cache: bool = True + ) -> pd.DataFrame: + """ + Build metadata table for a single dataset. + + Extracts sample-level metadata from experimental conditions hierarchy + and field definitions, with normalization and missing value handling. + + :param repo_id: Repository ID + :param config_name: Configuration name + :param use_cache: Whether to use/update cache + :return: DataFrame with one row per sample_id + + """ + cache_key = (repo_id, config_name) + + # Check cache + if use_cache and cache_key in self.cache: + return self.cache[cache_key] + + try: + # Load DataCard and CacheManager + card = DataCard(repo_id, token=self.token) + cache_mgr = HfCacheManager( + repo_id, duckdb_conn=duckdb.connect(":memory:"), token=self.token + ) + + # Get property mappings + property_mappings = self.config.get_property_mappings(repo_id, config_name) + if not property_mappings: + return pd.DataFrame() + + # Extract repo/config-level metadata + repo_metadata = self._extract_repo_level(card, config_name, property_mappings) + + # Extract field-level metadata + field_metadata = self._extract_field_level(card, config_name, property_mappings) + + # Get sample-level data from HuggingFace + config = card.get_config(config_name) + if config and hasattr(config, "metadata_fields") and config.metadata_fields: + # Select only metadata fields + columns = ", ".join(config.metadata_fields) + if "sample_id" not in config.metadata_fields: + columns = f"sample_id, {columns}" + sql = f"SELECT DISTINCT {columns} FROM {config_name}" + else: + # No metadata_fields specified, select all + sql = f"SELECT DISTINCT * FROM {config_name}" + + df = cache_mgr.query(sql, config_name) + + # One row per sample_id + if "sample_id" in df.columns: + df = df.groupby("sample_id").first().reset_index() + + # Add repo-level metadata as columns + for prop_name, values in repo_metadata.items(): + # Use first value (repo-level properties are constant) + df[prop_name] = values[0] if values else None + + # Add field-level metadata + if field_metadata: + df = self._add_field_metadata(df, field_metadata) + + # Cache result + if use_cache: + self.cache[cache_key] = df + + return df + + except Exception as e: + # Return empty DataFrame on error + return pd.DataFrame() + + def _extract_repo_level( + self, + card: DataCard, + config_name: str, + property_mappings: dict[str, PropertyMapping], + ) -> dict[str, list[str]]: + """ + Extract and normalize repo/config-level metadata. + + :param card: DataCard instance + :param config_name: Configuration name + :param property_mappings: Property mappings for this dataset + :return: Dict mapping property names to normalized values + + """ + metadata: dict[str, list[str]] = {} + + # Get experimental conditions + try: + conditions = card.get_experimental_conditions(config_name) + except DataCardError: + conditions = {} + + if not conditions: + return metadata + + # Extract each mapped property + for prop_name, mapping in property_mappings.items(): + # Skip field-level mappings + if mapping.field is not None: + continue + + # Build full path + full_path = f"experimental_conditions.{mapping.path}" + + # Get value at path + value = get_nested_value(conditions, full_path) + + # Handle missing values + missing_label = self.config.missing_value_labels.get(prop_name) + if value is None: + if missing_label: + metadata[prop_name] = [missing_label] + continue + + # Ensure value is a list + actual_values = [value] if not isinstance(value, list) else value + + # Normalize using aliases + aliases = self.config.factor_aliases.get(prop_name) + normalized_values = [ + normalize_value(v, aliases, missing_label) for v in actual_values + ] + + metadata[prop_name] = normalized_values + + return metadata + + def _extract_field_level( + self, + card: DataCard, + config_name: str, + property_mappings: dict[str, PropertyMapping], + ) -> dict[str, dict[str, Any]]: + """ + Extract and normalize field-level metadata. + + :param card: DataCard instance + :param config_name: Configuration name + :param property_mappings: Property mappings for this dataset + :return: Dict mapping field values to their normalized metadata + + """ + field_metadata: dict[str, dict[str, Any]] = {} + + # Group property mappings by field + field_mappings: dict[str, dict[str, str]] = {} + for prop_name, mapping in property_mappings.items(): + if mapping.field is not None: + field_name = mapping.field + if field_name not in field_mappings: + field_mappings[field_name] = {} + field_mappings[field_name][prop_name] = mapping.path + + # Process each field that has mappings + for field_name, prop_paths in field_mappings.items(): + # Get field definitions + definitions = card.get_field_definitions(config_name, field_name) + if not definitions: + continue + + # Extract metadata for each field value + for field_value, definition in definitions.items(): + if field_value not in field_metadata: + field_metadata[field_value] = {} + + for prop_name, path in prop_paths.items(): + # Get value at path + value = get_nested_value(definition, path) + + # Handle missing values + missing_label = self.config.missing_value_labels.get(prop_name) + if value is None: + if missing_label: + field_metadata[field_value][prop_name] = [missing_label] + continue + + # Ensure value is a list + actual_values = [value] if not isinstance(value, list) else value + + # Normalize using aliases + aliases = self.config.factor_aliases.get(prop_name) + normalized_values = [ + normalize_value(v, aliases, missing_label) for v in actual_values + ] + + field_metadata[field_value][prop_name] = normalized_values + + return field_metadata + + def _add_field_metadata( + self, df: pd.DataFrame, field_metadata: dict[str, dict[str, Any]] + ) -> pd.DataFrame: + """ + Add columns from field-level metadata to DataFrame. + + :param df: DataFrame with base sample metadata + :param field_metadata: Dict mapping field values to their properties + :return: DataFrame with additional property columns + + """ + # For each field value, add its properties as columns + for field_value, properties in field_metadata.items(): + for prop_name, prop_values in properties.items(): + # Initialize column if needed + if prop_name not in df.columns: + df[prop_name] = None + + # Find rows where any column matches field_value + for col in df.columns: + if col in [prop_name, "sample_id", "dataset_id"]: + continue + mask = df[col] == field_value + if mask.any(): + # Set property value (take first from list) + value = prop_values[0] if prop_values else None + df.loc[mask, prop_name] = value + + return df + + def _apply_filters( + self, + df: pd.DataFrame, + filters: dict[str, Any], + repo_id: str, + config_name: str, + ) -> pd.DataFrame: + """ + Apply filters to DataFrame with alias expansion and numeric handling. + + :param df: DataFrame to filter + :param filters: Dict of field:value pairs + :param repo_id: Repository ID (for alias lookup) + :param config_name: Config name (for alias lookup) + :return: Filtered DataFrame + + """ + for field, filter_value in filters.items(): + if field not in df.columns: + continue + + # Handle numeric range filters + if isinstance(filter_value, tuple): + operator = filter_value[0] + if operator == "between" and len(filter_value) == 3: + df = df[(df[field] >= filter_value[1]) & (df[field] <= filter_value[2])] + elif operator in (">=", ">", "<=", "<", "==", "!="): + if operator == ">=": + df = df[df[field] >= filter_value[1]] + elif operator == ">": + df = df[df[field] > filter_value[1]] + elif operator == "<=": + df = df[df[field] <= filter_value[1]] + elif operator == "<": + df = df[df[field] < filter_value[1]] + elif operator == "==": + df = df[df[field] == filter_value[1]] + elif operator == "!=": + df = df[df[field] != filter_value[1]] + else: + # Exact match with alias expansion + aliases = self.config.factor_aliases.get(field) + if aliases: + # Expand filter value to all aliases + expanded_values = [filter_value] + for alias_name, actual_values in aliases.items(): + if alias_name == filter_value: + # Add all actual values for this alias + expanded_values.extend([str(v) for v in actual_values]) + df = df[df[field].isin(expanded_values)] + else: + # No aliases, exact match + df = df[df[field] == filter_value] + + return df + + def _get_complete_data( + self, + repo_id: str, + config_name: str, + sample_ids: list[str], + metadata_df: pd.DataFrame, + ) -> pd.DataFrame: + """ + Get complete data (with measurements) for sample_ids. + + Uses WHERE sample_id IN (...) approach for efficient retrieval. + + :param repo_id: Repository ID + :param config_name: Configuration name + :param sample_ids: List of sample IDs to retrieve + :param metadata_df: Metadata DataFrame to merge with + :return: DataFrame with measurements and metadata + + """ + try: + cache_mgr = HfCacheManager( + repo_id, duckdb_conn=duckdb.connect(":memory:"), token=self.token + ) + + # Build IN clause + sample_id_list = ", ".join([f"'{sid}'" for sid in sample_ids]) + sql = f""" + SELECT * + FROM {config_name} + WHERE sample_id IN ({sample_id_list}) + """ + + full_df = cache_mgr.query(sql, config_name) + + # Merge with metadata (metadata_df has normalized fields) + # Drop metadata columns from full_df to avoid duplicates + metadata_cols = [ + col for col in metadata_df.columns if col not in ["sample_id", "dataset_id"] + ] + full_df = full_df.drop(columns=[c for c in metadata_cols if c in full_df.columns], errors="ignore") + + # Merge on sample_id + result = full_df.merge(metadata_df, on="sample_id", how="left") + + return result + + except Exception: + return pd.DataFrame() + + def __repr__(self) -> str: + """String representation.""" + n_repos = len(self.config.repositories) + n_datasets = sum( + len(rc.dataset) if rc.dataset else 0 + for rc in self.config.repositories.values() + ) + n_cached = len(self.cache) + return ( + f"VirtualDB({n_repos} repositories, {n_datasets} datasets configured, " + f"{n_cached} views cached)" + ) From 990f81fe35008d76c109dccf568239857bfbaabc Mon Sep 17 00:00:00 2001 From: chasem Date: Thu, 18 Dec 2025 12:04:46 -0600 Subject: [PATCH 24/49] tmp --- docs/tutorials/virtual_db_tutorial.ipynb | 626 +++++++++++++++++++++-- 1 file changed, 586 insertions(+), 40 deletions(-) diff --git a/docs/tutorials/virtual_db_tutorial.ipynb b/docs/tutorials/virtual_db_tutorial.ipynb index 14c613e..2ea0f72 100644 --- a/docs/tutorials/virtual_db_tutorial.ipynb +++ b/docs/tutorials/virtual_db_tutorial.ipynb @@ -67,7 +67,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Configuration saved to: /tmp/tmpf7f_eml_/vdb_config.yaml\n" + "Configuration saved to: /tmp/tmpi90ps0mq/vdb_config.yaml\n" ] } ], @@ -130,17 +130,11 @@ "metadata": {}, "outputs": [ { - "ename": "ValidationError", - "evalue": "1 validation error for MetadataConfig\n Value error, Invalid configuration for repository 'repositories': 1 validation error for RepositoryConfig\n Value error, Invalid repo-wide property 'BrentLab/harbison_2004': 1 validation error for PropertyMapping\npath\n Field required [type=missing, input_value={'dataset': {'harbison_20...'field': 'condition'}}}}, input_type=dict]\n For further information visit https://errors.pydantic.dev/2.12/v/missing [type=value_error, input_value={'BrentLab/harbison_2004'...emperature_celsius'}}}}}, input_type=dict]\n For further information visit https://errors.pydantic.dev/2.12/v/value_error [type=value_error, input_value={'repositories': {'BrentL...vironmental condition'}}, input_type=dict]\n For further information visit https://errors.pydantic.dev/2.12/v/value_error", - "output_type": "error", - "traceback": [ - "\u001b[31m---------------------------------------------------------------------------\u001b[39m", - "\u001b[31mValidationError\u001b[39m Traceback (most recent call last)", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[2]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;66;03m# Initialize VirtualDB with the configuration\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m vdb = \u001b[43mVirtualDB\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mstr\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mtemp_config\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 4\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33mVirtualDB initialized successfully!\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 5\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mConfigured repositories: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mlen\u001b[39m(vdb.config.repositories)\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/code/tfbp/tfbpapi/tfbpapi/virtual_db.py:191\u001b[39m, in \u001b[36mVirtualDB.__init__\u001b[39m\u001b[34m(self, config_path, token)\u001b[39m\n\u001b[32m 181\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__init__\u001b[39m(\u001b[38;5;28mself\u001b[39m, config_path: Path | \u001b[38;5;28mstr\u001b[39m, token: \u001b[38;5;28mstr\u001b[39m | \u001b[38;5;28;01mNone\u001b[39;00m = \u001b[38;5;28;01mNone\u001b[39;00m):\n\u001b[32m 182\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m 183\u001b[39m \u001b[33;03m Initialize VirtualDB with configuration and optional auth token.\u001b[39;00m\n\u001b[32m 184\u001b[39m \n\u001b[32m (...)\u001b[39m\u001b[32m 189\u001b[39m \n\u001b[32m 190\u001b[39m \u001b[33;03m \"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m191\u001b[39m \u001b[38;5;28mself\u001b[39m.config = \u001b[43mMetadataConfig\u001b[49m\u001b[43m.\u001b[49m\u001b[43mfrom_yaml\u001b[49m\u001b[43m(\u001b[49m\u001b[43mconfig_path\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 192\u001b[39m \u001b[38;5;28mself\u001b[39m.token = token\n\u001b[32m 193\u001b[39m \u001b[38;5;28mself\u001b[39m.cache: \u001b[38;5;28mdict\u001b[39m[\u001b[38;5;28mtuple\u001b[39m[\u001b[38;5;28mstr\u001b[39m, \u001b[38;5;28mstr\u001b[39m], pd.DataFrame] = {}\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/code/tfbp/tfbpapi/tfbpapi/models.py:531\u001b[39m, in \u001b[36mMetadataConfig.from_yaml\u001b[39m\u001b[34m(cls, path)\u001b[39m\n\u001b[32m 528\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(data, \u001b[38;5;28mdict\u001b[39m):\n\u001b[32m 529\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[33m\"\u001b[39m\u001b[33mConfiguration must be a YAML dict\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m--> \u001b[39m\u001b[32m531\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mcls\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mmodel_validate\u001b[49m\u001b[43m(\u001b[49m\u001b[43mdata\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/pydantic/main.py:716\u001b[39m, in \u001b[36mBaseModel.model_validate\u001b[39m\u001b[34m(cls, obj, strict, extra, from_attributes, context, by_alias, by_name)\u001b[39m\n\u001b[32m 710\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m by_alias \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mFalse\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m by_name \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mTrue\u001b[39;00m:\n\u001b[32m 711\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m PydanticUserError(\n\u001b[32m 712\u001b[39m \u001b[33m'\u001b[39m\u001b[33mAt least one of `by_alias` or `by_name` must be set to True.\u001b[39m\u001b[33m'\u001b[39m,\n\u001b[32m 713\u001b[39m code=\u001b[33m'\u001b[39m\u001b[33mvalidate-by-alias-and-name-false\u001b[39m\u001b[33m'\u001b[39m,\n\u001b[32m 714\u001b[39m )\n\u001b[32m--> \u001b[39m\u001b[32m716\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mcls\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m__pydantic_validator__\u001b[49m\u001b[43m.\u001b[49m\u001b[43mvalidate_python\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 717\u001b[39m \u001b[43m \u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 718\u001b[39m \u001b[43m \u001b[49m\u001b[43mstrict\u001b[49m\u001b[43m=\u001b[49m\u001b[43mstrict\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 719\u001b[39m \u001b[43m \u001b[49m\u001b[43mextra\u001b[49m\u001b[43m=\u001b[49m\u001b[43mextra\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 720\u001b[39m \u001b[43m \u001b[49m\u001b[43mfrom_attributes\u001b[49m\u001b[43m=\u001b[49m\u001b[43mfrom_attributes\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 721\u001b[39m \u001b[43m \u001b[49m\u001b[43mcontext\u001b[49m\u001b[43m=\u001b[49m\u001b[43mcontext\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 722\u001b[39m \u001b[43m \u001b[49m\u001b[43mby_alias\u001b[49m\u001b[43m=\u001b[49m\u001b[43mby_alias\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 723\u001b[39m \u001b[43m \u001b[49m\u001b[43mby_name\u001b[49m\u001b[43m=\u001b[49m\u001b[43mby_name\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 724\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[31mValidationError\u001b[39m: 1 validation error for MetadataConfig\n Value error, Invalid configuration for repository 'repositories': 1 validation error for RepositoryConfig\n Value error, Invalid repo-wide property 'BrentLab/harbison_2004': 1 validation error for PropertyMapping\npath\n Field required [type=missing, input_value={'dataset': {'harbison_20...'field': 'condition'}}}}, input_type=dict]\n For further information visit https://errors.pydantic.dev/2.12/v/missing [type=value_error, input_value={'BrentLab/harbison_2004'...emperature_celsius'}}}}}, input_type=dict]\n For further information visit https://errors.pydantic.dev/2.12/v/value_error [type=value_error, input_value={'repositories': {'BrentL...vironmental condition'}}, input_type=dict]\n For further information visit https://errors.pydantic.dev/2.12/v/value_error" + "name": "stdout", + "output_type": "stream", + "text": [ + "VirtualDB initialized successfully!\n", + "Configured repositories: 2\n" ] } ], @@ -163,9 +157,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "All available fields:\n", + " - carbon_source\n", + " - environmental_condition\n", + " - temperature_celsius\n" + ] + } + ], "source": [ "# Get all fields defined in any dataset\n", "all_fields = vdb.get_fields()\n", @@ -177,9 +182,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Common fields (present in all datasets):\n", + " - carbon_source\n", + " - temperature_celsius\n" + ] + } + ], "source": [ "# Get fields present in ALL datasets (common fields)\n", "common_fields = vdb.get_common_fields()\n", @@ -191,9 +206,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Fields in harbison_2004:\n", + " - carbon_source\n", + " - environmental_condition\n", + " - temperature_celsius\n" + ] + } + ], "source": [ "# Get fields for a specific dataset\n", "harbison_fields = vdb.get_fields(\"BrentLab/harbison_2004\", \"harbison_2004\")\n", @@ -214,9 +240,32 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Fetching 1 files: 100%|██████████| 1/1 [00:09<00:00, 9.60s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Unique carbon sources (normalized):\n", + " - unspecified\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], "source": [ "# Get all unique values for a field (normalized)\n", "carbon_sources = vdb.get_unique_values(\"carbon_source\")\n", @@ -228,9 +277,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Carbon sources by dataset:\n", + "\n", + "BrentLab/kemmeren_2014/kemmeren_2014:\n", + " - unspecified\n" + ] + } + ], "source": [ "# Get values broken down by dataset\n", "carbon_by_dataset = vdb.get_unique_values(\"carbon_source\", by_dataset=True)\n", @@ -262,9 +322,104 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 0 samples with glucose\n", + "\n", + "Columns: ['sample_id', 'regulator_locus_tag', 'regulator_symbol', 'carbon_source', 'dataset_id']\n", + "\n", + "First few rows:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "sample_id", + "rawType": "int32", + "type": "integer" + }, + { + "name": "regulator_locus_tag", + "rawType": "object", + "type": "string" + }, + { + "name": "regulator_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "carbon_source", + "rawType": "object", + "type": "string" + }, + { + "name": "dataset_id", + "rawType": "object", + "type": "string" + } + ], + "ref": "57bb029a-d940-4e06-ae20-6eeae41e836a", + "rows": [], + "shape": { + "columns": 5, + "rows": 0 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sample_idregulator_locus_tagregulator_symbolcarbon_sourcedataset_id
\n", + "
" + ], + "text/plain": [ + "Empty DataFrame\n", + "Columns: [sample_id, regulator_locus_tag, regulator_symbol, carbon_source, dataset_id]\n", + "Index: []" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Query all datasets for samples grown on glucose\n", "glucose_samples = vdb.query(filters={\"carbon_source\": \"glucose\"})\n", @@ -286,9 +441,70 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 0 samples from harbison_2004\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + } + ], + "ref": "a0d7c9a2-a3e0-4146-86ec-12e51a2e6968", + "rows": [], + "shape": { + "columns": 0, + "rows": 0 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
" + ], + "text/plain": [ + "Empty DataFrame\n", + "Columns: []\n", + "Index: []" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Query only harbison_2004\n", "harbison_glucose = vdb.query(\n", @@ -311,9 +527,88 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Columns: ['sample_id', 'carbon_source', 'dataset_id']\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "sample_id", + "rawType": "int32", + "type": "integer" + }, + { + "name": "carbon_source", + "rawType": "object", + "type": "string" + }, + { + "name": "dataset_id", + "rawType": "object", + "type": "string" + } + ], + "ref": "6dffa2f6-a87e-4ee2-ba81-22829fb14d26", + "rows": [], + "shape": { + "columns": 3, + "rows": 0 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sample_idcarbon_sourcedataset_id
\n", + "
" + ], + "text/plain": [ + "Empty DataFrame\n", + "Columns: [sample_id, carbon_source, dataset_id]\n", + "Index: []" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Get just sample_id, carbon_source, and temperature\n", "minimal_data = vdb.query(\n", @@ -343,9 +638,100 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 0 samples with glucose at 30C\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "sample_id", + "rawType": "int32", + "type": "integer" + }, + { + "name": "regulator_locus_tag", + "rawType": "object", + "type": "string" + }, + { + "name": "regulator_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "carbon_source", + "rawType": "object", + "type": "string" + }, + { + "name": "dataset_id", + "rawType": "object", + "type": "string" + } + ], + "ref": "8309daf0-f866-44e1-8ca4-3b5fa071dc9b", + "rows": [], + "shape": { + "columns": 5, + "rows": 0 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sample_idregulator_locus_tagregulator_symbolcarbon_sourcedataset_id
\n", + "
" + ], + "text/plain": [ + "Empty DataFrame\n", + "Columns: [sample_id, regulator_locus_tag, regulator_symbol, carbon_source, dataset_id]\n", + "Index: []" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Samples with glucose at 30C\n", "glucose_30c = vdb.query(\n", @@ -368,9 +754,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 1487 samples at >= 30C\n", + "Found 1487 samples between 28-32C\n" + ] + } + ], "source": [ "# Samples at temperature >= 30C\n", "warm_samples = vdb.query(\n", @@ -398,9 +793,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 0 galactose samples\n", + "\n", + "This query internally expanded to:\n", + " WHERE carbon_source IN ('D-galactose', 'gal', 'galactose')\n" + ] + } + ], "source": [ "# Query for \"galactose\" matches \"D-galactose\", \"gal\", and \"galactose\"\n", "galactose_samples = vdb.query(filters={\"carbon_source\": \"galactose\"})\n", @@ -422,9 +828,73 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Complete data: 0 rows\n", + "Columns: []\n", + "\n", + "First few measurements:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + } + ], + "ref": "a3896349-b6ba-4827-b7be-e4cfa318f2b2", + "rows": [], + "shape": { + "columns": 0, + "rows": 0 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
" + ], + "text/plain": [ + "Empty DataFrame\n", + "Columns: []\n", + "Index: []" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Get complete data with measurements\n", "complete_data = vdb.query(\n", @@ -441,9 +911,70 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Binding data: 0 measurements\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + } + ], + "ref": "fc59efba-e365-4b1c-b9ff-31828897cde2", + "rows": [], + "shape": { + "columns": 0, + "rows": 0 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
" + ], + "text/plain": [ + "Empty DataFrame\n", + "Columns: []\n", + "Index: []" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# You can combine complete=True with field selection\n", "# Get just the binding data columns\n", @@ -548,9 +1079,24 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "KeyError", + "evalue": "'_repo_id'", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mKeyError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[16]\u001b[39m\u001b[32m, line 7\u001b[39m\n\u001b[32m 4\u001b[39m all_samples = vdb.query()\n\u001b[32m 6\u001b[39m \u001b[38;5;66;03m# Count by dataset and carbon source\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m7\u001b[39m summary = \u001b[43mall_samples\u001b[49m\u001b[43m.\u001b[49m\u001b[43mgroupby\u001b[49m\u001b[43m(\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43m_repo_id\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43m_config_name\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43mcarbon_source\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m.size()\n\u001b[32m 8\u001b[39m summary = summary.reset_index(name=\u001b[33m'\u001b[39m\u001b[33mnum_samples\u001b[39m\u001b[33m'\u001b[39m)\n\u001b[32m 10\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33mSample counts by dataset and carbon source:\u001b[39m\u001b[33m\"\u001b[39m)\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/pandas/core/frame.py:9210\u001b[39m, in \u001b[36mDataFrame.groupby\u001b[39m\u001b[34m(self, by, axis, level, as_index, sort, group_keys, observed, dropna)\u001b[39m\n\u001b[32m 9207\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m level \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m by \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m 9208\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[33m\"\u001b[39m\u001b[33mYou have to supply one of \u001b[39m\u001b[33m'\u001b[39m\u001b[33mby\u001b[39m\u001b[33m'\u001b[39m\u001b[33m and \u001b[39m\u001b[33m'\u001b[39m\u001b[33mlevel\u001b[39m\u001b[33m'\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m-> \u001b[39m\u001b[32m9210\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mDataFrameGroupBy\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 9211\u001b[39m \u001b[43m \u001b[49m\u001b[43mobj\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 9212\u001b[39m \u001b[43m \u001b[49m\u001b[43mkeys\u001b[49m\u001b[43m=\u001b[49m\u001b[43mby\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9213\u001b[39m \u001b[43m \u001b[49m\u001b[43maxis\u001b[49m\u001b[43m=\u001b[49m\u001b[43maxis\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9214\u001b[39m \u001b[43m \u001b[49m\u001b[43mlevel\u001b[49m\u001b[43m=\u001b[49m\u001b[43mlevel\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9215\u001b[39m \u001b[43m \u001b[49m\u001b[43mas_index\u001b[49m\u001b[43m=\u001b[49m\u001b[43mas_index\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9216\u001b[39m \u001b[43m \u001b[49m\u001b[43msort\u001b[49m\u001b[43m=\u001b[49m\u001b[43msort\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9217\u001b[39m \u001b[43m \u001b[49m\u001b[43mgroup_keys\u001b[49m\u001b[43m=\u001b[49m\u001b[43mgroup_keys\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9218\u001b[39m \u001b[43m \u001b[49m\u001b[43mobserved\u001b[49m\u001b[43m=\u001b[49m\u001b[43mobserved\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9219\u001b[39m \u001b[43m \u001b[49m\u001b[43mdropna\u001b[49m\u001b[43m=\u001b[49m\u001b[43mdropna\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9220\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/pandas/core/groupby/groupby.py:1331\u001b[39m, in \u001b[36mGroupBy.__init__\u001b[39m\u001b[34m(self, obj, keys, axis, level, grouper, exclusions, selection, as_index, sort, group_keys, observed, dropna)\u001b[39m\n\u001b[32m 1328\u001b[39m \u001b[38;5;28mself\u001b[39m.dropna = dropna\n\u001b[32m 1330\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m grouper \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m-> \u001b[39m\u001b[32m1331\u001b[39m grouper, exclusions, obj = \u001b[43mget_grouper\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 1332\u001b[39m \u001b[43m \u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1333\u001b[39m \u001b[43m \u001b[49m\u001b[43mkeys\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1334\u001b[39m \u001b[43m \u001b[49m\u001b[43maxis\u001b[49m\u001b[43m=\u001b[49m\u001b[43maxis\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1335\u001b[39m \u001b[43m \u001b[49m\u001b[43mlevel\u001b[49m\u001b[43m=\u001b[49m\u001b[43mlevel\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1336\u001b[39m \u001b[43m \u001b[49m\u001b[43msort\u001b[49m\u001b[43m=\u001b[49m\u001b[43msort\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1337\u001b[39m \u001b[43m \u001b[49m\u001b[43mobserved\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mobserved\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mis\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mlib\u001b[49m\u001b[43m.\u001b[49m\u001b[43mno_default\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01melse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mobserved\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1338\u001b[39m \u001b[43m \u001b[49m\u001b[43mdropna\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mdropna\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1339\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1341\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m observed \u001b[38;5;129;01mis\u001b[39;00m lib.no_default:\n\u001b[32m 1342\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28many\u001b[39m(ping._passed_categorical \u001b[38;5;28;01mfor\u001b[39;00m ping \u001b[38;5;129;01min\u001b[39;00m grouper.groupings):\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/pandas/core/groupby/grouper.py:1043\u001b[39m, in \u001b[36mget_grouper\u001b[39m\u001b[34m(obj, key, axis, level, sort, observed, validate, dropna)\u001b[39m\n\u001b[32m 1041\u001b[39m in_axis, level, gpr = \u001b[38;5;28;01mFalse\u001b[39;00m, gpr, \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m 1042\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m-> \u001b[39m\u001b[32m1043\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m(gpr)\n\u001b[32m 1044\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(gpr, Grouper) \u001b[38;5;129;01mand\u001b[39;00m gpr.key \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m 1045\u001b[39m \u001b[38;5;66;03m# Add key to exclusions\u001b[39;00m\n\u001b[32m 1046\u001b[39m exclusions.add(gpr.key)\n", + "\u001b[31mKeyError\u001b[39m: '_repo_id'" + ] + } + ], "source": [ "# Compare number of samples by carbon source across datasets\n", "\n", From d33274e36bf2354c0a54dcd542b143f524ebec7e Mon Sep 17 00:00:00 2001 From: chasem Date: Wed, 7 Jan 2026 17:11:11 -0600 Subject: [PATCH 25/49] tfbpapi virtual_db is working --- docs/brentlab_yeastresources_collection.md | 24 +- docs/huggingface_datacard.md | 42 +- docs/tutorials/cache_manager_tutorial.ipynb | 224 +- docs/tutorials/datacard_tutorial.ipynb | 89 +- docs/tutorials/virtual_db_tutorial.ipynb | 4202 +++++++++++++----- docs/virtual_database_concepts.md | 284 +- docs/virtual_db.md | 5 + pyproject.toml | 10 - tfbpapi/datacard.py | 67 +- tfbpapi/hf_cache_manager.py | 59 +- tfbpapi/models.py | 224 +- tfbpapi/tests/example_datacards.py | 5 +- tfbpapi/tests/test_hf_cache_manager.py | 271 +- tfbpapi/tests/test_metadata_config_models.py | 122 +- tfbpapi/tests/test_models.py | 4 +- tfbpapi/tests/test_virtual_db.py | 346 +- tfbpapi/virtual_db.py | 615 ++- 17 files changed, 4910 insertions(+), 1683 deletions(-) diff --git a/docs/brentlab_yeastresources_collection.md b/docs/brentlab_yeastresources_collection.md index 29d3561..4f49a02 100644 --- a/docs/brentlab_yeastresources_collection.md +++ b/docs/brentlab_yeastresources_collection.md @@ -4,7 +4,7 @@ This document describes the BrentLab yeast resources collection on HuggingFace a ## Collection Overview -The BrentLab yeast resources collection contains 10 datasets related to yeast transcription factor binding and gene expression regulation: +The BrentLab yeast resources collection contains 11 datasets related to yeast transcription factor binding and gene expression regulation: 1. **barkai_compendium** - ChEC-seq binding data across multiple GEO series 2. **callingcards** - Calling Cards transposon-based binding data @@ -15,7 +15,8 @@ The BrentLab yeast resources collection contains 10 datasets related to yeast tr 7. **kemmeren_2014** - TF deletion expression profiling 8. **mahendrawada_2025** - ChEC-seq and nascent RNA-seq data 9. **rossi_2021** - ChIP-exo binding data -10. **yeast_genome_resources** - Reference genomic features +10. **yeast_comparative_analysis** - Cross-dataset comparative analyses +11. **yeast_genome_resources** - Reference genomic features ## Standardized Media Names @@ -234,6 +235,25 @@ Separate metadata configs or embedded metadata via `metadata_fields`: metadata_fields: ["regulator_locus_tag", "regulator_symbol", "condition"] ``` +### comparative + +**yeast_comparative_analysis** provides cross-dataset analysis results: + +- **dto config**: Direct Target Overlap analysis comparing binding and perturbation experiments +- Uses `source_sample` role for composite identifiers +- Format: `"repo_id;config_name;sample_id"` (semicolon-separated) +- Contains 8 quantitative measures: rank thresholds, set sizes, FDR, p-values +- Partitioned by binding_repo_dataset and perturbation_repo_dataset + +**Composite Sample Identifiers**: +Comparative datasets use composite identifiers to reference samples from other datasets: +- `binding_id`: Points to a binding experiment (e.g., `BrentLab/callingcards;annotated_features;1`) +- `perturbation_id`: Points to a perturbation experiment (e.g., `BrentLab/hackett_2020;hackett_2020;200`) + +**Typical structure**: source_sample_1 x source_sample_2 x ... x measurements + +**Use case**: Answer questions like "Which binding experiments show significant overlap with perturbation effects?" + ## Categorical Condition Definitions Many datasets define categorical experimental conditions using the `definitions` field. diff --git a/docs/huggingface_datacard.md b/docs/huggingface_datacard.md index 5cb30d7..d56c771 100644 --- a/docs/huggingface_datacard.md +++ b/docs/huggingface_datacard.md @@ -50,20 +50,42 @@ Position-level data across genomic coordinates ### 4. `metadata` Experimental metadata and sample descriptions -- **Use case**: Sample information, experimental conditions, protocol details +- **Use case**: Sample information, experimental conditions, protocol details. Note + that this can also include per-sample QC metrics. For cross-sample QC or analysis, + see [comparative](#5-comparative) below. - **Structure**: One row per sample - **Common fields**: Sample identifiers, experimental conditions, publication info - **Special field**: `applies_to` - Optional list of config names this metadata applies to -### 5. `qc_data` -Quality control metrics and assessments -- **Use case**: QC metrics derived from raw or processed data, cross-dataset quality - assessments, validation metrics -- **Structure**: One row per sample, measurement, or QC evaluation -- **Common fields**: QC metrics, quality flags, threshold references, possibly - references to source datasets -- **Note**: QC datasets can be derived from single or multiple source configs within - a repository or across repositories +### 5. `comparative` + +Quality control metrics, validation results, and cross-dataset analysis outputs. + +**Use cases**: +- Cross-dataset quality assessments and validation metrics +- Analysis results relating samples across datasets or repositories +- Comparative analyses (e.g., binding vs expression correlation) + +**Structure**: One row represents an observation on 2 or more samples. Note that the + name of the column containing the sample references isn't specified. However, the + role and format of the sample references are strictly defined. See + [Defining Sample References](#defining-sample-references) below. + +#### Defining Sample References + +The name of the field which contains the sample reference is user-defined. However, +the contents of that field, and its role, must be as follows: + +- **`source_sample`**: Fields containing composite sample identifiers. This must be in + the format `"repo_id;config_name;sample_id"`. + +``` +"repo_id;config_name;sample_id" +``` + +Examples: +- `"BrentLab/harbison_2004;harbison_2004;CBF1_YPD"` +- `"BrentLab/kemmeren_2014;kemmeren_2014;sample_42"` ## Experimental Conditions diff --git a/docs/tutorials/cache_manager_tutorial.ipynb b/docs/tutorials/cache_manager_tutorial.ipynb index a905a8b..899b45f 100644 --- a/docs/tutorials/cache_manager_tutorial.ipynb +++ b/docs/tutorials/cache_manager_tutorial.ipynb @@ -48,8 +48,8 @@ "HfCacheManager initialized for: BrentLab/mahendrawada_2025\n", "DuckDB connection: Active\n", "Logger configured: Yes\n", - "Current HF cache size: 5.2G\n", - "Cached repositories: 10\n" + "Current HF cache size: 5.5G\n", + "Cached repositories: 11\n" ] } ], @@ -92,7 +92,7 @@ { "data": { "text/plain": [ - "HFCacheInfo(size_on_disk=5198144267, repos=frozenset({CachedRepoInfo(repo_id='BrentLab/barkai_compendium', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium'), size_on_disk=3579068970, nb_files=504, revisions=frozenset({CachedRevisionInfo(commit_hash='a987ef37c72fcd07b18828d320bc4305480daade', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade'), size_on_disk=3579068970, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417639/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/43c5fb0a4f049fa5895f8167928d047cc4954aa37db1fc8a9892fe9f99c401bf'), size_on_disk=782319, blob_last_accessed=1756926308.7694294, blob_last_modified=1756926309.6284256), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417694/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/058b507f75cb01bb5912e14e7bbb4e25a1de945e578323bce9f920f31a9ced82'), size_on_disk=11980395, blob_last_accessed=1756926320.09838, blob_last_modified=1756926321.5623736), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417643/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/66dfa3feea4410ba34f5d5d37c7b01e1fa2cdd4ff38f9dfb10d20d55e40e449f'), size_on_disk=1012185, blob_last_accessed=1756926309.326427, blob_last_modified=1756926310.0784237), CachedFileInfo(file_name='genome_map.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b8f0ed936cb43c4649126e9b7596cef0ab626a2d'), size_on_disk=142786, blob_last_accessed=1756926300.410466, blob_last_modified=1756926300.4954655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417701/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a0dd9587c7525a610a3794cf41f6db24a6ba22a12ed852dc27381a68db3e8fca'), size_on_disk=8781514, blob_last_accessed=1756926321.6273735, blob_last_modified=1756926323.1263669), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417706/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4f1ea5592a855f3bf5739b0caae1c69c7606276d125c3dfe8e98379d12bedf1b'), size_on_disk=1154142, blob_last_accessed=1756926322.9053679, blob_last_modified=1756926323.5173652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381054/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1a9f9c650973937d852d2de54c0d73336d1516184d29e203563fd9bf8d7fefa5'), size_on_disk=3346485, blob_last_accessed=1756926778.1077476, blob_last_modified=1756926778.8637433), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417619/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0a4d4159941caa34a5e079821d49047d1434077926b2933c27d384e04ed2e5b5'), size_on_disk=9949802, blob_last_accessed=1756926302.5744565, blob_last_modified=1756926304.05045), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380996/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/09e663ce2a60df5635f4e4ea77d6350e7752a9bb5e81fbdfd818869ac53ca805'), size_on_disk=13309208, blob_last_accessed=1756926375.0511415, blob_last_modified=1756926376.0221374), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380977/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/74237b16fd38090785d203d96bae72e1f5ce919d423f793cfb9d3b86623358e6'), size_on_disk=11114601, blob_last_accessed=1756926371.6651561, blob_last_modified=1756926373.12615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417906/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/34d3381dd45e579dcc298e91cc95174c4219cf548401b8f2392cf746fd508c5b'), size_on_disk=13795318, blob_last_accessed=1756926356.3642225, blob_last_modified=1756926357.640217), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417920/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/00a869703f03e3df3f0f51a7d93d52dd7eac5174ed2a1b752207460e71c7b41a'), size_on_disk=14880236, blob_last_accessed=1756926359.1242106, blob_last_modified=1756926360.409205), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381060/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1bb05aa71fe55db65fc3868b131854c1e6666dca8cc370874f517aa0756d21ac'), size_on_disk=9137992, blob_last_accessed=1756926778.9637427, blob_last_modified=1756926780.2307358), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417890/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3eca80a3220fd0294fdfca83740ba70a20af764d28833494948705c4d542ded3'), size_on_disk=9018889, blob_last_accessed=1756926353.7652338, blob_last_modified=1756926354.7252295), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380943/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/987771cb978dbc949eff5044071149a00223c6771efcc870850e3c8cbe0648c3'), size_on_disk=13397144, blob_last_accessed=1756926367.295175, blob_last_modified=1756926368.7601688), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417662/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/90008c75533e5a395bf92b2a4382cfb8d6c27af4921615cac22bc1965375efb5'), size_on_disk=12701661, blob_last_accessed=1756926313.0204108, blob_last_modified=1756926314.158406), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417752/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/efc9f1ecf4a7b06b23da46fb8c7d1879b67a9d6603ed753d09e2028396ec05c5'), size_on_disk=3420764, blob_last_accessed=1756926329.26534, blob_last_modified=1756926330.0613368), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417848/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eec4ffff5d145c0ee0850d5e68787ab90e6510cffa5803579db44e3a575822dc'), size_on_disk=13556826, blob_last_accessed=1756926346.1882668, blob_last_modified=1756926347.2852619), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380950/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f508a5da82832b920577592b853da2c2d76ebf0adca587e10b63507f46dc5066'), size_on_disk=3452513, blob_last_accessed=1756926368.7781687, blob_last_modified=1756926369.7081647), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417940/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/270559ca1db4605de241e8642461b4d7304d124e4fc624f7d8a8a59867301a4c'), size_on_disk=2328150, blob_last_accessed=1756926363.182193, blob_last_modified=1756926363.9021897), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417730/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f2554716083cfa1bb54c2098315b92e1b3537eb265da884c00ba7e7d4c050da'), size_on_disk=14980864, blob_last_accessed=1756926325.5143564, blob_last_modified=1756926326.803351), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417632/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/23c561fde2e6c2973ec1913373c343a5c49d6cc0dbb44ff21eb5aff87cd77d3a'), size_on_disk=18481828, blob_last_accessed=1756926306.2764404, blob_last_modified=1756926308.5744302), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417736/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/671a67d7275810840cafc9db32e43385c4ba61b007d702e7eb5cb244a0e4afa9'), size_on_disk=13855124, blob_last_accessed=1756926326.1693537, blob_last_modified=1756926327.261349), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417684/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eee25a9a91c54d0404db884532d1b85555f2e1de0c664abcbda4343f9d729842'), size_on_disk=10310520, blob_last_accessed=1756926317.7173905, blob_last_modified=1756926319.3473833), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417761/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ba86b2041414034d38b95ccbf3c65957642c56b56914240999986ebc76b9e193'), size_on_disk=1635118, blob_last_accessed=1756926330.478335, blob_last_modified=1756926331.0933323), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417813/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f7d5239d0d39e171aad1e84964f8cdc3c7a325fe8c38b6829e114cb3997e658'), size_on_disk=10238322, blob_last_accessed=1756926339.4142962, blob_last_modified=1756926340.8722897), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417823/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1887333f71d11fe7c43748662f58d9ba3fb12f3f6ca30a61d7e1c7a0c95f7064'), size_on_disk=9444476, blob_last_accessed=1756926341.6372864, blob_last_modified=1756926342.8442812), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417791/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8d2eb1bdd1a5cae023abb271e5858cf111879953cb0d6267f4ebf21a3d30c4ad'), size_on_disk=5232904, blob_last_accessed=1756926335.2033143, blob_last_modified=1756926336.417309), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417915/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9fbfc50c3e1f756169d25442880ba33e5cf237e3c83a0d674b64ace47e8d3322'), size_on_disk=13801295, blob_last_accessed=1756926358.318214, blob_last_modified=1756926359.4592092), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417614/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dac699f09872b0c58b742c692b341f0b00b7d83284cec5ef838c276a6f48908c'), size_on_disk=1759475, blob_last_accessed=1756926301.7274601, blob_last_modified=1756926302.4774568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417648/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fb706eb700e8fc003822b897accd3501360d737fd1a9ffcf26c3e9d9b25a5e35'), size_on_disk=1285054, blob_last_accessed=1756926310.3104227, blob_last_modified=1756926311.0174196), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381013/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c831ca7cc9cfd2cc2ccd92a0168b2e14dedf2bde9e29780dfaf6d281b31e9462'), size_on_disk=4648281, blob_last_accessed=1756926377.4421313, blob_last_modified=1756926378.3451273), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381040/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a7c622a8ae1f5083eef26ecbcc2444055a9265b674ae80002e5468c94c0eb070'), size_on_disk=9682671, blob_last_accessed=1756926774.782766, blob_last_modified=1756926776.7967548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417797/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/12ee278356fa75b0ad555fe06376c78b71630324b7b89aa28abe960c187d256f'), size_on_disk=14391201, blob_last_accessed=1756926336.3743093, blob_last_modified=1756926338.322301), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417665/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7c9b3d448e53fcac9334df092cc003f438cbc462e6785ac3d61202bd65a7125f'), size_on_disk=7481605, blob_last_accessed=1756926313.490409, blob_last_modified=1756926314.7744033), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417914/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d062dbf660e1ef240ff8ef99ff2d486d9caaabda14575e6365ce6f725fa91ccf'), size_on_disk=8599250, blob_last_accessed=1756926358.2422144, blob_last_modified=1756926360.3412054), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381018/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/878bf028aca9daeebe7ed0f8ee522580d512771ec4a5e35bfc62ac675176ad15'), size_on_disk=701951, blob_last_accessed=1756926378.2281277, blob_last_modified=1756926378.643126), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8168980e59c50149bfb9866a1e8855c235f2882b988df99331c9247f65e9813e'), size_on_disk=2289686, blob_last_accessed=1756926376.4721353, blob_last_modified=1756926377.2151322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417725/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4195b9ed6506437a735fd8061cd1b79211c99169c4319c4f72b801a501f2b0f9'), size_on_disk=2233032, blob_last_accessed=1756926324.958359, blob_last_modified=1756926325.594356), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417863/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4aabb9d1a4b105c7669deb0f13cd635ab7433ed1ae02f5785aad014d604f1504'), size_on_disk=6900487, blob_last_accessed=1756926348.4532568, blob_last_modified=1756926349.2742534), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417617/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9ad746ec09ed51ed497d87f3dd64b11916b70e6833ce66141bc8feb6ba73d1d3'), size_on_disk=23082279, blob_last_accessed=1756926302.1754582, blob_last_modified=1756926303.6944516), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417943/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0e4191a403f5070636bcb281bd80d39e11c70fb502fa2b18150032d58f2e0740'), size_on_disk=5396199, blob_last_accessed=1756926363.7271905, blob_last_modified=1756926364.542187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417790/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f88bed27521de8285bbb92cf95242820116e6fa2c23ab578e6608a6a25e0c04e'), size_on_disk=10836858, blob_last_accessed=1756926335.078315, blob_last_modified=1756926336.1843102), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417794/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f15be566c029c52cf46f6258e589529afb3a002f010710a54d04dd6151f8b684'), size_on_disk=10205023, blob_last_accessed=1756926335.9063113, blob_last_modified=1756926337.151306), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380952/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3811d6e6194d8b88c84fc0931cba844da23677e76e1e9a25e2890df968a7e529'), size_on_disk=1003172, blob_last_accessed=1756926368.8351684, blob_last_modified=1756926369.4641657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417847/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4dd39704bc9c1d60a8271f16206887171ee92abde023c53501c1560533a688c1'), size_on_disk=4755120, blob_last_accessed=1756926346.125267, blob_last_modified=1756926346.8372638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381023/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/22e3e0f4cd1f2c755ec1ac13b99d2d6c98d0158319478f2deca4e89aefc5e88e'), size_on_disk=3860195, blob_last_accessed=1756926378.5651264, blob_last_modified=1756926379.6101217), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417827/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8bfd1221f930e86ffd6150c82aae85f00a23fff33c672e40f47d671f5a2e4d17'), size_on_disk=7291599, blob_last_accessed=1756926342.156284, blob_last_modified=1756926343.1502798), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417886/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62fe87e7519370c4ac46e6d9d0abf314a8f92d9a5d6804ebddae0ed095641219'), size_on_disk=7968977, blob_last_accessed=1756926352.9472373, blob_last_modified=1756926353.7992337), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380934/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d264f2a91c87696f6a07f573f1d921c91e1d53e3afca59610ba4703b3683b617'), size_on_disk=4601537, blob_last_accessed=1756926366.1091802, blob_last_modified=1756926366.9191768), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417609/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e3d8451242fe9ecf9f82e2ac1a406144840a6182178228e6e1fa67e6a04a318a'), size_on_disk=3176651, blob_last_accessed=1756926300.651465, blob_last_modified=1756926302.0864584), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417849/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/23b6606ffcf116cf066b103bf1b6b4fb33c40e544e20e4565fa44c5bbcea4bdd'), size_on_disk=5689527, blob_last_accessed=1756926346.584265, blob_last_modified=1756926347.3892615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417808/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0251ae27bd961d6c962253c555963b42148092cdaa2616a78f6872cf1a4e5e4d'), size_on_disk=12900784, blob_last_accessed=1756926338.3973005, blob_last_modified=1756926339.439296), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417628/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/291f0e4d2d0fa1656de1583bd9e131fd4d2fc1934d0bb70a6774f7e7f90e62ae'), size_on_disk=15444530, blob_last_accessed=1756926305.1314452, blob_last_modified=1756926307.1334364), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417671/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bced5a56092fea7de5754bebc33caaab643f27236f8d9020e7bc6b30b9045d6e'), size_on_disk=5347180, blob_last_accessed=1756926314.7704034, blob_last_modified=1756926315.8293986), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381062/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/76151e17f242c5108010debea961b0b13f3821be2fcb77fd923a36cc0e672695'), size_on_disk=4880117, blob_last_accessed=1756926779.3357406, blob_last_modified=1756926780.5167341), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417843/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6569e23f0eb660e9f7111b9cdd69e7ae797ba265647ee44e4cbf2d8babdeca9e'), size_on_disk=2706587, blob_last_accessed=1756926345.3402703, blob_last_modified=1756926346.125267), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c103eef2a9834296fb1328b7012b5b9184c5a3f131817dfca257843e863d34b2'), size_on_disk=9829417, blob_last_accessed=1756926362.1531975, blob_last_modified=1756926363.3531923), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bbfeb8571ee8915d793698c2116cef9f2ea61573c66b4ae7066078271c6172d6'), size_on_disk=5132301, blob_last_accessed=1756926362.6441953, blob_last_modified=1756926363.7271905), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417756/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ede027f2c13a1e6669becf6194ace85af5800d5fe77db16ec47bfb3abaa735d3'), size_on_disk=8620421, blob_last_accessed=1756926329.968337, blob_last_modified=1756926331.0303326), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417700/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1240fbb2497074bb7ff98b1bc6399b8a8ad5d320246285abfb6738e27e2bbdeb'), size_on_disk=19312447, blob_last_accessed=1756926321.6763732, blob_last_modified=1756926322.8383682), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380951/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b9b68679dd2da94e4aa4852ae3de3ee5b823c1fb1483f4ece3eee5d947014785'), size_on_disk=1154119, blob_last_accessed=1756926368.8281684, blob_last_modified=1756926369.410166), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417687/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fa4f79173f5a5ed278c7ef80d9fb220aa9c615de97bae7963969e27ecf0e587c'), size_on_disk=13352339, blob_last_accessed=1756926318.3003879, blob_last_modified=1756926320.3913789), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3fe2828766efd936f02efae2da3e068ef9b28ea374e594e09850e3e8ff0d8164'), size_on_disk=4421540, blob_last_accessed=1756926375.7961383, blob_last_modified=1756926376.7581341), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417642/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/591f688d0d9f98350a1d636e6f93ed37563f3ea9765ca6b633490533b0cf32d4'), size_on_disk=11498542, blob_last_accessed=1756926309.2804272, blob_last_modified=1756926310.8714201), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417817/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ccf81aa26021a009a212ef8f8bee71c12f5c8d0a2b97ffdb2c4d86d13c8b8133'), size_on_disk=7521086, blob_last_accessed=1756926340.0802932, blob_last_modified=1756926341.3222878), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381047/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dda6f6ca669a0e5c9d34b548305cbeb6b82f1b70086e06d3604e7e096fd7fb25'), size_on_disk=6799004, blob_last_accessed=1756926776.8777544, blob_last_modified=1756926778.4717455), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417689/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e27d1d153e5b135a09ec5db80bb05ace721fd60ac90f0693ac34ca3160fdfbdd'), size_on_disk=5015020, blob_last_accessed=1756926319.2973835, blob_last_modified=1756926321.220375), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417909/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c5de46a74c16aee32dfb2be89a50dceaa5704c0c550076dcec483d1c21d983af'), size_on_disk=5969725, blob_last_accessed=1756926357.3362184, blob_last_modified=1756926358.5162132), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417634/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/529d984782684991b2eb7be2d345b5a4e8a6e08d3dbe30ad34495d8380464e6e'), size_on_disk=16306189, blob_last_accessed=1756926306.810438, blob_last_modified=1756926310.239423), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417668/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b426513aab4e071a24b7d27100a4da4da8c71c8e9b3e82163808b0ec6a42b8d0'), size_on_disk=6011682, blob_last_accessed=1756926314.5374043, blob_last_modified=1756926315.4354005), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417947/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/50bb9cc74904c9d3b6cc3ec8bc12c7ad1fb89204be6f89a863e703a94d224512'), size_on_disk=4023877, blob_last_accessed=1756926364.0161893, blob_last_modified=1756926364.9561853), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417895/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2daca9b311da1afa40f578c6df7dc4b0039dd4857663c98cd87c665a547b7144'), size_on_disk=11915424, blob_last_accessed=1756926354.7232296, blob_last_modified=1756926355.8262248), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380933/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/07c22e11c56ce5ecb6c22c910c3d3a0d9b54fd341bef0513b4bf42a9c53ec6a0'), size_on_disk=10138164, blob_last_accessed=1756926365.8761814, blob_last_modified=1756926366.7471776), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417789/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6bd6339b995a5730b6b65ec65f680b57195469ebee9f2e15de6e604b069d4b0e'), size_on_disk=9612750, blob_last_accessed=1756926334.6493168, blob_last_modified=1756926335.8183117), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4c942b91ac018330aeb4da512a3e47ae4531a434b6e9933cf3e2bb476a77f9ca'), size_on_disk=6312758, blob_last_accessed=1756926366.1041803, blob_last_modified=1756926367.0141764), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381043/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f6ded8e6496dcfdd499edc382d19da2071f25daebbb250b375b9a2706e9d8211'), size_on_disk=9139336, blob_last_accessed=1756926775.6057615, blob_last_modified=1756926777.1057532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417683/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6131abcd72bd4c6a8627685c4df8a295af50e824f52a5cde156adae7cd694bba'), size_on_disk=15545733, blob_last_accessed=1756926317.382392, blob_last_modified=1756926319.0483847), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417766/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/39da4f16b82cc0b5019d8526f2e3eed49de9760e319f8e839db621bf4c1ab021'), size_on_disk=6482957, blob_last_accessed=1756926331.2123318, blob_last_modified=1756926332.1133277), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417613/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/695d3095619f3a313d25c506b9d0493f171896f1439bcf3bd1cd932d1f6fed5d'), size_on_disk=2812565, blob_last_accessed=1756926301.6434605, blob_last_modified=1756926302.4814568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380936/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5a931bc1181001314c3822051c493ba7a8b87b5bb26b8866783612f2bcf49c62'), size_on_disk=3467608, blob_last_accessed=1756926366.19118, blob_last_modified=1756926367.2261755), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381015/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/345c83c5ab13d230542683014d3d6073ba5a348e3063fea325843981c6c6e7c1'), size_on_disk=3807314, blob_last_accessed=1756926377.67913, blob_last_modified=1756926378.4971266), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381036/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ff76dabe83d2948240dd846fcc2a969380b7bd9f8d2eff2c71c299f0b14134cd'), size_on_disk=2593393, blob_last_accessed=1756926379.8661208, blob_last_modified=1756926380.5611176), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417675/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/79072181ebce12769f2f0a1971977d6691dde1841b53d7b54cf9d4f47f53b3dc'), size_on_disk=6478433, blob_last_accessed=1756926315.763399, blob_last_modified=1756926317.2643924), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417806/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2befcad73b2aa8170ceaf8c249ceba45e87037771e86c9bb4bd7addc5815bbec'), size_on_disk=5276603, blob_last_accessed=1756926337.9863024, blob_last_modified=1756926338.8112986), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380979/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef743a2a6a8fe44b62963b981ef0429676e7854243ab07d450d9c3ab569c7e8e'), size_on_disk=3735979, blob_last_accessed=1756926372.2201538, blob_last_modified=1756926373.6791475), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417656/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/03f8707a323ef7563c8ec2d6352d24c6454d043c4bbbca417680df5c7db208f8'), size_on_disk=10029839, blob_last_accessed=1756926311.588417, blob_last_modified=1756926312.8104117), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380928/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/251ffcb8e06f2ac0db0791065248327a9d1eeef33a07f5c6b25946e04b6c46d8'), size_on_disk=2289693, blob_last_accessed=1756926365.037185, blob_last_modified=1756926366.1961799), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417674/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5970db11c3efe2bba4eb50d07480cb0b46ed74dfc3ddf12d611ecd61f1d5fb68'), size_on_disk=4619941, blob_last_accessed=1756926315.5114, blob_last_modified=1756926317.630391), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417667/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef18f0ec136ef4b0facd217b32604556a7c9f514d75e26d2be17eb8da9b74a14'), size_on_disk=7979245, blob_last_accessed=1756926314.2284057, blob_last_modified=1756926315.734399), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fb5958ec4e10f5c7e19ebc55b24f26012e6d8ccf'), size_on_disk=8004, blob_last_accessed=1756926300.2594666, blob_last_modified=1756926300.3214662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417931/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7e70415ed0dfb1eb42e46789c905ccf2b593dc192d907d939d87acd59cd963e1'), size_on_disk=4991063, blob_last_accessed=1756926361.306201, blob_last_modified=1756926363.1161933), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417825/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/efc4971b63b21d6aee96e17d9e9b2cc2e3fd34608cc3722e36032c594fc03e48'), size_on_disk=8550531, blob_last_accessed=1756926341.8732853, blob_last_modified=1756926345.0782714), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417907/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b24926bd36cf33ea9107043b9a47db8239ad6208745c56002513225e8cb80a6e'), size_on_disk=7594380, blob_last_accessed=1756926356.7332208, blob_last_modified=1756926357.882216), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381068/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/16e4412527449a3cb9d2da129c3309a71b2ec9b33a7a39cb9bb31d41ce5d2e04'), size_on_disk=14909067, blob_last_accessed=1756926780.5687337, blob_last_modified=1756926782.3187242), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417753/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a8969c75211310735ad33d5af57327f2babc86276adc7cefcda254cc9fc9baa5'), size_on_disk=6433095, blob_last_accessed=1756926329.577339, blob_last_modified=1756926330.4023352), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417750/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aedd94fe4b42547108a807242052397b6dfbfda0eb4e56a9f2e267b312251147'), size_on_disk=1531584, blob_last_accessed=1756926329.1973405, blob_last_modified=1756926329.9403372), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381020/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c85f953439e9f6918c7f7c8194bcbbebe0ef8a96070119813025a42bde8c08fe'), size_on_disk=897644, blob_last_accessed=1756926378.3451273, blob_last_modified=1756926378.7061257), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381045/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdc8f9e6447280a7fc713c2a8f2157eb9076a5c01a7eb8437176e9ee30d6d4ab'), size_on_disk=4939421, blob_last_accessed=1756926776.3437574, blob_last_modified=1756926777.2507522), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417844/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ae034bb207abc0c649fb60aae78e798d46647f53b4e0352c6510fb7c8339a234'), size_on_disk=5408445, blob_last_accessed=1756926345.5212696, blob_last_modified=1756926346.4342656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417830/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/21e2dbb49f07cb7c2d7277f5bf1a442e216a2991677c3c4ece5c51316795ea77'), size_on_disk=12760016, blob_last_accessed=1756926342.9432807, blob_last_modified=1756926344.1602755), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417928/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/97e74e01fd641bfef68b6206a3c62ab41b5c28d8ea8369ce4198df1b3387b266'), size_on_disk=2449472, blob_last_accessed=1756926360.9072027, blob_last_modified=1756926361.3542008), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417760/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0500e5835116a61477f2d98554d0dd97e8d932a6b564f709c40c4884f2b49052'), size_on_disk=2234621, blob_last_accessed=1756926330.2873356, blob_last_modified=1756926330.8273335), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417708/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b58038866c334daaf757a086f99c3a5c5962b8563769ee0a3205bcdcbb0144ec'), size_on_disk=250863, blob_last_accessed=1756926323.0003674, blob_last_modified=1756926323.366366), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417884/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ff7d3efa4a231f5130be299d8d0ff67fe4973856e4db18e8a6c53d47ba6b28a7'), size_on_disk=11684836, blob_last_accessed=1756926352.791238, blob_last_modified=1756926354.0932324), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417918/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9f1cf3832969364b6cee1c817d3583a554b765bdbc1ff3f5b29692b7ea98ee7e'), size_on_disk=6936136, blob_last_accessed=1756926358.5802128, blob_last_modified=1756926360.9132028), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417901/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/69432386b347f5b364c02d270b38891358fa2068465354d1ada84d5450ea5bf0'), size_on_disk=9484337, blob_last_accessed=1756926355.6292257, blob_last_modified=1756926357.0582194), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381070/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/32c8f4c4efdbe6e137add61b0e31c27ae6a9eb5e8b463e9e4410a27cc51a57d3'), size_on_disk=29864184, blob_last_accessed=1756926780.7487328, blob_last_modified=1756926783.3167186), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417670/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5e36b63ef1fbb55f821ed2c3d61f0d0bf86cf00846e86ff9dbe6f4d9597f9dc8'), size_on_disk=10881106, blob_last_accessed=1756926314.7704034, blob_last_modified=1756926315.980398), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380973/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/343eb57057b5af866386fb4c02d0c41b888e206f5473ed7b4041200cfb93dbd5'), size_on_disk=1511721, blob_last_accessed=1756926371.3611574, blob_last_modified=1756926372.4931526), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381017/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1d9b2dbb5079eca871182c2dce1d681b943f1362072c18e255a4bd6d1068b840'), size_on_disk=2436549, blob_last_accessed=1756926378.161128, blob_last_modified=1756926378.7931254), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417660/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c394d7b0cfca3f3782dca86380e1a55903111d32efde98173af8a0b726ef5e7d'), size_on_disk=6441490, blob_last_accessed=1756926312.4744132, blob_last_modified=1756926313.4024093), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417840/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7e32bef3c0f7f7520382325aba12f5cf9405c22ebce8554b1d877fe24d2da52b'), size_on_disk=9679209, blob_last_accessed=1756926345.0092719, blob_last_modified=1756926346.0542672), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417882/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ac2bd9710196abc4469d4d78ccbde6ddc67e9ef39ade7d50c32b0d951866e653'), size_on_disk=10076041, blob_last_accessed=1756926352.116241, blob_last_modified=1756926353.223236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417746/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/432442dbdd5578acedcbf6eddc14cfe0d460facc7b0dea857225fbd4e6a4242c'), size_on_disk=2726090, blob_last_accessed=1756926328.6353428, blob_last_modified=1756926329.1983404), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417625/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/96b04f28e74e25bd1502a4ea9aca8047bfa8462e61cced0d726af4a336e1a585'), size_on_disk=17384720, blob_last_accessed=1756926304.4014485, blob_last_modified=1756926306.2074406), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417720/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/961772a081bde1795af5caea9d7562e2b1f889981bd3c3f1c792ad9093a758d7'), size_on_disk=659095, blob_last_accessed=1756926324.1233625, blob_last_modified=1756926324.6243603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380947/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3d6062f97d18f4945c36590355a312df2a16206c26626392cfb44d9613aea49d'), size_on_disk=9201129, blob_last_accessed=1756926368.5321698, blob_last_modified=1756926369.383166), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381063/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1e4361a7d20f8e92e8efc238365e0ed06eb021243864321c93d4638fa21634c4'), size_on_disk=10916323, blob_last_accessed=1756926779.3497405, blob_last_modified=1756926780.685733), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417768/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8bb8e088eb5123049e418c868ab42d94fb48946f934a8313f4cf283e3bfa09ba'), size_on_disk=4124375, blob_last_accessed=1756926331.3153312, blob_last_modified=1756926332.2313273), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417845/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ca2b89837de406b10d72533a3e3d5457b3512b704f7cea395fb0e1b88828393e'), size_on_disk=4648586, blob_last_accessed=1756926345.6932688, blob_last_modified=1756926346.5862648), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417861/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef65c45e0e86b7d04e274c39408b332cc94879c0804f16acf7c537b14bb6498c'), size_on_disk=10436399, blob_last_accessed=1756926348.3832572, blob_last_modified=1756926349.6192517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417894/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/12f262a12783317d135ffc5e1d281a3d43b746a82ed8053c15de824b94bd5ef8'), size_on_disk=6449687, blob_last_accessed=1756926354.4772308, blob_last_modified=1756926355.4742265), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417677/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cfcdac65879c0c26771c1cd1c4a656d4db20e6adb2ed3ad833cf23cd35a9d848'), size_on_disk=13135194, blob_last_accessed=1756926315.9083984, blob_last_modified=1756926318.1283886), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380942/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a958d997c1aff07ec07304485ee818280bc7714dd9c485ea73ef7fda81f2fe63'), size_on_disk=11407010, blob_last_accessed=1756926367.109176, blob_last_modified=1756926368.6211693), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380925/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdf190d30101829153acfd62edd4d689ac65a7d737f0e80978c7d460819b5caa'), size_on_disk=9220100, blob_last_accessed=1756926364.6101868, blob_last_modified=1756926366.1331801), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417854/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d52c8b14e197b179a9b43fbc89bb3205d09d458161ee49ac15a62b7c1f7a39fe'), size_on_disk=6203801, blob_last_accessed=1756926347.311262, blob_last_modified=1756926348.387257), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417717/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/689842dffacf7efa4ba6cd9b4314d195e89f9105eb71675f27019a9e25be63db'), size_on_disk=102777, blob_last_accessed=1756926323.9603631, blob_last_modified=1756926324.5793605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380966/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f27a802d18bf78781f3d168370e0c0e241a130d04a5871d7416442be3ce3e40f'), size_on_disk=4241927, blob_last_accessed=1756926370.3881617, blob_last_modified=1756926371.272158), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380986/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f23d3060df5cb96778fbdf10fff0155c931e1a1e8cc077c7f1582542d54827f5'), size_on_disk=5858046, blob_last_accessed=1756926373.285149, blob_last_modified=1756926374.5971434), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417896/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/71e393229e9071bad31abe5dc3d41bd5d719a5dcb68077827861b4b4e9a49a85'), size_on_disk=6055777, blob_last_accessed=1756926354.7882292, blob_last_modified=1756926355.8442247), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381022/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/980ed07a51dfad581fc2f7bce44031c92445822ba7e994cbfc9b5508cdf97c85'), size_on_disk=2103246, blob_last_accessed=1756926378.5151265, blob_last_modified=1756926378.9441247), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417949/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6123bc7bd312e6e37fabadee561c7dacad32d26bada92c9785c783f94fc50a37'), size_on_disk=1320707, blob_last_accessed=1756926364.4151876, blob_last_modified=1756926365.0891848), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417740/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e91586589c967e7bcbd521989874b21c024da79365d0d9916ffc66b774043e01'), size_on_disk=11875677, blob_last_accessed=1756926326.8903506, blob_last_modified=1756926327.6843472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380963/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c0a30240a0a99347e114928a15a24268495fd37480fde6822f503438bdf3ed48'), size_on_disk=579777, blob_last_accessed=1756926370.2681623, blob_last_modified=1756926370.81716), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417912/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4451877e599fa65e1f00f67b5fcd41fe74719d8bcb19ddacac419fb347c77cf5'), size_on_disk=4943909, blob_last_accessed=1756926357.7152166, blob_last_modified=1756926358.6302128), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417757/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6c9c5dbd0513759ace8599b2542a1f99fe0c30feb318665ebdb55bfb111c8c67'), size_on_disk=5834465, blob_last_accessed=1756926330.011337, blob_last_modified=1756926330.7623336), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381011/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/839fea471c67e08e0b68ae6344432c76da559e67c505c3f6120b20db2f1f753e'), size_on_disk=4378212, blob_last_accessed=1756926377.2971318, blob_last_modified=1756926378.0891285), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381049/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c571bcc5f87a79c7cd38b9614ee83b0d5cc742094e17a7990c829394fca193be'), size_on_disk=3753119, blob_last_accessed=1756926777.145753, blob_last_modified=1756926778.2097468), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417828/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eb4987a8ef94e1691c74c1d828d7d9313da4691b577118a73524def2a90c6d0b'), size_on_disk=4827634, blob_last_accessed=1756926342.3472834, blob_last_modified=1756926343.2782793), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417604/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e6dd3abdf75462f1f5d16f19383b98a8a4c817dc3b13c7cea2b4bea611c40c62'), size_on_disk=961320, blob_last_accessed=1756926300.390466, blob_last_modified=1756926301.3534617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417922/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fba17c65fa210922e57c533f5146b4237677149a5022fb836c1cd52dc89855b2'), size_on_disk=10194446, blob_last_accessed=1756926359.5812085, blob_last_modified=1756926360.610204), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417631/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b4b8c985528935ec46c6c7e78482af1ed26238137fc63b2196c6631e2d3796ab'), size_on_disk=27651513, blob_last_accessed=1756926306.2814403, blob_last_modified=1756926309.1994276), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417692/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/82405dc0a21bd225cc6a9e813e62d4dc0522719abf1e677c954a6ec38763b306'), size_on_disk=4873514, blob_last_accessed=1756926319.6253822, blob_last_modified=1756926320.6443777), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417898/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ced2c80d4f9d2aa28260b885a20b1c90b41d8eb54ee92f418ed592b97b954a9e'), size_on_disk=6422129, blob_last_accessed=1756926355.1262279, blob_last_modified=1756926356.0962236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417864/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/15d259c52e80a2d2d9c20274ef940237c90001d32f2aecb80779335a708222ff'), size_on_disk=3706383, blob_last_accessed=1756926348.5722563, blob_last_modified=1756926350.0962496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417826/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3532f474125bbdc0445e46fe8be83801ca9c079afb1dedd7cd95e25994c50d17'), size_on_disk=11492099, blob_last_accessed=1756926341.9132853, blob_last_modified=1756926343.2502794), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380949/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/65090ef93158269c98bc2e747e505cea9d460f0959d0b2957e58543cd1944163'), size_on_disk=2110872, blob_last_accessed=1756926368.705169, blob_last_modified=1756926369.142167), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417818/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3b30217b127ff88f165762c5054c392d9489b9007ba7ca8b86f18b44f9988e4d'), size_on_disk=8388230, blob_last_accessed=1756926340.2372925, blob_last_modified=1756926341.5632868), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417620/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b973f1a83f111c4e114a40d3731ff24779cafcb7b71523fe8571cf2ed849b316'), size_on_disk=20780203, blob_last_accessed=1756926302.5494566, blob_last_modified=1756926306.7334383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380946/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62fe0084bf7a8f9f484e76366f203e1c68862a211d5d501c08e19b8f28678d6b'), size_on_disk=8090103, blob_last_accessed=1756926367.6871734, blob_last_modified=1756926368.7681687), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417698/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d240b652ebca64784e037cc8ffbda424e2c92f368c5bb9c730930f48f29facc2'), size_on_disk=1855826, blob_last_accessed=1756926321.2993748, blob_last_modified=1756926322.0703714), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380929/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aeddf7cf3aee11ee08c90d305e66fc682e675959c171ecfaa591bc1d9015ebbc'), size_on_disk=6656759, blob_last_accessed=1756926365.1011846, blob_last_modified=1756926366.0181806), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417807/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ebc732f5d06d745bb63af3c7f5966722972fa722ef9d0194d71cf6d9485fda83'), size_on_disk=14049230, blob_last_accessed=1756926338.268301, blob_last_modified=1756926339.5272956), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417637/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ca1a954c7b2aca56529daf9a89249ad4e6ec2373e29ac7c19d2af1533afc4263'), size_on_disk=2776397, blob_last_accessed=1756926307.8804333, blob_last_modified=1756926309.1794276), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417821/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c29db68a3430cf8cb01716d90a92105387777ed1361992eb9501271c35359cfb'), size_on_disk=6597050, blob_last_accessed=1756926341.1692884, blob_last_modified=1756926342.2702837), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417929/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/85b194132215311994e48908799e31357d1094663a232e8170a4edcfa5a11eff'), size_on_disk=9155231, blob_last_accessed=1756926360.9862025, blob_last_modified=1756926361.9231985), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380955/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/369e0f1de51ea197e928618e562613eeed2ed59ce5c1389ce07ff6dd96d391b4'), size_on_disk=3596907, blob_last_accessed=1756926369.4561658, blob_last_modified=1756926370.2661622), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417743/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d4eb93049648498d2fdc1f125574429a4dfdec65e626ad9fa16bdf0d0cf599d8'), size_on_disk=2280534, blob_last_accessed=1756926327.462348, blob_last_modified=1756926329.1343408), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417788/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fd6f44471f1b86568bc059f25e5ccec7bda2b85b9f8be536aeaf2305fe015f5d'), size_on_disk=9112879, blob_last_accessed=1756926334.546317, blob_last_modified=1756926335.971311), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380945/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/51efeee4439e81d920c4d9e860de321d90c72703600044e52215c774e1540650'), size_on_disk=6902367, blob_last_accessed=1756926367.356175, blob_last_modified=1756926368.6011696), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381059/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/59daa71f55c9c0da6f8d56fc2497491477312fbdbcb9e92d4951874f2ed72686'), size_on_disk=13124154, blob_last_accessed=1756926778.926743, blob_last_modified=1756926781.8157268), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380974/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ea9eb19e91fedadd9e46d5d7dc9169995065af5b6b8e823f1667117be9d737d0'), size_on_disk=8889742, blob_last_accessed=1756926371.5481567, blob_last_modified=1756926372.2801535), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417934/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5816750a355a277e532d46ffa5159f8a2ec1ce66686fb12f07ccab4b4f7b3c98'), size_on_disk=7324082, blob_last_accessed=1756926361.995198, blob_last_modified=1756926363.89619), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380985/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2ec986412ef2a9e6e59f4ea3dc278b27ab3975b5025d00bd1cd38fe1867b0fea'), size_on_disk=7208882, blob_last_accessed=1756926373.2251494, blob_last_modified=1756926373.9591463), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417876/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0713329434bfbd55f1998400d9d44c1d8c7fec6cb1b646c0fdb926f1046f9b47'), size_on_disk=11246388, blob_last_accessed=1756926350.971246, blob_last_modified=1756926352.0472412), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380980/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e36f58da696f0827c72fed8647c2a45a0499fdc5d1859386dbf025388f07e16d'), size_on_disk=2288305, blob_last_accessed=1756926372.371153, blob_last_modified=1756926373.2051497), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417869/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8dd08ab28007f90a664a6fbde23298ae2bb0ded917d5d75b1275c41e3eb5f7dd'), size_on_disk=16604892, blob_last_accessed=1756926349.6872516, blob_last_modified=1756926352.7202382), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417649/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6baf4bcd82d9fe55b8dcdc7602c0d39d20954675ef4c5484331ff25dac8ad3f4'), size_on_disk=2672976, blob_last_accessed=1756926310.8234205, blob_last_modified=1756926311.4704177), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417764/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8a30e54d96d5b83981e37a0d8f6cae9e6584819a76a200f2730d9de32e282d02'), size_on_disk=2641764, blob_last_accessed=1756926331.099332, blob_last_modified=1756926332.088328), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417652/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d38c028233981eb275c44995eafa73172388a9e0e704fe278b0cedbab49d429f'), size_on_disk=12947754, blob_last_accessed=1756926311.0344195, blob_last_modified=1756926312.1414146), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417855/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ac2218ccb9f80664ce377351ea1dc84bf3f70b36006a86cffdb761a40ddab791'), size_on_disk=1981158, blob_last_accessed=1756926347.3992615, blob_last_modified=1756926348.3062575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417719/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cc0a365d1d79f0469571216394f0cb211b9096f2a567e0a347a18f0c496bac06'), size_on_disk=2076369, blob_last_accessed=1756926324.010363, blob_last_modified=1756926324.951359), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417903/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3ddfaef9dc57d0f63b8613408aba3af803dabd5ee87a1584edd9391b2b41bec3'), size_on_disk=11218813, blob_last_accessed=1756926355.9432244, blob_last_modified=1756926357.2232187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417709/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/395b1dd171ab9759e06a22580672bef65aac2acd7d548e38098d207c80071d89'), size_on_disk=717131, blob_last_accessed=1756926322.9983675, blob_last_modified=1756926323.6373646), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417870/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ca1bc9825b47ee9ac59e53bc6ada3b1a9e8aba1b57c57c57947afae7926054b'), size_on_disk=10710319, blob_last_accessed=1756926349.7052515, blob_last_modified=1756926350.7552469), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417742/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1a87c492aab479dd0c013fbe80e006f18ba80de751da600b95babcb3f6e88dad'), size_on_disk=2054520, blob_last_accessed=1756926327.3293486, blob_last_modified=1756926328.4213438), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380990/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4e04122d2830db35f2b82b29efaca846fe10d7638c88c3e4d3cf78bf10cf245a'), size_on_disk=2093813, blob_last_accessed=1756926373.9261465, blob_last_modified=1756926375.1611412), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f00bc431128183affc8964f9610f3e9486a043bcea5b31b3ebbcbd9b0070a448'), size_on_disk=6096918, blob_last_accessed=1756926377.3431315, blob_last_modified=1756926378.2571278), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381025/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aea8c4d9f3783191d3e89f4566811f01089f3b0b76b736c5167bda54693b0bc4'), size_on_disk=339954, blob_last_accessed=1756926378.7781255, blob_last_modified=1756926379.125124), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380958/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ef3849004b6c72205ea1b298cf6a586f1114061ca94925229eda191d4666885'), size_on_disk=3288900, blob_last_accessed=1756926369.617165, blob_last_modified=1756926370.303162), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417899/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b4349f94cb0679468e9c28a9439c1a71c88d7288adc3bdad0ca31a2590f1d4e0'), size_on_disk=7910137, blob_last_accessed=1756926355.3832269, blob_last_modified=1756926356.3022227), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380931/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/be287270314fed3dded81e0c24a66d8bd991b219fd6a157f1aeaa1690262e563'), size_on_disk=4324179, blob_last_accessed=1756926365.1611843, blob_last_modified=1756926366.1241803), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417776/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/01c4a97f92cd5e2919759d52d083eb824408a2fd7ade9413a060c6b20d233963'), size_on_disk=2759569, blob_last_accessed=1756926332.8593245, blob_last_modified=1756926333.629321), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417618/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d2ce91297551bc97b4fca46007f80c8846e5f9e9c78d9c1d7ded02fc31b3ef38'), size_on_disk=9239111, blob_last_accessed=1756926302.3654573, blob_last_modified=1756926304.3714485), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417862/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cea0cfbd611f8c6482ec33f8ea32f304a481d1c1225d3dacea432dd359fc976b'), size_on_disk=5244392, blob_last_accessed=1756926348.381257, blob_last_modified=1756926349.6162517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381026/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e41f2d7a1b084a404e7f5e9e003b27f88665a983ec73859f7eb42cd8bfa2bdd3'), size_on_disk=2857045, blob_last_accessed=1756926378.898125, blob_last_modified=1756926379.7281213), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417872/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7d71ca12594ab85797fca8c1b88947cb0f3ece93596f1a4c5adc04c11c0abf68'), size_on_disk=8522283, blob_last_accessed=1756926350.1742494, blob_last_modified=1756926351.180245), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417941/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/30188c510d2d7041e1525e304edc8f25b569a1771cf00844cb27300a052441e8'), size_on_disk=7032333, blob_last_accessed=1756926363.2551925, blob_last_modified=1756926364.2891881), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417680/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aa9e972e7427b1db5556008756651933c8792891339607dee2ee231733072d1c'), size_on_disk=18443288, blob_last_accessed=1756926316.5853953, blob_last_modified=1756926321.4263742), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417738/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c3d82a351044aa8cd5d43979369088ccf2f563022d4f7aa3683ef20923202770'), size_on_disk=19772535, blob_last_accessed=1756926326.3913527, blob_last_modified=1756926328.6913426), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417654/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/67d658902a2b91d0d36345be06dc00c85af1b88ba40c37deaa091705d97b3ffc'), size_on_disk=6323092, blob_last_accessed=1756926311.166419, blob_last_modified=1756926314.4344046), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417946/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef43c84affe53bbdefcd70cb97a4a4d1e483824b8b6f91e3360a11094609f815'), size_on_disk=4853893, blob_last_accessed=1756926364.0141892, blob_last_modified=1756926365.0591848), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417641/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/355d8669b1d4c5576d8aa900582800fdf1819360f0db91d5e0299342b49884fb'), size_on_disk=6322689, blob_last_accessed=1756926309.3414268, blob_last_modified=1756926310.7974205), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417796/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/52d8262071c9c9de8cd8fb52be9e67acf125b8ce67033749fb92989f0d1f9345'), size_on_disk=10184069, blob_last_accessed=1756926336.2803097, blob_last_modified=1756926337.4333048), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417627/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4027ab6ecd21364ca365bbe7192797ed924c817e6edc841d750111b2e3f6f45a'), size_on_disk=21089243, blob_last_accessed=1756926305.0144458, blob_last_modified=1756926309.6264257), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417651/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b95b6be3a7a6a5b4af3bbf15c5a4a6b3893f3030ef6d1dcfe79e6c6554e3d3cb'), size_on_disk=13551797, blob_last_accessed=1756926310.9544199, blob_last_modified=1756926314.6814036), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417630/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/26fdbadf7c1252a8fd73ac85ea7430d942d2d11ddc43ea1b2590aaa008e08337'), size_on_disk=3015575, blob_last_accessed=1756926305.7104428, blob_last_modified=1756926306.7214384), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417802/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ea68aebb727f77d846a145d43ec16fa2496e9c05d49a8791aa131b8e3e536452'), size_on_disk=4881005, blob_last_accessed=1756926337.2223055, blob_last_modified=1756926338.1273017), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381028/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8d40caa48e7b9d32d2a0548732ef91c01ede9d0f4f6d74f045817e818c52c808'), size_on_disk=18141950, blob_last_accessed=1756926378.9691246, blob_last_modified=1756926778.2687466), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417858/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2d7539fcb6297592fe3755ccc88e122441925fbaf9f5600d4daa88639630fd8b'), size_on_disk=1021831, blob_last_accessed=1756926347.8042595, blob_last_modified=1756926348.5072565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417815/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ee8a37cd03dfe154008718f2a0f7fd95b3fdf468f99b1fc73ca515433fa45930'), size_on_disk=6563669, blob_last_accessed=1756926339.6062953, blob_last_modified=1756926341.8062856), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381061/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d44d57e9c03bb6e6e750809eea88657c2960ed251b8acaa1352a07f4cb346a82'), size_on_disk=3789431, blob_last_accessed=1756926779.0527422, blob_last_modified=1756926779.8977375), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380997/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/add73ab9723ef88a47192fa169b89aaf17c16a0841fc9e53c1c5d73cfdc2ad50'), size_on_disk=15536494, blob_last_accessed=1756926375.1291413, blob_last_modified=1756926377.6141305), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380932/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f214422d02adb2b0a1037eb424d8574123cf27d8a5cd1418feb5733b0249f60c'), size_on_disk=6251591, blob_last_accessed=1756926365.6181824, blob_last_modified=1756926366.5551784), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380971/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0147090dd5a74f558b67151503ff635423a8af661b56d1faf06bb50a59adfb3a'), size_on_disk=1276600, blob_last_accessed=1756926370.9411592, blob_last_modified=1756926371.462157), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417836/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4fb964c54f4a37387c7844902d6171011a9a81efff244f365efcb156f630158f'), size_on_disk=3312893, blob_last_accessed=1756926344.2532752, blob_last_modified=1756926345.0702715), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417846/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a3dc440aac6a69d75ec73242a30e59d8183023a2677f1576b5e73d037d6e6654'), size_on_disk=2680468, blob_last_accessed=1756926346.0562673, blob_last_modified=1756926346.7392642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381044/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/89a79f2c5f6258380ff4776af49db6119ac0c04342686705cfde94feebd7c9ca'), size_on_disk=3576881, blob_last_accessed=1756926776.065759, blob_last_modified=1756926778.8987432), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381024/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5182750a0c8af8253cc98ecad8715519463d5c18adbe067488dfef91198c0d80'), size_on_disk=603678, blob_last_accessed=1756926378.7151258, blob_last_modified=1756926379.128124), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417638/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c8cface311cd55cd1542ed94d609da72cd2b3087422c28ad26c60e1d81a3e6bd'), size_on_disk=993699, blob_last_accessed=1756926308.4724307, blob_last_modified=1756926309.2634273), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417663/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cc7010c3ed70c3083d49d88c0f95dbe8f6eff873d4b5848eeae06a22df71a479'), size_on_disk=8938116, blob_last_accessed=1756926313.18441, blob_last_modified=1756926314.5674043), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380962/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/72be01aa42db736ffac8236c23c16edea00bdf5da88c3114fc94d9eb3daa4ee9'), size_on_disk=1460258, blob_last_accessed=1756926370.2361624, blob_last_modified=1756926370.6811604), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417814/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9f70b1469a6d0ad8758aa7a44e754e07ccc401906f8af403a72075d763919535'), size_on_disk=7618977, blob_last_accessed=1756926339.5142956, blob_last_modified=1756926341.0872889), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417676/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/53c35af59283469a22148b2841cb8574faf80d59162a33d269560283c9228af2'), size_on_disk=12006227, blob_last_accessed=1756926315.8083987, blob_last_modified=1756926316.8563943), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417666/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/38008595447ab722c1889d0e19babf74299822ac7733504a4f09e1af1e4ad1ac'), size_on_disk=7712422, blob_last_accessed=1756926313.945407, blob_last_modified=1756926315.073402), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417856/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3dc4e868f31759c9ac3d43f40097f5ed2f0efb6307e3bc1ccbed70c50a16ee4e'), size_on_disk=6126482, blob_last_accessed=1756926347.3832614, blob_last_modified=1756926348.3162575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417718/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3ba23a0e7c5b3a455e95fbf6a22b51687d5330c19207241bdfd189e3776f5ba3'), size_on_disk=3304954, blob_last_accessed=1756926324.0233629, blob_last_modified=1756926324.986359), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417942/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f3a5b1eb0f954ddf66a8dfa1a38465726d2251730d3898865535269a43c7fa4a'), size_on_disk=6911573, blob_last_accessed=1756926363.415192, blob_last_modified=1756926364.3521879), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417695/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d6ae8d39c2949235dd4d82e7a0e56d9f20c20ba5f1b7a445fbba1f0df29c88b7'), size_on_disk=7710772, blob_last_accessed=1756926320.4913783, blob_last_modified=1756926321.959372), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417678/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/825d9bcd10c008b68cc84e92f20608c0d52770ac028bbc4a9219f1cd210ab8c4'), size_on_disk=29320273, blob_last_accessed=1756926316.0503976, blob_last_modified=1756926318.3993874), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380972/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/91a8106ca7ac2babcabd1549072b2b730909e1386f827e732428bff2bb5251cb'), size_on_disk=6712599, blob_last_accessed=1756926371.1721582, blob_last_modified=1756926372.0711544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417888/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/961a35161b04a0e892f46a0a90b6d4776d7fe07ef82fc7016c95b1a885c4274f'), size_on_disk=9651505, blob_last_accessed=1756926353.298236, blob_last_modified=1756926354.65023), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417921/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/05fb4ff0001b88b711eee9f29ae30642d940667f7166121c9782f464057880f5'), size_on_disk=7891276, blob_last_accessed=1756926359.3782094, blob_last_modified=1756926361.2442014), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417799/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cba2fb579c7706a7ff38fad3c7c649000560c1052e681adea986c5cc02c02cfb'), size_on_disk=5688906, blob_last_accessed=1756926336.7133079, blob_last_modified=1756926337.7073035), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417867/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f4783b00d4674df92d84f9a304fb9d143c7b9e4754e3efcb9c072a49a9b9298b'), size_on_disk=8536709, blob_last_accessed=1756926349.3882527, blob_last_modified=1756926350.2882488), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417887/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/58707813ec63af2eb1a72b0c8317b4701b4777df8f828f2e08a0cbb1ccfa534f'), size_on_disk=9080847, blob_last_accessed=1756926353.0712368, blob_last_modified=1756926354.412231), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bd149efcdaa899981b965dc424d16fb66668a7666a059c6bc5043572331394c2'), size_on_disk=4513644, blob_last_accessed=1756926377.1751323, blob_last_modified=1756926378.1161282), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417820/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9d36f1c17151e3203186142abff734c0182377d5287b3f27d00917608501abb3'), size_on_disk=8116588, blob_last_accessed=1756926340.9562893, blob_last_modified=1756926342.0902843), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381033/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5fff567b96a4649d787e93ef8cdca2d8ce977b7f2eb25d6f78e50bc30c798bce'), size_on_disk=1712156, blob_last_accessed=1756926379.7391212, blob_last_modified=1756926380.3661187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417710/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9837ecd3769eaaf407d939ff09d6ae74d832630b27353251942bd262cd70fc35'), size_on_disk=330457, blob_last_accessed=1756926323.098367, blob_last_modified=1756926323.797364), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417767/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8d0afed33cd85298c709656da4b210e3fe87aa4d6822208d744200816d59b95b'), size_on_disk=3739401, blob_last_accessed=1756926331.2333317, blob_last_modified=1756926331.8623288), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380964/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3a63df07de544662fb04d5388f34b0c8068efbc19f464b673d8a860a57b5b422'), size_on_disk=956844, blob_last_accessed=1756926370.333162, blob_last_modified=1756926370.8741596), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417801/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4bbb1b3faac1a4c15eaac8c81260f4f18493d2a082915cbe4c480ce76a94140e'), size_on_disk=4600965, blob_last_accessed=1756926337.090306, blob_last_modified=1756926337.9153025), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417711/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e30dbc09cd2dfb4722d60d0833123542e9c5096f70b97867cd15d466aa7b927a'), size_on_disk=96859, blob_last_accessed=1756926323.1863666, blob_last_modified=1756926323.7823641), CachedFileInfo(file_name='GSE222268_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE222268_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e0f1d5b82fd97798d0c02d992c49c0e83d8dcf82e07e001284e78d9656355481'), size_on_disk=4411, blob_last_accessed=1756926300.1784668, blob_last_modified=1756926300.5344653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417737/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2e1d5b2306dd24257d4ba261baa42b324c9f4f8ca3a1d30e0f920c4f4a5f99ba'), size_on_disk=1342511, blob_last_accessed=1756926326.2713532, blob_last_modified=1756926326.9043505), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380927/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/38e67ea2fef57610ff63c0c298a6db65ae1c0a159f2b1e61940f2e65306b8fa9'), size_on_disk=7297320, blob_last_accessed=1756926364.969185, blob_last_modified=1756926365.7781818), CachedFileInfo(file_name='GSE178430_metadata.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE178430_metadata.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9673758f13ec096ec4a89642bbe34d60975f5f05'), size_on_disk=61, blob_last_accessed=1756926300.2374666, blob_last_modified=1756926300.2994664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417702/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/41123195fa339a4ffabf6d686a284c343d0519089a18b828818c92ec43a26eae'), size_on_disk=6213776, blob_last_accessed=1756926322.0263717, blob_last_modified=1756926323.0323672), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381032/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/335a75b123ae38273f1b1a10f16e9547eec356f68a1d3078926f011d3332e2b5'), size_on_disk=656385, blob_last_accessed=1756926379.6781216, blob_last_modified=1756926775.4077625), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417771/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8fe5513ae86b28ecca3d4c70bdb73097f578dd31b4d08a072960b61550f582fa'), size_on_disk=3970195, blob_last_accessed=1756926331.9313285, blob_last_modified=1756926332.8643246), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417716/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cd98cf97346b62e4f1efcace598c9c273d9ad9b8d8692e35f50b11d46f7ed8a6'), size_on_disk=5100666, blob_last_accessed=1756926323.8513637, blob_last_modified=1756926324.8793592), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417833/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/48792db399d7f0aab6765109e8c01abd61a2da93e5d0bdf7c325bcffe01fe113'), size_on_disk=8139822, blob_last_accessed=1756926343.3262792, blob_last_modified=1756926344.480274), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417786/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdd4770c9b3e18e86f9a46ff924e3a562e37b632d6f0e8ad11627956a3416e09'), size_on_disk=11354740, blob_last_accessed=1756926334.3243182, blob_last_modified=1756926335.4083135), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417900/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/45d5a0bbf39e542f22aa4e36186673d1246533dc513f006312a26286496f6ead'), size_on_disk=11235417, blob_last_accessed=1756926355.535226, blob_last_modified=1756926356.6632211), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4331f53e52f628f99a9695fa70339b08eb69a011cc8a3cbdb08dfa8e6c5d3ddd'), size_on_disk=10779086, blob_last_accessed=1756926376.8381338, blob_last_modified=1756926377.9141293), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417686/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6a42f24b250baf0d88696bc123f01494a400b3f03f2ff2a1fc828f9295721a16'), size_on_disk=12070748, blob_last_accessed=1756926318.2063882, blob_last_modified=1756926319.627382), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380926/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fd708dfb8084d767fde32c52acb58e785083ce7becbd2e160277f31460719346'), size_on_disk=6579161, blob_last_accessed=1756926364.8451858, blob_last_modified=1756926365.5441828), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380981/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/198a0b08846a11e72de25e0e9c44f495ef88c9260de2e036d240715004c04953'), size_on_disk=8535317, blob_last_accessed=1756926372.5771523, blob_last_modified=1756926373.8291469), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417602/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/26ae91a914e9ad095615c88ac3c62c99e28ae2687ccd66007f13ac8fbbffa3fe'), size_on_disk=3717181, blob_last_accessed=1756926448.0367467, blob_last_modified=1756926301.5714607), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417889/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/295f9efb0d78e3a116ce4022a7830297570446e2017a3fcd3b3d423c0c20664b'), size_on_disk=9617979, blob_last_accessed=1756926353.3102357, blob_last_modified=1756926354.7322295), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ad0e80a28e4cd2027893adee5afceab851576ed2637c1a8e94d0af2adae6ace1'), size_on_disk=5088900, blob_last_accessed=1756926375.41114, blob_last_modified=1756926376.3821359), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417804/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/16ee23ae4dbe8a4d968a614f5e6de65028b6edce012231a50eb776d65f949d34'), size_on_disk=2107502, blob_last_accessed=1756926337.7883031, blob_last_modified=1756926338.5083), CachedFileInfo(file_name='GSE209631_metadata.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE209631_metadata.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fe31b19357a717d3d51d1d201620d37fca79b1c8'), size_on_disk=61, blob_last_accessed=1756926300.2214668, blob_last_modified=1756926300.2814665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417735/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/634551506800bc76183ce179c8c8f324cb6f0f577f6cf0b629b74ce4f1e995d5'), size_on_disk=19171195, blob_last_accessed=1756926326.0393543, blob_last_modified=1756926329.4873393), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417793/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8a51bd4d953e697022a85f9b527afd45126988ba4bbb796d71c9e5f6c8fffc44'), size_on_disk=10797388, blob_last_accessed=1756926335.4883132, blob_last_modified=1756926336.7013078), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417650/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c4d103888f768ff3cb0c68f4b747cb8d8aad8058b75174160cfc42b2504407f1'), size_on_disk=1337049, blob_last_accessed=1756926310.89842, blob_last_modified=1756926311.4924176), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417865/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/348bfc1685f79e9ec7f559e96b61b8c43628d611378b6c4ead8bffea44415f1d'), size_on_disk=9233047, blob_last_accessed=1756926348.8982549, blob_last_modified=1756926349.9182506), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417868/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/52e3f224a0147264d4ce477d26f4e11ff0745c328eb3e2ac44209dba1c8c55eb'), size_on_disk=5039768, blob_last_accessed=1756926349.6852515, blob_last_modified=1756926350.6082475), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381046/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a96c1819b1010c40fc4d92418e407cc5b5f48f9e24883d4b4cdf3e02d23b688a'), size_on_disk=3071378, blob_last_accessed=1756926776.6277559, blob_last_modified=1756926777.855749), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381037/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5cef560daffa3a5b1e841a312937eca595ced0bbdb3e6c641b13359a0340fea1'), size_on_disk=2090047, blob_last_accessed=1756926379.8821206, blob_last_modified=1756926380.4171183), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417732/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f99cdb10c12a75e1d020125d68b5c5302263eaef050b5c6b0032e27a0ac9a23c'), size_on_disk=685742, blob_last_accessed=1756926325.7463555, blob_last_modified=1756926326.326353), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380987/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2eb045d9f88b952eed8f8354e77c682bc3003bd2e9eacb0eb0c2df5ca44a34cc'), size_on_disk=4463796, blob_last_accessed=1756926373.559148, blob_last_modified=1756926374.4241443), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381065/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/99cc73bc4c0908da04c01c7c59eff995a478ffc023c31fb970c7bb9789b2d70b'), size_on_disk=14673323, blob_last_accessed=1756926780.0087368, blob_last_modified=1756926781.5847282), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417640/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/411d9146359847b5805450f67cbde4f1cbd51e3c2fae4bbcd6295db7426cb2ca'), size_on_disk=7000528, blob_last_accessed=1756926308.9124289, blob_last_modified=1756926310.0344238), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/64a119c8028083521d3872e2daca1545f2f8753eb8acfc751bf0c6d25803a3f8'), size_on_disk=2003211, blob_last_accessed=1756926376.794134, blob_last_modified=1756926377.2761319), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417696/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2e1bce702520ca07d9037bd4de4d21bb30ef6b7fdf7de9d75ea232acc036a72e'), size_on_disk=31303382, blob_last_accessed=1756926320.7203774, blob_last_modified=1756926777.0657532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417810/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b807197acb33acdd1b70eddedd59dc4d831e271d231d6c9bb6f1281fb4d5813f'), size_on_disk=9808564, blob_last_accessed=1756926338.8082986, blob_last_modified=1756926339.7202947), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417853/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5995033a04f017892b30c025a1527f9b6420bf1e7636c466cdd8d1462cf228c8'), size_on_disk=2186899, blob_last_accessed=1756926346.9012635, blob_last_modified=1756926347.8172596), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417893/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f3b9f2c711cb806350320b93dba528efd6433d0ca10c39e5e2dd6aa89a408bd'), size_on_disk=9896637, blob_last_accessed=1756926354.2372317, blob_last_modified=1756926355.535226), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417800/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8e563a7d64bf06e7fe26c2817271f626273eed4c7dd19db0b9fd8e14c25f73b3'), size_on_disk=9904697, blob_last_accessed=1756926336.7953074, blob_last_modified=1756926338.958298), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417713/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c5d75f4a32cadceee844da0c5ce703483f575fb927990a1615ee37205137d986'), size_on_disk=524444, blob_last_accessed=1756926323.533365, blob_last_modified=1756926324.045363), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381048/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/53cd017f084781da16e69ae01b406ac0d8824a4d991e39b22c428e664a3808f2'), size_on_disk=2181901, blob_last_accessed=1756926776.9907537, blob_last_modified=1756926777.7067497), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380976/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d27d3efdccd104ca208d69fe9d94fe9011cb6212b34739679b1c8f550c5e9df2'), size_on_disk=12889551, blob_last_accessed=1756926371.6141565, blob_last_modified=1756926372.8021512), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417774/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ad00f794fb29d2a609375d63b09170f396f055ae392cd05a31d5f8fb5ede7a33'), size_on_disk=2914273, blob_last_accessed=1756926332.1763275, blob_last_modified=1756926333.217323), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380983/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eec33f1158aa017b073fcb16d6400cd800eb79e8c0b78b5e2f831d7413e4a736'), size_on_disk=4478939, blob_last_accessed=1756926372.869151, blob_last_modified=1756926373.6721475), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417834/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a69c65a4d32399355863fa14d4a1e92ecff2c92498e122e8e77796de16d42948'), size_on_disk=8537233, blob_last_accessed=1756926343.3672788, blob_last_modified=1756926345.2462707), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417926/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/303d44a38fbb646cf211ce1bd03fe2d24bb004f385c63d05ab8e707c5b283f0c'), size_on_disk=10766909, blob_last_accessed=1756926360.6072042, blob_last_modified=1756926361.6751995), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417734/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/10d4c6d9acc5b56370fc32062262ae3871008e20e960c6b352ac27a4e229c905'), size_on_disk=1495194, blob_last_accessed=1756926325.9453547, blob_last_modified=1756926326.745351), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417910/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/694d86a1e469836a53e1ce5dba0d64b77b8f68590a2034e2a1ef5310457dc7fb'), size_on_disk=4494010, blob_last_accessed=1756926357.3112185, blob_last_modified=1756926358.3942137), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417722/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f29055ed874a3a40735526808a6739884efecd8d7c5e7e9eb1fcaa8e495ade3'), size_on_disk=345818, blob_last_accessed=1756926324.5753605, blob_last_modified=1756926325.2483575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417945/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6689ba375c993d00d1f0e716d3bc39604d68df654a67b2518c56f4d54c7f4ca6'), size_on_disk=11289854, blob_last_accessed=1756926364.0191894, blob_last_modified=1756926364.9011855), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417839/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a04fc093d2a4fe873de135165bd62a38a1cb9b29860881ed973022ff128dc6d8'), size_on_disk=6789775, blob_last_accessed=1756926344.5502737, blob_last_modified=1756926346.4242656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417944/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/01e0e606e4e8b2203e6497ffc8c231bce8672a43d83098ba838c8edfdb052c27'), size_on_disk=4563261, blob_last_accessed=1756926363.89319, blob_last_modified=1756926364.6951864), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417681/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6e4ea424e9eab4dc3ea304621992e1db158565a8ce30374980b1084a653a0132'), size_on_disk=7689435, blob_last_accessed=1756926316.934394, blob_last_modified=1756926318.231388), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417779/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/918376b5cb0b63e3e531ac990854e5a7b461e7251f5ed562c5a92cbf6a5a0141'), size_on_disk=9459105, blob_last_accessed=1756926333.1593232, blob_last_modified=1756926334.2033186), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417917/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2118491b5ba6b4af1c0d394eec3139b0258cf3a4e42b4d1a02ab177247852081'), size_on_disk=14019020, blob_last_accessed=1756926358.4622135, blob_last_modified=1756926359.8632073), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381064/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8569620c4d2efb8dd163aa69d8edf8f855b96eba637e754a126a88e6f0c73192'), size_on_disk=20598404, blob_last_accessed=1756926779.800738, blob_last_modified=1756926781.1587305), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417871/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e44cb883893b5e30baa752dfbd773b31795f2072a7ec4f473371f435989cad42'), size_on_disk=6264896, blob_last_accessed=1756926349.9852502, blob_last_modified=1756926350.937246), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/059cc8a8b1f7918ab1b6499d3d9843100b3b37fe6dcd57dfe9ae512c6030cad6'), size_on_disk=2109635, blob_last_accessed=1756926376.7021344, blob_last_modified=1756926377.469131), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417930/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62f17648a07c7675d6a101e3d12efeae247c742e37eaacf96f95e03dceedefde'), size_on_disk=20574235, blob_last_accessed=1756926361.2242014, blob_last_modified=1756926362.5701957), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417731/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/39ec5969bc8f3acceb3aa30b72f0c0101785a36da340f3983ad00d8cc80160a4'), size_on_disk=1557745, blob_last_accessed=1756926325.658356, blob_last_modified=1756926326.2043536), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381055/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0aad0d3724742845da7f0afae0c18ab5c992bec76c02e0ffda057f00d0894d35'), size_on_disk=3355008, blob_last_accessed=1756926778.3377461, blob_last_modified=1756926779.263741), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381058/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9dc97b5892fcc8af593c03fad2040396b9b10626a655319ad40b38acd75e1d7b'), size_on_disk=11534270, blob_last_accessed=1756926778.543745, blob_last_modified=1756926779.7317386), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417880/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b08db0a2399fc4e5b6f59c19b7af05b1294d0fac10b2c336b627234fa828f1be'), size_on_disk=9818886, blob_last_accessed=1756926351.5782433, blob_last_modified=1756926353.246236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380982/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d1e44b0b82ea22fb6c513ce02b49facf5f38b64b2baa91b24942fc6858125e37'), size_on_disk=6977161, blob_last_accessed=1756926372.8241513, blob_last_modified=1756926373.4951482), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417704/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/477a83e4a5eace957a036c5ec65ef4539442c078058c1a44a877763cb850c86d'), size_on_disk=459034, blob_last_accessed=1756926322.1393712, blob_last_modified=1756926322.8993678), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417724/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e5beee12039dfe6b4dae180a31ac0897dea725754554db43e2df9f99da94db58'), size_on_disk=4535402, blob_last_accessed=1756926324.69836, blob_last_modified=1756926325.9703546), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381027/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ba9aba2cc5b9bd9e83599ccba5920f028ac9c6936436dac50f45c606f3abd841'), size_on_disk=6537548, blob_last_accessed=1756926379.0151243, blob_last_modified=1756926775.5467618), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417688/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f21ccaca0f6782b6ce2b70b50c9dc93487b6aaec03db6c30d9cb0c9fc01dda42'), size_on_disk=16023102, blob_last_accessed=1756926318.5463867, blob_last_modified=1756926320.0283804), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417608/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d225f561536fca4d073d2418a32efc3df68e9666a3403c7d8b1ee0a520861322'), size_on_disk=873305, blob_last_accessed=1756926300.603465, blob_last_modified=1756926301.521461), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417897/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/309aab637d037638082524649ef7b0828885a98e786c64af01072d68968b7d66'), size_on_disk=7857749, blob_last_accessed=1756926354.8022292, blob_last_modified=1756926355.9902241), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381051/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ba017d941936d755013757711f5de4661acd187ef79524662d85485ba6588f34'), size_on_disk=8199472, blob_last_accessed=1756926777.322752, blob_last_modified=1756926778.4757454), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417824/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/65d997db1339fc5a8380a61fcf1ab1a6d28fb9142fc7d78c89236dc218b91ea3'), size_on_disk=8218856, blob_last_accessed=1756926341.7612858, blob_last_modified=1756926343.0512803), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417659/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/20f21bb1ba86fdbee1918776793b1ff2275be49933c72eaba90b8be8582f7692'), size_on_disk=15311232, blob_last_accessed=1756926312.2364142, blob_last_modified=1756926313.8474073), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380937/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e31f54581c4a4a47033065033f3f9266913edad654543dd07b20d0ffe54847dd'), size_on_disk=2691574, blob_last_accessed=1756926366.2071798, blob_last_modified=1756926367.1951756), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417644/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0c788a7ba70689a796af823df5cc14504dfdc854617952459367364c4498cdeb'), size_on_disk=949200, blob_last_accessed=1756926309.758425, blob_last_modified=1756926310.6444213), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417745/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ab84f2e8e76311cfe45a4b525b239186e835cab37f1e5222fdc35c286064e5e'), size_on_disk=1270105, blob_last_accessed=1756926328.596343, blob_last_modified=1756926329.1233408), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380989/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3c8b646955209386a1d97882163b7c9816463d2dea68fbed2cc960817ffeb332'), size_on_disk=12022144, blob_last_accessed=1756926373.7761471, blob_last_modified=1756926375.1511412), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/43f12fb4826be4c7a3dd64800fc72cbcab2a9ce5bed7f7b5b9aee974deb7c06d'), size_on_disk=1116877, blob_last_accessed=1756926376.094137, blob_last_modified=1756926376.6491346), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417913/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/35c1a160b767f9f5870216ed33a1dc24804690772a5baee58f623c6dc0632127'), size_on_disk=9135350, blob_last_accessed=1756926357.9462156, blob_last_modified=1756926359.3142097), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380944/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7331bfc92dc902a721083ac4ea027481c90ce204d7dca7c493fec8f284db0155'), size_on_disk=8432479, blob_last_accessed=1756926367.299175, blob_last_modified=1756926368.6141694), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417690/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/283af9b17d776b75f6ff6ad1f426d6d0e0e5aa1f2089a20910edcb6763191b69'), size_on_disk=12265448, blob_last_accessed=1756926319.171384, blob_last_modified=1756926321.4153743), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417739/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1c9e3fcff1dc467673efe70177cb4d678d7eff7b5b2bb3ded247d7a458b466dc'), size_on_disk=21974051, blob_last_accessed=1756926326.8623507, blob_last_modified=1756926328.7393425), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417727/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/414c702fdf3dd97c536f3237d2c38e0ef5ad8d08bb7c991ee252e16d57b201b6'), size_on_disk=2636905, blob_last_accessed=1756926325.0683584, blob_last_modified=1756926325.8093553), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380960/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/60771297311bf96c3213c2a96321062a06067c91d2bba2fce69a7a6d7a0c980e'), size_on_disk=560607, blob_last_accessed=1756926369.7811644, blob_last_modified=1756926370.3121622), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417612/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ee40f7af39e729ee79ef18d6777bbd632b7cc7811041a32d54f4a02b42638464'), size_on_disk=3509045, blob_last_accessed=1756926301.5874608, blob_last_modified=1756926302.6274562), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417881/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bf380975f31e362e8964000688bff364809bbe2e8e3efa9ceaa71253ca6beefb'), size_on_disk=8717095, blob_last_accessed=1756926351.848242, blob_last_modified=1756926352.9732373), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380988/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/27b6e71986cf2b7da20b64dbda0ba47e08753f7184ddd57ed288f11df88d21e1'), size_on_disk=16063760, blob_last_accessed=1756926373.7421472, blob_last_modified=1756926374.932142), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381030/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e5baa8386faa327b26d27d6c8e6b265dddac1d55565e81d724a1980b93d871a3'), size_on_disk=1867415, blob_last_accessed=1756926379.2021236, blob_last_modified=1756926379.6681216), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417714/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d6abeb724a8c8b23217f3465284564da5baffee59ee4e3af582e0a3705ae32c6'), size_on_disk=262443, blob_last_accessed=1756926323.756364, blob_last_modified=1756926324.256362), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417744/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/faf407f0b251cf5f58c8ac7becee4b995327fd9a7d6599c3045fde8a78e1e811'), size_on_disk=2548979, blob_last_accessed=1756926327.7843466, blob_last_modified=1756926328.7443423), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380968/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/549ef4e39a437adb99efecdc9b05fee3ca4b31c878b99f7a4cf1398f405ca049'), size_on_disk=3575926, blob_last_accessed=1756926370.7441602, blob_last_modified=1756926371.5941565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417832/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/268a3b7a91ccc3f3909042a711454c336b1887705de99442bda022bcbc26a01e'), size_on_disk=7655304, blob_last_accessed=1756926343.2332795, blob_last_modified=1756926344.3042748), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380948/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6ac219aa7f5be77c21bde60c80ccad59edf825e2a009d6a4b7fc8bd4d8be83d4'), size_on_disk=6046323, blob_last_accessed=1756926368.6691692, blob_last_modified=1756926369.5471654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380941/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9a23f03fee8d60d752bd0bc1e59e9205abea4f3a78e3272b9a03447f076383d5'), size_on_disk=3514837, blob_last_accessed=1756926366.9851766, blob_last_modified=1756926368.7591689), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417851/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2d8a571fae50598143744a1f13d40cec2ea8c2198e87aae17f96d7c8a00460d0'), size_on_disk=11051527, blob_last_accessed=1756926346.6472647, blob_last_modified=1756926347.6582603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417783/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9603c95097c451e616c54d52d3474b866453409905ea16365d019e77c6ca31b3'), size_on_disk=4176778, blob_last_accessed=1756926333.6983209, blob_last_modified=1756926334.4773176), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417835/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6245fc8d220aad556115d4b831f72ae6a9beac48ed6e2aba811284aa0ccab4f7'), size_on_disk=7888215, blob_last_accessed=1756926343.795277, blob_last_modified=1756926344.942272), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380970/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bdec4a51127eaf09abb5d25d7f3a732afc3b561105fd18ba1df44d04e6dd90a2'), size_on_disk=3545790, blob_last_accessed=1756926370.9301593, blob_last_modified=1756926371.5451567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417811/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/da92b1c1579a2295da085050305367bd02a47fd52a74d1ec2a9f227b5e808b11'), size_on_disk=6127159, blob_last_accessed=1756926338.8782983, blob_last_modified=1756926340.1172931), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380992/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dbc6b1de202e7fb25701c02607facc3e8c332550907ca0b70ba177533a311d6d'), size_on_disk=2551903, blob_last_accessed=1756926374.2281451, blob_last_modified=1756926375.0341415), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417773/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d7980069a740f581dbcfd9416372452203d636d8ad4c9352c3b2d6483cd27379'), size_on_disk=5178664, blob_last_accessed=1756926332.1683276, blob_last_modified=1756926333.411322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380969/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/10410488c74f35a493cef2ffc4a64b28b336be1fb32a2b6dac692e0d7fbd4271'), size_on_disk=1973405, blob_last_accessed=1756926371.041159, blob_last_modified=1756926372.1191542), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381053/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6ca1dc55841bbb6d8d8a83d1a87d00570c1eea7cf1963db8cb04ddd427711fb6'), size_on_disk=10277654, blob_last_accessed=1756926777.9197485, blob_last_modified=1756926779.9187374), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380965/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6b384b53be751cec9056e74c2c8ce9fe2e532cfbf6a38e43bb09ce74ec817f3a'), size_on_disk=12974487, blob_last_accessed=1756926370.3761618, blob_last_modified=1756926371.4361572), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417838/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0dcd4dc268902c8e0845a1ee81938a81a00412014d5b198a41a523cc3450dfc4'), size_on_disk=10178872, blob_last_accessed=1756926344.5452738, blob_last_modified=1756926345.3902702), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381042/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/80a0c18e8d146043e9c95c6a86552d28e28267a489b3e5c6f2a11319cf47b877'), size_on_disk=4477936, blob_last_accessed=1756926775.660761, blob_last_modified=1756926776.9177542), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417765/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bd2b5a1b1593db002ea9c159d4c40d1d8addddbf124207a7b32b0bb43e2e4d72'), size_on_disk=2696437, blob_last_accessed=1756926331.2103317, blob_last_modified=1756926332.046328), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417647/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8c01c928abe00b5afa6a2292820695797391b3671cc110b9026f5fc5cb8564ae'), size_on_disk=2891716, blob_last_accessed=1756926310.1614234, blob_last_modified=1756926310.93942), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417747/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7011e7ff4b64ca9c57bdab93cfcfc52938e7b0574b48417feb4f6185f6c118b9'), size_on_disk=4791771, blob_last_accessed=1756926328.7593424, blob_last_modified=1756926329.9543371), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381069/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aa8f6186814bf973f8b4448a0c4b692c31594e03905c75d5691b982d6ccde97d'), size_on_disk=13705106, blob_last_accessed=1756926780.5807338, blob_last_modified=1756926782.319724), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380940/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4f59a7f9287c427bed27910f08a041e460eff2490c21e2fbdaa5cc9348f5921e'), size_on_disk=6062382, blob_last_accessed=1756926366.8141773, blob_last_modified=1756926368.3051708), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417809/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/486100d34df42ce97476d9753f8d57edb5f53596eaeca2d0cf81f5f95a4f4e12'), size_on_disk=7376702, blob_last_accessed=1756926338.5872996, blob_last_modified=1756926339.9172938), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417891/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cabef83ee373e37d528b127d813f1535faede4b98c5524d83e9906df83558b07'), size_on_disk=12596609, blob_last_accessed=1756926353.9052331, blob_last_modified=1756926355.2992272), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417904/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cf9194917da2f4c6c3f0411256b2604e8c14959373d7431047de2e23cffbb9bf'), size_on_disk=7269877, blob_last_accessed=1756926356.062224, blob_last_modified=1756926358.1762147), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417908/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/128e54326b088977000f8caaba7d09e324281ed7fe0e3e5f2779783f0a82f293'), size_on_disk=3614805, blob_last_accessed=1756926357.1272192, blob_last_modified=1756926358.2542143), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381034/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/112a8e705f7536fa06c630165d3c409f87a5784ad626b891cc2b3919e24bdd14'), size_on_disk=250813, blob_last_accessed=1756926379.795121, blob_last_modified=1756926380.1791193), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417866/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b0424cd7567723443472cb266e090a30e2b64ee6b09bb1b94d8dcfdb9f0b0c43'), size_on_disk=9685580, blob_last_accessed=1756926349.109254, blob_last_modified=1756926350.493248), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417860/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/72e751d246e9b938ce9ab4380e2a1114ecd3c36d5829e3298f391c1edfefae24'), size_on_disk=3996315, blob_last_accessed=1756926347.9312592, blob_last_modified=1756926348.819255), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417729/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8383a0e86c2bfe15c46045980d6768eb4e7e3c4c157ff1ae7ce20144e245ca80'), size_on_disk=3058876, blob_last_accessed=1756926325.3113573, blob_last_modified=1756926326.103354), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417805/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/661f37ed294c22f6c3b02d5c1ea25794f5e33090c246185e891ee82153e2e5f2'), size_on_disk=3071166, blob_last_accessed=1756926337.8643029, blob_last_modified=1756926339.3292964), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417877/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b170370a220db5677b9c6193707d32d1d14b9082ca574b9562c4ac8ea4c72c26'), size_on_disk=10449263, blob_last_accessed=1756926351.0392456, blob_last_modified=1756926352.32624), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417924/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f2053fcaa7f21b1703e3491e167b457c29759b2fe9535f0cb2bfa257a1b86cea'), size_on_disk=6988370, blob_last_accessed=1756926359.940207, blob_last_modified=1756926360.8152032), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417905/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b9d291469b7a414bab157f45bfd3a1f7ffaaf931de25fec9eb18012a18b70939'), size_on_disk=4418829, blob_last_accessed=1756926356.1612234, blob_last_modified=1756926357.2652185), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380998/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6808b8e1fa74b807321826b2c54182a3b6e257f0a223be0a6a94caf9463959e9'), size_on_disk=16847111, blob_last_accessed=1756926375.2231407, blob_last_modified=1756926376.572135), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417932/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f040d4f594fe69143a7c3f04f0498be2a30fb9b5e78d42ec464fee65efcc42dd'), size_on_disk=9231914, blob_last_accessed=1756926361.4262006, blob_last_modified=1756926362.5771956), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417636/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4a3b2c7076107dcc47a87179cecab9e2051350c94e951a7aadc2ff0cf6fdbaca'), size_on_disk=43479381, blob_last_accessed=1756926307.6494343, blob_last_modified=1756926311.4484177), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381067/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/da9c5ab3c0f970f9c2af4c0f5f0e9a95d47769e8096ff6b9d76092becbc8517e'), size_on_disk=9837433, blob_last_accessed=1756926780.2977352, blob_last_modified=1756926781.5777283), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417754/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0d840353f1148a08928dfb3177a0abfe635c406fc77d6d9958743585ade6b872'), size_on_disk=1101891, blob_last_accessed=1756926329.758338, blob_last_modified=1756926330.215336), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381039/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5f95b6de8d0d73589c612313eaed6ce1e6adf734e795719f23df6f54cbbedc69'), size_on_disk=1656495, blob_last_accessed=1756926379.8751206, blob_last_modified=1756926380.283119), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380984/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a15ab5e5a0ead07dc2a530a1e4abb714027a95ce090c7a0eda5ec2bb714d903f'), size_on_disk=8070704, blob_last_accessed=1756926373.2251494, blob_last_modified=1756926374.1601455), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417624/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/28c1de2a4bd6b421cd05c3bb211ffde83e4b830127f106ec7be42ad288e26ef6'), size_on_disk=16758887, blob_last_accessed=1756926303.7884512, blob_last_modified=1756926305.6184433), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417703/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e82b21d919b84770e2d141eaea09aff82ef57c3ff3dbd15c588c1b8cff212c9e'), size_on_disk=1462841, blob_last_accessed=1756926322.0533717, blob_last_modified=1756926322.898368), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380995/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e0cae334f1cb71be0be1e8ceb5fc6ad23d22e4db20cb64b1314c3d080f2bb5f7'), size_on_disk=6315860, blob_last_accessed=1756926374.931142, blob_last_modified=1756926375.7271385), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417787/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/17f33e06b6bd77c0bbd3141b53fc2d9550b0c54e99b18fe79ddad931e3a283a9'), size_on_disk=12355842, blob_last_accessed=1756926334.5003173, blob_last_modified=1756926336.2953095), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417672/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f0741c46f1460c30d4d9b88ca1f7c0ca77a040cf25a34d82fab4c777a921ac26'), size_on_disk=18268758, blob_last_accessed=1756926314.9764023, blob_last_modified=1756926316.5143957), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417936/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8ba89583b8c822b64968df9eca8273d12270429f0f820d6027f13c66504c383d'), size_on_disk=4552381, blob_last_accessed=1756926362.205197, blob_last_modified=1756926363.171193), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380956/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/15047f69259f2cbc2cb08949d07ac6fde505bade6a0e3bb3912b183a48a4b288'), size_on_disk=2282680, blob_last_accessed=1756926369.4771657, blob_last_modified=1756926370.1431627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/225bf60f7c9796682210dc48d07a30d3dabab8ebc13abb7a84834279fa131020'), size_on_disk=5307353, blob_last_accessed=1756926376.2401364, blob_last_modified=1756926377.3751316), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417645/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/761eba472193b23cbf7735946e08aa1c4db5b1abd5125f1aad22ddabbf872f04'), size_on_disk=2080044, blob_last_accessed=1756926309.765425, blob_last_modified=1756926311.5084174), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417712/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/59cbfa83b9abe13f60d6a80b35afe857e3ffd761366133f60505312a2b5a72e2'), size_on_disk=641243, blob_last_accessed=1756926323.2163665, blob_last_modified=1756926323.7173643), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417923/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a32971eb62fe5c4c08298b1bdd97a916d8ef8f5898aee42aaf3fbaa8069e92d1'), size_on_disk=10004280, blob_last_accessed=1756926359.919207, blob_last_modified=1756926362.0851977), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417693/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/98a5478f5bd4feb033ac69510f2bf785c87777377b7b8b6dce24b8f89dbd6a03'), size_on_disk=18580692, blob_last_accessed=1756926319.7013817, blob_last_modified=1756926321.9783719), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417812/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9e8445f413d26051897f0fe3e7566b116a9a55062b78201990de334f17aecb31'), size_on_disk=19905089, blob_last_accessed=1756926339.0272977, blob_last_modified=1756926340.6232908), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417755/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f4ffef985aed69a80417b5179ef9923bd73378a93a97f2dff128b1f7716b4599'), size_on_disk=8437424, blob_last_accessed=1756926329.777338, blob_last_modified=1756926331.2423315), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417772/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a21b204a2da7654dd5bf4e7965a3c1e19159c16052232030787175a830ff0233'), size_on_disk=8204405, blob_last_accessed=1756926332.1323278, blob_last_modified=1756926333.453322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381052/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/652334151cb539b888d9cfcb60d9711d80bd74ec65cfa3799de10b2970ab9c09'), size_on_disk=5788579, blob_last_accessed=1756926777.7767494, blob_last_modified=1756926779.283741), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380999/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/acb2ee4711dab5800afb7ccbea72c8c80268b0a848723e7bd8ecf197decb982d'), size_on_disk=2605563, blob_last_accessed=1756926375.2281408, blob_last_modified=1756926376.1321368), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417751/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9a5733130de69115a731511dac8bf577fc5609a7f9649106ae9a12ffae7011ba'), size_on_disk=1660869, blob_last_accessed=1756926329.2023404, blob_last_modified=1756926329.9043374), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380959/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4693c1876e880fee1ec277fd5ae9127aafa9f40d41a3d5ed482dfcfa8cef55f1'), size_on_disk=873902, blob_last_accessed=1756926369.7061646, blob_last_modified=1756926370.1731627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417749/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8460c24102627d611c8918a1620078ee1bdf9929c44cdca863cd0dbb23f9cf1f'), size_on_disk=4938551, blob_last_accessed=1756926328.814342, blob_last_modified=1756926329.7143383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380957/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5a2362d35ac90fb1926051e6313886a649074b0022b128741a22a5ee24bb095d'), size_on_disk=5388789, blob_last_accessed=1756926369.5311654, blob_last_modified=1756926370.8571596), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417874/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6674c316eab3b03bbbe3502565efff7650445f5e1453933336d74938e42a14bf'), size_on_disk=6201897, blob_last_accessed=1756926350.5772476, blob_last_modified=1756926351.5142436), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417792/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/196cd0cf279f6bc85ca0dbeb1f089721f63eb167644d999b45d8cfe9a1f52acf'), size_on_disk=11982413, blob_last_accessed=1756926335.4593132, blob_last_modified=1756926336.651308), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417778/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/54e8ea03e214967eb4a1c7b50a086c4d7083d2c9c95dd0419e545fb0e8f18422'), size_on_disk=9345257, blob_last_accessed=1756926333.1513233, blob_last_modified=1756926334.0903192), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417933/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4fe347d88754646d39d93dd8a78e857d04d1903f7163a21495038b087d5f6b8a'), size_on_disk=12389872, blob_last_accessed=1756926361.7481992, blob_last_modified=1756926362.9061942), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417726/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/739e474fc54ffee0c7e6300d3abb1f83d33d48c78d137ed848f837fa186b9308'), size_on_disk=2430088, blob_last_accessed=1756926325.0663583, blob_last_modified=1756926325.6803558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380978/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a1d811dc3a4bfd44683ddf8bd0401a71d4941497c5cac8d8040a597fc5d26587'), size_on_disk=5902739, blob_last_accessed=1756926372.160154, blob_last_modified=1756926373.09715), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417878/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d3d105cc45280a5ea88c2bba754c0c5849d0cec7112a0926e9ed0c946e7f5cfd'), size_on_disk=8838404, blob_last_accessed=1756926351.2492447, blob_last_modified=1756926352.7352383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380993/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d568d29486ad3ec6f67f87b78cacb8b7102c591f979ec2bdb17ff75bde789670'), size_on_disk=3031778, blob_last_accessed=1756926374.496144, blob_last_modified=1756926375.2661407), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417892/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7ac6b85018422c74c894467b7b53b1c2dc7c5298fc31654bdea5ca70493e0a87'), size_on_disk=8194725, blob_last_accessed=1756926354.176232, blob_last_modified=1756926354.9712286), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417798/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0ac62fa7573f4b3c756fd9c280be4b97ec0c7c756e8c76307d4fab56d29e1c08'), size_on_disk=10509245, blob_last_accessed=1756926336.5193086, blob_last_modified=1756926337.800303), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417873/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d282d59c3c191bd7b7fa7251108224d0ac3929bc98f96a2c43f283f8a5cd046a'), size_on_disk=7609540, blob_last_accessed=1756926350.3852484, blob_last_modified=1756926351.373244), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417770/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0e7437c90c616b78ccc0963e0388385028b70d66fbe73e0ceb4e30e2d0239816'), size_on_disk=8252889, blob_last_accessed=1756926331.6563299, blob_last_modified=1756926333.0863235), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417603/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdfb1fd9b21341104e6423c684b0ae8a52ed1701eb3e232babb98340f953ea5b'), size_on_disk=4460026, blob_last_accessed=1756926300.366466, blob_last_modified=1756926301.2574623), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417795/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b995a48f28ae3baa145438a39c9fed73fa8689eb31e07ff02a3bc6bc827eb76e'), size_on_disk=8032882, blob_last_accessed=1756926336.1053104, blob_last_modified=1756926336.9993064), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381014/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5c5106dfb24bcfb1436aeb87b8218df873417ca2809aed71ca58e9b48ffacd29'), size_on_disk=1425306, blob_last_accessed=1756926377.5811305, blob_last_modified=1756926378.2571278), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417837/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/04d099809ca78bd87dcb9921398681ac64e827439049a37e97a71ba0828debfb'), size_on_disk=8348990, blob_last_accessed=1756926344.4012744, blob_last_modified=1756926345.6152692), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417785/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/209799965b03585e1143e881d87c5cea80d37e06637d2fc3f7e1ad2eaadfd3bc'), size_on_disk=11557515, blob_last_accessed=1756926334.1813188, blob_last_modified=1756926335.1353147), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417669/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8eaf08a45dbd72787c5250403f3d3079db9902e77527c7ca438abc740fdfb753'), size_on_disk=7376929, blob_last_accessed=1756926314.6724036, blob_last_modified=1756926316.0383978), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417733/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/542261ed8bf189205b611485f1a878f28cae5c90e452ba9cad687eacfdfc2814'), size_on_disk=2645461, blob_last_accessed=1756926325.8883548, blob_last_modified=1756926327.3873484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417819/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a1ba6808d672dbc9c58c26364c5cd9ed0b8a3041e6714844846648d3cd1aa050'), size_on_disk=3244517, blob_last_accessed=1756926340.7022905, blob_last_modified=1756926341.6952863), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417857/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/64f710e0b8a62bb6eddf3508f133b6b82af8bd21dd07f2319af6d07981085b1e'), size_on_disk=11109477, blob_last_accessed=1756926347.471261, blob_last_modified=1756926349.6202517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417841/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cb829be078dd8b533a90c2c548dfb2ce0455026be54c36572337e0a1ddeeb2cf'), size_on_disk=5791736, blob_last_accessed=1756926345.138271, blob_last_modified=1756926345.9902675), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417925/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8cdb783613764a35fd3a73db89463db6876951d443f5651a9a46877a53917f3f'), size_on_disk=912848, blob_last_accessed=1756926360.409205, blob_last_modified=1756926361.1532018), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417916/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2abad8555b89fbb5009e8f2a95e553a5ce579d1841ae4e71eec7b137f3282ef1'), size_on_disk=9236466, blob_last_accessed=1756926358.371214, blob_last_modified=1756926359.0582108), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417850/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c08fca103aa3dd1944dcf5c73b67397263842dc687b4101c8ab2c1a63d50205a'), size_on_disk=1182282, blob_last_accessed=1756926346.5122652, blob_last_modified=1756926347.3082619), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417635/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d2d1c241467bd5c55cf059f793b4236c99d48e59bec853d1052ff6c644e50d1b'), size_on_disk=3044373, blob_last_accessed=1756926307.2014363, blob_last_modified=1756926308.391431), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417606/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f76c81d98337c9e127bf5516af0dd135458fd622c01f2b5c9fd85ce6edfa4c7f'), size_on_disk=9502783, blob_last_accessed=1756926300.5794652, blob_last_modified=1756926301.6394606), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417653/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/59233342d3c3b71fce95d5295cf7c1e5ef3f4254dbc120229cb83b6c8115c759'), size_on_disk=12208357, blob_last_accessed=1756926311.136419, blob_last_modified=1756926312.3904135), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417629/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/29c9c86be94792d797005b106ab00d066634bea44fbf17e81738a00cbffa264b'), size_on_disk=22750780, blob_last_accessed=1756926305.5594435, blob_last_modified=1756926307.5804346), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381066/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ae017c72cf3b91ea3fdd40f63a126f640e7049340baca502ff3228c6ee40a7b4'), size_on_disk=13139272, blob_last_accessed=1756926780.000737, blob_last_modified=1756926782.6217225), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417902/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2d72fc510cd348af7611ca170b7804b228b74bd38e7ff5e437375c1cc926fa95'), size_on_disk=8952511, blob_last_accessed=1756926355.9322243, blob_last_modified=1756926357.2422187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380991/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dd86db1779581d5853d4d6a0fa2aeacdb00f92c832683ad788743f17db625db4'), size_on_disk=2640507, blob_last_accessed=1756926374.030146, blob_last_modified=1756926374.8661423), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381035/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/410edf5a6097a77bcec6880ec2618b4aec4bc10c44607163fe2b27aca0a05117'), size_on_disk=1451911, blob_last_accessed=1756926379.8691208, blob_last_modified=1756926380.5191178), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417657/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62398c518588ad11a939f95736cf0ed01a39890567cd4a8f05e9c9ba1155f9c1'), size_on_disk=10144285, blob_last_accessed=1756926311.5634172, blob_last_modified=1756926313.1174104), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417762/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c8b34d14be6dd44af03432d758f705cf81e86a46831c6deb4b0944248709630a'), size_on_disk=3024339, blob_last_accessed=1756926330.943333, blob_last_modified=1756926331.59933), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417633/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0b38824aeb2fac57eca256e3652539deb4bc221eeb5afc3701a997885f9ad9d0'), size_on_disk=19311587, blob_last_accessed=1756926306.8324378, blob_last_modified=1756926308.847429), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381041/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a53caa1961d9245d82db10989b8a8d601760ccf26f67666d5558937830c51e31'), size_on_disk=6621865, blob_last_accessed=1756926774.784766, blob_last_modified=1756926775.9747593), CachedFileInfo(file_name='GSE209631_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE209631_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/be49eef90682fbd55a5e60ada8b4c1c554e1b5a2ec1209996269db020d3bb074'), size_on_disk=4258, blob_last_accessed=1756926300.173467, blob_last_modified=1756926300.594465), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417697/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/198a3cd31740f347f0308db2914df4fc0fc303b4a9f040fbaf79fb5ca0170e7f'), size_on_disk=5521438, blob_last_accessed=1756926321.4153743, blob_last_modified=1756926322.4743698), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417611/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c82d882f6538d65316dd01e26a82af13e41717952c79e20dae6c3f1d9724ab60'), size_on_disk=3720891, blob_last_accessed=1756926301.4614613, blob_last_modified=1756926302.2704577), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417605/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f689b77eccd8d95d70fb616f4e2aa03a41201aec2a8c3c86849b7b145cb0f796'), size_on_disk=1787545, blob_last_accessed=1756926300.5184655, blob_last_modified=1756926301.74346), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417673/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/535d074f70f4553e24f3171fe5cfbd1821635ce7ca6ef93f8665876de620d6c9'), size_on_disk=6559693, blob_last_accessed=1756926315.1844015, blob_last_modified=1756926317.0733933), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381031/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b4705ac4577c8e7b116d1a1b677967f69412b0e4716e0de7fa7ee97bc9be4d74'), size_on_disk=4551217, blob_last_accessed=1756926379.2931232, blob_last_modified=1756926776.2707577), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417780/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4f1b7c3ded6441c8e906c3494ae63750d25303ee6f8d8e05120dc11ce8d79080'), size_on_disk=9025914, blob_last_accessed=1756926333.2923226, blob_last_modified=1756926334.387318), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417911/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3ca8b1b14564fbc90b44995a61a34d5a67482eb5fd867ce4f9f2196de2b73539'), size_on_disk=3051177, blob_last_accessed=1756926357.404218, blob_last_modified=1756926358.3022141), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417883/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8e23cc97e00adfd244a989f83702f4ce7a998ba7998dd95908a1b4ec256bc054'), size_on_disk=10709227, blob_last_accessed=1756926352.4542394, blob_last_modified=1756926353.675234), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417664/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/91fc5dce8497d1fc5466fb5ac31e44a20b295d8f97825500f256f6830ea2f983'), size_on_disk=8203399, blob_last_accessed=1756926313.1894102, blob_last_modified=1756926314.6624038), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381021/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3e80794742d10b6d120e70c4e4f1d7f77123ddf8aa18499f686b4abc8eadd325'), size_on_disk=1524611, blob_last_accessed=1756926378.410127, blob_last_modified=1756926378.897125), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417621/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/354c654ace9bc123bcd9009c56af518b98f1a547a7ccd79f348d0e533607a41e'), size_on_disk=20457307, blob_last_accessed=1756926302.5744565, blob_last_modified=1756926307.7874336), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380975/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ea018f4882a60f5fb07a4bbae51bd494e44e6df9b215d9da5e405687a567dcbf'), size_on_disk=8369150, blob_last_accessed=1756926371.6051564, blob_last_modified=1756926372.7471516), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380939/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dc6382d9dba7855504a6c7fa2a4adc44f564e25ff5c54eedd082c3cbe0a520a0'), size_on_disk=9235430, blob_last_accessed=1756926366.630178, blob_last_modified=1756926367.5861738), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417781/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9dd7989f90f8762246868ccd7d11c9ce876b2b33099efc3a4c5bf1bc7cbcca80'), size_on_disk=6923944, blob_last_accessed=1756926333.4783218, blob_last_modified=1756926335.3743136), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417784/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fd3dd53cf34f48c235e4f771378f42a5605446bbc62d108a5637a042513fd75d'), size_on_disk=4826642, blob_last_accessed=1756926333.7233207, blob_last_modified=1756926334.576317), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417919/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f5504428a85399cb212c74235abf9cec068dba03136a87ea79eae6d63a3cf430'), size_on_disk=7011211, blob_last_accessed=1756926358.6892123, blob_last_modified=1756926359.8312075), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417927/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8e639025209cff8d34b7e35a5fa371282bb725173225097a39f8f2101a49deed'), size_on_disk=7241057, blob_last_accessed=1756926360.6762037, blob_last_modified=1756926362.1171975), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381000/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/503f854145f3a220aa936072c3e66410d34914fef948992399a4addbfc977186'), size_on_disk=3904727, blob_last_accessed=1756926375.3401403, blob_last_modified=1756926376.5601351), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417748/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/db4c67c001a1f688f70918f0e25d3ddd996392b33b9f1144a14551bc0026d036'), size_on_disk=3246481, blob_last_accessed=1756926328.8123422, blob_last_modified=1756926329.6943383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381056/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/382194ca9758ddc18f059a649b13c5a21ead3a7f1fa5e012065315047a037a58'), size_on_disk=2351268, blob_last_accessed=1756926778.362746, blob_last_modified=1756926778.9827425), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381057/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4e6c617d22a655d32847df8b5334f4fb7236a15b8bf6e30aaabcbb0a4c55782f'), size_on_disk=17494867, blob_last_accessed=1756926778.5417452, blob_last_modified=1756926780.4997342), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417885/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6d5faeda491268a8ec88393883d743aac6cd14d4837698164ca32ca1052da36f'), size_on_disk=7668243, blob_last_accessed=1756926352.806238, blob_last_modified=1756926354.1422322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417661/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c6effb515a20066646b7863cb86e9dc012e14187e432ba7458cd6af18662bfd6'), size_on_disk=22122748, blob_last_accessed=1756926312.9054115, blob_last_modified=1756926315.6813993), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417699/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0c64ecbe6b245299fe04c04239d792fbae0f1b288562fe0c7912a24de56f0ad5'), size_on_disk=5707915, blob_last_accessed=1756926321.6203735, blob_last_modified=1756926322.8483682), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417816/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/86256a1b98c6d44750fd21ac9ea099262f1f0ec03d182b65741d669fb80f289e'), size_on_disk=10618044, blob_last_accessed=1756926339.7852945, blob_last_modified=1756926341.8372855), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417679/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bc48687cfd5c03c1a71787981180295dd3a27cf4d2c46bd26bb68cdf8b35925a'), size_on_disk=17346576, blob_last_accessed=1756926316.1203973, blob_last_modified=1756926317.84439), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417685/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/32edfbf9226b7ef1060ebe3e6986a1803d60408ccda969528cf4a59c5785c722'), size_on_disk=16967633, blob_last_accessed=1756926317.9163895, blob_last_modified=1756926319.5263824), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417852/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/43431a444b2761ed9b6fa4c7e6edfc8da31b64ff933408c517bc771e9f3e94dc'), size_on_disk=6967416, blob_last_accessed=1756926346.8272638, blob_last_modified=1756926347.8462594), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756926300.2174668, blob_last_modified=1756926300.2804666), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417842/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c763016dcbf1b53df840e3ea3c1b45234e2d9e797114000efaf692e2c394d487'), size_on_disk=9967694, blob_last_accessed=1756926345.1472712, blob_last_modified=1756926347.1812623), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417610/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a93c708ade83c1c4521589a51835a1dcc98972ab900e0eefdb8ce34e64394bf4'), size_on_disk=2566580, blob_last_accessed=1756926301.328462, blob_last_modified=1756926302.4814568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417831/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/53d810292e5a513a8ab543fe18783f8d493cedbadc7c97400dc103a4675954df'), size_on_disk=6767934, blob_last_accessed=1756926343.12528, blob_last_modified=1756926344.4612741), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417723/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f397cef2d9c491fb034826727c63b3c224f6ed791c9b4dcf884c63c55c9961c'), size_on_disk=2800893, blob_last_accessed=1756926324.70836, blob_last_modified=1756926325.4453568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417769/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e640018be0b2de04e0ed77c7586c413990876e91348531f43511c20da5fe279a'), size_on_disk=11568261, blob_last_accessed=1756926331.59533, blob_last_modified=1756926333.0913236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417705/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/92b4f5af6793c98b854bcca83c86ac0cb41ec4e40ec23d3ba9c4ff287852c63c'), size_on_disk=332184, blob_last_accessed=1756926322.5923693, blob_last_modified=1756926323.1503668), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417691/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/48de5a2968610e5ab855522d27f535d40e1973c1835ec32fcfa5bcd91a0795dc'), size_on_disk=14481663, blob_last_accessed=1756926319.447383, blob_last_modified=1756926321.2093751), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417948/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d43ea802a452aa9d57004381e1688222e61bd313b107077c1c663555547d1d44'), size_on_disk=1748124, blob_last_accessed=1756926364.3661878, blob_last_modified=1756926365.038185), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417646/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2b3d78fc052de8647b1ba6e83245b5f375b4e6c10952feff795855160458a1d5'), size_on_disk=396843, blob_last_accessed=1756926310.1544235, blob_last_modified=1756926311.0694194), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381050/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f3c3bac37a1d4ec633edf53ba305c970eb3de412fd54dfbaf0cdf89f39574334'), size_on_disk=1518412, blob_last_accessed=1756926777.1687527, blob_last_modified=1756926778.028748), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417728/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2be09708238c50a31b4e9b334c6202c26eaa6a950ebf3de4287651999d45097e'), size_on_disk=3385901, blob_last_accessed=1756926325.0603585, blob_last_modified=1756926325.869355), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4c4a265f9e3e8d373550ea58ca1cde6094c823f090336974fde3e7cee117fd99'), size_on_disk=1770007, blob_last_accessed=1756926376.6771345, blob_last_modified=1756926377.1011326), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417875/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8dc3634dae23cad7f562d15903341a67d3048929bd2198e53f71529b00a3508a'), size_on_disk=11490812, blob_last_accessed=1756926350.6762471, blob_last_modified=1756926351.7832425), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417775/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2ca9ba5e1394191206945f41168d555519fe3963d4c39f251c1ead57f2a323f4'), size_on_disk=1059891, blob_last_accessed=1756926332.3723266, blob_last_modified=1756926332.773325), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417803/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1fd8b542fb1b0e18bc5346c50ad85da440ff6190a987467ec6876ed3ec51a4e8'), size_on_disk=4928668, blob_last_accessed=1756926337.5433042, blob_last_modified=1756926338.7182992), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417741/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/734d5947cb26e877b6ae9e9adaf84575974a4fe2fb464ba11c4320906f17a42a'), size_on_disk=4566889, blob_last_accessed=1756926326.96635, blob_last_modified=1756926328.5653431), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381038/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/415f62b8333a02df8a87ba85e1d0de30a6cc9fdb07f7c8dc02d30f81ed4c14ef'), size_on_disk=2424198, blob_last_accessed=1756926379.8751206, blob_last_modified=1756926380.4921181), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380967/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3abcaaa4db306d580dc27882172ced53d591b8aa69a1805bd2a9eea0448ff941'), size_on_disk=1245882, blob_last_accessed=1756926370.6731606, blob_last_modified=1756926371.1021585), CachedFileInfo(file_name='GSE178430_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE178430_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/be2c4af55a502d7fa22abb28afe6db347950327bde1bc7e97dbb68fdb0c59f3d'), size_on_disk=9398, blob_last_accessed=1756926861.9192877, blob_last_modified=1756926300.5344653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417682/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/83b3100cfd1dc6918855c4a711ff607b36f40971655733855a7df353b4d5fca5'), size_on_disk=14037100, blob_last_accessed=1756926317.1663928, blob_last_modified=1756926319.1013844), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417822/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62cbbb0172158a2c0776000136424032a794757ca9e9f971e5f445deb20e8579'), size_on_disk=6040431, blob_last_accessed=1756926341.3882875, blob_last_modified=1756926342.8302813), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417626/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1981a2a1e321acc5c35bbeb9b61a12636c27f28d6e216de20093afdbee9f8689'), size_on_disk=12046611, blob_last_accessed=1756926304.4464483, blob_last_modified=1756926306.2084405), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380961/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fc4356aaa2a30217145b4aecc4f3fdd2575fcd696a2603fea3d8112998f770c7'), size_on_disk=798599, blob_last_accessed=1756926370.1791627, blob_last_modified=1756926370.589161), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380953/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5b3410430d33f3123b935982be90037f2d0a8e657f6b0b70a7042846d5ddd1b0'), size_on_disk=1192961, blob_last_accessed=1756926368.8401685, blob_last_modified=1756926369.627165), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381071/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cf15be48b47b6c369c6a4290b783b9b22b01d6cb89d04c738c275241548b811b'), size_on_disk=2543373, blob_last_accessed=1756926781.2297301, blob_last_modified=1756926782.309724), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417623/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/562a43ff381a2ca177fb1cc3dbe47fc2cf7cade495bad511ca7ed75aefca7a98'), size_on_disk=19049552, blob_last_accessed=1756926302.696456, blob_last_modified=1756926305.4934437), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417715/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ac5effc2ec07a8fb49bac4d77b7d4129a18ae2180807874ea02d1c252a3b68bd'), size_on_disk=520286, blob_last_accessed=1756926323.8693635, blob_last_modified=1756926324.477361), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417859/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e4c5915df6cb5bb450e1948d8c43bbe031d09ec345688ab51f2d8ea60f3be09f'), size_on_disk=12709203, blob_last_accessed=1756926347.8872592, blob_last_modified=1756926348.9972544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417937/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e432985b09ba72eab54fc008b87b20a718dbae96d920a61b47de7e16d214227c'), size_on_disk=6445266, blob_last_accessed=1756926362.6341953, blob_last_modified=1756926363.6641908), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417763/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/91d8a58ead35fc52f7c2649ce9509a92236a20ca8c26f5f2ef4ba1a38e64afb1'), size_on_disk=230495, blob_last_accessed=1756926330.8943331, blob_last_modified=1756926331.4653306), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380954/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/338886924ec42501a99a4b842e18fdbed28200d52daaf43e00ee601a147e0188'), size_on_disk=4903852, blob_last_accessed=1756926369.2841666, blob_last_modified=1756926370.101163), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417707/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/89192f5007f730a90c823219ea107c799935f5e824963090abad813603160fa8'), size_on_disk=2615872, blob_last_accessed=1756926322.9183679, blob_last_modified=1756926323.9473634), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417622/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/28eed3b67a6206b453d8adb775edf2bea32cfd54ba5b37d51787f67280e0317f'), size_on_disk=20411775, blob_last_accessed=1756926302.674456, blob_last_modified=1756926304.8564465), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417758/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/54f8e8d84e5b8b9457aaa143829fcfd5a4715d0fb184478ce659cd6c6efe7be1'), size_on_disk=4176901, blob_last_accessed=1756926330.0203369, blob_last_modified=1756926331.1383321), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417939/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a0fc7804b026f95809f33087deb75f00beeb9635475b62864d3e58e948361bfa'), size_on_disk=9099632, blob_last_accessed=1756926362.9701939, blob_last_modified=1756926363.9411898), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417658/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b6cf4c024743be14fcd5730f0ddbb960dbcd83d07adf8668639f9a1cdf195368'), size_on_disk=8754108, blob_last_accessed=1756926311.5764172, blob_last_modified=1756926313.1134105), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417829/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/63a9af789279180a07d8e8afe07bb0e9b8aa32c4340e01347a6f7ee809198308'), size_on_disk=5677879, blob_last_accessed=1756926342.904281, blob_last_modified=1756926343.7332773), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417721/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5457a24eab864fb369ae0e6fbdb302876e2a6c65ae83444f18bc01ad08ecfc0c'), size_on_disk=2073045, blob_last_accessed=1756926324.3243616, blob_last_modified=1756926324.9983587), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417759/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b2dad4cf4b03c846adf9d5229aeab340e58034c751bc68b37bb0c8402a7c071b'), size_on_disk=1372312, blob_last_accessed=1756926330.1243365, blob_last_modified=1756926331.1623318), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417782/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d6624acaa7c4ec21ae02d6d1322b8669a148308095e714b41bff868483fe2fa9'), size_on_disk=11742567, blob_last_accessed=1756926333.5273216, blob_last_modified=1756926334.824316), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381029/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6431cec7b7404a0773d8715761e2b161f01ebd8471950e71e17dad8976d83f3c'), size_on_disk=7229691, blob_last_accessed=1756926379.0071244, blob_last_modified=1756926776.565756), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417879/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6714b3f5155dd540962a7541625f45e14e6a93b4c01e913790b70818e50c821b'), size_on_disk=8601872, blob_last_accessed=1756926351.4612439, blob_last_modified=1756926352.8182378), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380994/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7c5f237d9c32233a005e39386360f02d9e1932f9ac267ac3458f96aaf59e01ef'), size_on_disk=2496053, blob_last_accessed=1756926374.8051426, blob_last_modified=1756926375.3471403), CachedFileInfo(file_name='GSE222268_metadata.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE222268_metadata.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4459325dc2deeb9f00d803b89cd8fdc823ed383f'), size_on_disk=61, blob_last_accessed=1756926300.2714665, blob_last_modified=1756926300.3444662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417607/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/623d085888c300fba1d05a9018ec3d4c31912f2b91a872ef34feda3322889b70'), size_on_disk=6809356, blob_last_accessed=1756926300.611465, blob_last_modified=1756926301.75846), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417615/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3b8b3c4c686238c7c642151bbdcb2ba7796de56c1922e558a1e7f08507dc5abb'), size_on_disk=2866472, blob_last_accessed=1756926301.8314598, blob_last_modified=1756926302.5744565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381016/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0e597280417f8f8b5b476bc9d66033afc9412759f5e2f9bd9f3367932cc6f03f'), size_on_disk=820782, blob_last_accessed=1756926377.9791288, blob_last_modified=1756926378.4041271), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417777/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bd953fd8a3ee2a5c90382c1c9adc8091fbca801aa87f07a409b6dd5a9704421f'), size_on_disk=1267333, blob_last_accessed=1756926332.9623241, blob_last_modified=1756926333.6353211), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380930/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/734e9bc8119c7a507e9cf84f9926b1f9d06ccc8b0062a9b4dc36631f6f1bc6d1'), size_on_disk=4604638, blob_last_accessed=1756926365.1301844, blob_last_modified=1756926365.9251812), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417616/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0b0f3c9481fd0e254abd544ba803de0c9da9b3a686451975f981d02fc481148d'), size_on_disk=15058562, blob_last_accessed=1756926301.8254597, blob_last_modified=1756926305.0234458), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417655/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/25d98ba17be9b354b0ec7a0e513a6ff9d4593dd1cadf269f28bcea1ca9c71565'), size_on_disk=5461224, blob_last_accessed=1756926311.5504172, blob_last_modified=1756926312.9474113), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381019/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3023e878b223b00b77d3411a6d20195b85aa7e61a8445fe725ff694c77816053'), size_on_disk=1022843, blob_last_accessed=1756926378.3191273, blob_last_modified=1756926378.864125), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fc6783c3340c869bc3838a9a9d5abb8bca67361feeef12c42b6cf478d73bfd66'), size_on_disk=9020794, blob_last_accessed=1756926366.2571797, blob_last_modified=1756926367.2561753)}), refs=frozenset({'main'}), last_modified=1756926783.3167186)}), last_accessed=1756926861.9192877, last_modified=1756926783.3167186), CachedRepoInfo(repo_id='BrentLab/mahendrawada_2025', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025'), size_on_disk=94317969, nb_files=8, revisions=frozenset({CachedRevisionInfo(commit_hash='3b912489743a1797199beb023a49b14f7e3ba19d', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/3b912489743a1797199beb023a49b14f7e3ba19d'), size_on_disk=1957331, files=frozenset({CachedFileInfo(file_name='chec_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/3b912489743a1797199beb023a49b14f7e3ba19d/chec_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/dd3e967d8c54c6056dc9fd6bdb921c3eed9d10df3a449273bd08faeb1b936765'), size_on_disk=1220601, blob_last_accessed=1759345153.538238, blob_last_modified=1758569549.9902885), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/3b912489743a1797199beb023a49b14f7e3ba19d/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/c8cbc56ef9fad47193120cce2affd35374b8373d'), size_on_disk=18689, blob_last_accessed=1759345153.4962385, blob_last_modified=1758569549.1302917), CachedFileInfo(file_name='features_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/3b912489743a1797199beb023a49b14f7e3ba19d/features_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/618ea89d880c8a1b4c6d05cc3c13a70cbb05784d10bf2b6c4fc954705203096c'), size_on_disk=431436, blob_last_accessed=1759345156.897212, blob_last_modified=1758652945.6395862), CachedFileInfo(file_name='rnaseq_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/3b912489743a1797199beb023a49b14f7e3ba19d/rnaseq_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/4411789f50aab449658c100f2758ceb410022a0a30f956fd5fe41152915580f9'), size_on_disk=286605, blob_last_accessed=1759345153.5952375, blob_last_modified=1758569588.8621461)}), refs=frozenset(), last_modified=1758652945.6395862), CachedRevisionInfo(commit_hash='8bea431be57e21633be4174df291540b1fae212a', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/8bea431be57e21633be4174df291540b1fae212a'), size_on_disk=18603, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/8bea431be57e21633be4174df291540b1fae212a/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/6a2a5a6a8006e386c0e47a2a98151442046d3d41'), size_on_disk=18603, blob_last_accessed=1758568089.249362, blob_last_modified=1758568089.246362)}), refs=frozenset(), last_modified=1758568089.246362), CachedRevisionInfo(commit_hash='874a5dfe4052a1e71b6544a2e5b2c99ad4286c04', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/874a5dfe4052a1e71b6544a2e5b2c99ad4286c04'), size_on_disk=18603, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/874a5dfe4052a1e71b6544a2e5b2c99ad4286c04/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/685c0e56e0c68e20e3c2af8e38267095d686c03c'), size_on_disk=18603, blob_last_accessed=1758569041.2889845, blob_last_modified=1758569041.2879846)}), refs=frozenset(), last_modified=1758569041.2879846), CachedRevisionInfo(commit_hash='af5ac9dc922b7fbd14f460c2fe94b727db1e1245', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/af5ac9dc922b7fbd14f460c2fe94b727db1e1245'), size_on_disk=92323432, files=frozenset({CachedFileInfo(file_name='reprocess_diffcontrol_5prime.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/af5ac9dc922b7fbd14f460c2fe94b727db1e1245/reprocess_diffcontrol_5prime.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/eba528776f8fe8da63c39ee0660527bc35f989ac6a976daf09270860cad6d735'), size_on_disk=92302997, blob_last_accessed=1763665270.5143101, blob_last_modified=1763578870.280984), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/af5ac9dc922b7fbd14f460c2fe94b727db1e1245/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/8f0cf5e3dd2bb5bdc2f8fde970e31a2404481fec'), size_on_disk=20435, blob_last_accessed=1764298314.9501038, blob_last_modified=1763578711.307058)}), refs=frozenset({'main'}), last_modified=1763578870.280984)}), last_accessed=1764298314.9501038, last_modified=1763578870.280984), CachedRepoInfo(repo_id='BrentLab/hughes_2006', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006'), size_on_disk=11969274, nb_files=11, revisions=frozenset({CachedRevisionInfo(commit_hash='51ae7addf429c955a97934a15b242ff8a2ccd653', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/51ae7addf429c955a97934a15b242ff8a2ccd653'), size_on_disk=5701, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/51ae7addf429c955a97934a15b242ff8a2ccd653/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/f799807a7bf81229b0d063e6c939339c1f5c90f4'), size_on_disk=5701, blob_last_accessed=1756855057.8437808, blob_last_modified=1756855057.8417807)}), refs=frozenset(), last_modified=1756855057.8417807), CachedRevisionInfo(commit_hash='0de73d0932e423cfbbfbc1b21d029c96490dc200', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/0de73d0932e423cfbbfbc1b21d029c96490dc200'), size_on_disk=8791, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/0de73d0932e423cfbbfbc1b21d029c96490dc200/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/76efc047ec3c08e362cfcaac776b7e05c02f8a6b'), size_on_disk=8791, blob_last_accessed=1764712744.7980804, blob_last_modified=1764712744.7960804)}), refs=frozenset(), last_modified=1764712744.7960804), CachedRevisionInfo(commit_hash='1009546961df1cf9743a03383826d8a5e010bb38', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/1009546961df1cf9743a03383826d8a5e010bb38'), size_on_disk=17152, files=frozenset({CachedFileInfo(file_name='metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/1009546961df1cf9743a03383826d8a5e010bb38/metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/54173aa9fff98e5b077e2b24e139ad600a6d7cea74a47b0305cb0fbdf1c14d9b'), size_on_disk=8362, blob_last_accessed=1764713263.750364, blob_last_modified=1764713263.741364), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/1009546961df1cf9743a03383826d8a5e010bb38/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/572115c6e031637a3ac876795a6f91d3653bf5e2'), size_on_disk=8790, blob_last_accessed=1764713262.7733643, blob_last_modified=1764713223.5823674)}), refs=frozenset({'main'}), last_modified=1764713263.741364), CachedRevisionInfo(commit_hash='cc20720c4fe7de9151a051aafbd41e6de2b4cd84', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84'), size_on_disk=11937630, files=frozenset({CachedFileInfo(file_name='knockout.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/knockout.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/5e000e2e9401011c9ecc81655c94d55b7f9469ed856216fa21cef2883a904c93'), size_on_disk=5571518, blob_last_accessed=1756856029.2493467, blob_last_modified=1756856029.2143471), CachedFileInfo(file_name='overexpression.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/overexpression.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/9b8cc1feede780efe0e8537555ed1cbbf067e7fa9aa7a7edc74e5a343c64a443'), size_on_disk=6337781, blob_last_accessed=1756856028.536351, blob_last_modified=1756856029.230347), CachedFileInfo(file_name='metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/e27a67137876123c68c229705cfbb8e06ed3ee76a3a868b66d80d624f8d03671'), size_on_disk=7942, blob_last_accessed=1756856028.533351, blob_last_modified=1756856028.9083488), CachedFileInfo(file_name='checksum.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/checksum.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/973b686313e32c96b6e0ef1dbc84020857c073ef'), size_on_disk=159, blob_last_accessed=1756856028.5883508, blob_last_modified=1756856028.6513503), CachedFileInfo(file_name='parse_hughes_2006.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/scripts/parse_hughes_2006.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/0c2645bdc0ab7f6be38a428a0a92752e9d3f88d5'), size_on_disk=9255, blob_last_accessed=1756856028.5803509, blob_last_modified=1756856028.6393504), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/842b551fb3d1ba1e6f1daa35157a0c7c616f7655'), size_on_disk=8514, blob_last_accessed=1756855276.4871445, blob_last_modified=1756855276.4861445), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756856028.69735, blob_last_modified=1756856028.7793496)}), refs=frozenset(), last_modified=1756856029.230347)}), last_accessed=1764713263.750364, last_modified=1764713263.741364), CachedRepoInfo(repo_id='BrentLab/callingcards', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards'), size_on_disk=49606009, nb_files=137, revisions=frozenset({CachedRevisionInfo(commit_hash='9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0'), size_on_disk=49588983, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6140/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/66c2da17957d7e6a8b159d3573879ac779d0094e35150b51e2643f7ddee36e1f'), size_on_disk=332958, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4236548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=MAG001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/30b63d014686a103b5e7fc6571e902fa9c08630a96557cc12ec9dfcf60871306'), size_on_disk=347333, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.559651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR007B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6f0620b102e7cbfadced7d5530545a185036b53118d3e2b7f074c56403d80f4b'), size_on_disk=263185, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7616518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7295/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/19bd07f913e0af3460e6b4b5091971ceed8f4aa510cf18b82ec36dabb67c68b8'), size_on_disk=272619, blob_last_accessed=1763591393.1151721, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6863/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec534c91cae4413d30477014131d39b51c9c236dfbf150874bd21ec3de821613'), size_on_disk=389025, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648269.6376605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7118/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/be8bd409b83526d5671501de6ef2980177955fdcc3a13d5e153d2593493b2a1e'), size_on_disk=346308, blob_last_accessed=1763591392.9281735, blob_last_modified=1758648270.1336627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7297/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4bd4088cbfcccdf96def916bb8fd9d673327d3e57c2a4a3e57299743ce4aca7'), size_on_disk=261344, blob_last_accessed=1763591393.588169, blob_last_modified=1758648270.6516652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5301_5088/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/65ef775934339b80eda59a141cf791a74e1c6f12513d8143307cebe2f11c3738'), size_on_disk=410106, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.9516528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5617/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bc01241e8363373c995a0fa7439aa1e432a60eb8a7805124fff302311f638361'), size_on_disk=351950, blob_last_accessed=1763591393.6541686, blob_last_modified=1758648268.1026535), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7367/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a7778bb24506047963575fa96126157895c7f329996c729c3e7d6070595955c3'), size_on_disk=324361, blob_last_accessed=1763591392.1631787, blob_last_modified=1758648270.628665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4d098c33564f99ec6aed77d585d1f6e871806f441f099790eef4d6478e322af'), size_on_disk=264459, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7486472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6073/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d68ca6411ba47eb63adad81e88fa1341a0ee269897268957ae936dcc9ba7788'), size_on_disk=450031, blob_last_accessed=1763591393.1341722, blob_last_modified=1758648268.3886547), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6061/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73e03845d0fbff1a5931e4f792e2c1965ade02fc622ffc08e05c267ac6cd3231'), size_on_disk=394930, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.447655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7211/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73d7747698fa1168c89f12c105bbe900d5969d27a82ae2302d0720d761fbfdab'), size_on_disk=298482, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.1276627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6770/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/082f14f7b0d5fc47e088efc40ae85a8ec41f99437c66da96ca69af210bce61cc'), size_on_disk=318118, blob_last_accessed=1763591392.8081744, blob_last_modified=1758648269.3776593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/62e9a627f20607bc4887ebc9277131f974ec80ff39ac386775f4b8f2f5cb9d1d'), size_on_disk=346782, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7476518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/11485fd7bb94d0093ce2508d541fea3ce64f934197d8a5f7bb26ecbdb59f8dbb'), size_on_disk=124642, blob_last_accessed=1763591392.3321776, blob_last_modified=1758648267.0666487), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5961/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/49cac5c2df1adf790a9ac0209db6c0945de50b8191d403e8b1ad386ee2dcb35e'), size_on_disk=321368, blob_last_accessed=1763591392.560176, blob_last_modified=1758648268.215654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6532/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3041f19cf0dcb3e0bd9b1178994351a22f3297ccd43c5e291df8bfe0ad8d5d02'), size_on_disk=516437, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.8236568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6506/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/986d9d3b4ed064d66ebaa0f2502b933f1650a20ac4bbbba804e2cde03b9454c0'), size_on_disk=378407, blob_last_accessed=1763591391.97318, blob_last_modified=1758648268.7656565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5614/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1aa702ff57d34ebed961043e0efae064bfdb62f589cbb515311ae8ea37ba8c76'), size_on_disk=429423, blob_last_accessed=1763591393.4671698, blob_last_modified=1758648268.0406532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6764/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec87eef9e2e8cdf3c3a10ead28dcf1bfa76e94e12a42422a46c8559fb18207c2'), size_on_disk=75684, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.4036593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02313d4d85859a7353e56d89b6e14bcec5160849b5c0089ccbecaa1cda5012cf'), size_on_disk=301213, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4356549), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6635/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6c5b7f1fda3a7f5679870f438d13a45558f7f8da0f479c5144d7751bc8f7fbbc'), size_on_disk=420658, blob_last_accessed=1763591391.95018, blob_last_modified=1758648269.3956594), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ef036874e53ee98dfe1b5d84c42749fc40da9279cd573174321ac83201c1651'), size_on_disk=349762, blob_last_accessed=1763591393.0791724, blob_last_modified=1758648267.4966507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=KB001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38c939d54d3ed77a09650d2ccf00a6ed0e20390b8be722f1c3ed7881a5904a60'), size_on_disk=351677, blob_last_accessed=1763591392.9151735, blob_last_modified=1758648267.5566509), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e5b3174998823af40d055f46ca06e7bb44601cbe97b133062cd3e0491dd30ac2'), size_on_disk=323308, blob_last_accessed=1763591393.153172, blob_last_modified=1758648267.2686496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fc9311b65abb851fbbfd8bb0c0765af59069659f5a715aee546a8644b0af4ec2'), size_on_disk=210948, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648267.6766515), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7283/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0dc91bfacca82c5ed23de7757d59f9f808d541d96dd79aab56f0c4252e7aabf3'), size_on_disk=265146, blob_last_accessed=1763591392.7731745, blob_last_modified=1758648270.401664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6374/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e410602f56bc94d9831a2bc8bdb865ee4ebe9862a65b603165b655fbcc4b9914'), size_on_disk=441531, blob_last_accessed=1763591393.1891718, blob_last_modified=1758648268.5356555), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/33d7303ed077f489ee28021daca0bbc859feb1cfd383dfc9a856ea13994801a9'), size_on_disk=331877, blob_last_accessed=1763591393.292171, blob_last_modified=1758648268.2226539), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6545/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/260c4a280f52024cb12d44559a6189641ee02efbaf20168435196efbfc69d741'), size_on_disk=404812, blob_last_accessed=1763591392.3761773, blob_last_modified=1758648268.8256567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR006B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fcc5e886ec7c6363a5ab51b03d3da189ac3e0cde0a0f79d4f0c46043a6b61838'), size_on_disk=253857, blob_last_accessed=1763591392.0501795, blob_last_modified=1758648267.785652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS011/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ea7cb0559b342dd0ec278e7d5846a5ba0c0d867ea04d545f72e0d1c9472a0cc'), size_on_disk=363979, blob_last_accessed=1763591392.9791732, blob_last_modified=1758648267.0116484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6872/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02fabe6e32f52fe6fca923beb0a6cc2898d991304e23a3e75f3ae8a6bcf361c6'), size_on_disk=274362, blob_last_accessed=1763591393.6231687, blob_last_modified=1758648269.6966608), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a76f0849e518faedfe50e6f0c76908a3f6beed34fd0b607603db1859493595d0'), size_on_disk=292312, blob_last_accessed=1763587658.6168654, blob_last_modified=1758648266.7676473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5399/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b65d0aa7ddc98940f91f2ae5f07e1d3f81dda688792552b91fcc39fb4f9ededa'), size_on_disk=256729, blob_last_accessed=1763591392.5471761, blob_last_modified=1758648267.972653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6437/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/556084dbbc174db5450872d6f5e66cc72091380ea32006977dd176e23c105a7f'), size_on_disk=389840, blob_last_accessed=1763591392.714175, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6567/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0e9713a94594dfcbc80134af52449e12ef207ca9149f5a96c43b6cb457b2431c'), size_on_disk=478809, blob_last_accessed=1763591392.6241755, blob_last_modified=1758648268.893657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7370/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ff4dce26afb34b291220c17625be4a28078f9721a8b000ea177c4add3bf1a039'), size_on_disk=241010, blob_last_accessed=1763591393.7701678, blob_last_modified=1758648270.6726654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5a70fa6f22aeda3246d17646cc4ec7c5a46e1993f9981b19d9fd5a8cc99bf11f'), size_on_disk=308858, blob_last_accessed=1763591391.9411802, blob_last_modified=1758648267.0186484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6177/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b91ffef1dfcdf7952df46956d76a4b364aa0d8342453636cf09e6a343607184f'), size_on_disk=387618, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.424655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6959/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c0cb6c2ec2bda19b0c1da5d5fc2229d0b6d6987d75646fa230c0c14d591cf1f1'), size_on_disk=537588, blob_last_accessed=1763591392.851174, blob_last_modified=1758648269.8686616), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6390/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f324cdf73148c8b0fa23456bc4f51cff3032d08ed42e44dd9fcc4815a6d969c'), size_on_disk=560601, blob_last_accessed=1763591392.7391748, blob_last_modified=1758648268.6206558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7151/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9969e4607d6bf42424f5d2ce8f0d16e3d2d89d069cae6d75997e99c88a68dc77'), size_on_disk=275572, blob_last_accessed=1763591393.7231681, blob_last_modified=1758648270.1476629), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4e8332a5db6260f6357f940e62e3e7b622785f386e81cdb9c75b538d318f4a6'), size_on_disk=158978, blob_last_accessed=1763591393.5471692, blob_last_modified=1758648266.7626472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6421/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1e3bacd3bcf1fcb46cfc5f9ab66c2af57e190f3d006031e7ef37864fbd36aaca'), size_on_disk=495452, blob_last_accessed=1763591392.2161784, blob_last_modified=1758648268.6366558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6708/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/35520976736159652e3be8066e77b1371522f63a8b3564d9b0d9f5b1598a144d'), size_on_disk=253955, blob_last_accessed=1763591393.6881683, blob_last_modified=1758648269.1996584), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6551/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c3729692991cdcafe9baa3d2805b3866f72fe9bc89ba22c6d2cdae4d5b5bbf5f'), size_on_disk=545477, blob_last_accessed=1763591393.0611725, blob_last_modified=1758648268.8376567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1fa09a5a6e6e69198501d994050adfcad7ff39a3cc2fe94e7ea177414e1bc968'), size_on_disk=303532, blob_last_accessed=1763591393.7091682, blob_last_modified=1758648267.2776496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ac8c590c19e7b95b9aac571b1394976039f25fdd66e62a68ecb99925433f8e54'), size_on_disk=355706, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0226483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d3e36418bd46acf9537a8f864b1a1361a062d72fec2b093789b88ed655ec117'), size_on_disk=377393, blob_last_accessed=1763591393.0451727, blob_last_modified=1758648267.0136483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dcb33135a9d854d17a6b80348f6bee8becea9ca3808583a034f89c5e4f460c54'), size_on_disk=218643, blob_last_accessed=1763591393.4831698, blob_last_modified=1758648267.9636528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/00e5602b87ac84670a98c081e6520ca744038f4e9b3a72a78cb61f5926be56b1'), size_on_disk=251087, blob_last_accessed=1763591392.5781758, blob_last_modified=1758648267.8286521), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e91fd764a83947c14f35d7a2d8c0d156c0a61a32a1da96273718ef4be720787'), size_on_disk=361855, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.7196517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6527/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/10081f44b1c2f5ad32489da51fc9ac5b5289b54688a7e78725db247a644b7e4d'), size_on_disk=494640, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.882657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6861/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6955b1a1746fe41162ea1d7a098482bbfdb3d0e411c809b52d698e285627ce03'), size_on_disk=385103, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6146603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6689/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/90ea249116dc6a9be86974e1ec0acf79558dfd56a5666be2e05d2107b8c4ee60'), size_on_disk=366787, blob_last_accessed=1763591393.6661685, blob_last_modified=1758648269.1406581), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6021/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eea0d101dd58c9619b41a94009fc0dff8156d2d36d7f2b88bb653e96123a032e'), size_on_disk=439545, blob_last_accessed=1763591393.5531693, blob_last_modified=1758648268.258654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6920/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/45122fcfb3abc3a43631023712c224dc6e00dba566fb2e15deb4ab4501ad2538'), size_on_disk=446998, blob_last_accessed=1763591392.8881738, blob_last_modified=1758648269.6416605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6448/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/182dc134914a5ddb02992ab6a4f1c12d2450aa331820bf7bd95693dc999bcfae'), size_on_disk=390210, blob_last_accessed=1763591392.9591732, blob_last_modified=1758648268.672656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6954/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/aeed3c55d81a4eb0c568486e476211c599ce2bf37f9dff22c7dfca23f5d80495'), size_on_disk=476054, blob_last_accessed=1763591393.011173, blob_last_modified=1758648269.943662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6853/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ed47ecb2aa870fdbf12920fd4486933262c0a24318c9c8c5531e359a3c416c90'), size_on_disk=515114, blob_last_accessed=1763591393.2661712, blob_last_modified=1758648269.4276595), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1c8a8cb23b1ec073827d6f5343a3bf03a9d7a04cf2ae3fd3ea4cc5ac37673b38'), size_on_disk=342987, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.6226559), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6572/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3488eaac5e4b9b3418b909138e6b50e3f9ebaf3e7345a4a10b6ced18b66152e4'), size_on_disk=440579, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9506574), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6055/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/31bb70b4c90654ecb276664f5c914679322b24985ec66d5b396a99f253ebfa17'), size_on_disk=377015, blob_last_accessed=1763591393.5701692, blob_last_modified=1758648268.3076544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6513/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c8f9c31704c2f8732b4668e1e6410034b8adf2ada39ff55935aa0199e920d055'), size_on_disk=507367, blob_last_accessed=1763591392.1451788, blob_last_modified=1758648268.8406568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6965/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9ae4546d58672f0632757c94b595af8b7c40da215611783fdc1a65000075ec98'), size_on_disk=509677, blob_last_accessed=1763591392.2731779, blob_last_modified=1758648269.933662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6354/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/80b129a46defc7b06e472a35935bfaf410bc5e456475b2885feb2c8a4934a81b'), size_on_disk=173225, blob_last_accessed=1763591393.4981697, blob_last_modified=1758648268.4986553), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bd2017e75947ead2c06f9d1936347f7fdcfac7fbff620e0b39077208a02c3bd9'), size_on_disk=303094, blob_last_accessed=1763591393.1271722, blob_last_modified=1758648269.9296618), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6731/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/23ecd71c4e3c1c547295bb756d4654eb413ff288d10118ab286cd21ccd95b666'), size_on_disk=346187, blob_last_accessed=1763591392.1871786, blob_last_modified=1758648269.1906583), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ac08e026f6d48c034f13a49f3531c90b6c070907'), size_on_disk=9519, blob_last_accessed=1763588875.9720669, blob_last_modified=1763588875.9700668), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/530b5650d91dae3bf169f967af8e800611e3e01daeb1811d6fd21c2612ac4ad9'), size_on_disk=315170, blob_last_accessed=1763591393.6121688, blob_last_modified=1758648266.7956474), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5690/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4831e3e037d421a14efbe1da8c369015ec7d01df351bb27f0ef0400c8b94c693'), size_on_disk=311754, blob_last_accessed=1763591392.9021738, blob_last_modified=1758648268.211654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d4c0cbbf6075a8b5732e3cef2ba154d95d21aa0efc7e49d947cceebd8742b30'), size_on_disk=269007, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4106548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7258/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7b1860ba3be7dbf053203fa5690da2002f7841186940231acea6064e70f0bdc8'), size_on_disk=171331, blob_last_accessed=1763591392.6171756, blob_last_modified=1758648270.3746638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6651/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7644af4cc956ea08809ab6c80c65095e3fdbd36e056040c0eaded3773d546359'), size_on_disk=559951, blob_last_accessed=1763591393.6361687, blob_last_modified=1758648269.1856585), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7155/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/521fc1bea6963640645c21cbdb8d1de07793f11213c23ac71d249f3252b32f54'), size_on_disk=304554, blob_last_accessed=1763591393.5841691, blob_last_modified=1758648270.1366627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6924/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5e9b3a94d73c1720e6b05ea55000054905a8672edea7f950736ce3fe191f8faf'), size_on_disk=412939, blob_last_accessed=1763591393.748168, blob_last_modified=1758648269.7066607), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6923/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/454ca6a52d4b305eddc4b6967ef9604be4861269fdcfff419883daed9b2fe48b'), size_on_disk=337423, blob_last_accessed=1763591393.5031695, blob_last_modified=1758648269.6476605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5801/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/12e91a415cc2a149af4c34c3c8b032821ff301e2828dfb9f4636bd6b2913ea3d'), size_on_disk=360485, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.1636536), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7063/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9427d8f8c54e3c0cc9d720958ef15d3859f00cdfb678559e0268387a36a8ef37'), size_on_disk=404051, blob_last_accessed=1763591392.5301762, blob_last_modified=1758648269.8976617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS006b/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7eedae811a5b3dd4389e7e814d35b022bfaf01fc4d2cfb47be6e6786d6d58c64'), size_on_disk=315912, blob_last_accessed=1763591393.2571712, blob_last_modified=1758648266.7546473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bbe701ffd546a2f4359f821262f7b44232bdc328d9d4719bfdaa8f2aedc6d2e9'), size_on_disk=346079, blob_last_accessed=1763591393.0861723, blob_last_modified=1758648267.589651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5986/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f1179c75ac6e91de03b67a73882f3b9c07c8e435d938d3059de489403adde7a'), size_on_disk=328749, blob_last_accessed=1763591392.691175, blob_last_modified=1758648268.228654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7331/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f159a9811b8ac1b5e252b52427979a3bddce2e7131b13f84046b31e47f7fa399'), size_on_disk=242821, blob_last_accessed=1763591393.2501714, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eb61968b5ec5eadd9087e42ead29333f91195dd5b3f9c5a616b41e94ca23c494'), size_on_disk=330245, blob_last_accessed=1763591393.6761684, blob_last_modified=1758648269.710661), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6808/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c71f5371e8c803cfc7fd507d0b3c11f7548db5d7a0fa42c68977169369ea036e'), size_on_disk=422880, blob_last_accessed=1763591392.9491735, blob_last_modified=1758648269.3916593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ab338795b8bbf334650386a123f6e0df41363cb5dcbb778d485ce58804e96b29'), size_on_disk=375506, blob_last_accessed=1763591392.9711733, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7237/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/110a743162ace7792a44dfe48d7afd40bb67c45c722038c981548477ebc5d8fe'), size_on_disk=313207, blob_last_accessed=1763591393.143172, blob_last_modified=1758648270.1426628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7137/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/703fe9f8552adcbdfb5e640509cb62df2fa734b43fe1e98fc1fd342415f823d7'), size_on_disk=515398, blob_last_accessed=1763589053.7698598, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f427ea0f4ded3b6ff841153d8ad87d72a26a964c189246fb6fd5c439bd8b0c1e'), size_on_disk=353623, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.812652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7115/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/205443fd87ce5c793dab86eb75c21dab7dc2ae2edac41d7e6314cfe491bdb01d'), size_on_disk=321850, blob_last_accessed=1763591393.2331715, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6855/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48e48378d24404e926bc1c44b10af60d4b12f82124ff2f80bc0b69b4a0f98740'), size_on_disk=609248, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6556606), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6798/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/899cde55032cd593be0013e1f6ff33fe9a12f4616477b1410d86fae1b2008a09'), size_on_disk=342376, blob_last_accessed=1763591392.7951744, blob_last_modified=1758648269.4006593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6677/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4ee6e1d3e2b9a9bbcc8851048d2b16f4aea6a1e2f752afb9e0bf75a56adf7042'), size_on_disk=322147, blob_last_accessed=1763591392.393177, blob_last_modified=1758648269.0826578), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7243/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cb59e645fff2683db692b4d2f514602ba210271b632cd113853febe941c6485f'), size_on_disk=281696, blob_last_accessed=1763591392.701175, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7269/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5bae329d2318610f627fc562a5163253c7d61e0ef83c53808b220133b2299264'), size_on_disk=203263, blob_last_accessed=1763591392.541176, blob_last_modified=1758648270.4096642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e20f616f6e340b76f6446d2cdffb6cc9feb81297bd0307733f835acf4142cfd'), size_on_disk=269523, blob_last_accessed=1763591392.3461773, blob_last_modified=1758648267.2696495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6443/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4deb4634e8b4bf141a2410e94b2d99e2b31a0e8f037de9a1c6e14696025bffae'), size_on_disk=494775, blob_last_accessed=1763591392.991173, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6640/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48339729112d22aa5aabf411674bfb90a567ec4c7a07ea7930738645a96823ef'), size_on_disk=329042, blob_last_accessed=1763591392.6391754, blob_last_modified=1758648269.090658), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6573/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e435989051765cf2955d8cd733904e8b60e5f3121e1c2fdc987f4e6f71fe3970'), size_on_disk=265022, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9586573), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/d1cd38451bc41ef14dbc1566c4704b004b2c0661f667210c81dba09bec90540f'), size_on_disk=244487, blob_last_accessed=1763591393.6301687, blob_last_modified=1758648267.3246498), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/74cbce50c258bdddb7e42c8fd5b0b2a9da74fe3cf89a9ab5a53ee4fde7c1dee6'), size_on_disk=186142, blob_last_accessed=1763591392.842174, blob_last_modified=1758648266.9966483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7351/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cc708adf186f28db09dfdd4ac43865a90482b2192306949eab01492598cf23e7'), size_on_disk=198070, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.610665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7185/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/14f67668aca8d8f8aa2b8af98835da683a7fc3f22aa09cdcd7f943a37632391e'), size_on_disk=201650, blob_last_accessed=1763591393.2191715, blob_last_modified=1758648270.1076627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7256/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1af21cbdd7f6f6effff15a1b061a48f85132d582f08c8fd42e04ea58bde7ec00'), size_on_disk=206710, blob_last_accessed=1763591392.586176, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5610/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a86512ae1000a994c86e9aebaf30cf9ce1a59160155f3dc1afa6cbde0a1fc47e'), size_on_disk=307382, blob_last_accessed=1763591393.5291693, blob_last_modified=1758648267.994653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6739/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/58b5ed63f2197f406dd3df6a569975d776722193007912718979eaed4d4e5f9c'), size_on_disk=399160, blob_last_accessed=1763591392.6551754, blob_last_modified=1758648269.331659), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/128015caa722289251a45aa3d56cabac6b5946eb4e1be16eb950555fb24ac413'), size_on_disk=172328, blob_last_accessed=1763591393.735168, blob_last_modified=1758648267.2826495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9f7663ccf72b0681ae5866c13500fbde98f35877fd677ac0d49c17447d17d6ce'), size_on_disk=332767, blob_last_accessed=1763591392.1731787, blob_last_modified=1758648266.744647), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5654/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38961219eb66dec31505ebcc5088f16684be9891b96fffa8c9a13cd3f13254d0'), size_on_disk=447782, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.1876538), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6738/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e6d612d16538a06c985f1134f3fdfd367944b2a85cedfb8b0ddad1679004c940'), size_on_disk=333560, blob_last_accessed=1763591392.8651738, blob_last_modified=1758648269.3676593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6b92129c2fb99028b6cab9509cf1a4c40b142109cde34c9a130d375001dcd211'), size_on_disk=267273, blob_last_accessed=1763591392.7811744, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6454/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c35b60861d2fc7a3f4256869e14e629aed07cd2ccce76608578ceb8c6ec423e1'), size_on_disk=498326, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.7226562), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d50648ed5c9f593f830aff661d13b3d34bd14f0bf93b16169df4765c9f47fc8'), size_on_disk=295924, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7426472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7266/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9aea2d6f5606d24e1999d450379b1f8c8b9d80eded9e784fb729faa516a27026'), size_on_disk=324480, blob_last_accessed=1763591392.0401795, blob_last_modified=1758648270.379664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=MAG002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f7f35257d4c1f7fa5812ea31c1de791e08ab5e3a4e7ca42b2bb12b2c64f7ed7'), size_on_disk=490174, blob_last_accessed=1763591392.829174, blob_last_modified=1758648267.583651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dc74862f02ff12b8c906046f1f03953d33f5516091d58397b0f9a0fc05bb4b5c'), size_on_disk=315435, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.5006506), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7276/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4e0d576cc4a6a85be64f2198e5001a9f5b02b506072df6f695cfdfc719cb041'), size_on_disk=204036, blob_last_accessed=1763591392.987173, blob_last_modified=1758648270.4646642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=composite/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1f8adf8fb4992283714df8587126211d54e6a0580b8be88c1d5e911196aed783'), size_on_disk=4814573, blob_last_accessed=1763591392.0561793, blob_last_modified=1758648268.0256531), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7cb5cca74f1bda2f8f1c8ef962e54d34228361eaa9dd8b21e0d1dd7092ff8164'), size_on_disk=229703, blob_last_accessed=1763591392.555176, blob_last_modified=1758648267.0126483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6657/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/95ad2225152f51e2e31c030ff6708aeebccee5e548035d6f43c9cc3554bdfe5e'), size_on_disk=507730, blob_last_accessed=1763591393.163172, blob_last_modified=1758648269.1676583), CachedFileInfo(file_name='annotated_features_meta.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features_meta.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/2be4b693d333698f7ada7ef40ffaf927c0160d7975ebcf6e215cbc4ba1be1604'), size_on_disk=11813, blob_last_accessed=1763587594.724117, blob_last_modified=1758648264.795638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4b2ff192aa4808cb50b1c1b1667060f98505dd63c1aa58c7d8354e41194957e4'), size_on_disk=289767, blob_last_accessed=1763591393.4871697, blob_last_modified=1758648266.7706473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ddd58117dbedfe7e9dc3fd946187e7bd5d88fe8b0f38999c6e4b5660938bb787'), size_on_disk=359739, blob_last_accessed=1763591393.5981688, blob_last_modified=1758648267.2726495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6983/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4ee29f84ab279c520521a6c5a5eed9e5e5581b2e39378bdd898eec67d13654f'), size_on_disk=410123, blob_last_accessed=1763591392.6751752, blob_last_modified=1758648269.8636615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/646c843611bf88c7f5dcd5b1a664d724c3b24b6434cc52d9a54de62ccaf6c542'), size_on_disk=255949, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.4506505), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fa1a44bd33df22ec6b5a8baf3be17e51a11522b5fdd545a38e6e163cc5ae8207'), size_on_disk=241911, blob_last_accessed=1763591392.572176, blob_last_modified=1758648267.5226507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/71a33d11c3ff714ce0730145c740eec4fe88812c6136b8d10b86fa8d4054991f'), size_on_disk=186500, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7286518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7332/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f7f62ee008fa2b5cc688cbd80c888f1bf9da538caa245b63581520d485338a99'), size_on_disk=309595, blob_last_accessed=1763591393.1021724, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c9b19726dc45896e4f86a5acc52d07cc422f895bfaaaddb332aaceedff36560c'), size_on_disk=178793, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0266485), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0230df4f1d3748337e5315bb1d22a0373fdab95f9bc900041b0e34966d868718'), size_on_disk=311116, blob_last_accessed=1763591392.9241736, blob_last_modified=1758648267.2506495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7240/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4460d188b4cd3b362f33acf0f0b5312a267b37f8fb2dab4b2577c6c3bbb1231'), size_on_disk=307039, blob_last_accessed=1763591392.6101756, blob_last_modified=1758648270.403664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e871cf06f596bfe74896b62aa28d6b1e6f2f0b8408630fb146c7e29cfd88fbf0'), size_on_disk=232721, blob_last_accessed=1763591392.7871745, blob_last_modified=1758648267.991653)}), refs=frozenset(), last_modified=1763588875.9700668), CachedRevisionInfo(commit_hash='27becd0ab489579a50c38ba896dd03680c5fd2da', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da'), size_on_disk=49585603, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ab338795b8bbf334650386a123f6e0df41363cb5dcbb778d485ce58804e96b29'), size_on_disk=375506, blob_last_accessed=1763591392.9711733, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6965/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9ae4546d58672f0632757c94b595af8b7c40da215611783fdc1a65000075ec98'), size_on_disk=509677, blob_last_accessed=1763591392.2731779, blob_last_modified=1758648269.933662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e871cf06f596bfe74896b62aa28d6b1e6f2f0b8408630fb146c7e29cfd88fbf0'), size_on_disk=232721, blob_last_accessed=1763591392.7871745, blob_last_modified=1758648267.991653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7331/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f159a9811b8ac1b5e252b52427979a3bddce2e7131b13f84046b31e47f7fa399'), size_on_disk=242821, blob_last_accessed=1763591393.2501714, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6532/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3041f19cf0dcb3e0bd9b1178994351a22f3297ccd43c5e291df8bfe0ad8d5d02'), size_on_disk=516437, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.8236568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6177/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b91ffef1dfcdf7952df46956d76a4b364aa0d8342453636cf09e6a343607184f'), size_on_disk=387618, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.424655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6390/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f324cdf73148c8b0fa23456bc4f51cff3032d08ed42e44dd9fcc4815a6d969c'), size_on_disk=560601, blob_last_accessed=1763591392.7391748, blob_last_modified=1758648268.6206558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6954/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/aeed3c55d81a4eb0c568486e476211c599ce2bf37f9dff22c7dfca23f5d80495'), size_on_disk=476054, blob_last_accessed=1763591393.011173, blob_last_modified=1758648269.943662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ef036874e53ee98dfe1b5d84c42749fc40da9279cd573174321ac83201c1651'), size_on_disk=349762, blob_last_accessed=1763591393.0791724, blob_last_modified=1758648267.4966507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7258/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7b1860ba3be7dbf053203fa5690da2002f7841186940231acea6064e70f0bdc8'), size_on_disk=171331, blob_last_accessed=1763591392.6171756, blob_last_modified=1758648270.3746638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS011/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ea7cb0559b342dd0ec278e7d5846a5ba0c0d867ea04d545f72e0d1c9472a0cc'), size_on_disk=363979, blob_last_accessed=1763591392.9791732, blob_last_modified=1758648267.0116484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6513/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c8f9c31704c2f8732b4668e1e6410034b8adf2ada39ff55935aa0199e920d055'), size_on_disk=507367, blob_last_accessed=1763591392.1451788, blob_last_modified=1758648268.8406568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6506/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/986d9d3b4ed064d66ebaa0f2502b933f1650a20ac4bbbba804e2cde03b9454c0'), size_on_disk=378407, blob_last_accessed=1763591391.97318, blob_last_modified=1758648268.7656565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6573/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e435989051765cf2955d8cd733904e8b60e5f3121e1c2fdc987f4e6f71fe3970'), size_on_disk=265022, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9586573), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6454/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c35b60861d2fc7a3f4256869e14e629aed07cd2ccce76608578ceb8c6ec423e1'), size_on_disk=498326, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.7226562), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6443/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4deb4634e8b4bf141a2410e94b2d99e2b31a0e8f037de9a1c6e14696025bffae'), size_on_disk=494775, blob_last_accessed=1763591392.991173, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7240/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4460d188b4cd3b362f33acf0f0b5312a267b37f8fb2dab4b2577c6c3bbb1231'), size_on_disk=307039, blob_last_accessed=1763591392.6101756, blob_last_modified=1758648270.403664), CachedFileInfo(file_name='annotated_features_meta.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features_meta.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/2be4b693d333698f7ada7ef40ffaf927c0160d7975ebcf6e215cbc4ba1be1604'), size_on_disk=11813, blob_last_accessed=1763587594.724117, blob_last_modified=1758648264.795638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6421/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1e3bacd3bcf1fcb46cfc5f9ab66c2af57e190f3d006031e7ef37864fbd36aaca'), size_on_disk=495452, blob_last_accessed=1763591392.2161784, blob_last_modified=1758648268.6366558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7118/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/be8bd409b83526d5671501de6ef2980177955fdcc3a13d5e153d2593493b2a1e'), size_on_disk=346308, blob_last_accessed=1763591392.9281735, blob_last_modified=1758648270.1336627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4d098c33564f99ec6aed77d585d1f6e871806f441f099790eef4d6478e322af'), size_on_disk=264459, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7486472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6055/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/31bb70b4c90654ecb276664f5c914679322b24985ec66d5b396a99f253ebfa17'), size_on_disk=377015, blob_last_accessed=1763591393.5701692, blob_last_modified=1758648268.3076544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5690/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4831e3e037d421a14efbe1da8c369015ec7d01df351bb27f0ef0400c8b94c693'), size_on_disk=311754, blob_last_accessed=1763591392.9021738, blob_last_modified=1758648268.211654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6731/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/23ecd71c4e3c1c547295bb756d4654eb413ff288d10118ab286cd21ccd95b666'), size_on_disk=346187, blob_last_accessed=1763591392.1871786, blob_last_modified=1758648269.1906583), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7211/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73d7747698fa1168c89f12c105bbe900d5969d27a82ae2302d0720d761fbfdab'), size_on_disk=298482, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.1276627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6708/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/35520976736159652e3be8066e77b1371522f63a8b3564d9b0d9f5b1598a144d'), size_on_disk=253955, blob_last_accessed=1763591393.6881683, blob_last_modified=1758648269.1996584), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6b92129c2fb99028b6cab9509cf1a4c40b142109cde34c9a130d375001dcd211'), size_on_disk=267273, blob_last_accessed=1763591392.7811744, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6551/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c3729692991cdcafe9baa3d2805b3866f72fe9bc89ba22c6d2cdae4d5b5bbf5f'), size_on_disk=545477, blob_last_accessed=1763591393.0611725, blob_last_modified=1758648268.8376567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5617/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bc01241e8363373c995a0fa7439aa1e432a60eb8a7805124fff302311f638361'), size_on_disk=351950, blob_last_accessed=1763591393.6541686, blob_last_modified=1758648268.1026535), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/33d7303ed077f489ee28021daca0bbc859feb1cfd383dfc9a856ea13994801a9'), size_on_disk=331877, blob_last_accessed=1763591393.292171, blob_last_modified=1758648268.2226539), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6657/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/95ad2225152f51e2e31c030ff6708aeebccee5e548035d6f43c9cc3554bdfe5e'), size_on_disk=507730, blob_last_accessed=1763591393.163172, blob_last_modified=1758648269.1676583), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dc74862f02ff12b8c906046f1f03953d33f5516091d58397b0f9a0fc05bb4b5c'), size_on_disk=315435, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.5006506), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/00e5602b87ac84670a98c081e6520ca744038f4e9b3a72a78cb61f5926be56b1'), size_on_disk=251087, blob_last_accessed=1763591392.5781758, blob_last_modified=1758648267.8286521), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e91fd764a83947c14f35d7a2d8c0d156c0a61a32a1da96273718ef4be720787'), size_on_disk=361855, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.7196517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7063/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9427d8f8c54e3c0cc9d720958ef15d3859f00cdfb678559e0268387a36a8ef37'), size_on_disk=404051, blob_last_accessed=1763591392.5301762, blob_last_modified=1758648269.8976617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/11485fd7bb94d0093ce2508d541fea3ce64f934197d8a5f7bb26ecbdb59f8dbb'), size_on_disk=124642, blob_last_accessed=1763591392.3321776, blob_last_modified=1758648267.0666487), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c9b19726dc45896e4f86a5acc52d07cc422f895bfaaaddb332aaceedff36560c'), size_on_disk=178793, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0266485), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9f7663ccf72b0681ae5866c13500fbde98f35877fd677ac0d49c17447d17d6ce'), size_on_disk=332767, blob_last_accessed=1763591392.1731787, blob_last_modified=1758648266.744647), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6923/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/454ca6a52d4b305eddc4b6967ef9604be4861269fdcfff419883daed9b2fe48b'), size_on_disk=337423, blob_last_accessed=1763591393.5031695, blob_last_modified=1758648269.6476605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02313d4d85859a7353e56d89b6e14bcec5160849b5c0089ccbecaa1cda5012cf'), size_on_disk=301213, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4356549), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6527/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/10081f44b1c2f5ad32489da51fc9ac5b5289b54688a7e78725db247a644b7e4d'), size_on_disk=494640, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.882657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7269/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5bae329d2318610f627fc562a5163253c7d61e0ef83c53808b220133b2299264'), size_on_disk=203263, blob_last_accessed=1763591392.541176, blob_last_modified=1758648270.4096642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1c8a8cb23b1ec073827d6f5343a3bf03a9d7a04cf2ae3fd3ea4cc5ac37673b38'), size_on_disk=342987, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.6226559), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6738/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e6d612d16538a06c985f1134f3fdfd367944b2a85cedfb8b0ddad1679004c940'), size_on_disk=333560, blob_last_accessed=1763591392.8651738, blob_last_modified=1758648269.3676593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7370/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ff4dce26afb34b291220c17625be4a28078f9721a8b000ea177c4add3bf1a039'), size_on_disk=241010, blob_last_accessed=1763591393.7701678, blob_last_modified=1758648270.6726654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d3e36418bd46acf9537a8f864b1a1361a062d72fec2b093789b88ed655ec117'), size_on_disk=377393, blob_last_accessed=1763591393.0451727, blob_last_modified=1758648267.0136483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dcb33135a9d854d17a6b80348f6bee8becea9ca3808583a034f89c5e4f460c54'), size_on_disk=218643, blob_last_accessed=1763591393.4831698, blob_last_modified=1758648267.9636528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ddd58117dbedfe7e9dc3fd946187e7bd5d88fe8b0f38999c6e4b5660938bb787'), size_on_disk=359739, blob_last_accessed=1763591393.5981688, blob_last_modified=1758648267.2726495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6448/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/182dc134914a5ddb02992ab6a4f1c12d2450aa331820bf7bd95693dc999bcfae'), size_on_disk=390210, blob_last_accessed=1763591392.9591732, blob_last_modified=1758648268.672656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6739/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/58b5ed63f2197f406dd3df6a569975d776722193007912718979eaed4d4e5f9c'), size_on_disk=399160, blob_last_accessed=1763591392.6551754, blob_last_modified=1758648269.331659), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=KB001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38c939d54d3ed77a09650d2ccf00a6ed0e20390b8be722f1c3ed7881a5904a60'), size_on_disk=351677, blob_last_accessed=1763591392.9151735, blob_last_modified=1758648267.5566509), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5801/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/12e91a415cc2a149af4c34c3c8b032821ff301e2828dfb9f4636bd6b2913ea3d'), size_on_disk=360485, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.1636536), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR006B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fcc5e886ec7c6363a5ab51b03d3da189ac3e0cde0a0f79d4f0c46043a6b61838'), size_on_disk=253857, blob_last_accessed=1763591392.0501795, blob_last_modified=1758648267.785652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6677/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4ee6e1d3e2b9a9bbcc8851048d2b16f4aea6a1e2f752afb9e0bf75a56adf7042'), size_on_disk=322147, blob_last_accessed=1763591392.393177, blob_last_modified=1758648269.0826578), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6140/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/66c2da17957d7e6a8b159d3573879ac779d0094e35150b51e2643f7ddee36e1f'), size_on_disk=332958, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4236548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7237/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/110a743162ace7792a44dfe48d7afd40bb67c45c722038c981548477ebc5d8fe'), size_on_disk=313207, blob_last_accessed=1763591393.143172, blob_last_modified=1758648270.1426628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6798/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/899cde55032cd593be0013e1f6ff33fe9a12f4616477b1410d86fae1b2008a09'), size_on_disk=342376, blob_last_accessed=1763591392.7951744, blob_last_modified=1758648269.4006593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6861/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6955b1a1746fe41162ea1d7a098482bbfdb3d0e411c809b52d698e285627ce03'), size_on_disk=385103, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6146603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6920/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/45122fcfb3abc3a43631023712c224dc6e00dba566fb2e15deb4ab4501ad2538'), size_on_disk=446998, blob_last_accessed=1763591392.8881738, blob_last_modified=1758648269.6416605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/74cbce50c258bdddb7e42c8fd5b0b2a9da74fe3cf89a9ab5a53ee4fde7c1dee6'), size_on_disk=186142, blob_last_accessed=1763591392.842174, blob_last_modified=1758648266.9966483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/128015caa722289251a45aa3d56cabac6b5946eb4e1be16eb950555fb24ac413'), size_on_disk=172328, blob_last_accessed=1763591393.735168, blob_last_modified=1758648267.2826495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0230df4f1d3748337e5315bb1d22a0373fdab95f9bc900041b0e34966d868718'), size_on_disk=311116, blob_last_accessed=1763591392.9241736, blob_last_modified=1758648267.2506495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6635/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6c5b7f1fda3a7f5679870f438d13a45558f7f8da0f479c5144d7751bc8f7fbbc'), size_on_disk=420658, blob_last_accessed=1763591391.95018, blob_last_modified=1758648269.3956594), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6073/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d68ca6411ba47eb63adad81e88fa1341a0ee269897268957ae936dcc9ba7788'), size_on_disk=450031, blob_last_accessed=1763591393.1341722, blob_last_modified=1758648268.3886547), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7332/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f7f62ee008fa2b5cc688cbd80c888f1bf9da538caa245b63581520d485338a99'), size_on_disk=309595, blob_last_accessed=1763591393.1021724, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS006b/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7eedae811a5b3dd4389e7e814d35b022bfaf01fc4d2cfb47be6e6786d6d58c64'), size_on_disk=315912, blob_last_accessed=1763591393.2571712, blob_last_modified=1758648266.7546473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6651/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7644af4cc956ea08809ab6c80c65095e3fdbd36e056040c0eaded3773d546359'), size_on_disk=559951, blob_last_accessed=1763591393.6361687, blob_last_modified=1758648269.1856585), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4e8332a5db6260f6357f940e62e3e7b622785f386e81cdb9c75b538d318f4a6'), size_on_disk=158978, blob_last_accessed=1763591393.5471692, blob_last_modified=1758648266.7626472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6764/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec87eef9e2e8cdf3c3a10ead28dcf1bfa76e94e12a42422a46c8559fb18207c2'), size_on_disk=75684, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.4036593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7295/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/19bd07f913e0af3460e6b4b5091971ceed8f4aa510cf18b82ec36dabb67c68b8'), size_on_disk=272619, blob_last_accessed=1763591393.1151721, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6872/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02fabe6e32f52fe6fca923beb0a6cc2898d991304e23a3e75f3ae8a6bcf361c6'), size_on_disk=274362, blob_last_accessed=1763591393.6231687, blob_last_modified=1758648269.6966608), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=MAG001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/30b63d014686a103b5e7fc6571e902fa9c08630a96557cc12ec9dfcf60871306'), size_on_disk=347333, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.559651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d50648ed5c9f593f830aff661d13b3d34bd14f0bf93b16169df4765c9f47fc8'), size_on_disk=295924, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7426472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6863/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec534c91cae4413d30477014131d39b51c9c236dfbf150874bd21ec3de821613'), size_on_disk=389025, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648269.6376605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR007B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6f0620b102e7cbfadced7d5530545a185036b53118d3e2b7f074c56403d80f4b'), size_on_disk=263185, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7616518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bd2017e75947ead2c06f9d1936347f7fdcfac7fbff620e0b39077208a02c3bd9'), size_on_disk=303094, blob_last_accessed=1763591393.1271722, blob_last_modified=1758648269.9296618), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/530b5650d91dae3bf169f967af8e800611e3e01daeb1811d6fd21c2612ac4ad9'), size_on_disk=315170, blob_last_accessed=1763591393.6121688, blob_last_modified=1758648266.7956474), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bbe701ffd546a2f4359f821262f7b44232bdc328d9d4719bfdaa8f2aedc6d2e9'), size_on_disk=346079, blob_last_accessed=1763591393.0861723, blob_last_modified=1758648267.589651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6640/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48339729112d22aa5aabf411674bfb90a567ec4c7a07ea7930738645a96823ef'), size_on_disk=329042, blob_last_accessed=1763591392.6391754, blob_last_modified=1758648269.090658), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5654/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38961219eb66dec31505ebcc5088f16684be9891b96fffa8c9a13cd3f13254d0'), size_on_disk=447782, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.1876538), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=composite/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1f8adf8fb4992283714df8587126211d54e6a0580b8be88c1d5e911196aed783'), size_on_disk=4814573, blob_last_accessed=1763591392.0561793, blob_last_modified=1758648268.0256531), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/71a33d11c3ff714ce0730145c740eec4fe88812c6136b8d10b86fa8d4054991f'), size_on_disk=186500, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7286518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7243/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cb59e645fff2683db692b4d2f514602ba210271b632cd113853febe941c6485f'), size_on_disk=281696, blob_last_accessed=1763591392.701175, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7185/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/14f67668aca8d8f8aa2b8af98835da683a7fc3f22aa09cdcd7f943a37632391e'), size_on_disk=201650, blob_last_accessed=1763591393.2191715, blob_last_modified=1758648270.1076627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7276/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4e0d576cc4a6a85be64f2198e5001a9f5b02b506072df6f695cfdfc719cb041'), size_on_disk=204036, blob_last_accessed=1763591392.987173, blob_last_modified=1758648270.4646642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7151/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9969e4607d6bf42424f5d2ce8f0d16e3d2d89d069cae6d75997e99c88a68dc77'), size_on_disk=275572, blob_last_accessed=1763591393.7231681, blob_last_modified=1758648270.1476629), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7266/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9aea2d6f5606d24e1999d450379b1f8c8b9d80eded9e784fb729faa516a27026'), size_on_disk=324480, blob_last_accessed=1763591392.0401795, blob_last_modified=1758648270.379664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5399/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b65d0aa7ddc98940f91f2ae5f07e1d3f81dda688792552b91fcc39fb4f9ededa'), size_on_disk=256729, blob_last_accessed=1763591392.5471761, blob_last_modified=1758648267.972653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6354/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/80b129a46defc7b06e472a35935bfaf410bc5e456475b2885feb2c8a4934a81b'), size_on_disk=173225, blob_last_accessed=1763591393.4981697, blob_last_modified=1758648268.4986553), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/d1cd38451bc41ef14dbc1566c4704b004b2c0661f667210c81dba09bec90540f'), size_on_disk=244487, blob_last_accessed=1763591393.6301687, blob_last_modified=1758648267.3246498), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5961/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/49cac5c2df1adf790a9ac0209db6c0945de50b8191d403e8b1ad386ee2dcb35e'), size_on_disk=321368, blob_last_accessed=1763591392.560176, blob_last_modified=1758648268.215654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5986/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f1179c75ac6e91de03b67a73882f3b9c07c8e435d938d3059de489403adde7a'), size_on_disk=328749, blob_last_accessed=1763591392.691175, blob_last_modified=1758648268.228654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7297/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4bd4088cbfcccdf96def916bb8fd9d673327d3e57c2a4a3e57299743ce4aca7'), size_on_disk=261344, blob_last_accessed=1763591393.588169, blob_last_modified=1758648270.6516652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/62e9a627f20607bc4887ebc9277131f974ec80ff39ac386775f4b8f2f5cb9d1d'), size_on_disk=346782, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7476518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7256/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1af21cbdd7f6f6effff15a1b061a48f85132d582f08c8fd42e04ea58bde7ec00'), size_on_disk=206710, blob_last_accessed=1763591392.586176, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7115/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/205443fd87ce5c793dab86eb75c21dab7dc2ae2edac41d7e6314cfe491bdb01d'), size_on_disk=321850, blob_last_accessed=1763591393.2331715, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7cb5cca74f1bda2f8f1c8ef962e54d34228361eaa9dd8b21e0d1dd7092ff8164'), size_on_disk=229703, blob_last_accessed=1763591392.555176, blob_last_modified=1758648267.0126483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5610/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a86512ae1000a994c86e9aebaf30cf9ce1a59160155f3dc1afa6cbde0a1fc47e'), size_on_disk=307382, blob_last_accessed=1763591393.5291693, blob_last_modified=1758648267.994653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1fa09a5a6e6e69198501d994050adfcad7ff39a3cc2fe94e7ea177414e1bc968'), size_on_disk=303532, blob_last_accessed=1763591393.7091682, blob_last_modified=1758648267.2776496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5614/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1aa702ff57d34ebed961043e0efae064bfdb62f589cbb515311ae8ea37ba8c76'), size_on_disk=429423, blob_last_accessed=1763591393.4671698, blob_last_modified=1758648268.0406532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=MAG002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f7f35257d4c1f7fa5812ea31c1de791e08ab5e3a4e7ca42b2bb12b2c64f7ed7'), size_on_disk=490174, blob_last_accessed=1763591392.829174, blob_last_modified=1758648267.583651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fa1a44bd33df22ec6b5a8baf3be17e51a11522b5fdd545a38e6e163cc5ae8207'), size_on_disk=241911, blob_last_accessed=1763591392.572176, blob_last_modified=1758648267.5226507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7137/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/703fe9f8552adcbdfb5e640509cb62df2fa734b43fe1e98fc1fd342415f823d7'), size_on_disk=515398, blob_last_accessed=1763589053.7698598, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6924/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5e9b3a94d73c1720e6b05ea55000054905a8672edea7f950736ce3fe191f8faf'), size_on_disk=412939, blob_last_accessed=1763591393.748168, blob_last_modified=1758648269.7066607), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6061/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73e03845d0fbff1a5931e4f792e2c1965ade02fc622ffc08e05c267ac6cd3231'), size_on_disk=394930, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.447655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6689/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/90ea249116dc6a9be86974e1ec0acf79558dfd56a5666be2e05d2107b8c4ee60'), size_on_disk=366787, blob_last_accessed=1763591393.6661685, blob_last_modified=1758648269.1406581), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6021/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eea0d101dd58c9619b41a94009fc0dff8156d2d36d7f2b88bb653e96123a032e'), size_on_disk=439545, blob_last_accessed=1763591393.5531693, blob_last_modified=1758648268.258654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6545/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/260c4a280f52024cb12d44559a6189641ee02efbaf20168435196efbfc69d741'), size_on_disk=404812, blob_last_accessed=1763591392.3761773, blob_last_modified=1758648268.8256567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e5b3174998823af40d055f46ca06e7bb44601cbe97b133062cd3e0491dd30ac2'), size_on_disk=323308, blob_last_accessed=1763591393.153172, blob_last_modified=1758648267.2686496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6567/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0e9713a94594dfcbc80134af52449e12ef207ca9149f5a96c43b6cb457b2431c'), size_on_disk=478809, blob_last_accessed=1763591392.6241755, blob_last_modified=1758648268.893657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ac8c590c19e7b95b9aac571b1394976039f25fdd66e62a68ecb99925433f8e54'), size_on_disk=355706, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0226483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f427ea0f4ded3b6ff841153d8ad87d72a26a964c189246fb6fd5c439bd8b0c1e'), size_on_disk=353623, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.812652), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a88564c359f73b5ac3f70a4635f33a1ec57f527f'), size_on_disk=6139, blob_last_accessed=1763587733.4135532, blob_last_modified=1758648264.1986353), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6853/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ed47ecb2aa870fdbf12920fd4486933262c0a24318c9c8c5531e359a3c416c90'), size_on_disk=515114, blob_last_accessed=1763591393.2661712, blob_last_modified=1758648269.4276595), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d4c0cbbf6075a8b5732e3cef2ba154d95d21aa0efc7e49d947cceebd8742b30'), size_on_disk=269007, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4106548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6437/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/556084dbbc174db5450872d6f5e66cc72091380ea32006977dd176e23c105a7f'), size_on_disk=389840, blob_last_accessed=1763591392.714175, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7155/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/521fc1bea6963640645c21cbdb8d1de07793f11213c23ac71d249f3252b32f54'), size_on_disk=304554, blob_last_accessed=1763591393.5841691, blob_last_modified=1758648270.1366627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4b2ff192aa4808cb50b1c1b1667060f98505dd63c1aa58c7d8354e41194957e4'), size_on_disk=289767, blob_last_accessed=1763591393.4871697, blob_last_modified=1758648266.7706473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fc9311b65abb851fbbfd8bb0c0765af59069659f5a715aee546a8644b0af4ec2'), size_on_disk=210948, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648267.6766515), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a76f0849e518faedfe50e6f0c76908a3f6beed34fd0b607603db1859493595d0'), size_on_disk=292312, blob_last_accessed=1763587658.6168654, blob_last_modified=1758648266.7676473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7351/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cc708adf186f28db09dfdd4ac43865a90482b2192306949eab01492598cf23e7'), size_on_disk=198070, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.610665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6770/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/082f14f7b0d5fc47e088efc40ae85a8ec41f99437c66da96ca69af210bce61cc'), size_on_disk=318118, blob_last_accessed=1763591392.8081744, blob_last_modified=1758648269.3776593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6983/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4ee29f84ab279c520521a6c5a5eed9e5e5581b2e39378bdd898eec67d13654f'), size_on_disk=410123, blob_last_accessed=1763591392.6751752, blob_last_modified=1758648269.8636615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6959/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c0cb6c2ec2bda19b0c1da5d5fc2229d0b6d6987d75646fa230c0c14d591cf1f1'), size_on_disk=537588, blob_last_accessed=1763591392.851174, blob_last_modified=1758648269.8686616), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6808/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c71f5371e8c803cfc7fd507d0b3c11f7548db5d7a0fa42c68977169369ea036e'), size_on_disk=422880, blob_last_accessed=1763591392.9491735, blob_last_modified=1758648269.3916593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/646c843611bf88c7f5dcd5b1a664d724c3b24b6434cc52d9a54de62ccaf6c542'), size_on_disk=255949, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.4506505), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5301_5088/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/65ef775934339b80eda59a141cf791a74e1c6f12513d8143307cebe2f11c3738'), size_on_disk=410106, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.9516528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e20f616f6e340b76f6446d2cdffb6cc9feb81297bd0307733f835acf4142cfd'), size_on_disk=269523, blob_last_accessed=1763591392.3461773, blob_last_modified=1758648267.2696495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6572/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3488eaac5e4b9b3418b909138e6b50e3f9ebaf3e7345a4a10b6ced18b66152e4'), size_on_disk=440579, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9506574), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eb61968b5ec5eadd9087e42ead29333f91195dd5b3f9c5a616b41e94ca23c494'), size_on_disk=330245, blob_last_accessed=1763591393.6761684, blob_last_modified=1758648269.710661), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6374/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e410602f56bc94d9831a2bc8bdb865ee4ebe9862a65b603165b655fbcc4b9914'), size_on_disk=441531, blob_last_accessed=1763591393.1891718, blob_last_modified=1758648268.5356555), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6855/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48e48378d24404e926bc1c44b10af60d4b12f82124ff2f80bc0b69b4a0f98740'), size_on_disk=609248, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6556606), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7367/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a7778bb24506047963575fa96126157895c7f329996c729c3e7d6070595955c3'), size_on_disk=324361, blob_last_accessed=1763591392.1631787, blob_last_modified=1758648270.628665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7283/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0dc91bfacca82c5ed23de7757d59f9f808d541d96dd79aab56f0c4252e7aabf3'), size_on_disk=265146, blob_last_accessed=1763591392.7731745, blob_last_modified=1758648270.401664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5a70fa6f22aeda3246d17646cc4ec7c5a46e1993f9981b19d9fd5a8cc99bf11f'), size_on_disk=308858, blob_last_accessed=1763591391.9411802, blob_last_modified=1758648267.0186484)}), refs=frozenset(), last_modified=1758648270.6726654), CachedRevisionInfo(commit_hash='6864b2582ce35f92a8dde59851786319c2c494bc', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc'), size_on_disk=49590351, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6443/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4deb4634e8b4bf141a2410e94b2d99e2b31a0e8f037de9a1c6e14696025bffae'), size_on_disk=494775, blob_last_accessed=1763591392.991173, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6863/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec534c91cae4413d30477014131d39b51c9c236dfbf150874bd21ec3de821613'), size_on_disk=389025, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648269.6376605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7295/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/19bd07f913e0af3460e6b4b5091971ceed8f4aa510cf18b82ec36dabb67c68b8'), size_on_disk=272619, blob_last_accessed=1763591393.1151721, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6513/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c8f9c31704c2f8732b4668e1e6410034b8adf2ada39ff55935aa0199e920d055'), size_on_disk=507367, blob_last_accessed=1763591392.1451788, blob_last_modified=1758648268.8406568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7269/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5bae329d2318610f627fc562a5163253c7d61e0ef83c53808b220133b2299264'), size_on_disk=203263, blob_last_accessed=1763591392.541176, blob_last_modified=1758648270.4096642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6965/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9ae4546d58672f0632757c94b595af8b7c40da215611783fdc1a65000075ec98'), size_on_disk=509677, blob_last_accessed=1763591392.2731779, blob_last_modified=1758648269.933662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/71a33d11c3ff714ce0730145c740eec4fe88812c6136b8d10b86fa8d4054991f'), size_on_disk=186500, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7286518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6640/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48339729112d22aa5aabf411674bfb90a567ec4c7a07ea7930738645a96823ef'), size_on_disk=329042, blob_last_accessed=1763591392.6391754, blob_last_modified=1758648269.090658), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7211/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73d7747698fa1168c89f12c105bbe900d5969d27a82ae2302d0720d761fbfdab'), size_on_disk=298482, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.1276627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7237/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/110a743162ace7792a44dfe48d7afd40bb67c45c722038c981548477ebc5d8fe'), size_on_disk=313207, blob_last_accessed=1763591393.143172, blob_last_modified=1758648270.1426628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1c8a8cb23b1ec073827d6f5343a3bf03a9d7a04cf2ae3fd3ea4cc5ac37673b38'), size_on_disk=342987, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.6226559), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9f7663ccf72b0681ae5866c13500fbde98f35877fd677ac0d49c17447d17d6ce'), size_on_disk=332767, blob_last_accessed=1763591392.1731787, blob_last_modified=1758648266.744647), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6545/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/260c4a280f52024cb12d44559a6189641ee02efbaf20168435196efbfc69d741'), size_on_disk=404812, blob_last_accessed=1763591392.3761773, blob_last_modified=1758648268.8256567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6437/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/556084dbbc174db5450872d6f5e66cc72091380ea32006977dd176e23c105a7f'), size_on_disk=389840, blob_last_accessed=1763591392.714175, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6923/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/454ca6a52d4b305eddc4b6967ef9604be4861269fdcfff419883daed9b2fe48b'), size_on_disk=337423, blob_last_accessed=1763591393.5031695, blob_last_modified=1758648269.6476605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5986/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f1179c75ac6e91de03b67a73882f3b9c07c8e435d938d3059de489403adde7a'), size_on_disk=328749, blob_last_accessed=1763591392.691175, blob_last_modified=1758648268.228654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6506/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/986d9d3b4ed064d66ebaa0f2502b933f1650a20ac4bbbba804e2cde03b9454c0'), size_on_disk=378407, blob_last_accessed=1763591391.97318, blob_last_modified=1758648268.7656565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6983/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4ee29f84ab279c520521a6c5a5eed9e5e5581b2e39378bdd898eec67d13654f'), size_on_disk=410123, blob_last_accessed=1763591392.6751752, blob_last_modified=1758648269.8636615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6532/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3041f19cf0dcb3e0bd9b1178994351a22f3297ccd43c5e291df8bfe0ad8d5d02'), size_on_disk=516437, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.8236568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6738/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e6d612d16538a06c985f1134f3fdfd367944b2a85cedfb8b0ddad1679004c940'), size_on_disk=333560, blob_last_accessed=1763591392.8651738, blob_last_modified=1758648269.3676593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS006b/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7eedae811a5b3dd4389e7e814d35b022bfaf01fc4d2cfb47be6e6786d6d58c64'), size_on_disk=315912, blob_last_accessed=1763591393.2571712, blob_last_modified=1758648266.7546473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/128015caa722289251a45aa3d56cabac6b5946eb4e1be16eb950555fb24ac413'), size_on_disk=172328, blob_last_accessed=1763591393.735168, blob_last_modified=1758648267.2826495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0230df4f1d3748337e5315bb1d22a0373fdab95f9bc900041b0e34966d868718'), size_on_disk=311116, blob_last_accessed=1763591392.9241736, blob_last_modified=1758648267.2506495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5a70fa6f22aeda3246d17646cc4ec7c5a46e1993f9981b19d9fd5a8cc99bf11f'), size_on_disk=308858, blob_last_accessed=1763591391.9411802, blob_last_modified=1758648267.0186484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7240/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4460d188b4cd3b362f33acf0f0b5312a267b37f8fb2dab4b2577c6c3bbb1231'), size_on_disk=307039, blob_last_accessed=1763591392.6101756, blob_last_modified=1758648270.403664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6374/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e410602f56bc94d9831a2bc8bdb865ee4ebe9862a65b603165b655fbcc4b9914'), size_on_disk=441531, blob_last_accessed=1763591393.1891718, blob_last_modified=1758648268.5356555), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6770/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/082f14f7b0d5fc47e088efc40ae85a8ec41f99437c66da96ca69af210bce61cc'), size_on_disk=318118, blob_last_accessed=1763591392.8081744, blob_last_modified=1758648269.3776593), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fc266bb938fa146c46d1a7bd5446d5d27b3bf202'), size_on_disk=10887, blob_last_accessed=1763649507.5438576, blob_last_modified=1763649507.5418575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/00e5602b87ac84670a98c081e6520ca744038f4e9b3a72a78cb61f5926be56b1'), size_on_disk=251087, blob_last_accessed=1763591392.5781758, blob_last_modified=1758648267.8286521), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6021/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eea0d101dd58c9619b41a94009fc0dff8156d2d36d7f2b88bb653e96123a032e'), size_on_disk=439545, blob_last_accessed=1763591393.5531693, blob_last_modified=1758648268.258654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6551/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c3729692991cdcafe9baa3d2805b3866f72fe9bc89ba22c6d2cdae4d5b5bbf5f'), size_on_disk=545477, blob_last_accessed=1763591393.0611725, blob_last_modified=1758648268.8376567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5614/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1aa702ff57d34ebed961043e0efae064bfdb62f589cbb515311ae8ea37ba8c76'), size_on_disk=429423, blob_last_accessed=1763591393.4671698, blob_last_modified=1758648268.0406532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02313d4d85859a7353e56d89b6e14bcec5160849b5c0089ccbecaa1cda5012cf'), size_on_disk=301213, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4356549), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6572/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3488eaac5e4b9b3418b909138e6b50e3f9ebaf3e7345a4a10b6ced18b66152e4'), size_on_disk=440579, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9506574), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ddd58117dbedfe7e9dc3fd946187e7bd5d88fe8b0f38999c6e4b5660938bb787'), size_on_disk=359739, blob_last_accessed=1763591393.5981688, blob_last_modified=1758648267.2726495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6073/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d68ca6411ba47eb63adad81e88fa1341a0ee269897268957ae936dcc9ba7788'), size_on_disk=450031, blob_last_accessed=1763591393.1341722, blob_last_modified=1758648268.3886547), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6421/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1e3bacd3bcf1fcb46cfc5f9ab66c2af57e190f3d006031e7ef37864fbd36aaca'), size_on_disk=495452, blob_last_accessed=1763591392.2161784, blob_last_modified=1758648268.6366558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/530b5650d91dae3bf169f967af8e800611e3e01daeb1811d6fd21c2612ac4ad9'), size_on_disk=315170, blob_last_accessed=1763591393.6121688, blob_last_modified=1758648266.7956474), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bd2017e75947ead2c06f9d1936347f7fdcfac7fbff620e0b39077208a02c3bd9'), size_on_disk=303094, blob_last_accessed=1763591393.1271722, blob_last_modified=1758648269.9296618), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c9b19726dc45896e4f86a5acc52d07cc422f895bfaaaddb332aaceedff36560c'), size_on_disk=178793, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0266485), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7063/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9427d8f8c54e3c0cc9d720958ef15d3859f00cdfb678559e0268387a36a8ef37'), size_on_disk=404051, blob_last_accessed=1763591392.5301762, blob_last_modified=1758648269.8976617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6635/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6c5b7f1fda3a7f5679870f438d13a45558f7f8da0f479c5144d7751bc8f7fbbc'), size_on_disk=420658, blob_last_accessed=1763591391.95018, blob_last_modified=1758648269.3956594), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ef036874e53ee98dfe1b5d84c42749fc40da9279cd573174321ac83201c1651'), size_on_disk=349762, blob_last_accessed=1763591393.0791724, blob_last_modified=1758648267.4966507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4e8332a5db6260f6357f940e62e3e7b622785f386e81cdb9c75b538d318f4a6'), size_on_disk=158978, blob_last_accessed=1763591393.5471692, blob_last_modified=1758648266.7626472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6177/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b91ffef1dfcdf7952df46956d76a4b364aa0d8342453636cf09e6a343607184f'), size_on_disk=387618, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.424655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eb61968b5ec5eadd9087e42ead29333f91195dd5b3f9c5a616b41e94ca23c494'), size_on_disk=330245, blob_last_accessed=1763591393.6761684, blob_last_modified=1758648269.710661), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6689/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/90ea249116dc6a9be86974e1ec0acf79558dfd56a5666be2e05d2107b8c4ee60'), size_on_disk=366787, blob_last_accessed=1763591393.6661685, blob_last_modified=1758648269.1406581), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7185/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/14f67668aca8d8f8aa2b8af98835da683a7fc3f22aa09cdcd7f943a37632391e'), size_on_disk=201650, blob_last_accessed=1763591393.2191715, blob_last_modified=1758648270.1076627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7370/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ff4dce26afb34b291220c17625be4a28078f9721a8b000ea177c4add3bf1a039'), size_on_disk=241010, blob_last_accessed=1763591393.7701678, blob_last_modified=1758648270.6726654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5399/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b65d0aa7ddc98940f91f2ae5f07e1d3f81dda688792552b91fcc39fb4f9ededa'), size_on_disk=256729, blob_last_accessed=1763591392.5471761, blob_last_modified=1758648267.972653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6567/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0e9713a94594dfcbc80134af52449e12ef207ca9149f5a96c43b6cb457b2431c'), size_on_disk=478809, blob_last_accessed=1763591392.6241755, blob_last_modified=1758648268.893657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6390/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f324cdf73148c8b0fa23456bc4f51cff3032d08ed42e44dd9fcc4815a6d969c'), size_on_disk=560601, blob_last_accessed=1763591392.7391748, blob_last_modified=1758648268.6206558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6808/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c71f5371e8c803cfc7fd507d0b3c11f7548db5d7a0fa42c68977169369ea036e'), size_on_disk=422880, blob_last_accessed=1763591392.9491735, blob_last_modified=1758648269.3916593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fc9311b65abb851fbbfd8bb0c0765af59069659f5a715aee546a8644b0af4ec2'), size_on_disk=210948, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648267.6766515), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7367/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a7778bb24506047963575fa96126157895c7f329996c729c3e7d6070595955c3'), size_on_disk=324361, blob_last_accessed=1763591392.1631787, blob_last_modified=1758648270.628665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6448/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/182dc134914a5ddb02992ab6a4f1c12d2450aa331820bf7bd95693dc999bcfae'), size_on_disk=390210, blob_last_accessed=1763591392.9591732, blob_last_modified=1758648268.672656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5654/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38961219eb66dec31505ebcc5088f16684be9891b96fffa8c9a13cd3f13254d0'), size_on_disk=447782, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.1876538), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6055/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/31bb70b4c90654ecb276664f5c914679322b24985ec66d5b396a99f253ebfa17'), size_on_disk=377015, blob_last_accessed=1763591393.5701692, blob_last_modified=1758648268.3076544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/74cbce50c258bdddb7e42c8fd5b0b2a9da74fe3cf89a9ab5a53ee4fde7c1dee6'), size_on_disk=186142, blob_last_accessed=1763591392.842174, blob_last_modified=1758648266.9966483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e91fd764a83947c14f35d7a2d8c0d156c0a61a32a1da96273718ef4be720787'), size_on_disk=361855, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.7196517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7cb5cca74f1bda2f8f1c8ef962e54d34228361eaa9dd8b21e0d1dd7092ff8164'), size_on_disk=229703, blob_last_accessed=1763591392.555176, blob_last_modified=1758648267.0126483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ac8c590c19e7b95b9aac571b1394976039f25fdd66e62a68ecb99925433f8e54'), size_on_disk=355706, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0226483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7332/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f7f62ee008fa2b5cc688cbd80c888f1bf9da538caa245b63581520d485338a99'), size_on_disk=309595, blob_last_accessed=1763591393.1021724, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7151/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9969e4607d6bf42424f5d2ce8f0d16e3d2d89d069cae6d75997e99c88a68dc77'), size_on_disk=275572, blob_last_accessed=1763591393.7231681, blob_last_modified=1758648270.1476629), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4d098c33564f99ec6aed77d585d1f6e871806f441f099790eef4d6478e322af'), size_on_disk=264459, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7486472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d3e36418bd46acf9537a8f864b1a1361a062d72fec2b093789b88ed655ec117'), size_on_disk=377393, blob_last_accessed=1763591393.0451727, blob_last_modified=1758648267.0136483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dcb33135a9d854d17a6b80348f6bee8becea9ca3808583a034f89c5e4f460c54'), size_on_disk=218643, blob_last_accessed=1763591393.4831698, blob_last_modified=1758648267.9636528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6140/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/66c2da17957d7e6a8b159d3573879ac779d0094e35150b51e2643f7ddee36e1f'), size_on_disk=332958, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4236548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6573/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e435989051765cf2955d8cd733904e8b60e5f3121e1c2fdc987f4e6f71fe3970'), size_on_disk=265022, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9586573), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6354/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/80b129a46defc7b06e472a35935bfaf410bc5e456475b2885feb2c8a4934a81b'), size_on_disk=173225, blob_last_accessed=1763591393.4981697, blob_last_modified=1758648268.4986553), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f427ea0f4ded3b6ff841153d8ad87d72a26a964c189246fb6fd5c439bd8b0c1e'), size_on_disk=353623, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.812652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6954/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/aeed3c55d81a4eb0c568486e476211c599ce2bf37f9dff22c7dfca23f5d80495'), size_on_disk=476054, blob_last_accessed=1763591393.011173, blob_last_modified=1758648269.943662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6739/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/58b5ed63f2197f406dd3df6a569975d776722193007912718979eaed4d4e5f9c'), size_on_disk=399160, blob_last_accessed=1763591392.6551754, blob_last_modified=1758648269.331659), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6924/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5e9b3a94d73c1720e6b05ea55000054905a8672edea7f950736ce3fe191f8faf'), size_on_disk=412939, blob_last_accessed=1763591393.748168, blob_last_modified=1758648269.7066607), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=composite/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1f8adf8fb4992283714df8587126211d54e6a0580b8be88c1d5e911196aed783'), size_on_disk=4814573, blob_last_accessed=1763591392.0561793, blob_last_modified=1758648268.0256531), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR007B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6f0620b102e7cbfadced7d5530545a185036b53118d3e2b7f074c56403d80f4b'), size_on_disk=263185, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7616518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6764/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec87eef9e2e8cdf3c3a10ead28dcf1bfa76e94e12a42422a46c8559fb18207c2'), size_on_disk=75684, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.4036593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4b2ff192aa4808cb50b1c1b1667060f98505dd63c1aa58c7d8354e41194957e4'), size_on_disk=289767, blob_last_accessed=1763591393.4871697, blob_last_modified=1758648266.7706473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d50648ed5c9f593f830aff661d13b3d34bd14f0bf93b16169df4765c9f47fc8'), size_on_disk=295924, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7426472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7256/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1af21cbdd7f6f6effff15a1b061a48f85132d582f08c8fd42e04ea58bde7ec00'), size_on_disk=206710, blob_last_accessed=1763591392.586176, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5801/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/12e91a415cc2a149af4c34c3c8b032821ff301e2828dfb9f4636bd6b2913ea3d'), size_on_disk=360485, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.1636536), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=MAG002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f7f35257d4c1f7fa5812ea31c1de791e08ab5e3a4e7ca42b2bb12b2c64f7ed7'), size_on_disk=490174, blob_last_accessed=1763591392.829174, blob_last_modified=1758648267.583651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fa1a44bd33df22ec6b5a8baf3be17e51a11522b5fdd545a38e6e163cc5ae8207'), size_on_disk=241911, blob_last_accessed=1763591392.572176, blob_last_modified=1758648267.5226507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dc74862f02ff12b8c906046f1f03953d33f5516091d58397b0f9a0fc05bb4b5c'), size_on_disk=315435, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.5006506), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6731/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/23ecd71c4e3c1c547295bb756d4654eb413ff288d10118ab286cd21ccd95b666'), size_on_disk=346187, blob_last_accessed=1763591392.1871786, blob_last_modified=1758648269.1906583), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6920/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/45122fcfb3abc3a43631023712c224dc6e00dba566fb2e15deb4ab4501ad2538'), size_on_disk=446998, blob_last_accessed=1763591392.8881738, blob_last_modified=1758648269.6416605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6657/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/95ad2225152f51e2e31c030ff6708aeebccee5e548035d6f43c9cc3554bdfe5e'), size_on_disk=507730, blob_last_accessed=1763591393.163172, blob_last_modified=1758648269.1676583), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR006B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fcc5e886ec7c6363a5ab51b03d3da189ac3e0cde0a0f79d4f0c46043a6b61838'), size_on_disk=253857, blob_last_accessed=1763591392.0501795, blob_last_modified=1758648267.785652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7283/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0dc91bfacca82c5ed23de7757d59f9f808d541d96dd79aab56f0c4252e7aabf3'), size_on_disk=265146, blob_last_accessed=1763591392.7731745, blob_last_modified=1758648270.401664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e871cf06f596bfe74896b62aa28d6b1e6f2f0b8408630fb146c7e29cfd88fbf0'), size_on_disk=232721, blob_last_accessed=1763591392.7871745, blob_last_modified=1758648267.991653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS011/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ea7cb0559b342dd0ec278e7d5846a5ba0c0d867ea04d545f72e0d1c9472a0cc'), size_on_disk=363979, blob_last_accessed=1763591392.9791732, blob_last_modified=1758648267.0116484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5301_5088/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/65ef775934339b80eda59a141cf791a74e1c6f12513d8143307cebe2f11c3738'), size_on_disk=410106, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.9516528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7118/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/be8bd409b83526d5671501de6ef2980177955fdcc3a13d5e153d2593493b2a1e'), size_on_disk=346308, blob_last_accessed=1763591392.9281735, blob_last_modified=1758648270.1336627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e20f616f6e340b76f6446d2cdffb6cc9feb81297bd0307733f835acf4142cfd'), size_on_disk=269523, blob_last_accessed=1763591392.3461773, blob_last_modified=1758648267.2696495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7266/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9aea2d6f5606d24e1999d450379b1f8c8b9d80eded9e784fb729faa516a27026'), size_on_disk=324480, blob_last_accessed=1763591392.0401795, blob_last_modified=1758648270.379664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6527/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/10081f44b1c2f5ad32489da51fc9ac5b5289b54688a7e78725db247a644b7e4d'), size_on_disk=494640, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.882657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7297/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4bd4088cbfcccdf96def916bb8fd9d673327d3e57c2a4a3e57299743ce4aca7'), size_on_disk=261344, blob_last_accessed=1763591393.588169, blob_last_modified=1758648270.6516652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/d1cd38451bc41ef14dbc1566c4704b004b2c0661f667210c81dba09bec90540f'), size_on_disk=244487, blob_last_accessed=1763591393.6301687, blob_last_modified=1758648267.3246498), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5610/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a86512ae1000a994c86e9aebaf30cf9ce1a59160155f3dc1afa6cbde0a1fc47e'), size_on_disk=307382, blob_last_accessed=1763591393.5291693, blob_last_modified=1758648267.994653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6061/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73e03845d0fbff1a5931e4f792e2c1965ade02fc622ffc08e05c267ac6cd3231'), size_on_disk=394930, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.447655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5617/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bc01241e8363373c995a0fa7439aa1e432a60eb8a7805124fff302311f638361'), size_on_disk=351950, blob_last_accessed=1763591393.6541686, blob_last_modified=1758648268.1026535), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6651/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7644af4cc956ea08809ab6c80c65095e3fdbd36e056040c0eaded3773d546359'), size_on_disk=559951, blob_last_accessed=1763591393.6361687, blob_last_modified=1758648269.1856585), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/11485fd7bb94d0093ce2508d541fea3ce64f934197d8a5f7bb26ecbdb59f8dbb'), size_on_disk=124642, blob_last_accessed=1763591392.3321776, blob_last_modified=1758648267.0666487), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7115/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/205443fd87ce5c793dab86eb75c21dab7dc2ae2edac41d7e6314cfe491bdb01d'), size_on_disk=321850, blob_last_accessed=1763591393.2331715, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6872/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02fabe6e32f52fe6fca923beb0a6cc2898d991304e23a3e75f3ae8a6bcf361c6'), size_on_disk=274362, blob_last_accessed=1763591393.6231687, blob_last_modified=1758648269.6966608), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7243/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cb59e645fff2683db692b4d2f514602ba210271b632cd113853febe941c6485f'), size_on_disk=281696, blob_last_accessed=1763591392.701175, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/62e9a627f20607bc4887ebc9277131f974ec80ff39ac386775f4b8f2f5cb9d1d'), size_on_disk=346782, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7476518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6855/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48e48378d24404e926bc1c44b10af60d4b12f82124ff2f80bc0b69b4a0f98740'), size_on_disk=609248, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6556606), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e5b3174998823af40d055f46ca06e7bb44601cbe97b133062cd3e0491dd30ac2'), size_on_disk=323308, blob_last_accessed=1763591393.153172, blob_last_modified=1758648267.2686496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=KB001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38c939d54d3ed77a09650d2ccf00a6ed0e20390b8be722f1c3ed7881a5904a60'), size_on_disk=351677, blob_last_accessed=1763591392.9151735, blob_last_modified=1758648267.5566509), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5961/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/49cac5c2df1adf790a9ac0209db6c0945de50b8191d403e8b1ad386ee2dcb35e'), size_on_disk=321368, blob_last_accessed=1763591392.560176, blob_last_modified=1758648268.215654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7258/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7b1860ba3be7dbf053203fa5690da2002f7841186940231acea6064e70f0bdc8'), size_on_disk=171331, blob_last_accessed=1763591392.6171756, blob_last_modified=1758648270.3746638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7331/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f159a9811b8ac1b5e252b52427979a3bddce2e7131b13f84046b31e47f7fa399'), size_on_disk=242821, blob_last_accessed=1763591393.2501714, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a76f0849e518faedfe50e6f0c76908a3f6beed34fd0b607603db1859493595d0'), size_on_disk=292312, blob_last_accessed=1763587658.6168654, blob_last_modified=1758648266.7676473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7137/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/703fe9f8552adcbdfb5e640509cb62df2fa734b43fe1e98fc1fd342415f823d7'), size_on_disk=515398, blob_last_accessed=1763589053.7698598, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bbe701ffd546a2f4359f821262f7b44232bdc328d9d4719bfdaa8f2aedc6d2e9'), size_on_disk=346079, blob_last_accessed=1763591393.0861723, blob_last_modified=1758648267.589651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7276/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4e0d576cc4a6a85be64f2198e5001a9f5b02b506072df6f695cfdfc719cb041'), size_on_disk=204036, blob_last_accessed=1763591392.987173, blob_last_modified=1758648270.4646642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1fa09a5a6e6e69198501d994050adfcad7ff39a3cc2fe94e7ea177414e1bc968'), size_on_disk=303532, blob_last_accessed=1763591393.7091682, blob_last_modified=1758648267.2776496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6708/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/35520976736159652e3be8066e77b1371522f63a8b3564d9b0d9f5b1598a144d'), size_on_disk=253955, blob_last_accessed=1763591393.6881683, blob_last_modified=1758648269.1996584), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ab338795b8bbf334650386a123f6e0df41363cb5dcbb778d485ce58804e96b29'), size_on_disk=375506, blob_last_accessed=1763591392.9711733, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6959/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c0cb6c2ec2bda19b0c1da5d5fc2229d0b6d6987d75646fa230c0c14d591cf1f1'), size_on_disk=537588, blob_last_accessed=1763591392.851174, blob_last_modified=1758648269.8686616), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/33d7303ed077f489ee28021daca0bbc859feb1cfd383dfc9a856ea13994801a9'), size_on_disk=331877, blob_last_accessed=1763591393.292171, blob_last_modified=1758648268.2226539), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7155/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/521fc1bea6963640645c21cbdb8d1de07793f11213c23ac71d249f3252b32f54'), size_on_disk=304554, blob_last_accessed=1763591393.5841691, blob_last_modified=1758648270.1366627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6853/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ed47ecb2aa870fdbf12920fd4486933262c0a24318c9c8c5531e359a3c416c90'), size_on_disk=515114, blob_last_accessed=1763591393.2661712, blob_last_modified=1758648269.4276595), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6454/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c35b60861d2fc7a3f4256869e14e629aed07cd2ccce76608578ceb8c6ec423e1'), size_on_disk=498326, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.7226562), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6798/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/899cde55032cd593be0013e1f6ff33fe9a12f4616477b1410d86fae1b2008a09'), size_on_disk=342376, blob_last_accessed=1763591392.7951744, blob_last_modified=1758648269.4006593), CachedFileInfo(file_name='annotated_features_meta.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features_meta.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/2be4b693d333698f7ada7ef40ffaf927c0160d7975ebcf6e215cbc4ba1be1604'), size_on_disk=11813, blob_last_accessed=1763587594.724117, blob_last_modified=1758648264.795638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=MAG001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/30b63d014686a103b5e7fc6571e902fa9c08630a96557cc12ec9dfcf60871306'), size_on_disk=347333, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.559651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6677/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4ee6e1d3e2b9a9bbcc8851048d2b16f4aea6a1e2f752afb9e0bf75a56adf7042'), size_on_disk=322147, blob_last_accessed=1763591392.393177, blob_last_modified=1758648269.0826578), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5690/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4831e3e037d421a14efbe1da8c369015ec7d01df351bb27f0ef0400c8b94c693'), size_on_disk=311754, blob_last_accessed=1763591392.9021738, blob_last_modified=1758648268.211654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d4c0cbbf6075a8b5732e3cef2ba154d95d21aa0efc7e49d947cceebd8742b30'), size_on_disk=269007, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4106548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/646c843611bf88c7f5dcd5b1a664d724c3b24b6434cc52d9a54de62ccaf6c542'), size_on_disk=255949, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.4506505), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6861/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6955b1a1746fe41162ea1d7a098482bbfdb3d0e411c809b52d698e285627ce03'), size_on_disk=385103, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6146603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7351/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cc708adf186f28db09dfdd4ac43865a90482b2192306949eab01492598cf23e7'), size_on_disk=198070, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.610665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6b92129c2fb99028b6cab9509cf1a4c40b142109cde34c9a130d375001dcd211'), size_on_disk=267273, blob_last_accessed=1763591392.7811744, blob_last_modified=1758648269.9106617)}), refs=frozenset({'main'}), last_modified=1763649507.5418575)}), last_accessed=1763649507.5438576, last_modified=1763649507.5418575), CachedRepoInfo(repo_id='BrentLab/kemmeren_2014', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014'), size_on_disk=319245924, nb_files=5, revisions=frozenset({CachedRevisionInfo(commit_hash='b76f0dfe8477b300000faecf16b7d315fbf59344', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/b76f0dfe8477b300000faecf16b7d315fbf59344'), size_on_disk=319228170, files=frozenset({CachedFileInfo(file_name='kemmeren_2014.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/b76f0dfe8477b300000faecf16b7d315fbf59344/kemmeren_2014.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/3c463017444bd7690959c0d6fff0d0ebe2faa7b916dd9c44e305156f6c98b863'), size_on_disk=319218929, blob_last_accessed=1760552530.221978, blob_last_modified=1756832530.9644527), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/b76f0dfe8477b300000faecf16b7d315fbf59344/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/199dce9d01d3884716dd3a785a85751cb9b1de3e'), size_on_disk=9241, blob_last_accessed=1760552530.0869784, blob_last_modified=1758654056.5908144)}), refs=frozenset(), last_modified=1758654056.5908144), CachedRevisionInfo(commit_hash='95a5f915ad49dfe4af75632861d9da69de2b525f', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/95a5f915ad49dfe4af75632861d9da69de2b525f'), size_on_disk=10286, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/95a5f915ad49dfe4af75632861d9da69de2b525f/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/05a15ab9764763e5d1957023162e13b0068a5697'), size_on_disk=10286, blob_last_accessed=1765901283.9662895, blob_last_modified=1765415815.7076464)}), refs=frozenset({'main'}), last_modified=1765415815.7076464), CachedRevisionInfo(commit_hash='a6c9a954a1def1774b68582f117ad1abc5864a07', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07'), size_on_disk=319226397, files=frozenset({CachedFileInfo(file_name='kemmeren_2014.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07/kemmeren_2014.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/3c463017444bd7690959c0d6fff0d0ebe2faa7b916dd9c44e305156f6c98b863'), size_on_disk=319218929, blob_last_accessed=1760552530.221978, blob_last_modified=1756832530.9644527), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/a29a64128c10ef9691ee773131972c61f720ba07'), size_on_disk=5007, blob_last_accessed=1756832529.5094638, blob_last_modified=1756832529.5404634), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756832529.5094638, blob_last_modified=1756832529.5434635)}), refs=frozenset(), last_modified=1756832530.9644527)}), last_accessed=1765901283.9662895, last_modified=1765415815.7076464), CachedRepoInfo(repo_id='BrentLab/hu_2007_reimand_2010', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010'), size_on_disk=42682732, nb_files=4, revisions=frozenset({CachedRevisionInfo(commit_hash='31ccd35daf785420014a1346a7db064dd306d4f8', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8'), size_on_disk=42682732, files=frozenset({CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756844741.8097973, blob_last_modified=1756844741.8767972), CachedFileInfo(file_name='hu_2007_reimand_2010.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/hu_2007_reimand_2010.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/eb9be5a1f01907a81eb2ab4fbbee36ff8c50186f'), size_on_disk=63, blob_last_accessed=1756844741.7997973, blob_last_modified=1756844741.8557973), CachedFileInfo(file_name='hu_2007_reimand_2010.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/hu_2007_reimand_2010.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/47a15f3e23f8cb50af3217d00c6439d29a7422b5190116e4453a5f78cb22540d'), size_on_disk=42677516, blob_last_accessed=1756844742.1787965, blob_last_modified=1756844742.1477966), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/886f4614dfd1ef33709d462908ae18608c13e8ba'), size_on_disk=2692, blob_last_accessed=1756844742.0937967, blob_last_modified=1756844742.1537964)}), refs=frozenset({'main'}), last_modified=1756844742.1537964)}), last_accessed=1756844742.1787965, last_modified=1756844742.1537964), CachedRepoInfo(repo_id='BrentLab/rossi_2021', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021'), size_on_disk=213734041, nb_files=48, revisions=frozenset({CachedRevisionInfo(commit_hash='dcf529428b66f5efc8c44ea06fb4733c41952e03', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/dcf529428b66f5efc8c44ea06fb4733c41952e03'), size_on_disk=15570962, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/dcf529428b66f5efc8c44ea06fb4733c41952e03/genome_map/accession=SRR11466106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/04bacc0f8c03ee000ba3b69320522d0b1be909686474b6e8a126173a0ceae8c4'), size_on_disk=15547870, blob_last_accessed=1762805142.7999256, blob_last_modified=1756944357.1972551), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/dcf529428b66f5efc8c44ea06fb4733c41952e03/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/192e51c15075f65f15da70558261e4c95bb1392a'), size_on_disk=8396, blob_last_accessed=1762805108.5211468, blob_last_modified=1762805108.5201466), CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/dcf529428b66f5efc8c44ea06fb4733c41952e03/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1762805126.6580298, blob_last_modified=1756944310.9395182)}), refs=frozenset({'main'}), last_modified=1762805108.5201466), CachedRevisionInfo(commit_hash='6b29baae54286d5fbdd1c70f499c03319badad51', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51'), size_on_disk=213707016, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466115/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/c08ce1194dd98cb028a6038944317e70ac1545e32b21dd323eafa57b16611e6e'), size_on_disk=3503431, blob_last_accessed=1757598690.9468212, blob_last_modified=1757104405.9427147), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466143/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/54bd4af6ed8caf52a5810598d96c9b235266542586a692112cb247a209c2649f'), size_on_disk=10801408, blob_last_accessed=1757598691.2688208, blob_last_modified=1757104413.3726768), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466107/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/2f924bc58d82a21b621b051addd1913c77369afaa738171ef0962548fc1a1021'), size_on_disk=5670198, blob_last_accessed=1757598690.8608212, blob_last_modified=1757104405.0417192), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466124/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/39ef32412cae257b724c3b797ea482eeffb5b29afef4246d997ea525a2124054'), size_on_disk=7162800, blob_last_accessed=1757598691.075821, blob_last_modified=1757104409.5966961), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466108/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/56875f81a3cc7c1d40b3042b2c443058eb0f9a2b7e7ebc2e1704c098be612628'), size_on_disk=1765819, blob_last_accessed=1757598690.8238213, blob_last_modified=1757104407.1947083), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466127/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/1565c5175e05c322f160529cb4bb74d5e8468458f150f228d6aadf2b670c3b4a'), size_on_disk=11670434, blob_last_accessed=1757598691.092821, blob_last_modified=1757104411.0776885), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466125/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/0bba21490ea0a6297031b0ff3219fc0158d9bbcf15099906c86a770b1a1312fc'), size_on_disk=2624759, blob_last_accessed=1757598691.081821, blob_last_modified=1757104409.4426968), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466129/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/b290d4c9436756b444d0da1af1229b0a2a6e877fd968c5d01b73efa96e62cff8'), size_on_disk=6955953, blob_last_accessed=1757598691.196821, blob_last_modified=1757104410.78769), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466126/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/34292b90d99b5a5be542c7dd10b11336605330639d92e93b092e567a56fe7d93'), size_on_disk=2850164, blob_last_accessed=1757598691.086821, blob_last_modified=1757104410.0976934), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466144/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/57b3d2c7917bfa29312cc51c921beff914858c61762e28a658eb29d8eaf3348b'), size_on_disk=918271, blob_last_accessed=1757598691.2758207, blob_last_modified=1757104413.554676), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466112/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8f3441964a046d3c148b41f335948a085d26884e1a890e515711a2d1b48a40be'), size_on_disk=4883601, blob_last_accessed=1757598690.9128213, blob_last_modified=1757104408.5027015), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466142/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/3dc044b5cc1b7bc8070d8de4b7b870524b296f26b4f43b22dce2dcbf161d4c74'), size_on_disk=1739520, blob_last_accessed=1757598691.2588208, blob_last_modified=1757104415.5896657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466149/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/ae4d5237fde214a92d3267fbe11f6f1f88569cc7f3f7ccb40fed200871bf33b1'), size_on_disk=10933582, blob_last_accessed=1757598691.3518207, blob_last_modified=1757104415.1316679), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466109/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/6588704c47423e671054df9cb2318c045138b0018f2eefff61ef91a25848eb58'), size_on_disk=5259413, blob_last_accessed=1757598690.8298213, blob_last_modified=1757104404.9987195), CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1762805126.6580298, blob_last_modified=1756944310.9395182), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466141/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/e9efb807ce35fc3e43b1ff0d27b80646c7d137c587abd9d6247393e44d4f30a0'), size_on_disk=986450, blob_last_accessed=1757598691.2628207, blob_last_modified=1757104413.129678), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/04bacc0f8c03ee000ba3b69320522d0b1be909686474b6e8a126173a0ceae8c4'), size_on_disk=15547870, blob_last_accessed=1762805142.7999256, blob_last_modified=1756944357.1972551), CachedFileInfo(file_name='genome_map.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/51978dc2125afb40a41a29672f8ae750308d9a63'), size_on_disk=5311747, blob_last_accessed=1757598690.9468212, blob_last_modified=1757104406.477712), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466118/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/192057e6004dd5cd303e8b1abf54cb72005e211c315665e28eec7b617a84d95a'), size_on_disk=728586, blob_last_accessed=1757598691.0068212, blob_last_modified=1757104407.0917087), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466122/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/6362da9e07f02d4c9dc05f15f638ca1b1bf0de5989da4ee8c6f0768f2453f98f'), size_on_disk=1249990, blob_last_accessed=1757598691.0398211, blob_last_modified=1757104411.9016843), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466150/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/80cd2d325e5d7a50c6ecbf79a052f45c54a93b9423e90735bf8989aada9bb9eb'), size_on_disk=3234303, blob_last_accessed=1757598691.3798206, blob_last_modified=1757104417.837654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466139/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/25c7d3ffbacca76fd998edffe0c762e220f448140019aa2d389facff5117ce48'), size_on_disk=176811, blob_last_accessed=1757598691.2478209, blob_last_modified=1757104412.6266806), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466110/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/d5283e08e898b4f59837f7ff2064f79c8a828994f868c637894f4e07df36daa5'), size_on_disk=13123795, blob_last_accessed=1757598690.8318212, blob_last_modified=1757104407.6787057), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466132/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/cc41509e63473d565341ed36fd6881fecfd537e59c003f4779d4c4ec7b49cb54'), size_on_disk=1906184, blob_last_accessed=1757598691.161821, blob_last_modified=1757104413.4366765), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466131/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/bdce0842b5568e406a799dbe36b4b9645a30fa58eb0a707f24ed3bb3900fbf67'), size_on_disk=11759994, blob_last_accessed=1757598691.157821, blob_last_modified=1757104411.1146884), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466111/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/ba431fc9a05f40581e450429d467226a8e3c49195de355437f9044bb0fdfe524'), size_on_disk=5749460, blob_last_accessed=1757598690.9028213, blob_last_modified=1757104404.9707196), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466114/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/2c07f671cca471d6ad3e10e071cd1b976a89a45ff32920913d8fc36dbeaa1678'), size_on_disk=10992797, blob_last_accessed=1757598690.9498212, blob_last_modified=1757104406.3287127), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466123/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/57b7ca5d419676590538573f295b99db05a952e20cb4c6b2a1618974611d5051'), size_on_disk=3672899, blob_last_accessed=1757598691.073821, blob_last_modified=1757104409.590696), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466135/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/d44d381db0b8c0cb4960796e48d3d16e3d766aff87cbc686789a0c5799ba0a98'), size_on_disk=5204256, blob_last_accessed=1757598691.180821, blob_last_modified=1757104412.4376817), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466113/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/acec3b041a74ab45dbeb03e7f183c2b3d16479aadd64a81f80a74aa42d56368d'), size_on_disk=5866774, blob_last_accessed=1757598690.921821, blob_last_modified=1757104409.0226989), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466134/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/0b23434181eb1fc864e858cfca82d2d8fed1524a8f7dbea3bf576f6f452cf08b'), size_on_disk=12398132, blob_last_accessed=1757598691.169821, blob_last_modified=1757104412.552681), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466140/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/25f98a891820b5a91c11629ed5615d40ffe1fe4a6cba1b2c6642067d76b5be87'), size_on_disk=68174, blob_last_accessed=1757598691.2588208, blob_last_modified=1757104415.6456654), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/09f6dce90ec0e64d8f18614091dd18ad2e86213a'), size_on_disk=4403, blob_last_accessed=1757598690.987821, blob_last_modified=1757104409.9196944), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466136/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/f5e7fd644d30ae0c8e2c72bc341155bfe5df2ce102b68b08ff4d243470026fcc'), size_on_disk=343033, blob_last_accessed=1757598691.1958208, blob_last_modified=1757104411.7116852), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466119/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/0157d9136c2c2bb5640866449adf4bab8c62b3d7f7fb4262981908c77986f89e'), size_on_disk=12516152, blob_last_accessed=1757598691.0068212, blob_last_modified=1757104412.3166823), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466120/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/4220a31e6a7be79341828d41da77ef77fb5a8026980d4445fec18c57b5229c1f'), size_on_disk=3644577, blob_last_accessed=1757598691.034821, blob_last_modified=1757104408.3467023), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466121/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/78d0f377f8a756b28f2f5296932368c9c6b764d2972dc93aa6dd354591d29860'), size_on_disk=7053621, blob_last_accessed=1757598691.0328212, blob_last_modified=1757104408.5097015), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1757598690.9228213, blob_last_modified=1757104403.6017265), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466116/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/587a7b86dffe3d7ade9adeeb94029ea1de54e61fb2c2445dc527979b7f1c24ac'), size_on_disk=5596311, blob_last_accessed=1757598690.988821, blob_last_modified=1757104407.3507075), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466130/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/73db821736e3e47c09da250e369fa06812e02e5abe7a4a3ceab5d1020422b66c'), size_on_disk=1920920, blob_last_accessed=1757598691.1328208, blob_last_modified=1757104410.9306893), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466117/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/49065d4cee74c20f34303405e34e284e29f19082b76fa883a38aeab24cadf674'), size_on_disk=1742958, blob_last_accessed=1757598690.995821, blob_last_modified=1757104411.8276846), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466128/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/f631797d2bc13a580f570637d94a0b9ea37deb2fd7ef92e32c66fd9014260525'), size_on_disk=1059587, blob_last_accessed=1757598691.118821, blob_last_modified=1757104410.6996903), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466133/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/b6d6913e14b52d5d51dab2f254815da89e03cebe75e80356b27cf18e31eb048f'), size_on_disk=5090722, blob_last_accessed=1757598691.169821, blob_last_modified=1757104412.180683)}), refs=frozenset(), last_modified=1757104417.837654), CachedRevisionInfo(commit_hash='6aac54b4b3fbb414561a8b6cc7acf87880c3c3c2', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6aac54b4b3fbb414561a8b6cc7acf87880c3c3c2'), size_on_disk=4665, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6aac54b4b3fbb414561a8b6cc7acf87880c3c3c2/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/14e1e02ad3804c0a4146a52509460e9cbb759d93'), size_on_disk=4665, blob_last_accessed=1758306843.4906857, blob_last_modified=1758156479.1516545)}), refs=frozenset(), last_modified=1758156479.1516545), CachedRevisionInfo(commit_hash='824bf517ff0722a085c86ac7924df0ab1278c8bf', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/824bf517ff0722a085c86ac7924df0ab1278c8bf'), size_on_disk=14696, files=frozenset({CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/824bf517ff0722a085c86ac7924df0ab1278c8bf/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1762805126.6580298, blob_last_modified=1756944310.9395182)}), refs=frozenset(), last_modified=1756944310.9395182), CachedRevisionInfo(commit_hash='3ce77eb2070d7bb43049ba26bb462fded0ff7863', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/3ce77eb2070d7bb43049ba26bb462fded0ff7863'), size_on_disk=15562566, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/3ce77eb2070d7bb43049ba26bb462fded0ff7863/genome_map/accession=SRR11466106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/04bacc0f8c03ee000ba3b69320522d0b1be909686474b6e8a126173a0ceae8c4'), size_on_disk=15547870, blob_last_accessed=1762805142.7999256, blob_last_modified=1756944357.1972551), CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/3ce77eb2070d7bb43049ba26bb462fded0ff7863/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1762805126.6580298, blob_last_modified=1756944310.9395182)}), refs=frozenset(), last_modified=1756944357.1972551), CachedRevisionInfo(commit_hash='1acaa5629922414e8efe23a5d023bd44965824c8', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1acaa5629922414e8efe23a5d023bd44965824c8'), size_on_disk=4683, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1acaa5629922414e8efe23a5d023bd44965824c8/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/bb690d71edd44885609ed660d926f3cf2c4c473e'), size_on_disk=4683, blob_last_accessed=1758306349.7238934, blob_last_modified=1758156991.1235294)}), refs=frozenset(), last_modified=1758156991.1235294), CachedRevisionInfo(commit_hash='22ebbfcf62a7ed665fb8eceaea53b58b45a1709e', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/22ebbfcf62a7ed665fb8eceaea53b58b45a1709e'), size_on_disk=4602, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/22ebbfcf62a7ed665fb8eceaea53b58b45a1709e/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/e03b8d10ba59c549907a309ad8343133b7b902fc'), size_on_disk=4602, blob_last_accessed=1757613581.062093, blob_last_modified=1757613581.0610929)}), refs=frozenset(), last_modified=1757613581.0610929), CachedRevisionInfo(commit_hash='1e07e46fd0b5348243487ac4a573b570a2a3946b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1e07e46fd0b5348243487ac4a573b570a2a3946b'), size_on_disk=4683, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1e07e46fd0b5348243487ac4a573b570a2a3946b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/bb690d71edd44885609ed660d926f3cf2c4c473e'), size_on_disk=4683, blob_last_accessed=1758306349.7238934, blob_last_modified=1758156991.1235294)}), refs=frozenset(), last_modified=1758156991.1235294), CachedRevisionInfo(commit_hash='664d95cdd482d753bc139d26119061c2fa6eaa76', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/664d95cdd482d753bc139d26119061c2fa6eaa76'), size_on_disk=4679, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/664d95cdd482d753bc139d26119061c2fa6eaa76/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/ff6f75db42c0690f0e0285e56c1aa95bc5cf1300'), size_on_disk=4679, blob_last_accessed=1758157110.1705706, blob_last_modified=1758157110.1695707)}), refs=frozenset(), last_modified=1758157110.1695707)}), last_accessed=1762805142.7999256, last_modified=1762805108.5201466), CachedRepoInfo(repo_id='BrentLab/harbison_2004', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004'), size_on_disk=70377892, nb_files=10, revisions=frozenset({CachedRevisionInfo(commit_hash='2916ad316cc0d8a1ce2e7f35d3707b831b203e87', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/2916ad316cc0d8a1ce2e7f35d3707b831b203e87'), size_on_disk=7160, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/2916ad316cc0d8a1ce2e7f35d3707b831b203e87/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/7e3ec66f5479346ca9d082545ff0b6a960fb60d5'), size_on_disk=7160, blob_last_accessed=1758649475.233073, blob_last_modified=1758155946.7669883)}), refs=frozenset(), last_modified=1758155946.7669883), CachedRevisionInfo(commit_hash='073cdeb1b541c28b1a97df9dbc6be6af1d9c23b6', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/073cdeb1b541c28b1a97df9dbc6be6af1d9c23b6'), size_on_disk=5145, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/073cdeb1b541c28b1a97df9dbc6be6af1d9c23b6/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/f7a6cf3dd4477f08e508f38665350e8d11589963'), size_on_disk=5145, blob_last_accessed=1755876214.0380862, blob_last_modified=1755876188.1642818)}), refs=frozenset(), last_modified=1755876188.1642818), CachedRevisionInfo(commit_hash='a0f6f1b23505c4e6a471e65a43a33ff5cced0733', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a0f6f1b23505c4e6a471e65a43a33ff5cced0733'), size_on_disk=25423545, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a0f6f1b23505c4e6a471e65a43a33ff5cced0733/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b5fbd9e98fd8ddadeeb5631e3b6f5055e917c98d'), size_on_disk=7679, blob_last_accessed=1759345152.2162483, blob_last_modified=1758649637.161202), CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a0f6f1b23505c4e6a471e65a43a33ff5cced0733/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/91904b3e84754bd6011cd4b385bbc99d69a899e5c7562f039a121a744ba95a0a'), size_on_disk=25415866, blob_last_accessed=1763588775.480192, blob_last_modified=1758649664.1292322)}), refs=frozenset(), last_modified=1758649664.1292322), CachedRevisionInfo(commit_hash='5eeb06e389a648a36087e20eabad1f961e22dc5a', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/5eeb06e389a648a36087e20eabad1f961e22dc5a'), size_on_disk=44894945, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/5eeb06e389a648a36087e20eabad1f961e22dc5a/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/aa43ea37112a84a60d21ce254c1cc1e433def6dc'), size_on_disk=8550, blob_last_accessed=1765406568.606891, blob_last_modified=1765292615.6690626), CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/5eeb06e389a648a36087e20eabad1f961e22dc5a/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b89f865a471fbf3a8054871f8fe79507f6f4be5c2291dcd19030ec8fd4a5325c'), size_on_disk=44886395, blob_last_accessed=1765901285.6763043, blob_last_modified=1765314558.8410568)}), refs=frozenset(), last_modified=1765314558.8410568), CachedRevisionInfo(commit_hash='4e54b7fafd79829bcd179b508efd11a3dc7182cc', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/4e54b7fafd79829bcd179b508efd11a3dc7182cc'), size_on_disk=44900498, files=frozenset({CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/4e54b7fafd79829bcd179b508efd11a3dc7182cc/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b89f865a471fbf3a8054871f8fe79507f6f4be5c2291dcd19030ec8fd4a5325c'), size_on_disk=44886395, blob_last_accessed=1765901285.6763043, blob_last_modified=1765314558.8410568), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/4e54b7fafd79829bcd179b508efd11a3dc7182cc/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/ef196364c79e1a0ec01b3ab6db8dae798f0f8fd5'), size_on_disk=14103, blob_last_accessed=1765911961.5656407, blob_last_modified=1765819563.7734888)}), refs=frozenset(), last_modified=1765819563.7734888), CachedRevisionInfo(commit_hash='95dace55563820030e6bcac2d4b2a9a386a2369b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/95dace55563820030e6bcac2d4b2a9a386a2369b'), size_on_disk=44900425, files=frozenset({CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/95dace55563820030e6bcac2d4b2a9a386a2369b/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b89f865a471fbf3a8054871f8fe79507f6f4be5c2291dcd19030ec8fd4a5325c'), size_on_disk=44886395, blob_last_accessed=1765901285.6763043, blob_last_modified=1765314558.8410568), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/95dace55563820030e6bcac2d4b2a9a386a2369b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/1cfc9a691644fd13ba4e3c4d141caf8bc8dadc92'), size_on_disk=14030, blob_last_accessed=1765809787.9431984, blob_last_modified=1765415607.8811436)}), refs=frozenset(), last_modified=1765415607.8811436), CachedRevisionInfo(commit_hash='a33c34b373e379dfa9bd4922d281790180bb1217', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a33c34b373e379dfa9bd4922d281790180bb1217'), size_on_disk=44899466, files=frozenset({CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a33c34b373e379dfa9bd4922d281790180bb1217/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b89f865a471fbf3a8054871f8fe79507f6f4be5c2291dcd19030ec8fd4a5325c'), size_on_disk=44886395, blob_last_accessed=1765901285.6763043, blob_last_modified=1765314558.8410568), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a33c34b373e379dfa9bd4922d281790180bb1217/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/3cb3997d01aa1c91d0719f954e1cf207976c8a7d'), size_on_disk=13071, blob_last_accessed=1765916907.5030358, blob_last_modified=1765916907.339037)}), refs=frozenset({'main'}), last_modified=1765916907.339037), CachedRevisionInfo(commit_hash='3ff5f32dfeef3c0b2b7d6cc860b259b0ad095829', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/3ff5f32dfeef3c0b2b7d6cc860b259b0ad095829'), size_on_disk=25421759, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/3ff5f32dfeef3c0b2b7d6cc860b259b0ad095829/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/342343ab9f71e7ce0394c540bed737de39f2cf08'), size_on_disk=5893, blob_last_accessed=1764345374.74204, blob_last_modified=1763587990.060371), CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/3ff5f32dfeef3c0b2b7d6cc860b259b0ad095829/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/91904b3e84754bd6011cd4b385bbc99d69a899e5c7562f039a121a744ba95a0a'), size_on_disk=25415866, blob_last_accessed=1763588775.480192, blob_last_modified=1758649664.1292322)}), refs=frozenset(), last_modified=1763587990.060371)}), last_accessed=1765916907.5030358, last_modified=1765916907.339037), CachedRepoInfo(repo_id='BrentLab/yeast_genome_resources', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources'), size_on_disk=114475, nb_files=7, revisions=frozenset({CachedRevisionInfo(commit_hash='6607f7be76546563db0a68a9365c0a858bb5f3a1', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/6607f7be76546563db0a68a9365c0a858bb5f3a1'), size_on_disk=4495, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/6607f7be76546563db0a68a9365c0a858bb5f3a1/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/a31c4647c502767c22b5fe725ec8de58686a02d4'), size_on_disk=4495, blob_last_accessed=1755813454.8008156, blob_last_modified=1755813454.7998157)}), refs=frozenset(), last_modified=1755813454.7998157), CachedRevisionInfo(commit_hash='7441b9a8c858332e730e7ddb9d255460ae698626', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/7441b9a8c858332e730e7ddb9d255460ae698626'), size_on_disk=87714, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/7441b9a8c858332e730e7ddb9d255460ae698626/features/chr=chrII/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/d64638adb2290412a962abffe0fbd0546ff139b29064bbf313b0a768449454a5'), size_on_disk=82276, blob_last_accessed=1755812630.656903, blob_last_modified=1755812631.0999012), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/7441b9a8c858332e730e7ddb9d255460ae698626/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/e63c2087b2e56dc15e8ac6cf5693c2391127b732'), size_on_disk=5438, blob_last_accessed=1755816785.70087, blob_last_modified=1755816785.6988702)}), refs=frozenset(), last_modified=1755816785.6988702), CachedRevisionInfo(commit_hash='25b47a191e40728efc2437d906e84317f922b36b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/25b47a191e40728efc2437d906e84317f922b36b'), size_on_disk=5426, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/25b47a191e40728efc2437d906e84317f922b36b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/9cdf7b342b249f25f2c2cbe46a2e0a26ded4f692'), size_on_disk=5426, blob_last_accessed=1755815405.3308983, blob_last_modified=1755815405.3298984)}), refs=frozenset(), last_modified=1755815405.3298984), CachedRevisionInfo(commit_hash='aae6e5ea8139e23940d11b4d7e77684c78bb7f6b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/aae6e5ea8139e23940d11b4d7e77684c78bb7f6b'), size_on_disk=4585, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/aae6e5ea8139e23940d11b4d7e77684c78bb7f6b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/4320f8f18620d988a5fc32872a671ace406fd0a2'), size_on_disk=4585, blob_last_accessed=1755814342.1923234, blob_last_modified=1755814342.1913235)}), refs=frozenset(), last_modified=1755814342.1913235), CachedRevisionInfo(commit_hash='a5ecb243ba27ca4b4d6ad1b60cc160c033ca2be6', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/a5ecb243ba27ca4b4d6ad1b60cc160c033ca2be6'), size_on_disk=82276, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/a5ecb243ba27ca4b4d6ad1b60cc160c033ca2be6/features/chr=chrII/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/d64638adb2290412a962abffe0fbd0546ff139b29064bbf313b0a768449454a5'), size_on_disk=82276, blob_last_accessed=1755812630.656903, blob_last_modified=1755812631.0999012)}), refs=frozenset(), last_modified=1755812631.0999012), CachedRevisionInfo(commit_hash='42beb28478e27c5d4c5bb2308b12100e6489ef07', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/42beb28478e27c5d4c5bb2308b12100e6489ef07'), size_on_disk=6125, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/42beb28478e27c5d4c5bb2308b12100e6489ef07/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/ab6cc475062a2767a41b2aaa006faf4d9d2d60d6'), size_on_disk=6125, blob_last_accessed=1759787821.8450553, blob_last_modified=1758155946.5549896)}), refs=frozenset({'main'}), last_modified=1758155946.5549896), CachedRevisionInfo(commit_hash='15fdb72f8c31ae58f3e3ce7c90be279383743a2f', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/15fdb72f8c31ae58f3e3ce7c90be279383743a2f'), size_on_disk=88406, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/15fdb72f8c31ae58f3e3ce7c90be279383743a2f/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/645da8028fd6abe86ee305f76eab78cdf29be364'), size_on_disk=6130, blob_last_accessed=1755819093.2316637, blob_last_modified=1755819093.2306638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/15fdb72f8c31ae58f3e3ce7c90be279383743a2f/features/chr=chrII/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/d64638adb2290412a962abffe0fbd0546ff139b29064bbf313b0a768449454a5'), size_on_disk=82276, blob_last_accessed=1755812630.656903, blob_last_modified=1755812631.0999012)}), refs=frozenset(), last_modified=1755819093.2306638)}), last_accessed=1759787821.8450553, last_modified=1758155946.5549896), CachedRepoInfo(repo_id='BrentLab/hackett_2020', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020'), size_on_disk=817026981, nb_files=22, revisions=frozenset({CachedRevisionInfo(commit_hash='4fef197cd055065207d66ea42aec9746b23f38c8', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/4fef197cd055065207d66ea42aec9746b23f38c8'), size_on_disk=9431, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/4fef197cd055065207d66ea42aec9746b23f38c8/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/0a3330d889673abc635b58a78c221ba5b3e36200'), size_on_disk=9431, blob_last_accessed=1765809790.786152, blob_last_modified=1765650535.7166286)}), refs=frozenset({'main'}), last_modified=1765650535.7166286), CachedRevisionInfo(commit_hash='2c196866d4f057cb5cf0adf1fe35f2c712c61025', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025'), size_on_disk=404565376, files=frozenset({CachedFileInfo(file_name='hackett_2020.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/hackett_2020.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4f47951c65c799b5c74ee3782b49c16cbb038d50'), size_on_disk=55, blob_last_accessed=1757105553.3228161, blob_last_modified=1756843395.5719483), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c3e72ccb1b8deba4bbfd18abe6081de7ec3914d9'), size_on_disk=8879, blob_last_accessed=1757105552.290822, blob_last_modified=1757104206.2667632), CachedFileInfo(file_name='parse_mcisaac_data.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/scripts/parse_mcisaac_data.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0'), size_on_disk=4691, blob_last_accessed=1756843395.7579472, blob_last_modified=1756843395.820947), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1760552530.4189773, blob_last_modified=1756843405.225893), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756843395.5059488, blob_last_modified=1756843395.5679483)}), refs=frozenset(), last_modified=1757104206.2667632), CachedRevisionInfo(commit_hash='1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43'), size_on_disk=2678425, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=45/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/db8595cd20dd7440c890c46025cf0643da8ec6be3e15bc18a8759b3fecdc02af'), size_on_disk=349256, blob_last_accessed=1756145253.7795417, blob_last_modified=1756145254.6225288), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=5/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/ed331d9d2b27a0958b12368c7b5d2d3383b111d3d1c39ed0e77b78561dc78b6a'), size_on_disk=348626, blob_last_accessed=1756145253.7845416, blob_last_modified=1756145254.6395288), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=90/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/ebd06af83cc386d9094ce6b8d795b155c76c47c96da1a458743348264f1638ae'), size_on_disk=349917, blob_last_accessed=1756145253.7845416, blob_last_modified=1756145254.7235274), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=30/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/a85bd6b418d9644d9adaa1269c27f97469a4aaee51af63cf1aa041f62cd8ba2c'), size_on_disk=350044, blob_last_accessed=1756145253.7915416, blob_last_modified=1756145254.6385286), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/45cb0687c3ea72e7b32ad17dd799bc38ad02f80b'), size_on_disk=16034, blob_last_accessed=1756138543.6766906, blob_last_modified=1756138543.6746907), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=10/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4a2eb1051cad96c5b39a014f19b092f042b03700cc1b1a40c2de9e7edd73edf9'), size_on_disk=352201, blob_last_accessed=1756145253.7915416, blob_last_modified=1756145254.6375287), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=15/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/51dfb68ed2518f0dd8ab4d4070d4896b6eca912ecb7e4aa773d857e3096020b5'), size_on_disk=351273, blob_last_accessed=1756145217.972108, blob_last_modified=1756142799.7054222), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=20/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/97adc0bb2b436c325555d82bb7f0ffb0e250d73092fcbf03ac0c00fa2b762e51'), size_on_disk=351379, blob_last_accessed=1756145253.7895415, blob_last_modified=1756145254.6265287), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=0/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/409962e1914ac9e9d631e4ae73e04b167822d0915d8d8d4a87703d4c7281ffb9'), size_on_disk=209695, blob_last_accessed=1756145254.7305272, blob_last_modified=1756145254.608529)}), refs=frozenset(), last_modified=1756145254.7235274), CachedRevisionInfo(commit_hash='60b3ecf6a06e709f0397b327ece5c31016303b5c', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/60b3ecf6a06e709f0397b327ece5c31016303b5c'), size_on_disk=404558833, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/60b3ecf6a06e709f0397b327ece5c31016303b5c/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/6f54100c2bb84a6bc059c17e0954dad0ea451666'), size_on_disk=9543, blob_last_accessed=1760552529.9219792, blob_last_modified=1758155372.1328666), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/60b3ecf6a06e709f0397b327ece5c31016303b5c/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1760552530.4189773, blob_last_modified=1756843405.225893)}), refs=frozenset(), last_modified=1758155372.1328666), CachedRevisionInfo(commit_hash='ca2500351e8aa9fc5b996516cd25a60e8298734d', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/ca2500351e8aa9fc5b996516cd25a60e8298734d'), size_on_disk=6294, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/ca2500351e8aa9fc5b996516cd25a60e8298734d/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/ebb31f16ba92f3c0a9eec2aaf4897b3a7662cfad'), size_on_disk=6294, blob_last_accessed=1764169619.6288424, blob_last_modified=1764169578.8420532)}), refs=frozenset(), last_modified=1764169578.8420532), CachedRevisionInfo(commit_hash='6bea8e8497a12fec83927a941ba912a0ec781822', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/6bea8e8497a12fec83927a941ba912a0ec781822'), size_on_disk=8027, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/6bea8e8497a12fec83927a941ba912a0ec781822/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/159b3e518310d0bf928d14a691e41a77d5540aaa'), size_on_disk=8027, blob_last_accessed=1765299157.242701, blob_last_modified=1765299156.9627051)}), refs=frozenset(), last_modified=1765299156.9627051), CachedRevisionInfo(commit_hash='5b09fb31bac250b0bfc099c1bdb32bc2673e754b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/5b09fb31bac250b0bfc099c1bdb32bc2673e754b'), size_on_disk=409731660, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/5b09fb31bac250b0bfc099c1bdb32bc2673e754b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/dacac6a8023596c4c46dce14e80f861d4e4a7401'), size_on_disk=9583, blob_last_accessed=1765309535.4347303, blob_last_modified=1765308942.1208022), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/5b09fb31bac250b0bfc099c1bdb32bc2673e754b/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/10f544e6c4e4da0b411f1084cbe7f9188a842bc6578b699bdf02f3507aff6592'), size_on_disk=409722077, blob_last_accessed=1765311990.5457778, blob_last_modified=1765311990.540778)}), refs=frozenset(), last_modified=1765311990.540778), CachedRevisionInfo(commit_hash='7192b0e2275ba5aa3ffb3752b87f1c3595f2331a', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a'), size_on_disk=404565656, files=frozenset({CachedFileInfo(file_name='parse_mcisaac_data.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/scripts/parse_mcisaac_data.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0'), size_on_disk=4691, blob_last_accessed=1756843395.7579472, blob_last_modified=1756843395.820947), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/f3899cd97da03a3543db10d76c15ee0442bb136e'), size_on_disk=9159, blob_last_accessed=1758145252.5929751, blob_last_modified=1757541416.40237), CachedFileInfo(file_name='hackett_2020.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/hackett_2020.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4f47951c65c799b5c74ee3782b49c16cbb038d50'), size_on_disk=55, blob_last_accessed=1757105553.3228161, blob_last_modified=1756843395.5719483), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756843395.5059488, blob_last_modified=1756843395.5679483), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1760552530.4189773, blob_last_modified=1756843405.225893)}), refs=frozenset(), last_modified=1757541416.40237), CachedRevisionInfo(commit_hash='b9d39535e175295d9cba23489a49fafebbac5ca0', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0'), size_on_disk=404565563, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/03e579e00c632cdb393c0ffafd9d15b069390e5c'), size_on_disk=9066, blob_last_accessed=1757439136.0598524, blob_last_modified=1757105945.6476595), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756843395.5059488, blob_last_modified=1756843395.5679483), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1760552530.4189773, blob_last_modified=1756843405.225893), CachedFileInfo(file_name='hackett_2020.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/hackett_2020.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4f47951c65c799b5c74ee3782b49c16cbb038d50'), size_on_disk=55, blob_last_accessed=1757105553.3228161, blob_last_modified=1756843395.5719483), CachedFileInfo(file_name='parse_mcisaac_data.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/scripts/parse_mcisaac_data.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0'), size_on_disk=4691, blob_last_accessed=1756843395.7579472, blob_last_modified=1756843395.820947)}), refs=frozenset(), last_modified=1757105945.6476595)}), last_accessed=1765809790.786152, last_modified=1765650535.7166286)}), warnings=[])" + "HFCacheInfo(size_on_disk=5525278711, repos=frozenset({CachedRepoInfo(repo_id='BrentLab/yeast_comparative_analysis', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis'), size_on_disk=166072, nb_files=7, revisions=frozenset({CachedRevisionInfo(commit_hash='ac03d065bb493bc9dd7a77460fdaf6f954968b0b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis/snapshots/ac03d065bb493bc9dd7a77460fdaf6f954968b0b'), size_on_disk=166072, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis/snapshots/ac03d065bb493bc9dd7a77460fdaf6f954968b0b/dto/binding_repo_dataset=harbison_2004-harbison_2004/perturbation_repo_dataset=hu_2007_reimand_2010-hu_2007_reimand_2010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis/blobs/30024adb7ad73354c2c962d168dee64c9601c5ee3dcfea72c24178a5638dc32b'), size_on_disk=4498, blob_last_accessed=1767824941.5591376, blob_last_modified=1767824941.5531375), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis/snapshots/ac03d065bb493bc9dd7a77460fdaf6f954968b0b/dto/binding_repo_dataset=harbison_2004-harbison_2004/perturbation_repo_dataset=Hackett_2020-hackett_2020/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis/blobs/0372fb07783a76efc38492cd738f4bf3eef044a6069f00a1d447cf2dae3e243c'), size_on_disk=20942, blob_last_accessed=1767824941.5571375, blob_last_modified=1767824940.9771397), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis/snapshots/ac03d065bb493bc9dd7a77460fdaf6f954968b0b/dto/binding_repo_dataset=callingcards-annotated_features/perturbation_repo_dataset=Hackett_2020-hackett_2020/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis/blobs/fd88d84c5e45f81dd6432d5e5a6f9056dd38cfb03c162442d1d5928151bcb2aa'), size_on_disk=100988, blob_last_accessed=1767824941.5591376, blob_last_modified=1767824941.0061395), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis/snapshots/ac03d065bb493bc9dd7a77460fdaf6f954968b0b/dto/binding_repo_dataset=callingcards-annotated_features/perturbation_repo_dataset=kemmeren_2014-kemmeren_2014/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis/blobs/8454d4bba35420099ec175dd81eeaacec4b1ec43adf8110b278974716c46c25c'), size_on_disk=19150, blob_last_accessed=1767824941.5591376, blob_last_modified=1767824940.9881396), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis/snapshots/ac03d065bb493bc9dd7a77460fdaf6f954968b0b/dto/binding_repo_dataset=harbison_2004-harbison_2004/perturbation_repo_dataset=kemmeren_2014-kemmeren_2014/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis/blobs/cbacede910cd43bf24ffdde99be52f8ec211c9b54ed1f0865034b67101ffcda7'), size_on_disk=10728, blob_last_accessed=1767824941.5591376, blob_last_modified=1767824940.9741397), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis/snapshots/ac03d065bb493bc9dd7a77460fdaf6f954968b0b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis/blobs/5c68356d43f7f19567f17bea53a021de93e97e3f'), size_on_disk=3660, blob_last_accessed=1767808305.8494713, blob_last_modified=1767808305.8474715), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis/snapshots/ac03d065bb493bc9dd7a77460fdaf6f954968b0b/dto/binding_repo_dataset=callingcards-annotated_features/perturbation_repo_dataset=hu_2007_reimand_2010-hu_2007_reimand_2010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis/blobs/872b0b76a4eaa911c4319fc5103864b7f3d22ec5b25faeb551694985f69a8697'), size_on_disk=6106, blob_last_accessed=1767824941.5591376, blob_last_modified=1767824940.9561398)}), refs=frozenset({'main'}), last_modified=1767824941.5531375)}), last_accessed=1767824941.5591376, last_modified=1767824941.5531375), CachedRepoInfo(repo_id='BrentLab/yeast_genome_resources', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources'), size_on_disk=114475, nb_files=7, revisions=frozenset({CachedRevisionInfo(commit_hash='15fdb72f8c31ae58f3e3ce7c90be279383743a2f', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/15fdb72f8c31ae58f3e3ce7c90be279383743a2f'), size_on_disk=88406, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/15fdb72f8c31ae58f3e3ce7c90be279383743a2f/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/645da8028fd6abe86ee305f76eab78cdf29be364'), size_on_disk=6130, blob_last_accessed=1755819093.2316637, blob_last_modified=1755819093.2306638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/15fdb72f8c31ae58f3e3ce7c90be279383743a2f/features/chr=chrII/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/d64638adb2290412a962abffe0fbd0546ff139b29064bbf313b0a768449454a5'), size_on_disk=82276, blob_last_accessed=1755812630.656903, blob_last_modified=1755812631.0999012)}), refs=frozenset(), last_modified=1755819093.2306638), CachedRevisionInfo(commit_hash='42beb28478e27c5d4c5bb2308b12100e6489ef07', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/42beb28478e27c5d4c5bb2308b12100e6489ef07'), size_on_disk=6125, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/42beb28478e27c5d4c5bb2308b12100e6489ef07/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/ab6cc475062a2767a41b2aaa006faf4d9d2d60d6'), size_on_disk=6125, blob_last_accessed=1759787821.8450553, blob_last_modified=1758155946.5549896)}), refs=frozenset({'main'}), last_modified=1758155946.5549896), CachedRevisionInfo(commit_hash='25b47a191e40728efc2437d906e84317f922b36b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/25b47a191e40728efc2437d906e84317f922b36b'), size_on_disk=5426, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/25b47a191e40728efc2437d906e84317f922b36b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/9cdf7b342b249f25f2c2cbe46a2e0a26ded4f692'), size_on_disk=5426, blob_last_accessed=1755815405.3308983, blob_last_modified=1755815405.3298984)}), refs=frozenset(), last_modified=1755815405.3298984), CachedRevisionInfo(commit_hash='7441b9a8c858332e730e7ddb9d255460ae698626', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/7441b9a8c858332e730e7ddb9d255460ae698626'), size_on_disk=87714, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/7441b9a8c858332e730e7ddb9d255460ae698626/features/chr=chrII/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/d64638adb2290412a962abffe0fbd0546ff139b29064bbf313b0a768449454a5'), size_on_disk=82276, blob_last_accessed=1755812630.656903, blob_last_modified=1755812631.0999012), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/7441b9a8c858332e730e7ddb9d255460ae698626/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/e63c2087b2e56dc15e8ac6cf5693c2391127b732'), size_on_disk=5438, blob_last_accessed=1755816785.70087, blob_last_modified=1755816785.6988702)}), refs=frozenset(), last_modified=1755816785.6988702), CachedRevisionInfo(commit_hash='a5ecb243ba27ca4b4d6ad1b60cc160c033ca2be6', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/a5ecb243ba27ca4b4d6ad1b60cc160c033ca2be6'), size_on_disk=82276, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/a5ecb243ba27ca4b4d6ad1b60cc160c033ca2be6/features/chr=chrII/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/d64638adb2290412a962abffe0fbd0546ff139b29064bbf313b0a768449454a5'), size_on_disk=82276, blob_last_accessed=1755812630.656903, blob_last_modified=1755812631.0999012)}), refs=frozenset(), last_modified=1755812631.0999012), CachedRevisionInfo(commit_hash='aae6e5ea8139e23940d11b4d7e77684c78bb7f6b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/aae6e5ea8139e23940d11b4d7e77684c78bb7f6b'), size_on_disk=4585, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/aae6e5ea8139e23940d11b4d7e77684c78bb7f6b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/4320f8f18620d988a5fc32872a671ace406fd0a2'), size_on_disk=4585, blob_last_accessed=1755814342.1923234, blob_last_modified=1755814342.1913235)}), refs=frozenset(), last_modified=1755814342.1913235), CachedRevisionInfo(commit_hash='6607f7be76546563db0a68a9365c0a858bb5f3a1', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/6607f7be76546563db0a68a9365c0a858bb5f3a1'), size_on_disk=4495, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/6607f7be76546563db0a68a9365c0a858bb5f3a1/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/a31c4647c502767c22b5fe725ec8de58686a02d4'), size_on_disk=4495, blob_last_accessed=1755813454.8008156, blob_last_modified=1755813454.7998157)}), refs=frozenset(), last_modified=1755813454.7998157)}), last_accessed=1759787821.8450553, last_modified=1758155946.5549896), CachedRepoInfo(repo_id='BrentLab/barkai_compendium', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium'), size_on_disk=3579068970, nb_files=504, revisions=frozenset({CachedRevisionInfo(commit_hash='a987ef37c72fcd07b18828d320bc4305480daade', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade'), size_on_disk=3579068970, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417941/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/30188c510d2d7041e1525e304edc8f25b569a1771cf00844cb27300a052441e8'), size_on_disk=7032333, blob_last_accessed=1756926363.2551925, blob_last_modified=1756926364.2891881), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417914/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d062dbf660e1ef240ff8ef99ff2d486d9caaabda14575e6365ce6f725fa91ccf'), size_on_disk=8599250, blob_last_accessed=1756926358.2422144, blob_last_modified=1756926360.3412054), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381017/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1d9b2dbb5079eca871182c2dce1d681b943f1362072c18e255a4bd6d1068b840'), size_on_disk=2436549, blob_last_accessed=1756926378.161128, blob_last_modified=1756926378.7931254), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381028/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8d40caa48e7b9d32d2a0548732ef91c01ede9d0f4f6d74f045817e818c52c808'), size_on_disk=18141950, blob_last_accessed=1756926378.9691246, blob_last_modified=1756926778.2687466), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417928/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/97e74e01fd641bfef68b6206a3c62ab41b5c28d8ea8369ce4198df1b3387b266'), size_on_disk=2449472, blob_last_accessed=1756926360.9072027, blob_last_modified=1756926361.3542008), CachedFileInfo(file_name='GSE178430_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE178430_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/be2c4af55a502d7fa22abb28afe6db347950327bde1bc7e97dbb68fdb0c59f3d'), size_on_disk=9398, blob_last_accessed=1756926861.9192877, blob_last_modified=1756926300.5344653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417837/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/04d099809ca78bd87dcb9921398681ac64e827439049a37e97a71ba0828debfb'), size_on_disk=8348990, blob_last_accessed=1756926344.4012744, blob_last_modified=1756926345.6152692), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417625/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/96b04f28e74e25bd1502a4ea9aca8047bfa8462e61cced0d726af4a336e1a585'), size_on_disk=17384720, blob_last_accessed=1756926304.4014485, blob_last_modified=1756926306.2074406), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417735/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/634551506800bc76183ce179c8c8f324cb6f0f577f6cf0b629b74ce4f1e995d5'), size_on_disk=19171195, blob_last_accessed=1756926326.0393543, blob_last_modified=1756926329.4873393), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417627/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4027ab6ecd21364ca365bbe7192797ed924c817e6edc841d750111b2e3f6f45a'), size_on_disk=21089243, blob_last_accessed=1756926305.0144458, blob_last_modified=1756926309.6264257), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417943/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0e4191a403f5070636bcb281bd80d39e11c70fb502fa2b18150032d58f2e0740'), size_on_disk=5396199, blob_last_accessed=1756926363.7271905, blob_last_modified=1756926364.542187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417673/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/535d074f70f4553e24f3171fe5cfbd1821635ce7ca6ef93f8665876de620d6c9'), size_on_disk=6559693, blob_last_accessed=1756926315.1844015, blob_last_modified=1756926317.0733933), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417720/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/961772a081bde1795af5caea9d7562e2b1f889981bd3c3f1c792ad9093a758d7'), size_on_disk=659095, blob_last_accessed=1756926324.1233625, blob_last_modified=1756926324.6243603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417698/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d240b652ebca64784e037cc8ffbda424e2c92f368c5bb9c730930f48f29facc2'), size_on_disk=1855826, blob_last_accessed=1756926321.2993748, blob_last_modified=1756926322.0703714), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417777/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bd953fd8a3ee2a5c90382c1c9adc8091fbca801aa87f07a409b6dd5a9704421f'), size_on_disk=1267333, blob_last_accessed=1756926332.9623241, blob_last_modified=1756926333.6353211), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381019/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3023e878b223b00b77d3411a6d20195b85aa7e61a8445fe725ff694c77816053'), size_on_disk=1022843, blob_last_accessed=1756926378.3191273, blob_last_modified=1756926378.864125), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417844/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ae034bb207abc0c649fb60aae78e798d46647f53b4e0352c6510fb7c8339a234'), size_on_disk=5408445, blob_last_accessed=1756926345.5212696, blob_last_modified=1756926346.4342656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417835/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6245fc8d220aad556115d4b831f72ae6a9beac48ed6e2aba811284aa0ccab4f7'), size_on_disk=7888215, blob_last_accessed=1756926343.795277, blob_last_modified=1756926344.942272), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381023/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/22e3e0f4cd1f2c755ec1ac13b99d2d6c98d0158319478f2deca4e89aefc5e88e'), size_on_disk=3860195, blob_last_accessed=1756926378.5651264, blob_last_modified=1756926379.6101217), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417757/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6c9c5dbd0513759ace8599b2542a1f99fe0c30feb318665ebdb55bfb111c8c67'), size_on_disk=5834465, blob_last_accessed=1756926330.011337, blob_last_modified=1756926330.7623336), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417778/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/54e8ea03e214967eb4a1c7b50a086c4d7083d2c9c95dd0419e545fb0e8f18422'), size_on_disk=9345257, blob_last_accessed=1756926333.1513233, blob_last_modified=1756926334.0903192), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417647/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8c01c928abe00b5afa6a2292820695797391b3671cc110b9026f5fc5cb8564ae'), size_on_disk=2891716, blob_last_accessed=1756926310.1614234, blob_last_modified=1756926310.93942), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381060/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1bb05aa71fe55db65fc3868b131854c1e6666dca8cc370874f517aa0756d21ac'), size_on_disk=9137992, blob_last_accessed=1756926778.9637427, blob_last_modified=1756926780.2307358), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417688/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f21ccaca0f6782b6ce2b70b50c9dc93487b6aaec03db6c30d9cb0c9fc01dda42'), size_on_disk=16023102, blob_last_accessed=1756926318.5463867, blob_last_modified=1756926320.0283804), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380972/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/91a8106ca7ac2babcabd1549072b2b730909e1386f827e732428bff2bb5251cb'), size_on_disk=6712599, blob_last_accessed=1756926371.1721582, blob_last_modified=1756926372.0711544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381041/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a53caa1961d9245d82db10989b8a8d601760ccf26f67666d5558937830c51e31'), size_on_disk=6621865, blob_last_accessed=1756926774.784766, blob_last_modified=1756926775.9747593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380925/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdf190d30101829153acfd62edd4d689ac65a7d737f0e80978c7d460819b5caa'), size_on_disk=9220100, blob_last_accessed=1756926364.6101868, blob_last_modified=1756926366.1331801), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417945/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6689ba375c993d00d1f0e716d3bc39604d68df654a67b2518c56f4d54c7f4ca6'), size_on_disk=11289854, blob_last_accessed=1756926364.0191894, blob_last_modified=1756926364.9011855), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381016/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0e597280417f8f8b5b476bc9d66033afc9412759f5e2f9bd9f3367932cc6f03f'), size_on_disk=820782, blob_last_accessed=1756926377.9791288, blob_last_modified=1756926378.4041271), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417748/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/db4c67c001a1f688f70918f0e25d3ddd996392b33b9f1144a14551bc0026d036'), size_on_disk=3246481, blob_last_accessed=1756926328.8123422, blob_last_modified=1756926329.6943383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380949/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/65090ef93158269c98bc2e747e505cea9d460f0959d0b2957e58543cd1944163'), size_on_disk=2110872, blob_last_accessed=1756926368.705169, blob_last_modified=1756926369.142167), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417636/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4a3b2c7076107dcc47a87179cecab9e2051350c94e951a7aadc2ff0cf6fdbaca'), size_on_disk=43479381, blob_last_accessed=1756926307.6494343, blob_last_modified=1756926311.4484177), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417747/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7011e7ff4b64ca9c57bdab93cfcfc52938e7b0574b48417feb4f6185f6c118b9'), size_on_disk=4791771, blob_last_accessed=1756926328.7593424, blob_last_modified=1756926329.9543371), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381064/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8569620c4d2efb8dd163aa69d8edf8f855b96eba637e754a126a88e6f0c73192'), size_on_disk=20598404, blob_last_accessed=1756926779.800738, blob_last_modified=1756926781.1587305), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417870/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ca1bc9825b47ee9ac59e53bc6ada3b1a9e8aba1b57c57c57947afae7926054b'), size_on_disk=10710319, blob_last_accessed=1756926349.7052515, blob_last_modified=1756926350.7552469), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417710/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9837ecd3769eaaf407d939ff09d6ae74d832630b27353251942bd262cd70fc35'), size_on_disk=330457, blob_last_accessed=1756926323.098367, blob_last_modified=1756926323.797364), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417658/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b6cf4c024743be14fcd5730f0ddbb960dbcd83d07adf8668639f9a1cdf195368'), size_on_disk=8754108, blob_last_accessed=1756926311.5764172, blob_last_modified=1756926313.1134105), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417663/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cc7010c3ed70c3083d49d88c0f95dbe8f6eff873d4b5848eeae06a22df71a479'), size_on_disk=8938116, blob_last_accessed=1756926313.18441, blob_last_modified=1756926314.5674043), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417640/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/411d9146359847b5805450f67cbde4f1cbd51e3c2fae4bbcd6295db7426cb2ca'), size_on_disk=7000528, blob_last_accessed=1756926308.9124289, blob_last_modified=1756926310.0344238), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417779/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/918376b5cb0b63e3e531ac990854e5a7b461e7251f5ed562c5a92cbf6a5a0141'), size_on_disk=9459105, blob_last_accessed=1756926333.1593232, blob_last_modified=1756926334.2033186), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417946/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef43c84affe53bbdefcd70cb97a4a4d1e483824b8b6f91e3360a11094609f815'), size_on_disk=4853893, blob_last_accessed=1756926364.0141892, blob_last_modified=1756926365.0591848), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4c4a265f9e3e8d373550ea58ca1cde6094c823f090336974fde3e7cee117fd99'), size_on_disk=1770007, blob_last_accessed=1756926376.6771345, blob_last_modified=1756926377.1011326), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417869/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8dd08ab28007f90a664a6fbde23298ae2bb0ded917d5d75b1275c41e3eb5f7dd'), size_on_disk=16604892, blob_last_accessed=1756926349.6872516, blob_last_modified=1756926352.7202382), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417933/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4fe347d88754646d39d93dd8a78e857d04d1903f7163a21495038b087d5f6b8a'), size_on_disk=12389872, blob_last_accessed=1756926361.7481992, blob_last_modified=1756926362.9061942), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417881/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bf380975f31e362e8964000688bff364809bbe2e8e3efa9ceaa71253ca6beefb'), size_on_disk=8717095, blob_last_accessed=1756926351.848242, blob_last_modified=1756926352.9732373), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381063/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1e4361a7d20f8e92e8efc238365e0ed06eb021243864321c93d4638fa21634c4'), size_on_disk=10916323, blob_last_accessed=1756926779.3497405, blob_last_modified=1756926780.685733), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417632/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/23c561fde2e6c2973ec1913373c343a5c49d6cc0dbb44ff21eb5aff87cd77d3a'), size_on_disk=18481828, blob_last_accessed=1756926306.2764404, blob_last_modified=1756926308.5744302), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381020/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c85f953439e9f6918c7f7c8194bcbbebe0ef8a96070119813025a42bde8c08fe'), size_on_disk=897644, blob_last_accessed=1756926378.3451273, blob_last_modified=1756926378.7061257), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417732/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f99cdb10c12a75e1d020125d68b5c5302263eaef050b5c6b0032e27a0ac9a23c'), size_on_disk=685742, blob_last_accessed=1756926325.7463555, blob_last_modified=1756926326.326353), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380958/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ef3849004b6c72205ea1b298cf6a586f1114061ca94925229eda191d4666885'), size_on_disk=3288900, blob_last_accessed=1756926369.617165, blob_last_modified=1756926370.303162), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417736/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/671a67d7275810840cafc9db32e43385c4ba61b007d702e7eb5cb244a0e4afa9'), size_on_disk=13855124, blob_last_accessed=1756926326.1693537, blob_last_modified=1756926327.261349), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381013/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c831ca7cc9cfd2cc2ccd92a0168b2e14dedf2bde9e29780dfaf6d281b31e9462'), size_on_disk=4648281, blob_last_accessed=1756926377.4421313, blob_last_modified=1756926378.3451273), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417637/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ca1a954c7b2aca56529daf9a89249ad4e6ec2373e29ac7c19d2af1533afc4263'), size_on_disk=2776397, blob_last_accessed=1756926307.8804333, blob_last_modified=1756926309.1794276), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417898/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ced2c80d4f9d2aa28260b885a20b1c90b41d8eb54ee92f418ed592b97b954a9e'), size_on_disk=6422129, blob_last_accessed=1756926355.1262279, blob_last_modified=1756926356.0962236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417786/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdd4770c9b3e18e86f9a46ff924e3a562e37b632d6f0e8ad11627956a3416e09'), size_on_disk=11354740, blob_last_accessed=1756926334.3243182, blob_last_modified=1756926335.4083135), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417680/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aa9e972e7427b1db5556008756651933c8792891339607dee2ee231733072d1c'), size_on_disk=18443288, blob_last_accessed=1756926316.5853953, blob_last_modified=1756926321.4263742), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380988/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/27b6e71986cf2b7da20b64dbda0ba47e08753f7184ddd57ed288f11df88d21e1'), size_on_disk=16063760, blob_last_accessed=1756926373.7421472, blob_last_modified=1756926374.932142), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417784/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fd3dd53cf34f48c235e4f771378f42a5605446bbc62d108a5637a042513fd75d'), size_on_disk=4826642, blob_last_accessed=1756926333.7233207, blob_last_modified=1756926334.576317), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417708/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b58038866c334daaf757a086f99c3a5c5962b8563769ee0a3205bcdcbb0144ec'), size_on_disk=250863, blob_last_accessed=1756926323.0003674, blob_last_modified=1756926323.366366), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417893/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f3b9f2c711cb806350320b93dba528efd6433d0ca10c39e5e2dd6aa89a408bd'), size_on_disk=9896637, blob_last_accessed=1756926354.2372317, blob_last_modified=1756926355.535226), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417766/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/39da4f16b82cc0b5019d8526f2e3eed49de9760e319f8e839db621bf4c1ab021'), size_on_disk=6482957, blob_last_accessed=1756926331.2123318, blob_last_modified=1756926332.1133277), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417761/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ba86b2041414034d38b95ccbf3c65957642c56b56914240999986ebc76b9e193'), size_on_disk=1635118, blob_last_accessed=1756926330.478335, blob_last_modified=1756926331.0933323), CachedFileInfo(file_name='GSE178430_metadata.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE178430_metadata.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9673758f13ec096ec4a89642bbe34d60975f5f05'), size_on_disk=61, blob_last_accessed=1756926300.2374666, blob_last_modified=1756926300.2994664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417775/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2ca9ba5e1394191206945f41168d555519fe3963d4c39f251c1ead57f2a323f4'), size_on_disk=1059891, blob_last_accessed=1756926332.3723266, blob_last_modified=1756926332.773325), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417612/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ee40f7af39e729ee79ef18d6777bbd632b7cc7811041a32d54f4a02b42638464'), size_on_disk=3509045, blob_last_accessed=1756926301.5874608, blob_last_modified=1756926302.6274562), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417715/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ac5effc2ec07a8fb49bac4d77b7d4129a18ae2180807874ea02d1c252a3b68bd'), size_on_disk=520286, blob_last_accessed=1756926323.8693635, blob_last_modified=1756926324.477361), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417641/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/355d8669b1d4c5576d8aa900582800fdf1819360f0db91d5e0299342b49884fb'), size_on_disk=6322689, blob_last_accessed=1756926309.3414268, blob_last_modified=1756926310.7974205), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417740/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e91586589c967e7bcbd521989874b21c024da79365d0d9916ffc66b774043e01'), size_on_disk=11875677, blob_last_accessed=1756926326.8903506, blob_last_modified=1756926327.6843472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380970/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bdec4a51127eaf09abb5d25d7f3a732afc3b561105fd18ba1df44d04e6dd90a2'), size_on_disk=3545790, blob_last_accessed=1756926370.9301593, blob_last_modified=1756926371.5451567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417852/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/43431a444b2761ed9b6fa4c7e6edfc8da31b64ff933408c517bc771e9f3e94dc'), size_on_disk=6967416, blob_last_accessed=1756926346.8272638, blob_last_modified=1756926347.8462594), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380999/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/acb2ee4711dab5800afb7ccbea72c8c80268b0a848723e7bd8ecf197decb982d'), size_on_disk=2605563, blob_last_accessed=1756926375.2281408, blob_last_modified=1756926376.1321368), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417723/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f397cef2d9c491fb034826727c63b3c224f6ed791c9b4dcf884c63c55c9961c'), size_on_disk=2800893, blob_last_accessed=1756926324.70836, blob_last_modified=1756926325.4453568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381031/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b4705ac4577c8e7b116d1a1b677967f69412b0e4716e0de7fa7ee97bc9be4d74'), size_on_disk=4551217, blob_last_accessed=1756926379.2931232, blob_last_modified=1756926776.2707577), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/225bf60f7c9796682210dc48d07a30d3dabab8ebc13abb7a84834279fa131020'), size_on_disk=5307353, blob_last_accessed=1756926376.2401364, blob_last_modified=1756926377.3751316), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417697/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/198a3cd31740f347f0308db2914df4fc0fc303b4a9f040fbaf79fb5ca0170e7f'), size_on_disk=5521438, blob_last_accessed=1756926321.4153743, blob_last_modified=1756926322.4743698), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417892/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7ac6b85018422c74c894467b7b53b1c2dc7c5298fc31654bdea5ca70493e0a87'), size_on_disk=8194725, blob_last_accessed=1756926354.176232, blob_last_modified=1756926354.9712286), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380976/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d27d3efdccd104ca208d69fe9d94fe9011cb6212b34739679b1c8f550c5e9df2'), size_on_disk=12889551, blob_last_accessed=1756926371.6141565, blob_last_modified=1756926372.8021512), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417772/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a21b204a2da7654dd5bf4e7965a3c1e19159c16052232030787175a830ff0233'), size_on_disk=8204405, blob_last_accessed=1756926332.1323278, blob_last_modified=1756926333.453322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417915/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9fbfc50c3e1f756169d25442880ba33e5cf237e3c83a0d674b64ace47e8d3322'), size_on_disk=13801295, blob_last_accessed=1756926358.318214, blob_last_modified=1756926359.4592092), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417861/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef65c45e0e86b7d04e274c39408b332cc94879c0804f16acf7c537b14bb6498c'), size_on_disk=10436399, blob_last_accessed=1756926348.3832572, blob_last_modified=1756926349.6192517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417841/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cb829be078dd8b533a90c2c548dfb2ce0455026be54c36572337e0a1ddeeb2cf'), size_on_disk=5791736, blob_last_accessed=1756926345.138271, blob_last_modified=1756926345.9902675), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417742/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1a87c492aab479dd0c013fbe80e006f18ba80de751da600b95babcb3f6e88dad'), size_on_disk=2054520, blob_last_accessed=1756926327.3293486, blob_last_modified=1756926328.4213438), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381045/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdc8f9e6447280a7fc713c2a8f2157eb9076a5c01a7eb8437176e9ee30d6d4ab'), size_on_disk=4939421, blob_last_accessed=1756926776.3437574, blob_last_modified=1756926777.2507522), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417849/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/23b6606ffcf116cf066b103bf1b6b4fb33c40e544e20e4565fa44c5bbcea4bdd'), size_on_disk=5689527, blob_last_accessed=1756926346.584265, blob_last_modified=1756926347.3892615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380961/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fc4356aaa2a30217145b4aecc4f3fdd2575fcd696a2603fea3d8112998f770c7'), size_on_disk=798599, blob_last_accessed=1756926370.1791627, blob_last_modified=1756926370.589161), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417793/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8a51bd4d953e697022a85f9b527afd45126988ba4bbb796d71c9e5f6c8fffc44'), size_on_disk=10797388, blob_last_accessed=1756926335.4883132, blob_last_modified=1756926336.7013078), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417620/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b973f1a83f111c4e114a40d3731ff24779cafcb7b71523fe8571cf2ed849b316'), size_on_disk=20780203, blob_last_accessed=1756926302.5494566, blob_last_modified=1756926306.7334383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381029/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6431cec7b7404a0773d8715761e2b161f01ebd8471950e71e17dad8976d83f3c'), size_on_disk=7229691, blob_last_accessed=1756926379.0071244, blob_last_modified=1756926776.565756), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417902/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2d72fc510cd348af7611ca170b7804b228b74bd38e7ff5e437375c1cc926fa95'), size_on_disk=8952511, blob_last_accessed=1756926355.9322243, blob_last_modified=1756926357.2422187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380973/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/343eb57057b5af866386fb4c02d0c41b888e206f5473ed7b4041200cfb93dbd5'), size_on_disk=1511721, blob_last_accessed=1756926371.3611574, blob_last_modified=1756926372.4931526), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417739/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1c9e3fcff1dc467673efe70177cb4d678d7eff7b5b2bb3ded247d7a458b466dc'), size_on_disk=21974051, blob_last_accessed=1756926326.8623507, blob_last_modified=1756926328.7393425), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381038/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/415f62b8333a02df8a87ba85e1d0de30a6cc9fdb07f7c8dc02d30f81ed4c14ef'), size_on_disk=2424198, blob_last_accessed=1756926379.8751206, blob_last_modified=1756926380.4921181), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417654/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/67d658902a2b91d0d36345be06dc00c85af1b88ba40c37deaa091705d97b3ffc'), size_on_disk=6323092, blob_last_accessed=1756926311.166419, blob_last_modified=1756926314.4344046), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417614/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dac699f09872b0c58b742c692b341f0b00b7d83284cec5ef838c276a6f48908c'), size_on_disk=1759475, blob_last_accessed=1756926301.7274601, blob_last_modified=1756926302.4774568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380944/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7331bfc92dc902a721083ac4ea027481c90ce204d7dca7c493fec8f284db0155'), size_on_disk=8432479, blob_last_accessed=1756926367.299175, blob_last_modified=1756926368.6141694), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417896/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/71e393229e9071bad31abe5dc3d41bd5d719a5dcb68077827861b4b4e9a49a85'), size_on_disk=6055777, blob_last_accessed=1756926354.7882292, blob_last_modified=1756926355.8442247), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417704/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/477a83e4a5eace957a036c5ec65ef4539442c078058c1a44a877763cb850c86d'), size_on_disk=459034, blob_last_accessed=1756926322.1393712, blob_last_modified=1756926322.8993678), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417764/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8a30e54d96d5b83981e37a0d8f6cae9e6584819a76a200f2730d9de32e282d02'), size_on_disk=2641764, blob_last_accessed=1756926331.099332, blob_last_modified=1756926332.088328), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417634/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/529d984782684991b2eb7be2d345b5a4e8a6e08d3dbe30ad34495d8380464e6e'), size_on_disk=16306189, blob_last_accessed=1756926306.810438, blob_last_modified=1756926310.239423), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417642/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/591f688d0d9f98350a1d636e6f93ed37563f3ea9765ca6b633490533b0cf32d4'), size_on_disk=11498542, blob_last_accessed=1756926309.2804272, blob_last_modified=1756926310.8714201), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417781/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9dd7989f90f8762246868ccd7d11c9ce876b2b33099efc3a4c5bf1bc7cbcca80'), size_on_disk=6923944, blob_last_accessed=1756926333.4783218, blob_last_modified=1756926335.3743136), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417884/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ff7d3efa4a231f5130be299d8d0ff67fe4973856e4db18e8a6c53d47ba6b28a7'), size_on_disk=11684836, blob_last_accessed=1756926352.791238, blob_last_modified=1756926354.0932324), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417646/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2b3d78fc052de8647b1ba6e83245b5f375b4e6c10952feff795855160458a1d5'), size_on_disk=396843, blob_last_accessed=1756926310.1544235, blob_last_modified=1756926311.0694194), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417699/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0c64ecbe6b245299fe04c04239d792fbae0f1b288562fe0c7912a24de56f0ad5'), size_on_disk=5707915, blob_last_accessed=1756926321.6203735, blob_last_modified=1756926322.8483682), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417606/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f76c81d98337c9e127bf5516af0dd135458fd622c01f2b5c9fd85ce6edfa4c7f'), size_on_disk=9502783, blob_last_accessed=1756926300.5794652, blob_last_modified=1756926301.6394606), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380987/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2eb045d9f88b952eed8f8354e77c682bc3003bd2e9eacb0eb0c2df5ca44a34cc'), size_on_disk=4463796, blob_last_accessed=1756926373.559148, blob_last_modified=1756926374.4241443), CachedFileInfo(file_name='GSE222268_metadata.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE222268_metadata.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4459325dc2deeb9f00d803b89cd8fdc823ed383f'), size_on_disk=61, blob_last_accessed=1756926300.2714665, blob_last_modified=1756926300.3444662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417621/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/354c654ace9bc123bcd9009c56af518b98f1a547a7ccd79f348d0e533607a41e'), size_on_disk=20457307, blob_last_accessed=1756926302.5744565, blob_last_modified=1756926307.7874336), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417613/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/695d3095619f3a313d25c506b9d0493f171896f1439bcf3bd1cd932d1f6fed5d'), size_on_disk=2812565, blob_last_accessed=1756926301.6434605, blob_last_modified=1756926302.4814568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bd149efcdaa899981b965dc424d16fb66668a7666a059c6bc5043572331394c2'), size_on_disk=4513644, blob_last_accessed=1756926377.1751323, blob_last_modified=1756926378.1161282), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417714/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d6abeb724a8c8b23217f3465284564da5baffee59ee4e3af582e0a3705ae32c6'), size_on_disk=262443, blob_last_accessed=1756926323.756364, blob_last_modified=1756926324.256362), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417652/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d38c028233981eb275c44995eafa73172388a9e0e704fe278b0cedbab49d429f'), size_on_disk=12947754, blob_last_accessed=1756926311.0344195, blob_last_modified=1756926312.1414146), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381051/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ba017d941936d755013757711f5de4661acd187ef79524662d85485ba6588f34'), size_on_disk=8199472, blob_last_accessed=1756926777.322752, blob_last_modified=1756926778.4757454), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417905/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b9d291469b7a414bab157f45bfd3a1f7ffaaf931de25fec9eb18012a18b70939'), size_on_disk=4418829, blob_last_accessed=1756926356.1612234, blob_last_modified=1756926357.2652185), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417655/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/25d98ba17be9b354b0ec7a0e513a6ff9d4593dd1cadf269f28bcea1ca9c71565'), size_on_disk=5461224, blob_last_accessed=1756926311.5504172, blob_last_modified=1756926312.9474113), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381068/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/16e4412527449a3cb9d2da129c3309a71b2ec9b33a7a39cb9bb31d41ce5d2e04'), size_on_disk=14909067, blob_last_accessed=1756926780.5687337, blob_last_modified=1756926782.3187242), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417927/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8e639025209cff8d34b7e35a5fa371282bb725173225097a39f8f2101a49deed'), size_on_disk=7241057, blob_last_accessed=1756926360.6762037, blob_last_modified=1756926362.1171975), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381037/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5cef560daffa3a5b1e841a312937eca595ced0bbdb3e6c641b13359a0340fea1'), size_on_disk=2090047, blob_last_accessed=1756926379.8821206, blob_last_modified=1756926380.4171183), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417737/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2e1d5b2306dd24257d4ba261baa42b324c9f4f8ca3a1d30e0f920c4f4a5f99ba'), size_on_disk=1342511, blob_last_accessed=1756926326.2713532, blob_last_modified=1756926326.9043505), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417882/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ac2bd9710196abc4469d4d78ccbde6ddc67e9ef39ade7d50c32b0d951866e653'), size_on_disk=10076041, blob_last_accessed=1756926352.116241, blob_last_modified=1756926353.223236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417798/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0ac62fa7573f4b3c756fd9c280be4b97ec0c7c756e8c76307d4fab56d29e1c08'), size_on_disk=10509245, blob_last_accessed=1756926336.5193086, blob_last_modified=1756926337.800303), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417926/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/303d44a38fbb646cf211ce1bd03fe2d24bb004f385c63d05ab8e707c5b283f0c'), size_on_disk=10766909, blob_last_accessed=1756926360.6072042, blob_last_modified=1756926361.6751995), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417838/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0dcd4dc268902c8e0845a1ee81938a81a00412014d5b198a41a523cc3450dfc4'), size_on_disk=10178872, blob_last_accessed=1756926344.5452738, blob_last_modified=1756926345.3902702), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417879/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6714b3f5155dd540962a7541625f45e14e6a93b4c01e913790b70818e50c821b'), size_on_disk=8601872, blob_last_accessed=1756926351.4612439, blob_last_modified=1756926352.8182378), CachedFileInfo(file_name='GSE209631_metadata.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE209631_metadata.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fe31b19357a717d3d51d1d201620d37fca79b1c8'), size_on_disk=61, blob_last_accessed=1756926300.2214668, blob_last_modified=1756926300.2814665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417773/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d7980069a740f581dbcfd9416372452203d636d8ad4c9352c3b2d6483cd27379'), size_on_disk=5178664, blob_last_accessed=1756926332.1683276, blob_last_modified=1756926333.411322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380984/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a15ab5e5a0ead07dc2a530a1e4abb714027a95ce090c7a0eda5ec2bb714d903f'), size_on_disk=8070704, blob_last_accessed=1756926373.2251494, blob_last_modified=1756926374.1601455), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417794/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f15be566c029c52cf46f6258e589529afb3a002f010710a54d04dd6151f8b684'), size_on_disk=10205023, blob_last_accessed=1756926335.9063113, blob_last_modified=1756926337.151306), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417851/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2d8a571fae50598143744a1f13d40cec2ea8c2198e87aae17f96d7c8a00460d0'), size_on_disk=11051527, blob_last_accessed=1756926346.6472647, blob_last_modified=1756926347.6582603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417635/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d2d1c241467bd5c55cf059f793b4236c99d48e59bec853d1052ff6c644e50d1b'), size_on_disk=3044373, blob_last_accessed=1756926307.2014363, blob_last_modified=1756926308.391431), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417676/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/53c35af59283469a22148b2841cb8574faf80d59162a33d269560283c9228af2'), size_on_disk=12006227, blob_last_accessed=1756926315.8083987, blob_last_modified=1756926316.8563943), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381024/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5182750a0c8af8253cc98ecad8715519463d5c18adbe067488dfef91198c0d80'), size_on_disk=603678, blob_last_accessed=1756926378.7151258, blob_last_modified=1756926379.128124), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381043/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f6ded8e6496dcfdd499edc382d19da2071f25daebbb250b375b9a2706e9d8211'), size_on_disk=9139336, blob_last_accessed=1756926775.6057615, blob_last_modified=1756926777.1057532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417782/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d6624acaa7c4ec21ae02d6d1322b8669a148308095e714b41bff868483fe2fa9'), size_on_disk=11742567, blob_last_accessed=1756926333.5273216, blob_last_modified=1756926334.824316), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417724/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e5beee12039dfe6b4dae180a31ac0897dea725754554db43e2df9f99da94db58'), size_on_disk=4535402, blob_last_accessed=1756926324.69836, blob_last_modified=1756926325.9703546), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381069/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aa8f6186814bf973f8b4448a0c4b692c31594e03905c75d5691b982d6ccde97d'), size_on_disk=13705106, blob_last_accessed=1756926780.5807338, blob_last_modified=1756926782.319724), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417756/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ede027f2c13a1e6669becf6194ace85af5800d5fe77db16ec47bfb3abaa735d3'), size_on_disk=8620421, blob_last_accessed=1756926329.968337, blob_last_modified=1756926331.0303326), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381014/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5c5106dfb24bcfb1436aeb87b8218df873417ca2809aed71ca58e9b48ffacd29'), size_on_disk=1425306, blob_last_accessed=1756926377.5811305, blob_last_modified=1756926378.2571278), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380962/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/72be01aa42db736ffac8236c23c16edea00bdf5da88c3114fc94d9eb3daa4ee9'), size_on_disk=1460258, blob_last_accessed=1756926370.2361624, blob_last_modified=1756926370.6811604), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fb5958ec4e10f5c7e19ebc55b24f26012e6d8ccf'), size_on_disk=8004, blob_last_accessed=1756926300.2594666, blob_last_modified=1756926300.3214662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417805/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/661f37ed294c22f6c3b02d5c1ea25794f5e33090c246185e891ee82153e2e5f2'), size_on_disk=3071166, blob_last_accessed=1756926337.8643029, blob_last_modified=1756926339.3292964), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417752/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/efc9f1ecf4a7b06b23da46fb8c7d1879b67a9d6603ed753d09e2028396ec05c5'), size_on_disk=3420764, blob_last_accessed=1756926329.26534, blob_last_modified=1756926330.0613368), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417917/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2118491b5ba6b4af1c0d394eec3139b0258cf3a4e42b4d1a02ab177247852081'), size_on_disk=14019020, blob_last_accessed=1756926358.4622135, blob_last_modified=1756926359.8632073), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417694/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/058b507f75cb01bb5912e14e7bbb4e25a1de945e578323bce9f920f31a9ced82'), size_on_disk=11980395, blob_last_accessed=1756926320.09838, blob_last_modified=1756926321.5623736), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417631/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b4b8c985528935ec46c6c7e78482af1ed26238137fc63b2196c6631e2d3796ab'), size_on_disk=27651513, blob_last_accessed=1756926306.2814403, blob_last_modified=1756926309.1994276), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380995/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e0cae334f1cb71be0be1e8ceb5fc6ad23d22e4db20cb64b1314c3d080f2bb5f7'), size_on_disk=6315860, blob_last_accessed=1756926374.931142, blob_last_modified=1756926375.7271385), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417909/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c5de46a74c16aee32dfb2be89a50dceaa5704c0c550076dcec483d1c21d983af'), size_on_disk=5969725, blob_last_accessed=1756926357.3362184, blob_last_modified=1756926358.5162132), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417936/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8ba89583b8c822b64968df9eca8273d12270429f0f820d6027f13c66504c383d'), size_on_disk=4552381, blob_last_accessed=1756926362.205197, blob_last_modified=1756926363.171193), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417944/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/01e0e606e4e8b2203e6497ffc8c231bce8672a43d83098ba838c8edfdb052c27'), size_on_disk=4563261, blob_last_accessed=1756926363.89319, blob_last_modified=1756926364.6951864), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417672/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f0741c46f1460c30d4d9b88ca1f7c0ca77a040cf25a34d82fab4c777a921ac26'), size_on_disk=18268758, blob_last_accessed=1756926314.9764023, blob_last_modified=1756926316.5143957), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f00bc431128183affc8964f9610f3e9486a043bcea5b31b3ebbcbd9b0070a448'), size_on_disk=6096918, blob_last_accessed=1756926377.3431315, blob_last_modified=1756926378.2571278), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417659/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/20f21bb1ba86fdbee1918776793b1ff2275be49933c72eaba90b8be8582f7692'), size_on_disk=15311232, blob_last_accessed=1756926312.2364142, blob_last_modified=1756926313.8474073), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417722/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f29055ed874a3a40735526808a6739884efecd8d7c5e7e9eb1fcaa8e495ade3'), size_on_disk=345818, blob_last_accessed=1756926324.5753605, blob_last_modified=1756926325.2483575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417759/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b2dad4cf4b03c846adf9d5229aeab340e58034c751bc68b37bb0c8402a7c071b'), size_on_disk=1372312, blob_last_accessed=1756926330.1243365, blob_last_modified=1756926331.1623318), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417609/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e3d8451242fe9ecf9f82e2ac1a406144840a6182178228e6e1fa67e6a04a318a'), size_on_disk=3176651, blob_last_accessed=1756926300.651465, blob_last_modified=1756926302.0864584), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417876/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0713329434bfbd55f1998400d9d44c1d8c7fec6cb1b646c0fdb926f1046f9b47'), size_on_disk=11246388, blob_last_accessed=1756926350.971246, blob_last_modified=1756926352.0472412), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417922/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fba17c65fa210922e57c533f5146b4237677149a5022fb836c1cd52dc89855b2'), size_on_disk=10194446, blob_last_accessed=1756926359.5812085, blob_last_modified=1756926360.610204), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380953/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5b3410430d33f3123b935982be90037f2d0a8e657f6b0b70a7042846d5ddd1b0'), size_on_disk=1192961, blob_last_accessed=1756926368.8401685, blob_last_modified=1756926369.627165), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380974/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ea9eb19e91fedadd9e46d5d7dc9169995065af5b6b8e823f1667117be9d737d0'), size_on_disk=8889742, blob_last_accessed=1756926371.5481567, blob_last_modified=1756926372.2801535), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380978/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a1d811dc3a4bfd44683ddf8bd0401a71d4941497c5cac8d8040a597fc5d26587'), size_on_disk=5902739, blob_last_accessed=1756926372.160154, blob_last_modified=1756926373.09715), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381065/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/99cc73bc4c0908da04c01c7c59eff995a478ffc023c31fb970c7bb9789b2d70b'), size_on_disk=14673323, blob_last_accessed=1756926780.0087368, blob_last_modified=1756926781.5847282), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417932/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f040d4f594fe69143a7c3f04f0498be2a30fb9b5e78d42ec464fee65efcc42dd'), size_on_disk=9231914, blob_last_accessed=1756926361.4262006, blob_last_modified=1756926362.5771956), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417623/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/562a43ff381a2ca177fb1cc3dbe47fc2cf7cade495bad511ca7ed75aefca7a98'), size_on_disk=19049552, blob_last_accessed=1756926302.696456, blob_last_modified=1756926305.4934437), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417815/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ee8a37cd03dfe154008718f2a0f7fd95b3fdf468f99b1fc73ca515433fa45930'), size_on_disk=6563669, blob_last_accessed=1756926339.6062953, blob_last_modified=1756926341.8062856), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417901/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/69432386b347f5b364c02d270b38891358fa2068465354d1ada84d5450ea5bf0'), size_on_disk=9484337, blob_last_accessed=1756926355.6292257, blob_last_modified=1756926357.0582194), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417921/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/05fb4ff0001b88b711eee9f29ae30642d940667f7166121c9782f464057880f5'), size_on_disk=7891276, blob_last_accessed=1756926359.3782094, blob_last_modified=1756926361.2442014), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417762/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c8b34d14be6dd44af03432d758f705cf81e86a46831c6deb4b0944248709630a'), size_on_disk=3024339, blob_last_accessed=1756926330.943333, blob_last_modified=1756926331.59933), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380966/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f27a802d18bf78781f3d168370e0c0e241a130d04a5871d7416442be3ce3e40f'), size_on_disk=4241927, blob_last_accessed=1756926370.3881617, blob_last_modified=1756926371.272158), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417685/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/32edfbf9226b7ef1060ebe3e6986a1803d60408ccda969528cf4a59c5785c722'), size_on_disk=16967633, blob_last_accessed=1756926317.9163895, blob_last_modified=1756926319.5263824), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417768/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8bb8e088eb5123049e418c868ab42d94fb48946f934a8313f4cf283e3bfa09ba'), size_on_disk=4124375, blob_last_accessed=1756926331.3153312, blob_last_modified=1756926332.2313273), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380934/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d264f2a91c87696f6a07f573f1d921c91e1d53e3afca59610ba4703b3683b617'), size_on_disk=4601537, blob_last_accessed=1756926366.1091802, blob_last_modified=1756926366.9191768), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417920/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/00a869703f03e3df3f0f51a7d93d52dd7eac5174ed2a1b752207460e71c7b41a'), size_on_disk=14880236, blob_last_accessed=1756926359.1242106, blob_last_modified=1756926360.409205), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380930/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/734e9bc8119c7a507e9cf84f9926b1f9d06ccc8b0062a9b4dc36631f6f1bc6d1'), size_on_disk=4604638, blob_last_accessed=1756926365.1301844, blob_last_modified=1756926365.9251812), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417745/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ab84f2e8e76311cfe45a4b525b239186e835cab37f1e5222fdc35c286064e5e'), size_on_disk=1270105, blob_last_accessed=1756926328.596343, blob_last_modified=1756926329.1233408), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417716/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cd98cf97346b62e4f1efcace598c9c273d9ad9b8d8692e35f50b11d46f7ed8a6'), size_on_disk=5100666, blob_last_accessed=1756926323.8513637, blob_last_modified=1756926324.8793592), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417820/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9d36f1c17151e3203186142abff734c0182377d5287b3f27d00917608501abb3'), size_on_disk=8116588, blob_last_accessed=1756926340.9562893, blob_last_modified=1756926342.0902843), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380939/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dc6382d9dba7855504a6c7fa2a4adc44f564e25ff5c54eedd082c3cbe0a520a0'), size_on_disk=9235430, blob_last_accessed=1756926366.630178, blob_last_modified=1756926367.5861738), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417774/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ad00f794fb29d2a609375d63b09170f396f055ae392cd05a31d5f8fb5ede7a33'), size_on_disk=2914273, blob_last_accessed=1756926332.1763275, blob_last_modified=1756926333.217323), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381070/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/32c8f4c4efdbe6e137add61b0e31c27ae6a9eb5e8b463e9e4410a27cc51a57d3'), size_on_disk=29864184, blob_last_accessed=1756926780.7487328, blob_last_modified=1756926783.3167186), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417949/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6123bc7bd312e6e37fabadee561c7dacad32d26bada92c9785c783f94fc50a37'), size_on_disk=1320707, blob_last_accessed=1756926364.4151876, blob_last_modified=1756926365.0891848), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380956/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/15047f69259f2cbc2cb08949d07ac6fde505bade6a0e3bb3912b183a48a4b288'), size_on_disk=2282680, blob_last_accessed=1756926369.4771657, blob_last_modified=1756926370.1431627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417910/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/694d86a1e469836a53e1ce5dba0d64b77b8f68590a2034e2a1ef5310457dc7fb'), size_on_disk=4494010, blob_last_accessed=1756926357.3112185, blob_last_modified=1756926358.3942137), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417863/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4aabb9d1a4b105c7669deb0f13cd635ab7433ed1ae02f5785aad014d604f1504'), size_on_disk=6900487, blob_last_accessed=1756926348.4532568, blob_last_modified=1756926349.2742534), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417681/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6e4ea424e9eab4dc3ea304621992e1db158565a8ce30374980b1084a653a0132'), size_on_disk=7689435, blob_last_accessed=1756926316.934394, blob_last_modified=1756926318.231388), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381025/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aea8c4d9f3783191d3e89f4566811f01089f3b0b76b736c5167bda54693b0bc4'), size_on_disk=339954, blob_last_accessed=1756926378.7781255, blob_last_modified=1756926379.125124), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417692/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/82405dc0a21bd225cc6a9e813e62d4dc0522719abf1e677c954a6ec38763b306'), size_on_disk=4873514, blob_last_accessed=1756926319.6253822, blob_last_modified=1756926320.6443777), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380926/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fd708dfb8084d767fde32c52acb58e785083ce7becbd2e160277f31460719346'), size_on_disk=6579161, blob_last_accessed=1756926364.8451858, blob_last_modified=1756926365.5441828), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381015/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/345c83c5ab13d230542683014d3d6073ba5a348e3063fea325843981c6c6e7c1'), size_on_disk=3807314, blob_last_accessed=1756926377.67913, blob_last_modified=1756926378.4971266), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417894/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/12f262a12783317d135ffc5e1d281a3d43b746a82ed8053c15de824b94bd5ef8'), size_on_disk=6449687, blob_last_accessed=1756926354.4772308, blob_last_modified=1756926355.4742265), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417919/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f5504428a85399cb212c74235abf9cec068dba03136a87ea79eae6d63a3cf430'), size_on_disk=7011211, blob_last_accessed=1756926358.6892123, blob_last_modified=1756926359.8312075), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417810/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b807197acb33acdd1b70eddedd59dc4d831e271d231d6c9bb6f1281fb4d5813f'), size_on_disk=9808564, blob_last_accessed=1756926338.8082986, blob_last_modified=1756926339.7202947), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417726/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/739e474fc54ffee0c7e6300d3abb1f83d33d48c78d137ed848f837fa186b9308'), size_on_disk=2430088, blob_last_accessed=1756926325.0663583, blob_last_modified=1756926325.6803558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381056/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/382194ca9758ddc18f059a649b13c5a21ead3a7f1fa5e012065315047a037a58'), size_on_disk=2351268, blob_last_accessed=1756926778.362746, blob_last_modified=1756926778.9827425), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417738/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c3d82a351044aa8cd5d43979369088ccf2f563022d4f7aa3683ef20923202770'), size_on_disk=19772535, blob_last_accessed=1756926326.3913527, blob_last_modified=1756926328.6913426), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417859/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e4c5915df6cb5bb450e1948d8c43bbe031d09ec345688ab51f2d8ea60f3be09f'), size_on_disk=12709203, blob_last_accessed=1756926347.8872592, blob_last_modified=1756926348.9972544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380967/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3abcaaa4db306d580dc27882172ced53d591b8aa69a1805bd2a9eea0448ff941'), size_on_disk=1245882, blob_last_accessed=1756926370.6731606, blob_last_modified=1756926371.1021585), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417839/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a04fc093d2a4fe873de135165bd62a38a1cb9b29860881ed973022ff128dc6d8'), size_on_disk=6789775, blob_last_accessed=1756926344.5502737, blob_last_modified=1756926346.4242656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417931/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7e70415ed0dfb1eb42e46789c905ccf2b593dc192d907d939d87acd59cd963e1'), size_on_disk=4991063, blob_last_accessed=1756926361.306201, blob_last_modified=1756926363.1161933), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4c942b91ac018330aeb4da512a3e47ae4531a434b6e9933cf3e2bb476a77f9ca'), size_on_disk=6312758, blob_last_accessed=1756926366.1041803, blob_last_modified=1756926367.0141764), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380940/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4f59a7f9287c427bed27910f08a041e460eff2490c21e2fbdaa5cc9348f5921e'), size_on_disk=6062382, blob_last_accessed=1756926366.8141773, blob_last_modified=1756926368.3051708), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417767/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8d0afed33cd85298c709656da4b210e3fe87aa4d6822208d744200816d59b95b'), size_on_disk=3739401, blob_last_accessed=1756926331.2333317, blob_last_modified=1756926331.8623288), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ad0e80a28e4cd2027893adee5afceab851576ed2637c1a8e94d0af2adae6ace1'), size_on_disk=5088900, blob_last_accessed=1756926375.41114, blob_last_modified=1756926376.3821359), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417628/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/291f0e4d2d0fa1656de1583bd9e131fd4d2fc1934d0bb70a6774f7e7f90e62ae'), size_on_disk=15444530, blob_last_accessed=1756926305.1314452, blob_last_modified=1756926307.1334364), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4331f53e52f628f99a9695fa70339b08eb69a011cc8a3cbdb08dfa8e6c5d3ddd'), size_on_disk=10779086, blob_last_accessed=1756926376.8381338, blob_last_modified=1756926377.9141293), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381021/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3e80794742d10b6d120e70c4e4f1d7f77123ddf8aa18499f686b4abc8eadd325'), size_on_disk=1524611, blob_last_accessed=1756926378.410127, blob_last_modified=1756926378.897125), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417923/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a32971eb62fe5c4c08298b1bdd97a916d8ef8f5898aee42aaf3fbaa8069e92d1'), size_on_disk=10004280, blob_last_accessed=1756926359.919207, blob_last_modified=1756926362.0851977), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417891/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cabef83ee373e37d528b127d813f1535faede4b98c5524d83e9906df83558b07'), size_on_disk=12596609, blob_last_accessed=1756926353.9052331, blob_last_modified=1756926355.2992272), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417644/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0c788a7ba70689a796af823df5cc14504dfdc854617952459367364c4498cdeb'), size_on_disk=949200, blob_last_accessed=1756926309.758425, blob_last_modified=1756926310.6444213), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417885/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6d5faeda491268a8ec88393883d743aac6cd14d4837698164ca32ca1052da36f'), size_on_disk=7668243, blob_last_accessed=1756926352.806238, blob_last_modified=1756926354.1422322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417828/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eb4987a8ef94e1691c74c1d828d7d9313da4691b577118a73524def2a90c6d0b'), size_on_disk=4827634, blob_last_accessed=1756926342.3472834, blob_last_modified=1756926343.2782793), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380937/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e31f54581c4a4a47033065033f3f9266913edad654543dd07b20d0ffe54847dd'), size_on_disk=2691574, blob_last_accessed=1756926366.2071798, blob_last_modified=1756926367.1951756), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417916/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2abad8555b89fbb5009e8f2a95e553a5ce579d1841ae4e71eec7b137f3282ef1'), size_on_disk=9236466, blob_last_accessed=1756926358.371214, blob_last_modified=1756926359.0582108), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417712/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/59cbfa83b9abe13f60d6a80b35afe857e3ffd761366133f60505312a2b5a72e2'), size_on_disk=641243, blob_last_accessed=1756926323.2163665, blob_last_modified=1756926323.7173643), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380979/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef743a2a6a8fe44b62963b981ef0429676e7854243ab07d450d9c3ab569c7e8e'), size_on_disk=3735979, blob_last_accessed=1756926372.2201538, blob_last_modified=1756926373.6791475), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417682/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/83b3100cfd1dc6918855c4a711ff607b36f40971655733855a7df353b4d5fca5'), size_on_disk=14037100, blob_last_accessed=1756926317.1663928, blob_last_modified=1756926319.1013844), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417713/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c5d75f4a32cadceee844da0c5ce703483f575fb927990a1615ee37205137d986'), size_on_disk=524444, blob_last_accessed=1756926323.533365, blob_last_modified=1756926324.045363), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381053/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6ca1dc55841bbb6d8d8a83d1a87d00570c1eea7cf1963db8cb04ddd427711fb6'), size_on_disk=10277654, blob_last_accessed=1756926777.9197485, blob_last_modified=1756926779.9187374), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417711/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e30dbc09cd2dfb4722d60d0833123542e9c5096f70b97867cd15d466aa7b927a'), size_on_disk=96859, blob_last_accessed=1756926323.1863666, blob_last_modified=1756926323.7823641), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417686/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6a42f24b250baf0d88696bc123f01494a400b3f03f2ff2a1fc828f9295721a16'), size_on_disk=12070748, blob_last_accessed=1756926318.2063882, blob_last_modified=1756926319.627382), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381042/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/80a0c18e8d146043e9c95c6a86552d28e28267a489b3e5c6f2a11319cf47b877'), size_on_disk=4477936, blob_last_accessed=1756926775.660761, blob_last_modified=1756926776.9177542), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417854/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d52c8b14e197b179a9b43fbc89bb3205d09d458161ee49ac15a62b7c1f7a39fe'), size_on_disk=6203801, blob_last_accessed=1756926347.311262, blob_last_modified=1756926348.387257), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c103eef2a9834296fb1328b7012b5b9184c5a3f131817dfca257843e863d34b2'), size_on_disk=9829417, blob_last_accessed=1756926362.1531975, blob_last_modified=1756926363.3531923), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417633/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0b38824aeb2fac57eca256e3652539deb4bc221eeb5afc3701a997885f9ad9d0'), size_on_disk=19311587, blob_last_accessed=1756926306.8324378, blob_last_modified=1756926308.847429), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417895/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2daca9b311da1afa40f578c6df7dc4b0039dd4857663c98cd87c665a547b7144'), size_on_disk=11915424, blob_last_accessed=1756926354.7232296, blob_last_modified=1756926355.8262248), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417832/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/268a3b7a91ccc3f3909042a711454c336b1887705de99442bda022bcbc26a01e'), size_on_disk=7655304, blob_last_accessed=1756926343.2332795, blob_last_modified=1756926344.3042748), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417862/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cea0cfbd611f8c6482ec33f8ea32f304a481d1c1225d3dacea432dd359fc976b'), size_on_disk=5244392, blob_last_accessed=1756926348.381257, blob_last_modified=1756926349.6162517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380933/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/07c22e11c56ce5ecb6c22c910c3d3a0d9b54fd341bef0513b4bf42a9c53ec6a0'), size_on_disk=10138164, blob_last_accessed=1756926365.8761814, blob_last_modified=1756926366.7471776), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380963/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c0a30240a0a99347e114928a15a24268495fd37480fde6822f503438bdf3ed48'), size_on_disk=579777, blob_last_accessed=1756926370.2681623, blob_last_modified=1756926370.81716), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417679/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bc48687cfd5c03c1a71787981180295dd3a27cf4d2c46bd26bb68cdf8b35925a'), size_on_disk=17346576, blob_last_accessed=1756926316.1203973, blob_last_modified=1756926317.84439), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381046/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a96c1819b1010c40fc4d92418e407cc5b5f48f9e24883d4b4cdf3e02d23b688a'), size_on_disk=3071378, blob_last_accessed=1756926776.6277559, blob_last_modified=1756926777.855749), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417721/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5457a24eab864fb369ae0e6fbdb302876e2a6c65ae83444f18bc01ad08ecfc0c'), size_on_disk=2073045, blob_last_accessed=1756926324.3243616, blob_last_modified=1756926324.9983587), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3fe2828766efd936f02efae2da3e068ef9b28ea374e594e09850e3e8ff0d8164'), size_on_disk=4421540, blob_last_accessed=1756926375.7961383, blob_last_modified=1756926376.7581341), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381061/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d44d57e9c03bb6e6e750809eea88657c2960ed251b8acaa1352a07f4cb346a82'), size_on_disk=3789431, blob_last_accessed=1756926779.0527422, blob_last_modified=1756926779.8977375), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417866/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b0424cd7567723443472cb266e090a30e2b64ee6b09bb1b94d8dcfdb9f0b0c43'), size_on_disk=9685580, blob_last_accessed=1756926349.109254, blob_last_modified=1756926350.493248), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417629/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/29c9c86be94792d797005b106ab00d066634bea44fbf17e81738a00cbffa264b'), size_on_disk=22750780, blob_last_accessed=1756926305.5594435, blob_last_modified=1756926307.5804346), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380952/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3811d6e6194d8b88c84fc0931cba844da23677e76e1e9a25e2890df968a7e529'), size_on_disk=1003172, blob_last_accessed=1756926368.8351684, blob_last_modified=1756926369.4641657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417948/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d43ea802a452aa9d57004381e1688222e61bd313b107077c1c663555547d1d44'), size_on_disk=1748124, blob_last_accessed=1756926364.3661878, blob_last_modified=1756926365.038185), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417709/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/395b1dd171ab9759e06a22580672bef65aac2acd7d548e38098d207c80071d89'), size_on_disk=717131, blob_last_accessed=1756926322.9983675, blob_last_modified=1756926323.6373646), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381022/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/980ed07a51dfad581fc2f7bce44031c92445822ba7e994cbfc9b5508cdf97c85'), size_on_disk=2103246, blob_last_accessed=1756926378.5151265, blob_last_modified=1756926378.9441247), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417803/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1fd8b542fb1b0e18bc5346c50ad85da440ff6190a987467ec6876ed3ec51a4e8'), size_on_disk=4928668, blob_last_accessed=1756926337.5433042, blob_last_modified=1756926338.7182992), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417814/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9f70b1469a6d0ad8758aa7a44e754e07ccc401906f8af403a72075d763919535'), size_on_disk=7618977, blob_last_accessed=1756926339.5142956, blob_last_modified=1756926341.0872889), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417842/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c763016dcbf1b53df840e3ea3c1b45234e2d9e797114000efaf692e2c394d487'), size_on_disk=9967694, blob_last_accessed=1756926345.1472712, blob_last_modified=1756926347.1812623), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380983/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eec33f1158aa017b073fcb16d6400cd800eb79e8c0b78b5e2f831d7413e4a736'), size_on_disk=4478939, blob_last_accessed=1756926372.869151, blob_last_modified=1756926373.6721475), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381052/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/652334151cb539b888d9cfcb60d9711d80bd74ec65cfa3799de10b2970ab9c09'), size_on_disk=5788579, blob_last_accessed=1756926777.7767494, blob_last_modified=1756926779.283741), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/059cc8a8b1f7918ab1b6499d3d9843100b3b37fe6dcd57dfe9ae512c6030cad6'), size_on_disk=2109635, blob_last_accessed=1756926376.7021344, blob_last_modified=1756926377.469131), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417858/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2d7539fcb6297592fe3755ccc88e122441925fbaf9f5600d4daa88639630fd8b'), size_on_disk=1021831, blob_last_accessed=1756926347.8042595, blob_last_modified=1756926348.5072565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381055/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0aad0d3724742845da7f0afae0c18ab5c992bec76c02e0ffda057f00d0894d35'), size_on_disk=3355008, blob_last_accessed=1756926778.3377461, blob_last_modified=1756926779.263741), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380957/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5a2362d35ac90fb1926051e6313886a649074b0022b128741a22a5ee24bb095d'), size_on_disk=5388789, blob_last_accessed=1756926369.5311654, blob_last_modified=1756926370.8571596), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417661/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c6effb515a20066646b7863cb86e9dc012e14187e432ba7458cd6af18662bfd6'), size_on_disk=22122748, blob_last_accessed=1756926312.9054115, blob_last_modified=1756926315.6813993), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380936/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5a931bc1181001314c3822051c493ba7a8b87b5bb26b8866783612f2bcf49c62'), size_on_disk=3467608, blob_last_accessed=1756926366.19118, blob_last_modified=1756926367.2261755), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417670/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5e36b63ef1fbb55f821ed2c3d61f0d0bf86cf00846e86ff9dbe6f4d9597f9dc8'), size_on_disk=10881106, blob_last_accessed=1756926314.7704034, blob_last_modified=1756926315.980398), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417750/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aedd94fe4b42547108a807242052397b6dfbfda0eb4e56a9f2e267b312251147'), size_on_disk=1531584, blob_last_accessed=1756926329.1973405, blob_last_modified=1756926329.9403372), CachedFileInfo(file_name='genome_map.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b8f0ed936cb43c4649126e9b7596cef0ab626a2d'), size_on_disk=142786, blob_last_accessed=1756926300.410466, blob_last_modified=1756926300.4954655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417843/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6569e23f0eb660e9f7111b9cdd69e7ae797ba265647ee44e4cbf2d8babdeca9e'), size_on_disk=2706587, blob_last_accessed=1756926345.3402703, blob_last_modified=1756926346.125267), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417918/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9f1cf3832969364b6cee1c817d3583a554b765bdbc1ff3f5b29692b7ea98ee7e'), size_on_disk=6936136, blob_last_accessed=1756926358.5802128, blob_last_modified=1756926360.9132028), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417765/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bd2b5a1b1593db002ea9c159d4c40d1d8addddbf124207a7b32b0bb43e2e4d72'), size_on_disk=2696437, blob_last_accessed=1756926331.2103317, blob_last_modified=1756926332.046328), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380985/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2ec986412ef2a9e6e59f4ea3dc278b27ab3975b5025d00bd1cd38fe1867b0fea'), size_on_disk=7208882, blob_last_accessed=1756926373.2251494, blob_last_modified=1756926373.9591463), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417826/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3532f474125bbdc0445e46fe8be83801ca9c079afb1dedd7cd95e25994c50d17'), size_on_disk=11492099, blob_last_accessed=1756926341.9132853, blob_last_modified=1756926343.2502794), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417818/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3b30217b127ff88f165762c5054c392d9489b9007ba7ca8b86f18b44f9988e4d'), size_on_disk=8388230, blob_last_accessed=1756926340.2372925, blob_last_modified=1756926341.5632868), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417618/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d2ce91297551bc97b4fca46007f80c8846e5f9e9c78d9c1d7ded02fc31b3ef38'), size_on_disk=9239111, blob_last_accessed=1756926302.3654573, blob_last_modified=1756926304.3714485), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417822/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62cbbb0172158a2c0776000136424032a794757ca9e9f971e5f445deb20e8579'), size_on_disk=6040431, blob_last_accessed=1756926341.3882875, blob_last_modified=1756926342.8302813), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417649/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6baf4bcd82d9fe55b8dcdc7602c0d39d20954675ef4c5484331ff25dac8ad3f4'), size_on_disk=2672976, blob_last_accessed=1756926310.8234205, blob_last_modified=1756926311.4704177), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417700/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1240fbb2497074bb7ff98b1bc6399b8a8ad5d320246285abfb6738e27e2bbdeb'), size_on_disk=19312447, blob_last_accessed=1756926321.6763732, blob_last_modified=1756926322.8383682), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417611/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c82d882f6538d65316dd01e26a82af13e41717952c79e20dae6c3f1d9724ab60'), size_on_disk=3720891, blob_last_accessed=1756926301.4614613, blob_last_modified=1756926302.2704577), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417845/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ca2b89837de406b10d72533a3e3d5457b3512b704f7cea395fb0e1b88828393e'), size_on_disk=4648586, blob_last_accessed=1756926345.6932688, blob_last_modified=1756926346.5862648), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381030/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e5baa8386faa327b26d27d6c8e6b265dddac1d55565e81d724a1980b93d871a3'), size_on_disk=1867415, blob_last_accessed=1756926379.2021236, blob_last_modified=1756926379.6681216), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417769/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e640018be0b2de04e0ed77c7586c413990876e91348531f43511c20da5fe279a'), size_on_disk=11568261, blob_last_accessed=1756926331.59533, blob_last_modified=1756926333.0913236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417703/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e82b21d919b84770e2d141eaea09aff82ef57c3ff3dbd15c588c1b8cff212c9e'), size_on_disk=1462841, blob_last_accessed=1756926322.0533717, blob_last_modified=1756926322.898368), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417678/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/825d9bcd10c008b68cc84e92f20608c0d52770ac028bbc4a9219f1cd210ab8c4'), size_on_disk=29320273, blob_last_accessed=1756926316.0503976, blob_last_modified=1756926318.3993874), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417746/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/432442dbdd5578acedcbf6eddc14cfe0d460facc7b0dea857225fbd4e6a4242c'), size_on_disk=2726090, blob_last_accessed=1756926328.6353428, blob_last_modified=1756926329.1983404), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417648/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fb706eb700e8fc003822b897accd3501360d737fd1a9ffcf26c3e9d9b25a5e35'), size_on_disk=1285054, blob_last_accessed=1756926310.3104227, blob_last_modified=1756926311.0174196), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417857/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/64f710e0b8a62bb6eddf3508f133b6b82af8bd21dd07f2319af6d07981085b1e'), size_on_disk=11109477, blob_last_accessed=1756926347.471261, blob_last_modified=1756926349.6202517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381071/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cf15be48b47b6c369c6a4290b783b9b22b01d6cb89d04c738c275241548b811b'), size_on_disk=2543373, blob_last_accessed=1756926781.2297301, blob_last_modified=1756926782.309724), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380991/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dd86db1779581d5853d4d6a0fa2aeacdb00f92c832683ad788743f17db625db4'), size_on_disk=2640507, blob_last_accessed=1756926374.030146, blob_last_modified=1756926374.8661423), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417607/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/623d085888c300fba1d05a9018ec3d4c31912f2b91a872ef34feda3322889b70'), size_on_disk=6809356, blob_last_accessed=1756926300.611465, blob_last_modified=1756926301.75846), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417831/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/53d810292e5a513a8ab543fe18783f8d493cedbadc7c97400dc103a4675954df'), size_on_disk=6767934, blob_last_accessed=1756926343.12528, blob_last_modified=1756926344.4612741), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381000/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/503f854145f3a220aa936072c3e66410d34914fef948992399a4addbfc977186'), size_on_disk=3904727, blob_last_accessed=1756926375.3401403, blob_last_modified=1756926376.5601351), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417912/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4451877e599fa65e1f00f67b5fcd41fe74719d8bcb19ddacac419fb347c77cf5'), size_on_disk=4943909, blob_last_accessed=1756926357.7152166, blob_last_modified=1756926358.6302128), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417645/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/761eba472193b23cbf7735946e08aa1c4db5b1abd5125f1aad22ddabbf872f04'), size_on_disk=2080044, blob_last_accessed=1756926309.765425, blob_last_modified=1756926311.5084174), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417913/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/35c1a160b767f9f5870216ed33a1dc24804690772a5baee58f623c6dc0632127'), size_on_disk=9135350, blob_last_accessed=1756926357.9462156, blob_last_modified=1756926359.3142097), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8168980e59c50149bfb9866a1e8855c235f2882b988df99331c9247f65e9813e'), size_on_disk=2289686, blob_last_accessed=1756926376.4721353, blob_last_modified=1756926377.2151322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380990/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4e04122d2830db35f2b82b29efaca846fe10d7638c88c3e4d3cf78bf10cf245a'), size_on_disk=2093813, blob_last_accessed=1756926373.9261465, blob_last_modified=1756926375.1611412), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417797/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/12ee278356fa75b0ad555fe06376c78b71630324b7b89aa28abe960c187d256f'), size_on_disk=14391201, blob_last_accessed=1756926336.3743093, blob_last_modified=1756926338.322301), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417816/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/86256a1b98c6d44750fd21ac9ea099262f1f0ec03d182b65741d669fb80f289e'), size_on_disk=10618044, blob_last_accessed=1756926339.7852945, blob_last_modified=1756926341.8372855), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417701/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a0dd9587c7525a610a3794cf41f6db24a6ba22a12ed852dc27381a68db3e8fca'), size_on_disk=8781514, blob_last_accessed=1756926321.6273735, blob_last_modified=1756926323.1263669), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417662/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/90008c75533e5a395bf92b2a4382cfb8d6c27af4921615cac22bc1965375efb5'), size_on_disk=12701661, blob_last_accessed=1756926313.0204108, blob_last_modified=1756926314.158406), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417813/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f7d5239d0d39e171aad1e84964f8cdc3c7a325fe8c38b6829e114cb3997e658'), size_on_disk=10238322, blob_last_accessed=1756926339.4142962, blob_last_modified=1756926340.8722897), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380982/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d1e44b0b82ea22fb6c513ce02b49facf5f38b64b2baa91b24942fc6858125e37'), size_on_disk=6977161, blob_last_accessed=1756926372.8241513, blob_last_modified=1756926373.4951482), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417865/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/348bfc1685f79e9ec7f559e96b61b8c43628d611378b6c4ead8bffea44415f1d'), size_on_disk=9233047, blob_last_accessed=1756926348.8982549, blob_last_modified=1756926349.9182506), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417696/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2e1bce702520ca07d9037bd4de4d21bb30ef6b7fdf7de9d75ea232acc036a72e'), size_on_disk=31303382, blob_last_accessed=1756926320.7203774, blob_last_modified=1756926777.0657532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417940/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/270559ca1db4605de241e8642461b4d7304d124e4fc624f7d8a8a59867301a4c'), size_on_disk=2328150, blob_last_accessed=1756926363.182193, blob_last_modified=1756926363.9021897), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417689/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e27d1d153e5b135a09ec5db80bb05ace721fd60ac90f0693ac34ca3160fdfbdd'), size_on_disk=5015020, blob_last_accessed=1756926319.2973835, blob_last_modified=1756926321.220375), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381027/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ba9aba2cc5b9bd9e83599ccba5920f028ac9c6936436dac50f45c606f3abd841'), size_on_disk=6537548, blob_last_accessed=1756926379.0151243, blob_last_modified=1756926775.5467618), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380928/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/251ffcb8e06f2ac0db0791065248327a9d1eeef33a07f5c6b25946e04b6c46d8'), size_on_disk=2289693, blob_last_accessed=1756926365.037185, blob_last_modified=1756926366.1961799), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417605/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f689b77eccd8d95d70fb616f4e2aa03a41201aec2a8c3c86849b7b145cb0f796'), size_on_disk=1787545, blob_last_accessed=1756926300.5184655, blob_last_modified=1756926301.74346), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417706/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4f1ea5592a855f3bf5739b0caae1c69c7606276d125c3dfe8e98379d12bedf1b'), size_on_disk=1154142, blob_last_accessed=1756926322.9053679, blob_last_modified=1756926323.5173652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417886/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62fe87e7519370c4ac46e6d9d0abf314a8f92d9a5d6804ebddae0ed095641219'), size_on_disk=7968977, blob_last_accessed=1756926352.9472373, blob_last_modified=1756926353.7992337), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417651/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b95b6be3a7a6a5b4af3bbf15c5a4a6b3893f3030ef6d1dcfe79e6c6554e3d3cb'), size_on_disk=13551797, blob_last_accessed=1756926310.9544199, blob_last_modified=1756926314.6814036), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417864/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/15d259c52e80a2d2d9c20274ef940237c90001d32f2aecb80779335a708222ff'), size_on_disk=3706383, blob_last_accessed=1756926348.5722563, blob_last_modified=1756926350.0962496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417834/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a69c65a4d32399355863fa14d4a1e92ecff2c92498e122e8e77796de16d42948'), size_on_disk=8537233, blob_last_accessed=1756926343.3672788, blob_last_modified=1756926345.2462707), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380964/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3a63df07de544662fb04d5388f34b0c8068efbc19f464b673d8a860a57b5b422'), size_on_disk=956844, blob_last_accessed=1756926370.333162, blob_last_modified=1756926370.8741596), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417650/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c4d103888f768ff3cb0c68f4b747cb8d8aad8058b75174160cfc42b2504407f1'), size_on_disk=1337049, blob_last_accessed=1756926310.89842, blob_last_modified=1756926311.4924176), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417877/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b170370a220db5677b9c6193707d32d1d14b9082ca574b9562c4ac8ea4c72c26'), size_on_disk=10449263, blob_last_accessed=1756926351.0392456, blob_last_modified=1756926352.32624), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417830/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/21e2dbb49f07cb7c2d7277f5bf1a442e216a2991677c3c4ece5c51316795ea77'), size_on_disk=12760016, blob_last_accessed=1756926342.9432807, blob_last_modified=1756926344.1602755), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380954/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/338886924ec42501a99a4b842e18fdbed28200d52daaf43e00ee601a147e0188'), size_on_disk=4903852, blob_last_accessed=1756926369.2841666, blob_last_modified=1756926370.101163), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417925/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8cdb783613764a35fd3a73db89463db6876951d443f5651a9a46877a53917f3f'), size_on_disk=912848, blob_last_accessed=1756926360.409205, blob_last_modified=1756926361.1532018), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417872/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7d71ca12594ab85797fca8c1b88947cb0f3ece93596f1a4c5adc04c11c0abf68'), size_on_disk=8522283, blob_last_accessed=1756926350.1742494, blob_last_modified=1756926351.180245), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417770/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0e7437c90c616b78ccc0963e0388385028b70d66fbe73e0ceb4e30e2d0239816'), size_on_disk=8252889, blob_last_accessed=1756926331.6563299, blob_last_modified=1756926333.0863235), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417868/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/52e3f224a0147264d4ce477d26f4e11ff0745c328eb3e2ac44209dba1c8c55eb'), size_on_disk=5039768, blob_last_accessed=1756926349.6852515, blob_last_modified=1756926350.6082475), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417900/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/45d5a0bbf39e542f22aa4e36186673d1246533dc513f006312a26286496f6ead'), size_on_disk=11235417, blob_last_accessed=1756926355.535226, blob_last_modified=1756926356.6632211), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417848/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eec4ffff5d145c0ee0850d5e68787ab90e6510cffa5803579db44e3a575822dc'), size_on_disk=13556826, blob_last_accessed=1756926346.1882668, blob_last_modified=1756926347.2852619), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417853/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5995033a04f017892b30c025a1527f9b6420bf1e7636c466cdd8d1462cf228c8'), size_on_disk=2186899, blob_last_accessed=1756926346.9012635, blob_last_modified=1756926347.8172596), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380942/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a958d997c1aff07ec07304485ee818280bc7714dd9c485ea73ef7fda81f2fe63'), size_on_disk=11407010, blob_last_accessed=1756926367.109176, blob_last_modified=1756926368.6211693), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417725/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4195b9ed6506437a735fd8061cd1b79211c99169c4319c4f72b801a501f2b0f9'), size_on_disk=2233032, blob_last_accessed=1756926324.958359, blob_last_modified=1756926325.594356), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380932/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f214422d02adb2b0a1037eb424d8574123cf27d8a5cd1418feb5733b0249f60c'), size_on_disk=6251591, blob_last_accessed=1756926365.6181824, blob_last_modified=1756926366.5551784), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417624/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/28c1de2a4bd6b421cd05c3bb211ffde83e4b830127f106ec7be42ad288e26ef6'), size_on_disk=16758887, blob_last_accessed=1756926303.7884512, blob_last_modified=1756926305.6184433), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417753/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a8969c75211310735ad33d5af57327f2babc86276adc7cefcda254cc9fc9baa5'), size_on_disk=6433095, blob_last_accessed=1756926329.577339, blob_last_modified=1756926330.4023352), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417787/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/17f33e06b6bd77c0bbd3141b53fc2d9550b0c54e99b18fe79ddad931e3a283a9'), size_on_disk=12355842, blob_last_accessed=1756926334.5003173, blob_last_modified=1756926336.2953095), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417897/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/309aab637d037638082524649ef7b0828885a98e786c64af01072d68968b7d66'), size_on_disk=7857749, blob_last_accessed=1756926354.8022292, blob_last_modified=1756926355.9902241), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417731/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/39ec5969bc8f3acceb3aa30b72f0c0101785a36da340f3983ad00d8cc80160a4'), size_on_disk=1557745, blob_last_accessed=1756926325.658356, blob_last_modified=1756926326.2043536), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380950/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f508a5da82832b920577592b853da2c2d76ebf0adca587e10b63507f46dc5066'), size_on_disk=3452513, blob_last_accessed=1756926368.7781687, blob_last_modified=1756926369.7081647), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417934/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5816750a355a277e532d46ffa5159f8a2ec1ce66686fb12f07ccab4b4f7b3c98'), size_on_disk=7324082, blob_last_accessed=1756926361.995198, blob_last_modified=1756926363.89619), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417878/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d3d105cc45280a5ea88c2bba754c0c5849d0cec7112a0926e9ed0c946e7f5cfd'), size_on_disk=8838404, blob_last_accessed=1756926351.2492447, blob_last_modified=1756926352.7352383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417806/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2befcad73b2aa8170ceaf8c249ceba45e87037771e86c9bb4bd7addc5815bbec'), size_on_disk=5276603, blob_last_accessed=1756926337.9863024, blob_last_modified=1756926338.8112986), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417702/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/41123195fa339a4ffabf6d686a284c343d0519089a18b828818c92ec43a26eae'), size_on_disk=6213776, blob_last_accessed=1756926322.0263717, blob_last_modified=1756926323.0323672), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417860/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/72e751d246e9b938ce9ab4380e2a1114ecd3c36d5829e3298f391c1edfefae24'), size_on_disk=3996315, blob_last_accessed=1756926347.9312592, blob_last_modified=1756926348.819255), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417615/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3b8b3c4c686238c7c642151bbdcb2ba7796de56c1922e558a1e7f08507dc5abb'), size_on_disk=2866472, blob_last_accessed=1756926301.8314598, blob_last_modified=1756926302.5744565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417856/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3dc4e868f31759c9ac3d43f40097f5ed2f0efb6307e3bc1ccbed70c50a16ee4e'), size_on_disk=6126482, blob_last_accessed=1756926347.3832614, blob_last_modified=1756926348.3162575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417907/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b24926bd36cf33ea9107043b9a47db8239ad6208745c56002513225e8cb80a6e'), size_on_disk=7594380, blob_last_accessed=1756926356.7332208, blob_last_modified=1756926357.882216), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417719/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cc0a365d1d79f0469571216394f0cb211b9096f2a567e0a347a18f0c496bac06'), size_on_disk=2076369, blob_last_accessed=1756926324.010363, blob_last_modified=1756926324.951359), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417792/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/196cd0cf279f6bc85ca0dbeb1f089721f63eb167644d999b45d8cfe9a1f52acf'), size_on_disk=11982413, blob_last_accessed=1756926335.4593132, blob_last_modified=1756926336.651308), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417617/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9ad746ec09ed51ed497d87f3dd64b11916b70e6833ce66141bc8feb6ba73d1d3'), size_on_disk=23082279, blob_last_accessed=1756926302.1754582, blob_last_modified=1756926303.6944516), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417755/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f4ffef985aed69a80417b5179ef9923bd73378a93a97f2dff128b1f7716b4599'), size_on_disk=8437424, blob_last_accessed=1756926329.777338, blob_last_modified=1756926331.2423315), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381057/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4e6c617d22a655d32847df8b5334f4fb7236a15b8bf6e30aaabcbb0a4c55782f'), size_on_disk=17494867, blob_last_accessed=1756926778.5417452, blob_last_modified=1756926780.4997342), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417817/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ccf81aa26021a009a212ef8f8bee71c12f5c8d0a2b97ffdb2c4d86d13c8b8133'), size_on_disk=7521086, blob_last_accessed=1756926340.0802932, blob_last_modified=1756926341.3222878), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417695/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d6ae8d39c2949235dd4d82e7a0e56d9f20c20ba5f1b7a445fbba1f0df29c88b7'), size_on_disk=7710772, blob_last_accessed=1756926320.4913783, blob_last_modified=1756926321.959372), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380971/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0147090dd5a74f558b67151503ff635423a8af661b56d1faf06bb50a59adfb3a'), size_on_disk=1276600, blob_last_accessed=1756926370.9411592, blob_last_modified=1756926371.462157), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417811/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/da92b1c1579a2295da085050305367bd02a47fd52a74d1ec2a9f227b5e808b11'), size_on_disk=6127159, blob_last_accessed=1756926338.8782983, blob_last_modified=1756926340.1172931), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417603/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdfb1fd9b21341104e6423c684b0ae8a52ed1701eb3e232babb98340f953ea5b'), size_on_disk=4460026, blob_last_accessed=1756926300.366466, blob_last_modified=1756926301.2574623), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380959/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4693c1876e880fee1ec277fd5ae9127aafa9f40d41a3d5ed482dfcfa8cef55f1'), size_on_disk=873902, blob_last_accessed=1756926369.7061646, blob_last_modified=1756926370.1731627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381054/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1a9f9c650973937d852d2de54c0d73336d1516184d29e203563fd9bf8d7fefa5'), size_on_disk=3346485, blob_last_accessed=1756926778.1077476, blob_last_modified=1756926778.8637433), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417819/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a1ba6808d672dbc9c58c26364c5cd9ed0b8a3041e6714844846648d3cd1aa050'), size_on_disk=3244517, blob_last_accessed=1756926340.7022905, blob_last_modified=1756926341.6952863), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380927/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/38e67ea2fef57610ff63c0c298a6db65ae1c0a159f2b1e61940f2e65306b8fa9'), size_on_disk=7297320, blob_last_accessed=1756926364.969185, blob_last_modified=1756926365.7781818), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417749/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8460c24102627d611c8918a1620078ee1bdf9929c44cdca863cd0dbb23f9cf1f'), size_on_disk=4938551, blob_last_accessed=1756926328.814342, blob_last_modified=1756926329.7143383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380996/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/09e663ce2a60df5635f4e4ea77d6350e7752a9bb5e81fbdfd818869ac53ca805'), size_on_disk=13309208, blob_last_accessed=1756926375.0511415, blob_last_modified=1756926376.0221374), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417626/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1981a2a1e321acc5c35bbeb9b61a12636c27f28d6e216de20093afdbee9f8689'), size_on_disk=12046611, blob_last_accessed=1756926304.4464483, blob_last_modified=1756926306.2084405), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381049/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c571bcc5f87a79c7cd38b9614ee83b0d5cc742094e17a7990c829394fca193be'), size_on_disk=3753119, blob_last_accessed=1756926777.145753, blob_last_modified=1756926778.2097468), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417604/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e6dd3abdf75462f1f5d16f19383b98a8a4c817dc3b13c7cea2b4bea611c40c62'), size_on_disk=961320, blob_last_accessed=1756926300.390466, blob_last_modified=1756926301.3534617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380977/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/74237b16fd38090785d203d96bae72e1f5ce919d423f793cfb9d3b86623358e6'), size_on_disk=11114601, blob_last_accessed=1756926371.6651561, blob_last_modified=1756926373.12615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417771/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8fe5513ae86b28ecca3d4c70bdb73097f578dd31b4d08a072960b61550f582fa'), size_on_disk=3970195, blob_last_accessed=1756926331.9313285, blob_last_modified=1756926332.8643246), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417733/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/542261ed8bf189205b611485f1a878f28cae5c90e452ba9cad687eacfdfc2814'), size_on_disk=2645461, blob_last_accessed=1756926325.8883548, blob_last_modified=1756926327.3873484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417683/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6131abcd72bd4c6a8627685c4df8a295af50e824f52a5cde156adae7cd694bba'), size_on_disk=15545733, blob_last_accessed=1756926317.382392, blob_last_modified=1756926319.0483847), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417730/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f2554716083cfa1bb54c2098315b92e1b3537eb265da884c00ba7e7d4c050da'), size_on_disk=14980864, blob_last_accessed=1756926325.5143564, blob_last_modified=1756926326.803351), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417760/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0500e5835116a61477f2d98554d0dd97e8d932a6b564f709c40c4884f2b49052'), size_on_disk=2234621, blob_last_accessed=1756926330.2873356, blob_last_modified=1756926330.8273335), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417639/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/43c5fb0a4f049fa5895f8167928d047cc4954aa37db1fc8a9892fe9f99c401bf'), size_on_disk=782319, blob_last_accessed=1756926308.7694294, blob_last_modified=1756926309.6284256), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380943/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/987771cb978dbc949eff5044071149a00223c6771efcc870850e3c8cbe0648c3'), size_on_disk=13397144, blob_last_accessed=1756926367.295175, blob_last_modified=1756926368.7601688), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417727/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/414c702fdf3dd97c536f3237d2c38e0ef5ad8d08bb7c991ee252e16d57b201b6'), size_on_disk=2636905, blob_last_accessed=1756926325.0683584, blob_last_modified=1756926325.8093553), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417616/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0b0f3c9481fd0e254abd544ba803de0c9da9b3a686451975f981d02fc481148d'), size_on_disk=15058562, blob_last_accessed=1756926301.8254597, blob_last_modified=1756926305.0234458), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380948/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6ac219aa7f5be77c21bde60c80ccad59edf825e2a009d6a4b7fc8bd4d8be83d4'), size_on_disk=6046323, blob_last_accessed=1756926368.6691692, blob_last_modified=1756926369.5471654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417790/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f88bed27521de8285bbb92cf95242820116e6fa2c23ab578e6608a6a25e0c04e'), size_on_disk=10836858, blob_last_accessed=1756926335.078315, blob_last_modified=1756926336.1843102), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417889/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/295f9efb0d78e3a116ce4022a7830297570446e2017a3fcd3b3d423c0c20664b'), size_on_disk=9617979, blob_last_accessed=1756926353.3102357, blob_last_modified=1756926354.7322295), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417622/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/28eed3b67a6206b453d8adb775edf2bea32cfd54ba5b37d51787f67280e0317f'), size_on_disk=20411775, blob_last_accessed=1756926302.674456, blob_last_modified=1756926304.8564465), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417840/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7e32bef3c0f7f7520382325aba12f5cf9405c22ebce8554b1d877fe24d2da52b'), size_on_disk=9679209, blob_last_accessed=1756926345.0092719, blob_last_modified=1756926346.0542672), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381026/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e41f2d7a1b084a404e7f5e9e003b27f88665a983ec73859f7eb42cd8bfa2bdd3'), size_on_disk=2857045, blob_last_accessed=1756926378.898125, blob_last_modified=1756926379.7281213), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381011/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/839fea471c67e08e0b68ae6344432c76da559e67c505c3f6120b20db2f1f753e'), size_on_disk=4378212, blob_last_accessed=1756926377.2971318, blob_last_modified=1756926378.0891285), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380941/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9a23f03fee8d60d752bd0bc1e59e9205abea4f3a78e3272b9a03447f076383d5'), size_on_disk=3514837, blob_last_accessed=1756926366.9851766, blob_last_modified=1756926368.7591689), CachedFileInfo(file_name='GSE209631_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE209631_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/be49eef90682fbd55a5e60ada8b4c1c554e1b5a2ec1209996269db020d3bb074'), size_on_disk=4258, blob_last_accessed=1756926300.173467, blob_last_modified=1756926300.594465), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417930/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62f17648a07c7675d6a101e3d12efeae247c742e37eaacf96f95e03dceedefde'), size_on_disk=20574235, blob_last_accessed=1756926361.2242014, blob_last_modified=1756926362.5701957), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417801/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4bbb1b3faac1a4c15eaac8c81260f4f18493d2a082915cbe4c480ce76a94140e'), size_on_disk=4600965, blob_last_accessed=1756926337.090306, blob_last_modified=1756926337.9153025), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380986/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f23d3060df5cb96778fbdf10fff0155c931e1a1e8cc077c7f1582542d54827f5'), size_on_disk=5858046, blob_last_accessed=1756926373.285149, blob_last_modified=1756926374.5971434), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417821/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c29db68a3430cf8cb01716d90a92105387777ed1361992eb9501271c35359cfb'), size_on_disk=6597050, blob_last_accessed=1756926341.1692884, blob_last_modified=1756926342.2702837), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417887/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/58707813ec63af2eb1a72b0c8317b4701b4777df8f828f2e08a0cbb1ccfa534f'), size_on_disk=9080847, blob_last_accessed=1756926353.0712368, blob_last_modified=1756926354.412231), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417871/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e44cb883893b5e30baa752dfbd773b31795f2072a7ec4f473371f435989cad42'), size_on_disk=6264896, blob_last_accessed=1756926349.9852502, blob_last_modified=1756926350.937246), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380968/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/549ef4e39a437adb99efecdc9b05fee3ca4b31c878b99f7a4cf1398f405ca049'), size_on_disk=3575926, blob_last_accessed=1756926370.7441602, blob_last_modified=1756926371.5941565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381067/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/da9c5ab3c0f970f9c2af4c0f5f0e9a95d47769e8096ff6b9d76092becbc8517e'), size_on_disk=9837433, blob_last_accessed=1756926780.2977352, blob_last_modified=1756926781.5777283), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381032/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/335a75b123ae38273f1b1a10f16e9547eec356f68a1d3078926f011d3332e2b5'), size_on_disk=656385, blob_last_accessed=1756926379.6781216, blob_last_modified=1756926775.4077625), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417904/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cf9194917da2f4c6c3f0411256b2604e8c14959373d7431047de2e23cffbb9bf'), size_on_disk=7269877, blob_last_accessed=1756926356.062224, blob_last_modified=1756926358.1762147), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417691/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/48de5a2968610e5ab855522d27f535d40e1973c1835ec32fcfa5bcd91a0795dc'), size_on_disk=14481663, blob_last_accessed=1756926319.447383, blob_last_modified=1756926321.2093751), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417808/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0251ae27bd961d6c962253c555963b42148092cdaa2616a78f6872cf1a4e5e4d'), size_on_disk=12900784, blob_last_accessed=1756926338.3973005, blob_last_modified=1756926339.439296), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417795/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b995a48f28ae3baa145438a39c9fed73fa8689eb31e07ff02a3bc6bc827eb76e'), size_on_disk=8032882, blob_last_accessed=1756926336.1053104, blob_last_modified=1756926336.9993064), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417669/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8eaf08a45dbd72787c5250403f3d3079db9902e77527c7ca438abc740fdfb753'), size_on_disk=7376929, blob_last_accessed=1756926314.6724036, blob_last_modified=1756926316.0383978), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417883/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8e23cc97e00adfd244a989f83702f4ce7a998ba7998dd95908a1b4ec256bc054'), size_on_disk=10709227, blob_last_accessed=1756926352.4542394, blob_last_modified=1756926353.675234), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417827/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8bfd1221f930e86ffd6150c82aae85f00a23fff33c672e40f47d671f5a2e4d17'), size_on_disk=7291599, blob_last_accessed=1756926342.156284, blob_last_modified=1756926343.1502798), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417873/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d282d59c3c191bd7b7fa7251108224d0ac3929bc98f96a2c43f283f8a5cd046a'), size_on_disk=7609540, blob_last_accessed=1756926350.3852484, blob_last_modified=1756926351.373244), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381039/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5f95b6de8d0d73589c612313eaed6ce1e6adf734e795719f23df6f54cbbedc69'), size_on_disk=1656495, blob_last_accessed=1756926379.8751206, blob_last_modified=1756926380.283119), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417789/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6bd6339b995a5730b6b65ec65f680b57195469ebee9f2e15de6e604b069d4b0e'), size_on_disk=9612750, blob_last_accessed=1756926334.6493168, blob_last_modified=1756926335.8183117), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417867/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f4783b00d4674df92d84f9a304fb9d143c7b9e4754e3efcb9c072a49a9b9298b'), size_on_disk=8536709, blob_last_accessed=1756926349.3882527, blob_last_modified=1756926350.2882488), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381035/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/410edf5a6097a77bcec6880ec2618b4aec4bc10c44607163fe2b27aca0a05117'), size_on_disk=1451911, blob_last_accessed=1756926379.8691208, blob_last_modified=1756926380.5191178), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381036/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ff76dabe83d2948240dd846fcc2a969380b7bd9f8d2eff2c71c299f0b14134cd'), size_on_disk=2593393, blob_last_accessed=1756926379.8661208, blob_last_modified=1756926380.5611176), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417707/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/89192f5007f730a90c823219ea107c799935f5e824963090abad813603160fa8'), size_on_disk=2615872, blob_last_accessed=1756926322.9183679, blob_last_modified=1756926323.9473634), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417666/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/38008595447ab722c1889d0e19babf74299822ac7733504a4f09e1af1e4ad1ac'), size_on_disk=7712422, blob_last_accessed=1756926313.945407, blob_last_modified=1756926315.073402), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417855/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ac2218ccb9f80664ce377351ea1dc84bf3f70b36006a86cffdb761a40ddab791'), size_on_disk=1981158, blob_last_accessed=1756926347.3992615, blob_last_modified=1756926348.3062575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380993/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d568d29486ad3ec6f67f87b78cacb8b7102c591f979ec2bdb17ff75bde789670'), size_on_disk=3031778, blob_last_accessed=1756926374.496144, blob_last_modified=1756926375.2661407), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bbfeb8571ee8915d793698c2116cef9f2ea61573c66b4ae7066078271c6172d6'), size_on_disk=5132301, blob_last_accessed=1756926362.6441953, blob_last_modified=1756926363.7271905), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417847/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4dd39704bc9c1d60a8271f16206887171ee92abde023c53501c1560533a688c1'), size_on_disk=4755120, blob_last_accessed=1756926346.125267, blob_last_modified=1756926346.8372638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417796/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/52d8262071c9c9de8cd8fb52be9e67acf125b8ce67033749fb92989f0d1f9345'), size_on_disk=10184069, blob_last_accessed=1756926336.2803097, blob_last_modified=1756926337.4333048), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417705/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/92b4f5af6793c98b854bcca83c86ac0cb41ec4e40ec23d3ba9c4ff287852c63c'), size_on_disk=332184, blob_last_accessed=1756926322.5923693, blob_last_modified=1756926323.1503668), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381018/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/878bf028aca9daeebe7ed0f8ee522580d512771ec4a5e35bfc62ac675176ad15'), size_on_disk=701951, blob_last_accessed=1756926378.2281277, blob_last_modified=1756926378.643126), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417903/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3ddfaef9dc57d0f63b8613408aba3af803dabd5ee87a1584edd9391b2b41bec3'), size_on_disk=11218813, blob_last_accessed=1756926355.9432244, blob_last_modified=1756926357.2232187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417684/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eee25a9a91c54d0404db884532d1b85555f2e1de0c664abcbda4343f9d729842'), size_on_disk=10310520, blob_last_accessed=1756926317.7173905, blob_last_modified=1756926319.3473833), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417780/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4f1b7c3ded6441c8e906c3494ae63750d25303ee6f8d8e05120dc11ce8d79080'), size_on_disk=9025914, blob_last_accessed=1756926333.2923226, blob_last_modified=1756926334.387318), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417734/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/10d4c6d9acc5b56370fc32062262ae3871008e20e960c6b352ac27a4e229c905'), size_on_disk=1495194, blob_last_accessed=1756926325.9453547, blob_last_modified=1756926326.745351), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417804/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/16ee23ae4dbe8a4d968a614f5e6de65028b6edce012231a50eb776d65f949d34'), size_on_disk=2107502, blob_last_accessed=1756926337.7883031, blob_last_modified=1756926338.5083), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417653/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/59233342d3c3b71fce95d5295cf7c1e5ef3f4254dbc120229cb83b6c8115c759'), size_on_disk=12208357, blob_last_accessed=1756926311.136419, blob_last_modified=1756926312.3904135), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417850/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c08fca103aa3dd1944dcf5c73b67397263842dc687b4101c8ab2c1a63d50205a'), size_on_disk=1182282, blob_last_accessed=1756926346.5122652, blob_last_modified=1756926347.3082619), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381059/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/59daa71f55c9c0da6f8d56fc2497491477312fbdbcb9e92d4951874f2ed72686'), size_on_disk=13124154, blob_last_accessed=1756926778.926743, blob_last_modified=1756926781.8157268), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417602/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/26ae91a914e9ad095615c88ac3c62c99e28ae2687ccd66007f13ac8fbbffa3fe'), size_on_disk=3717181, blob_last_accessed=1756926448.0367467, blob_last_modified=1756926301.5714607), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417823/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1887333f71d11fe7c43748662f58d9ba3fb12f3f6ca30a61d7e1c7a0c95f7064'), size_on_disk=9444476, blob_last_accessed=1756926341.6372864, blob_last_modified=1756926342.8442812), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417908/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/128e54326b088977000f8caaba7d09e324281ed7fe0e3e5f2779783f0a82f293'), size_on_disk=3614805, blob_last_accessed=1756926357.1272192, blob_last_modified=1756926358.2542143), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417677/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cfcdac65879c0c26771c1cd1c4a656d4db20e6adb2ed3ad833cf23cd35a9d848'), size_on_disk=13135194, blob_last_accessed=1756926315.9083984, blob_last_modified=1756926318.1283886), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380975/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ea018f4882a60f5fb07a4bbae51bd494e44e6df9b215d9da5e405687a567dcbf'), size_on_disk=8369150, blob_last_accessed=1756926371.6051564, blob_last_modified=1756926372.7471516), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417825/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/efc4971b63b21d6aee96e17d9e9b2cc2e3fd34608cc3722e36032c594fc03e48'), size_on_disk=8550531, blob_last_accessed=1756926341.8732853, blob_last_modified=1756926345.0782714), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380992/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dbc6b1de202e7fb25701c02607facc3e8c332550907ca0b70ba177533a311d6d'), size_on_disk=2551903, blob_last_accessed=1756926374.2281451, blob_last_modified=1756926375.0341415), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417728/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2be09708238c50a31b4e9b334c6202c26eaa6a950ebf3de4287651999d45097e'), size_on_disk=3385901, blob_last_accessed=1756926325.0603585, blob_last_modified=1756926325.869355), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381062/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/76151e17f242c5108010debea961b0b13f3821be2fcb77fd923a36cc0e672695'), size_on_disk=4880117, blob_last_accessed=1756926779.3357406, blob_last_modified=1756926780.5167341), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380997/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/add73ab9723ef88a47192fa169b89aaf17c16a0841fc9e53c1c5d73cfdc2ad50'), size_on_disk=15536494, blob_last_accessed=1756926375.1291413, blob_last_modified=1756926377.6141305), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417754/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0d840353f1148a08928dfb3177a0abfe635c406fc77d6d9958743585ade6b872'), size_on_disk=1101891, blob_last_accessed=1756926329.758338, blob_last_modified=1756926330.215336), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380969/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/10410488c74f35a493cef2ffc4a64b28b336be1fb32a2b6dac692e0d7fbd4271'), size_on_disk=1973405, blob_last_accessed=1756926371.041159, blob_last_modified=1756926372.1191542), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380965/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6b384b53be751cec9056e74c2c8ce9fe2e532cfbf6a38e43bb09ce74ec817f3a'), size_on_disk=12974487, blob_last_accessed=1756926370.3761618, blob_last_modified=1756926371.4361572), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417899/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b4349f94cb0679468e9c28a9439c1a71c88d7288adc3bdad0ca31a2590f1d4e0'), size_on_disk=7910137, blob_last_accessed=1756926355.3832269, blob_last_modified=1756926356.3022227), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380929/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aeddf7cf3aee11ee08c90d305e66fc682e675959c171ecfaa591bc1d9015ebbc'), size_on_disk=6656759, blob_last_accessed=1756926365.1011846, blob_last_modified=1756926366.0181806), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417690/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/283af9b17d776b75f6ff6ad1f426d6d0e0e5aa1f2089a20910edcb6763191b69'), size_on_disk=12265448, blob_last_accessed=1756926319.171384, blob_last_modified=1756926321.4153743), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417807/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ebc732f5d06d745bb63af3c7f5966722972fa722ef9d0194d71cf6d9485fda83'), size_on_disk=14049230, blob_last_accessed=1756926338.268301, blob_last_modified=1756926339.5272956), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756926300.2174668, blob_last_modified=1756926300.2804666), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417687/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fa4f79173f5a5ed278c7ef80d9fb220aa9c615de97bae7963969e27ecf0e587c'), size_on_disk=13352339, blob_last_accessed=1756926318.3003879, blob_last_modified=1756926320.3913789), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417619/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0a4d4159941caa34a5e079821d49047d1434077926b2933c27d384e04ed2e5b5'), size_on_disk=9949802, blob_last_accessed=1756926302.5744565, blob_last_modified=1756926304.05045), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417718/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3ba23a0e7c5b3a455e95fbf6a22b51687d5330c19207241bdfd189e3776f5ba3'), size_on_disk=3304954, blob_last_accessed=1756926324.0233629, blob_last_modified=1756926324.986359), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417799/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cba2fb579c7706a7ff38fad3c7c649000560c1052e681adea986c5cc02c02cfb'), size_on_disk=5688906, blob_last_accessed=1756926336.7133079, blob_last_modified=1756926337.7073035), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417924/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f2053fcaa7f21b1703e3491e167b457c29759b2fe9535f0cb2bfa257a1b86cea'), size_on_disk=6988370, blob_last_accessed=1756926359.940207, blob_last_modified=1756926360.8152032), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417667/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef18f0ec136ef4b0facd217b32604556a7c9f514d75e26d2be17eb8da9b74a14'), size_on_disk=7979245, blob_last_accessed=1756926314.2284057, blob_last_modified=1756926315.734399), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417824/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/65d997db1339fc5a8380a61fcf1ab1a6d28fb9142fc7d78c89236dc218b91ea3'), size_on_disk=8218856, blob_last_accessed=1756926341.7612858, blob_last_modified=1756926343.0512803), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417833/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/48792db399d7f0aab6765109e8c01abd61a2da93e5d0bdf7c325bcffe01fe113'), size_on_disk=8139822, blob_last_accessed=1756926343.3262792, blob_last_modified=1756926344.480274), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417809/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/486100d34df42ce97476d9753f8d57edb5f53596eaeca2d0cf81f5f95a4f4e12'), size_on_disk=7376702, blob_last_accessed=1756926338.5872996, blob_last_modified=1756926339.9172938), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417942/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f3a5b1eb0f954ddf66a8dfa1a38465726d2251730d3898865535269a43c7fa4a'), size_on_disk=6911573, blob_last_accessed=1756926363.415192, blob_last_modified=1756926364.3521879), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380998/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6808b8e1fa74b807321826b2c54182a3b6e257f0a223be0a6a94caf9463959e9'), size_on_disk=16847111, blob_last_accessed=1756926375.2231407, blob_last_modified=1756926376.572135), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417911/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3ca8b1b14564fbc90b44995a61a34d5a67482eb5fd867ce4f9f2196de2b73539'), size_on_disk=3051177, blob_last_accessed=1756926357.404218, blob_last_modified=1756926358.3022141), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417665/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7c9b3d448e53fcac9334df092cc003f438cbc462e6785ac3d61202bd65a7125f'), size_on_disk=7481605, blob_last_accessed=1756926313.490409, blob_last_modified=1756926314.7744033), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417657/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62398c518588ad11a939f95736cf0ed01a39890567cd4a8f05e9c9ba1155f9c1'), size_on_disk=10144285, blob_last_accessed=1756926311.5634172, blob_last_modified=1756926313.1174104), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380981/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/198a0b08846a11e72de25e0e9c44f495ef88c9260de2e036d240715004c04953'), size_on_disk=8535317, blob_last_accessed=1756926372.5771523, blob_last_modified=1756926373.8291469), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417874/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6674c316eab3b03bbbe3502565efff7650445f5e1453933336d74938e42a14bf'), size_on_disk=6201897, blob_last_accessed=1756926350.5772476, blob_last_modified=1756926351.5142436), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417668/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b426513aab4e071a24b7d27100a4da4da8c71c8e9b3e82163808b0ec6a42b8d0'), size_on_disk=6011682, blob_last_accessed=1756926314.5374043, blob_last_modified=1756926315.4354005), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417783/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9603c95097c451e616c54d52d3474b866453409905ea16365d019e77c6ca31b3'), size_on_disk=4176778, blob_last_accessed=1756926333.6983209, blob_last_modified=1756926334.4773176), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380960/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/60771297311bf96c3213c2a96321062a06067c91d2bba2fce69a7a6d7a0c980e'), size_on_disk=560607, blob_last_accessed=1756926369.7811644, blob_last_modified=1756926370.3121622), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417610/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a93c708ade83c1c4521589a51835a1dcc98972ab900e0eefdb8ce34e64394bf4'), size_on_disk=2566580, blob_last_accessed=1756926301.328462, blob_last_modified=1756926302.4814568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380947/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3d6062f97d18f4945c36590355a312df2a16206c26626392cfb44d9613aea49d'), size_on_disk=9201129, blob_last_accessed=1756926368.5321698, blob_last_modified=1756926369.383166), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fc6783c3340c869bc3838a9a9d5abb8bca67361feeef12c42b6cf478d73bfd66'), size_on_disk=9020794, blob_last_accessed=1756926366.2571797, blob_last_modified=1756926367.2561753), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417937/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e432985b09ba72eab54fc008b87b20a718dbae96d920a61b47de7e16d214227c'), size_on_disk=6445266, blob_last_accessed=1756926362.6341953, blob_last_modified=1756926363.6641908), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417890/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3eca80a3220fd0294fdfca83740ba70a20af764d28833494948705c4d542ded3'), size_on_disk=9018889, blob_last_accessed=1756926353.7652338, blob_last_modified=1756926354.7252295), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417788/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fd6f44471f1b86568bc059f25e5ccec7bda2b85b9f8be536aeaf2305fe015f5d'), size_on_disk=9112879, blob_last_accessed=1756926334.546317, blob_last_modified=1756926335.971311), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/43f12fb4826be4c7a3dd64800fc72cbcab2a9ce5bed7f7b5b9aee974deb7c06d'), size_on_disk=1116877, blob_last_accessed=1756926376.094137, blob_last_modified=1756926376.6491346), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381034/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/112a8e705f7536fa06c630165d3c409f87a5784ad626b891cc2b3919e24bdd14'), size_on_disk=250813, blob_last_accessed=1756926379.795121, blob_last_modified=1756926380.1791193), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381033/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5fff567b96a4649d787e93ef8cdca2d8ce977b7f2eb25d6f78e50bc30c798bce'), size_on_disk=1712156, blob_last_accessed=1756926379.7391212, blob_last_modified=1756926380.3661187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381048/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/53cd017f084781da16e69ae01b406ac0d8824a4d991e39b22c428e664a3808f2'), size_on_disk=2181901, blob_last_accessed=1756926776.9907537, blob_last_modified=1756926777.7067497), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417693/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/98a5478f5bd4feb033ac69510f2bf785c87777377b7b8b6dce24b8f89dbd6a03'), size_on_disk=18580692, blob_last_accessed=1756926319.7013817, blob_last_modified=1756926321.9783719), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417758/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/54f8e8d84e5b8b9457aaa143829fcfd5a4715d0fb184478ce659cd6c6efe7be1'), size_on_disk=4176901, blob_last_accessed=1756926330.0203369, blob_last_modified=1756926331.1383321), CachedFileInfo(file_name='GSE222268_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE222268_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e0f1d5b82fd97798d0c02d992c49c0e83d8dcf82e07e001284e78d9656355481'), size_on_disk=4411, blob_last_accessed=1756926300.1784668, blob_last_modified=1756926300.5344653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380994/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7c5f237d9c32233a005e39386360f02d9e1932f9ac267ac3458f96aaf59e01ef'), size_on_disk=2496053, blob_last_accessed=1756926374.8051426, blob_last_modified=1756926375.3471403), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381044/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/89a79f2c5f6258380ff4776af49db6119ac0c04342686705cfde94feebd7c9ca'), size_on_disk=3576881, blob_last_accessed=1756926776.065759, blob_last_modified=1756926778.8987432), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381066/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ae017c72cf3b91ea3fdd40f63a126f640e7049340baca502ff3228c6ee40a7b4'), size_on_disk=13139272, blob_last_accessed=1756926780.000737, blob_last_modified=1756926782.6217225), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417671/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bced5a56092fea7de5754bebc33caaab643f27236f8d9020e7bc6b30b9045d6e'), size_on_disk=5347180, blob_last_accessed=1756926314.7704034, blob_last_modified=1756926315.8293986), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417846/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a3dc440aac6a69d75ec73242a30e59d8183023a2677f1576b5e73d037d6e6654'), size_on_disk=2680468, blob_last_accessed=1756926346.0562673, blob_last_modified=1756926346.7392642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417608/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d225f561536fca4d073d2418a32efc3df68e9666a3403c7d8b1ee0a520861322'), size_on_disk=873305, blob_last_accessed=1756926300.603465, blob_last_modified=1756926301.521461), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417729/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8383a0e86c2bfe15c46045980d6768eb4e7e3c4c157ff1ae7ce20144e245ca80'), size_on_disk=3058876, blob_last_accessed=1756926325.3113573, blob_last_modified=1756926326.103354), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381047/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dda6f6ca669a0e5c9d34b548305cbeb6b82f1b70086e06d3604e7e096fd7fb25'), size_on_disk=6799004, blob_last_accessed=1756926776.8777544, blob_last_modified=1756926778.4717455), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417791/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8d2eb1bdd1a5cae023abb271e5858cf111879953cb0d6267f4ebf21a3d30c4ad'), size_on_disk=5232904, blob_last_accessed=1756926335.2033143, blob_last_modified=1756926336.417309), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417785/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/209799965b03585e1143e881d87c5cea80d37e06637d2fc3f7e1ad2eaadfd3bc'), size_on_disk=11557515, blob_last_accessed=1756926334.1813188, blob_last_modified=1756926335.1353147), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417829/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/63a9af789279180a07d8e8afe07bb0e9b8aa32c4340e01347a6f7ee809198308'), size_on_disk=5677879, blob_last_accessed=1756926342.904281, blob_last_modified=1756926343.7332773), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417664/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/91fc5dce8497d1fc5466fb5ac31e44a20b295d8f97825500f256f6830ea2f983'), size_on_disk=8203399, blob_last_accessed=1756926313.1894102, blob_last_modified=1756926314.6624038), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380946/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62fe0084bf7a8f9f484e76366f203e1c68862a211d5d501c08e19b8f28678d6b'), size_on_disk=8090103, blob_last_accessed=1756926367.6871734, blob_last_modified=1756926368.7681687), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380955/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/369e0f1de51ea197e928618e562613eeed2ed59ce5c1389ce07ff6dd96d391b4'), size_on_disk=3596907, blob_last_accessed=1756926369.4561658, blob_last_modified=1756926370.2661622), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417638/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c8cface311cd55cd1542ed94d609da72cd2b3087422c28ad26c60e1d81a3e6bd'), size_on_disk=993699, blob_last_accessed=1756926308.4724307, blob_last_modified=1756926309.2634273), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417656/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/03f8707a323ef7563c8ec2d6352d24c6454d043c4bbbca417680df5c7db208f8'), size_on_disk=10029839, blob_last_accessed=1756926311.588417, blob_last_modified=1756926312.8104117), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380931/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/be287270314fed3dded81e0c24a66d8bd991b219fd6a157f1aeaa1690262e563'), size_on_disk=4324179, blob_last_accessed=1756926365.1611843, blob_last_modified=1756926366.1241803), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417717/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/689842dffacf7efa4ba6cd9b4314d195e89f9105eb71675f27019a9e25be63db'), size_on_disk=102777, blob_last_accessed=1756926323.9603631, blob_last_modified=1756926324.5793605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417906/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/34d3381dd45e579dcc298e91cc95174c4219cf548401b8f2392cf746fd508c5b'), size_on_disk=13795318, blob_last_accessed=1756926356.3642225, blob_last_modified=1756926357.640217), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417800/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8e563a7d64bf06e7fe26c2817271f626273eed4c7dd19db0b9fd8e14c25f73b3'), size_on_disk=9904697, blob_last_accessed=1756926336.7953074, blob_last_modified=1756926338.958298), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381058/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9dc97b5892fcc8af593c03fad2040396b9b10626a655319ad40b38acd75e1d7b'), size_on_disk=11534270, blob_last_accessed=1756926778.543745, blob_last_modified=1756926779.7317386), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381040/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a7c622a8ae1f5083eef26ecbcc2444055a9265b674ae80002e5468c94c0eb070'), size_on_disk=9682671, blob_last_accessed=1756926774.782766, blob_last_modified=1756926776.7967548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380951/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b9b68679dd2da94e4aa4852ae3de3ee5b823c1fb1483f4ece3eee5d947014785'), size_on_disk=1154119, blob_last_accessed=1756926368.8281684, blob_last_modified=1756926369.410166), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417929/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/85b194132215311994e48908799e31357d1094663a232e8170a4edcfa5a11eff'), size_on_disk=9155231, blob_last_accessed=1756926360.9862025, blob_last_modified=1756926361.9231985), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417751/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9a5733130de69115a731511dac8bf577fc5609a7f9649106ae9a12ffae7011ba'), size_on_disk=1660869, blob_last_accessed=1756926329.2023404, blob_last_modified=1756926329.9043374), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/64a119c8028083521d3872e2daca1545f2f8753eb8acfc751bf0c6d25803a3f8'), size_on_disk=2003211, blob_last_accessed=1756926376.794134, blob_last_modified=1756926377.2761319), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417675/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/79072181ebce12769f2f0a1971977d6691dde1841b53d7b54cf9d4f47f53b3dc'), size_on_disk=6478433, blob_last_accessed=1756926315.763399, blob_last_modified=1756926317.2643924), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380945/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/51efeee4439e81d920c4d9e860de321d90c72703600044e52215c774e1540650'), size_on_disk=6902367, blob_last_accessed=1756926367.356175, blob_last_modified=1756926368.6011696), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380980/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e36f58da696f0827c72fed8647c2a45a0499fdc5d1859386dbf025388f07e16d'), size_on_disk=2288305, blob_last_accessed=1756926372.371153, blob_last_modified=1756926373.2051497), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417947/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/50bb9cc74904c9d3b6cc3ec8bc12c7ad1fb89204be6f89a863e703a94d224512'), size_on_disk=4023877, blob_last_accessed=1756926364.0161893, blob_last_modified=1756926364.9561853), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417875/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8dc3634dae23cad7f562d15903341a67d3048929bd2198e53f71529b00a3508a'), size_on_disk=11490812, blob_last_accessed=1756926350.6762471, blob_last_modified=1756926351.7832425), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417776/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/01c4a97f92cd5e2919759d52d083eb824408a2fd7ade9413a060c6b20d233963'), size_on_disk=2759569, blob_last_accessed=1756926332.8593245, blob_last_modified=1756926333.629321), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417939/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a0fc7804b026f95809f33087deb75f00beeb9635475b62864d3e58e948361bfa'), size_on_disk=9099632, blob_last_accessed=1756926362.9701939, blob_last_modified=1756926363.9411898), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417836/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4fb964c54f4a37387c7844902d6171011a9a81efff244f365efcb156f630158f'), size_on_disk=3312893, blob_last_accessed=1756926344.2532752, blob_last_modified=1756926345.0702715), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417744/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/faf407f0b251cf5f58c8ac7becee4b995327fd9a7d6599c3045fde8a78e1e811'), size_on_disk=2548979, blob_last_accessed=1756926327.7843466, blob_last_modified=1756926328.7443423), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417802/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ea68aebb727f77d846a145d43ec16fa2496e9c05d49a8791aa131b8e3e536452'), size_on_disk=4881005, blob_last_accessed=1756926337.2223055, blob_last_modified=1756926338.1273017), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417630/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/26fdbadf7c1252a8fd73ac85ea7430d942d2d11ddc43ea1b2590aaa008e08337'), size_on_disk=3015575, blob_last_accessed=1756926305.7104428, blob_last_modified=1756926306.7214384), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417812/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9e8445f413d26051897f0fe3e7566b116a9a55062b78201990de334f17aecb31'), size_on_disk=19905089, blob_last_accessed=1756926339.0272977, blob_last_modified=1756926340.6232908), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381050/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f3c3bac37a1d4ec633edf53ba305c970eb3de412fd54dfbaf0cdf89f39574334'), size_on_disk=1518412, blob_last_accessed=1756926777.1687527, blob_last_modified=1756926778.028748), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417660/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c394d7b0cfca3f3782dca86380e1a55903111d32efde98173af8a0b726ef5e7d'), size_on_disk=6441490, blob_last_accessed=1756926312.4744132, blob_last_modified=1756926313.4024093), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417763/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/91d8a58ead35fc52f7c2649ce9509a92236a20ca8c26f5f2ef4ba1a38e64afb1'), size_on_disk=230495, blob_last_accessed=1756926330.8943331, blob_last_modified=1756926331.4653306), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417880/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b08db0a2399fc4e5b6f59c19b7af05b1294d0fac10b2c336b627234fa828f1be'), size_on_disk=9818886, blob_last_accessed=1756926351.5782433, blob_last_modified=1756926353.246236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417741/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/734d5947cb26e877b6ae9e9adaf84575974a4fe2fb464ba11c4320906f17a42a'), size_on_disk=4566889, blob_last_accessed=1756926326.96635, blob_last_modified=1756926328.5653431), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417743/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d4eb93049648498d2fdc1f125574429a4dfdec65e626ad9fa16bdf0d0cf599d8'), size_on_disk=2280534, blob_last_accessed=1756926327.462348, blob_last_modified=1756926329.1343408), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380989/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3c8b646955209386a1d97882163b7c9816463d2dea68fbed2cc960817ffeb332'), size_on_disk=12022144, blob_last_accessed=1756926373.7761471, blob_last_modified=1756926375.1511412), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417643/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/66dfa3feea4410ba34f5d5d37c7b01e1fa2cdd4ff38f9dfb10d20d55e40e449f'), size_on_disk=1012185, blob_last_accessed=1756926309.326427, blob_last_modified=1756926310.0784237), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417888/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/961a35161b04a0e892f46a0a90b6d4776d7fe07ef82fc7016c95b1a885c4274f'), size_on_disk=9651505, blob_last_accessed=1756926353.298236, blob_last_modified=1756926354.65023), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417674/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5970db11c3efe2bba4eb50d07480cb0b46ed74dfc3ddf12d611ecd61f1d5fb68'), size_on_disk=4619941, blob_last_accessed=1756926315.5114, blob_last_modified=1756926317.630391)}), refs=frozenset({'main'}), last_modified=1756926783.3167186)}), last_accessed=1756926861.9192877, last_modified=1756926783.3167186), CachedRepoInfo(repo_id='BrentLab/kemmeren_2014', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014'), size_on_disk=646214296, nb_files=6, revisions=frozenset({CachedRevisionInfo(commit_hash='95a5f915ad49dfe4af75632861d9da69de2b525f', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/95a5f915ad49dfe4af75632861d9da69de2b525f'), size_on_disk=326978658, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/95a5f915ad49dfe4af75632861d9da69de2b525f/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/05a15ab9764763e5d1957023162e13b0068a5697'), size_on_disk=10286, blob_last_accessed=1767811670.0019596, blob_last_modified=1765415815.7076464), CachedFileInfo(file_name='kemmeren_2014.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/95a5f915ad49dfe4af75632861d9da69de2b525f/kemmeren_2014.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/12a63d4ccf5936c4a7ef7dc11f71287e1eb82b92901da87dd5b48e06f726c709'), size_on_disk=326968372, blob_last_accessed=1767811670.1439586, blob_last_modified=1766022970.1360188)}), refs=frozenset({'main'}), last_modified=1766022970.1360188), CachedRevisionInfo(commit_hash='a6c9a954a1def1774b68582f117ad1abc5864a07', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07'), size_on_disk=319226397, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/a29a64128c10ef9691ee773131972c61f720ba07'), size_on_disk=5007, blob_last_accessed=1756832529.5094638, blob_last_modified=1756832529.5404634), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756832529.5094638, blob_last_modified=1756832529.5434635), CachedFileInfo(file_name='kemmeren_2014.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07/kemmeren_2014.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/3c463017444bd7690959c0d6fff0d0ebe2faa7b916dd9c44e305156f6c98b863'), size_on_disk=319218929, blob_last_accessed=1760552530.221978, blob_last_modified=1756832530.9644527)}), refs=frozenset(), last_modified=1756832530.9644527), CachedRevisionInfo(commit_hash='b76f0dfe8477b300000faecf16b7d315fbf59344', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/b76f0dfe8477b300000faecf16b7d315fbf59344'), size_on_disk=319228170, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/b76f0dfe8477b300000faecf16b7d315fbf59344/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/199dce9d01d3884716dd3a785a85751cb9b1de3e'), size_on_disk=9241, blob_last_accessed=1760552530.0869784, blob_last_modified=1758654056.5908144), CachedFileInfo(file_name='kemmeren_2014.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/b76f0dfe8477b300000faecf16b7d315fbf59344/kemmeren_2014.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/3c463017444bd7690959c0d6fff0d0ebe2faa7b916dd9c44e305156f6c98b863'), size_on_disk=319218929, blob_last_accessed=1760552530.221978, blob_last_modified=1756832530.9644527)}), refs=frozenset(), last_modified=1758654056.5908144)}), last_accessed=1767811670.1439586, last_modified=1766022970.1360188), CachedRepoInfo(repo_id='BrentLab/hu_2007_reimand_2010', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010'), size_on_disk=42682732, nb_files=4, revisions=frozenset({CachedRevisionInfo(commit_hash='31ccd35daf785420014a1346a7db064dd306d4f8', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8'), size_on_disk=42682732, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/886f4614dfd1ef33709d462908ae18608c13e8ba'), size_on_disk=2692, blob_last_accessed=1756844742.0937967, blob_last_modified=1756844742.1537964), CachedFileInfo(file_name='hu_2007_reimand_2010.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/hu_2007_reimand_2010.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/eb9be5a1f01907a81eb2ab4fbbee36ff8c50186f'), size_on_disk=63, blob_last_accessed=1756844741.7997973, blob_last_modified=1756844741.8557973), CachedFileInfo(file_name='hu_2007_reimand_2010.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/hu_2007_reimand_2010.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/47a15f3e23f8cb50af3217d00c6439d29a7422b5190116e4453a5f78cb22540d'), size_on_disk=42677516, blob_last_accessed=1756844742.1787965, blob_last_modified=1756844742.1477966), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756844741.8097973, blob_last_modified=1756844741.8767972)}), refs=frozenset({'main'}), last_modified=1756844742.1537964)}), last_accessed=1756844742.1787965, last_modified=1756844742.1537964), CachedRepoInfo(repo_id='BrentLab/callingcards', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards'), size_on_disk=49606009, nb_files=137, revisions=frozenset({CachedRevisionInfo(commit_hash='9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0'), size_on_disk=49588983, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/00e5602b87ac84670a98c081e6520ca744038f4e9b3a72a78cb61f5926be56b1'), size_on_disk=251087, blob_last_accessed=1763591392.5781758, blob_last_modified=1758648267.8286521), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6657/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/95ad2225152f51e2e31c030ff6708aeebccee5e548035d6f43c9cc3554bdfe5e'), size_on_disk=507730, blob_last_accessed=1763591393.163172, blob_last_modified=1758648269.1676583), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6764/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec87eef9e2e8cdf3c3a10ead28dcf1bfa76e94e12a42422a46c8559fb18207c2'), size_on_disk=75684, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.4036593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7295/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/19bd07f913e0af3460e6b4b5091971ceed8f4aa510cf18b82ec36dabb67c68b8'), size_on_disk=272619, blob_last_accessed=1763591393.1151721, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6567/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0e9713a94594dfcbc80134af52449e12ef207ca9149f5a96c43b6cb457b2431c'), size_on_disk=478809, blob_last_accessed=1763591392.6241755, blob_last_modified=1758648268.893657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6527/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/10081f44b1c2f5ad32489da51fc9ac5b5289b54688a7e78725db247a644b7e4d'), size_on_disk=494640, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.882657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7118/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/be8bd409b83526d5671501de6ef2980177955fdcc3a13d5e153d2593493b2a1e'), size_on_disk=346308, blob_last_accessed=1763591392.9281735, blob_last_modified=1758648270.1336627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5399/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b65d0aa7ddc98940f91f2ae5f07e1d3f81dda688792552b91fcc39fb4f9ededa'), size_on_disk=256729, blob_last_accessed=1763591392.5471761, blob_last_modified=1758648267.972653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6739/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/58b5ed63f2197f406dd3df6a569975d776722193007912718979eaed4d4e5f9c'), size_on_disk=399160, blob_last_accessed=1763591392.6551754, blob_last_modified=1758648269.331659), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7185/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/14f67668aca8d8f8aa2b8af98835da683a7fc3f22aa09cdcd7f943a37632391e'), size_on_disk=201650, blob_last_accessed=1763591393.2191715, blob_last_modified=1758648270.1076627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7211/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73d7747698fa1168c89f12c105bbe900d5969d27a82ae2302d0720d761fbfdab'), size_on_disk=298482, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.1276627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6861/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6955b1a1746fe41162ea1d7a098482bbfdb3d0e411c809b52d698e285627ce03'), size_on_disk=385103, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6146603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6808/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c71f5371e8c803cfc7fd507d0b3c11f7548db5d7a0fa42c68977169369ea036e'), size_on_disk=422880, blob_last_accessed=1763591392.9491735, blob_last_modified=1758648269.3916593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6872/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02fabe6e32f52fe6fca923beb0a6cc2898d991304e23a3e75f3ae8a6bcf361c6'), size_on_disk=274362, blob_last_accessed=1763591393.6231687, blob_last_modified=1758648269.6966608), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6073/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d68ca6411ba47eb63adad81e88fa1341a0ee269897268957ae936dcc9ba7788'), size_on_disk=450031, blob_last_accessed=1763591393.1341722, blob_last_modified=1758648268.3886547), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ac08e026f6d48c034f13a49f3531c90b6c070907'), size_on_disk=9519, blob_last_accessed=1763588875.9720669, blob_last_modified=1763588875.9700668), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=MAG001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/30b63d014686a103b5e7fc6571e902fa9c08630a96557cc12ec9dfcf60871306'), size_on_disk=347333, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.559651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7297/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4bd4088cbfcccdf96def916bb8fd9d673327d3e57c2a4a3e57299743ce4aca7'), size_on_disk=261344, blob_last_accessed=1763591393.588169, blob_last_modified=1758648270.6516652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7367/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a7778bb24506047963575fa96126157895c7f329996c729c3e7d6070595955c3'), size_on_disk=324361, blob_last_accessed=1763591392.1631787, blob_last_modified=1758648270.628665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6708/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/35520976736159652e3be8066e77b1371522f63a8b3564d9b0d9f5b1598a144d'), size_on_disk=253955, blob_last_accessed=1763591393.6881683, blob_last_modified=1758648269.1996584), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d4c0cbbf6075a8b5732e3cef2ba154d95d21aa0efc7e49d947cceebd8742b30'), size_on_disk=269007, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4106548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6689/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/90ea249116dc6a9be86974e1ec0acf79558dfd56a5666be2e05d2107b8c4ee60'), size_on_disk=366787, blob_last_accessed=1763591393.6661685, blob_last_modified=1758648269.1406581), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/11485fd7bb94d0093ce2508d541fea3ce64f934197d8a5f7bb26ecbdb59f8dbb'), size_on_disk=124642, blob_last_accessed=1763591392.3321776, blob_last_modified=1758648267.0666487), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6983/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4ee29f84ab279c520521a6c5a5eed9e5e5581b2e39378bdd898eec67d13654f'), size_on_disk=410123, blob_last_accessed=1763591392.6751752, blob_last_modified=1758648269.8636615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e20f616f6e340b76f6446d2cdffb6cc9feb81297bd0307733f835acf4142cfd'), size_on_disk=269523, blob_last_accessed=1763591392.3461773, blob_last_modified=1758648267.2696495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7266/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9aea2d6f5606d24e1999d450379b1f8c8b9d80eded9e784fb729faa516a27026'), size_on_disk=324480, blob_last_accessed=1763591392.0401795, blob_last_modified=1758648270.379664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7151/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9969e4607d6bf42424f5d2ce8f0d16e3d2d89d069cae6d75997e99c88a68dc77'), size_on_disk=275572, blob_last_accessed=1763591393.7231681, blob_last_modified=1758648270.1476629), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6532/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3041f19cf0dcb3e0bd9b1178994351a22f3297ccd43c5e291df8bfe0ad8d5d02'), size_on_disk=516437, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.8236568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6055/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/31bb70b4c90654ecb276664f5c914679322b24985ec66d5b396a99f253ebfa17'), size_on_disk=377015, blob_last_accessed=1763591393.5701692, blob_last_modified=1758648268.3076544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/74cbce50c258bdddb7e42c8fd5b0b2a9da74fe3cf89a9ab5a53ee4fde7c1dee6'), size_on_disk=186142, blob_last_accessed=1763591392.842174, blob_last_modified=1758648266.9966483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/71a33d11c3ff714ce0730145c740eec4fe88812c6136b8d10b86fa8d4054991f'), size_on_disk=186500, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7286518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5690/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4831e3e037d421a14efbe1da8c369015ec7d01df351bb27f0ef0400c8b94c693'), size_on_disk=311754, blob_last_accessed=1763591392.9021738, blob_last_modified=1758648268.211654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6545/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/260c4a280f52024cb12d44559a6189641ee02efbaf20168435196efbfc69d741'), size_on_disk=404812, blob_last_accessed=1763591392.3761773, blob_last_modified=1758648268.8256567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6573/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e435989051765cf2955d8cd733904e8b60e5f3121e1c2fdc987f4e6f71fe3970'), size_on_disk=265022, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9586573), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7351/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cc708adf186f28db09dfdd4ac43865a90482b2192306949eab01492598cf23e7'), size_on_disk=198070, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.610665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a76f0849e518faedfe50e6f0c76908a3f6beed34fd0b607603db1859493595d0'), size_on_disk=292312, blob_last_accessed=1763587658.6168654, blob_last_modified=1758648266.7676473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=composite/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1f8adf8fb4992283714df8587126211d54e6a0580b8be88c1d5e911196aed783'), size_on_disk=4814573, blob_last_accessed=1763591392.0561793, blob_last_modified=1758648268.0256531), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e5b3174998823af40d055f46ca06e7bb44601cbe97b133062cd3e0491dd30ac2'), size_on_disk=323308, blob_last_accessed=1763591393.153172, blob_last_modified=1758648267.2686496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6437/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/556084dbbc174db5450872d6f5e66cc72091380ea32006977dd176e23c105a7f'), size_on_disk=389840, blob_last_accessed=1763591392.714175, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0230df4f1d3748337e5315bb1d22a0373fdab95f9bc900041b0e34966d868718'), size_on_disk=311116, blob_last_accessed=1763591392.9241736, blob_last_modified=1758648267.2506495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6454/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c35b60861d2fc7a3f4256869e14e629aed07cd2ccce76608578ceb8c6ec423e1'), size_on_disk=498326, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.7226562), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=KB001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38c939d54d3ed77a09650d2ccf00a6ed0e20390b8be722f1c3ed7881a5904a60'), size_on_disk=351677, blob_last_accessed=1763591392.9151735, blob_last_modified=1758648267.5566509), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ef036874e53ee98dfe1b5d84c42749fc40da9279cd573174321ac83201c1651'), size_on_disk=349762, blob_last_accessed=1763591393.0791724, blob_last_modified=1758648267.4966507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6923/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/454ca6a52d4b305eddc4b6967ef9604be4861269fdcfff419883daed9b2fe48b'), size_on_disk=337423, blob_last_accessed=1763591393.5031695, blob_last_modified=1758648269.6476605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5614/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1aa702ff57d34ebed961043e0efae064bfdb62f589cbb515311ae8ea37ba8c76'), size_on_disk=429423, blob_last_accessed=1763591393.4671698, blob_last_modified=1758648268.0406532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6853/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ed47ecb2aa870fdbf12920fd4486933262c0a24318c9c8c5531e359a3c416c90'), size_on_disk=515114, blob_last_accessed=1763591393.2661712, blob_last_modified=1758648269.4276595), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6572/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3488eaac5e4b9b3418b909138e6b50e3f9ebaf3e7345a4a10b6ced18b66152e4'), size_on_disk=440579, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9506574), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6731/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/23ecd71c4e3c1c547295bb756d4654eb413ff288d10118ab286cd21ccd95b666'), size_on_disk=346187, blob_last_accessed=1763591392.1871786, blob_last_modified=1758648269.1906583), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d50648ed5c9f593f830aff661d13b3d34bd14f0bf93b16169df4765c9f47fc8'), size_on_disk=295924, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7426472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR007B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6f0620b102e7cbfadced7d5530545a185036b53118d3e2b7f074c56403d80f4b'), size_on_disk=263185, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7616518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6924/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5e9b3a94d73c1720e6b05ea55000054905a8672edea7f950736ce3fe191f8faf'), size_on_disk=412939, blob_last_accessed=1763591393.748168, blob_last_modified=1758648269.7066607), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7063/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9427d8f8c54e3c0cc9d720958ef15d3859f00cdfb678559e0268387a36a8ef37'), size_on_disk=404051, blob_last_accessed=1763591392.5301762, blob_last_modified=1758648269.8976617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e871cf06f596bfe74896b62aa28d6b1e6f2f0b8408630fb146c7e29cfd88fbf0'), size_on_disk=232721, blob_last_accessed=1763591392.7871745, blob_last_modified=1758648267.991653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eb61968b5ec5eadd9087e42ead29333f91195dd5b3f9c5a616b41e94ca23c494'), size_on_disk=330245, blob_last_accessed=1763591393.6761684, blob_last_modified=1758648269.710661), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6b92129c2fb99028b6cab9509cf1a4c40b142109cde34c9a130d375001dcd211'), size_on_disk=267273, blob_last_accessed=1763591392.7811744, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6374/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e410602f56bc94d9831a2bc8bdb865ee4ebe9862a65b603165b655fbcc4b9914'), size_on_disk=441531, blob_last_accessed=1763591393.1891718, blob_last_modified=1758648268.5356555), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5a70fa6f22aeda3246d17646cc4ec7c5a46e1993f9981b19d9fd5a8cc99bf11f'), size_on_disk=308858, blob_last_accessed=1763591391.9411802, blob_last_modified=1758648267.0186484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6738/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e6d612d16538a06c985f1134f3fdfd367944b2a85cedfb8b0ddad1679004c940'), size_on_disk=333560, blob_last_accessed=1763591392.8651738, blob_last_modified=1758648269.3676593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5986/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f1179c75ac6e91de03b67a73882f3b9c07c8e435d938d3059de489403adde7a'), size_on_disk=328749, blob_last_accessed=1763591392.691175, blob_last_modified=1758648268.228654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d3e36418bd46acf9537a8f864b1a1361a062d72fec2b093789b88ed655ec117'), size_on_disk=377393, blob_last_accessed=1763591393.0451727, blob_last_modified=1758648267.0136483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6640/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48339729112d22aa5aabf411674bfb90a567ec4c7a07ea7930738645a96823ef'), size_on_disk=329042, blob_last_accessed=1763591392.6391754, blob_last_modified=1758648269.090658), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c9b19726dc45896e4f86a5acc52d07cc422f895bfaaaddb332aaceedff36560c'), size_on_disk=178793, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0266485), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6770/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/082f14f7b0d5fc47e088efc40ae85a8ec41f99437c66da96ca69af210bce61cc'), size_on_disk=318118, blob_last_accessed=1763591392.8081744, blob_last_modified=1758648269.3776593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/33d7303ed077f489ee28021daca0bbc859feb1cfd383dfc9a856ea13994801a9'), size_on_disk=331877, blob_last_accessed=1763591393.292171, blob_last_modified=1758648268.2226539), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5654/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38961219eb66dec31505ebcc5088f16684be9891b96fffa8c9a13cd3f13254d0'), size_on_disk=447782, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.1876538), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7240/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4460d188b4cd3b362f33acf0f0b5312a267b37f8fb2dab4b2577c6c3bbb1231'), size_on_disk=307039, blob_last_accessed=1763591392.6101756, blob_last_modified=1758648270.403664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bd2017e75947ead2c06f9d1936347f7fdcfac7fbff620e0b39077208a02c3bd9'), size_on_disk=303094, blob_last_accessed=1763591393.1271722, blob_last_modified=1758648269.9296618), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7155/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/521fc1bea6963640645c21cbdb8d1de07793f11213c23ac71d249f3252b32f54'), size_on_disk=304554, blob_last_accessed=1763591393.5841691, blob_last_modified=1758648270.1366627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6021/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eea0d101dd58c9619b41a94009fc0dff8156d2d36d7f2b88bb653e96123a032e'), size_on_disk=439545, blob_last_accessed=1763591393.5531693, blob_last_modified=1758648268.258654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/62e9a627f20607bc4887ebc9277131f974ec80ff39ac386775f4b8f2f5cb9d1d'), size_on_disk=346782, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7476518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fc9311b65abb851fbbfd8bb0c0765af59069659f5a715aee546a8644b0af4ec2'), size_on_disk=210948, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648267.6766515), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dcb33135a9d854d17a6b80348f6bee8becea9ca3808583a034f89c5e4f460c54'), size_on_disk=218643, blob_last_accessed=1763591393.4831698, blob_last_modified=1758648267.9636528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7269/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5bae329d2318610f627fc562a5163253c7d61e0ef83c53808b220133b2299264'), size_on_disk=203263, blob_last_accessed=1763591392.541176, blob_last_modified=1758648270.4096642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR006B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fcc5e886ec7c6363a5ab51b03d3da189ac3e0cde0a0f79d4f0c46043a6b61838'), size_on_disk=253857, blob_last_accessed=1763591392.0501795, blob_last_modified=1758648267.785652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/646c843611bf88c7f5dcd5b1a664d724c3b24b6434cc52d9a54de62ccaf6c542'), size_on_disk=255949, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.4506505), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7243/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cb59e645fff2683db692b4d2f514602ba210271b632cd113853febe941c6485f'), size_on_disk=281696, blob_last_accessed=1763591392.701175, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/530b5650d91dae3bf169f967af8e800611e3e01daeb1811d6fd21c2612ac4ad9'), size_on_disk=315170, blob_last_accessed=1763591393.6121688, blob_last_modified=1758648266.7956474), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7331/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f159a9811b8ac1b5e252b52427979a3bddce2e7131b13f84046b31e47f7fa399'), size_on_disk=242821, blob_last_accessed=1763591393.2501714, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5301_5088/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/65ef775934339b80eda59a141cf791a74e1c6f12513d8143307cebe2f11c3738'), size_on_disk=410106, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.9516528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7332/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f7f62ee008fa2b5cc688cbd80c888f1bf9da538caa245b63581520d485338a99'), size_on_disk=309595, blob_last_accessed=1763591393.1021724, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5610/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a86512ae1000a994c86e9aebaf30cf9ce1a59160155f3dc1afa6cbde0a1fc47e'), size_on_disk=307382, blob_last_accessed=1763591393.5291693, blob_last_modified=1758648267.994653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7370/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ff4dce26afb34b291220c17625be4a28078f9721a8b000ea177c4add3bf1a039'), size_on_disk=241010, blob_last_accessed=1763591393.7701678, blob_last_modified=1758648270.6726654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ac8c590c19e7b95b9aac571b1394976039f25fdd66e62a68ecb99925433f8e54'), size_on_disk=355706, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0226483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6863/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec534c91cae4413d30477014131d39b51c9c236dfbf150874bd21ec3de821613'), size_on_disk=389025, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648269.6376605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=MAG002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f7f35257d4c1f7fa5812ea31c1de791e08ab5e3a4e7ca42b2bb12b2c64f7ed7'), size_on_disk=490174, blob_last_accessed=1763591392.829174, blob_last_modified=1758648267.583651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f427ea0f4ded3b6ff841153d8ad87d72a26a964c189246fb6fd5c439bd8b0c1e'), size_on_disk=353623, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.812652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5801/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/12e91a415cc2a149af4c34c3c8b032821ff301e2828dfb9f4636bd6b2913ea3d'), size_on_disk=360485, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.1636536), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4b2ff192aa4808cb50b1c1b1667060f98505dd63c1aa58c7d8354e41194957e4'), size_on_disk=289767, blob_last_accessed=1763591393.4871697, blob_last_modified=1758648266.7706473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dc74862f02ff12b8c906046f1f03953d33f5516091d58397b0f9a0fc05bb4b5c'), size_on_disk=315435, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.5006506), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7258/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7b1860ba3be7dbf053203fa5690da2002f7841186940231acea6064e70f0bdc8'), size_on_disk=171331, blob_last_accessed=1763591392.6171756, blob_last_modified=1758648270.3746638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7283/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0dc91bfacca82c5ed23de7757d59f9f808d541d96dd79aab56f0c4252e7aabf3'), size_on_disk=265146, blob_last_accessed=1763591392.7731745, blob_last_modified=1758648270.401664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6651/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7644af4cc956ea08809ab6c80c65095e3fdbd36e056040c0eaded3773d546359'), size_on_disk=559951, blob_last_accessed=1763591393.6361687, blob_last_modified=1758648269.1856585), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6855/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48e48378d24404e926bc1c44b10af60d4b12f82124ff2f80bc0b69b4a0f98740'), size_on_disk=609248, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6556606), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6390/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f324cdf73148c8b0fa23456bc4f51cff3032d08ed42e44dd9fcc4815a6d969c'), size_on_disk=560601, blob_last_accessed=1763591392.7391748, blob_last_modified=1758648268.6206558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6061/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73e03845d0fbff1a5931e4f792e2c1965ade02fc622ffc08e05c267ac6cd3231'), size_on_disk=394930, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.447655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5961/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/49cac5c2df1adf790a9ac0209db6c0945de50b8191d403e8b1ad386ee2dcb35e'), size_on_disk=321368, blob_last_accessed=1763591392.560176, blob_last_modified=1758648268.215654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6635/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6c5b7f1fda3a7f5679870f438d13a45558f7f8da0f479c5144d7751bc8f7fbbc'), size_on_disk=420658, blob_last_accessed=1763591391.95018, blob_last_modified=1758648269.3956594), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7137/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/703fe9f8552adcbdfb5e640509cb62df2fa734b43fe1e98fc1fd342415f823d7'), size_on_disk=515398, blob_last_accessed=1763589053.7698598, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7237/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/110a743162ace7792a44dfe48d7afd40bb67c45c722038c981548477ebc5d8fe'), size_on_disk=313207, blob_last_accessed=1763591393.143172, blob_last_modified=1758648270.1426628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6551/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c3729692991cdcafe9baa3d2805b3866f72fe9bc89ba22c6d2cdae4d5b5bbf5f'), size_on_disk=545477, blob_last_accessed=1763591393.0611725, blob_last_modified=1758648268.8376567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7cb5cca74f1bda2f8f1c8ef962e54d34228361eaa9dd8b21e0d1dd7092ff8164'), size_on_disk=229703, blob_last_accessed=1763591392.555176, blob_last_modified=1758648267.0126483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6177/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b91ffef1dfcdf7952df46956d76a4b364aa0d8342453636cf09e6a343607184f'), size_on_disk=387618, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.424655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6506/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/986d9d3b4ed064d66ebaa0f2502b933f1650a20ac4bbbba804e2cde03b9454c0'), size_on_disk=378407, blob_last_accessed=1763591391.97318, blob_last_modified=1758648268.7656565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fa1a44bd33df22ec6b5a8baf3be17e51a11522b5fdd545a38e6e163cc5ae8207'), size_on_disk=241911, blob_last_accessed=1763591392.572176, blob_last_modified=1758648267.5226507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6513/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c8f9c31704c2f8732b4668e1e6410034b8adf2ada39ff55935aa0199e920d055'), size_on_disk=507367, blob_last_accessed=1763591392.1451788, blob_last_modified=1758648268.8406568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/128015caa722289251a45aa3d56cabac6b5946eb4e1be16eb950555fb24ac413'), size_on_disk=172328, blob_last_accessed=1763591393.735168, blob_last_modified=1758648267.2826495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6959/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c0cb6c2ec2bda19b0c1da5d5fc2229d0b6d6987d75646fa230c0c14d591cf1f1'), size_on_disk=537588, blob_last_accessed=1763591392.851174, blob_last_modified=1758648269.8686616), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1fa09a5a6e6e69198501d994050adfcad7ff39a3cc2fe94e7ea177414e1bc968'), size_on_disk=303532, blob_last_accessed=1763591393.7091682, blob_last_modified=1758648267.2776496), CachedFileInfo(file_name='annotated_features_meta.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features_meta.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/2be4b693d333698f7ada7ef40ffaf927c0160d7975ebcf6e215cbc4ba1be1604'), size_on_disk=11813, blob_last_accessed=1763587594.724117, blob_last_modified=1758648264.795638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6920/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/45122fcfb3abc3a43631023712c224dc6e00dba566fb2e15deb4ab4501ad2538'), size_on_disk=446998, blob_last_accessed=1763591392.8881738, blob_last_modified=1758648269.6416605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ddd58117dbedfe7e9dc3fd946187e7bd5d88fe8b0f38999c6e4b5660938bb787'), size_on_disk=359739, blob_last_accessed=1763591393.5981688, blob_last_modified=1758648267.2726495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6798/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/899cde55032cd593be0013e1f6ff33fe9a12f4616477b1410d86fae1b2008a09'), size_on_disk=342376, blob_last_accessed=1763591392.7951744, blob_last_modified=1758648269.4006593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6140/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/66c2da17957d7e6a8b159d3573879ac779d0094e35150b51e2643f7ddee36e1f'), size_on_disk=332958, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4236548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7256/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1af21cbdd7f6f6effff15a1b061a48f85132d582f08c8fd42e04ea58bde7ec00'), size_on_disk=206710, blob_last_accessed=1763591392.586176, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6677/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4ee6e1d3e2b9a9bbcc8851048d2b16f4aea6a1e2f752afb9e0bf75a56adf7042'), size_on_disk=322147, blob_last_accessed=1763591392.393177, blob_last_modified=1758648269.0826578), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6421/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1e3bacd3bcf1fcb46cfc5f9ab66c2af57e190f3d006031e7ef37864fbd36aaca'), size_on_disk=495452, blob_last_accessed=1763591392.2161784, blob_last_modified=1758648268.6366558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9f7663ccf72b0681ae5866c13500fbde98f35877fd677ac0d49c17447d17d6ce'), size_on_disk=332767, blob_last_accessed=1763591392.1731787, blob_last_modified=1758648266.744647), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6965/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9ae4546d58672f0632757c94b595af8b7c40da215611783fdc1a65000075ec98'), size_on_disk=509677, blob_last_accessed=1763591392.2731779, blob_last_modified=1758648269.933662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7115/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/205443fd87ce5c793dab86eb75c21dab7dc2ae2edac41d7e6314cfe491bdb01d'), size_on_disk=321850, blob_last_accessed=1763591393.2331715, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4d098c33564f99ec6aed77d585d1f6e871806f441f099790eef4d6478e322af'), size_on_disk=264459, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7486472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS011/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ea7cb0559b342dd0ec278e7d5846a5ba0c0d867ea04d545f72e0d1c9472a0cc'), size_on_disk=363979, blob_last_accessed=1763591392.9791732, blob_last_modified=1758648267.0116484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5617/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bc01241e8363373c995a0fa7439aa1e432a60eb8a7805124fff302311f638361'), size_on_disk=351950, blob_last_accessed=1763591393.6541686, blob_last_modified=1758648268.1026535), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7276/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4e0d576cc4a6a85be64f2198e5001a9f5b02b506072df6f695cfdfc719cb041'), size_on_disk=204036, blob_last_accessed=1763591392.987173, blob_last_modified=1758648270.4646642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6354/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/80b129a46defc7b06e472a35935bfaf410bc5e456475b2885feb2c8a4934a81b'), size_on_disk=173225, blob_last_accessed=1763591393.4981697, blob_last_modified=1758648268.4986553), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/d1cd38451bc41ef14dbc1566c4704b004b2c0661f667210c81dba09bec90540f'), size_on_disk=244487, blob_last_accessed=1763591393.6301687, blob_last_modified=1758648267.3246498), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1c8a8cb23b1ec073827d6f5343a3bf03a9d7a04cf2ae3fd3ea4cc5ac37673b38'), size_on_disk=342987, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.6226559), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6448/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/182dc134914a5ddb02992ab6a4f1c12d2450aa331820bf7bd95693dc999bcfae'), size_on_disk=390210, blob_last_accessed=1763591392.9591732, blob_last_modified=1758648268.672656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e91fd764a83947c14f35d7a2d8c0d156c0a61a32a1da96273718ef4be720787'), size_on_disk=361855, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.7196517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02313d4d85859a7353e56d89b6e14bcec5160849b5c0089ccbecaa1cda5012cf'), size_on_disk=301213, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4356549), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bbe701ffd546a2f4359f821262f7b44232bdc328d9d4719bfdaa8f2aedc6d2e9'), size_on_disk=346079, blob_last_accessed=1763591393.0861723, blob_last_modified=1758648267.589651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS006b/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7eedae811a5b3dd4389e7e814d35b022bfaf01fc4d2cfb47be6e6786d6d58c64'), size_on_disk=315912, blob_last_accessed=1763591393.2571712, blob_last_modified=1758648266.7546473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6443/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4deb4634e8b4bf141a2410e94b2d99e2b31a0e8f037de9a1c6e14696025bffae'), size_on_disk=494775, blob_last_accessed=1763591392.991173, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4e8332a5db6260f6357f940e62e3e7b622785f386e81cdb9c75b538d318f4a6'), size_on_disk=158978, blob_last_accessed=1763591393.5471692, blob_last_modified=1758648266.7626472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6954/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/aeed3c55d81a4eb0c568486e476211c599ce2bf37f9dff22c7dfca23f5d80495'), size_on_disk=476054, blob_last_accessed=1763591393.011173, blob_last_modified=1758648269.943662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ab338795b8bbf334650386a123f6e0df41363cb5dcbb778d485ce58804e96b29'), size_on_disk=375506, blob_last_accessed=1763591392.9711733, blob_last_modified=1758648270.1326628)}), refs=frozenset(), last_modified=1763588875.9700668), CachedRevisionInfo(commit_hash='27becd0ab489579a50c38ba896dd03680c5fd2da', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da'), size_on_disk=49585603, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d4c0cbbf6075a8b5732e3cef2ba154d95d21aa0efc7e49d947cceebd8742b30'), size_on_disk=269007, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4106548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7237/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/110a743162ace7792a44dfe48d7afd40bb67c45c722038c981548477ebc5d8fe'), size_on_disk=313207, blob_last_accessed=1763591393.143172, blob_last_modified=1758648270.1426628), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a88564c359f73b5ac3f70a4635f33a1ec57f527f'), size_on_disk=6139, blob_last_accessed=1763587733.4135532, blob_last_modified=1758648264.1986353), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9f7663ccf72b0681ae5866c13500fbde98f35877fd677ac0d49c17447d17d6ce'), size_on_disk=332767, blob_last_accessed=1763591392.1731787, blob_last_modified=1758648266.744647), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=composite/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1f8adf8fb4992283714df8587126211d54e6a0580b8be88c1d5e911196aed783'), size_on_disk=4814573, blob_last_accessed=1763591392.0561793, blob_last_modified=1758648268.0256531), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7297/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4bd4088cbfcccdf96def916bb8fd9d673327d3e57c2a4a3e57299743ce4aca7'), size_on_disk=261344, blob_last_accessed=1763591393.588169, blob_last_modified=1758648270.6516652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/128015caa722289251a45aa3d56cabac6b5946eb4e1be16eb950555fb24ac413'), size_on_disk=172328, blob_last_accessed=1763591393.735168, blob_last_modified=1758648267.2826495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4b2ff192aa4808cb50b1c1b1667060f98505dd63c1aa58c7d8354e41194957e4'), size_on_disk=289767, blob_last_accessed=1763591393.4871697, blob_last_modified=1758648266.7706473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7063/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9427d8f8c54e3c0cc9d720958ef15d3859f00cdfb678559e0268387a36a8ef37'), size_on_disk=404051, blob_last_accessed=1763591392.5301762, blob_last_modified=1758648269.8976617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6454/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c35b60861d2fc7a3f4256869e14e629aed07cd2ccce76608578ceb8c6ec423e1'), size_on_disk=498326, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.7226562), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e91fd764a83947c14f35d7a2d8c0d156c0a61a32a1da96273718ef4be720787'), size_on_disk=361855, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.7196517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7351/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cc708adf186f28db09dfdd4ac43865a90482b2192306949eab01492598cf23e7'), size_on_disk=198070, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.610665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6532/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3041f19cf0dcb3e0bd9b1178994351a22f3297ccd43c5e291df8bfe0ad8d5d02'), size_on_disk=516437, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.8236568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6448/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/182dc134914a5ddb02992ab6a4f1c12d2450aa331820bf7bd95693dc999bcfae'), size_on_disk=390210, blob_last_accessed=1763591392.9591732, blob_last_modified=1758648268.672656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5654/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38961219eb66dec31505ebcc5088f16684be9891b96fffa8c9a13cd3f13254d0'), size_on_disk=447782, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.1876538), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/71a33d11c3ff714ce0730145c740eec4fe88812c6136b8d10b86fa8d4054991f'), size_on_disk=186500, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7286518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e871cf06f596bfe74896b62aa28d6b1e6f2f0b8408630fb146c7e29cfd88fbf0'), size_on_disk=232721, blob_last_accessed=1763591392.7871745, blob_last_modified=1758648267.991653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5986/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f1179c75ac6e91de03b67a73882f3b9c07c8e435d938d3059de489403adde7a'), size_on_disk=328749, blob_last_accessed=1763591392.691175, blob_last_modified=1758648268.228654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5614/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1aa702ff57d34ebed961043e0efae064bfdb62f589cbb515311ae8ea37ba8c76'), size_on_disk=429423, blob_last_accessed=1763591393.4671698, blob_last_modified=1758648268.0406532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a76f0849e518faedfe50e6f0c76908a3f6beed34fd0b607603db1859493595d0'), size_on_disk=292312, blob_last_accessed=1763587658.6168654, blob_last_modified=1758648266.7676473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7185/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/14f67668aca8d8f8aa2b8af98835da683a7fc3f22aa09cdcd7f943a37632391e'), size_on_disk=201650, blob_last_accessed=1763591393.2191715, blob_last_modified=1758648270.1076627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7cb5cca74f1bda2f8f1c8ef962e54d34228361eaa9dd8b21e0d1dd7092ff8164'), size_on_disk=229703, blob_last_accessed=1763591392.555176, blob_last_modified=1758648267.0126483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5961/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/49cac5c2df1adf790a9ac0209db6c0945de50b8191d403e8b1ad386ee2dcb35e'), size_on_disk=321368, blob_last_accessed=1763591392.560176, blob_last_modified=1758648268.215654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5301_5088/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/65ef775934339b80eda59a141cf791a74e1c6f12513d8143307cebe2f11c3738'), size_on_disk=410106, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.9516528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5a70fa6f22aeda3246d17646cc4ec7c5a46e1993f9981b19d9fd5a8cc99bf11f'), size_on_disk=308858, blob_last_accessed=1763591391.9411802, blob_last_modified=1758648267.0186484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dc74862f02ff12b8c906046f1f03953d33f5516091d58397b0f9a0fc05bb4b5c'), size_on_disk=315435, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.5006506), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6872/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02fabe6e32f52fe6fca923beb0a6cc2898d991304e23a3e75f3ae8a6bcf361c6'), size_on_disk=274362, blob_last_accessed=1763591393.6231687, blob_last_modified=1758648269.6966608), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6527/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/10081f44b1c2f5ad32489da51fc9ac5b5289b54688a7e78725db247a644b7e4d'), size_on_disk=494640, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.882657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e20f616f6e340b76f6446d2cdffb6cc9feb81297bd0307733f835acf4142cfd'), size_on_disk=269523, blob_last_accessed=1763591392.3461773, blob_last_modified=1758648267.2696495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bbe701ffd546a2f4359f821262f7b44232bdc328d9d4719bfdaa8f2aedc6d2e9'), size_on_disk=346079, blob_last_accessed=1763591393.0861723, blob_last_modified=1758648267.589651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6689/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/90ea249116dc6a9be86974e1ec0acf79558dfd56a5666be2e05d2107b8c4ee60'), size_on_disk=366787, blob_last_accessed=1763591393.6661685, blob_last_modified=1758648269.1406581), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6677/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4ee6e1d3e2b9a9bbcc8851048d2b16f4aea6a1e2f752afb9e0bf75a56adf7042'), size_on_disk=322147, blob_last_accessed=1763591392.393177, blob_last_modified=1758648269.0826578), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ab338795b8bbf334650386a123f6e0df41363cb5dcbb778d485ce58804e96b29'), size_on_disk=375506, blob_last_accessed=1763591392.9711733, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6920/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/45122fcfb3abc3a43631023712c224dc6e00dba566fb2e15deb4ab4501ad2538'), size_on_disk=446998, blob_last_accessed=1763591392.8881738, blob_last_modified=1758648269.6416605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0230df4f1d3748337e5315bb1d22a0373fdab95f9bc900041b0e34966d868718'), size_on_disk=311116, blob_last_accessed=1763591392.9241736, blob_last_modified=1758648267.2506495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6573/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e435989051765cf2955d8cd733904e8b60e5f3121e1c2fdc987f4e6f71fe3970'), size_on_disk=265022, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9586573), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5801/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/12e91a415cc2a149af4c34c3c8b032821ff301e2828dfb9f4636bd6b2913ea3d'), size_on_disk=360485, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.1636536), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7370/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ff4dce26afb34b291220c17625be4a28078f9721a8b000ea177c4add3bf1a039'), size_on_disk=241010, blob_last_accessed=1763591393.7701678, blob_last_modified=1758648270.6726654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c9b19726dc45896e4f86a5acc52d07cc422f895bfaaaddb332aaceedff36560c'), size_on_disk=178793, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0266485), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6421/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1e3bacd3bcf1fcb46cfc5f9ab66c2af57e190f3d006031e7ef37864fbd36aaca'), size_on_disk=495452, blob_last_accessed=1763591392.2161784, blob_last_modified=1758648268.6366558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ef036874e53ee98dfe1b5d84c42749fc40da9279cd573174321ac83201c1651'), size_on_disk=349762, blob_last_accessed=1763591393.0791724, blob_last_modified=1758648267.4966507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7115/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/205443fd87ce5c793dab86eb75c21dab7dc2ae2edac41d7e6314cfe491bdb01d'), size_on_disk=321850, blob_last_accessed=1763591393.2331715, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ac8c590c19e7b95b9aac571b1394976039f25fdd66e62a68ecb99925433f8e54'), size_on_disk=355706, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0226483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6437/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/556084dbbc174db5450872d6f5e66cc72091380ea32006977dd176e23c105a7f'), size_on_disk=389840, blob_last_accessed=1763591392.714175, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7155/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/521fc1bea6963640645c21cbdb8d1de07793f11213c23ac71d249f3252b32f54'), size_on_disk=304554, blob_last_accessed=1763591393.5841691, blob_last_modified=1758648270.1366627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5610/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a86512ae1000a994c86e9aebaf30cf9ce1a59160155f3dc1afa6cbde0a1fc47e'), size_on_disk=307382, blob_last_accessed=1763591393.5291693, blob_last_modified=1758648267.994653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4d098c33564f99ec6aed77d585d1f6e871806f441f099790eef4d6478e322af'), size_on_disk=264459, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7486472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7211/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73d7747698fa1168c89f12c105bbe900d5969d27a82ae2302d0720d761fbfdab'), size_on_disk=298482, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.1276627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6354/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/80b129a46defc7b06e472a35935bfaf410bc5e456475b2885feb2c8a4934a81b'), size_on_disk=173225, blob_last_accessed=1763591393.4981697, blob_last_modified=1758648268.4986553), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6177/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b91ffef1dfcdf7952df46956d76a4b364aa0d8342453636cf09e6a343607184f'), size_on_disk=387618, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.424655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1c8a8cb23b1ec073827d6f5343a3bf03a9d7a04cf2ae3fd3ea4cc5ac37673b38'), size_on_disk=342987, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.6226559), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=MAG001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/30b63d014686a103b5e7fc6571e902fa9c08630a96557cc12ec9dfcf60871306'), size_on_disk=347333, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.559651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7276/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4e0d576cc4a6a85be64f2198e5001a9f5b02b506072df6f695cfdfc719cb041'), size_on_disk=204036, blob_last_accessed=1763591392.987173, blob_last_modified=1758648270.4646642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6651/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7644af4cc956ea08809ab6c80c65095e3fdbd36e056040c0eaded3773d546359'), size_on_disk=559951, blob_last_accessed=1763591393.6361687, blob_last_modified=1758648269.1856585), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7367/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a7778bb24506047963575fa96126157895c7f329996c729c3e7d6070595955c3'), size_on_disk=324361, blob_last_accessed=1763591392.1631787, blob_last_modified=1758648270.628665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6770/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/082f14f7b0d5fc47e088efc40ae85a8ec41f99437c66da96ca69af210bce61cc'), size_on_disk=318118, blob_last_accessed=1763591392.8081744, blob_last_modified=1758648269.3776593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/74cbce50c258bdddb7e42c8fd5b0b2a9da74fe3cf89a9ab5a53ee4fde7c1dee6'), size_on_disk=186142, blob_last_accessed=1763591392.842174, blob_last_modified=1758648266.9966483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7118/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/be8bd409b83526d5671501de6ef2980177955fdcc3a13d5e153d2593493b2a1e'), size_on_disk=346308, blob_last_accessed=1763591392.9281735, blob_last_modified=1758648270.1336627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f427ea0f4ded3b6ff841153d8ad87d72a26a964c189246fb6fd5c439bd8b0c1e'), size_on_disk=353623, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.812652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/62e9a627f20607bc4887ebc9277131f974ec80ff39ac386775f4b8f2f5cb9d1d'), size_on_disk=346782, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7476518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bd2017e75947ead2c06f9d1936347f7fdcfac7fbff620e0b39077208a02c3bd9'), size_on_disk=303094, blob_last_accessed=1763591393.1271722, blob_last_modified=1758648269.9296618), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/11485fd7bb94d0093ce2508d541fea3ce64f934197d8a5f7bb26ecbdb59f8dbb'), size_on_disk=124642, blob_last_accessed=1763591392.3321776, blob_last_modified=1758648267.0666487), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d50648ed5c9f593f830aff661d13b3d34bd14f0bf93b16169df4765c9f47fc8'), size_on_disk=295924, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7426472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6545/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/260c4a280f52024cb12d44559a6189641ee02efbaf20168435196efbfc69d741'), size_on_disk=404812, blob_last_accessed=1763591392.3761773, blob_last_modified=1758648268.8256567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6924/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5e9b3a94d73c1720e6b05ea55000054905a8672edea7f950736ce3fe191f8faf'), size_on_disk=412939, blob_last_accessed=1763591393.748168, blob_last_modified=1758648269.7066607), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/530b5650d91dae3bf169f967af8e800611e3e01daeb1811d6fd21c2612ac4ad9'), size_on_disk=315170, blob_last_accessed=1763591393.6121688, blob_last_modified=1758648266.7956474), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6853/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ed47ecb2aa870fdbf12920fd4486933262c0a24318c9c8c5531e359a3c416c90'), size_on_disk=515114, blob_last_accessed=1763591393.2661712, blob_last_modified=1758648269.4276595), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6567/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0e9713a94594dfcbc80134af52449e12ef207ca9149f5a96c43b6cb457b2431c'), size_on_disk=478809, blob_last_accessed=1763591392.6241755, blob_last_modified=1758648268.893657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6635/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6c5b7f1fda3a7f5679870f438d13a45558f7f8da0f479c5144d7751bc8f7fbbc'), size_on_disk=420658, blob_last_accessed=1763591391.95018, blob_last_modified=1758648269.3956594), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7332/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f7f62ee008fa2b5cc688cbd80c888f1bf9da538caa245b63581520d485338a99'), size_on_disk=309595, blob_last_accessed=1763591393.1021724, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6506/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/986d9d3b4ed064d66ebaa0f2502b933f1650a20ac4bbbba804e2cde03b9454c0'), size_on_disk=378407, blob_last_accessed=1763591391.97318, blob_last_modified=1758648268.7656565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6513/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c8f9c31704c2f8732b4668e1e6410034b8adf2ada39ff55935aa0199e920d055'), size_on_disk=507367, blob_last_accessed=1763591392.1451788, blob_last_modified=1758648268.8406568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7256/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1af21cbdd7f6f6effff15a1b061a48f85132d582f08c8fd42e04ea58bde7ec00'), size_on_disk=206710, blob_last_accessed=1763591392.586176, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6739/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/58b5ed63f2197f406dd3df6a569975d776722193007912718979eaed4d4e5f9c'), size_on_disk=399160, blob_last_accessed=1763591392.6551754, blob_last_modified=1758648269.331659), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6731/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/23ecd71c4e3c1c547295bb756d4654eb413ff288d10118ab286cd21ccd95b666'), size_on_disk=346187, blob_last_accessed=1763591392.1871786, blob_last_modified=1758648269.1906583), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6b92129c2fb99028b6cab9509cf1a4c40b142109cde34c9a130d375001dcd211'), size_on_disk=267273, blob_last_accessed=1763591392.7811744, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS011/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ea7cb0559b342dd0ec278e7d5846a5ba0c0d867ea04d545f72e0d1c9472a0cc'), size_on_disk=363979, blob_last_accessed=1763591392.9791732, blob_last_modified=1758648267.0116484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5617/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bc01241e8363373c995a0fa7439aa1e432a60eb8a7805124fff302311f638361'), size_on_disk=351950, blob_last_accessed=1763591393.6541686, blob_last_modified=1758648268.1026535), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5399/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b65d0aa7ddc98940f91f2ae5f07e1d3f81dda688792552b91fcc39fb4f9ededa'), size_on_disk=256729, blob_last_accessed=1763591392.5471761, blob_last_modified=1758648267.972653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6140/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/66c2da17957d7e6a8b159d3573879ac779d0094e35150b51e2643f7ddee36e1f'), size_on_disk=332958, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4236548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6965/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9ae4546d58672f0632757c94b595af8b7c40da215611783fdc1a65000075ec98'), size_on_disk=509677, blob_last_accessed=1763591392.2731779, blob_last_modified=1758648269.933662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS006b/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7eedae811a5b3dd4389e7e814d35b022bfaf01fc4d2cfb47be6e6786d6d58c64'), size_on_disk=315912, blob_last_accessed=1763591393.2571712, blob_last_modified=1758648266.7546473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/00e5602b87ac84670a98c081e6520ca744038f4e9b3a72a78cb61f5926be56b1'), size_on_disk=251087, blob_last_accessed=1763591392.5781758, blob_last_modified=1758648267.8286521), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/646c843611bf88c7f5dcd5b1a664d724c3b24b6434cc52d9a54de62ccaf6c542'), size_on_disk=255949, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.4506505), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6923/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/454ca6a52d4b305eddc4b6967ef9604be4861269fdcfff419883daed9b2fe48b'), size_on_disk=337423, blob_last_accessed=1763591393.5031695, blob_last_modified=1758648269.6476605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fa1a44bd33df22ec6b5a8baf3be17e51a11522b5fdd545a38e6e163cc5ae8207'), size_on_disk=241911, blob_last_accessed=1763591392.572176, blob_last_modified=1758648267.5226507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7269/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5bae329d2318610f627fc562a5163253c7d61e0ef83c53808b220133b2299264'), size_on_disk=203263, blob_last_accessed=1763591392.541176, blob_last_modified=1758648270.4096642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5690/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4831e3e037d421a14efbe1da8c369015ec7d01df351bb27f0ef0400c8b94c693'), size_on_disk=311754, blob_last_accessed=1763591392.9021738, blob_last_modified=1758648268.211654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6443/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4deb4634e8b4bf141a2410e94b2d99e2b31a0e8f037de9a1c6e14696025bffae'), size_on_disk=494775, blob_last_accessed=1763591392.991173, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1fa09a5a6e6e69198501d994050adfcad7ff39a3cc2fe94e7ea177414e1bc968'), size_on_disk=303532, blob_last_accessed=1763591393.7091682, blob_last_modified=1758648267.2776496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6374/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e410602f56bc94d9831a2bc8bdb865ee4ebe9862a65b603165b655fbcc4b9914'), size_on_disk=441531, blob_last_accessed=1763591393.1891718, blob_last_modified=1758648268.5356555), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6551/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c3729692991cdcafe9baa3d2805b3866f72fe9bc89ba22c6d2cdae4d5b5bbf5f'), size_on_disk=545477, blob_last_accessed=1763591393.0611725, blob_last_modified=1758648268.8376567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7283/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0dc91bfacca82c5ed23de7757d59f9f808d541d96dd79aab56f0c4252e7aabf3'), size_on_disk=265146, blob_last_accessed=1763591392.7731745, blob_last_modified=1758648270.401664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7266/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9aea2d6f5606d24e1999d450379b1f8c8b9d80eded9e784fb729faa516a27026'), size_on_disk=324480, blob_last_accessed=1763591392.0401795, blob_last_modified=1758648270.379664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4e8332a5db6260f6357f940e62e3e7b622785f386e81cdb9c75b538d318f4a6'), size_on_disk=158978, blob_last_accessed=1763591393.5471692, blob_last_modified=1758648266.7626472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6708/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/35520976736159652e3be8066e77b1371522f63a8b3564d9b0d9f5b1598a144d'), size_on_disk=253955, blob_last_accessed=1763591393.6881683, blob_last_modified=1758648269.1996584), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d3e36418bd46acf9537a8f864b1a1361a062d72fec2b093789b88ed655ec117'), size_on_disk=377393, blob_last_accessed=1763591393.0451727, blob_last_modified=1758648267.0136483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6954/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/aeed3c55d81a4eb0c568486e476211c599ce2bf37f9dff22c7dfca23f5d80495'), size_on_disk=476054, blob_last_accessed=1763591393.011173, blob_last_modified=1758648269.943662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7331/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f159a9811b8ac1b5e252b52427979a3bddce2e7131b13f84046b31e47f7fa399'), size_on_disk=242821, blob_last_accessed=1763591393.2501714, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6073/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d68ca6411ba47eb63adad81e88fa1341a0ee269897268957ae936dcc9ba7788'), size_on_disk=450031, blob_last_accessed=1763591393.1341722, blob_last_modified=1758648268.3886547), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6061/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73e03845d0fbff1a5931e4f792e2c1965ade02fc622ffc08e05c267ac6cd3231'), size_on_disk=394930, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.447655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6055/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/31bb70b4c90654ecb276664f5c914679322b24985ec66d5b396a99f253ebfa17'), size_on_disk=377015, blob_last_accessed=1763591393.5701692, blob_last_modified=1758648268.3076544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6021/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eea0d101dd58c9619b41a94009fc0dff8156d2d36d7f2b88bb653e96123a032e'), size_on_disk=439545, blob_last_accessed=1763591393.5531693, blob_last_modified=1758648268.258654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e5b3174998823af40d055f46ca06e7bb44601cbe97b133062cd3e0491dd30ac2'), size_on_disk=323308, blob_last_accessed=1763591393.153172, blob_last_modified=1758648267.2686496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6657/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/95ad2225152f51e2e31c030ff6708aeebccee5e548035d6f43c9cc3554bdfe5e'), size_on_disk=507730, blob_last_accessed=1763591393.163172, blob_last_modified=1758648269.1676583), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7243/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cb59e645fff2683db692b4d2f514602ba210271b632cd113853febe941c6485f'), size_on_disk=281696, blob_last_accessed=1763591392.701175, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6572/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3488eaac5e4b9b3418b909138e6b50e3f9ebaf3e7345a4a10b6ced18b66152e4'), size_on_disk=440579, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9506574), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/d1cd38451bc41ef14dbc1566c4704b004b2c0661f667210c81dba09bec90540f'), size_on_disk=244487, blob_last_accessed=1763591393.6301687, blob_last_modified=1758648267.3246498), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ddd58117dbedfe7e9dc3fd946187e7bd5d88fe8b0f38999c6e4b5660938bb787'), size_on_disk=359739, blob_last_accessed=1763591393.5981688, blob_last_modified=1758648267.2726495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6983/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4ee29f84ab279c520521a6c5a5eed9e5e5581b2e39378bdd898eec67d13654f'), size_on_disk=410123, blob_last_accessed=1763591392.6751752, blob_last_modified=1758648269.8636615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/33d7303ed077f489ee28021daca0bbc859feb1cfd383dfc9a856ea13994801a9'), size_on_disk=331877, blob_last_accessed=1763591393.292171, blob_last_modified=1758648268.2226539), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6863/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec534c91cae4413d30477014131d39b51c9c236dfbf150874bd21ec3de821613'), size_on_disk=389025, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648269.6376605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR006B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fcc5e886ec7c6363a5ab51b03d3da189ac3e0cde0a0f79d4f0c46043a6b61838'), size_on_disk=253857, blob_last_accessed=1763591392.0501795, blob_last_modified=1758648267.785652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6855/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48e48378d24404e926bc1c44b10af60d4b12f82124ff2f80bc0b69b4a0f98740'), size_on_disk=609248, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6556606), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=MAG002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f7f35257d4c1f7fa5812ea31c1de791e08ab5e3a4e7ca42b2bb12b2c64f7ed7'), size_on_disk=490174, blob_last_accessed=1763591392.829174, blob_last_modified=1758648267.583651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6808/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c71f5371e8c803cfc7fd507d0b3c11f7548db5d7a0fa42c68977169369ea036e'), size_on_disk=422880, blob_last_accessed=1763591392.9491735, blob_last_modified=1758648269.3916593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7137/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/703fe9f8552adcbdfb5e640509cb62df2fa734b43fe1e98fc1fd342415f823d7'), size_on_disk=515398, blob_last_accessed=1763589053.7698598, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fc9311b65abb851fbbfd8bb0c0765af59069659f5a715aee546a8644b0af4ec2'), size_on_disk=210948, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648267.6766515), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=KB001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38c939d54d3ed77a09650d2ccf00a6ed0e20390b8be722f1c3ed7881a5904a60'), size_on_disk=351677, blob_last_accessed=1763591392.9151735, blob_last_modified=1758648267.5566509), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dcb33135a9d854d17a6b80348f6bee8becea9ca3808583a034f89c5e4f460c54'), size_on_disk=218643, blob_last_accessed=1763591393.4831698, blob_last_modified=1758648267.9636528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eb61968b5ec5eadd9087e42ead29333f91195dd5b3f9c5a616b41e94ca23c494'), size_on_disk=330245, blob_last_accessed=1763591393.6761684, blob_last_modified=1758648269.710661), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7151/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9969e4607d6bf42424f5d2ce8f0d16e3d2d89d069cae6d75997e99c88a68dc77'), size_on_disk=275572, blob_last_accessed=1763591393.7231681, blob_last_modified=1758648270.1476629), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR007B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6f0620b102e7cbfadced7d5530545a185036b53118d3e2b7f074c56403d80f4b'), size_on_disk=263185, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7616518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6738/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e6d612d16538a06c985f1134f3fdfd367944b2a85cedfb8b0ddad1679004c940'), size_on_disk=333560, blob_last_accessed=1763591392.8651738, blob_last_modified=1758648269.3676593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7240/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4460d188b4cd3b362f33acf0f0b5312a267b37f8fb2dab4b2577c6c3bbb1231'), size_on_disk=307039, blob_last_accessed=1763591392.6101756, blob_last_modified=1758648270.403664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7258/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7b1860ba3be7dbf053203fa5690da2002f7841186940231acea6064e70f0bdc8'), size_on_disk=171331, blob_last_accessed=1763591392.6171756, blob_last_modified=1758648270.3746638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6959/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c0cb6c2ec2bda19b0c1da5d5fc2229d0b6d6987d75646fa230c0c14d591cf1f1'), size_on_disk=537588, blob_last_accessed=1763591392.851174, blob_last_modified=1758648269.8686616), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02313d4d85859a7353e56d89b6e14bcec5160849b5c0089ccbecaa1cda5012cf'), size_on_disk=301213, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4356549), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6861/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6955b1a1746fe41162ea1d7a098482bbfdb3d0e411c809b52d698e285627ce03'), size_on_disk=385103, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6146603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6640/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48339729112d22aa5aabf411674bfb90a567ec4c7a07ea7930738645a96823ef'), size_on_disk=329042, blob_last_accessed=1763591392.6391754, blob_last_modified=1758648269.090658), CachedFileInfo(file_name='annotated_features_meta.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features_meta.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/2be4b693d333698f7ada7ef40ffaf927c0160d7975ebcf6e215cbc4ba1be1604'), size_on_disk=11813, blob_last_accessed=1763587594.724117, blob_last_modified=1758648264.795638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6390/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f324cdf73148c8b0fa23456bc4f51cff3032d08ed42e44dd9fcc4815a6d969c'), size_on_disk=560601, blob_last_accessed=1763591392.7391748, blob_last_modified=1758648268.6206558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6798/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/899cde55032cd593be0013e1f6ff33fe9a12f4616477b1410d86fae1b2008a09'), size_on_disk=342376, blob_last_accessed=1763591392.7951744, blob_last_modified=1758648269.4006593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7295/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/19bd07f913e0af3460e6b4b5091971ceed8f4aa510cf18b82ec36dabb67c68b8'), size_on_disk=272619, blob_last_accessed=1763591393.1151721, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6764/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec87eef9e2e8cdf3c3a10ead28dcf1bfa76e94e12a42422a46c8559fb18207c2'), size_on_disk=75684, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.4036593)}), refs=frozenset(), last_modified=1758648270.6726654), CachedRevisionInfo(commit_hash='6864b2582ce35f92a8dde59851786319c2c494bc', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc'), size_on_disk=49590351, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS011/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ea7cb0559b342dd0ec278e7d5846a5ba0c0d867ea04d545f72e0d1c9472a0cc'), size_on_disk=363979, blob_last_accessed=1763591392.9791732, blob_last_modified=1758648267.0116484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6635/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6c5b7f1fda3a7f5679870f438d13a45558f7f8da0f479c5144d7751bc8f7fbbc'), size_on_disk=420658, blob_last_accessed=1763591391.95018, blob_last_modified=1758648269.3956594), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e20f616f6e340b76f6446d2cdffb6cc9feb81297bd0307733f835acf4142cfd'), size_on_disk=269523, blob_last_accessed=1763591392.3461773, blob_last_modified=1758648267.2696495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6872/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02fabe6e32f52fe6fca923beb0a6cc2898d991304e23a3e75f3ae8a6bcf361c6'), size_on_disk=274362, blob_last_accessed=1763591393.6231687, blob_last_modified=1758648269.6966608), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e5b3174998823af40d055f46ca06e7bb44601cbe97b133062cd3e0491dd30ac2'), size_on_disk=323308, blob_last_accessed=1763591393.153172, blob_last_modified=1758648267.2686496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/74cbce50c258bdddb7e42c8fd5b0b2a9da74fe3cf89a9ab5a53ee4fde7c1dee6'), size_on_disk=186142, blob_last_accessed=1763591392.842174, blob_last_modified=1758648266.9966483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ab338795b8bbf334650386a123f6e0df41363cb5dcbb778d485ce58804e96b29'), size_on_disk=375506, blob_last_accessed=1763591392.9711733, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e871cf06f596bfe74896b62aa28d6b1e6f2f0b8408630fb146c7e29cfd88fbf0'), size_on_disk=232721, blob_last_accessed=1763591392.7871745, blob_last_modified=1758648267.991653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f427ea0f4ded3b6ff841153d8ad87d72a26a964c189246fb6fd5c439bd8b0c1e'), size_on_disk=353623, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.812652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5654/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38961219eb66dec31505ebcc5088f16684be9891b96fffa8c9a13cd3f13254d0'), size_on_disk=447782, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.1876538), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7063/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9427d8f8c54e3c0cc9d720958ef15d3859f00cdfb678559e0268387a36a8ef37'), size_on_disk=404051, blob_last_accessed=1763591392.5301762, blob_last_modified=1758648269.8976617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5690/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4831e3e037d421a14efbe1da8c369015ec7d01df351bb27f0ef0400c8b94c693'), size_on_disk=311754, blob_last_accessed=1763591392.9021738, blob_last_modified=1758648268.211654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d4c0cbbf6075a8b5732e3cef2ba154d95d21aa0efc7e49d947cceebd8742b30'), size_on_disk=269007, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4106548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bbe701ffd546a2f4359f821262f7b44232bdc328d9d4719bfdaa8f2aedc6d2e9'), size_on_disk=346079, blob_last_accessed=1763591393.0861723, blob_last_modified=1758648267.589651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6853/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ed47ecb2aa870fdbf12920fd4486933262c0a24318c9c8c5531e359a3c416c90'), size_on_disk=515114, blob_last_accessed=1763591393.2661712, blob_last_modified=1758648269.4276595), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6764/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec87eef9e2e8cdf3c3a10ead28dcf1bfa76e94e12a42422a46c8559fb18207c2'), size_on_disk=75684, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.4036593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6855/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48e48378d24404e926bc1c44b10af60d4b12f82124ff2f80bc0b69b4a0f98740'), size_on_disk=609248, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6556606), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6640/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48339729112d22aa5aabf411674bfb90a567ec4c7a07ea7930738645a96823ef'), size_on_disk=329042, blob_last_accessed=1763591392.6391754, blob_last_modified=1758648269.090658), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7332/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f7f62ee008fa2b5cc688cbd80c888f1bf9da538caa245b63581520d485338a99'), size_on_disk=309595, blob_last_accessed=1763591393.1021724, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7243/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cb59e645fff2683db692b4d2f514602ba210271b632cd113853febe941c6485f'), size_on_disk=281696, blob_last_accessed=1763591392.701175, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7295/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/19bd07f913e0af3460e6b4b5091971ceed8f4aa510cf18b82ec36dabb67c68b8'), size_on_disk=272619, blob_last_accessed=1763591393.1151721, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/530b5650d91dae3bf169f967af8e800611e3e01daeb1811d6fd21c2612ac4ad9'), size_on_disk=315170, blob_last_accessed=1763591393.6121688, blob_last_modified=1758648266.7956474), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5301_5088/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/65ef775934339b80eda59a141cf791a74e1c6f12513d8143307cebe2f11c3738'), size_on_disk=410106, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.9516528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7283/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0dc91bfacca82c5ed23de7757d59f9f808d541d96dd79aab56f0c4252e7aabf3'), size_on_disk=265146, blob_last_accessed=1763591392.7731745, blob_last_modified=1758648270.401664), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fc266bb938fa146c46d1a7bd5446d5d27b3bf202'), size_on_disk=10887, blob_last_accessed=1763649507.5438576, blob_last_modified=1763649507.5418575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fa1a44bd33df22ec6b5a8baf3be17e51a11522b5fdd545a38e6e163cc5ae8207'), size_on_disk=241911, blob_last_accessed=1763591392.572176, blob_last_modified=1758648267.5226507), CachedFileInfo(file_name='annotated_features_meta.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features_meta.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/2be4b693d333698f7ada7ef40ffaf927c0160d7975ebcf6e215cbc4ba1be1604'), size_on_disk=11813, blob_last_accessed=1763587594.724117, blob_last_modified=1758648264.795638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=MAG002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f7f35257d4c1f7fa5812ea31c1de791e08ab5e3a4e7ca42b2bb12b2c64f7ed7'), size_on_disk=490174, blob_last_accessed=1763591392.829174, blob_last_modified=1758648267.583651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7118/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/be8bd409b83526d5671501de6ef2980177955fdcc3a13d5e153d2593493b2a1e'), size_on_disk=346308, blob_last_accessed=1763591392.9281735, blob_last_modified=1758648270.1336627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4e8332a5db6260f6357f940e62e3e7b622785f386e81cdb9c75b538d318f4a6'), size_on_disk=158978, blob_last_accessed=1763591393.5471692, blob_last_modified=1758648266.7626472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6920/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/45122fcfb3abc3a43631023712c224dc6e00dba566fb2e15deb4ab4501ad2538'), size_on_disk=446998, blob_last_accessed=1763591392.8881738, blob_last_modified=1758648269.6416605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR006B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fcc5e886ec7c6363a5ab51b03d3da189ac3e0cde0a0f79d4f0c46043a6b61838'), size_on_disk=253857, blob_last_accessed=1763591392.0501795, blob_last_modified=1758648267.785652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5801/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/12e91a415cc2a149af4c34c3c8b032821ff301e2828dfb9f4636bd6b2913ea3d'), size_on_disk=360485, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.1636536), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6390/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f324cdf73148c8b0fa23456bc4f51cff3032d08ed42e44dd9fcc4815a6d969c'), size_on_disk=560601, blob_last_accessed=1763591392.7391748, blob_last_modified=1758648268.6206558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=MAG001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/30b63d014686a103b5e7fc6571e902fa9c08630a96557cc12ec9dfcf60871306'), size_on_disk=347333, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.559651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6983/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4ee29f84ab279c520521a6c5a5eed9e5e5581b2e39378bdd898eec67d13654f'), size_on_disk=410123, blob_last_accessed=1763591392.6751752, blob_last_modified=1758648269.8636615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7370/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ff4dce26afb34b291220c17625be4a28078f9721a8b000ea177c4add3bf1a039'), size_on_disk=241010, blob_last_accessed=1763591393.7701678, blob_last_modified=1758648270.6726654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6532/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3041f19cf0dcb3e0bd9b1178994351a22f3297ccd43c5e291df8bfe0ad8d5d02'), size_on_disk=516437, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.8236568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1c8a8cb23b1ec073827d6f5343a3bf03a9d7a04cf2ae3fd3ea4cc5ac37673b38'), size_on_disk=342987, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.6226559), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e91fd764a83947c14f35d7a2d8c0d156c0a61a32a1da96273718ef4be720787'), size_on_disk=361855, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.7196517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/11485fd7bb94d0093ce2508d541fea3ce64f934197d8a5f7bb26ecbdb59f8dbb'), size_on_disk=124642, blob_last_accessed=1763591392.3321776, blob_last_modified=1758648267.0666487), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6572/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3488eaac5e4b9b3418b909138e6b50e3f9ebaf3e7345a4a10b6ced18b66152e4'), size_on_disk=440579, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9506574), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c9b19726dc45896e4f86a5acc52d07cc422f895bfaaaddb332aaceedff36560c'), size_on_disk=178793, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0266485), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7331/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f159a9811b8ac1b5e252b52427979a3bddce2e7131b13f84046b31e47f7fa399'), size_on_disk=242821, blob_last_accessed=1763591393.2501714, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6437/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/556084dbbc174db5450872d6f5e66cc72091380ea32006977dd176e23c105a7f'), size_on_disk=389840, blob_last_accessed=1763591392.714175, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fc9311b65abb851fbbfd8bb0c0765af59069659f5a715aee546a8644b0af4ec2'), size_on_disk=210948, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648267.6766515), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7351/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cc708adf186f28db09dfdd4ac43865a90482b2192306949eab01492598cf23e7'), size_on_disk=198070, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.610665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4d098c33564f99ec6aed77d585d1f6e871806f441f099790eef4d6478e322af'), size_on_disk=264459, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7486472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6861/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6955b1a1746fe41162ea1d7a098482bbfdb3d0e411c809b52d698e285627ce03'), size_on_disk=385103, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6146603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6959/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c0cb6c2ec2bda19b0c1da5d5fc2229d0b6d6987d75646fa230c0c14d591cf1f1'), size_on_disk=537588, blob_last_accessed=1763591392.851174, blob_last_modified=1758648269.8686616), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6923/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/454ca6a52d4b305eddc4b6967ef9604be4861269fdcfff419883daed9b2fe48b'), size_on_disk=337423, blob_last_accessed=1763591393.5031695, blob_last_modified=1758648269.6476605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7137/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/703fe9f8552adcbdfb5e640509cb62df2fa734b43fe1e98fc1fd342415f823d7'), size_on_disk=515398, blob_last_accessed=1763589053.7698598, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9f7663ccf72b0681ae5866c13500fbde98f35877fd677ac0d49c17447d17d6ce'), size_on_disk=332767, blob_last_accessed=1763591392.1731787, blob_last_modified=1758648266.744647), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6021/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eea0d101dd58c9619b41a94009fc0dff8156d2d36d7f2b88bb653e96123a032e'), size_on_disk=439545, blob_last_accessed=1763591393.5531693, blob_last_modified=1758648268.258654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/71a33d11c3ff714ce0730145c740eec4fe88812c6136b8d10b86fa8d4054991f'), size_on_disk=186500, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7286518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6965/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9ae4546d58672f0632757c94b595af8b7c40da215611783fdc1a65000075ec98'), size_on_disk=509677, blob_last_accessed=1763591392.2731779, blob_last_modified=1758648269.933662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5986/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f1179c75ac6e91de03b67a73882f3b9c07c8e435d938d3059de489403adde7a'), size_on_disk=328749, blob_last_accessed=1763591392.691175, blob_last_modified=1758648268.228654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6551/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c3729692991cdcafe9baa3d2805b3866f72fe9bc89ba22c6d2cdae4d5b5bbf5f'), size_on_disk=545477, blob_last_accessed=1763591393.0611725, blob_last_modified=1758648268.8376567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/128015caa722289251a45aa3d56cabac6b5946eb4e1be16eb950555fb24ac413'), size_on_disk=172328, blob_last_accessed=1763591393.735168, blob_last_modified=1758648267.2826495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6443/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4deb4634e8b4bf141a2410e94b2d99e2b31a0e8f037de9a1c6e14696025bffae'), size_on_disk=494775, blob_last_accessed=1763591392.991173, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5614/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1aa702ff57d34ebed961043e0efae064bfdb62f589cbb515311ae8ea37ba8c76'), size_on_disk=429423, blob_last_accessed=1763591393.4671698, blob_last_modified=1758648268.0406532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6448/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/182dc134914a5ddb02992ab6a4f1c12d2450aa331820bf7bd95693dc999bcfae'), size_on_disk=390210, blob_last_accessed=1763591392.9591732, blob_last_modified=1758648268.672656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7367/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a7778bb24506047963575fa96126157895c7f329996c729c3e7d6070595955c3'), size_on_disk=324361, blob_last_accessed=1763591392.1631787, blob_last_modified=1758648270.628665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ef036874e53ee98dfe1b5d84c42749fc40da9279cd573174321ac83201c1651'), size_on_disk=349762, blob_last_accessed=1763591393.0791724, blob_last_modified=1758648267.4966507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6b92129c2fb99028b6cab9509cf1a4c40b142109cde34c9a130d375001dcd211'), size_on_disk=267273, blob_last_accessed=1763591392.7811744, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6506/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/986d9d3b4ed064d66ebaa0f2502b933f1650a20ac4bbbba804e2cde03b9454c0'), size_on_disk=378407, blob_last_accessed=1763591391.97318, blob_last_modified=1758648268.7656565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7155/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/521fc1bea6963640645c21cbdb8d1de07793f11213c23ac71d249f3252b32f54'), size_on_disk=304554, blob_last_accessed=1763591393.5841691, blob_last_modified=1758648270.1366627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6924/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5e9b3a94d73c1720e6b05ea55000054905a8672edea7f950736ce3fe191f8faf'), size_on_disk=412939, blob_last_accessed=1763591393.748168, blob_last_modified=1758648269.7066607), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/646c843611bf88c7f5dcd5b1a664d724c3b24b6434cc52d9a54de62ccaf6c542'), size_on_disk=255949, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.4506505), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/00e5602b87ac84670a98c081e6520ca744038f4e9b3a72a78cb61f5926be56b1'), size_on_disk=251087, blob_last_accessed=1763591392.5781758, blob_last_modified=1758648267.8286521), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ddd58117dbedfe7e9dc3fd946187e7bd5d88fe8b0f38999c6e4b5660938bb787'), size_on_disk=359739, blob_last_accessed=1763591393.5981688, blob_last_modified=1758648267.2726495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6677/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4ee6e1d3e2b9a9bbcc8851048d2b16f4aea6a1e2f752afb9e0bf75a56adf7042'), size_on_disk=322147, blob_last_accessed=1763591392.393177, blob_last_modified=1758648269.0826578), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7240/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4460d188b4cd3b362f33acf0f0b5312a267b37f8fb2dab4b2577c6c3bbb1231'), size_on_disk=307039, blob_last_accessed=1763591392.6101756, blob_last_modified=1758648270.403664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6454/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c35b60861d2fc7a3f4256869e14e629aed07cd2ccce76608578ceb8c6ec423e1'), size_on_disk=498326, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.7226562), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6374/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e410602f56bc94d9831a2bc8bdb865ee4ebe9862a65b603165b655fbcc4b9914'), size_on_disk=441531, blob_last_accessed=1763591393.1891718, blob_last_modified=1758648268.5356555), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7185/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/14f67668aca8d8f8aa2b8af98835da683a7fc3f22aa09cdcd7f943a37632391e'), size_on_disk=201650, blob_last_accessed=1763591393.2191715, blob_last_modified=1758648270.1076627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS006b/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7eedae811a5b3dd4389e7e814d35b022bfaf01fc4d2cfb47be6e6786d6d58c64'), size_on_disk=315912, blob_last_accessed=1763591393.2571712, blob_last_modified=1758648266.7546473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/62e9a627f20607bc4887ebc9277131f974ec80ff39ac386775f4b8f2f5cb9d1d'), size_on_disk=346782, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7476518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6708/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/35520976736159652e3be8066e77b1371522f63a8b3564d9b0d9f5b1598a144d'), size_on_disk=253955, blob_last_accessed=1763591393.6881683, blob_last_modified=1758648269.1996584), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d3e36418bd46acf9537a8f864b1a1361a062d72fec2b093789b88ed655ec117'), size_on_disk=377393, blob_last_accessed=1763591393.0451727, blob_last_modified=1758648267.0136483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6061/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73e03845d0fbff1a5931e4f792e2c1965ade02fc622ffc08e05c267ac6cd3231'), size_on_disk=394930, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.447655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6731/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/23ecd71c4e3c1c547295bb756d4654eb413ff288d10118ab286cd21ccd95b666'), size_on_disk=346187, blob_last_accessed=1763591392.1871786, blob_last_modified=1758648269.1906583), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6573/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e435989051765cf2955d8cd733904e8b60e5f3121e1c2fdc987f4e6f71fe3970'), size_on_disk=265022, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9586573), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/d1cd38451bc41ef14dbc1566c4704b004b2c0661f667210c81dba09bec90540f'), size_on_disk=244487, blob_last_accessed=1763591393.6301687, blob_last_modified=1758648267.3246498), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7276/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4e0d576cc4a6a85be64f2198e5001a9f5b02b506072df6f695cfdfc719cb041'), size_on_disk=204036, blob_last_accessed=1763591392.987173, blob_last_modified=1758648270.4646642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6738/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e6d612d16538a06c985f1134f3fdfd367944b2a85cedfb8b0ddad1679004c940'), size_on_disk=333560, blob_last_accessed=1763591392.8651738, blob_last_modified=1758648269.3676593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6055/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/31bb70b4c90654ecb276664f5c914679322b24985ec66d5b396a99f253ebfa17'), size_on_disk=377015, blob_last_accessed=1763591393.5701692, blob_last_modified=1758648268.3076544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7269/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5bae329d2318610f627fc562a5163253c7d61e0ef83c53808b220133b2299264'), size_on_disk=203263, blob_last_accessed=1763591392.541176, blob_last_modified=1758648270.4096642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dcb33135a9d854d17a6b80348f6bee8becea9ca3808583a034f89c5e4f460c54'), size_on_disk=218643, blob_last_accessed=1763591393.4831698, blob_last_modified=1758648267.9636528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6689/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/90ea249116dc6a9be86974e1ec0acf79558dfd56a5666be2e05d2107b8c4ee60'), size_on_disk=366787, blob_last_accessed=1763591393.6661685, blob_last_modified=1758648269.1406581), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dc74862f02ff12b8c906046f1f03953d33f5516091d58397b0f9a0fc05bb4b5c'), size_on_disk=315435, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.5006506), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7266/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9aea2d6f5606d24e1999d450379b1f8c8b9d80eded9e784fb729faa516a27026'), size_on_disk=324480, blob_last_accessed=1763591392.0401795, blob_last_modified=1758648270.379664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6863/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec534c91cae4413d30477014131d39b51c9c236dfbf150874bd21ec3de821613'), size_on_disk=389025, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648269.6376605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7297/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4bd4088cbfcccdf96def916bb8fd9d673327d3e57c2a4a3e57299743ce4aca7'), size_on_disk=261344, blob_last_accessed=1763591393.588169, blob_last_modified=1758648270.6516652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eb61968b5ec5eadd9087e42ead29333f91195dd5b3f9c5a616b41e94ca23c494'), size_on_disk=330245, blob_last_accessed=1763591393.6761684, blob_last_modified=1758648269.710661), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6140/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/66c2da17957d7e6a8b159d3573879ac779d0094e35150b51e2643f7ddee36e1f'), size_on_disk=332958, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4236548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6513/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c8f9c31704c2f8732b4668e1e6410034b8adf2ada39ff55935aa0199e920d055'), size_on_disk=507367, blob_last_accessed=1763591392.1451788, blob_last_modified=1758648268.8406568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7256/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1af21cbdd7f6f6effff15a1b061a48f85132d582f08c8fd42e04ea58bde7ec00'), size_on_disk=206710, blob_last_accessed=1763591392.586176, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6421/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1e3bacd3bcf1fcb46cfc5f9ab66c2af57e190f3d006031e7ef37864fbd36aaca'), size_on_disk=495452, blob_last_accessed=1763591392.2161784, blob_last_modified=1758648268.6366558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1fa09a5a6e6e69198501d994050adfcad7ff39a3cc2fe94e7ea177414e1bc968'), size_on_disk=303532, blob_last_accessed=1763591393.7091682, blob_last_modified=1758648267.2776496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6567/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0e9713a94594dfcbc80134af52449e12ef207ca9149f5a96c43b6cb457b2431c'), size_on_disk=478809, blob_last_accessed=1763591392.6241755, blob_last_modified=1758648268.893657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d50648ed5c9f593f830aff661d13b3d34bd14f0bf93b16169df4765c9f47fc8'), size_on_disk=295924, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7426472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7cb5cca74f1bda2f8f1c8ef962e54d34228361eaa9dd8b21e0d1dd7092ff8164'), size_on_disk=229703, blob_last_accessed=1763591392.555176, blob_last_modified=1758648267.0126483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02313d4d85859a7353e56d89b6e14bcec5160849b5c0089ccbecaa1cda5012cf'), size_on_disk=301213, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4356549), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6527/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/10081f44b1c2f5ad32489da51fc9ac5b5289b54688a7e78725db247a644b7e4d'), size_on_disk=494640, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.882657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6739/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/58b5ed63f2197f406dd3df6a569975d776722193007912718979eaed4d4e5f9c'), size_on_disk=399160, blob_last_accessed=1763591392.6551754, blob_last_modified=1758648269.331659), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6177/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b91ffef1dfcdf7952df46956d76a4b364aa0d8342453636cf09e6a343607184f'), size_on_disk=387618, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.424655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5961/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/49cac5c2df1adf790a9ac0209db6c0945de50b8191d403e8b1ad386ee2dcb35e'), size_on_disk=321368, blob_last_accessed=1763591392.560176, blob_last_modified=1758648268.215654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6545/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/260c4a280f52024cb12d44559a6189641ee02efbaf20168435196efbfc69d741'), size_on_disk=404812, blob_last_accessed=1763591392.3761773, blob_last_modified=1758648268.8256567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/33d7303ed077f489ee28021daca0bbc859feb1cfd383dfc9a856ea13994801a9'), size_on_disk=331877, blob_last_accessed=1763591393.292171, blob_last_modified=1758648268.2226539), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR007B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6f0620b102e7cbfadced7d5530545a185036b53118d3e2b7f074c56403d80f4b'), size_on_disk=263185, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7616518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6808/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c71f5371e8c803cfc7fd507d0b3c11f7548db5d7a0fa42c68977169369ea036e'), size_on_disk=422880, blob_last_accessed=1763591392.9491735, blob_last_modified=1758648269.3916593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6651/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7644af4cc956ea08809ab6c80c65095e3fdbd36e056040c0eaded3773d546359'), size_on_disk=559951, blob_last_accessed=1763591393.6361687, blob_last_modified=1758648269.1856585), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=composite/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1f8adf8fb4992283714df8587126211d54e6a0580b8be88c1d5e911196aed783'), size_on_disk=4814573, blob_last_accessed=1763591392.0561793, blob_last_modified=1758648268.0256531), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7237/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/110a743162ace7792a44dfe48d7afd40bb67c45c722038c981548477ebc5d8fe'), size_on_disk=313207, blob_last_accessed=1763591393.143172, blob_last_modified=1758648270.1426628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0230df4f1d3748337e5315bb1d22a0373fdab95f9bc900041b0e34966d868718'), size_on_disk=311116, blob_last_accessed=1763591392.9241736, blob_last_modified=1758648267.2506495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7258/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7b1860ba3be7dbf053203fa5690da2002f7841186940231acea6064e70f0bdc8'), size_on_disk=171331, blob_last_accessed=1763591392.6171756, blob_last_modified=1758648270.3746638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ac8c590c19e7b95b9aac571b1394976039f25fdd66e62a68ecb99925433f8e54'), size_on_disk=355706, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0226483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4b2ff192aa4808cb50b1c1b1667060f98505dd63c1aa58c7d8354e41194957e4'), size_on_disk=289767, blob_last_accessed=1763591393.4871697, blob_last_modified=1758648266.7706473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7211/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73d7747698fa1168c89f12c105bbe900d5969d27a82ae2302d0720d761fbfdab'), size_on_disk=298482, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.1276627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5610/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a86512ae1000a994c86e9aebaf30cf9ce1a59160155f3dc1afa6cbde0a1fc47e'), size_on_disk=307382, blob_last_accessed=1763591393.5291693, blob_last_modified=1758648267.994653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bd2017e75947ead2c06f9d1936347f7fdcfac7fbff620e0b39077208a02c3bd9'), size_on_disk=303094, blob_last_accessed=1763591393.1271722, blob_last_modified=1758648269.9296618), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7151/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9969e4607d6bf42424f5d2ce8f0d16e3d2d89d069cae6d75997e99c88a68dc77'), size_on_disk=275572, blob_last_accessed=1763591393.7231681, blob_last_modified=1758648270.1476629), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6798/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/899cde55032cd593be0013e1f6ff33fe9a12f4616477b1410d86fae1b2008a09'), size_on_disk=342376, blob_last_accessed=1763591392.7951744, blob_last_modified=1758648269.4006593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6073/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d68ca6411ba47eb63adad81e88fa1341a0ee269897268957ae936dcc9ba7788'), size_on_disk=450031, blob_last_accessed=1763591393.1341722, blob_last_modified=1758648268.3886547), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5a70fa6f22aeda3246d17646cc4ec7c5a46e1993f9981b19d9fd5a8cc99bf11f'), size_on_disk=308858, blob_last_accessed=1763591391.9411802, blob_last_modified=1758648267.0186484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5617/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bc01241e8363373c995a0fa7439aa1e432a60eb8a7805124fff302311f638361'), size_on_disk=351950, blob_last_accessed=1763591393.6541686, blob_last_modified=1758648268.1026535), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=KB001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38c939d54d3ed77a09650d2ccf00a6ed0e20390b8be722f1c3ed7881a5904a60'), size_on_disk=351677, blob_last_accessed=1763591392.9151735, blob_last_modified=1758648267.5566509), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7115/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/205443fd87ce5c793dab86eb75c21dab7dc2ae2edac41d7e6314cfe491bdb01d'), size_on_disk=321850, blob_last_accessed=1763591393.2331715, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6657/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/95ad2225152f51e2e31c030ff6708aeebccee5e548035d6f43c9cc3554bdfe5e'), size_on_disk=507730, blob_last_accessed=1763591393.163172, blob_last_modified=1758648269.1676583), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6954/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/aeed3c55d81a4eb0c568486e476211c599ce2bf37f9dff22c7dfca23f5d80495'), size_on_disk=476054, blob_last_accessed=1763591393.011173, blob_last_modified=1758648269.943662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5399/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b65d0aa7ddc98940f91f2ae5f07e1d3f81dda688792552b91fcc39fb4f9ededa'), size_on_disk=256729, blob_last_accessed=1763591392.5471761, blob_last_modified=1758648267.972653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a76f0849e518faedfe50e6f0c76908a3f6beed34fd0b607603db1859493595d0'), size_on_disk=292312, blob_last_accessed=1763587658.6168654, blob_last_modified=1758648266.7676473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6354/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/80b129a46defc7b06e472a35935bfaf410bc5e456475b2885feb2c8a4934a81b'), size_on_disk=173225, blob_last_accessed=1763591393.4981697, blob_last_modified=1758648268.4986553), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6770/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/082f14f7b0d5fc47e088efc40ae85a8ec41f99437c66da96ca69af210bce61cc'), size_on_disk=318118, blob_last_accessed=1763591392.8081744, blob_last_modified=1758648269.3776593)}), refs=frozenset({'main'}), last_modified=1763649507.5418575)}), last_accessed=1763649507.5438576, last_modified=1763649507.5418575), CachedRepoInfo(repo_id='BrentLab/rossi_2021', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021'), size_on_disk=213734041, nb_files=48, revisions=frozenset({CachedRevisionInfo(commit_hash='22ebbfcf62a7ed665fb8eceaea53b58b45a1709e', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/22ebbfcf62a7ed665fb8eceaea53b58b45a1709e'), size_on_disk=4602, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/22ebbfcf62a7ed665fb8eceaea53b58b45a1709e/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/e03b8d10ba59c549907a309ad8343133b7b902fc'), size_on_disk=4602, blob_last_accessed=1757613581.062093, blob_last_modified=1757613581.0610929)}), refs=frozenset(), last_modified=1757613581.0610929), CachedRevisionInfo(commit_hash='3ce77eb2070d7bb43049ba26bb462fded0ff7863', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/3ce77eb2070d7bb43049ba26bb462fded0ff7863'), size_on_disk=15562566, files=frozenset({CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/3ce77eb2070d7bb43049ba26bb462fded0ff7863/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1762805126.6580298, blob_last_modified=1756944310.9395182), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/3ce77eb2070d7bb43049ba26bb462fded0ff7863/genome_map/accession=SRR11466106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/04bacc0f8c03ee000ba3b69320522d0b1be909686474b6e8a126173a0ceae8c4'), size_on_disk=15547870, blob_last_accessed=1762805142.7999256, blob_last_modified=1756944357.1972551)}), refs=frozenset(), last_modified=1756944357.1972551), CachedRevisionInfo(commit_hash='6aac54b4b3fbb414561a8b6cc7acf87880c3c3c2', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6aac54b4b3fbb414561a8b6cc7acf87880c3c3c2'), size_on_disk=4665, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6aac54b4b3fbb414561a8b6cc7acf87880c3c3c2/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/14e1e02ad3804c0a4146a52509460e9cbb759d93'), size_on_disk=4665, blob_last_accessed=1758306843.4906857, blob_last_modified=1758156479.1516545)}), refs=frozenset(), last_modified=1758156479.1516545), CachedRevisionInfo(commit_hash='824bf517ff0722a085c86ac7924df0ab1278c8bf', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/824bf517ff0722a085c86ac7924df0ab1278c8bf'), size_on_disk=14696, files=frozenset({CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/824bf517ff0722a085c86ac7924df0ab1278c8bf/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1762805126.6580298, blob_last_modified=1756944310.9395182)}), refs=frozenset(), last_modified=1756944310.9395182), CachedRevisionInfo(commit_hash='dcf529428b66f5efc8c44ea06fb4733c41952e03', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/dcf529428b66f5efc8c44ea06fb4733c41952e03'), size_on_disk=15570962, files=frozenset({CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/dcf529428b66f5efc8c44ea06fb4733c41952e03/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1762805126.6580298, blob_last_modified=1756944310.9395182), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/dcf529428b66f5efc8c44ea06fb4733c41952e03/genome_map/accession=SRR11466106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/04bacc0f8c03ee000ba3b69320522d0b1be909686474b6e8a126173a0ceae8c4'), size_on_disk=15547870, blob_last_accessed=1762805142.7999256, blob_last_modified=1756944357.1972551), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/dcf529428b66f5efc8c44ea06fb4733c41952e03/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/192e51c15075f65f15da70558261e4c95bb1392a'), size_on_disk=8396, blob_last_accessed=1762805108.5211468, blob_last_modified=1762805108.5201466)}), refs=frozenset({'main'}), last_modified=1762805108.5201466), CachedRevisionInfo(commit_hash='664d95cdd482d753bc139d26119061c2fa6eaa76', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/664d95cdd482d753bc139d26119061c2fa6eaa76'), size_on_disk=4679, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/664d95cdd482d753bc139d26119061c2fa6eaa76/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/ff6f75db42c0690f0e0285e56c1aa95bc5cf1300'), size_on_disk=4679, blob_last_accessed=1758157110.1705706, blob_last_modified=1758157110.1695707)}), refs=frozenset(), last_modified=1758157110.1695707), CachedRevisionInfo(commit_hash='1acaa5629922414e8efe23a5d023bd44965824c8', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1acaa5629922414e8efe23a5d023bd44965824c8'), size_on_disk=4683, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1acaa5629922414e8efe23a5d023bd44965824c8/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/bb690d71edd44885609ed660d926f3cf2c4c473e'), size_on_disk=4683, blob_last_accessed=1758306349.7238934, blob_last_modified=1758156991.1235294)}), refs=frozenset(), last_modified=1758156991.1235294), CachedRevisionInfo(commit_hash='6b29baae54286d5fbdd1c70f499c03319badad51', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51'), size_on_disk=213707016, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466139/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/25c7d3ffbacca76fd998edffe0c762e220f448140019aa2d389facff5117ce48'), size_on_disk=176811, blob_last_accessed=1757598691.2478209, blob_last_modified=1757104412.6266806), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466143/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/54bd4af6ed8caf52a5810598d96c9b235266542586a692112cb247a209c2649f'), size_on_disk=10801408, blob_last_accessed=1757598691.2688208, blob_last_modified=1757104413.3726768), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466136/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/f5e7fd644d30ae0c8e2c72bc341155bfe5df2ce102b68b08ff4d243470026fcc'), size_on_disk=343033, blob_last_accessed=1757598691.1958208, blob_last_modified=1757104411.7116852), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466129/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/b290d4c9436756b444d0da1af1229b0a2a6e877fd968c5d01b73efa96e62cff8'), size_on_disk=6955953, blob_last_accessed=1757598691.196821, blob_last_modified=1757104410.78769), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466123/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/57b7ca5d419676590538573f295b99db05a952e20cb4c6b2a1618974611d5051'), size_on_disk=3672899, blob_last_accessed=1757598691.073821, blob_last_modified=1757104409.590696), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466109/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/6588704c47423e671054df9cb2318c045138b0018f2eefff61ef91a25848eb58'), size_on_disk=5259413, blob_last_accessed=1757598690.8298213, blob_last_modified=1757104404.9987195), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466125/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/0bba21490ea0a6297031b0ff3219fc0158d9bbcf15099906c86a770b1a1312fc'), size_on_disk=2624759, blob_last_accessed=1757598691.081821, blob_last_modified=1757104409.4426968), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466120/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/4220a31e6a7be79341828d41da77ef77fb5a8026980d4445fec18c57b5229c1f'), size_on_disk=3644577, blob_last_accessed=1757598691.034821, blob_last_modified=1757104408.3467023), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466124/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/39ef32412cae257b724c3b797ea482eeffb5b29afef4246d997ea525a2124054'), size_on_disk=7162800, blob_last_accessed=1757598691.075821, blob_last_modified=1757104409.5966961), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466116/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/587a7b86dffe3d7ade9adeeb94029ea1de54e61fb2c2445dc527979b7f1c24ac'), size_on_disk=5596311, blob_last_accessed=1757598690.988821, blob_last_modified=1757104407.3507075), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466122/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/6362da9e07f02d4c9dc05f15f638ca1b1bf0de5989da4ee8c6f0768f2453f98f'), size_on_disk=1249990, blob_last_accessed=1757598691.0398211, blob_last_modified=1757104411.9016843), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466108/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/56875f81a3cc7c1d40b3042b2c443058eb0f9a2b7e7ebc2e1704c098be612628'), size_on_disk=1765819, blob_last_accessed=1757598690.8238213, blob_last_modified=1757104407.1947083), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466132/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/cc41509e63473d565341ed36fd6881fecfd537e59c003f4779d4c4ec7b49cb54'), size_on_disk=1906184, blob_last_accessed=1757598691.161821, blob_last_modified=1757104413.4366765), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466142/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/3dc044b5cc1b7bc8070d8de4b7b870524b296f26b4f43b22dce2dcbf161d4c74'), size_on_disk=1739520, blob_last_accessed=1757598691.2588208, blob_last_modified=1757104415.5896657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466131/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/bdce0842b5568e406a799dbe36b4b9645a30fa58eb0a707f24ed3bb3900fbf67'), size_on_disk=11759994, blob_last_accessed=1757598691.157821, blob_last_modified=1757104411.1146884), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466114/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/2c07f671cca471d6ad3e10e071cd1b976a89a45ff32920913d8fc36dbeaa1678'), size_on_disk=10992797, blob_last_accessed=1757598690.9498212, blob_last_modified=1757104406.3287127), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466128/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/f631797d2bc13a580f570637d94a0b9ea37deb2fd7ef92e32c66fd9014260525'), size_on_disk=1059587, blob_last_accessed=1757598691.118821, blob_last_modified=1757104410.6996903), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466121/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/78d0f377f8a756b28f2f5296932368c9c6b764d2972dc93aa6dd354591d29860'), size_on_disk=7053621, blob_last_accessed=1757598691.0328212, blob_last_modified=1757104408.5097015), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466126/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/34292b90d99b5a5be542c7dd10b11336605330639d92e93b092e567a56fe7d93'), size_on_disk=2850164, blob_last_accessed=1757598691.086821, blob_last_modified=1757104410.0976934), CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1762805126.6580298, blob_last_modified=1756944310.9395182), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466117/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/49065d4cee74c20f34303405e34e284e29f19082b76fa883a38aeab24cadf674'), size_on_disk=1742958, blob_last_accessed=1757598690.995821, blob_last_modified=1757104411.8276846), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466140/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/25f98a891820b5a91c11629ed5615d40ffe1fe4a6cba1b2c6642067d76b5be87'), size_on_disk=68174, blob_last_accessed=1757598691.2588208, blob_last_modified=1757104415.6456654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466144/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/57b3d2c7917bfa29312cc51c921beff914858c61762e28a658eb29d8eaf3348b'), size_on_disk=918271, blob_last_accessed=1757598691.2758207, blob_last_modified=1757104413.554676), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466112/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8f3441964a046d3c148b41f335948a085d26884e1a890e515711a2d1b48a40be'), size_on_disk=4883601, blob_last_accessed=1757598690.9128213, blob_last_modified=1757104408.5027015), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466118/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/192057e6004dd5cd303e8b1abf54cb72005e211c315665e28eec7b617a84d95a'), size_on_disk=728586, blob_last_accessed=1757598691.0068212, blob_last_modified=1757104407.0917087), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466110/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/d5283e08e898b4f59837f7ff2064f79c8a828994f868c637894f4e07df36daa5'), size_on_disk=13123795, blob_last_accessed=1757598690.8318212, blob_last_modified=1757104407.6787057), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466149/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/ae4d5237fde214a92d3267fbe11f6f1f88569cc7f3f7ccb40fed200871bf33b1'), size_on_disk=10933582, blob_last_accessed=1757598691.3518207, blob_last_modified=1757104415.1316679), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466119/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/0157d9136c2c2bb5640866449adf4bab8c62b3d7f7fb4262981908c77986f89e'), size_on_disk=12516152, blob_last_accessed=1757598691.0068212, blob_last_modified=1757104412.3166823), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/04bacc0f8c03ee000ba3b69320522d0b1be909686474b6e8a126173a0ceae8c4'), size_on_disk=15547870, blob_last_accessed=1762805142.7999256, blob_last_modified=1756944357.1972551), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466127/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/1565c5175e05c322f160529cb4bb74d5e8468458f150f228d6aadf2b670c3b4a'), size_on_disk=11670434, blob_last_accessed=1757598691.092821, blob_last_modified=1757104411.0776885), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466134/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/0b23434181eb1fc864e858cfca82d2d8fed1524a8f7dbea3bf576f6f452cf08b'), size_on_disk=12398132, blob_last_accessed=1757598691.169821, blob_last_modified=1757104412.552681), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1757598690.9228213, blob_last_modified=1757104403.6017265), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466111/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/ba431fc9a05f40581e450429d467226a8e3c49195de355437f9044bb0fdfe524'), size_on_disk=5749460, blob_last_accessed=1757598690.9028213, blob_last_modified=1757104404.9707196), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466130/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/73db821736e3e47c09da250e369fa06812e02e5abe7a4a3ceab5d1020422b66c'), size_on_disk=1920920, blob_last_accessed=1757598691.1328208, blob_last_modified=1757104410.9306893), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466150/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/80cd2d325e5d7a50c6ecbf79a052f45c54a93b9423e90735bf8989aada9bb9eb'), size_on_disk=3234303, blob_last_accessed=1757598691.3798206, blob_last_modified=1757104417.837654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466135/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/d44d381db0b8c0cb4960796e48d3d16e3d766aff87cbc686789a0c5799ba0a98'), size_on_disk=5204256, blob_last_accessed=1757598691.180821, blob_last_modified=1757104412.4376817), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466133/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/b6d6913e14b52d5d51dab2f254815da89e03cebe75e80356b27cf18e31eb048f'), size_on_disk=5090722, blob_last_accessed=1757598691.169821, blob_last_modified=1757104412.180683), CachedFileInfo(file_name='genome_map.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/51978dc2125afb40a41a29672f8ae750308d9a63'), size_on_disk=5311747, blob_last_accessed=1757598690.9468212, blob_last_modified=1757104406.477712), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466113/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/acec3b041a74ab45dbeb03e7f183c2b3d16479aadd64a81f80a74aa42d56368d'), size_on_disk=5866774, blob_last_accessed=1757598690.921821, blob_last_modified=1757104409.0226989), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466141/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/e9efb807ce35fc3e43b1ff0d27b80646c7d137c587abd9d6247393e44d4f30a0'), size_on_disk=986450, blob_last_accessed=1757598691.2628207, blob_last_modified=1757104413.129678), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466115/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/c08ce1194dd98cb028a6038944317e70ac1545e32b21dd323eafa57b16611e6e'), size_on_disk=3503431, blob_last_accessed=1757598690.9468212, blob_last_modified=1757104405.9427147), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466107/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/2f924bc58d82a21b621b051addd1913c77369afaa738171ef0962548fc1a1021'), size_on_disk=5670198, blob_last_accessed=1757598690.8608212, blob_last_modified=1757104405.0417192), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/09f6dce90ec0e64d8f18614091dd18ad2e86213a'), size_on_disk=4403, blob_last_accessed=1757598690.987821, blob_last_modified=1757104409.9196944)}), refs=frozenset(), last_modified=1757104417.837654), CachedRevisionInfo(commit_hash='1e07e46fd0b5348243487ac4a573b570a2a3946b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1e07e46fd0b5348243487ac4a573b570a2a3946b'), size_on_disk=4683, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1e07e46fd0b5348243487ac4a573b570a2a3946b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/bb690d71edd44885609ed660d926f3cf2c4c473e'), size_on_disk=4683, blob_last_accessed=1758306349.7238934, blob_last_modified=1758156991.1235294)}), refs=frozenset(), last_modified=1758156991.1235294)}), last_accessed=1762805142.7999256, last_modified=1762805108.5201466), CachedRepoInfo(repo_id='BrentLab/harbison_2004', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004'), size_on_disk=70377892, nb_files=10, revisions=frozenset({CachedRevisionInfo(commit_hash='2916ad316cc0d8a1ce2e7f35d3707b831b203e87', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/2916ad316cc0d8a1ce2e7f35d3707b831b203e87'), size_on_disk=7160, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/2916ad316cc0d8a1ce2e7f35d3707b831b203e87/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/7e3ec66f5479346ca9d082545ff0b6a960fb60d5'), size_on_disk=7160, blob_last_accessed=1758649475.233073, blob_last_modified=1758155946.7669883)}), refs=frozenset(), last_modified=1758155946.7669883), CachedRevisionInfo(commit_hash='4e54b7fafd79829bcd179b508efd11a3dc7182cc', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/4e54b7fafd79829bcd179b508efd11a3dc7182cc'), size_on_disk=44900498, files=frozenset({CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/4e54b7fafd79829bcd179b508efd11a3dc7182cc/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b89f865a471fbf3a8054871f8fe79507f6f4be5c2291dcd19030ec8fd4a5325c'), size_on_disk=44886395, blob_last_accessed=1767812938.7903292, blob_last_modified=1765314558.8410568), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/4e54b7fafd79829bcd179b508efd11a3dc7182cc/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/ef196364c79e1a0ec01b3ab6db8dae798f0f8fd5'), size_on_disk=14103, blob_last_accessed=1765911961.5656407, blob_last_modified=1765819563.7734888)}), refs=frozenset(), last_modified=1765819563.7734888), CachedRevisionInfo(commit_hash='a33c34b373e379dfa9bd4922d281790180bb1217', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a33c34b373e379dfa9bd4922d281790180bb1217'), size_on_disk=44899466, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a33c34b373e379dfa9bd4922d281790180bb1217/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/3cb3997d01aa1c91d0719f954e1cf207976c8a7d'), size_on_disk=13071, blob_last_accessed=1767811669.8189607, blob_last_modified=1765916907.339037), CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a33c34b373e379dfa9bd4922d281790180bb1217/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b89f865a471fbf3a8054871f8fe79507f6f4be5c2291dcd19030ec8fd4a5325c'), size_on_disk=44886395, blob_last_accessed=1767812938.7903292, blob_last_modified=1765314558.8410568)}), refs=frozenset({'main'}), last_modified=1765916907.339037), CachedRevisionInfo(commit_hash='3ff5f32dfeef3c0b2b7d6cc860b259b0ad095829', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/3ff5f32dfeef3c0b2b7d6cc860b259b0ad095829'), size_on_disk=25421759, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/3ff5f32dfeef3c0b2b7d6cc860b259b0ad095829/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/342343ab9f71e7ce0394c540bed737de39f2cf08'), size_on_disk=5893, blob_last_accessed=1764345374.74204, blob_last_modified=1763587990.060371), CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/3ff5f32dfeef3c0b2b7d6cc860b259b0ad095829/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/91904b3e84754bd6011cd4b385bbc99d69a899e5c7562f039a121a744ba95a0a'), size_on_disk=25415866, blob_last_accessed=1763588775.480192, blob_last_modified=1758649664.1292322)}), refs=frozenset(), last_modified=1763587990.060371), CachedRevisionInfo(commit_hash='073cdeb1b541c28b1a97df9dbc6be6af1d9c23b6', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/073cdeb1b541c28b1a97df9dbc6be6af1d9c23b6'), size_on_disk=5145, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/073cdeb1b541c28b1a97df9dbc6be6af1d9c23b6/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/f7a6cf3dd4477f08e508f38665350e8d11589963'), size_on_disk=5145, blob_last_accessed=1755876214.0380862, blob_last_modified=1755876188.1642818)}), refs=frozenset(), last_modified=1755876188.1642818), CachedRevisionInfo(commit_hash='a0f6f1b23505c4e6a471e65a43a33ff5cced0733', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a0f6f1b23505c4e6a471e65a43a33ff5cced0733'), size_on_disk=25423545, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a0f6f1b23505c4e6a471e65a43a33ff5cced0733/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b5fbd9e98fd8ddadeeb5631e3b6f5055e917c98d'), size_on_disk=7679, blob_last_accessed=1759345152.2162483, blob_last_modified=1758649637.161202), CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a0f6f1b23505c4e6a471e65a43a33ff5cced0733/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/91904b3e84754bd6011cd4b385bbc99d69a899e5c7562f039a121a744ba95a0a'), size_on_disk=25415866, blob_last_accessed=1763588775.480192, blob_last_modified=1758649664.1292322)}), refs=frozenset(), last_modified=1758649664.1292322), CachedRevisionInfo(commit_hash='95dace55563820030e6bcac2d4b2a9a386a2369b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/95dace55563820030e6bcac2d4b2a9a386a2369b'), size_on_disk=44900425, files=frozenset({CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/95dace55563820030e6bcac2d4b2a9a386a2369b/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b89f865a471fbf3a8054871f8fe79507f6f4be5c2291dcd19030ec8fd4a5325c'), size_on_disk=44886395, blob_last_accessed=1767812938.7903292, blob_last_modified=1765314558.8410568), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/95dace55563820030e6bcac2d4b2a9a386a2369b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/1cfc9a691644fd13ba4e3c4d141caf8bc8dadc92'), size_on_disk=14030, blob_last_accessed=1765809787.9431984, blob_last_modified=1765415607.8811436)}), refs=frozenset(), last_modified=1765415607.8811436), CachedRevisionInfo(commit_hash='5eeb06e389a648a36087e20eabad1f961e22dc5a', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/5eeb06e389a648a36087e20eabad1f961e22dc5a'), size_on_disk=44894945, files=frozenset({CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/5eeb06e389a648a36087e20eabad1f961e22dc5a/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b89f865a471fbf3a8054871f8fe79507f6f4be5c2291dcd19030ec8fd4a5325c'), size_on_disk=44886395, blob_last_accessed=1767812938.7903292, blob_last_modified=1765314558.8410568), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/5eeb06e389a648a36087e20eabad1f961e22dc5a/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/aa43ea37112a84a60d21ce254c1cc1e433def6dc'), size_on_disk=8550, blob_last_accessed=1765406568.606891, blob_last_modified=1765292615.6690626)}), refs=frozenset(), last_modified=1765314558.8410568)}), last_accessed=1767812938.7903292, last_modified=1765916907.339037), CachedRepoInfo(repo_id='BrentLab/hughes_2006', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006'), size_on_disk=11969274, nb_files=11, revisions=frozenset({CachedRevisionInfo(commit_hash='0de73d0932e423cfbbfbc1b21d029c96490dc200', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/0de73d0932e423cfbbfbc1b21d029c96490dc200'), size_on_disk=8791, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/0de73d0932e423cfbbfbc1b21d029c96490dc200/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/76efc047ec3c08e362cfcaac776b7e05c02f8a6b'), size_on_disk=8791, blob_last_accessed=1764712744.7980804, blob_last_modified=1764712744.7960804)}), refs=frozenset(), last_modified=1764712744.7960804), CachedRevisionInfo(commit_hash='cc20720c4fe7de9151a051aafbd41e6de2b4cd84', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84'), size_on_disk=11937630, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/842b551fb3d1ba1e6f1daa35157a0c7c616f7655'), size_on_disk=8514, blob_last_accessed=1756855276.4871445, blob_last_modified=1756855276.4861445), CachedFileInfo(file_name='parse_hughes_2006.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/scripts/parse_hughes_2006.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/0c2645bdc0ab7f6be38a428a0a92752e9d3f88d5'), size_on_disk=9255, blob_last_accessed=1756856028.5803509, blob_last_modified=1756856028.6393504), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756856028.69735, blob_last_modified=1756856028.7793496), CachedFileInfo(file_name='knockout.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/knockout.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/5e000e2e9401011c9ecc81655c94d55b7f9469ed856216fa21cef2883a904c93'), size_on_disk=5571518, blob_last_accessed=1756856029.2493467, blob_last_modified=1756856029.2143471), CachedFileInfo(file_name='metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/e27a67137876123c68c229705cfbb8e06ed3ee76a3a868b66d80d624f8d03671'), size_on_disk=7942, blob_last_accessed=1756856028.533351, blob_last_modified=1756856028.9083488), CachedFileInfo(file_name='overexpression.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/overexpression.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/9b8cc1feede780efe0e8537555ed1cbbf067e7fa9aa7a7edc74e5a343c64a443'), size_on_disk=6337781, blob_last_accessed=1756856028.536351, blob_last_modified=1756856029.230347), CachedFileInfo(file_name='checksum.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/checksum.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/973b686313e32c96b6e0ef1dbc84020857c073ef'), size_on_disk=159, blob_last_accessed=1756856028.5883508, blob_last_modified=1756856028.6513503)}), refs=frozenset(), last_modified=1756856029.230347), CachedRevisionInfo(commit_hash='1009546961df1cf9743a03383826d8a5e010bb38', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/1009546961df1cf9743a03383826d8a5e010bb38'), size_on_disk=17152, files=frozenset({CachedFileInfo(file_name='metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/1009546961df1cf9743a03383826d8a5e010bb38/metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/54173aa9fff98e5b077e2b24e139ad600a6d7cea74a47b0305cb0fbdf1c14d9b'), size_on_disk=8362, blob_last_accessed=1764713263.750364, blob_last_modified=1764713263.741364), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/1009546961df1cf9743a03383826d8a5e010bb38/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/572115c6e031637a3ac876795a6f91d3653bf5e2'), size_on_disk=8790, blob_last_accessed=1764713262.7733643, blob_last_modified=1764713223.5823674)}), refs=frozenset({'main'}), last_modified=1764713263.741364), CachedRevisionInfo(commit_hash='51ae7addf429c955a97934a15b242ff8a2ccd653', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/51ae7addf429c955a97934a15b242ff8a2ccd653'), size_on_disk=5701, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/51ae7addf429c955a97934a15b242ff8a2ccd653/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/f799807a7bf81229b0d063e6c939339c1f5c90f4'), size_on_disk=5701, blob_last_accessed=1756855057.8437808, blob_last_modified=1756855057.8417807)}), refs=frozenset(), last_modified=1756855057.8417807)}), last_accessed=1764713263.750364, last_modified=1764713263.741364), CachedRepoInfo(repo_id='BrentLab/hackett_2020', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020'), size_on_disk=817026981, nb_files=22, revisions=frozenset({CachedRevisionInfo(commit_hash='6bea8e8497a12fec83927a941ba912a0ec781822', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/6bea8e8497a12fec83927a941ba912a0ec781822'), size_on_disk=8027, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/6bea8e8497a12fec83927a941ba912a0ec781822/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/159b3e518310d0bf928d14a691e41a77d5540aaa'), size_on_disk=8027, blob_last_accessed=1765299157.242701, blob_last_modified=1765299156.9627051)}), refs=frozenset(), last_modified=1765299156.9627051), CachedRevisionInfo(commit_hash='60b3ecf6a06e709f0397b327ece5c31016303b5c', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/60b3ecf6a06e709f0397b327ece5c31016303b5c'), size_on_disk=404558833, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/60b3ecf6a06e709f0397b327ece5c31016303b5c/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/6f54100c2bb84a6bc059c17e0954dad0ea451666'), size_on_disk=9543, blob_last_accessed=1760552529.9219792, blob_last_modified=1758155372.1328666), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/60b3ecf6a06e709f0397b327ece5c31016303b5c/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1760552530.4189773, blob_last_modified=1756843405.225893)}), refs=frozenset(), last_modified=1758155372.1328666), CachedRevisionInfo(commit_hash='1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43'), size_on_disk=2678425, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=10/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4a2eb1051cad96c5b39a014f19b092f042b03700cc1b1a40c2de9e7edd73edf9'), size_on_disk=352201, blob_last_accessed=1756145253.7915416, blob_last_modified=1756145254.6375287), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=30/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/a85bd6b418d9644d9adaa1269c27f97469a4aaee51af63cf1aa041f62cd8ba2c'), size_on_disk=350044, blob_last_accessed=1756145253.7915416, blob_last_modified=1756145254.6385286), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=90/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/ebd06af83cc386d9094ce6b8d795b155c76c47c96da1a458743348264f1638ae'), size_on_disk=349917, blob_last_accessed=1756145253.7845416, blob_last_modified=1756145254.7235274), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=0/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/409962e1914ac9e9d631e4ae73e04b167822d0915d8d8d4a87703d4c7281ffb9'), size_on_disk=209695, blob_last_accessed=1756145254.7305272, blob_last_modified=1756145254.608529), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=45/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/db8595cd20dd7440c890c46025cf0643da8ec6be3e15bc18a8759b3fecdc02af'), size_on_disk=349256, blob_last_accessed=1756145253.7795417, blob_last_modified=1756145254.6225288), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=5/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/ed331d9d2b27a0958b12368c7b5d2d3383b111d3d1c39ed0e77b78561dc78b6a'), size_on_disk=348626, blob_last_accessed=1756145253.7845416, blob_last_modified=1756145254.6395288), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=15/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/51dfb68ed2518f0dd8ab4d4070d4896b6eca912ecb7e4aa773d857e3096020b5'), size_on_disk=351273, blob_last_accessed=1756145217.972108, blob_last_modified=1756142799.7054222), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=20/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/97adc0bb2b436c325555d82bb7f0ffb0e250d73092fcbf03ac0c00fa2b762e51'), size_on_disk=351379, blob_last_accessed=1756145253.7895415, blob_last_modified=1756145254.6265287), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/45cb0687c3ea72e7b32ad17dd799bc38ad02f80b'), size_on_disk=16034, blob_last_accessed=1756138543.6766906, blob_last_modified=1756138543.6746907)}), refs=frozenset(), last_modified=1756145254.7235274), CachedRevisionInfo(commit_hash='ca2500351e8aa9fc5b996516cd25a60e8298734d', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/ca2500351e8aa9fc5b996516cd25a60e8298734d'), size_on_disk=6294, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/ca2500351e8aa9fc5b996516cd25a60e8298734d/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/ebb31f16ba92f3c0a9eec2aaf4897b3a7662cfad'), size_on_disk=6294, blob_last_accessed=1764169619.6288424, blob_last_modified=1764169578.8420532)}), refs=frozenset(), last_modified=1764169578.8420532), CachedRevisionInfo(commit_hash='5b09fb31bac250b0bfc099c1bdb32bc2673e754b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/5b09fb31bac250b0bfc099c1bdb32bc2673e754b'), size_on_disk=409731660, files=frozenset({CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/5b09fb31bac250b0bfc099c1bdb32bc2673e754b/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/10f544e6c4e4da0b411f1084cbe7f9188a842bc6578b699bdf02f3507aff6592'), size_on_disk=409722077, blob_last_accessed=1765311990.5457778, blob_last_modified=1765311990.540778), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/5b09fb31bac250b0bfc099c1bdb32bc2673e754b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/dacac6a8023596c4c46dce14e80f861d4e4a7401'), size_on_disk=9583, blob_last_accessed=1765309535.4347303, blob_last_modified=1765308942.1208022)}), refs=frozenset(), last_modified=1765311990.540778), CachedRevisionInfo(commit_hash='7192b0e2275ba5aa3ffb3752b87f1c3595f2331a', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a'), size_on_disk=404565656, files=frozenset({CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1760552530.4189773, blob_last_modified=1756843405.225893), CachedFileInfo(file_name='hackett_2020.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/hackett_2020.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4f47951c65c799b5c74ee3782b49c16cbb038d50'), size_on_disk=55, blob_last_accessed=1757105553.3228161, blob_last_modified=1756843395.5719483), CachedFileInfo(file_name='parse_mcisaac_data.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/scripts/parse_mcisaac_data.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0'), size_on_disk=4691, blob_last_accessed=1756843395.7579472, blob_last_modified=1756843395.820947), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756843395.5059488, blob_last_modified=1756843395.5679483), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/f3899cd97da03a3543db10d76c15ee0442bb136e'), size_on_disk=9159, blob_last_accessed=1758145252.5929751, blob_last_modified=1757541416.40237)}), refs=frozenset(), last_modified=1757541416.40237), CachedRevisionInfo(commit_hash='4fef197cd055065207d66ea42aec9746b23f38c8', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/4fef197cd055065207d66ea42aec9746b23f38c8'), size_on_disk=9431, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/4fef197cd055065207d66ea42aec9746b23f38c8/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/0a3330d889673abc635b58a78c221ba5b3e36200'), size_on_disk=9431, blob_last_accessed=1765809790.786152, blob_last_modified=1765650535.7166286)}), refs=frozenset({'main'}), last_modified=1765650535.7166286), CachedRevisionInfo(commit_hash='2c196866d4f057cb5cf0adf1fe35f2c712c61025', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025'), size_on_disk=404565376, files=frozenset({CachedFileInfo(file_name='hackett_2020.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/hackett_2020.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4f47951c65c799b5c74ee3782b49c16cbb038d50'), size_on_disk=55, blob_last_accessed=1757105553.3228161, blob_last_modified=1756843395.5719483), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1760552530.4189773, blob_last_modified=1756843405.225893), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c3e72ccb1b8deba4bbfd18abe6081de7ec3914d9'), size_on_disk=8879, blob_last_accessed=1757105552.290822, blob_last_modified=1757104206.2667632), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756843395.5059488, blob_last_modified=1756843395.5679483), CachedFileInfo(file_name='parse_mcisaac_data.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/scripts/parse_mcisaac_data.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0'), size_on_disk=4691, blob_last_accessed=1756843395.7579472, blob_last_modified=1756843395.820947)}), refs=frozenset(), last_modified=1757104206.2667632), CachedRevisionInfo(commit_hash='b9d39535e175295d9cba23489a49fafebbac5ca0', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0'), size_on_disk=404565563, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/03e579e00c632cdb393c0ffafd9d15b069390e5c'), size_on_disk=9066, blob_last_accessed=1757439136.0598524, blob_last_modified=1757105945.6476595), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756843395.5059488, blob_last_modified=1756843395.5679483), CachedFileInfo(file_name='parse_mcisaac_data.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/scripts/parse_mcisaac_data.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0'), size_on_disk=4691, blob_last_accessed=1756843395.7579472, blob_last_modified=1756843395.820947), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1760552530.4189773, blob_last_modified=1756843405.225893), CachedFileInfo(file_name='hackett_2020.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/hackett_2020.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4f47951c65c799b5c74ee3782b49c16cbb038d50'), size_on_disk=55, blob_last_accessed=1757105553.3228161, blob_last_modified=1756843395.5719483)}), refs=frozenset(), last_modified=1757105945.6476595)}), last_accessed=1765809790.786152, last_modified=1765650535.7166286), CachedRepoInfo(repo_id='BrentLab/mahendrawada_2025', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025'), size_on_disk=94317969, nb_files=8, revisions=frozenset({CachedRevisionInfo(commit_hash='874a5dfe4052a1e71b6544a2e5b2c99ad4286c04', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/874a5dfe4052a1e71b6544a2e5b2c99ad4286c04'), size_on_disk=18603, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/874a5dfe4052a1e71b6544a2e5b2c99ad4286c04/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/685c0e56e0c68e20e3c2af8e38267095d686c03c'), size_on_disk=18603, blob_last_accessed=1758569041.2889845, blob_last_modified=1758569041.2879846)}), refs=frozenset(), last_modified=1758569041.2879846), CachedRevisionInfo(commit_hash='af5ac9dc922b7fbd14f460c2fe94b727db1e1245', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/af5ac9dc922b7fbd14f460c2fe94b727db1e1245'), size_on_disk=92323432, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/af5ac9dc922b7fbd14f460c2fe94b727db1e1245/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/8f0cf5e3dd2bb5bdc2f8fde970e31a2404481fec'), size_on_disk=20435, blob_last_accessed=1764298314.9501038, blob_last_modified=1763578711.307058), CachedFileInfo(file_name='reprocess_diffcontrol_5prime.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/af5ac9dc922b7fbd14f460c2fe94b727db1e1245/reprocess_diffcontrol_5prime.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/eba528776f8fe8da63c39ee0660527bc35f989ac6a976daf09270860cad6d735'), size_on_disk=92302997, blob_last_accessed=1763665270.5143101, blob_last_modified=1763578870.280984)}), refs=frozenset({'main'}), last_modified=1763578870.280984), CachedRevisionInfo(commit_hash='8bea431be57e21633be4174df291540b1fae212a', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/8bea431be57e21633be4174df291540b1fae212a'), size_on_disk=18603, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/8bea431be57e21633be4174df291540b1fae212a/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/6a2a5a6a8006e386c0e47a2a98151442046d3d41'), size_on_disk=18603, blob_last_accessed=1758568089.249362, blob_last_modified=1758568089.246362)}), refs=frozenset(), last_modified=1758568089.246362), CachedRevisionInfo(commit_hash='3b912489743a1797199beb023a49b14f7e3ba19d', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/3b912489743a1797199beb023a49b14f7e3ba19d'), size_on_disk=1957331, files=frozenset({CachedFileInfo(file_name='chec_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/3b912489743a1797199beb023a49b14f7e3ba19d/chec_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/dd3e967d8c54c6056dc9fd6bdb921c3eed9d10df3a449273bd08faeb1b936765'), size_on_disk=1220601, blob_last_accessed=1759345153.538238, blob_last_modified=1758569549.9902885), CachedFileInfo(file_name='rnaseq_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/3b912489743a1797199beb023a49b14f7e3ba19d/rnaseq_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/4411789f50aab449658c100f2758ceb410022a0a30f956fd5fe41152915580f9'), size_on_disk=286605, blob_last_accessed=1759345153.5952375, blob_last_modified=1758569588.8621461), CachedFileInfo(file_name='features_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/3b912489743a1797199beb023a49b14f7e3ba19d/features_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/618ea89d880c8a1b4c6d05cc3c13a70cbb05784d10bf2b6c4fc954705203096c'), size_on_disk=431436, blob_last_accessed=1759345156.897212, blob_last_modified=1758652945.6395862), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/3b912489743a1797199beb023a49b14f7e3ba19d/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/c8cbc56ef9fad47193120cce2affd35374b8373d'), size_on_disk=18689, blob_last_accessed=1759345153.4962385, blob_last_modified=1758569549.1302917)}), refs=frozenset(), last_modified=1758652945.6395862)}), last_accessed=1764298314.9501038, last_modified=1763578870.280984)}), warnings=[CorruptedCacheException(\"Snapshots dir doesn't exist in cached repo: /home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_dto/snapshots\")])" ] }, "execution_count": 2, @@ -268,20 +268,20 @@ "text": [ "Current HuggingFace Cache Overview:\n", "========================================\n", - "Total cache size: 5.2G\n", - "Number of repositories: 10\n", + "Total cache size: 5.5G\n", + "Number of repositories: 11\n", "\n", "Largest repositories (top 5):\n", " • BrentLab/barkai_compendium: 3.6G (1 revisions)\n", " • BrentLab/hackett_2020: 817.0M (9 revisions)\n", - " • BrentLab/kemmeren_2014: 319.2M (3 revisions)\n", + " • BrentLab/kemmeren_2014: 646.2M (3 revisions)\n", " • BrentLab/rossi_2021: 213.7M (9 revisions)\n", " • BrentLab/mahendrawada_2025: 94.3M (4 revisions)\n", - " ... and 5 more repositories\n", + " ... and 6 more repositories\n", "\n", - "Total revisions across all repos: 49\n", - "Revisions older than 30 days: 34\n", - "Recent revisions (≤30 days): 15\n" + "Total revisions across all repos: 50\n", + "Revisions older than 30 days: 41\n", + "Recent revisions (≤30 days): 9\n" ] } ], @@ -563,16 +563,16 @@ "text": [ "Current HuggingFace Cache Status:\n", "===================================\n", - "Total size: 5.2G\n", - "Number of repositories: 10\n", + "Total size: 5.5G\n", + "Number of repositories: 11\n", "\n", "Repository breakdown:\n", + " • BrentLab/yeast_comparative_analysis: 166.1K (1 revisions)\n", + " • BrentLab/yeast_genome_resources: 114.5K (7 revisions)\n", " • BrentLab/barkai_compendium: 3.6G (1 revisions)\n", - " • BrentLab/mahendrawada_2025: 94.3M (4 revisions)\n", - " • BrentLab/hughes_2006: 12.0M (4 revisions)\n", - " • BrentLab/callingcards: 49.6M (3 revisions)\n", - " • BrentLab/kemmeren_2014: 319.2M (3 revisions)\n", - " ... and 5 more repositories\n", + " • BrentLab/kemmeren_2014: 646.2M (3 revisions)\n", + " • BrentLab/hu_2007_reimand_2010: 42.7M (1 revisions)\n", + " ... and 6 more repositories\n", "\n", "Target repository (BrentLab/mahendrawada_2025) cache info:\n", " Size: 94.3M\n", @@ -644,7 +644,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "INFO:__main__:Found 34 old revisions. Will free 4.6G\n", + "INFO:__main__:Found 41 old revisions. Will free 4.7G\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" ] }, @@ -654,20 +654,20 @@ "text": [ "\n", "Cleanup strategy created:\n", - "Expected space freed: 4.6G\n", - "Items to delete: 59\n", + "Expected space freed: 4.7G\n", + "Items to delete: 46\n", "\n", "Breakdown of items to delete:\n", - " • Blob files: 39\n", + " • Blob files: 27\n", " • Reference files: 0\n", - " • Repository directories: 4\n", - " • Snapshot directories: 16\n", + " • Repository directories: 7\n", + " • Snapshot directories: 12\n", "\n", "Sample blob files to delete:\n", - " • /home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0\n", - " • /home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/51dfb68ed2518f0dd8ab4d4070d4896b6eca912ecb7e4aa773d857e3096020b5\n", - " • /home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/f799807a7bf81229b0d063e6c939339c1f5c90f4\n", - " ... and 36 more blob files\n" + " • /home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b5fbd9e98fd8ddadeeb5631e3b6f5055e917c98d\n", + " • /home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/a85bd6b418d9644d9adaa1269c27f97469a4aaee51af63cf1aa041f62cd8ba2c\n", + " • /home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c3e72ccb1b8deba4bbfd18abe6081de7ec3914d9\n", + " ... and 24 more blob files\n" ] } ], @@ -731,7 +731,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "INFO:__main__:Selected 16 revisions for deletion. Will free 3.6G\n", + "INFO:__main__:Selected 17 revisions for deletion. Will free 3.8G\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" ] }, @@ -741,8 +741,8 @@ "text": [ "\n", "Size-based cleanup strategy:\n", - "Expected space freed: 3.6G\n", - "Items to delete: 43\n", + "Expected space freed: 3.8G\n", + "Items to delete: 85\n", "\n", "Comparing cleanup strategies for 1GB:\n" ] @@ -751,9 +751,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "INFO:__main__:Selected 16 revisions for deletion. Will free 3.6G\n", - "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n", - "INFO:__main__:Selected 3 revisions for deletion. Will free 4.0G\n", + "INFO:__main__:Selected 17 revisions for deletion. Will free 3.8G\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" ] }, @@ -761,15 +759,14 @@ "name": "stdout", "output_type": "stream", "text": [ - " • oldest_first : 3.6G (43 items)\n", - " • largest_first : 4.0G (6 items)\n" + " • oldest_first : 3.8G (85 items)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "INFO:__main__:Selected 16 revisions for deletion. Will free 3.6G\n", + "INFO:__main__:Selected 4 revisions for deletion. Will free 4.0G\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" ] }, @@ -777,7 +774,22 @@ "name": "stdout", "output_type": "stream", "text": [ - " • least_used : 3.6G (43 items)\n" + " • largest_first : 4.0G (8 items)\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:__main__:Selected 17 revisions for deletion. Will free 3.8G\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " • least_used : 3.8G (85 items)\n" ] } ], @@ -864,27 +876,25 @@ "\n", "Per-repository revision analysis:\n", "\n", - " • BrentLab/barkai_compendium:\n", + " • BrentLab/yeast_comparative_analysis:\n", " Total revisions: 1\n", " Would keep: 1\n", " Would delete: 0\n", - " Keep: a987ef37 (modified: 1756926783.3167186)\n", + " Keep: ac03d065 (modified: 1767824941.5531375)\n", "\n", - " • BrentLab/mahendrawada_2025:\n", - " Total revisions: 4\n", + " • BrentLab/yeast_genome_resources:\n", + " Total revisions: 7\n", " Would keep: 2\n", - " Would delete: 2\n", - " Keep: af5ac9dc (modified: 1763578870.280984)\n", - " Keep: 3b912489 (modified: 1758652945.6395862)\n", - " Delete: 874a5dfe (modified: 1758569041.2879846)\n", + " Would delete: 5\n", + " Keep: 42beb284 (modified: 1758155946.5549896)\n", + " Keep: 15fdb72f (modified: 1755819093.2306638)\n", + " Delete: 7441b9a8 (modified: 1755816785.6988702)\n", "\n", - " • BrentLab/hughes_2006:\n", - " Total revisions: 4\n", - " Would keep: 2\n", - " Would delete: 2\n", - " Keep: 10095469 (modified: 1764713263.741364)\n", - " Keep: 0de73d09 (modified: 1764712744.7960804)\n", - " Delete: cc20720c (modified: 1756856029.230347)\n" + " • BrentLab/barkai_compendium:\n", + " Total revisions: 1\n", + " Would keep: 1\n", + " Would delete: 0\n", + " Keep: a987ef37 (modified: 1756926783.3167186)\n" ] } ], @@ -963,11 +973,13 @@ "name": "stderr", "output_type": "stream", "text": [ - "INFO:__main__:Found 34 old revisions. Will free 4.6G\n", + "INFO:__main__:Found 41 old revisions. Will free 4.7G\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n", "INFO:__main__:Found 31 unused revisions. Will free 642.9M\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n", - "INFO:__main__:Automated cleanup complete. Total freed: 4.9GB\n" + "INFO:__main__:Selected 9 revisions for deletion. Will free 2.8M\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n", + "INFO:__main__:Automated cleanup complete. Total freed: 5.0GB\n" ] }, { @@ -975,12 +987,13 @@ "output_type": "stream", "text": [ "\n", - "Automated cleanup executed 2 strategies:\n", - " 1. Strategy freed: 4.6G\n", + "Automated cleanup executed 3 strategies:\n", + " 1. Strategy freed: 4.7G\n", " 2. Strategy freed: 642.9M\n", + " 3. Strategy freed: 2.8M\n", "\n", - "Total space that would be freed: 4.9GB\n", - "Cache size after cleanup: 0B\n" + "Total space that would be freed: 5.0GB\n", + "Cache size after cleanup: 129.8MB\n" ] } ], @@ -1042,9 +1055,9 @@ "Demonstrating cache cleanup performance...\n", "\n", "1. Cache scanning performance:\n", - " Time to scan cache: 0.095 seconds\n", - " Repositories found: 10\n", - " Total cache size: 5.2G\n", + " Time to scan cache: 0.096 seconds\n", + " Repositories found: 11\n", + " Total cache size: 5.5G\n", "\n", "2. Cleanup strategy creation performance:\n" ] @@ -1053,7 +1066,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "INFO:__main__:Found 34 old revisions. Will free 4.6G\n", + "INFO:__main__:Found 41 old revisions. Will free 4.7G\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" ] }, @@ -1061,14 +1074,14 @@ "name": "stdout", "output_type": "stream", "text": [ - " Age cleanup strategy: 0.099 seconds\n" + " Age cleanup strategy: 0.094 seconds\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "INFO:__main__:Selected 16 revisions for deletion. Will free 3.6G\n", + "INFO:__main__:Selected 17 revisions for deletion. Will free 3.8G\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n", "INFO:__main__:Found 31 unused revisions. Will free 642.9M\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" @@ -1078,11 +1091,11 @@ "name": "stdout", "output_type": "stream", "text": [ - " Size cleanup strategy: 0.097 seconds\n", - " Revision cleanup strategy: 0.096 seconds\n", + " Size cleanup strategy: 0.093 seconds\n", + " Revision cleanup strategy: 0.100 seconds\n", "\n", "Performance insights:\n", - "• Cache scanning is fast: 0.095s for 10 repos\n", + "• Cache scanning is fast: 0.096s for 11 repos\n", "• Cleanup strategy creation is efficient\n", "• Dry runs allow safe preview of cleanup operations\n", "• Multiple strategies can be compared quickly\n" @@ -1252,8 +1265,8 @@ "Current Session State:\n", "Repository: BrentLab/mahendrawada_2025\n", "DuckDB tables: 0\n", - "HF cache size: 5.2G\n", - "Cache repositories: 10\n" + "HF cache size: 5.5G\n", + "Cache repositories: 11\n" ] } ], @@ -1326,7 +1339,7 @@ " • Check repository access permissions\n", "\n", "2. Cache Space and Performance Issues:\n", - " Current cache size: 5.2G\n", + " Current cache size: 5.5G\n", " • Use auto_clean_cache() for automated management\n", " • Monitor cache growth with scan_cache_dir()\n", " • Set appropriate size limits for your system\n", @@ -1343,7 +1356,7 @@ "\n", "Cache Health Check:\n", "✓ DuckDB connection: DuckDB OK\n", - "✓ Cache access: 10 repositories found\n" + "✓ Cache access: 11 repositories found\n" ] }, { @@ -1432,75 +1445,6 @@ "print(f\"Logger configured: {cache_manager.logger is not None}\")\n", "print(f\"Cache management ready: ✓\")" ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 10. Summary and Best Practices\n", - "\n", - "The HfCacheManager provides a sophisticated and efficient way to manage metadata and cache for HuggingFace genomics datasets." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Key HfCacheManager Features:\n", - "\n", - "1. **3-Case Metadata Strategy**: Intelligent caching that checks DuckDB → HF cache → download\n", - "2. **Automated Cache Cleanup**: Multiple strategies (age, size, revision-based) for cache management\n", - "3. **Cache Monitoring**: Real-time cache analysis and diagnostics\n", - "4. **DuckDB Integration**: Efficient metadata storage and querying without data duplication\n", - "5. **Embedded Metadata Extraction**: Extract metadata fields from data tables into queryable views\n", - "\n", - "### Cache Management Best Practices:\n", - "\n", - "1. **Monitor Cache Size**: Regular `scan_cache_dir()` checks prevent disk space issues\n", - "2. **Proactive Cleanup**: Use `auto_clean_cache()` before large data operations\n", - "3. **Strategic Retention**: Keep recent revisions, clean old ones with `clean_unused_revisions()`\n", - "4. **Size Management**: Set cache size limits appropriate for your system resources\n", - "5. **Integration with DataCard**: Use DataCard for exploration, HfCacheManager for cache management\n", - "\n", - "### Recommended Cache Management Workflow:\n", - "\n", - "```python\n", - "# 1. Initialize cache manager\n", - "from tfbpapi.HfCacheManager import HfCacheManager\n", - "import duckdb\n", - "\n", - "conn = duckdb.connect(':memory:')\n", - "cache_mgr = HfCacheManager(repo_id, conn)\n", - "\n", - "# 2. Check current cache status\n", - "from huggingface_hub import scan_cache_dir\n", - "cache_info = scan_cache_dir()\n", - "print(f\"Current cache: {cache_info.size_on_disk_str}\")\n", - "\n", - "# 3. Automated cleanup before heavy operations\n", - "cache_mgr.auto_clean_cache(\n", - " max_total_size=\"10GB\",\n", - " max_age_days=30,\n", - " keep_latest_per_repo=2,\n", - " dry_run=False\n", - ")\n", - "\n", - "# 4. Load data (uses 3-case strategy automatically)\n", - "from tfbpapi import HfQueryAPI\n", - "query_api = HfQueryAPI(repo_id, conn)\n", - "data = query_api.get_pandas('config_name')\n", - "\n", - "# 5. Extract embedded metadata if needed\n", - "cache_mgr._extract_embedded_metadata_field(\n", - " 'data_table', 'metadata_field', 'metadata_table'\n", - ")\n", - "\n", - "# 6. Regular maintenance\n", - "cache_mgr.clean_cache_by_age(max_age_days=30, dry_run=False)\n", - "```\n", - "\n", - "This approach ensures optimal cache performance while maintaining efficient access to genomics datasets." - ] } ], "metadata": { diff --git a/docs/tutorials/datacard_tutorial.ipynb b/docs/tutorials/datacard_tutorial.ipynb index e3e8ba8..1556a1c 100644 --- a/docs/tutorials/datacard_tutorial.ipynb +++ b/docs/tutorials/datacard_tutorial.ipynb @@ -25,7 +25,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -55,7 +55,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 15, "metadata": {}, "outputs": [ { @@ -90,7 +90,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -130,7 +130,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 17, "metadata": {}, "outputs": [ { @@ -162,40 +162,6 @@ " print(f\" Features: {len(config.dataset_info.features)}\")" ] }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "ename": "AttributeError", - "evalue": "'DataCard' object has no attribute 'explore_config'", - "output_type": "error", - "traceback": [ - "\u001b[31m---------------------------------------------------------------------------\u001b[39m", - "\u001b[31mAttributeError\u001b[39m Traceback (most recent call last)", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[6]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;66;03m# Explore the main data configuration in detail\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m config_info = \u001b[43mcard\u001b[49m\u001b[43m.\u001b[49m\u001b[43mexplore_config\u001b[49m(\u001b[33m'\u001b[39m\u001b[33mharbison_2004\u001b[39m\u001b[33m'\u001b[39m)\n\u001b[32m 4\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33mMain Configuration Details:\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 5\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33m=\u001b[39m\u001b[33m\"\u001b[39m * \u001b[32m40\u001b[39m)\n", - "\u001b[31mAttributeError\u001b[39m: 'DataCard' object has no attribute 'explore_config'" - ] - } - ], - "source": [ - "# Explore the main data configuration in detail\n", - "config_info = card.explore_config('harbison_2004')\n", - "\n", - "print(\"Main Configuration Details:\")\n", - "print(\"=\" * 40)\n", - "print(f\"Config name: {config_info['config_name']}\")\n", - "print(f\"Dataset type: {config_info['dataset_type']}\")\n", - "print(f\"Number of features: {config_info['num_features']}\")\n", - "\n", - "print(\"\\nFeatures:\")\n", - "for feature in config_info['features']:\n", - " role = f\" [{feature.get('role', 'no role')}]\" if 'role' in feature else \"\"\n", - " print(f\" • {feature['name']:25} {role}\")\n", - " print(f\" {feature['description']}\")" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -222,7 +188,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 18, "metadata": {}, "outputs": [ { @@ -262,7 +228,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 19, "metadata": {}, "outputs": [ { @@ -302,31 +268,7 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Fields with Experimental Conditions:\n", - "========================================\n", - "Found 1 condition field(s): ['condition']\n" - ] - } - ], - "source": [ - "# List all experimental condition fields\n", - "condition_fields = card.list_experimental_condition_fields('harbison_2004')\n", - "\n", - "print(\"Fields with Experimental Conditions:\")\n", - "print(\"=\" * 40)\n", - "print(f\"Found {len(condition_fields)} condition field(s): {condition_fields}\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, + "execution_count": 20, "metadata": {}, "outputs": [ { @@ -370,7 +312,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 21, "metadata": {}, "outputs": [ { @@ -382,7 +324,6 @@ "{\n", " \"description\": \"Rich media baseline condition\",\n", " \"temperature_celsius\": 30,\n", - " \"cultivation_method\": \"unspecified\",\n", " \"growth_phase_at_harvest\": {\n", " \"od600\": 0.8\n", " },\n", @@ -423,7 +364,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 22, "metadata": {}, "outputs": [ { @@ -437,7 +378,6 @@ " \"initial_temperature_celsius\": 30,\n", " \"temperature_shift_celsius\": 37,\n", " \"temperature_shift_duration_minutes\": 45,\n", - " \"cultivation_method\": \"unspecified\",\n", " \"growth_phase_at_harvest\": {\n", " \"od600\": 0.5\n", " },\n", @@ -484,7 +424,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 23, "metadata": {}, "outputs": [ { @@ -525,7 +465,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 24, "metadata": {}, "outputs": [ { @@ -533,7 +473,6 @@ "text/plain": [ "{'description': 'Rich media baseline condition',\n", " 'temperature_celsius': 30,\n", - " 'cultivation_method': 'unspecified',\n", " 'growth_phase_at_harvest': {'od600': 0.8},\n", " 'media': {'name': 'YPD',\n", " 'carbon_source': [{'compound': 'D-glucose', 'concentration_percent': 2}],\n", @@ -542,7 +481,7 @@ " {'compound': 'peptone', 'concentration_percent': 2}]}}" ] }, - "execution_count": 13, + "execution_count": 24, "metadata": {}, "output_type": "execute_result" } @@ -553,7 +492,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 25, "metadata": {}, "outputs": [ { @@ -609,7 +548,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 26, "metadata": {}, "outputs": [ { diff --git a/docs/tutorials/virtual_db_tutorial.ipynb b/docs/tutorials/virtual_db_tutorial.ipynb index 2ea0f72..c5f87f6 100644 --- a/docs/tutorials/virtual_db_tutorial.ipynb +++ b/docs/tutorials/virtual_db_tutorial.ipynb @@ -1,1267 +1,3087 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# VirtualDB Tutorial: Unified Cross-Dataset Queries\n", - "\n", - "The `VirtualDB` class provides a unified query interface across heterogeneous datasets with different experimental condition structures and terminologies. Each dataset defines conditions in its own way, with properties at different hierarchy levels and using different naming conventions. VirtualDB uses external YAML configuration to:\n", - "\n", - "- Map varying structures to a common schema\n", - "- Normalize factor level names (e.g., \"D-glucose\", \"dextrose\", \"glu\" all become \"glucose\")\n", - "- Enable cross-dataset queries with standardized field names and values\n", - "\n", - "In this tutorial, we'll explore how to use VirtualDB to query and compare data across multiple datasets." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 1. Introduction: Why VirtualDB?\n", - "\n", - "### The Problem\n", - "\n", - "When working with multiple genomic datasets, you'll encounter:\n", - "\n", - "1. **Inconsistent terminology**: One dataset uses \"D-glucose\", another uses \"glucose\", yet another uses \"glu\"\n", - "2. **Different hierarchies**: Conditions stored at repo-level vs config-level vs field-level\n", - "3. **Varying structures**: Different nesting patterns for the same logical information\n", - "\n", - "### The Solution\n", - "\n", - "VirtualDB abstracts these differences:\n", - "\n", - "- **Common schema**: Query all datasets using standardized field names\n", - "- **Automatic normalization**: \"D-glucose\" and \"glucose\" both match `carbon_source=\"glucose\"`\n", - "- **Unified interface**: Single query retrieves data from multiple datasets" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2. Basic Setup\n", - "\n", - "VirtualDB requires a YAML configuration file that defines:\n", - "- Which datasets to include\n", - "- How to map their fields to common names\n", - "- How to normalize varying terminologies" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ + "cells": [ { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chase/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", - " from .autonotebook import tqdm as notebook_tqdm\n" - ] + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# VirtualDB Tutorial: Unified Cross-Dataset Queries\n", + "\n", + "The `VirtualDB` class provides a unified query interface across heterogeneous datasets with different experimental condition structures and terminologies. Each dataset defines conditions in its own way, with properties at different hierarchy levels and using different naming conventions. VirtualDB uses external YAML configuration to:\n", + "\n", + "- Map varying structures to a common schema\n", + "- Normalize factor level names (e.g., \"D-glucose\", \"dextrose\", \"glu\" all become \"glucose\")\n", + "- Enable cross-dataset queries with standardized field names and values\n", + "\n", + "In this tutorial, we'll explore how to use VirtualDB to query and compare data across multiple datasets." + ] }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "Configuration saved to: /tmp/tmpi90ps0mq/vdb_config.yaml\n" - ] - } - ], - "source": [ - "from tfbpapi.virtual_db import VirtualDB\n", - "import pandas as pd\n", - "\n", - "# For this tutorial, we'll create a sample configuration\n", - "# In practice, you'd load this from a YAML file\n", - "config_yaml = \"\"\"\n", - "repositories:\n", - " BrentLab/harbison_2004:\n", - " dataset:\n", - " harbison_2004:\n", - " carbon_source:\n", - " field: condition\n", - " path: media.carbon_source.compound\n", - " temperature_celsius:\n", - " field: condition\n", - " path: temperature_celsius\n", - " environmental_condition:\n", - " field: condition\n", - "\n", - " BrentLab/kemmeren_2014:\n", - " dataset:\n", - " kemmeren_2014:\n", - " carbon_source:\n", - " path: media.carbon_source.compound\n", - " temperature_celsius:\n", - " path: temperature_celsius\n", - "\n", - "factor_aliases:\n", - " carbon_source:\n", - " glucose: [D-glucose, dextrose, glu]\n", - " galactose: [D-galactose, gal]\n", - " raffinose: [D-raffinose]\n", - "\n", - "missing_value_labels:\n", - " carbon_source: \"unspecified\"\n", - "\n", - "description:\n", - " carbon_source: The carbon source provided during growth\n", - " temperature_celsius: Growth temperature in degrees Celsius\n", - " environmental_condition: Named environmental condition\n", - "\"\"\"\n", - "\n", - "# Save config to temporary file\n", - "import tempfile\n", - "from pathlib import Path\n", - "\n", - "temp_config = Path(tempfile.mkdtemp()) / \"vdb_config.yaml\"\n", - "temp_config.write_text(config_yaml)\n", - "\n", - "print(f\"Configuration saved to: {temp_config}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "VirtualDB initialized successfully!\n", - "Configured repositories: 2\n" - ] - } - ], - "source": [ - "# Initialize VirtualDB with the configuration\n", - "vdb = VirtualDB(str(temp_config))\n", - "\n", - "print(\"VirtualDB initialized successfully!\")\n", - "print(f\"Configured repositories: {len(vdb.config.repositories)}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 3. Schema Discovery\n", - "\n", - "Before querying, let's explore what fields are available." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "All available fields:\n", - " - carbon_source\n", - " - environmental_condition\n", - " - temperature_celsius\n" - ] - } - ], - "source": [ - "# Get all fields defined in any dataset\n", - "all_fields = vdb.get_fields()\n", - "\n", - "print(\"All available fields:\")\n", - "for field in sorted(all_fields):\n", - " print(f\" - {field}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating a VirtualDB Specification\n", + "\n", + "VirtualDB requires a YAML configuration file that defines:\n", + "- Which datasets to include\n", + "- How to map their fields to common names\n", + "- How to normalize factor levels" + ] + }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "Common fields (present in all datasets):\n", - " - carbon_source\n", - " - temperature_celsius\n" - ] - } - ], - "source": [ - "# Get fields present in ALL datasets (common fields)\n", - "common_fields = vdb.get_common_fields()\n", - "\n", - "print(\"Common fields (present in all datasets):\")\n", - "for field in sorted(common_fields):\n", - " print(f\" - {field}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Configuration saved to: /tmp/tmpnetd9hv1/vdb_config.yaml\n" + ] + } + ], + "source": [ + "# For this tutorial, we'll create a sample configuration\n", + "# In practice, you'd load this from a YAML file\n", + "config_yaml = \"\"\"\n", + "repositories:\n", + " BrentLab/harbison_2004:\n", + " dataset:\n", + " harbison_2004:\n", + " sample_id:\n", + " field: sample_id\n", + " carbon_source:\n", + " field: condition\n", + " path: media.carbon_source.compound\n", + " temperature_celsius:\n", + " field: condition\n", + " path: temperature_celsius\n", + " dtype: numeric\n", + " environmental_condition:\n", + " field: condition\n", + "\n", + " comparative_analyses:\n", + " - repo: BrentLab/yeast_comparative_analysis\n", + " dataset: dto\n", + " via_field: binding_id\n", + "\n", + " BrentLab/kemmeren_2014:\n", + " dataset:\n", + " kemmeren_2014:\n", + " sample_id:\n", + " field: sample_id\n", + " carbon_source:\n", + " path: media.carbon_source.compound\n", + " temperature_celsius:\n", + " path: temperature_celsius\n", + " dtype: numeric\n", + "\n", + " comparative_analyses:\n", + " - repo: BrentLab/yeast_comparative_analysis\n", + " dataset: dto\n", + " via_field: perturbation_id\n", + "\n", + "factor_aliases:\n", + " carbon_source:\n", + " glucose: [D-glucose, dextrose, glu]\n", + " galactose: [D-galactose, gal]\n", + " raffinose: [D-raffinose]\n", + "\n", + "missing_value_labels:\n", + " carbon_source: \"unspecified\"\n", + "\n", + "description:\n", + " carbon_source: The carbon source provided during growth\n", + " temperature_celsius: Growth temperature in degrees Celsius\n", + " environmental_condition: Named environmental condition\n", + "\"\"\"\n", + "\n", + "# Save config to temporary file\n", + "import tempfile\n", + "from pathlib import Path\n", + "\n", + "temp_config = Path(tempfile.mkdtemp()) / \"vdb_config.yaml\"\n", + "temp_config.write_text(config_yaml)\n", + "\n", + "print(f\"Configuration saved to: {temp_config}\")" + ] + }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "Fields in harbison_2004:\n", - " - carbon_source\n", - " - environmental_condition\n", - " - temperature_celsius\n" - ] - } - ], - "source": [ - "# Get fields for a specific dataset\n", - "harbison_fields = vdb.get_fields(\"BrentLab/harbison_2004\", \"harbison_2004\")\n", - "\n", - "print(\"Fields in harbison_2004:\")\n", - "for field in sorted(harbison_fields):\n", - " print(f\" - {field}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Discovering Valid Values\n", - "\n", - "VirtualDB can tell you what values exist for each field." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chase/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "VirtualDB initialized successfully!\n", + "Configured repositories: 2\n" + ] + } + ], + "source": [ + "from tfbpapi.virtual_db import VirtualDB\n", + "\n", + "# Initialize VirtualDB with the configuration\n", + "vdb = VirtualDB(str(temp_config))\n", + "\n", + "print(\"VirtualDB initialized successfully!\")\n", + "print(f\"Configured repositories: {len(vdb.config.repositories)}\")" + ] + }, { - "name": "stderr", - "output_type": "stream", - "text": [ - "Fetching 1 files: 100%|██████████| 1/1 [00:09<00:00, 9.60s/it]" - ] + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Schema Discovery\n", + "\n", + "The VirtualDB class provides methods to inspect the unified schema after loading the\n", + "configuration." + ] }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "Unique carbon sources (normalized):\n", - " - unspecified\n" - ] + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "All available fields:\n", + " - carbon_source\n", + " - environmental_condition\n", + " - sample_id\n", + " - temperature_celsius\n" + ] + } + ], + "source": [ + "# Get all fields defined in any dataset\n", + "all_fields = vdb.get_fields()\n", + "\n", + "print(\"All available fields:\")\n", + "for field in sorted(all_fields):\n", + " print(f\" - {field}\")" + ] }, { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], - "source": [ - "# Get all unique values for a field (normalized)\n", - "carbon_sources = vdb.get_unique_values(\"carbon_source\")\n", - "\n", - "print(\"Unique carbon sources (normalized):\")\n", - "for source in sorted(carbon_sources):\n", - " print(f\" - {source}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Common fields (present in all datasets):\n", + " - carbon_source\n", + " - temperature_celsius\n" + ] + } + ], + "source": [ + "# Get fields present in ALL datasets (common fields)\n", + "common_fields = vdb.get_common_fields()\n", + "\n", + "print(\"Common fields (present in all datasets):\")\n", + "for field in sorted(common_fields):\n", + " print(f\" - {field}\")" + ] + }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "Carbon sources by dataset:\n", - "\n", - "BrentLab/kemmeren_2014/kemmeren_2014:\n", - " - unspecified\n" - ] - } - ], - "source": [ - "# Get values broken down by dataset\n", - "carbon_by_dataset = vdb.get_unique_values(\"carbon_source\", by_dataset=True)\n", - "\n", - "print(\"Carbon sources by dataset:\")\n", - "for dataset, sources in carbon_by_dataset.items():\n", - " print(f\"\\n{dataset}:\")\n", - " for source in sorted(sources):\n", - " print(f\" - {source}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 4. Simple Queries\n", - "\n", - "Now let's start querying data. The `query()` method is the primary interface." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Basic Query: All Samples with Glucose\n", - "\n", - "By default, queries return sample-level data (one row per sample) with all configured fields." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Datasets with comparative data\n", + "\n", + "\n", + "BrentLab/harbison_2004/harbison_2004:\n", + " - BrentLab/yeast_comparative_analysis/dto\n", + " via field: binding_id\n", + " fields available: 8\n", + "\n", + "BrentLab/kemmeren_2014/kemmeren_2014:\n", + " - BrentLab/yeast_comparative_analysis/dto\n", + " via field: perturbation_id\n", + " fields available: 8\n", + "Comparative data fields\n", + "\n", + "BrentLab/yeast_comparative_analysis/dto:\n", + " binding_id binding_rank_threshold binding_set_size \n", + " dto_empirical_pvalue dto_fdr perturbation_id \n", + " perturbation_rank_threshold perturbation_set_size \n" + ] + } + ], + "source": [ + "# Get fields that may be used to filter two or more datasets at a time\n", + "comp_info = vdb.get_comparative_analyses()\n", + "\n", + "print(\"Datasets with comparative data\\n\")\n", + "for primary_dataset, comparatives in sorted(comp_info[\"primary_to_comparative\"].items()):\n", + " print(f\"\\n{primary_dataset}:\")\n", + " for comp in comparatives:\n", + " comp_key = f\"{comp['comparative_repo']}/{comp['comparative_dataset']}\"\n", + " print(f\" - {comp_key}\")\n", + " print(f\" via field: {comp['via_field']}\")\n", + " num_fields = len(comp_info[\"comparative_fields\"].get(comp_key, []))\n", + " print(f\" fields available: {num_fields}\")\n", + "\n", + "# Show fields available from comparative datasets\n", + "print(\"Comparative data fields\")\n", + "for comp_dataset, fields in sorted(comp_info[\"comparative_fields\"].items()):\n", + " print(f\"\\n{comp_dataset}:\")\n", + " if fields:\n", + " # Print in columns for better readability\n", + " fields_sorted = sorted(fields)\n", + " for i in range(0, len(fields_sorted), 3):\n", + " row_fields = fields_sorted[i:i + 3]\n", + " print(\" \" + \" \".join(f\"{f:<28}\" for f in row_fields))\n", + " else:\n", + " print(\" (no fields found)\")" + ] + }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "Found 0 samples with glucose\n", - "\n", - "Columns: ['sample_id', 'regulator_locus_tag', 'regulator_symbol', 'carbon_source', 'dataset_id']\n", - "\n", - "First few rows:\n" - ] + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Discovering Valid Values\n", + "\n", + "VirtualDB can tell you what values exist for each field." + ] }, { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ { - "name": "sample_id", - "rawType": "int32", - "type": "integer" - }, - { - "name": "regulator_locus_tag", - "rawType": "object", - "type": "string" - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "Unique carbon sources (normalized):\n", + " - galactose\n", + " - glucose\n", + " - raffinose\n", + " - unspecified\n" + ] + } + ], + "source": [ + "# Get all unique values for a field (normalized)\n", + "carbon_source_factor_levels = vdb.get_unique_values(\"carbon_source\")\n", + "\n", + "print(\"Unique carbon sources (normalized):\")\n", + "for source in sorted(carbon_source_factor_levels):\n", + " print(f\" - {source}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ { - "name": "regulator_symbol", - "rawType": "object", - "type": "string" - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "Carbon sources by dataset:\n", + "\n", + "BrentLab/harbison_2004/harbison_2004:\n", + " - galactose\n", + " - glucose\n", + " - raffinose\n", + " - unspecified\n", + "\n", + "BrentLab/kemmeren_2014/kemmeren_2014:\n", + " - glucose\n" + ] + } + ], + "source": [ + "# Get values broken down by dataset\n", + "carbon_by_dataset = vdb.get_unique_values(\"carbon_source\", by_dataset=True)\n", + "\n", + "print(\"Carbon sources by dataset:\")\n", + "for dataset, sources in carbon_by_dataset.items():\n", + " print(f\"\\n{dataset}:\")\n", + " for source in sorted(sources):\n", + " print(f\" - {source}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4. Simple Queries\n", + "\n", + "Now let's start querying data. The `query()` method is the primary interface." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Basic Query: All Samples with Glucose\n", + "\n", + "By default, queries return sample-level data (one row per sample) with all configured fields." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ { - "name": "carbon_source", - "rawType": "object", - "type": "string" + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 1797 samples with glucose\n", + "\n", + "Columns: ['sample_id', 'regulator_locus_tag', 'regulator_symbol', 'condition', 'carbon_source', 'temperature_celsius', 'dataset_id']\n", + "\n", + "First few rows:\n" + ] }, { - "name": "dataset_id", - "rawType": "object", - "type": "string" + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "sample_id", + "rawType": "int32", + "type": "integer" + }, + { + "name": "regulator_locus_tag", + "rawType": "object", + "type": "string" + }, + { + "name": "regulator_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "condition", + "rawType": "object", + "type": "string" + }, + { + "name": "carbon_source", + "rawType": "object", + "type": "string" + }, + { + "name": "temperature_celsius", + "rawType": "float64", + "type": "float" + }, + { + "name": "dataset_id", + "rawType": "object", + "type": "string" + } + ], + "ref": "b22d63b4-294f-452a-b64e-f79320f4da61", + "rows": [ + [ + "0", + "1", + "YSC0017", + "MATA1", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "1", + "2", + "YAL051W", + "OAF1", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "2", + "3", + "YBL005W", + "PDR3", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "3", + "4", + "YBL008W", + "HIR1", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "4", + "5", + "YBL021C", + "HAP3", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ] + ], + "shape": { + "columns": 7, + "rows": 5 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sample_idregulator_locus_tagregulator_symbolconditioncarbon_sourcetemperature_celsiusdataset_id
01YSC0017MATA1YPDglucose30.0BrentLab/harbison_2004/harbison_2004
12YAL051WOAF1YPDglucose30.0BrentLab/harbison_2004/harbison_2004
23YBL005WPDR3YPDglucose30.0BrentLab/harbison_2004/harbison_2004
34YBL008WHIR1YPDglucose30.0BrentLab/harbison_2004/harbison_2004
45YBL021CHAP3YPDglucose30.0BrentLab/harbison_2004/harbison_2004
\n", + "
" + ], + "text/plain": [ + " sample_id regulator_locus_tag regulator_symbol condition carbon_source \\\n", + "0 1 YSC0017 MATA1 YPD glucose \n", + "1 2 YAL051W OAF1 YPD glucose \n", + "2 3 YBL005W PDR3 YPD glucose \n", + "3 4 YBL008W HIR1 YPD glucose \n", + "4 5 YBL021C HAP3 YPD glucose \n", + "\n", + " temperature_celsius dataset_id \n", + "0 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "1 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "2 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "3 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "4 30.0 BrentLab/harbison_2004/harbison_2004 " + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" } - ], - "ref": "57bb029a-d940-4e06-ae20-6eeae41e836a", - "rows": [], - "shape": { - "columns": 5, - "rows": 0 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sample_idregulator_locus_tagregulator_symbolcarbon_sourcedataset_id
\n", - "
" ], - "text/plain": [ - "Empty DataFrame\n", - "Columns: [sample_id, regulator_locus_tag, regulator_symbol, carbon_source, dataset_id]\n", - "Index: []" + "source": [ + "# Query all datasets for samples grown on glucose\n", + "glucose_samples = vdb.query(filters={\"carbon_source\": \"glucose\"})\n", + "\n", + "print(f\"Found {len(glucose_samples)} samples with glucose\")\n", + "print(f\"\\nColumns: {list(glucose_samples.columns)}\")\n", + "print(f\"\\nFirst few rows:\")\n", + "glucose_samples.head()" ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Query all datasets for samples grown on glucose\n", - "glucose_samples = vdb.query(filters={\"carbon_source\": \"glucose\"})\n", - "\n", - "print(f\"Found {len(glucose_samples)} samples with glucose\")\n", - "print(f\"\\nColumns: {list(glucose_samples.columns)}\")\n", - "print(f\"\\nFirst few rows:\")\n", - "glucose_samples.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Query Specific Datasets\n", - "\n", - "Limit your query to specific datasets using the `datasets` parameter." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ + }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "Found 0 samples from harbison_2004\n" - ] + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Query Specific Datasets\n", + "\n", + "Limit your query to specific datasets using the `datasets` parameter." + ] }, { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ { - "name": "index", - "rawType": "int64", - "type": "integer" + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 310 samples from harbison_2004\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "sample_id", + "rawType": "int32", + "type": "integer" + }, + { + "name": "regulator_locus_tag", + "rawType": "object", + "type": "string" + }, + { + "name": "regulator_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "condition", + "rawType": "object", + "type": "string" + }, + { + "name": "carbon_source", + "rawType": "object", + "type": "string" + }, + { + "name": "temperature_celsius", + "rawType": "float64", + "type": "float" + }, + { + "name": "dataset_id", + "rawType": "object", + "type": "string" + } + ], + "ref": "0c9ebf95-0bf1-46d7-83ee-57b87c5def44", + "rows": [ + [ + "0", + "1", + "YSC0017", + "MATA1", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "1", + "2", + "YAL051W", + "OAF1", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "2", + "3", + "YBL005W", + "PDR3", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "3", + "4", + "YBL008W", + "HIR1", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "4", + "5", + "YBL021C", + "HAP3", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ] + ], + "shape": { + "columns": 7, + "rows": 5 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sample_idregulator_locus_tagregulator_symbolconditioncarbon_sourcetemperature_celsiusdataset_id
01YSC0017MATA1YPDglucose30.0BrentLab/harbison_2004/harbison_2004
12YAL051WOAF1YPDglucose30.0BrentLab/harbison_2004/harbison_2004
23YBL005WPDR3YPDglucose30.0BrentLab/harbison_2004/harbison_2004
34YBL008WHIR1YPDglucose30.0BrentLab/harbison_2004/harbison_2004
45YBL021CHAP3YPDglucose30.0BrentLab/harbison_2004/harbison_2004
\n", + "
" + ], + "text/plain": [ + " sample_id regulator_locus_tag regulator_symbol condition carbon_source \\\n", + "0 1 YSC0017 MATA1 YPD glucose \n", + "1 2 YAL051W OAF1 YPD glucose \n", + "2 3 YBL005W PDR3 YPD glucose \n", + "3 4 YBL008W HIR1 YPD glucose \n", + "4 5 YBL021C HAP3 YPD glucose \n", + "\n", + " temperature_celsius dataset_id \n", + "0 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "1 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "2 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "3 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "4 30.0 BrentLab/harbison_2004/harbison_2004 " + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" } - ], - "ref": "a0d7c9a2-a3e0-4146-86ec-12e51a2e6968", - "rows": [], - "shape": { - "columns": 0, - "rows": 0 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
\n", - "
" ], - "text/plain": [ - "Empty DataFrame\n", - "Columns: []\n", - "Index: []" + "source": [ + "# Query only harbison_2004\n", + "harbison_glucose = vdb.query(\n", + " filters={\"carbon_source\": \"glucose\"},\n", + " datasets=[(\"BrentLab/harbison_2004\", \"harbison_2004\")]\n", + ")\n", + "\n", + "print(f\"Found {len(harbison_glucose)} samples from harbison_2004\")\n", + "harbison_glucose.head()" ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Query only harbison_2004\n", - "harbison_glucose = vdb.query(\n", - " filters={\"carbon_source\": \"glucose\"},\n", - " datasets=[(\"BrentLab/harbison_2004\", \"harbison_2004\")]\n", - ")\n", - "\n", - "print(f\"Found {len(harbison_glucose)} samples from harbison_2004\")\n", - "harbison_glucose.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Select Specific Fields\n", - "\n", - "Return only the fields you need with the `fields` parameter." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ + }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "Columns: ['sample_id', 'carbon_source', 'dataset_id']\n" - ] + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Select Specific Fields\n", + "\n", + "Return only the fields you need with the `fields` parameter." + ] }, { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ { - "name": "index", - "rawType": "int64", - "type": "integer" + "name": "stdout", + "output_type": "stream", + "text": [ + "Columns: ['sample_id', 'carbon_source', 'temperature_celsius', 'dataset_id']\n" + ] }, { - "name": "sample_id", - "rawType": "int32", - "type": "integer" - }, + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "sample_id", + "rawType": "int32", + "type": "integer" + }, + { + "name": "carbon_source", + "rawType": "object", + "type": "string" + }, + { + "name": "temperature_celsius", + "rawType": "float64", + "type": "float" + }, + { + "name": "dataset_id", + "rawType": "object", + "type": "string" + } + ], + "ref": "38c6fa44-08cf-4751-9476-7bca3cb1c41c", + "rows": [ + [ + "0", + "1", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "1", + "2", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "2", + "3", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "3", + "4", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "4", + "5", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ] + ], + "shape": { + "columns": 4, + "rows": 5 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sample_idcarbon_sourcetemperature_celsiusdataset_id
01glucose30.0BrentLab/harbison_2004/harbison_2004
12glucose30.0BrentLab/harbison_2004/harbison_2004
23glucose30.0BrentLab/harbison_2004/harbison_2004
34glucose30.0BrentLab/harbison_2004/harbison_2004
45glucose30.0BrentLab/harbison_2004/harbison_2004
\n", + "
" + ], + "text/plain": [ + " sample_id carbon_source temperature_celsius \\\n", + "0 1 glucose 30.0 \n", + "1 2 glucose 30.0 \n", + "2 3 glucose 30.0 \n", + "3 4 glucose 30.0 \n", + "4 5 glucose 30.0 \n", + "\n", + " dataset_id \n", + "0 BrentLab/harbison_2004/harbison_2004 \n", + "1 BrentLab/harbison_2004/harbison_2004 \n", + "2 BrentLab/harbison_2004/harbison_2004 \n", + "3 BrentLab/harbison_2004/harbison_2004 \n", + "4 BrentLab/harbison_2004/harbison_2004 " + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Get just sample_id, carbon_source, and temperature\n", + "minimal_data = vdb.query(\n", + " filters={\"carbon_source\": \"glucose\"},\n", + " fields=[\"sample_id\", \"carbon_source\", \"temperature_celsius\"]\n", + ")\n", + "\n", + "print(f\"Columns: {list(minimal_data.columns)}\")\n", + "minimal_data.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 5. Advanced Queries\n", + "\n", + "VirtualDB supports more sophisticated query patterns." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Multiple Filter Conditions" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ { - "name": "carbon_source", - "rawType": "object", - "type": "string" + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 1791 samples with glucose at 30C\n" + ] }, { - "name": "dataset_id", - "rawType": "object", - "type": "string" + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "sample_id", + "rawType": "int32", + "type": "integer" + }, + { + "name": "regulator_locus_tag", + "rawType": "object", + "type": "string" + }, + { + "name": "regulator_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "condition", + "rawType": "object", + "type": "string" + }, + { + "name": "carbon_source", + "rawType": "object", + "type": "string" + }, + { + "name": "temperature_celsius", + "rawType": "float64", + "type": "float" + }, + { + "name": "dataset_id", + "rawType": "object", + "type": "string" + } + ], + "ref": "357f69ed-ad79-4401-8458-5a1cc48f14c5", + "rows": [ + [ + "0", + "1", + "YSC0017", + "MATA1", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "1", + "2", + "YAL051W", + "OAF1", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "2", + "3", + "YBL005W", + "PDR3", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "3", + "4", + "YBL008W", + "HIR1", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "4", + "5", + "YBL021C", + "HAP3", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ] + ], + "shape": { + "columns": 7, + "rows": 5 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sample_idregulator_locus_tagregulator_symbolconditioncarbon_sourcetemperature_celsiusdataset_id
01YSC0017MATA1YPDglucose30.0BrentLab/harbison_2004/harbison_2004
12YAL051WOAF1YPDglucose30.0BrentLab/harbison_2004/harbison_2004
23YBL005WPDR3YPDglucose30.0BrentLab/harbison_2004/harbison_2004
34YBL008WHIR1YPDglucose30.0BrentLab/harbison_2004/harbison_2004
45YBL021CHAP3YPDglucose30.0BrentLab/harbison_2004/harbison_2004
\n", + "
" + ], + "text/plain": [ + " sample_id regulator_locus_tag regulator_symbol condition carbon_source \\\n", + "0 1 YSC0017 MATA1 YPD glucose \n", + "1 2 YAL051W OAF1 YPD glucose \n", + "2 3 YBL005W PDR3 YPD glucose \n", + "3 4 YBL008W HIR1 YPD glucose \n", + "4 5 YBL021C HAP3 YPD glucose \n", + "\n", + " temperature_celsius dataset_id \n", + "0 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "1 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "2 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "3 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "4 30.0 BrentLab/harbison_2004/harbison_2004 " + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" } - ], - "ref": "6dffa2f6-a87e-4ee2-ba81-22829fb14d26", - "rows": [], - "shape": { - "columns": 3, - "rows": 0 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sample_idcarbon_sourcedataset_id
\n", - "
" ], - "text/plain": [ - "Empty DataFrame\n", - "Columns: [sample_id, carbon_source, dataset_id]\n", - "Index: []" + "source": [ + "# Samples with glucose at 30C\n", + "glucose_30c = vdb.query(\n", + " filters={\n", + " \"carbon_source\": \"glucose\",\n", + " \"temperature_celsius\": 30\n", + " }\n", + ")\n", + "\n", + "print(f\"Found {len(glucose_30c)} samples with glucose at 30C\")\n", + "glucose_30c.head()" ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Get just sample_id, carbon_source, and temperature\n", - "minimal_data = vdb.query(\n", - " filters={\"carbon_source\": \"glucose\"},\n", - " fields=[\"sample_id\", \"carbon_source\", \"temperature_celsius\"]\n", - ")\n", - "\n", - "print(f\"Columns: {list(minimal_data.columns)}\")\n", - "minimal_data.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 5. Advanced Queries\n", - "\n", - "VirtualDB supports more sophisticated query patterns." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Multiple Filter Conditions" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ + }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "Found 0 samples with glucose at 30C\n" - ] + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Numeric Range Queries" + ] }, { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ { - "name": "index", - "rawType": "int64", - "type": "integer" - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 1833 samples at >= 30C\n", + "Found 1833 samples between 28-32C\n" + ] + } + ], + "source": [ + "# Samples at temperature >= 30C\n", + "warm_samples = vdb.query(\n", + " filters={\"temperature_celsius\": (\">=\", 30)}\n", + ")\n", + "\n", + "print(f\"Found {len(warm_samples)} samples at >= 30C\")\n", + "\n", + "# Samples between 28C and 32C\n", + "moderate_temp = vdb.query(\n", + " filters={\"temperature_celsius\": (\"between\", 28, 32)}\n", + ")\n", + "\n", + "print(f\"Found {len(moderate_temp)} samples between 28-32C\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Factor Alias Expansion\n", + "\n", + "When you query for a normalized value, VirtualDB automatically expands to all original aliases." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ { - "name": "sample_id", - "rawType": "int32", - "type": "integer" - }, + "name": "stdout", + "output_type": "stream", + "text": [ + " sample_id regulator_locus_tag regulator_symbol condition carbon_source \\\n", + "0 68 YDR277C MTH1 GAL galactose \n", + "1 112 YGL035C MIG1 GAL galactose \n", + "2 197 YKL038W RGT1 GAL galactose \n", + "3 335 YPL248C GAL4 GAL galactose \n", + "\n", + " temperature_celsius dataset_id \n", + "0 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "1 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "2 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "3 30.0 BrentLab/harbison_2004/harbison_2004 \n" + ] + } + ], + "source": [ + "# Query for \"galactose\" matches \"D-galactose\", \"gal\", and \"galactose\"\n", + "galactose_samples = vdb.query(filters={\"carbon_source\": \"galactose\"})\n", + "\n", + "print(galactose_samples)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Complete Data Retrieval\n", + "\n", + "By default, `query()` returns sample-level metadata (one row per sample). \n", + "Set `complete=True` to get all measurements (many rows per sample)." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ { - "name": "regulator_locus_tag", - "rawType": "object", - "type": "string" + "name": "stdout", + "output_type": "stream", + "text": [ + "Complete data: 1930060 rows\n", + "Columns: ['sample_id', 'db_id', 'target_locus_tag', 'target_symbol', 'effect', 'pvalue', 'regulator_locus_tag', 'regulator_symbol', 'condition', 'carbon_source', 'temperature_celsius', 'dataset_id']\n", + "\n", + "First few measurements:\n" + ] }, { - "name": "regulator_symbol", - "rawType": "object", - "type": "string" - }, + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "sample_id", + "rawType": "int32", + "type": "integer" + }, + { + "name": "db_id", + "rawType": "float64", + "type": "float" + }, + { + "name": "target_locus_tag", + "rawType": "object", + "type": "string" + }, + { + "name": "target_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "effect", + "rawType": "float64", + "type": "float" + }, + { + "name": "pvalue", + "rawType": "float64", + "type": "float" + }, + { + "name": "regulator_locus_tag", + "rawType": "object", + "type": "string" + }, + { + "name": "regulator_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "condition", + "rawType": "object", + "type": "string" + }, + { + "name": "carbon_source", + "rawType": "object", + "type": "string" + }, + { + "name": "temperature_celsius", + "rawType": "float64", + "type": "float" + }, + { + "name": "dataset_id", + "rawType": "object", + "type": "string" + } + ], + "ref": "0b10b74e-6f1a-42af-8654-7811d039bfac", + "rows": [ + [ + "0", + "1", + "0.0", + "YAL001C", + "TFC3", + "1.697754", + "0.068704735", + "YSC0017", + "MATA1", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "1", + "1", + "0.0", + "YAL002W", + "VPS8", + null, + null, + "YSC0017", + "MATA1", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "2", + "1", + "0.0", + "YAL003W", + "EFB1", + null, + null, + "YSC0017", + "MATA1", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "3", + "1", + "0.0", + "YAL004W", + "YAL004W", + "0.74534215", + "0.83592938", + "YSC0017", + "MATA1", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "4", + "1", + "0.0", + "YAL005C", + "SSA1", + null, + null, + "YSC0017", + "MATA1", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ] + ], + "shape": { + "columns": 12, + "rows": 5 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sample_iddb_idtarget_locus_tagtarget_symboleffectpvalueregulator_locus_tagregulator_symbolconditioncarbon_sourcetemperature_celsiusdataset_id
010.0YAL001CTFC31.6977540.068705YSC0017MATA1YPDglucose30.0BrentLab/harbison_2004/harbison_2004
110.0YAL002WVPS8NaNNaNYSC0017MATA1YPDglucose30.0BrentLab/harbison_2004/harbison_2004
210.0YAL003WEFB1NaNNaNYSC0017MATA1YPDglucose30.0BrentLab/harbison_2004/harbison_2004
310.0YAL004WYAL004W0.7453420.835929YSC0017MATA1YPDglucose30.0BrentLab/harbison_2004/harbison_2004
410.0YAL005CSSA1NaNNaNYSC0017MATA1YPDglucose30.0BrentLab/harbison_2004/harbison_2004
\n", + "
" + ], + "text/plain": [ + " sample_id db_id target_locus_tag target_symbol effect pvalue \\\n", + "0 1 0.0 YAL001C TFC3 1.697754 0.068705 \n", + "1 1 0.0 YAL002W VPS8 NaN NaN \n", + "2 1 0.0 YAL003W EFB1 NaN NaN \n", + "3 1 0.0 YAL004W YAL004W 0.745342 0.835929 \n", + "4 1 0.0 YAL005C SSA1 NaN NaN \n", + "\n", + " regulator_locus_tag regulator_symbol condition carbon_source \\\n", + "0 YSC0017 MATA1 YPD glucose \n", + "1 YSC0017 MATA1 YPD glucose \n", + "2 YSC0017 MATA1 YPD glucose \n", + "3 YSC0017 MATA1 YPD glucose \n", + "4 YSC0017 MATA1 YPD glucose \n", + "\n", + " temperature_celsius dataset_id \n", + "0 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "1 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "2 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "3 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "4 30.0 BrentLab/harbison_2004/harbison_2004 " + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Get complete data with measurements\n", + "complete_data = vdb.query(\n", + " filters={\"carbon_source\": \"glucose\"},\n", + " datasets=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", + " complete=True\n", + ")\n", + "\n", + "print(f\"Complete data: {len(complete_data)} rows\")\n", + "print(f\"Columns: {list(complete_data.columns)}\")\n", + "print(\"\\nFirst few measurements:\")\n", + "complete_data.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ { - "name": "carbon_source", - "rawType": "object", - "type": "string" + "name": "stdout", + "output_type": "stream", + "text": [ + "Binding data: 1930060 measurements\n" + ] }, { - "name": "dataset_id", - "rawType": "object", - "type": "string" + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "sample_id", + "rawType": "int32", + "type": "integer" + }, + { + "name": "regulator_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "target_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "effect", + "rawType": "float64", + "type": "float" + }, + { + "name": "pvalue", + "rawType": "float64", + "type": "float" + }, + { + "name": "dataset_id", + "rawType": "object", + "type": "string" + } + ], + "ref": "0b7cb890-7e9c-44d2-9ef5-59374bcf3a8a", + "rows": [ + [ + "0", + "2", + "OAF1", + "TFC3", + "1.5895642", + "0.088986168", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "1", + "2", + "OAF1", + "VPS8", + "1.1413208", + "0.32480496", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "2", + "2", + "OAF1", + "EFB1", + "0.72911994", + "0.87882413", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "3", + "2", + "OAF1", + "YAL004W", + "1.1679044", + "0.28225283", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "4", + "2", + "OAF1", + "SSA1", + "0.72911994", + "0.87882413", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "5", + "2", + "OAF1", + "ERP2", + "1.0508274", + "0.43070675", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "6", + "2", + "OAF1", + "FUN14", + "1.3478761", + "0.15551056", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "7", + "2", + "OAF1", + "SPO7", + "0.93967306", + "0.57823415", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "8", + "2", + "OAF1", + "MDM10", + "0.93967306", + "0.57823415", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "9", + "2", + "OAF1", + "SWC3", + "0.86566703", + "0.6711192", + "BrentLab/harbison_2004/harbison_2004" + ] + ], + "shape": { + "columns": 6, + "rows": 10 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sample_idregulator_symboltarget_symboleffectpvaluedataset_id
02OAF1TFC31.5895640.088986BrentLab/harbison_2004/harbison_2004
12OAF1VPS81.1413210.324805BrentLab/harbison_2004/harbison_2004
22OAF1EFB10.7291200.878824BrentLab/harbison_2004/harbison_2004
32OAF1YAL004W1.1679040.282253BrentLab/harbison_2004/harbison_2004
42OAF1SSA10.7291200.878824BrentLab/harbison_2004/harbison_2004
52OAF1ERP21.0508270.430707BrentLab/harbison_2004/harbison_2004
62OAF1FUN141.3478760.155511BrentLab/harbison_2004/harbison_2004
72OAF1SPO70.9396730.578234BrentLab/harbison_2004/harbison_2004
82OAF1MDM100.9396730.578234BrentLab/harbison_2004/harbison_2004
92OAF1SWC30.8656670.671119BrentLab/harbison_2004/harbison_2004
\n", + "
" + ], + "text/plain": [ + " sample_id regulator_symbol target_symbol effect pvalue \\\n", + "0 2 OAF1 TFC3 1.589564 0.088986 \n", + "1 2 OAF1 VPS8 1.141321 0.324805 \n", + "2 2 OAF1 EFB1 0.729120 0.878824 \n", + "3 2 OAF1 YAL004W 1.167904 0.282253 \n", + "4 2 OAF1 SSA1 0.729120 0.878824 \n", + "5 2 OAF1 ERP2 1.050827 0.430707 \n", + "6 2 OAF1 FUN14 1.347876 0.155511 \n", + "7 2 OAF1 SPO7 0.939673 0.578234 \n", + "8 2 OAF1 MDM10 0.939673 0.578234 \n", + "9 2 OAF1 SWC3 0.865667 0.671119 \n", + "\n", + " dataset_id \n", + "0 BrentLab/harbison_2004/harbison_2004 \n", + "1 BrentLab/harbison_2004/harbison_2004 \n", + "2 BrentLab/harbison_2004/harbison_2004 \n", + "3 BrentLab/harbison_2004/harbison_2004 \n", + "4 BrentLab/harbison_2004/harbison_2004 \n", + "5 BrentLab/harbison_2004/harbison_2004 \n", + "6 BrentLab/harbison_2004/harbison_2004 \n", + "7 BrentLab/harbison_2004/harbison_2004 \n", + "8 BrentLab/harbison_2004/harbison_2004 \n", + "9 BrentLab/harbison_2004/harbison_2004 " + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" } - ], - "ref": "8309daf0-f866-44e1-8ca4-3b5fa071dc9b", - "rows": [], - "shape": { - "columns": 5, - "rows": 0 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sample_idregulator_locus_tagregulator_symbolcarbon_sourcedataset_id
\n", - "
" ], - "text/plain": [ - "Empty DataFrame\n", - "Columns: [sample_id, regulator_locus_tag, regulator_symbol, carbon_source, dataset_id]\n", - "Index: []" + "source": [ + "# You can combine complete=True with field selection\n", + "# Get just the binding data columns\n", + "binding_data = vdb.query(\n", + " filters={\"carbon_source\": \"glucose\"},\n", + " datasets=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", + " fields=[\"sample_id\", \"regulator_symbol\", \"target_symbol\", \"effect\", \"pvalue\"],\n", + " complete=True\n", + ")\n", + "\n", + "print(f\"Binding data: {len(binding_data)} measurements\")\n", + "binding_data.head(10)" ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Samples with glucose at 30C\n", - "glucose_30c = vdb.query(\n", - " filters={\n", - " \"carbon_source\": \"glucose\",\n", - " \"temperature_celsius\": 30\n", - " }\n", - ")\n", - "\n", - "print(f\"Found {len(glucose_30c)} samples with glucose at 30C\")\n", - "glucose_30c.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Numeric Range Queries" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ + }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "Found 1487 samples at >= 30C\n", - "Found 1487 samples between 28-32C\n" - ] - } - ], - "source": [ - "# Samples at temperature >= 30C\n", - "warm_samples = vdb.query(\n", - " filters={\"temperature_celsius\": (\">=\", 30)}\n", - ")\n", - "\n", - "print(f\"Found {len(warm_samples)} samples at >= 30C\")\n", - "\n", - "# Samples between 28C and 32C\n", - "moderate_temp = vdb.query(\n", - " filters={\"temperature_celsius\": (\"between\", 28, 32)}\n", - ")\n", - "\n", - "print(f\"Found {len(moderate_temp)} samples between 28-32C\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Factor Alias Expansion\n", - "\n", - "When you query for a normalized value, VirtualDB automatically expands to all original aliases." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example analysis\n", + "\n", + "The following is an example of using VirtualDB to extract and summarize data across\n", + "datasets." + ] + }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "Found 0 galactose samples\n", - "\n", - "This query internally expanded to:\n", - " WHERE carbon_source IN ('D-galactose', 'gal', 'galactose')\n" - ] - } - ], - "source": [ - "# Query for \"galactose\" matches \"D-galactose\", \"gal\", and \"galactose\"\n", - "galactose_samples = vdb.query(filters={\"carbon_source\": \"galactose\"})\n", - "\n", - "print(f\"Found {len(galactose_samples)} galactose samples\")\n", - "print(\"\\nThis query internally expanded to:\")\n", - "print(\" WHERE carbon_source IN ('D-galactose', 'gal', 'galactose')\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Complete Data Retrieval\n", - "\n", - "By default, `query()` returns sample-level metadata (one row per sample). \n", - "Set `complete=True` to get all measurements (many rows per sample)." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Sample counts by dataset and carbon source:\n", + " dataset_id carbon_source num_samples\n", + "BrentLab/harbison_2004/harbison_2004 galactose 4\n", + "BrentLab/harbison_2004/harbison_2004 glucose 310\n", + "BrentLab/harbison_2004/harbison_2004 raffinose 1\n", + "BrentLab/harbison_2004/harbison_2004 unspecified 37\n", + "BrentLab/kemmeren_2014/kemmeren_2014 glucose 1487\n" + ] + } + ], + "source": [ + "# Compare number of samples by carbon source across datasets\n", + "\n", + "# Get all samples\n", + "all_samples = vdb.query()\n", + "\n", + "# Count by dataset and carbon source\n", + "summary = all_samples.groupby(['dataset_id', 'carbon_source']).size()\n", + "summary = summary.reset_index(name='num_samples')\n", + "\n", + "print(\"Sample counts by dataset and carbon source:\")\n", + "print(summary.to_string(index=False))" + ] + }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "Complete data: 0 rows\n", - "Columns: []\n", - "\n", - "First few measurements:\n" - ] + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Glucose samples by temperature:\n", + " 30.0C: 1791 samples\n" + ] + } + ], + "source": [ + "# Compare glucose experiments at different temperatures\n", + "\n", + "glucose_by_temp = vdb.query(\n", + " filters={\"carbon_source\": \"glucose\"},\n", + " fields=[\"sample_id\", \"temperature_celsius\", \"environmental_condition\"]\n", + ")\n", + "\n", + "# Count samples by temperature\n", + "temp_counts = glucose_by_temp['temperature_celsius'].value_counts().sort_index()\n", + "\n", + "print(\"Glucose samples by temperature:\")\n", + "for temp, count in temp_counts.items():\n", + " print(f\" {temp}C: {count} samples\")" + ] }, { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ { - "name": "index", - "rawType": "int64", - "type": "integer" + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 18678 FHL1 binding measurements in glucose\n", + "Significant targets: 379\n", + "\n", + "Top 10 targets by effect size:\n", + "target_symbol effect pvalue\n", + " RPS5 24.145013 9.739702e-09\n", + " RPL11A 20.585725 1.232356e-08\n", + " PRE2 20.585725 1.232356e-08\n", + " SRF1 20.342898 1.226799e-08\n", + " SLX8 20.057080 1.513076e-08\n", + " RPL23B 20.057080 1.513076e-08\n", + " RPL40A 19.262139 1.761808e-08\n", + " MLP2 19.262139 1.761808e-08\n", + " RPS6A 18.704379 1.544172e-08\n", + " RPL22A 17.926705 1.560357e-08\n" + ] } - ], - "ref": "a3896349-b6ba-4827-b7be-e4cfa318f2b2", - "rows": [], - "shape": { - "columns": 0, - "rows": 0 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
\n", - "
" ], - "text/plain": [ - "Empty DataFrame\n", - "Columns: []\n", - "Index: []" + "source": [ + "# Get binding data for a specific regulator across datasets\n", + "\n", + "# Query for FHL1 binding in glucose conditions\n", + "fhl1_binding = vdb.query(\n", + " filters={\n", + " \"carbon_source\": \"glucose\",\n", + " \"regulator_symbol\": \"FHL1\"\n", + " },\n", + " fields=[\"sample_id\", \"regulator_symbol\", \"target_symbol\", \"effect\", \"pvalue\"],\n", + " complete=True\n", + ")\n", + "\n", + "print(f\"Found {len(fhl1_binding)} FHL1 binding measurements in glucose\")\n", + "\n", + "# Find significant targets (p < 0.001)\n", + "significant = fhl1_binding[fhl1_binding['pvalue'] < 0.001]\n", + "print(f\"Significant targets: {len(significant)}\")\n", + "\n", + "# Top 10 by effect size\n", + "top_targets = significant.nlargest(10, 'effect')[['target_symbol', 'effect', 'pvalue']]\n", + "print(\"\\nTop 10 targets by effect size:\")\n", + "print(top_targets.to_string(index=False))" ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Get complete data with measurements\n", - "complete_data = vdb.query(\n", - " filters={\"carbon_source\": \"glucose\"},\n", - " datasets=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", - " complete=True\n", - ")\n", - "\n", - "print(f\"Complete data: {len(complete_data)} rows\")\n", - "print(f\"Columns: {list(complete_data.columns)}\")\n", - "print(\"\\nFirst few measurements:\")\n", - "complete_data.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ + }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "Binding data: 0 measurements\n" - ] + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Querying Comparative Datasets\n", + "\n", + "Comparative datasets like DTO (Direct Target Overlap) contain analysis results that relate samples across multiple datasets. These datasets can be queried directly to find significant cross-dataset relationships." + ] }, { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Fetching 6 files: 100%|██████████| 6/6 [00:00<00:00, 65536.00it/s]\n", + "Fetching 6 files: 100%|██████████| 6/6 [00:00<00:00, 57325.34it/s]" + ] + }, { - "name": "index", - "rawType": "int64", - "type": "integer" + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 32 FHL1 binding measurements\n", + "\n", + "Columns: ['sample_id', 'regulator_symbol', 'condition', 'dto_fdr', 'perturbation_id', 'dataset_id']\n", + "\n", + "Rows with DTO data: 4\n", + "\n", + "First few results:\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "sample_id", + "rawType": "int32", + "type": "integer" + }, + { + "name": "regulator_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "condition", + "rawType": "object", + "type": "string" + }, + { + "name": "dto_fdr", + "rawType": "float64", + "type": "float" + }, + { + "name": "perturbation_id", + "rawType": "object", + "type": "string" + }, + { + "name": "dataset_id", + "rawType": "object", + "type": "string" + } + ], + "ref": "a0eb6112-b457-4642-add7-4bcd5068e495", + "rows": [ + [ + "0", + "345", + "FHL1", + "H2O2Hi", + "0.4549087454017032", + "BrentLab/Hackett_2020;hackett_2020;1666", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "1", + "345", + "FHL1", + "H2O2Hi", + null, + "BrentLab/Hackett_2020;hackett_2020;1665", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "2", + "345", + "FHL1", + "H2O2Hi", + null, + "BrentLab/Hackett_2020;hackett_2020;1667", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "3", + "345", + "FHL1", + "H2O2Hi", + null, + "BrentLab/Hackett_2020;hackett_2020;1669", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "4", + "345", + "FHL1", + "H2O2Hi", + null, + "BrentLab/Hackett_2020;hackett_2020;1663", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "5", + "345", + "FHL1", + "H2O2Hi", + null, + "BrentLab/Hackett_2020;hackett_2020;1664", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "6", + "345", + "FHL1", + "H2O2Hi", + null, + "BrentLab/Hackett_2020;hackett_2020;1670", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "7", + "345", + "FHL1", + "H2O2Hi", + null, + "BrentLab/Hackett_2020;hackett_2020;1668", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "8", + "346", + "FHL1", + "RAPA", + null, + "BrentLab/Hackett_2020;hackett_2020;1667", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "9", + "346", + "FHL1", + "RAPA", + null, + "BrentLab/Hackett_2020;hackett_2020;1663", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "10", + "346", + "FHL1", + "RAPA", + null, + "BrentLab/Hackett_2020;hackett_2020;1670", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "11", + "346", + "FHL1", + "RAPA", + null, + "BrentLab/Hackett_2020;hackett_2020;1668", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "12", + "346", + "FHL1", + "RAPA", + "0.0", + "BrentLab/Hackett_2020;hackett_2020;1666", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "13", + "346", + "FHL1", + "RAPA", + null, + "BrentLab/Hackett_2020;hackett_2020;1669", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "14", + "346", + "FHL1", + "RAPA", + null, + "BrentLab/Hackett_2020;hackett_2020;1664", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "15", + "346", + "FHL1", + "RAPA", + null, + "BrentLab/Hackett_2020;hackett_2020;1665", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "16", + "347", + "FHL1", + "SM", + null, + "BrentLab/Hackett_2020;hackett_2020;1667", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "17", + "347", + "FHL1", + "SM", + "0.0221957781456953", + "BrentLab/Hackett_2020;hackett_2020;1666", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "18", + "347", + "FHL1", + "SM", + null, + "BrentLab/Hackett_2020;hackett_2020;1669", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "19", + "347", + "FHL1", + "SM", + null, + "BrentLab/Hackett_2020;hackett_2020;1664", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "20", + "347", + "FHL1", + "SM", + null, + "BrentLab/Hackett_2020;hackett_2020;1663", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "21", + "347", + "FHL1", + "SM", + null, + "BrentLab/Hackett_2020;hackett_2020;1670", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "22", + "347", + "FHL1", + "SM", + null, + "BrentLab/Hackett_2020;hackett_2020;1668", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "23", + "347", + "FHL1", + "SM", + null, + "BrentLab/Hackett_2020;hackett_2020;1665", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "24", + "348", + "FHL1", + "YPD", + null, + "BrentLab/Hackett_2020;hackett_2020;1664", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "25", + "348", + "FHL1", + "YPD", + "0.089578429724277", + "BrentLab/Hackett_2020;hackett_2020;1666", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "26", + "348", + "FHL1", + "YPD", + null, + "BrentLab/Hackett_2020;hackett_2020;1663", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "27", + "348", + "FHL1", + "YPD", + null, + "BrentLab/Hackett_2020;hackett_2020;1667", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "28", + "348", + "FHL1", + "YPD", + null, + "BrentLab/Hackett_2020;hackett_2020;1669", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "29", + "348", + "FHL1", + "YPD", + null, + "BrentLab/Hackett_2020;hackett_2020;1665", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "30", + "348", + "FHL1", + "YPD", + null, + "BrentLab/Hackett_2020;hackett_2020;1670", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "31", + "348", + "FHL1", + "YPD", + null, + "BrentLab/Hackett_2020;hackett_2020;1668", + "BrentLab/harbison_2004/harbison_2004" + ] + ], + "shape": { + "columns": 6, + "rows": 32 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sample_idregulator_symbolconditiondto_fdrperturbation_iddataset_id
0345FHL1H2O2Hi0.454909BrentLab/Hackett_2020;hackett_2020;1666BrentLab/harbison_2004/harbison_2004
1345FHL1H2O2HiNaNBrentLab/Hackett_2020;hackett_2020;1665BrentLab/harbison_2004/harbison_2004
2345FHL1H2O2HiNaNBrentLab/Hackett_2020;hackett_2020;1667BrentLab/harbison_2004/harbison_2004
3345FHL1H2O2HiNaNBrentLab/Hackett_2020;hackett_2020;1669BrentLab/harbison_2004/harbison_2004
4345FHL1H2O2HiNaNBrentLab/Hackett_2020;hackett_2020;1663BrentLab/harbison_2004/harbison_2004
5345FHL1H2O2HiNaNBrentLab/Hackett_2020;hackett_2020;1664BrentLab/harbison_2004/harbison_2004
6345FHL1H2O2HiNaNBrentLab/Hackett_2020;hackett_2020;1670BrentLab/harbison_2004/harbison_2004
7345FHL1H2O2HiNaNBrentLab/Hackett_2020;hackett_2020;1668BrentLab/harbison_2004/harbison_2004
8346FHL1RAPANaNBrentLab/Hackett_2020;hackett_2020;1667BrentLab/harbison_2004/harbison_2004
9346FHL1RAPANaNBrentLab/Hackett_2020;hackett_2020;1663BrentLab/harbison_2004/harbison_2004
10346FHL1RAPANaNBrentLab/Hackett_2020;hackett_2020;1670BrentLab/harbison_2004/harbison_2004
11346FHL1RAPANaNBrentLab/Hackett_2020;hackett_2020;1668BrentLab/harbison_2004/harbison_2004
12346FHL1RAPA0.000000BrentLab/Hackett_2020;hackett_2020;1666BrentLab/harbison_2004/harbison_2004
13346FHL1RAPANaNBrentLab/Hackett_2020;hackett_2020;1669BrentLab/harbison_2004/harbison_2004
14346FHL1RAPANaNBrentLab/Hackett_2020;hackett_2020;1664BrentLab/harbison_2004/harbison_2004
15346FHL1RAPANaNBrentLab/Hackett_2020;hackett_2020;1665BrentLab/harbison_2004/harbison_2004
16347FHL1SMNaNBrentLab/Hackett_2020;hackett_2020;1667BrentLab/harbison_2004/harbison_2004
17347FHL1SM0.022196BrentLab/Hackett_2020;hackett_2020;1666BrentLab/harbison_2004/harbison_2004
18347FHL1SMNaNBrentLab/Hackett_2020;hackett_2020;1669BrentLab/harbison_2004/harbison_2004
19347FHL1SMNaNBrentLab/Hackett_2020;hackett_2020;1664BrentLab/harbison_2004/harbison_2004
20347FHL1SMNaNBrentLab/Hackett_2020;hackett_2020;1663BrentLab/harbison_2004/harbison_2004
21347FHL1SMNaNBrentLab/Hackett_2020;hackett_2020;1670BrentLab/harbison_2004/harbison_2004
22347FHL1SMNaNBrentLab/Hackett_2020;hackett_2020;1668BrentLab/harbison_2004/harbison_2004
23347FHL1SMNaNBrentLab/Hackett_2020;hackett_2020;1665BrentLab/harbison_2004/harbison_2004
24348FHL1YPDNaNBrentLab/Hackett_2020;hackett_2020;1664BrentLab/harbison_2004/harbison_2004
25348FHL1YPD0.089578BrentLab/Hackett_2020;hackett_2020;1666BrentLab/harbison_2004/harbison_2004
26348FHL1YPDNaNBrentLab/Hackett_2020;hackett_2020;1663BrentLab/harbison_2004/harbison_2004
27348FHL1YPDNaNBrentLab/Hackett_2020;hackett_2020;1667BrentLab/harbison_2004/harbison_2004
28348FHL1YPDNaNBrentLab/Hackett_2020;hackett_2020;1669BrentLab/harbison_2004/harbison_2004
29348FHL1YPDNaNBrentLab/Hackett_2020;hackett_2020;1665BrentLab/harbison_2004/harbison_2004
30348FHL1YPDNaNBrentLab/Hackett_2020;hackett_2020;1670BrentLab/harbison_2004/harbison_2004
31348FHL1YPDNaNBrentLab/Hackett_2020;hackett_2020;1668BrentLab/harbison_2004/harbison_2004
\n", + "
" + ], + "text/plain": [ + " sample_id regulator_symbol condition dto_fdr \\\n", + "0 345 FHL1 H2O2Hi 0.454909 \n", + "1 345 FHL1 H2O2Hi NaN \n", + "2 345 FHL1 H2O2Hi NaN \n", + "3 345 FHL1 H2O2Hi NaN \n", + "4 345 FHL1 H2O2Hi NaN \n", + "5 345 FHL1 H2O2Hi NaN \n", + "6 345 FHL1 H2O2Hi NaN \n", + "7 345 FHL1 H2O2Hi NaN \n", + "8 346 FHL1 RAPA NaN \n", + "9 346 FHL1 RAPA NaN \n", + "10 346 FHL1 RAPA NaN \n", + "11 346 FHL1 RAPA NaN \n", + "12 346 FHL1 RAPA 0.000000 \n", + "13 346 FHL1 RAPA NaN \n", + "14 346 FHL1 RAPA NaN \n", + "15 346 FHL1 RAPA NaN \n", + "16 347 FHL1 SM NaN \n", + "17 347 FHL1 SM 0.022196 \n", + "18 347 FHL1 SM NaN \n", + "19 347 FHL1 SM NaN \n", + "20 347 FHL1 SM NaN \n", + "21 347 FHL1 SM NaN \n", + "22 347 FHL1 SM NaN \n", + "23 347 FHL1 SM NaN \n", + "24 348 FHL1 YPD NaN \n", + "25 348 FHL1 YPD 0.089578 \n", + "26 348 FHL1 YPD NaN \n", + "27 348 FHL1 YPD NaN \n", + "28 348 FHL1 YPD NaN \n", + "29 348 FHL1 YPD NaN \n", + "30 348 FHL1 YPD NaN \n", + "31 348 FHL1 YPD NaN \n", + "\n", + " perturbation_id \\\n", + "0 BrentLab/Hackett_2020;hackett_2020;1666 \n", + "1 BrentLab/Hackett_2020;hackett_2020;1665 \n", + "2 BrentLab/Hackett_2020;hackett_2020;1667 \n", + "3 BrentLab/Hackett_2020;hackett_2020;1669 \n", + "4 BrentLab/Hackett_2020;hackett_2020;1663 \n", + "5 BrentLab/Hackett_2020;hackett_2020;1664 \n", + "6 BrentLab/Hackett_2020;hackett_2020;1670 \n", + "7 BrentLab/Hackett_2020;hackett_2020;1668 \n", + "8 BrentLab/Hackett_2020;hackett_2020;1667 \n", + "9 BrentLab/Hackett_2020;hackett_2020;1663 \n", + "10 BrentLab/Hackett_2020;hackett_2020;1670 \n", + "11 BrentLab/Hackett_2020;hackett_2020;1668 \n", + "12 BrentLab/Hackett_2020;hackett_2020;1666 \n", + "13 BrentLab/Hackett_2020;hackett_2020;1669 \n", + "14 BrentLab/Hackett_2020;hackett_2020;1664 \n", + "15 BrentLab/Hackett_2020;hackett_2020;1665 \n", + "16 BrentLab/Hackett_2020;hackett_2020;1667 \n", + "17 BrentLab/Hackett_2020;hackett_2020;1666 \n", + "18 BrentLab/Hackett_2020;hackett_2020;1669 \n", + "19 BrentLab/Hackett_2020;hackett_2020;1664 \n", + "20 BrentLab/Hackett_2020;hackett_2020;1663 \n", + "21 BrentLab/Hackett_2020;hackett_2020;1670 \n", + "22 BrentLab/Hackett_2020;hackett_2020;1668 \n", + "23 BrentLab/Hackett_2020;hackett_2020;1665 \n", + "24 BrentLab/Hackett_2020;hackett_2020;1664 \n", + "25 BrentLab/Hackett_2020;hackett_2020;1666 \n", + "26 BrentLab/Hackett_2020;hackett_2020;1663 \n", + "27 BrentLab/Hackett_2020;hackett_2020;1667 \n", + "28 BrentLab/Hackett_2020;hackett_2020;1669 \n", + "29 BrentLab/Hackett_2020;hackett_2020;1665 \n", + "30 BrentLab/Hackett_2020;hackett_2020;1670 \n", + "31 BrentLab/Hackett_2020;hackett_2020;1668 \n", + "\n", + " dataset_id \n", + "0 BrentLab/harbison_2004/harbison_2004 \n", + "1 BrentLab/harbison_2004/harbison_2004 \n", + "2 BrentLab/harbison_2004/harbison_2004 \n", + "3 BrentLab/harbison_2004/harbison_2004 \n", + "4 BrentLab/harbison_2004/harbison_2004 \n", + "5 BrentLab/harbison_2004/harbison_2004 \n", + "6 BrentLab/harbison_2004/harbison_2004 \n", + "7 BrentLab/harbison_2004/harbison_2004 \n", + "8 BrentLab/harbison_2004/harbison_2004 \n", + "9 BrentLab/harbison_2004/harbison_2004 \n", + "10 BrentLab/harbison_2004/harbison_2004 \n", + "11 BrentLab/harbison_2004/harbison_2004 \n", + "12 BrentLab/harbison_2004/harbison_2004 \n", + "13 BrentLab/harbison_2004/harbison_2004 \n", + "14 BrentLab/harbison_2004/harbison_2004 \n", + "15 BrentLab/harbison_2004/harbison_2004 \n", + "16 BrentLab/harbison_2004/harbison_2004 \n", + "17 BrentLab/harbison_2004/harbison_2004 \n", + "18 BrentLab/harbison_2004/harbison_2004 \n", + "19 BrentLab/harbison_2004/harbison_2004 \n", + "20 BrentLab/harbison_2004/harbison_2004 \n", + "21 BrentLab/harbison_2004/harbison_2004 \n", + "22 BrentLab/harbison_2004/harbison_2004 \n", + "23 BrentLab/harbison_2004/harbison_2004 \n", + "24 BrentLab/harbison_2004/harbison_2004 \n", + "25 BrentLab/harbison_2004/harbison_2004 \n", + "26 BrentLab/harbison_2004/harbison_2004 \n", + "27 BrentLab/harbison_2004/harbison_2004 \n", + "28 BrentLab/harbison_2004/harbison_2004 \n", + "29 BrentLab/harbison_2004/harbison_2004 \n", + "30 BrentLab/harbison_2004/harbison_2004 \n", + "31 BrentLab/harbison_2004/harbison_2004 " + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" } - ], - "ref": "fc59efba-e365-4b1c-b9ff-31828897cde2", - "rows": [], - "shape": { - "columns": 0, - "rows": 0 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
\n", - "
" ], - "text/plain": [ - "Empty DataFrame\n", - "Columns: []\n", - "Index: []" + "source": [ + "# Query harbison_2004 binding data enriched with DTO metrics\n", + "# This demonstrates field-based joins: requesting dto_fdr field\n", + "# while querying the primary binding dataset\n", + "\n", + "binding_with_dto = vdb.query(\n", + " datasets=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", + " filters={\"regulator_symbol\": \"FHL1\"},\n", + " fields=[\"sample_id\", \"regulator_symbol\", \"condition\", \"dto_fdr\", \"binding_id\", \"perturbation_id\"],\n", + ")\n", + "\n", + "print(f\"Found {len(binding_with_dto)} FHL1 binding measurements\")\n", + "print(f\"\\nColumns: {list(binding_with_dto.columns)}\")\n", + "print(f\"\\nRows with DTO data: {binding_with_dto['dto_fdr'].notna().sum()}\")\n", + "print(f\"\\nFirst few results:\")\n", + "binding_with_dto" ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# You can combine complete=True with field selection\n", - "# Get just the binding data columns\n", - "binding_data = vdb.query(\n", - " filters={\"carbon_source\": \"glucose\"},\n", - " datasets=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", - " fields=[\"sample_id\", \"regulator_symbol\", \"target_symbol\", \"effect\", \"pvalue\"],\n", - " complete=True\n", - ")\n", - "\n", - "print(f\"Binding data: {len(binding_data)} measurements\")\n", - "binding_data.head(10)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 6. Understanding the Configuration\n", - "\n", - "Let's examine the configuration structure in more detail." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Property Mapping Levels\n", - "\n", - "Properties can be extracted at three hierarchy levels:\n", - "\n", - "1. **Repository-wide**: Common to all datasets\n", - "```yaml\n", - "repositories:\n", - " BrentLab/some_repo:\n", - " nitrogen_source: # Applies to all datasets in this repo\n", - " path: media.nitrogen_source.name\n", - "```\n", - "\n", - "2. **Dataset-specific**: Specific to one config\n", - "```yaml\n", - " dataset:\n", - " config_name:\n", - " phosphate_source: # Only for this dataset\n", - " path: media.phosphate_source.compound\n", - "```\n", - "\n", - "3. **Field-level**: Varies per sample\n", - "```yaml\n", - " carbon_source:\n", - " field: condition # Extract from 'condition' field\n", - " path: media.carbon_source.compound\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Field Aliases (Column Renaming)\n", - "\n", - "When you specify just a `field` without a `path`, it creates a column alias:\n", - "\n", - "```yaml\n", - "environmental_condition:\n", - " field: condition # Renames 'condition' to 'environmental_condition'\n", - "```\n", - "\n", - "This is useful for:\n", - "- Standardizing field names across datasets\n", - "- Enabling normalization of the field values" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Factor Aliases (Value Normalization)\n", - "\n", - "The `factor_aliases` section maps varying terminologies to standard values:\n", - "\n", - "```yaml\n", - "factor_aliases:\n", - " carbon_source:\n", - " glucose: [D-glucose, dextrose, glu]\n", - " galactose: [D-galactose, gal]\n", - "```\n", - "\n", - "This means:\n", - "- Querying for `\"glucose\"` matches `\"D-glucose\"`, `\"dextrose\"`, and `\"glu\"`\n", - "- All returned values are normalized to the canonical form (`\"glucose\"`)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 7. Real-World Example: Cross-Dataset Comparison\n", - "\n", - "Let's do a practical analysis comparing experiments across datasets." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ + }, { - "ename": "KeyError", - "evalue": "'_repo_id'", - "output_type": "error", - "traceback": [ - "\u001b[31m---------------------------------------------------------------------------\u001b[39m", - "\u001b[31mKeyError\u001b[39m Traceback (most recent call last)", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[16]\u001b[39m\u001b[32m, line 7\u001b[39m\n\u001b[32m 4\u001b[39m all_samples = vdb.query()\n\u001b[32m 6\u001b[39m \u001b[38;5;66;03m# Count by dataset and carbon source\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m7\u001b[39m summary = \u001b[43mall_samples\u001b[49m\u001b[43m.\u001b[49m\u001b[43mgroupby\u001b[49m\u001b[43m(\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43m_repo_id\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43m_config_name\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43mcarbon_source\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m.size()\n\u001b[32m 8\u001b[39m summary = summary.reset_index(name=\u001b[33m'\u001b[39m\u001b[33mnum_samples\u001b[39m\u001b[33m'\u001b[39m)\n\u001b[32m 10\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33mSample counts by dataset and carbon source:\u001b[39m\u001b[33m\"\u001b[39m)\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/pandas/core/frame.py:9210\u001b[39m, in \u001b[36mDataFrame.groupby\u001b[39m\u001b[34m(self, by, axis, level, as_index, sort, group_keys, observed, dropna)\u001b[39m\n\u001b[32m 9207\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m level \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m by \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m 9208\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[33m\"\u001b[39m\u001b[33mYou have to supply one of \u001b[39m\u001b[33m'\u001b[39m\u001b[33mby\u001b[39m\u001b[33m'\u001b[39m\u001b[33m and \u001b[39m\u001b[33m'\u001b[39m\u001b[33mlevel\u001b[39m\u001b[33m'\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m-> \u001b[39m\u001b[32m9210\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mDataFrameGroupBy\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 9211\u001b[39m \u001b[43m \u001b[49m\u001b[43mobj\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 9212\u001b[39m \u001b[43m \u001b[49m\u001b[43mkeys\u001b[49m\u001b[43m=\u001b[49m\u001b[43mby\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9213\u001b[39m \u001b[43m \u001b[49m\u001b[43maxis\u001b[49m\u001b[43m=\u001b[49m\u001b[43maxis\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9214\u001b[39m \u001b[43m \u001b[49m\u001b[43mlevel\u001b[49m\u001b[43m=\u001b[49m\u001b[43mlevel\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9215\u001b[39m \u001b[43m \u001b[49m\u001b[43mas_index\u001b[49m\u001b[43m=\u001b[49m\u001b[43mas_index\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9216\u001b[39m \u001b[43m \u001b[49m\u001b[43msort\u001b[49m\u001b[43m=\u001b[49m\u001b[43msort\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9217\u001b[39m \u001b[43m \u001b[49m\u001b[43mgroup_keys\u001b[49m\u001b[43m=\u001b[49m\u001b[43mgroup_keys\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9218\u001b[39m \u001b[43m \u001b[49m\u001b[43mobserved\u001b[49m\u001b[43m=\u001b[49m\u001b[43mobserved\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9219\u001b[39m \u001b[43m \u001b[49m\u001b[43mdropna\u001b[49m\u001b[43m=\u001b[49m\u001b[43mdropna\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9220\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/pandas/core/groupby/groupby.py:1331\u001b[39m, in \u001b[36mGroupBy.__init__\u001b[39m\u001b[34m(self, obj, keys, axis, level, grouper, exclusions, selection, as_index, sort, group_keys, observed, dropna)\u001b[39m\n\u001b[32m 1328\u001b[39m \u001b[38;5;28mself\u001b[39m.dropna = dropna\n\u001b[32m 1330\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m grouper \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m-> \u001b[39m\u001b[32m1331\u001b[39m grouper, exclusions, obj = \u001b[43mget_grouper\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 1332\u001b[39m \u001b[43m \u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1333\u001b[39m \u001b[43m \u001b[49m\u001b[43mkeys\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1334\u001b[39m \u001b[43m \u001b[49m\u001b[43maxis\u001b[49m\u001b[43m=\u001b[49m\u001b[43maxis\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1335\u001b[39m \u001b[43m \u001b[49m\u001b[43mlevel\u001b[49m\u001b[43m=\u001b[49m\u001b[43mlevel\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1336\u001b[39m \u001b[43m \u001b[49m\u001b[43msort\u001b[49m\u001b[43m=\u001b[49m\u001b[43msort\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1337\u001b[39m \u001b[43m \u001b[49m\u001b[43mobserved\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mobserved\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mis\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mlib\u001b[49m\u001b[43m.\u001b[49m\u001b[43mno_default\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01melse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mobserved\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1338\u001b[39m \u001b[43m \u001b[49m\u001b[43mdropna\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mdropna\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1339\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1341\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m observed \u001b[38;5;129;01mis\u001b[39;00m lib.no_default:\n\u001b[32m 1342\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28many\u001b[39m(ping._passed_categorical \u001b[38;5;28;01mfor\u001b[39;00m ping \u001b[38;5;129;01min\u001b[39;00m grouper.groupings):\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/pandas/core/groupby/grouper.py:1043\u001b[39m, in \u001b[36mget_grouper\u001b[39m\u001b[34m(obj, key, axis, level, sort, observed, validate, dropna)\u001b[39m\n\u001b[32m 1041\u001b[39m in_axis, level, gpr = \u001b[38;5;28;01mFalse\u001b[39;00m, gpr, \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m 1042\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m-> \u001b[39m\u001b[32m1043\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m(gpr)\n\u001b[32m 1044\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(gpr, Grouper) \u001b[38;5;129;01mand\u001b[39;00m gpr.key \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m 1045\u001b[39m \u001b[38;5;66;03m# Add key to exclusions\u001b[39;00m\n\u001b[32m 1046\u001b[39m exclusions.add(gpr.key)\n", - "\u001b[31mKeyError\u001b[39m: '_repo_id'" - ] + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Fetching 6 files: 100%|██████████| 6/6 [00:00<00:00, 122760.12it/s]\n", + "Fetching 6 files: 100%|██████████| 6/6 [00:00<00:00, 35951.18it/s]\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "sample_id", + "rawType": "int32", + "type": "integer" + }, + { + "name": "regulator_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "perturbation_id", + "rawType": "object", + "type": "string" + }, + { + "name": "dto_empirical_pvalue", + "rawType": "float64", + "type": "float" + }, + { + "name": "dataset_id", + "rawType": "object", + "type": "string" + } + ], + "ref": "f666fc22-ce67-46fc-80bb-c44baafdf799", + "rows": [ + [ + "0", + "347", + "FHL1", + "BrentLab/Hackett_2020;hackett_2020;1666", + "0.297", + "BrentLab/harbison_2004/harbison_2004" + ] + ], + "shape": { + "columns": 5, + "rows": 1 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sample_idregulator_symbolperturbation_iddto_empirical_pvaluedataset_id
0347FHL1BrentLab/Hackett_2020;hackett_2020;16660.297BrentLab/harbison_2004/harbison_2004
\n", + "
" + ], + "text/plain": [ + " sample_id regulator_symbol perturbation_id \\\n", + "0 347 FHL1 BrentLab/Hackett_2020;hackett_2020;1666 \n", + "\n", + " dto_empirical_pvalue dataset_id \n", + "0 0.297 BrentLab/harbison_2004/harbison_2004 " + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# You can also filter on comparative dataset fields\n", + "# This returns only binding measurements with significant DTO results\n", + "\n", + "significant_dtos = vdb.query(\n", + " datasets=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", + " filters={\n", + " \"regulator_symbol\": \"FHL1\",\n", + " # the threshold is high here b/c FHL1 didn't have significant results in harbison\n", + " \"dto_empirical_pvalue\": (\"<\", 0.5)\n", + " },\n", + " fields=[\"sample_id\", \"regulator_symbol\", \"target_symbol\", \"perturbation_id\", \"dto_empirical_pvalue\"],\n", + ")\n", + "\n", + "significant_dtos" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "tfbpapi-py3.11", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" } - ], - "source": [ - "# Compare number of samples by carbon source across datasets\n", - "\n", - "# Get all samples\n", - "all_samples = vdb.query()\n", - "\n", - "# Count by dataset and carbon source\n", - "summary = all_samples.groupby(['_repo_id', '_config_name', 'carbon_source']).size()\n", - "summary = summary.reset_index(name='num_samples')\n", - "\n", - "print(\"Sample counts by dataset and carbon source:\")\n", - "print(summary.to_string(index=False))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Compare glucose experiments at different temperatures\n", - "\n", - "glucose_by_temp = vdb.query(\n", - " filters={\"carbon_source\": \"glucose\"},\n", - " fields=[\"sample_id\", \"temperature_celsius\", \"environmental_condition\"]\n", - ")\n", - "\n", - "# Count samples by temperature\n", - "temp_counts = glucose_by_temp['temperature_celsius'].value_counts().sort_index()\n", - "\n", - "print(\"Glucose samples by temperature:\")\n", - "for temp, count in temp_counts.items():\n", - " print(f\" {temp}C: {count} samples\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Get binding data for a specific regulator across datasets\n", - "\n", - "# Query for FHL1 binding in glucose conditions\n", - "fhl1_binding = vdb.query(\n", - " filters={\n", - " \"carbon_source\": \"glucose\",\n", - " \"regulator_symbol\": \"FHL1\"\n", - " },\n", - " fields=[\"sample_id\", \"regulator_symbol\", \"target_symbol\", \"effect\", \"pvalue\"],\n", - " complete=True\n", - ")\n", - "\n", - "print(f\"Found {len(fhl1_binding)} FHL1 binding measurements in glucose\")\n", - "\n", - "# Find significant targets (p < 0.001)\n", - "significant = fhl1_binding[fhl1_binding['pvalue'] < 0.001]\n", - "print(f\"Significant targets: {len(significant)}\")\n", - "\n", - "# Top 10 by effect size\n", - "top_targets = significant.nlargest(10, 'effect')[['target_symbol', 'effect', 'pvalue']]\n", - "print(\"\\nTop 10 targets by effect size:\")\n", - "print(top_targets.to_string(index=False))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 8. Best Practices\n", - "\n", - "### 1. Use Field Selection\n", - "\n", - "Only request the fields you need - it's more efficient:\n", - "\n", - "```python\n", - "# Good: Only get what you need\n", - "df = vdb.query(\n", - " filters={\"carbon_source\": \"glucose\"},\n", - " fields=[\"sample_id\", \"temperature_celsius\"]\n", - ")\n", - "\n", - "# Less efficient: Get all fields\n", - "df = vdb.query(filters={\"carbon_source\": \"glucose\"})\n", - "```\n", - "\n", - "### 2. Filter Before Complete Data\n", - "\n", - "Use filters to reduce the dataset before getting complete data:\n", - "\n", - "```python\n", - "# Good: Filter first\n", - "df = vdb.query(\n", - " filters={\"carbon_source\": \"glucose\", \"temperature_celsius\": 30},\n", - " complete=True\n", - ")\n", - "\n", - "# Less efficient: Get all data then filter in pandas\n", - "df = vdb.query(complete=True)\n", - "df = df[(df['carbon_source'] == 'glucose') & (df['temperature_celsius'] == 30)]\n", - "```\n", - "\n", - "### 3. Use Normalized Values\n", - "\n", - "Always query using the normalized (canonical) values from your config:\n", - "\n", - "```python\n", - "# Good: Use canonical value\n", - "df = vdb.query(filters={\"carbon_source\": \"glucose\"})\n", - "\n", - "# Might miss data: Using original value\n", - "df = vdb.query(filters={\"carbon_source\": \"D-glucose\"}) # Only matches exact string\n", - "```\n", - "\n", - "### 4. Check Available Values First\n", - "\n", - "Use `get_unique_values()` to discover what values exist:\n", - "\n", - "```python\n", - "# See what carbon sources are available\n", - "sources = vdb.get_unique_values(\"carbon_source\")\n", - "print(sources)\n", - "\n", - "# Then query for specific ones\n", - "df = vdb.query(filters={\"carbon_source\": \"galactose\"})\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Summary\n", - "\n", - "VirtualDB provides a powerful abstraction for querying heterogeneous genomic datasets:\n", - "\n", - "1. **Schema Discovery**: Explore available fields and values with `get_fields()` and `get_unique_values()`\n", - "2. **Unified Queries**: Query across datasets using standardized field names\n", - "3. **Automatic Normalization**: Varying terminologies automatically mapped to common values\n", - "4. **Flexible Filtering**: Support for exact matches, ranges, and complex conditions\n", - "5. **Sample or Complete Data**: Choose between metadata-only or full measurements\n", - "\n", - "For more details on the configuration format and design principles, see the [Virtual Database Design](../virtual_database_concepts.md) documentation." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "tfbpapi-py3.11", - "language": "python", - "name": "python3" }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.9" - } - }, - "nbformat": 4, - "nbformat_minor": 4 + "nbformat": 4, + "nbformat_minor": 4 } diff --git a/docs/virtual_database_concepts.md b/docs/virtual_database_concepts.md index 093502a..55fe6c9 100644 --- a/docs/virtual_database_concepts.md +++ b/docs/virtual_database_concepts.md @@ -11,13 +11,16 @@ queries with standardized field names and values. ## Configuration Structure -A configuration file defines the virtual database schema with four sections: +This is a basic example of a VirtualDB configuration YAML file: ```yaml -# ===== Repository Configurations ===== repositories: # Each repository defines a "table" in the virtual database BrentLab/harbison_2004: + # REQUIRED: Specify which field is the sample identifier. At this level, it means + # that all datasets have a field `sample_id` that uniquely identifies samples. + sample_id: + field: sample_id # Repository-wide properties (apply to all datasets in this repository) nitrogen_source: path: media.nitrogen_source.name @@ -33,19 +36,42 @@ repositories: carbon_source: field: condition path: media.carbon_source.compound + dtype: string # Optional: specify data type # Field without path (column alias with normalization) environmental_condition: field: condition + # if there is a `comparative_analysis` dataset that you want to link to + # a given dataset, you can declare it at the dataset level + # For more information on this section, see the section + # 'Comparative Datasets in VirtualDB' + comparative_analyses: + # specify the comparative analysis repo + - repo: BrentLab/yeast_comparative_analysis + # and dataset + dataset: dto + # and the field in the comparative analysis that links back tot this + # dataset. Note that this field should have role `source_sample`, and it + # should therefore be formated as `repo_id;config_name;sample_id` where the + # sample_id is derived from the field in this dataset that is specified + # for this dataset in the `sample_id` field above. + via_field: perturbation_id + BrentLab/kemmeren_2014: dataset: kemmeren_2014: + # REQUIRED: If `sample_id` isn't defined at the repo level, then it must be + # defined at the dataset level for each dataset in the repo + sample_id: + field: sample_id # Same logical fields, different physical paths carbon_source: path: media.carbon_source.compound + dtype: string temperature_celsius: path: temperature_celsius + dtype: numeric # Enables numeric filtering with comparison operators # ===== Normalization Rules ===== # Map varying terminologies to standardized values @@ -96,18 +122,93 @@ Paths use dot notation to navigate nested structures: - `field: condition, path: media.carbon_source.compound` to looks in field `condition`'s definitions to navigates to `media.carbon_source.compound` +### Data Type Specifications + +Field mappings support an optional `dtype` parameter to ensure proper type handling +during metadata extraction and query filtering. + +**Supported dtypes**: +- `string` - Text data (default if not specified) +- `numeric` - Numeric values (integers or floating-point numbers) +- `bool` - Boolean values (true/false) + +**When to use dtype**: + +1. **Numeric filtering**: Required for fields used with comparison operators + (`<`, `>`, `<=`, `>=`, `between`) +2. **Type consistency**: When source data might be extracted with incorrect type +3. **Performance**: Helps with query optimization and prevents type mismatches + +**Type conversion process**: + +Type conversion happens during metadata extraction: +1. Extract value from source using path +2. Convert to specified dtype if provided +3. Store in metadata DataFrame with correct type + +**Example - The problem**: +```python +# Without dtype: temperature extracted as string "30" +# Comparison fails or produces incorrect results +df = vdb.query(filters={"temperature_celsius": (">", 25)}) +# String comparison: "30" > 25 evaluates incorrectly +``` + +**Example - The solution**: +```yaml +temperature_celsius: + path: temperature_celsius + dtype: numeric # Ensures numeric type for proper comparison +``` + +```python +# With dtype: temperature extracted as numeric 30.0 +# Comparison works correctly +df = vdb.query(filters={"temperature_celsius": (">", 25)}) +# Numeric comparison: 30.0 > 25 is True (correct!) +``` + +**Usage examples**: +```yaml +repositories: + BrentLab/example: + dataset: + example_dataset: + # String field for categorical data + strain_background: + path: strain_background + dtype: string + + # Numeric field for quantitative filtering + temperature_celsius: + path: temperature_celsius + dtype: numeric + + # Numeric field for concentration measurements + drug_concentration_um: + path: drug_treatment.concentration_um + dtype: numeric + + # Boolean field + is_heat_shock: + path: is_heat_shock + dtype: bool +``` + ## VirtualDB Structure VirtualDB maintains a collection of dataset-specific metadata tables, one per configured dataset. Each table has the same structure (standardized schema) but -contains data specific to that dataset. +contains data specific to that dataset. -### Internal Structure +Unless directed, these tables are not stored on desk and instead generated via +query against the source parquet files. Think of them as a typical database view. -When materialized (or conceptually if not materialized), VirtualDB contains: +### Internal Structure ```python { + # Primary datasets with sample_id ("BrentLab/harbison_2004", "harbison_2004"): DataFrame( # Columns: sample_id, carbon_source, temperature_celsius, nitrogen_source, ... # Values: Normalized according to factor_aliases @@ -124,14 +225,32 @@ When materialized (or conceptually if not materialized), VirtualDB contains: # sample_id carbon_source temperature_celsius # kemmeren_001 glucose 30 # kemmeren_002 raffinose 30 + ), + + # Comparative datasets with parsed composite identifiers + ("BrentLab/yeast_comparative_analysis", "dto"): DataFrame( + # Original composite ID columns preserved + # Columns: binding_id, perturbation_id, dto_fdr, dto_empirical_pvalue, ... + # Example rows: + # binding_id perturbation_id dto_fdr + # BrentLab/harbison_2004;harbison_2004;harbison_001 BrentLab/kemmeren_2014;kemmeren_2014;sample_42 0.001 + # BrentLab/harbison_2004;harbison_2004;harbison_002 BrentLab/kemmeren_2014;kemmeren_2014;sample_43 0.045 + # + # When materialized with foreign keys, additional parsed columns are created: + # Columns: binding_id, binding_repo_id, binding_config_name, binding_sample_id, + # perturbation_id, perturbation_repo_id, perturbation_config_name, perturbation_sample_id, + # dto_fdr, dto_empirical_pvalue, ... + # Example rows: + # binding_repo_id binding_config_name binding_sample_id dto_fdr + # BrentLab/harbison_2004 harbison_2004 harbison_001 0.001 + # BrentLab/harbison_2004 harbison_2004 harbison_002 0.045 ) } ``` ### View Materialization -By default, VirtualDB computes views on-demand. For performance, views can be -materialized (cached): +Tables can be cached for faster subsequent queries via materialization: ```python # Cache all views for faster subsequent queries @@ -145,7 +264,7 @@ vdb.invalidate_cache() vdb.invalidate_cache([("BrentLab/harbison_2004", "harbison_2004")]) ``` -Materialized views are stored locally and reused for queries until invalidated. +Materialized views are stored locally and reused for queries. ## VirtualDB Interface @@ -239,7 +358,7 @@ df = vdb.query( ### Factor Alias Expansion When querying with aliased values, VirtualDB automatically expands to all -original values: +original values specified in the configuration: ```python # User queries for normalized value @@ -249,8 +368,6 @@ df = vdb.query(filters={"carbon_source": "galactose"}) # WHERE carbon_source IN ('D-galactose', 'gal', 'galactose') ``` -This ensures all samples are retrieved regardless of original terminology. - ### Numeric Field Filtering Numeric fields support exact matching and range queries: @@ -261,35 +378,148 @@ df = vdb.query(filters={"temperature_celsius": 30}) # Range queries df = vdb.query(filters={"temperature_celsius": (">=", 28)}) +# inclusive of the boundaries, ie [28, 32] df = vdb.query(filters={"temperature_celsius": ("between", 28, 32)}) -# Missing value labels (from missing_value_labels config) +# Missing value labels. This analogous to how factor_aliases work. In this case, it +# will return where the temprature_celsius is missing/None/Null/NaN/etc and/or the +# value matches the specified label, in this case "room". If the missing value label +# is a character value and the field is a numeric field, then only missing values will +# be matched. df = vdb.query(filters={"temperature_celsius": "room"}) # Matches samples where temperature is None/missing ``` -## Design Principles +## Comparative Datasets in VirtualDB -### Virtual Views by Default +Comparative datasets differ from other dataset types in that they represent +relationships between samples across datasets rather than individual samples. +Each row relates 2+ samples from other datasets. -Views are computed on-demand unless explicitly materialized, but optionally cached, -This provides: +### Structure -- Reduces setup time when a new instance instantiates MetadataBuilder/VirtualDB -- No storage overhead for casual queries +Comparative datasets use `source_sample` fields instead of a single `sample_id`: +- Multiple fields with `role: source_sample` +- Each contains composite identifier: `"repo_id;config_name;sample_id"` +- Example: `binding_id = "BrentLab/harbison_2004;harbison_2004;42"` -### External Configuration as Schema +### Querying Comparative Data -The YAML configuration serves as a **database schema definition**: +Comparative datasets can be queried in two ways: **direct queries** for analysis +results, and **field-based queries** to enrich primary dataset queries with +comparative metrics. -- Defines what fields exist (logical schema) -- Maps to physical data structures (via paths) -- Specifies normalization rules (via aliases) -- Documents field semantics (via descriptions) +#### Direct Queries -This separation enables: +Query the comparative dataset directly to find analysis results: -- Schema updates without code changes +```python +# Find significant DTO results across all experiments +dto_results = vdb.query( + datasets=[("BrentLab/yeast_comparative_analysis", "dto")], + filters={"dto_fdr": ("<", 0.05)}, + complete=True +) +# Returns: binding_id, perturbation_id, dto_fdr, dto_empirical_pvalue, +# binding_rank_threshold, perturbation_rank_threshold, ... + +# Filter by source dataset +dto_for_harbison = vdb.query( + datasets=[("BrentLab/yeast_comparative_analysis", "dto")], + filters={"binding_id": ("contains", "harbison_2004")}, + complete=True +) + +# Combine filters on both metrics and source samples +high_quality_dto = vdb.query( + datasets=[("BrentLab/yeast_comparative_analysis", "dto")], + filters={ + "dto_fdr": ("<", 0.01), + "binding_id": ("contains", "callingcards") + }, + complete=True +) +``` + +#### Field-based Queries + +```python +# Query binding data, automatically include DTO metrics +binding_with_dto = vdb.query( + datasets=[("BrentLab/callingcards", "annotated_features")], + filters={"regulator_locus_tag": "YJR060W"}, + fields=["sample_id", "target_locus_tag", "binding_score", "dto_fdr"], + complete=True +) +# Returns binding data WITH dto_fdr joined automatically via composite ID + +# Query perturbation data, include derived significance field +perturbation_with_significance = vdb.query( + datasets=[("BrentLab/hackett_2020", "hackett_2020")], + filters={"regulator_locus_tag": "YJR060W"}, + fields=["sample_id", "target_locus_tag", "log2fc", "is_significant"], + complete=True +) +# Returns perturbation data WITH is_significant (computed from dto_fdr < 0.05) +``` + +### Configuration + +Comparative datasets work differently - +**primary datasets declare which comparative datasets reference them**: + +```yaml +repositories: + # Primary dataset (e.g., binding data) + BrentLab/callingcards: + dataset: + annotated_features: + # REQUIRED: Specify which field is the sample identifier + sample_id: + field: sample_id + + # OPTIONAL: Declare comparative analyses that include this dataset + comparative_analyses: + - repo: BrentLab/yeast_comparative_analysis + dataset: dto + via_field: binding_id + # VirtualDB knows composite format: "BrentLab/callingcards;annotated_features;" + + # Regular fields + regulator_locus_tag: + field: regulator_locus_tag + # ... other fields + + # Another primary dataset (e.g., perturbation data) + BrentLab/hu_2007_reimand_2010: + dataset: + data: + sample_id: + field: sample_id + + comparative_analyses: + - repo: BrentLab/yeast_comparative_analysis + dataset: dto + via_field: perturbation_id + + # Regular fields + # ... other fields + + # Comparative dataset - OPTIONAL field mappings for renaming/aliasing + BrentLab/yeast_comparative_analysis: + dataset: + dto: + # Optional: Rename fields for clarity or add derived columns + fdr: + field: dto_fdr # Rename dto_fdr to fdr + + empirical_pvalue: + field: dto_empirical_pvalue # Rename for consistency + + is_significant: + # Derived field: computed from dto_fdr + expression: "dto_fdr < 0.05" +``` -See: +## See Also - [DataCard Documentation](huggingface_datacard.md) diff --git a/docs/virtual_db.md b/docs/virtual_db.md index bec3ab4..8fe590e 100644 --- a/docs/virtual_db.md +++ b/docs/virtual_db.md @@ -14,3 +14,8 @@ ::: tfbpapi.virtual_db.normalize_value options: show_root_heading: true + +## Usage + +For comprehensive usage documentation including comparative datasets, see +[Virtual Database Concepts](virtual_database_concepts.md). diff --git a/pyproject.toml b/pyproject.toml index b8024b0..e3710f2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,18 +9,8 @@ readme = "README.md" [tool.poetry.dependencies] python = "^3.11" requests = "^2.32.3" -aiohttp = "^3.11.18" -cachetools = "^5.5.2" -scikit-learn = "^1.6.1" -requests-toolbelt = "^1.0.0" -responses = "^0.25.7" -aioresponses = "^0.7.8" -numpy = "^2.2.5" -dotenv = "^0.9.9" pandas = "^2.3.1" huggingface-hub = "^0.34.4" -types-requests = "^2.32.4.20250809" -datasets = "^4.0.0" duckdb = "^1.3.2" pydantic = "^2.11.9" diff --git a/tfbpapi/datacard.py b/tfbpapi/datacard.py index bc4cf72..b8798fc 100644 --- a/tfbpapi/datacard.py +++ b/tfbpapi/datacard.py @@ -22,11 +22,14 @@ from pydantic import ValidationError from tfbpapi.errors import DataCardError, DataCardValidationError, HfDataFetchError -from tfbpapi.fetchers import HfDataCardFetcher, HfRepoStructureFetcher, HfSizeInfoFetcher +from tfbpapi.fetchers import ( + HfDataCardFetcher, + HfRepoStructureFetcher, + HfSizeInfoFetcher, +) from tfbpapi.models import ( DatasetCard, DatasetConfig, - DatasetType, ExtractedMetadata, FeatureInfo, MetadataRelationship, @@ -37,14 +40,9 @@ class DataCard: """ Parser and explorer for HuggingFace dataset metadata. - DataCard parses HuggingFace dataset cards into flexible Python objects, - enabling users to drill down into the YAML structure to understand dataset - organization, experimental conditions, and metadata relationships. - The parsed structure uses Pydantic models with `extra="allow"` to accept arbitrary fields (like experimental_conditions) without requiring code - changes. This makes the system flexible enough to handle domain-specific - metadata variations. + changes. Key capabilities: - Parse dataset card YAML into structured objects @@ -53,7 +51,7 @@ class DataCard: - Extract metadata schema for table design - Discover metadata relationships - Example (new API): + Example: >>> card = DataCard("BrentLab/harbison_2004") >>> # Use context manager for config exploration >>> with card.config("harbison_2004") as cfg: @@ -174,48 +172,6 @@ def get_features(self, config_name: str) -> list[FeatureInfo]: return config.dataset_info.features - def get_features_by_role( - self, config_name: str, role: str | None = None - ) -> dict[str, list[str]]: - """ - Get features grouped by role. - - If role is specified, returns only features with that role. - If role is None, returns all features grouped by role. - - :param config_name: Configuration name - :param role: Optional specific role to filter by - :return: Dict mapping role -> list of field names - :raises DataCardError: If config not found - - Example: - >>> # Get all features by role - >>> by_role = card.get_features_by_role("config_name") - >>> # {'regulator_identifier': ['regulator_locus_tag'], - >>> # 'target_identifier': ['target_locus_tag'], ...} - >>> - >>> # Get only experimental condition features - >>> cond_fields = card.get_features_by_role("config_name", - ... "experimental_condition") - >>> # {'experimental_condition': ['condition', 'treatment']} - - """ - features = self.get_features(config_name) - - # Group by role - by_role: dict[str, list[str]] = {} - for feature in features: - feature_role = feature.role if feature.role else "no_role" - if feature_role not in by_role: - by_role[feature_role] = [] - by_role[feature_role].append(feature.name) - - # Filter by specific role if requested - if role is not None: - return {role: by_role.get(role, [])} - - return by_role - def _extract_partition_values( self, config: DatasetConfig, field_name: str ) -> set[str]: @@ -321,7 +277,8 @@ def extract_metadata_schema(self, config_name: str) -> dict[str, Any]: - **Field roles**: Which fields are regulators, targets, conditions, etc. - **Top-level conditions**: Repo-wide conditions (constant for all samples) - - **Config-level conditions**: Config-specific conditions (constant for this config) + - **Config-level conditions**: Config-specific conditions + (constant for this config) - **Field-level definitions**: Per-sample condition definitions The returned schema provides all the information needed to: @@ -410,7 +367,8 @@ def get_experimental_conditions( All conditions are returned as flexible dicts that preserve the original YAML structure. Navigate nested dicts to access specific values. - :param config_name: Optional config name. If provided, merges top and config levels + :param config_name: Optional config name. If provided, merges top + and config levels :return: Dict of experimental conditions (empty dict if none defined) Example: @@ -471,7 +429,8 @@ def get_field_definitions( :param config_name: Configuration name :param field_name: Field name (typically has role=experimental_condition) - :return: Dict mapping field values to their definition dicts (empty if no definitions) + :return: Dict mapping field values to their definition dicts + (empty if no definitions) :raises DataCardError: If config or field not found Example: diff --git a/tfbpapi/hf_cache_manager.py b/tfbpapi/hf_cache_manager.py index 0a4378c..26ca708 100644 --- a/tfbpapi/hf_cache_manager.py +++ b/tfbpapi/hf_cache_manager.py @@ -135,7 +135,9 @@ def _load_metadata_from_cache(self, config, table_name: str) -> bool: return False # Load cached parquet files into DuckDB - self._create_duckdb_table_from_files(cached_files, table_name) + self._create_duckdb_table_from_files( + cached_files, table_name, config.config_name + ) self.logger.info( f"Loaded {len(cached_files)} cached files into {table_name}" ) @@ -190,7 +192,9 @@ def _download_and_load_metadata(self, config, table_name: str) -> bool: return False # Load downloaded files into DuckDB - self._create_duckdb_table_from_files(downloaded_files, table_name) + self._create_duckdb_table_from_files( + downloaded_files, table_name, config.config_name + ) self.logger.info( f"Downloaded and loaded {len(downloaded_files)} files into {table_name}" ) @@ -203,7 +207,7 @@ def _download_and_load_metadata(self, config, table_name: str) -> bool: return False def _create_duckdb_table_from_files( - self, file_paths: list[str], table_name: str + self, file_paths: list[str], table_name: str, config_name: str ) -> None: """Create DuckDB table/view from parquet files.""" if len(file_paths) == 1: @@ -225,6 +229,50 @@ def _create_duckdb_table_from_files( f"Created DuckDB view {table_name} from {len(file_paths)} files" ) + # Validate source_sample fields if they exist + self._validate_source_sample_fields(table_name, config_name) + + def _validate_source_sample_fields(self, table_name: str, config_name: str) -> None: + """ + Validate source_sample fields have correct format. + + Composite sample identifiers must be in the format: + "repo_id;config_name;sample_id" (exactly 3 semicolon-separated parts) + + """ + config = self.get_config(config_name) + + # Find all source_sample fields + source_sample_fields = [ + f.name + for f in config.dataset_info.features # type: ignore + if f.role == "source_sample" + ] + + if not source_sample_fields: + return # No validation needed + + # For each field, validate format + for field_name in source_sample_fields: + query = f""" + SELECT {field_name}, + LENGTH({field_name}) - LENGTH(REPLACE({field_name}, ';', '')) + AS semicolon_count + FROM {table_name} + WHERE semicolon_count != 2 + LIMIT 1 + """ + result = self.duckdb_conn.execute(query).fetchone() + + if result: + raise ValueError( + f"Invalid format in field '{field_name}' " + f"with role='source_sample'. " + f"Expected 'repo_id;config_name;sample_id' " + f"(3 semicolon-separated parts), " + f"but found: '{result[0]}'" + ) + def _extract_embedded_metadata_field( self, data_table_name: str, field_name: str, metadata_table_name: str ) -> bool: @@ -519,9 +567,7 @@ def _format_bytes(self, bytes_size: int) -> str: size /= 1024.0 return f"{size:.1f}TB" - def query( - self, sql: str, config_name: str, refresh_cache: bool = False - ) -> Any: + def query(self, sql: str, config_name: str, refresh_cache: bool = False) -> Any: """ Execute SQL query against a specific dataset configuration. @@ -543,6 +589,7 @@ def query( "SELECT DISTINCT sample_id FROM harbison_2004", "harbison_2004" ) + """ # Validate config exists if config_name not in [c.config_name for c in self.configs]: diff --git a/tfbpapi/models.py b/tfbpapi/models.py index a1489f7..bb86f2e 100644 --- a/tfbpapi/models.py +++ b/tfbpapi/models.py @@ -24,7 +24,7 @@ class DatasetType(str, Enum): ANNOTATED_FEATURES = "annotated_features" GENOME_MAP = "genome_map" METADATA = "metadata" - QC_DATA = "qc_data" + COMPARATIVE = "comparative" class FeatureInfo(BaseModel): @@ -43,7 +43,8 @@ class FeatureInfo(BaseModel): description: str = Field(..., description="Description of the field") role: str | None = Field( default=None, - description="Optional semantic role. 'experimental_condition' has special behavior.", + description="Optional semantic role. 'experimental_condition' " + "has special behavior.", ) definitions: dict[str, Any] | None = Field( default=None, @@ -107,13 +108,13 @@ class DatasetConfig(BaseModel): @field_validator("applies_to") @classmethod def applies_to_only_for_metadata(cls, v, info): - """Validate that applies_to is only used for metadata or qc_data configs.""" + """Validate that applies_to is only used for metadata or comparative configs.""" if v is not None: dataset_type = info.data.get("dataset_type") - if dataset_type not in (DatasetType.METADATA, DatasetType.QC_DATA): + if dataset_type not in (DatasetType.METADATA, DatasetType.COMPARATIVE): raise ValueError( "applies_to field is only valid " - "for metadata and qc_data dataset types" + "for metadata and comparative dataset types" ) return v @@ -231,6 +232,42 @@ class MetadataRelationship(BaseModel): # ============================================================================ +class ComparativeAnalysis(BaseModel): + """ + Reference to a comparative dataset that includes this dataset. + + Comparative datasets relate samples across multiple source datasets. + This model specifies which comparative dataset references the current + dataset and through which field (via_field). + + Attributes: + repo: HuggingFace repository ID of the comparative dataset + dataset: Config name of the comparative dataset + via_field: Field in the comparative dataset containing composite + identifiers that reference this dataset's samples. + Format: "repo_id;config_name;sample_id" + + Example: + ```python + # In BrentLab/callingcards config + ComparativeAnalysis( + repo="BrentLab/yeast_comparative_analysis", + dataset="dto", + via_field="binding_id" + ) + # Means: dto dataset has a binding_id field with values like: + # "BrentLab/callingcards;annotated_features;123" + ``` + + """ + + repo: str = Field(..., description="Comparative dataset repository ID") + dataset: str = Field(..., description="Comparative dataset config name") + via_field: str = Field( + ..., description="Field containing composite sample identifiers" + ) + + class PropertyMapping(BaseModel): """ Mapping specification for a single property. @@ -243,6 +280,12 @@ class PropertyMapping(BaseModel): field: Optional field name for field-level properties. When specified, looks in this field's definitions. When omitted, looks in repo/config-level experimental_conditions. + expression: Optional SQL expression for derived/computed fields. + When specified, creates a computed column. + Cannot be used with field or path. + dtype: Optional data type specification for type conversion. + Supported values: 'string', 'numeric', 'bool'. + When specified, extracted values are converted to this type. Examples: Field-level property with path: @@ -254,10 +297,19 @@ class PropertyMapping(BaseModel): Field-level column alias (no path): PropertyMapping(field="condition") + Derived field with expression: + PropertyMapping(expression="dto_fdr < 0.05") + """ field: str | None = Field(None, description="Field name for field-level properties") path: str | None = Field(None, description="Dot-notation path to property") + expression: str | None = Field( + None, description="SQL expression for derived fields" + ) + dtype: str | None = Field( + None, description="Data type for conversion: 'string', 'numeric', or 'bool'" + ) @field_validator("path") @classmethod @@ -275,21 +327,106 @@ def validate_field(cls, v: str | None) -> str | None: raise ValueError("field cannot be empty or whitespace") return v.strip() if v else None + @field_validator("expression") + @classmethod + def validate_expression(cls, v: str | None) -> str | None: + """Ensure expression is not empty string if provided.""" + if v is not None and not v.strip(): + raise ValueError("expression cannot be empty or whitespace") + return v.strip() if v else None + @model_validator(mode="after") def validate_at_least_one_specified(self) -> "PropertyMapping": - """Ensure at least field or path is specified.""" - if self.field is None and self.path is None: - raise ValueError("At least one of 'field' or 'path' must be specified") + """Ensure at least one field type is specified and mutually exclusive.""" + if self.expression is not None: + if self.field is not None or self.path is not None: + raise ValueError( + "expression cannot be used with field or path - " + "derived fields are computed, not extracted" + ) + elif self.field is None and self.path is None: + raise ValueError( + "At least one of 'field', 'path', or 'expression' must be specified" + ) return self +class DatasetVirtualDBConfig(BaseModel): + """ + VirtualDB configuration for a specific dataset within a repository. + + Attributes: + sample_id: Mapping for the sample identifier field (required for + primary datasets) + comparative_analyses: Optional list of comparative datasets that + reference this dataset + properties: Property mappings for this specific dataset (field names to + PropertyMapping) + + Example: + ```yaml + # In BrentLab/callingcards config + annotated_features: + sample_id: + field: sample_id + comparative_analyses: + - repo: BrentLab/yeast_comparative_analysis + dataset: dto + via_field: binding_id + regulator_locus_tag: + field: regulator_locus_tag + dto_fdr: # Field from comparative dataset, optional renaming + field: dto_fdr + ``` + + """ + + sample_id: PropertyMapping | None = Field( + None, description="Mapping for sample identifier field" + ) + comparative_analyses: list[ComparativeAnalysis] = Field( + default_factory=list, + description="Comparative datasets referencing this dataset", + ) + # Allow additional property mappings via extra fields + model_config = ConfigDict(extra="allow") + + @model_validator(mode="before") + @classmethod + def parse_property_mappings(cls, data: Any) -> Any: + """Parse extra fields as PropertyMapping objects.""" + if not isinstance(data, dict): + return data + + # Process all fields except sample_id and comparative_analyses + result = {} + for key, value in data.items(): + if key in ("sample_id", "comparative_analyses"): + # These are typed fields, let Pydantic handle them + result[key] = value + elif isinstance(value, dict): + # Assume it's a PropertyMapping + try: + result[key] = PropertyMapping.model_validate(value) + except Exception as e: + raise ValueError( + f"Invalid PropertyMapping for field '{key}': {e}" + ) from e + else: + # Already parsed or wrong type + result[key] = value + + return result + + class RepositoryConfig(BaseModel): """ Configuration for a single repository. Eg BrentLab/harbison_2004. Attributes: properties: Repo-wide property mappings that apply to all datasets - dataset: Dataset-specific property mappings (override repo-wide) + dataset: Dataset-specific configurations including sample_id, + comparative_analyses, and property mappings Example: ```python @@ -298,12 +435,21 @@ class RepositoryConfig(BaseModel): "temperature_celsius": PropertyMapping(path="temperature_celsius") }, dataset={ - "dataset_name": { - "carbon_source": PropertyMapping( + "dataset_name": DatasetVirtualDBConfig( + sample_id=PropertyMapping(field="sample_id"), + comparative_analyses=[ + ComparativeAnalysis( + repo="BrentLab/yeast_comparative_analysis", + dataset="dto", + via_field="binding_id" + ) + ], + # Additional property mappings via extra fields + **{"carbon_source": PropertyMapping( field="condition", path="media.carbon_source" - ) - } + )} + ) } ) ``` @@ -313,44 +459,43 @@ class RepositoryConfig(BaseModel): properties: dict[str, PropertyMapping] = Field( default_factory=dict, description="Repo-wide property mappings" ) - dataset: dict[str, dict[str, PropertyMapping]] | None = Field( - None, description="Dataset-specific property mappings" + dataset: dict[str, DatasetVirtualDBConfig] | None = Field( + None, description="Dataset-specific configurations" ) @model_validator(mode="before") @classmethod def parse_structure(cls, data: Any) -> Any: - """Parse raw dict structure into typed PropertyMapping objects.""" + """Parse raw dict structure into typed objects.""" if not isinstance(data, dict): return data # Extract and parse dataset section dataset_section = data.get("dataset") - parsed_datasets: dict[str, dict[str, PropertyMapping]] | None = None + parsed_datasets: dict[str, DatasetVirtualDBConfig] | None = None if dataset_section: if not isinstance(dataset_section, dict): raise ValueError("'dataset' key must contain a dict") parsed_datasets = {} - for dataset_name, properties in dataset_section.items(): - if not isinstance(properties, dict): - raise ValueError( - f"Dataset '{dataset_name}' must contain a dict of properties" + for dataset_name, config_dict in dataset_section.items(): + if not isinstance(config_dict, dict): + raise ValueError(f"Dataset '{dataset_name}' must contain a dict") + + # Parse DatasetVirtualDBConfig + # The config_dict may contain: + # - sample_id (PropertyMapping) + # - comparative_analyses (list[ComparativeAnalysis]) + # - Other fields as PropertyMappings (via extra="allow") + try: + parsed_datasets[dataset_name] = ( + DatasetVirtualDBConfig.model_validate(config_dict) ) - - # Parse each property mapping into PropertyMapping object - parsed_datasets[dataset_name] = {} - for prop_name, mapping in properties.items(): - try: - parsed_datasets[dataset_name][prop_name] = ( - PropertyMapping.model_validate(mapping) - ) - except Exception as e: - raise ValueError( - f"Invalid property '{prop_name}' in dataset " - f"'{dataset_name}': {e}" - ) from e + except Exception as e: + raise ValueError( + f"Invalid configuration for dataset '{dataset_name}': {e}" + ) from e # Parse repo-wide properties (all keys except 'dataset') parsed_properties = {} @@ -375,7 +520,8 @@ class MetadataConfig(BaseModel): Attributes: factor_aliases: Optional mappings of standardized names to actual values. - Example: {"carbon_source": {"glucose": ["D-glucose", "dextrose"]}} + Example: {"carbon_source": + {"glucose": ["D-glucose", "dextrose"]}} missing_value_labels: Labels for missing values by property name description: Human-readable descriptions for each property repositories: Dict mapping repository IDs to their configurations @@ -500,7 +646,8 @@ def parse_repositories(cls, data: Any) -> Any: if not repositories_data: raise ValueError( - "Configuration must have a 'repositories' key with at least one repository" + "Configuration must have a 'repositories' key " + "with at least one repository" ) if not isinstance(repositories_data, dict): @@ -579,6 +726,9 @@ def get_property_mappings( # Override with dataset-specific properties if repo_config.dataset and config_name in repo_config.dataset: - mappings.update(repo_config.dataset[config_name]) + dataset_config = repo_config.dataset[config_name] + # DatasetVirtualDBConfig stores property mappings in model_extra + if hasattr(dataset_config, "model_extra") and dataset_config.model_extra: + mappings.update(dataset_config.model_extra) return mappings diff --git a/tfbpapi/tests/example_datacards.py b/tfbpapi/tests/example_datacards.py index 1d732de..36b023f 100644 --- a/tfbpapi/tests/example_datacards.py +++ b/tfbpapi/tests/example_datacards.py @@ -1,3 +1,4 @@ +# flake8: noqa """ Three diverse datacard examples for testing datacard parsing and database construction. @@ -468,11 +469,11 @@ - config_name: qc_metrics description: Quality control metrics for all genome coverage samples - dataset_type: qc_data + dataset_type: comparative applies_to: ["genome_coverage"] data_files: - split: train - path: qc_data.parquet + path: comparative_data.parquet dataset_info: features: - name: sample_id diff --git a/tfbpapi/tests/test_hf_cache_manager.py b/tfbpapi/tests/test_hf_cache_manager.py index 94fb568..aa395df 100644 --- a/tfbpapi/tests/test_hf_cache_manager.py +++ b/tfbpapi/tests/test_hf_cache_manager.py @@ -96,8 +96,11 @@ def test_create_duckdb_table_from_files_single_file( mock_conn = Mock() test_cache_manager = HfCacheManager("test/repo", mock_conn) + # Mock the validation method since we're testing table creation + test_cache_manager._validate_source_sample_fields = Mock() # type: ignore + test_cache_manager._create_duckdb_table_from_files( - [str(parquet_file)], "test_table" + [str(parquet_file)], "test_table", "test_config" ) mock_conn.execute.assert_called_once() @@ -122,7 +125,12 @@ def test_create_duckdb_table_from_files_multiple_files( mock_conn = Mock() test_cache_manager = HfCacheManager("test/repo", mock_conn) - test_cache_manager._create_duckdb_table_from_files(files, "test_table") + # Mock the validation method since we're testing table creation + test_cache_manager._validate_source_sample_fields = Mock() # type: ignore + + test_cache_manager._create_duckdb_table_from_files( + files, "test_table", "test_config" + ) mock_conn.execute.assert_called_once() sql_call = mock_conn.execute.call_args[0][0] @@ -381,9 +389,12 @@ def test_metadata_workflow_integration(self, tmpdir): mock_conn = Mock() test_cache_manager = HfCacheManager("test/repo", mock_conn) + # Mock the validation method since we're testing table creation + test_cache_manager._validate_source_sample_fields = Mock() # type: ignore + # Test _create_duckdb_table_from_files directly test_cache_manager._create_duckdb_table_from_files( - [str(metadata_file)], "metadata_test_metadata" + [str(metadata_file)], "metadata_test_metadata", "test_metadata" ) # Verify the SQL was generated correctly @@ -516,3 +527,257 @@ def mock_delete_revisions(*revision_hashes): cache_info.delete_revisions = mock_delete_revisions return cache_info + + +class TestSourceSampleValidation: + """Test validation of source_sample field format.""" + + def setup_method(self): + """Set up test fixtures.""" + self.conn = duckdb.connect(":memory:") + self.repo_id = "test/repo" + + def test_valid_source_sample_format(self, tmpdir): + """Test that valid source_sample format passes validation.""" + # Create parquet file with valid composite identifiers + parquet_file = tmpdir.join("valid_data.parquet") + self.conn.execute( + f""" + COPY ( + SELECT + 'BrentLab/harbison_2004;harbison_2004;CBF1_YPD' + as binding_sample_ref, + 'gene_' || (row_number() OVER()) as target_locus_tag, + random() * 100 as binding_score + FROM range(5) + ) TO '{parquet_file}' (FORMAT PARQUET) + """ + ) + + # Create mock datacard with source_sample field + mock_feature = Mock() + mock_feature.name = "binding_sample_ref" + mock_feature.role = "source_sample" + + mock_dataset_info = Mock() + mock_dataset_info.features = [mock_feature] + + mock_config = Mock() + mock_config.config_name = "test_config" + mock_config.dataset_info = mock_dataset_info + + with patch("tfbpapi.hf_cache_manager.DataCard.__init__", return_value=None): + cache_manager = HfCacheManager(self.repo_id, self.conn) + cache_manager.get_config = Mock(return_value=mock_config) # type: ignore + + # Should not raise any error + cache_manager._create_duckdb_table_from_files( + [str(parquet_file)], "test_table", "test_config" + ) + + def test_invalid_source_sample_two_parts(self, tmpdir): + """Test that source_sample with only 2 parts raises ValueError.""" + # Create parquet file with invalid format (only 2 parts) + parquet_file = tmpdir.join("invalid_data.parquet") + self.conn.execute( + f""" + COPY ( + SELECT + 'BrentLab/harbison_2004;CBF1_YPD' as binding_sample_ref, + 'gene_' || (row_number() OVER()) as target_locus_tag, + random() * 100 as binding_score + FROM range(5) + ) TO '{parquet_file}' (FORMAT PARQUET) + """ + ) + + # Create mock datacard with source_sample field + mock_feature = Mock() + mock_feature.name = "binding_sample_ref" + mock_feature.role = "source_sample" + + mock_dataset_info = Mock() + mock_dataset_info.features = [mock_feature] + + mock_config = Mock() + mock_config.config_name = "test_config" + mock_config.dataset_info = mock_dataset_info + + with patch("tfbpapi.hf_cache_manager.DataCard.__init__", return_value=None): + cache_manager = HfCacheManager(self.repo_id, self.conn) + cache_manager.get_config = Mock(return_value=mock_config) # type: ignore + + # Should raise ValueError with clear message + with pytest.raises(ValueError) as exc_info: + cache_manager._create_duckdb_table_from_files( + [str(parquet_file)], "test_table", "test_config" + ) + + error_msg = str(exc_info.value) + assert "Invalid format in field 'binding_sample_ref'" in error_msg + assert "role='source_sample'" in error_msg + assert "3 semicolon-separated parts" in error_msg + assert "BrentLab/harbison_2004;CBF1_YPD" in error_msg + + def test_invalid_source_sample_one_part(self, tmpdir): + """Test that source_sample with only 1 part raises ValueError.""" + # Create parquet file with invalid format (only 1 part) + parquet_file = tmpdir.join("invalid_data.parquet") + self.conn.execute( + f""" + COPY ( + SELECT + 'CBF1_YPD' as binding_sample_ref, + 'gene_' || (row_number() OVER()) as target_locus_tag, + random() * 100 as binding_score + FROM range(5) + ) TO '{parquet_file}' (FORMAT PARQUET) + """ + ) + + # Create mock datacard with source_sample field + mock_feature = Mock() + mock_feature.name = "binding_sample_ref" + mock_feature.role = "source_sample" + + mock_dataset_info = Mock() + mock_dataset_info.features = [mock_feature] + + mock_config = Mock() + mock_config.config_name = "test_config" + mock_config.dataset_info = mock_dataset_info + + with patch("tfbpapi.hf_cache_manager.DataCard.__init__", return_value=None): + cache_manager = HfCacheManager(self.repo_id, self.conn) + cache_manager.get_config = Mock(return_value=mock_config) # type: ignore + + # Should raise ValueError + with pytest.raises(ValueError) as exc_info: + cache_manager._create_duckdb_table_from_files( + [str(parquet_file)], "test_table", "test_config" + ) + + error_msg = str(exc_info.value) + assert "Invalid format in field 'binding_sample_ref'" in error_msg + assert "CBF1_YPD" in error_msg + + def test_invalid_source_sample_four_parts(self, tmpdir): + """Test that source_sample with 4 parts raises ValueError.""" + # Create parquet file with invalid format (4 parts) + parquet_file = tmpdir.join("invalid_data.parquet") + self.conn.execute( + f""" + COPY ( + SELECT + 'a;b;c;d' as binding_sample_ref, + 'gene_' || (row_number() OVER()) as target_locus_tag, + random() * 100 as binding_score + FROM range(5) + ) TO '{parquet_file}' (FORMAT PARQUET) + """ + ) + + # Create mock datacard with source_sample field + mock_feature = Mock() + mock_feature.name = "binding_sample_ref" + mock_feature.role = "source_sample" + + mock_dataset_info = Mock() + mock_dataset_info.features = [mock_feature] + + mock_config = Mock() + mock_config.config_name = "test_config" + mock_config.dataset_info = mock_dataset_info + + with patch("tfbpapi.hf_cache_manager.DataCard.__init__", return_value=None): + cache_manager = HfCacheManager(self.repo_id, self.conn) + cache_manager.get_config = Mock(return_value=mock_config) # type: ignore + + # Should raise ValueError + with pytest.raises(ValueError) as exc_info: + cache_manager._create_duckdb_table_from_files( + [str(parquet_file)], "test_table", "test_config" + ) + + error_msg = str(exc_info.value) + assert "Invalid format in field 'binding_sample_ref'" in error_msg + assert "a;b;c;d" in error_msg + + def test_no_source_sample_fields(self, tmpdir): + """Test that validation is skipped when no source_sample fields exist.""" + # Create parquet file with normal data + parquet_file = tmpdir.join("normal_data.parquet") + self.conn.execute( + f""" + COPY ( + SELECT + 'gene_' || (row_number() OVER()) as target_locus_tag, + random() * 100 as expression_value + FROM range(5) + ) TO '{parquet_file}' (FORMAT PARQUET) + """ + ) + + # Create mock datacard without source_sample fields + mock_feature = Mock() + mock_feature.name = "target_locus_tag" + mock_feature.role = "target_identifier" + + mock_dataset_info = Mock() + mock_dataset_info.features = [mock_feature] + + mock_config = Mock() + mock_config.config_name = "test_config" + mock_config.dataset_info = mock_dataset_info + + with patch("tfbpapi.hf_cache_manager.DataCard.__init__", return_value=None): + cache_manager = HfCacheManager(self.repo_id, self.conn) + cache_manager.get_config = Mock(return_value=mock_config) # type: ignore + + # Should not raise any error + cache_manager._create_duckdb_table_from_files( + [str(parquet_file)], "test_table", "test_config" + ) + + def test_multiple_source_sample_fields(self, tmpdir): + """Test validation with multiple source_sample fields.""" + # Create parquet file with multiple composite identifier fields + parquet_file = tmpdir.join("multi_ref_data.parquet") + self.conn.execute( + f""" + COPY ( + SELECT + 'BrentLab/harbison_2004;harbison_2004;CBF1_YPD' + as binding_sample_ref, + 'BrentLab/kemmeren_2014;kemmeren_2014;sample_42' + as expression_sample_ref, + 'gene_' || (row_number() OVER()) as target_locus_tag + FROM range(5) + ) TO '{parquet_file}' (FORMAT PARQUET) + """ + ) + + # Create mock datacard with multiple source_sample fields + mock_feature1 = Mock() + mock_feature1.name = "binding_sample_ref" + mock_feature1.role = "source_sample" + + mock_feature2 = Mock() + mock_feature2.name = "expression_sample_ref" + mock_feature2.role = "source_sample" + + mock_dataset_info = Mock() + mock_dataset_info.features = [mock_feature1, mock_feature2] + + mock_config = Mock() + mock_config.config_name = "test_config" + mock_config.dataset_info = mock_dataset_info + + with patch("tfbpapi.hf_cache_manager.DataCard.__init__", return_value=None): + cache_manager = HfCacheManager(self.repo_id, self.conn) + cache_manager.get_config = Mock(return_value=mock_config) # type: ignore + + # Both fields are valid - should not raise + cache_manager._create_duckdb_table_from_files( + [str(parquet_file)], "test_table", "test_config" + ) diff --git a/tfbpapi/tests/test_metadata_config_models.py b/tfbpapi/tests/test_metadata_config_models.py index 09d6523..1697930 100644 --- a/tfbpapi/tests/test_metadata_config_models.py +++ b/tfbpapi/tests/test_metadata_config_models.py @@ -5,10 +5,8 @@ """ -from pathlib import Path - import pytest -import yaml +import yaml # type: ignore from pydantic import ValidationError from tfbpapi.models import ( @@ -63,10 +61,97 @@ def test_valid_field_only_mapping(self): assert mapping.path is None def test_invalid_neither_field_nor_path(self): - """Test that at least one of field or path is required.""" + """Test that at least one of field, path, or expression is required.""" with pytest.raises(ValidationError) as exc_info: PropertyMapping() - assert "At least one of 'field' or 'path' must be specified" in str(exc_info.value) + assert ( + "At least one of 'field', 'path', or 'expression' must be specified" + in str(exc_info.value) + ) + + def test_valid_expression_only(self): + """Test valid expression-only mapping (derived field).""" + mapping = PropertyMapping(expression="dto_fdr < 0.05") + assert mapping.expression == "dto_fdr < 0.05" + assert mapping.field is None + assert mapping.path is None + + def test_invalid_expression_with_field(self): + """Test that expression cannot be combined with field.""" + with pytest.raises(ValidationError) as exc_info: + PropertyMapping(expression="dto_fdr < 0.05", field="sample_id") + assert "expression cannot be used with field or path" in str(exc_info.value) + + def test_invalid_expression_with_path(self): + """Test that expression cannot be combined with path.""" + with pytest.raises(ValidationError) as exc_info: + PropertyMapping(expression="dto_fdr < 0.05", path="media.carbon_source") + assert "expression cannot be used with field or path" in str(exc_info.value) + + +class TestComparativeAnalysis: + """Tests for ComparativeAnalysis model.""" + + def test_valid_comparative_analysis(self): + """Test valid comparative analysis configuration.""" + from tfbpapi.models import ComparativeAnalysis + + ca = ComparativeAnalysis( + repo="BrentLab/yeast_comparative_analysis", + dataset="dto", + via_field="binding_id", + ) + assert ca.repo == "BrentLab/yeast_comparative_analysis" + assert ca.dataset == "dto" + assert ca.via_field == "binding_id" + + +class TestDatasetVirtualDBConfig: + """Tests for DatasetVirtualDBConfig model.""" + + def test_valid_config_with_sample_id(self): + """Test valid dataset config with sample_id.""" + from tfbpapi.models import DatasetVirtualDBConfig, PropertyMapping + + config = DatasetVirtualDBConfig(sample_id=PropertyMapping(field="sample_id")) + assert config.sample_id is not None + assert config.sample_id.field == "sample_id" + + def test_valid_config_with_comparative_analyses(self): + """Test valid dataset config with comparative analyses.""" + from tfbpapi.models import DatasetVirtualDBConfig + + config_dict = { + "sample_id": {"field": "sample_id"}, + "comparative_analyses": [ + { + "repo": "BrentLab/yeast_comparative_analysis", + "dataset": "dto", + "via_field": "binding_id", + } + ], + } + config = DatasetVirtualDBConfig.model_validate(config_dict) + assert config.sample_id is not None + assert len(config.comparative_analyses) == 1 + assert ( + config.comparative_analyses[0].repo == "BrentLab/yeast_comparative_analysis" + ) + + def test_config_with_extra_property_mappings(self): + """Test that extra fields are parsed as PropertyMappings.""" + from tfbpapi.models import DatasetVirtualDBConfig + + config_dict = { + "sample_id": {"field": "sample_id"}, + "regulator_locus_tag": {"field": "regulator_locus_tag"}, + "dto_fdr": {"expression": "dto_fdr < 0.05"}, + } + config = DatasetVirtualDBConfig.model_validate(config_dict) + + # Access extra fields via model_extra + assert "regulator_locus_tag" in config.model_extra + assert "dto_fdr" in config.model_extra class TestRepositoryConfig: @@ -110,8 +195,11 @@ def test_valid_field_only_property(self): config = RepositoryConfig.model_validate(config_data) assert config.dataset is not None assert "dataset1" in config.dataset - assert config.dataset["dataset1"]["carbon_source"].field == "condition" - assert config.dataset["dataset1"]["carbon_source"].path is None + # Access extra field via model_extra + dataset_config = config.dataset["dataset1"] + assert "carbon_source" in dataset_config.model_extra + assert dataset_config.model_extra["carbon_source"].field == "condition" + assert dataset_config.model_extra["carbon_source"].path is None def test_valid_repo_wide_field_only_property(self): """Test that repo-wide field-only properties are valid.""" @@ -136,7 +224,9 @@ def test_valid_config_with_aliases(self, tmp_path): }, "repositories": { "BrentLab/test": { - "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + "dataset": { + "test": {"carbon_source": {"path": "media.carbon_source"}} + } } }, } @@ -158,7 +248,9 @@ def test_valid_config_without_aliases(self, tmp_path): config_data = { "repositories": { "BrentLab/test": { - "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + "dataset": { + "test": {"carbon_source": {"path": "media.carbon_source"}} + } } } } @@ -176,7 +268,9 @@ def test_valid_config_empty_aliases(self, tmp_path): "factor_aliases": {}, "repositories": { "BrentLab/test": { - "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + "dataset": { + "test": {"carbon_source": {"path": "media.carbon_source"}} + } } }, } @@ -248,7 +342,9 @@ def test_aliases_allow_numeric_values(self): }, "repositories": { "BrentLab/test": { - "dataset": {"test": {"temperature": {"path": "temperature_celsius"}}} + "dataset": { + "test": {"temperature": {"path": "temperature_celsius"}} + } } }, } @@ -310,7 +406,9 @@ def test_get_property_mappings(self, tmp_path): "BrentLab/kemmeren_2014": { "temperature": {"path": "temperature_celsius"}, # Repo-wide "dataset": { - "kemmeren_2014": {"carbon_source": {"path": "media.carbon_source"}} + "kemmeren_2014": { + "carbon_source": {"path": "media.carbon_source"} + } }, } }, diff --git a/tfbpapi/tests/test_models.py b/tfbpapi/tests/test_models.py index bf52f75..1771c4d 100644 --- a/tfbpapi/tests/test_models.py +++ b/tfbpapi/tests/test_models.py @@ -30,7 +30,7 @@ def test_dataset_type_values(self): assert DatasetType.ANNOTATED_FEATURES == "annotated_features" assert DatasetType.GENOME_MAP == "genome_map" assert DatasetType.METADATA == "metadata" - assert DatasetType.QC_DATA == "qc_data" + assert DatasetType.COMPARATIVE == "comparative" def test_dataset_type_from_string(self): """Test creating DatasetType from string.""" @@ -157,7 +157,7 @@ def test_dataset_info_with_partitioning(self): partitioning=PartitioningInfo(enabled=True, partition_by=["chr"]), ) assert len(dataset_info.features) == 2 - assert dataset_info.partitioning.enabled is True + assert dataset_info.partitioning.enabled is True # type: ignore class TestDatasetConfig: diff --git a/tfbpapi/tests/test_virtual_db.py b/tfbpapi/tests/test_virtual_db.py index 3728e3e..1293bf9 100644 --- a/tfbpapi/tests/test_virtual_db.py +++ b/tfbpapi/tests/test_virtual_db.py @@ -5,11 +5,12 @@ """ -from pathlib import Path import tempfile -import pytest +from pathlib import Path + import pandas as pd -import yaml +import pytest +import yaml # type: ignore from tfbpapi.virtual_db import VirtualDB, get_nested_value, normalize_value @@ -33,10 +34,7 @@ def test_get_nested_value_list_extraction(self): """Test extracting property from list of dicts.""" data = { "media": { - "carbon_source": [ - {"compound": "glucose"}, - {"compound": "galactose"} - ] + "carbon_source": [{"compound": "glucose"}, {"compound": "galactose"}] } } result = get_nested_value(data, "media.carbon_source.compound") @@ -44,7 +42,7 @@ def test_get_nested_value_list_extraction(self): def test_get_nested_value_non_dict(self): """Test that non-dict input returns None.""" - result = get_nested_value("not a dict", "path") + result = get_nested_value("not a dict", "path") # type: ignore assert result is None def test_normalize_value_exact_match(self): @@ -90,15 +88,11 @@ def create_test_config(self, **overrides): "factor_aliases": { "carbon_source": { "glucose": ["D-glucose", "dextrose"], - "galactose": ["D-galactose", "Galactose"] + "galactose": ["D-galactose", "Galactose"], } }, - "missing_value_labels": { - "carbon_source": "unspecified" - }, - "description": { - "carbon_source": "Carbon source in growth media" - }, + "missing_value_labels": {"carbon_source": "unspecified"}, + "description": {"carbon_source": "Carbon source in growth media"}, "repositories": { "BrentLab/test_repo": { "temperature_celsius": {"path": "temperature_celsius"}, @@ -106,19 +100,19 @@ def create_test_config(self, **overrides): "test_dataset": { "carbon_source": { "field": "condition", - "path": "media.carbon_source.compound" + "path": "media.carbon_source.compound", } } - } + }, } - } + }, } config.update(overrides) return config def test_init_with_valid_config(self): """Test VirtualDB initialization with valid config.""" - with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: yaml.dump(self.create_test_config(), f) config_path = f.name @@ -132,7 +126,7 @@ def test_init_with_valid_config(self): def test_init_with_token(self): """Test VirtualDB initialization with HF token.""" - with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: yaml.dump(self.create_test_config(), f) config_path = f.name @@ -149,7 +143,7 @@ def test_init_missing_config_file(self): def test_repr(self): """Test string representation.""" - with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: yaml.dump(self.create_test_config(), f) config_path = f.name @@ -176,25 +170,28 @@ def create_multi_dataset_config(self): "temperature_celsius": {"path": "temperature_celsius"}, "dataset": { "dataset1": { - "carbon_source": {"field": "condition", "path": "media.carbon_source"} + "carbon_source": { + "field": "condition", + "path": "media.carbon_source", + } } - } + }, }, "BrentLab/repo2": { "nitrogen_source": {"path": "media.nitrogen_source"}, "dataset": { "dataset2": { "carbon_source": {"path": "media.carbon_source"}, - "temperature_celsius": {"path": "temperature_celsius"} + "temperature_celsius": {"path": "temperature_celsius"}, } - } - } - } + }, + }, + }, } def test_get_fields_all_datasets(self): """Test getting all fields across all datasets.""" - with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: yaml.dump(self.create_multi_dataset_config(), f) config_path = f.name @@ -210,7 +207,7 @@ def test_get_fields_all_datasets(self): def test_get_fields_specific_dataset(self): """Test getting fields for specific dataset.""" - with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: yaml.dump(self.create_multi_dataset_config(), f) config_path = f.name @@ -226,7 +223,7 @@ def test_get_fields_specific_dataset(self): def test_get_fields_invalid_partial_args(self): """Test error when only one of repo_id/config_name provided.""" - with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: yaml.dump(self.create_multi_dataset_config(), f) config_path = f.name @@ -239,7 +236,7 @@ def test_get_fields_invalid_partial_args(self): def test_get_common_fields(self): """Test getting fields common to all datasets.""" - with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: yaml.dump(self.create_multi_dataset_config(), f) config_path = f.name @@ -254,14 +251,6 @@ def test_get_common_fields(self): finally: Path(config_path).unlink() - def test_get_common_fields_empty_config(self): - """Test getting common fields with no repositories.""" - config = {"factor_aliases": {}} - with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: - # This will fail validation (needs at least one repo) - # So we skip this test - pass - class TestCaching: """Tests for view materialization and caching.""" @@ -278,12 +267,12 @@ def create_simple_config(self): } } } - } + }, } def test_invalidate_cache_all(self): """Test invalidating all cache.""" - with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: yaml.dump(self.create_simple_config(), f) config_path = f.name @@ -300,7 +289,7 @@ def test_invalidate_cache_all(self): def test_invalidate_cache_specific(self): """Test invalidating specific dataset cache.""" - with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: yaml.dump(self.create_simple_config(), f) config_path = f.name @@ -323,13 +312,15 @@ class TestFiltering: def test_apply_filters_exact_match(self): """Test exact value matching in filters.""" - df = pd.DataFrame({ - "sample_id": ["s1", "s2", "s3"], - "carbon_source": ["glucose", "galactose", "glucose"] - }) + df = pd.DataFrame( + { + "sample_id": ["s1", "s2", "s3"], + "carbon_source": ["glucose", "galactose", "glucose"], + } + ) # Create minimal VirtualDB instance - with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: config = { "repositories": { "BrentLab/test": { @@ -354,17 +345,18 @@ def test_apply_filters_exact_match(self): def test_apply_filters_numeric_range(self): """Test numeric range filtering.""" - df = pd.DataFrame({ - "sample_id": ["s1", "s2", "s3"], - "temperature_celsius": [25, 30, 37] - }) + df = pd.DataFrame( + {"sample_id": ["s1", "s2", "s3"], "temperature_celsius": [25, 30, 37]} + ) - with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: config = { "repositories": { "BrentLab/test": { "dataset": { - "test": {"temperature_celsius": {"path": "temperature_celsius"}} + "test": { + "temperature_celsius": {"path": "temperature_celsius"} + } } } } @@ -384,7 +376,10 @@ def test_apply_filters_numeric_range(self): # Test between operator filtered = vdb._apply_filters( - df, {"temperature_celsius": ("between", 28, 32)}, "BrentLab/test", "test" + df, + {"temperature_celsius": ("between", 28, 32)}, + "BrentLab/test", + "test", ) assert len(filtered) == 1 assert filtered.iloc[0]["temperature_celsius"] == 30 @@ -393,17 +388,17 @@ def test_apply_filters_numeric_range(self): def test_apply_filters_with_alias_expansion(self): """Test filter with alias expansion.""" - df = pd.DataFrame({ - "sample_id": ["s1", "s2", "s3"], - "carbon_source": ["glucose", "D-glucose", "galactose"] - }) + df = pd.DataFrame( + { + "sample_id": ["s1", "s2", "s3"], + "carbon_source": ["glucose", "D-glucose", "galactose"], + } + ) - with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: config = { "factor_aliases": { - "carbon_source": { - "glucose": ["D-glucose", "dextrose", "glucose"] - } + "carbon_source": {"glucose": ["D-glucose", "dextrose", "glucose"]} }, "repositories": { "BrentLab/test": { @@ -411,7 +406,7 @@ def test_apply_filters_with_alias_expansion(self): "test": {"carbon_source": {"path": "media.carbon_source"}} } } - } + }, } yaml.dump(config, f) config_path = f.name @@ -432,23 +427,14 @@ class TestExtraction: def test_add_field_metadata(self): """Test adding field-level metadata to DataFrame.""" - df = pd.DataFrame({ - "sample_id": ["s1", "s2"], - "condition": ["YPD", "YPG"] - }) + df = pd.DataFrame({"sample_id": ["s1", "s2"], "condition": ["YPD", "YPG"]}) field_metadata = { - "YPD": { - "carbon_source": ["glucose"], - "growth_media": ["YPD"] - }, - "YPG": { - "carbon_source": ["glycerol"], - "growth_media": ["YPG"] - } + "YPD": {"carbon_source": ["glucose"], "growth_media": ["YPD"]}, + "YPG": {"carbon_source": ["glycerol"], "growth_media": ["YPG"]}, } - with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: config = { "repositories": { "BrentLab/test": { @@ -467,8 +453,14 @@ def test_add_field_metadata(self): assert "carbon_source" in result.columns assert "growth_media" in result.columns - assert result.loc[result["condition"] == "YPD", "carbon_source"].iloc[0] == "glucose" - assert result.loc[result["condition"] == "YPG", "carbon_source"].iloc[0] == "glycerol" + assert ( + result.loc[result["condition"] == "YPD", "carbon_source"].iloc[0] + == "glucose" + ) + assert ( + result.loc[result["condition"] == "YPG", "carbon_source"].iloc[0] + == "glycerol" + ) finally: Path(config_path).unlink() @@ -478,7 +470,7 @@ class TestQuery: def test_query_empty_result(self): """Test query with no matching datasets.""" - with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: config = { "repositories": { "BrentLab/test": { @@ -501,6 +493,202 @@ def test_query_empty_result(self): Path(config_path).unlink() +class TestComparativeDatasets: + """Tests for comparative dataset field-based joins.""" + + def test_parse_composite_identifier(self): + """Test parsing composite identifiers.""" + composite_id = "BrentLab/harbison_2004;harbison_2004;sample_42" + repo, config, sample = VirtualDB._parse_composite_identifier(composite_id) + assert repo == "BrentLab/harbison_2004" + assert config == "harbison_2004" + assert sample == "sample_42" + + def test_parse_composite_identifier_invalid(self): + """Test that invalid composite IDs raise errors.""" + with pytest.raises(ValueError, match="Invalid composite ID format"): + VirtualDB._parse_composite_identifier("invalid:format") + + def test_get_comparative_fields_for_dataset(self): + """Test getting comparative fields mapping.""" + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: + config = { + "repositories": { + "BrentLab/primary": { + "dataset": { + "primary_data": { + "sample_id": {"field": "sample_id"}, + "comparative_analyses": [ + { + "repo": "BrentLab/comparative", + "dataset": "comp_data", + "via_field": "binding_id", + } + ], + } + } + }, + "BrentLab/comparative": { + "dataset": { + "comp_data": { + "dto_fdr": {"field": "dto_fdr"}, + "dto_pvalue": {"field": "dto_empirical_pvalue"}, + } + } + }, + } + } + yaml.dump(config, f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + field_mapping = vdb._get_comparative_fields_for_dataset( + "BrentLab/primary", "primary_data" + ) + + # Should have dto_fdr and dto_pvalue, but NOT binding_id (via_field) + assert "dto_fdr" in field_mapping + assert "dto_pvalue" in field_mapping + assert "binding_id" not in field_mapping + + # Check mapping structure + assert field_mapping["dto_fdr"]["comp_repo"] == "BrentLab/comparative" + assert field_mapping["dto_fdr"]["comp_dataset"] == "comp_data" + assert field_mapping["dto_fdr"]["via_field"] == "binding_id" + finally: + Path(config_path).unlink() + + def test_get_comparative_fields_no_links(self): + """Test that datasets without comparative links return empty mapping.""" + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: + config = { + "repositories": { + "BrentLab/primary": { + "dataset": { + "primary_data": {"sample_id": {"field": "sample_id"}} + } + } + } + } + yaml.dump(config, f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + field_mapping = vdb._get_comparative_fields_for_dataset( + "BrentLab/primary", "primary_data" + ) + assert field_mapping == {} + finally: + Path(config_path).unlink() + + def test_get_comparative_analyses(self): + """Test getting comparative analysis relationships.""" + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: + config = { + "repositories": { + "BrentLab/primary": { + "dataset": { + "primary_data": { + "sample_id": {"field": "sample_id"}, + "comparative_analyses": [ + { + "repo": "BrentLab/comparative", + "dataset": "comp_data", + "via_field": "binding_id", + } + ], + } + } + }, + "BrentLab/comparative": { + "dataset": {"comp_data": {"dto_fdr": {"field": "dto_fdr"}}} + }, + } + } + yaml.dump(config, f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + info = vdb.get_comparative_analyses() + + # Check primary to comparative mapping + assert "BrentLab/primary/primary_data" in info["primary_to_comparative"] + links = info["primary_to_comparative"]["BrentLab/primary/primary_data"] + assert len(links) == 1 + assert links[0]["comparative_repo"] == "BrentLab/comparative" + assert links[0]["comparative_dataset"] == "comp_data" + assert links[0]["via_field"] == "binding_id" + + # Check comparative fields + assert "BrentLab/comparative/comp_data" in info["comparative_fields"] + assert ( + "dto_fdr" + in info["comparative_fields"]["BrentLab/comparative/comp_data"] + ) + finally: + Path(config_path).unlink() + + def test_get_comparative_analyses_filtered(self): + """Test filtering comparative analyses by repo and config.""" + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: + config = { + "repositories": { + "BrentLab/primary1": { + "dataset": { + "data1": { + "sample_id": {"field": "sample_id"}, + "comparative_analyses": [ + { + "repo": "BrentLab/comp", + "dataset": "comp_data", + "via_field": "id1", + } + ], + } + } + }, + "BrentLab/primary2": { + "dataset": { + "data2": { + "sample_id": {"field": "sample_id"}, + "comparative_analyses": [ + { + "repo": "BrentLab/comp", + "dataset": "comp_data", + "via_field": "id2", + } + ], + } + } + }, + } + } + yaml.dump(config, f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + + # Get all + all_info = vdb.get_comparative_analyses() + assert len(all_info["primary_to_comparative"]) == 2 + + # Filter by repo and config + filtered = vdb.get_comparative_analyses("BrentLab/primary1", "data1") + assert len(filtered["primary_to_comparative"]) == 1 + assert "BrentLab/primary1/data1" in filtered["primary_to_comparative"] + + # Filter by repo only + repo_filtered = vdb.get_comparative_analyses("BrentLab/primary2") + assert len(repo_filtered["primary_to_comparative"]) == 1 + assert "BrentLab/primary2/data2" in repo_filtered["primary_to_comparative"] + finally: + Path(config_path).unlink() + + # Note: Full integration tests with real HuggingFace datasets would go here # but are excluded as they require network access and specific test datasets. # These tests cover the core logic and would be supplemented with integration diff --git a/tfbpapi/virtual_db.py b/tfbpapi/virtual_db.py index 245bf18..f6dd12e 100644 --- a/tfbpapi/virtual_db.py +++ b/tfbpapi/virtual_db.py @@ -64,7 +64,8 @@ def get_nested_value(data: dict, path: str) -> Any: List of dicts - extract property from each item: get_nested_value( - {"media": {"carbon_source": [{"compound": "glucose"}, {"compound": "galactose"}]}}, + {"media": {"carbon_source": [{"compound": "glucose"}, + {"compound": "galactose"}]}}, "media.carbon_source.compound" ) Returns: ["glucose", "galactose"] @@ -82,7 +83,8 @@ def get_nested_value(data: dict, path: str) -> Any: return None current = current[key] elif isinstance(current, list): - # If current is a list and we have more keys, extract property from each item + # If current is a list and we have more keys, + # extract property from each item if i < len(keys): # Extract the remaining path from each list item remaining_path = ".".join(keys[i:]) @@ -191,6 +193,8 @@ def __init__(self, config_path: Path | str, token: str | None = None): self.config = MetadataConfig.from_yaml(config_path) self.token = token self.cache: dict[tuple[str, str], pd.DataFrame] = {} + # Build mapping of comparative dataset references + self._comparative_links = self._build_comparative_links() def get_fields( self, repo_id: str | None = None, config_name: str | None = None @@ -216,7 +220,9 @@ def get_fields( return sorted(mappings.keys()) if repo_id is not None or config_name is not None: - raise ValueError("Both repo_id and config_name must be provided, or neither") + raise ValueError( + "Both repo_id and config_name must be provided, or neither" + ) # Get all fields across all datasets all_fields: set[str] = set() @@ -225,8 +231,16 @@ def get_fields( all_fields.update(repo_config.properties.keys()) # Add dataset-specific fields if repo_config.dataset: - for dataset_props in repo_config.dataset.values(): - all_fields.update(dataset_props.keys()) + for dataset_config in repo_config.dataset.values(): + # DatasetVirtualDBConfig stores property mappings in model_extra + if ( + hasattr(dataset_config, "model_extra") + and dataset_config.model_extra + ): + all_fields.update(dataset_config.model_extra.keys()) + # Also include special fields if they exist + if dataset_config.sample_id: + all_fields.add("sample_id") return sorted(all_fields) @@ -312,6 +326,101 @@ def get_unique_values( else: return sorted(all_values) + def get_comparative_analyses( + self, repo_id: str | None = None, config_name: str | None = None + ) -> dict[str, Any]: + """ + Get information about comparative analysis relationships. + + Returns information about which comparative datasets are available + and how they link to primary datasets. Useful for discovering + what cross-dataset analyses can be performed. + + :param repo_id: Optional repository ID to filter to specific repo + :param config_name: Optional config name (requires repo_id) + :return: Dictionary with two keys: + - "primary_to_comparative": Maps primary datasets to their + comparative analyses + - "comparative_fields": Maps comparative datasets to fields + available for joining + :raises ValueError: If config_name provided without repo_id + + Examples: + Get all comparative analysis relationships: + info = vdb.get_comparative_analyses() + + Get relationships for specific primary dataset: + info = vdb.get_comparative_analyses( + "BrentLab/callingcards", "annotated_features" + ) + + """ + if config_name and not repo_id: + raise ValueError("repo_id required when config_name is specified") + + primary_to_comparative: dict[str, list[dict[str, str]]] = {} + comparative_fields: dict[str, list[str]] = {} + + # Filter links based on parameters + if repo_id and config_name: + # Specific dataset requested + links_to_process = { + (repo_id, config_name): self._comparative_links.get( + (repo_id, config_name), {} + ) + } + elif repo_id: + # All configs in specific repo + links_to_process = { + k: v for k, v in self._comparative_links.items() if k[0] == repo_id + } + else: + # All links + links_to_process = self._comparative_links + + # Build primary to comparative mapping + for (prim_repo, prim_config), link_info in links_to_process.items(): + if "comparative_analyses" not in link_info: + continue + + dataset_key = f"{prim_repo}/{prim_config}" + primary_to_comparative[dataset_key] = [] + + for ca in link_info["comparative_analyses"]: + primary_to_comparative[dataset_key].append( + { + "comparative_repo": ca["repo"], + "comparative_dataset": ca["dataset"], + "via_field": ca["via_field"], + } + ) + + # Track which fields are available from comparative datasets + comp_key = f"{ca['repo']}/{ca['dataset']}" + if comp_key not in comparative_fields: + # Get fields from the comparative dataset + # First try config mappings + comp_fields = self.get_fields(ca["repo"], ca["dataset"]) + + # If no mappings, get actual fields from DataCard + if not comp_fields: + try: + card = DataCard(ca["repo"], token=self.token) + config = card.get_config(ca["dataset"]) + if config and config.dataset_info: + comp_fields = [ + f.name for f in config.dataset_info.features + ] + except Exception: + comp_fields = [] + + comparative_fields[comp_key] = comp_fields + + return { + "primary_to_comparative": primary_to_comparative, + "comparative_fields": comparative_fields, + } + def query( self, filters: dict[str, Any] | None = None, @@ -366,11 +475,59 @@ def query( if metadata_df.empty: continue - # Apply filters + # Separate filters into primary and comparative + primary_filters = {} + comparative_filters = {} if filters: - metadata_df = self._apply_filters(metadata_df, filters, repo_id, config_name) + # Get comparative field mapping + comp_field_mapping = self._get_comparative_fields_for_dataset( + repo_id, config_name + ) + for field, value in filters.items(): + if field in comp_field_mapping: + comparative_filters[field] = value + else: + primary_filters[field] = value + + # Apply primary filters first + if primary_filters: + metadata_df = self._apply_filters( + metadata_df, primary_filters, repo_id, config_name + ) + + # Enrich with comparative data if needed + # IMPORTANT: Do this BEFORE getting complete data so comparative fields + # are joined at the sample level, not measurement level + # This happens when: fields are requested from comparative datasets + # OR when filtering on comparative fields + if fields or comparative_filters: + comp_field_mapping = self._get_comparative_fields_for_dataset( + repo_id, config_name + ) + if fields: + requested_comp_fields = [ + f for f in fields if f in comp_field_mapping + ] + # Also need fields that are filtered on + filtered_comp_fields = [ + f for f in comparative_filters.keys() if f in comp_field_mapping + ] + all_comp_fields = list( + set(requested_comp_fields + filtered_comp_fields) + ) + if all_comp_fields: + metadata_df = self._enrich_with_comparative_data( + metadata_df, repo_id, config_name, all_comp_fields + ) + + # Apply comparative filters after enrichment + if comparative_filters: + metadata_df = self._apply_filters( + metadata_df, comparative_filters, repo_id, config_name + ) # If complete=True, join with full data + # Do this AFTER comparative enrichment so DTO fields are already added if complete: sample_ids = metadata_df["sample_id"].tolist() if sample_ids: @@ -390,10 +547,11 @@ def query( for field in fields: if field in metadata_df.columns and field not in keep_cols: keep_cols.append(field) - metadata_df = metadata_df[keep_cols] + metadata_df = metadata_df[keep_cols].copy() # Add dataset identifier if "dataset_id" not in metadata_df.columns: + metadata_df = metadata_df.copy() metadata_df["dataset_id"] = f"{repo_id}/{config_name}" results.append(metadata_df) @@ -404,9 +562,7 @@ def query( # Concatenate results, filling NaN for missing columns return pd.concat(results, ignore_index=True, sort=False) - def materialize_views( - self, datasets: list[tuple[str, str]] | None = None - ) -> None: + def materialize_views(self, datasets: list[tuple[str, str]] | None = None) -> None: """ Build and cache metadata DataFrames for faster subsequent queries. @@ -430,9 +586,7 @@ def materialize_views( # Build and cache self._build_metadata_table(repo_id, config_name, use_cache=False) - def invalidate_cache( - self, datasets: list[tuple[str, str]] | None = None - ) -> None: + def invalidate_cache(self, datasets: list[tuple[str, str]] | None = None) -> None: """ Clear cached metadata DataFrames. @@ -451,14 +605,304 @@ def invalidate_cache( if dataset_key in self.cache: del self.cache[dataset_key] + def _build_comparative_links(self) -> dict[tuple[str, str], dict[str, Any]]: + """ + Build mapping of primary datasets to their comparative dataset references. + + Returns dict keyed by (repo_id, config_name) with value being dict: { + "comparative_analyses": [ { "repo": comparative_repo_id, + "dataset": comparative_config_name, "via_field": + field_name_with_composite_ids } ] } + + """ + links: dict[tuple[str, str], dict[str, Any]] = {} + + for repo_id, repo_config in self.config.repositories.items(): + if not repo_config.dataset: + continue + + for config_name, dataset_config in repo_config.dataset.items(): + if dataset_config.comparative_analyses: + links[(repo_id, config_name)] = { + "comparative_analyses": [ + { + "repo": ca.repo, + "dataset": ca.dataset, + "via_field": ca.via_field, + } + for ca in dataset_config.comparative_analyses + ] + } + + return links + + def _get_comparative_fields_for_dataset( + self, repo_id: str, config_name: str + ) -> dict[str, dict[str, str]]: + """ + Get mapping of comparative fields available for a primary dataset. + + :param repo_id: Primary dataset repository ID + :param config_name: Primary dataset config name + :return: Dict mapping field_name to comparative dataset info + {field_name: { + "comp_repo": comparative_repo_id, + "comp_dataset": comparative_dataset_name, + "via_field": field_with_composite_ids + }} + + Example: + For callingcards dataset linked to DTO via binding_id: + { + "dto_fdr": { + "comp_repo": "BrentLab/yeast_comparative_analysis", + "comp_dataset": "dto", + "via_field": "binding_id" + }, + "dto_empirical_pvalue": {...} + } + + """ + field_mapping: dict[str, dict[str, str]] = {} + + # Get comparative analyses for this dataset + links = self._comparative_links.get((repo_id, config_name), {}) + if "comparative_analyses" not in links: + return field_mapping + + # For each comparative dataset, get its fields + for ca in links["comparative_analyses"]: + comp_repo = ca["repo"] + comp_dataset = ca["dataset"] + via_field = ca["via_field"] + + # Get fields from comparative dataset + comp_fields = self.get_fields(comp_repo, comp_dataset) + + # If no fields from config, try DataCard + if not comp_fields: + try: + from tfbpapi.datacard import DataCard + + card = DataCard(comp_repo, token=self.token) + config = card.get_config(comp_dataset) + if config and config.dataset_info: + comp_fields = [f.name for f in config.dataset_info.features] + except Exception: + comp_fields = [] + + # Map each field to this comparative dataset + for field_name in comp_fields: + # Skip the via_field itself (it's the join key) + if field_name == via_field: + continue + + field_mapping[field_name] = { + "comp_repo": comp_repo, + "comp_dataset": comp_dataset, + "via_field": via_field, + } + + return field_mapping + + def _enrich_with_comparative_data( + self, + primary_df: pd.DataFrame, + repo_id: str, + config_name: str, + requested_fields: list[str], + ) -> pd.DataFrame: + """ + Enrich primary dataset with fields from comparative datasets. + + :param primary_df: Primary dataset DataFrame with sample_id column + :param repo_id: Primary dataset repository ID + :param config_name: Primary dataset config name + :param requested_fields: List of field names requested by user + :return: DataFrame enriched with comparative fields + + """ + # Get mapping of which fields come from which comparative datasets + comp_field_mapping = self._get_comparative_fields_for_dataset( + repo_id, config_name + ) + + if not comp_field_mapping: + return primary_df + + # Find which requested fields are from comparative datasets + comp_fields_to_fetch = [f for f in requested_fields if f in comp_field_mapping] + + if not comp_fields_to_fetch: + return primary_df + + # Group fields by comparative dataset to minimize queries + by_comp_dataset: dict[tuple[str, str, str], list[str]] = {} + for field in comp_fields_to_fetch: + info = comp_field_mapping[field] + key = (info["comp_repo"], info["comp_dataset"], info["via_field"]) + if key not in by_comp_dataset: + by_comp_dataset[key] = [] + by_comp_dataset[key].append(field) + + # For each comparative dataset, load and join + result_df = primary_df.copy() + + for (comp_repo, comp_dataset, via_field), fields in by_comp_dataset.items(): + try: + # Load comparative dataset using HfCacheManager + # but query the raw data table instead of metadata view + from tfbpapi.hf_cache_manager import HfCacheManager + + comp_cache_mgr = HfCacheManager( + comp_repo, duckdb_conn=duckdb.connect(":memory:"), token=self.token + ) + + # Get the config to load data + comp_config = comp_cache_mgr.get_config(comp_dataset) + if not comp_config: + continue + + # Load the data (this will download and register parquet files) + result = comp_cache_mgr._get_metadata_for_config(comp_config) + if not result.get("success", False): + continue + + # Now query the raw data table directly (not the metadata view) + # The raw table name is config_name without "metadata_" prefix + select_fields = [via_field] + fields + columns = ", ".join(select_fields) + + # Query the actual parquet data by creating a view from the files + try: + # Get file paths that were loaded + import glob + + from huggingface_hub import snapshot_download + + cache_dir = snapshot_download( + repo_id=comp_repo, + repo_type="dataset", + allow_patterns=f"{comp_dataset}/**/*.parquet", + token=self.token, + ) + + parquet_files = glob.glob( + f"{cache_dir}/{comp_dataset}/**/*.parquet", recursive=True + ) + + if not parquet_files: + continue + + # Create a temporary view from parquet files + temp_view = f"temp_{comp_dataset}_raw" + files_sql = ", ".join([f"'{f}'" for f in parquet_files]) + comp_cache_mgr.duckdb_conn.execute( + f"CREATE OR REPLACE VIEW {temp_view} AS " + f"SELECT * FROM read_parquet([{files_sql}])" + ) + + # Query the view + sql = f"SELECT {columns} FROM {temp_view}" + comp_df = comp_cache_mgr.duckdb_conn.execute(sql).fetchdf() + + except Exception: + # If direct parquet loading fails, skip this comparative dataset + continue + + if comp_df.empty: + continue + + # Parse composite identifiers to extract sample_id + # via_field contains values like + # "BrentLab/harbison_2004;harbison_2004;123" + # We need to extract the third component and match on + # current repo/config + def extract_sample_id(composite_id: str) -> str | None: + """Extract sample_id if composite matches current dataset.""" + if pd.isna(composite_id): + return None + try: + parts = composite_id.split(";") + if len(parts) != 3: + return None + # Check if this composite ID references our dataset + if parts[0] == repo_id and parts[1] == config_name: + return parts[2] + return None + except Exception: + return None + + comp_df["_join_sample_id"] = comp_df[via_field].apply(extract_sample_id) + + # Convert _join_sample_id to match primary_df sample_id dtype + # This handles cases where sample_id is int but composite has string + if "_join_sample_id" in comp_df.columns: + primary_dtype = primary_df["sample_id"].dtype + if pd.api.types.is_integer_dtype(primary_dtype): + # Convert to numeric, coercing errors to NaN + comp_df["_join_sample_id"] = pd.to_numeric( + comp_df["_join_sample_id"], errors="coerce" + ) + elif pd.api.types.is_string_dtype(primary_dtype): + comp_df["_join_sample_id"] = comp_df["_join_sample_id"].astype( + str + ) + + # Filter to only rows that match our dataset + comp_df = comp_df[comp_df["_join_sample_id"].notna()].copy() + + if comp_df.empty: + continue + + # Drop the via_field column (we don't need it in results) + comp_df = comp_df.drop(columns=[via_field]) + + # Merge with primary data + result_df = result_df.merge( + comp_df, left_on="sample_id", right_on="_join_sample_id", how="left" + ) + + # Drop the temporary join column + result_df = result_df.drop(columns=["_join_sample_id"]) + + except Exception: + # If enrichment fails for this comparative dataset, continue + continue + + return result_df + + @staticmethod + def _parse_composite_identifier(composite_id: str) -> tuple[str, str, str]: + """ + Parse composite sample identifier into components. + + :param composite_id: Composite ID in format "repo_id;config_name;sample_id" + :return: Tuple of (repo_id, config_name, sample_id) + + Example: + _parse_composite_identifier( + "BrentLab/harbison_2004;harbison_2004;sample_42" + ) + Returns: ("BrentLab/harbison_2004", "harbison_2004", "sample_42") + + """ + parts = composite_id.split(";") + if len(parts) != 3: + raise ValueError( + f"Invalid composite ID format: {composite_id}. " + "Expected 'repo_id;config_name;sample_id'" + ) + return parts[0], parts[1], parts[2] + def _build_metadata_table( self, repo_id: str, config_name: str, use_cache: bool = True ) -> pd.DataFrame: """ Build metadata table for a single dataset. - Extracts sample-level metadata from experimental conditions hierarchy - and field definitions, with normalization and missing value handling. + Extracts sample-level metadata from experimental conditions hierarchy and field + definitions, with normalization and missing value handling. :param repo_id: Repository ID :param config_name: Configuration name @@ -485,17 +929,31 @@ def _build_metadata_table( return pd.DataFrame() # Extract repo/config-level metadata - repo_metadata = self._extract_repo_level(card, config_name, property_mappings) + repo_metadata = self._extract_repo_level( + card, config_name, property_mappings + ) # Extract field-level metadata - field_metadata = self._extract_field_level(card, config_name, property_mappings) + field_metadata = self._extract_field_level( + card, config_name, property_mappings + ) # Get sample-level data from HuggingFace config = card.get_config(config_name) + + # Check if this is a comparative dataset + from tfbpapi.models import DatasetType + + is_comparative = ( + config + and hasattr(config, "dataset_type") + and config.dataset_type == DatasetType.COMPARATIVE + ) + if config and hasattr(config, "metadata_fields") and config.metadata_fields: # Select only metadata fields columns = ", ".join(config.metadata_fields) - if "sample_id" not in config.metadata_fields: + if not is_comparative and "sample_id" not in config.metadata_fields: columns = f"sample_id, {columns}" sql = f"SELECT DISTINCT {columns} FROM {config_name}" else: @@ -504,8 +962,9 @@ def _build_metadata_table( df = cache_mgr.query(sql, config_name) - # One row per sample_id - if "sample_id" in df.columns: + # For non-comparative datasets: one row per sample_id + # For comparative datasets: keep all rows (each row is a relationship) + if not is_comparative and "sample_id" in df.columns: df = df.groupby("sample_id").first().reset_index() # Add repo-level metadata as columns @@ -517,6 +976,9 @@ def _build_metadata_table( if field_metadata: df = self._add_field_metadata(df, field_metadata) + # Apply dtype conversions to DataFrame columns + df = self._apply_column_dtypes(df, property_mappings) + # Cache result if use_cache: self.cache[cache_key] = df @@ -524,9 +986,72 @@ def _build_metadata_table( return df except Exception as e: + # Log error for debugging with full traceback + import traceback + + print(f"Error downloading metadata for {config_name}: {e}") + traceback.print_exc() # Return empty DataFrame on error return pd.DataFrame() + def _apply_column_dtypes( + self, df: pd.DataFrame, property_mappings: dict[str, PropertyMapping] + ) -> pd.DataFrame: + """ + Apply dtype conversions to DataFrame columns based on property mappings. + + :param df: DataFrame to apply conversions to + :param property_mappings: Property mappings with dtype specifications + :return: DataFrame with converted column dtypes + + """ + for prop_name, mapping in property_mappings.items(): + # Skip if no dtype specified or column doesn't exist + if not mapping.dtype or prop_name not in df.columns: + continue + + # Convert column dtype + try: + if mapping.dtype == "numeric": + df[prop_name] = pd.to_numeric(df[prop_name], errors="coerce") + elif mapping.dtype == "bool": + df[prop_name] = df[prop_name].astype(bool) + elif mapping.dtype == "string": + df[prop_name] = df[prop_name].astype(str) + except (ValueError, TypeError): + # Conversion failed, leave as is + pass + + return df + + def _convert_dtype(self, value: Any, dtype: str) -> Any: + """ + Convert value to specified data type. + + :param value: The value to convert to a given `dtype` + :param dtype: Target data type ("numeric", "bool", "string") + + :return: Converted value or None if conversion fails + + """ + if value is None: + return None + + try: + if dtype == "numeric": + # Try float first (handles both int and float) + return float(value) + elif dtype == "bool": + return bool(value) + elif dtype == "string": + return str(value) + else: + # Unknown dtype, pass through unchanged + return value + except (ValueError, TypeError): + # Conversion failed, return None + return None + def _extract_repo_level( self, card: DataCard, @@ -560,10 +1085,12 @@ def _extract_repo_level( continue # Build full path - full_path = f"experimental_conditions.{mapping.path}" + # Note: `conditions` is already the experimental_conditions dict, + # so we don't add the prefix + full_path = mapping.path # Get value at path - value = get_nested_value(conditions, full_path) + value = get_nested_value(conditions, full_path) # type: ignore # Handle missing values missing_label = self.config.missing_value_labels.get(prop_name) @@ -575,6 +1102,12 @@ def _extract_repo_level( # Ensure value is a list actual_values = [value] if not isinstance(value, list) else value + # Apply dtype conversion if specified + if mapping.dtype: + actual_values = [ + self._convert_dtype(v, mapping.dtype) for v in actual_values + ] + # Normalize using aliases aliases = self.config.factor_aliases.get(prop_name) normalized_values = [ @@ -603,16 +1136,18 @@ def _extract_field_level( field_metadata: dict[str, dict[str, Any]] = {} # Group property mappings by field - field_mappings: dict[str, dict[str, str]] = {} + field_mappings: dict[str, dict[str, PropertyMapping]] = {} for prop_name, mapping in property_mappings.items(): - if mapping.field is not None: + # Only process if field is specified AND path exists + # (no path means it's just a column alias, not metadata extraction) + if mapping.field is not None and mapping.path is not None: field_name = mapping.field if field_name not in field_mappings: field_mappings[field_name] = {} - field_mappings[field_name][prop_name] = mapping.path + field_mappings[field_name][prop_name] = mapping # Process each field that has mappings - for field_name, prop_paths in field_mappings.items(): + for field_name, prop_mappings_dict in field_mappings.items(): # Get field definitions definitions = card.get_field_definitions(config_name, field_name) if not definitions: @@ -623,9 +1158,9 @@ def _extract_field_level( if field_value not in field_metadata: field_metadata[field_value] = {} - for prop_name, path in prop_paths.items(): + for prop_name, mapping in prop_mappings_dict.items(): # Get value at path - value = get_nested_value(definition, path) + value = get_nested_value(definition, mapping.path) # type: ignore # Handle missing values missing_label = self.config.missing_value_labels.get(prop_name) @@ -637,10 +1172,17 @@ def _extract_field_level( # Ensure value is a list actual_values = [value] if not isinstance(value, list) else value + # Apply dtype conversion if specified + if mapping.dtype: + actual_values = [ + self._convert_dtype(v, mapping.dtype) for v in actual_values + ] + # Normalize using aliases aliases = self.config.factor_aliases.get(prop_name) normalized_values = [ - normalize_value(v, aliases, missing_label) for v in actual_values + normalize_value(v, aliases, missing_label) + for v in actual_values ] field_metadata[field_value][prop_name] = normalized_values @@ -702,7 +1244,9 @@ def _apply_filters( if isinstance(filter_value, tuple): operator = filter_value[0] if operator == "between" and len(filter_value) == 3: - df = df[(df[field] >= filter_value[1]) & (df[field] <= filter_value[2])] + df = df[ + (df[field] >= filter_value[1]) & (df[field] <= filter_value[2]) + ] elif operator in (">=", ">", "<=", "<", "==", "!="): if operator == ">=": df = df[df[field] >= filter_value[1]] @@ -770,9 +1314,14 @@ def _get_complete_data( # Merge with metadata (metadata_df has normalized fields) # Drop metadata columns from full_df to avoid duplicates metadata_cols = [ - col for col in metadata_df.columns if col not in ["sample_id", "dataset_id"] + col + for col in metadata_df.columns + if col not in ["sample_id", "dataset_id"] ] - full_df = full_df.drop(columns=[c for c in metadata_cols if c in full_df.columns], errors="ignore") + full_df = full_df.drop( + columns=[c for c in metadata_cols if c in full_df.columns], + errors="ignore", + ) # Merge on sample_id result = full_df.merge(metadata_df, on="sample_id", how="left") From e71f8041366debd976994f7d23f484d0c9b11b34 Mon Sep 17 00:00:00 2001 From: chasem Date: Mon, 8 Sep 2025 12:05:54 -0500 Subject: [PATCH 26/49] resolving requests missing types --- .gitignore | 10 +- .pre-commit-config.yaml | 1 + pyproject.toml | 3 + tfbpapi/AbstractAPI.py | 2 +- tfbpapi/AbstractHfAPI.py | 410 +++++++++++++++++++++++++++ tfbpapi/BindingManualQCAPI.py | 2 +- tfbpapi/DtoAPI.py | 2 +- tfbpapi/ExpressionManualQCAPI.py | 2 +- tfbpapi/HfCacheManager.py | 271 ++++++++++++++++++ tfbpapi/RankResponseAPI.py | 2 +- tfbpapi/UnivariateModelsAPI.py | 2 +- tfbpapi/tests/conftest.py | 26 ++ tfbpapi/tests/conftests.py | 0 tfbpapi/tests/data/cache_info.pkl | Bin 0 -> 123975 bytes tfbpapi/tests/test_HfCacheManager.py | 55 ++++ tfbpapi/tests/test_ParamsDict.py | 2 +- 16 files changed, 781 insertions(+), 9 deletions(-) create mode 100644 tfbpapi/AbstractHfAPI.py create mode 100644 tfbpapi/HfCacheManager.py create mode 100644 tfbpapi/tests/conftest.py delete mode 100644 tfbpapi/tests/conftests.py create mode 100644 tfbpapi/tests/data/cache_info.pkl create mode 100644 tfbpapi/tests/test_HfCacheManager.py diff --git a/.gitignore b/.gitignore index 3eeb70f..cf9aeb9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,14 @@ -#mac files +# claude +CLAUDE.md +.claude/ +claude_output/ +claude_logs/ + +# mac files **/.DS_Store # Dataset directory -data/ +./data/ # logs **/logs/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0961d95..e8284d5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -62,6 +62,7 @@ repos: rev: v1.15.0 hooks: - id: mypy + additional_dependencies: [types-requests] - repo: https://github.com/markdownlint/markdownlint rev: v0.12.0 diff --git a/pyproject.toml b/pyproject.toml index 27e077a..56c6f30 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,12 +18,15 @@ aioresponses = "^0.7.8" numpy = "^2.2.5" dotenv = "^0.9.9" pandas = "^2.3.1" +huggingface-hub = "^0.34.4" +types-requests = "^2.32.4.20250809" [tool.poetry.group.dev.dependencies] pytest = "^8.3.5" pytest-snapshot = "^0.9.0" pytest-asyncio = "^0.26.0" +types-requests = "^2.32.4.20250809" [tool.pytest.ini_options] diff --git a/tfbpapi/AbstractAPI.py b/tfbpapi/AbstractAPI.py index 19c4eb6..39bd10a 100644 --- a/tfbpapi/AbstractAPI.py +++ b/tfbpapi/AbstractAPI.py @@ -5,7 +5,7 @@ from typing import Any import pandas as pd -import requests # type: ignore +import requests from tfbpapi.Cache import Cache from tfbpapi.ParamsDict import ParamsDict diff --git a/tfbpapi/AbstractHfAPI.py b/tfbpapi/AbstractHfAPI.py new file mode 100644 index 0000000..14db82f --- /dev/null +++ b/tfbpapi/AbstractHfAPI.py @@ -0,0 +1,410 @@ +import logging +import os +from abc import ABC, abstractmethod +from collections.abc import Mapping +from pathlib import Path +from typing import Any, Literal + +import requests +from huggingface_hub import hf_hub_download, repo_info, snapshot_download +from huggingface_hub.constants import HF_HUB_CACHE +from requests import HTTPError + + +class RepoTooLargeError(ValueError): + """Raised when repository exceeds auto-download threshold.""" + + pass + + +class AbstractHfAPI(ABC): + """Abstract base class for creating Hugging Face API clients.""" + + # TODO: can revision be set to "latest" by default? + def __init__( + self, + repo_id: str, + repo_type: Literal["model", "dataset", "space"] = "dataset", + token: str | None = None, + cache_dir: str | Path = HF_HUB_CACHE, + ): + """ + Initialize the HF-backed API client. + + :param repo_id: The repo identifier on HF (e.g., "user/dataset"). Eg, + "BrentLab/yeast_genome_resources" + :param token: Optional. Not necessary for public repos. May be set via the + HF_TOKEN environment variable. + :param repo_type: One of {"model", "dataset", "space"}. Defaults to "dataset". + :param cache_dir: HF cache_dir for hf_hub_download and snapshot_download (see + huggingface_hub docs). May be passed via the HF_CACHE_DIR environmental + variable. If not set, the default HF cache directory is used. + + """ + self.logger = logging.getLogger(self.__class__.__name__) + + # Let user input override env var, but use the env var if available + resolved_token = token or os.getenv("HF_TOKEN", None) + resolved_cache_dir = cache_dir or os.getenv("HF_CACHE_DIR", HF_HUB_CACHE) + if isinstance(resolved_cache_dir, str): + resolved_cache_dir = Path(resolved_cache_dir) + + self.token = resolved_token + self.repo_id = repo_id + self.repo_type = repo_type + self.cache_dir = resolved_cache_dir + + @property + def token(self) -> str | None: + return self._token + + @token.setter + def token(self, value: str | None) -> None: + # TODO: if a token is provided, then validate that it works. Only necessary + # if token is not None of course + self._token = value + + @property + def repo_id(self) -> str: + return self._repo_id + + @repo_id.setter + def repo_id(self, value: str) -> None: + self._repo_id = value + try: + self._get_dataset_size(value) + except (HTTPError, ValueError) as e: + self.logger.warning(f"Could not validate repo_id {value}: {e}") + self.logger.info( + "Repo validation skipped - will be checked on first download" + ) + + @property + def cache_dir(self) -> Path: + return self._cache_dir + + @cache_dir.setter + def cache_dir(self, value: str | Path) -> None: + """Set the cache directory for huggingface_hub downloads.""" + path = Path(value) + if not path.exists(): + raise FileNotFoundError(f"Cache directory does not exist: {path}") + self._cache_dir = path + + @property + def size(self) -> dict[str, Any] | None: + """Size information from the HF Dataset Server API (if available).""" + return self._size if hasattr(self, "_size") else None + + @size.setter + def size(self, value: dict[str, Any]) -> None: + self._size = value + + @property + def snapshot_path(self) -> Path | None: + """Path to the last downloaded snapshot (if any).""" + return self._snapshot_path if hasattr(self, "_snapshot_path") else None + + @snapshot_path.setter + def snapshot_path(self, value: str | Path | None) -> None: + if value is None: + self._snapshot_path = None + else: + self._snapshot_path = Path(value) + + def _get_dataset_size(self, repo_id: str | None = None) -> None: + """ + Get dataset size information from HuggingFace Dataset Server API. + + :returns: Dict containing size information with additional metadata about + completeness + :raises requests.HTTPError: If the API request fails + :raises ValueError: If the dataset doesn't exist or isn't accessible + + """ + repo_id = repo_id or self.repo_id + url = f"https://datasets-server.huggingface.co/size?dataset={repo_id}" + + headers = {} + if self.token: + headers["Authorization"] = f"Bearer {self.token}" + + response = requests.get(url, headers=headers) + response.raise_for_status() + + data = response.json() + + # Check if size determination was partial + is_partial = data.get("partial", False) + + if is_partial: + self.logger.warning( + f"Size information for {repo_id} is incomplete. " + "The dataset is too large for complete size determination. " + "Reported numbers may be lower than actual size." + ) + + # Add metadata about completeness to the response + if "size" in data and "dataset" in data["size"]: + data["size"]["dataset"]["size_determination_complete"] = not is_partial + data["size"]["dataset"]["size_warning"] = ( + "Partial size only - actual dataset may be larger" + if is_partial + else "Complete size information" + ) + + self.size = data + + def _check_repo_size_and_decide_strategy( + self, + auto_download_threshold_mb: float, + force_full_download: bool, + allow_patterns: list[str] | str | None, + ignore_patterns: list[str] | str | None, + **kwargs, + ) -> tuple[list[str] | str | None, list[str] | str | None]: + """ + Check repo size and decide download strategy. + + Returns: + Tuple of (allow_patterns, ignore_patterns) to use for download + + """ + if force_full_download or auto_download_threshold_mb <= 0: + return None, None + + try: + # Get repo info to estimate size + revision = kwargs.get("revision") + if revision is not None: + info = repo_info( + repo_id=self.repo_id, + repo_type=self.repo_type, + token=self.token, + revision=str(revision), + ) + else: + info = repo_info( + repo_id=self.repo_id, + repo_type=self.repo_type, + token=self.token, + ) + + # Estimate total size from siblings (files in repo) + total_size_bytes = sum( + getattr(sibling, "size", 0) or 0 + for sibling in getattr(info, "siblings", []) + ) + total_size_mb = total_size_bytes / (1024 * 1024) + + self.logger.info(f"Estimated repo size: {total_size_mb:.2f} MB") + + # If small enough, download everything + if total_size_mb <= auto_download_threshold_mb: + self.logger.info( + f"Repo size ({total_size_mb:.2f} MB) under threshold " + f"({auto_download_threshold_mb} MB), downloading full repo" + ) + return None, None + else: + raise RepoTooLargeError( + f"Repo size ({total_size_mb:.2f} MB) exceeds threshold " + f"({auto_download_threshold_mb} MB). Use a selective download " + "method via `files`, `allow_patterns` or `ignore_patterns`, " + "or increase `auto_download_threshold_mb` and try again." + ) + + except Exception as e: + self.logger.warning( + f"Could not determine repo size: {e}. Proceeding with " + f"pattern-based download." + ) + return allow_patterns, ignore_patterns + + def _download_single_file( + self, + filename: str, + dry_run: bool = False, + **kwargs, + ) -> Path: + """ + Download a single file using hf_hub_download. + + :param filename: File to download + :param dry_run: If True, log what would be downloaded without downloading + :param kwargs: Additional arguments passed directly to hf_hub_download + :return: Path to the downloaded file + + """ + self.logger.info(f"Downloading single file: {filename}") + + if dry_run: + self.logger.info(f"[DRY RUN] Would download {filename} from {self.repo_id}") + return Path("dry_run_path") + + # Build base arguments + hf_kwargs = { + "repo_id": self.repo_id, + "repo_type": self.repo_type, + "filename": filename, + "token": self.token, + **kwargs, # User kwargs override defaults + } + + # Set cache_dir only if local_dir not specified + if "local_dir" not in hf_kwargs and self.cache_dir is not None: + hf_kwargs["cache_dir"] = str(self.cache_dir) + + # Ensure string conversion for Path-like arguments + if "local_dir" in hf_kwargs and hf_kwargs["local_dir"] is not None: + hf_kwargs["local_dir"] = str(hf_kwargs["local_dir"]) + if "cache_dir" in hf_kwargs and hf_kwargs["cache_dir"] is not None: + hf_kwargs["cache_dir"] = str(hf_kwargs["cache_dir"]) + + file_path = hf_hub_download(**hf_kwargs) + self._snapshot_path = Path(file_path).parent + return Path(file_path) + + def _download_snapshot( + self, + dry_run: bool = False, + **kwargs, + ) -> Path: + """ + Download repository snapshot using snapshot_download. + + :param dry_run: If True, log what would be downloaded without downloading + :param kwargs: Additional arguments passed directly to snapshot_download + :return: Path to the downloaded snapshot + + """ + # Log download plan + if dry_run: + self.logger.info(f"[DRY RUN] Would download from {self.repo_id}:") + self.logger.info(f" - allow_patterns: {kwargs.get('allow_patterns')}") + self.logger.info(f" - ignore_patterns: {kwargs.get('ignore_patterns')}") + return Path("dry_run_path") + + # Execute snapshot download + self.logger.info( + f"Downloading repo snapshot with patterns - " + f"allow: {kwargs.get('allow_patterns')}, " + f"ignore: {kwargs.get('ignore_patterns')}" + ) + + # Build base arguments + # note that kwargs passed into this method will override defaults, + # including repo_id, etc + snapshot_kwargs = { + "repo_id": self.repo_id, + "repo_type": self.repo_type, + "token": self.token, + **kwargs, + } + + # Set cache_dir only if local_dir not specified and cache_dir wasn't passed in + # by user + if ( + "local_dir" not in snapshot_kwargs + and "cache_dir" not in snapshot_kwargs + and self.cache_dir is not None + ): + snapshot_kwargs["cache_dir"] = str(self.cache_dir) + + # if allow_patterns or ignore_patterns are strings, convert to list + for pattern_key in ["allow_patterns", "ignore_patterns"]: + if ( + pattern_key in snapshot_kwargs + and snapshot_kwargs[pattern_key] is not None + ): + patterns = snapshot_kwargs[pattern_key] + if isinstance(patterns, str): + snapshot_kwargs[pattern_key] = [patterns] + + snapshot_path = snapshot_download(**snapshot_kwargs) + self.snapshot_path = Path(snapshot_path) + return self.snapshot_path + + def download( + self, + *, + files: list[str] | str | None = None, + force_full_download: bool = False, + auto_download_threshold_mb: float = 100.0, + dry_run: bool = False, + **kwargs, + ) -> Path: + """ + Download dataset by file, patterns or if the dataset is small enough, the entire + repo. + + :param files: Specific file(s) to download. If provided, uses hf_hub_download + :param force_full_download: If True, always download entire repo regardless of + size + :param auto_download_threshold_mb: Auto-download full repo if estimated size < + this (MB) + :param dry_run: If True, log what would be downloaded without actually + downloading + :param kwargs: Additional arguments passed to hf_hub_download or + snapshot_download. Common args: revision, local_dir, cache_dir, + local_files_only, allow_patterns, ignore_patterns, etc. + :return: Path to downloaded content (file or directory). + + """ + # Handle single file download + if files is not None: + if isinstance(files, str): + files = [files] + + if len(files) == 1: + return self._download_single_file( + filename=files[0], + dry_run=dry_run, + **kwargs, + ) + else: + # Multiple specific files - use filtered snapshot_download + self.logger.info(f"Downloading specific files: {files}") + if kwargs.get("allow_patterns") is not None: + self.logger.warning( + "`allow_patterns` will be overridden by `files` argument" + ) + kwargs["allow_patterns"] = files + + # Check repo size and adjust download strategy if needed + allow_patterns, ignore_patterns = self._check_repo_size_and_decide_strategy( + auto_download_threshold_mb=auto_download_threshold_mb, + force_full_download=force_full_download, + allow_patterns=kwargs.get("allow_patterns"), + ignore_patterns=kwargs.get("ignore_patterns"), + **kwargs, + ) + + # Update kwargs with determined patterns + if allow_patterns is not None: + kwargs["allow_patterns"] = allow_patterns + if ignore_patterns is not None: + kwargs["ignore_patterns"] = ignore_patterns + + # Execute snapshot download + return self._download_snapshot(dry_run=dry_run, **kwargs) + + @abstractmethod + def parse_datacard(self, *args: Any, **kwargs: Any) -> Mapping[str, Any]: + """ + Abstract method to parse a datacard from the downloaded content. + + Must be implemented by subclasses. + + """ + raise NotImplementedError("Subclasses must implement this method.") + + @abstractmethod + def query(self, *args: Any, **kwargs: Any) -> Any: + """ + Abstract method to query the API. + + Must be implemented by subclasses. + + """ + raise NotImplementedError("Subclasses must implement this method.") diff --git a/tfbpapi/BindingManualQCAPI.py b/tfbpapi/BindingManualQCAPI.py index df4169b..02b6a21 100644 --- a/tfbpapi/BindingManualQCAPI.py +++ b/tfbpapi/BindingManualQCAPI.py @@ -2,7 +2,7 @@ from typing import Any import pandas as pd -import requests # type: ignore +import requests from tfbpapi.AbstractRecordsOnlyAPI import AbstractRecordsOnlyAPI diff --git a/tfbpapi/DtoAPI.py b/tfbpapi/DtoAPI.py index bc8d404..b8780b6 100644 --- a/tfbpapi/DtoAPI.py +++ b/tfbpapi/DtoAPI.py @@ -6,7 +6,7 @@ import aiohttp import pandas as pd -import requests # type: ignore +import requests from tfbpapi.AbstractRecordsOnlyAPI import AbstractRecordsOnlyAPI diff --git a/tfbpapi/ExpressionManualQCAPI.py b/tfbpapi/ExpressionManualQCAPI.py index 80023e6..15ff863 100644 --- a/tfbpapi/ExpressionManualQCAPI.py +++ b/tfbpapi/ExpressionManualQCAPI.py @@ -2,7 +2,7 @@ from typing import Any import pandas as pd -import requests # type: ignore +import requests from tfbpapi.AbstractRecordsOnlyAPI import AbstractRecordsOnlyAPI diff --git a/tfbpapi/HfCacheManager.py b/tfbpapi/HfCacheManager.py new file mode 100644 index 0000000..95a0e02 --- /dev/null +++ b/tfbpapi/HfCacheManager.py @@ -0,0 +1,271 @@ +import logging +from datetime import datetime, timedelta +from typing import Literal + +from huggingface_hub import scan_cache_dir +from huggingface_hub.utils import DeleteCacheStrategy + + +class HFCacheManager: + """Cache memory management for Hugging Face Hub cache.""" + + def __init__(self, logger: logging.Logger | None = None): + self.logger = logger or logging.getLogger(__name__) + + def clean_cache_by_age( + self, + max_age_days: int = 30, + dry_run: bool = True, + ) -> DeleteCacheStrategy: + """ + Clean cache entries older than specified age. + + :param max_age_days: Remove revisions older than this many days + :param dry_run: If True, show what would be deleted without executing + size_threshold: Only delete if total cache size exceeds this (e.g., "10GB") + + :return: DeleteCacheStrategy object that can be executed + + """ + cache_info = scan_cache_dir() + cutoff_date = datetime.now() - timedelta(days=max_age_days) + + old_revisions = [] + for repo in cache_info.repos: + for revision in repo.revisions: + # Check if revision is older than cutoff + revision_date = datetime.fromtimestamp(revision.last_modified) + if revision_date < cutoff_date: + old_revisions.append(revision.commit_hash) + self.logger.debug( + f"Marking for deletion: {revision.commit_hash} " + f"(last modified: {revision.last_modified})" + ) + + if not old_revisions: + self.logger.info("No old revisions found to delete") + # return None + + delete_strategy = cache_info.delete_revisions(*old_revisions) + + self.logger.info( + f"Found {len(old_revisions)} old revisions. " + f"Will free {delete_strategy.expected_freed_size_str}" + ) + + if not dry_run: + delete_strategy.execute() + self.logger.info( + f"Cache cleanup completed. Freed " + f"{delete_strategy.expected_freed_size_str}" + ) + else: + self.logger.info("Dry run completed. Use dry_run=False to execute deletion") + + return delete_strategy + + def clean_cache_by_size( + self, + target_size: str, + strategy: Literal[ + "oldest_first", "largest_first", "least_used" + ] = "oldest_first", + dry_run: bool = True, + ) -> DeleteCacheStrategy: + """ + Clean cache to reach target size by removing revisions. + + :param target_size: Target cache size (e.g., "5GB", "500MB") + :param strategy: Deletion strategy - "oldest_first", "largest_first", + "least_used" + :param dry_run: If True, show what would be deleted without executing + + :return: DeleteCacheStrategy object that can be executed + + """ + cache_info = scan_cache_dir() + current_size = cache_info.size_on_disk + target_bytes = self._parse_size_string(target_size) + + if current_size <= target_bytes: + self.logger.info( + f"Cache size ({cache_info.size_on_disk_str}) already below " + f"target ({target_size})" + ) + + bytes_to_free = current_size - target_bytes + + # Get all revisions sorted by strategy + all_revisions = [] + for repo in cache_info.repos: + for revision in repo.revisions: + all_revisions.append(revision) + + # Sort revisions based on strategy + if strategy == "oldest_first": + all_revisions.sort(key=lambda r: r.last_modified) + elif strategy == "largest_first": + all_revisions.sort(key=lambda r: r.size_on_disk, reverse=True) + elif strategy == "least_used": + # Use last_modified as proxy for usage + all_revisions.sort(key=lambda r: r.last_modified) + else: + raise ValueError(f"Unknown strategy: {strategy}") + + # Select revisions to delete + revisions_to_delete = [] + freed_bytes = 0 + + for revision in all_revisions: + if freed_bytes >= bytes_to_free: + break + revisions_to_delete.append(revision.commit_hash) + freed_bytes += revision.size_on_disk + + if not revisions_to_delete: + self.logger.warning("No revisions selected for deletion") + + delete_strategy = cache_info.delete_revisions(*revisions_to_delete) + + self.logger.info( + f"Selected {len(revisions_to_delete)} revisions for deletion. " + f"Will free {delete_strategy.expected_freed_size_str}" + ) + + if not dry_run: + delete_strategy.execute() + self.logger.info( + f"Cache cleanup completed. Freed " + f"{delete_strategy.expected_freed_size_str}" + ) + else: + self.logger.info("Dry run completed. Use dry_run=False to execute deletion") + + return delete_strategy + + def clean_unused_revisions( + self, keep_latest: int = 2, dry_run: bool = True + ) -> DeleteCacheStrategy: + """ + Clean unused revisions, keeping only the latest N revisions per repo. + + :param keep_latest: Number of latest revisions to keep per repo + :param dry_run: If True, show what would be deleted without executing + :return: DeleteCacheStrategy object that can be executed + + """ + cache_info = scan_cache_dir() + revisions_to_delete = [] + + for repo in cache_info.repos: + # Sort revisions by last modified (newest first) + sorted_revisions = sorted( + repo.revisions, key=lambda r: r.last_modified, reverse=True + ) + + # Keep the latest N, mark the rest for deletion + if len(sorted_revisions) > keep_latest: + old_revisions = sorted_revisions[keep_latest:] + for revision in old_revisions: + revisions_to_delete.append(revision.commit_hash) + self.logger.debug( + f"Marking old revision for deletion: {repo.repo_id} - " + f"{revision.commit_hash}" + ) + + delete_strategy = cache_info.delete_revisions(*revisions_to_delete) + + self.logger.info( + f"Found {len(revisions_to_delete)} unused revisions. " + f"Will free {delete_strategy.expected_freed_size_str}" + ) + + if not dry_run: + delete_strategy.execute() + self.logger.info( + f"Cache cleanup completed. Freed " + f"{delete_strategy.expected_freed_size_str}" + ) + else: + self.logger.info("Dry run completed. Use dry_run=False to execute deletion") + + return delete_strategy + + def auto_clean_cache( + self, + max_age_days: int = 30, + max_total_size: str = "10GB", + keep_latest_per_repo: int = 2, + dry_run: bool = True, + ) -> list[DeleteCacheStrategy]: + """ + Automated cache cleaning with multiple strategies. + + :param max_age_days: Remove revisions older than this + :param max_total_size: Target maximum cache size + :param keep_latest_per_repo: Keep this many latest revisions per repo + :param dry_run: If True, show what would be deleted without executing + :return: List of DeleteCacheStrategy objects that were executed + + """ + strategies_executed = [] + + self.logger.info("Starting automated cache cleanup...") + + # Step 1: Remove very old revisions + strategy = self.clean_cache_by_age(max_age_days=max_age_days, dry_run=dry_run) + if strategy: + strategies_executed.append(strategy) + + # Step 2: Remove unused revisions (keep only latest per repo) + strategy = self.clean_unused_revisions( + keep_latest=keep_latest_per_repo, dry_run=dry_run + ) + if strategy: + strategies_executed.append(strategy) + + # Step 3: If still over size limit, remove more aggressively + cache_info = scan_cache_dir() + if cache_info.size_on_disk > self._parse_size_string(max_total_size): + strategy = self.clean_cache_by_size( + target_size=max_total_size, strategy="oldest_first", dry_run=dry_run + ) + if strategy: + strategies_executed.append(strategy) + + total_freed = sum(s.expected_freed_size for s in strategies_executed) + self.logger.info( + f"Automated cleanup complete. Total freed: " + f"{self._format_bytes(total_freed)}" + ) + + return strategies_executed + + def _parse_size_string(self, size_str: str) -> int: + """Parse size string like '10GB' to bytes.""" + size_str = size_str.upper().strip() + + # Check longer units first to avoid partial matches + multipliers = {"TB": 1024**4, "GB": 1024**3, "MB": 1024**2, "KB": 1024, "B": 1} + + for unit, multiplier in multipliers.items(): + if size_str.endswith(unit): + number = float(size_str[: -len(unit)]) + return int(number * multiplier) + + # If no unit specified, assume bytes + return int(size_str) + + def _format_bytes(self, bytes_size: int) -> str: + """Format bytes into human readable string.""" + if bytes_size == 0: + return "0B" + + # iterate over common units, dividing by 1024 each time, to find an + # appropriate unit. Default to TB if the size is very large + size = float(bytes_size) + for unit in ["B", "KB", "MB", "GB", "TB"]: + if size < 1024.0: + return f"{size:.1f}{unit}" + size /= 1024.0 + return f"{size:.1f}TB" diff --git a/tfbpapi/RankResponseAPI.py b/tfbpapi/RankResponseAPI.py index 6ed3330..e12e0f2 100644 --- a/tfbpapi/RankResponseAPI.py +++ b/tfbpapi/RankResponseAPI.py @@ -8,7 +8,7 @@ import aiohttp import pandas as pd -from requests import Response, delete, post # type: ignore +from requests import Response, delete, post from requests_toolbelt import MultipartEncoder from tfbpapi.AbstractRecordsAndFilesAPI import ( diff --git a/tfbpapi/UnivariateModelsAPI.py b/tfbpapi/UnivariateModelsAPI.py index d3bc632..57cc303 100644 --- a/tfbpapi/UnivariateModelsAPI.py +++ b/tfbpapi/UnivariateModelsAPI.py @@ -6,7 +6,7 @@ import aiohttp import pandas as pd -import requests # type: ignore +import requests from tfbpapi.AbstractRecordsOnlyAPI import AbstractRecordsOnlyAPI diff --git a/tfbpapi/tests/conftest.py b/tfbpapi/tests/conftest.py new file mode 100644 index 0000000..47b7b94 --- /dev/null +++ b/tfbpapi/tests/conftest.py @@ -0,0 +1,26 @@ +import pickle +from pathlib import Path +from unittest.mock import patch + +import pytest + + +@pytest.fixture +def mock_cache_info(): + """Load real cache data from pickle file.""" + cache_file = Path(__file__).parent / "data" / "cache_info.pkl" + + if not cache_file.exists(): + pytest.skip( + "test_cache_data.pkl not found. Run cache data generation script first." + ) + + with open(cache_file, "rb") as f: + return pickle.load(f) + + +@pytest.fixture +def mock_scan_cache_dir(mock_cache_info): + """Mock scan_cache_dir to return our pickled cache data.""" + with patch("huggingface_hub.scan_cache_dir", return_value=mock_cache_info): + yield mock_cache_info diff --git a/tfbpapi/tests/conftests.py b/tfbpapi/tests/conftests.py deleted file mode 100644 index e69de29..0000000 diff --git a/tfbpapi/tests/data/cache_info.pkl b/tfbpapi/tests/data/cache_info.pkl new file mode 100644 index 0000000000000000000000000000000000000000..7906d8e30dd63514636537b3083c3ee20c7995e7 GIT binary patch literal 123975 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 literal 0 HcmV?d00001 diff --git a/tfbpapi/tests/test_HfCacheManager.py b/tfbpapi/tests/test_HfCacheManager.py new file mode 100644 index 0000000..0a4e472 --- /dev/null +++ b/tfbpapi/tests/test_HfCacheManager.py @@ -0,0 +1,55 @@ +from tfbpapi.HfCacheManager import HFCacheManager + + +def test_parse_size_string(): + """Test size string parsing.""" + cache_manager = HFCacheManager() + assert cache_manager._parse_size_string("10KB") == 10 * 1024 + assert cache_manager._parse_size_string("5MB") == 5 * 1024**2 + assert cache_manager._parse_size_string("2GB") == 2 * 1024**3 + assert cache_manager._parse_size_string("1TB") == 1 * 1024**4 + assert cache_manager._parse_size_string("500") == 500 + + +def test_clean_cache_by_age(mock_scan_cache_dir): + """Test age-based cache cleaning.""" + + cache_manager = HFCacheManager() + + # This will use the mocked scan_cache_dir + strategy = cache_manager.clean_cache_by_age(max_age_days=1, dry_run=True) + + # Test your logic + assert strategy is not None or len(mock_scan_cache_dir.repos) == 0 + if strategy: + assert len(strategy.repos) >= 0 + assert strategy.expected_freed_size >= 0 + + +def test_clean_cache_by_size(mock_scan_cache_dir): + """Test size-based cache cleaning.""" + + cache_manager = HFCacheManager() + + # Test with a very small target to force cleanup + strategy = cache_manager.clean_cache_by_size(target_size="1GB", dry_run=True) + + if mock_scan_cache_dir.size_on_disk > 1024: # If cache has data + assert strategy is not None + assert len(strategy.repos) > 0 + + +def test_auto_clean_cache(mock_scan_cache_dir): + """Test automated cache cleaning.""" + + cache_manager = HFCacheManager() + + strategies = cache_manager.auto_clean_cache( + max_age_days=1, + max_total_size="1KB", + keep_latest_per_repo=1, + dry_run=True, + ) + + # Should return a list of strategies + assert isinstance(strategies, list) diff --git a/tfbpapi/tests/test_ParamsDict.py b/tfbpapi/tests/test_ParamsDict.py index ee5a246..cccaeda 100644 --- a/tfbpapi/tests/test_ParamsDict.py +++ b/tfbpapi/tests/test_ParamsDict.py @@ -1,5 +1,5 @@ import pytest -import requests # type: ignore +import requests import responses from tfbpapi.ParamsDict import ParamsDict From 188e2766a2676001d6d5bcf2d1ec31ed3353166f Mon Sep 17 00:00:00 2001 From: chasem Date: Mon, 8 Sep 2025 14:37:39 -0500 Subject: [PATCH 27/49] adding tests for AbstractHfAPI --- tfbpapi/AbstractHfAPI.py | 247 +++++++--------- tfbpapi/tests/test_AbstractHfAPI.py | 435 ++++++++++++++++++++++++++++ 2 files changed, 540 insertions(+), 142 deletions(-) create mode 100644 tfbpapi/tests/test_AbstractHfAPI.py diff --git a/tfbpapi/AbstractHfAPI.py b/tfbpapi/AbstractHfAPI.py index 14db82f..58e0884 100644 --- a/tfbpapi/AbstractHfAPI.py +++ b/tfbpapi/AbstractHfAPI.py @@ -6,27 +6,27 @@ from typing import Any, Literal import requests -from huggingface_hub import hf_hub_download, repo_info, snapshot_download +from huggingface_hub import hf_hub_download, snapshot_download from huggingface_hub.constants import HF_HUB_CACHE from requests import HTTPError +# Constants +MB_TO_BYTES = 1024 * 1024 + class RepoTooLargeError(ValueError): """Raised when repository exceeds auto-download threshold.""" - pass - class AbstractHfAPI(ABC): """Abstract base class for creating Hugging Face API clients.""" - # TODO: can revision be set to "latest" by default? def __init__( self, repo_id: str, repo_type: Literal["model", "dataset", "space"] = "dataset", token: str | None = None, - cache_dir: str | Path = HF_HUB_CACHE, + cache_dir: str | Path | None = None, ): """ Initialize the HF-backed API client. @@ -39,20 +39,17 @@ def __init__( :param cache_dir: HF cache_dir for hf_hub_download and snapshot_download (see huggingface_hub docs). May be passed via the HF_CACHE_DIR environmental variable. If not set, the default HF cache directory is used. + :raises FileNotFoundError: If the specified cache_dir does not exist. """ self.logger = logging.getLogger(self.__class__.__name__) - # Let user input override env var, but use the env var if available - resolved_token = token or os.getenv("HF_TOKEN", None) - resolved_cache_dir = cache_dir or os.getenv("HF_CACHE_DIR", HF_HUB_CACHE) - if isinstance(resolved_cache_dir, str): - resolved_cache_dir = Path(resolved_cache_dir) - - self.token = resolved_token + self.token = token or os.getenv("HF_TOKEN", None) self.repo_id = repo_id self.repo_type = repo_type - self.cache_dir = resolved_cache_dir + self.cache_dir = Path( + cache_dir if cache_dir else os.getenv("HF_CACHE_DIR", HF_HUB_CACHE) + ) @property def token(self) -> str | None: @@ -70,14 +67,19 @@ def repo_id(self) -> str: @repo_id.setter def repo_id(self, value: str) -> None: + """ + Set the repo_id. + + This setter also calls _get_dataset_size to fetch size info and validate that + the repo exists and is accessible. No error is raised if the repo is not + accessible, but an error is logged. + + """ self._repo_id = value try: - self._get_dataset_size(value) + self._get_dataset_size(self._repo_id) except (HTTPError, ValueError) as e: - self.logger.warning(f"Could not validate repo_id {value}: {e}") - self.logger.info( - "Repo validation skipped - will be checked on first download" - ) + self.logger.error(f"Could not reach {value}: {e}") @property def cache_dir(self) -> Path: @@ -85,16 +87,27 @@ def cache_dir(self) -> Path: @cache_dir.setter def cache_dir(self, value: str | Path) -> None: - """Set the cache directory for huggingface_hub downloads.""" + """ + Set the cache directory for huggingface_hub downloads. + + :raises FileNotFoundError: If the specified directory does not exist. + + """ path = Path(value) if not path.exists(): - raise FileNotFoundError(f"Cache directory does not exist: {path}") + raise FileNotFoundError(f"Cache directory {path} does not exist") self._cache_dir = path @property def size(self) -> dict[str, Any] | None: - """Size information from the HF Dataset Server API (if available).""" - return self._size if hasattr(self, "_size") else None + """ + Size information from the HF Dataset Server API. + + This reaches the /size endpoint. See + https://github.com/huggingface/dataset-viewer/blob/8f0ae65f0ff64791111d37a725af437c3c752daf/docs/source/size.md + + """ + return getattr(self, "_size", None) @size.setter def size(self, value: dict[str, Any]) -> None: @@ -103,14 +116,40 @@ def size(self, value: dict[str, Any]) -> None: @property def snapshot_path(self) -> Path | None: """Path to the last downloaded snapshot (if any).""" - return self._snapshot_path if hasattr(self, "_snapshot_path") else None + return getattr(self, "_snapshot_path", None) @snapshot_path.setter def snapshot_path(self, value: str | Path | None) -> None: - if value is None: - self._snapshot_path = None - else: - self._snapshot_path = Path(value) + self._snapshot_path = None if value is None else Path(value) + + def _get_dataset_size_mb(self) -> float: + """Get dataset size in MB, returning inf if not available.""" + if not self.size: + return float("inf") + return ( + self.size.get("size", {}) + .get("dataset", {}) + .get("num_bytes_original_files", float("inf")) + / MB_TO_BYTES + ) + + def _ensure_str_paths(self, kwargs: dict[str, Any]) -> None: + """Ensure Path-like arguments are converted to strings.""" + for key in ["local_dir", "cache_dir"]: + if key in kwargs and kwargs[key] is not None: + kwargs[key] = str(kwargs[key]) + + def _build_auth_headers(self) -> dict[str, str]: + """Build authentication headers if token is available.""" + return {"Authorization": f"Bearer {self.token}"} if self.token else {} + + def _normalize_patterns(self, kwargs: dict[str, Any]) -> None: + """Convert string patterns to lists.""" + for pattern_key in ["allow_patterns", "ignore_patterns"]: + if pattern_key in kwargs and kwargs[pattern_key] is not None: + patterns = kwargs[pattern_key] + if isinstance(patterns, str): + kwargs[pattern_key] = [patterns] def _get_dataset_size(self, repo_id: str | None = None) -> None: """ @@ -125,11 +164,7 @@ def _get_dataset_size(self, repo_id: str | None = None) -> None: repo_id = repo_id or self.repo_id url = f"https://datasets-server.huggingface.co/size?dataset={repo_id}" - headers = {} - if self.token: - headers["Authorization"] = f"Bearer {self.token}" - - response = requests.get(url, headers=headers) + response = requests.get(url, headers=self._build_auth_headers()) response.raise_for_status() data = response.json() @@ -155,72 +190,6 @@ def _get_dataset_size(self, repo_id: str | None = None) -> None: self.size = data - def _check_repo_size_and_decide_strategy( - self, - auto_download_threshold_mb: float, - force_full_download: bool, - allow_patterns: list[str] | str | None, - ignore_patterns: list[str] | str | None, - **kwargs, - ) -> tuple[list[str] | str | None, list[str] | str | None]: - """ - Check repo size and decide download strategy. - - Returns: - Tuple of (allow_patterns, ignore_patterns) to use for download - - """ - if force_full_download or auto_download_threshold_mb <= 0: - return None, None - - try: - # Get repo info to estimate size - revision = kwargs.get("revision") - if revision is not None: - info = repo_info( - repo_id=self.repo_id, - repo_type=self.repo_type, - token=self.token, - revision=str(revision), - ) - else: - info = repo_info( - repo_id=self.repo_id, - repo_type=self.repo_type, - token=self.token, - ) - - # Estimate total size from siblings (files in repo) - total_size_bytes = sum( - getattr(sibling, "size", 0) or 0 - for sibling in getattr(info, "siblings", []) - ) - total_size_mb = total_size_bytes / (1024 * 1024) - - self.logger.info(f"Estimated repo size: {total_size_mb:.2f} MB") - - # If small enough, download everything - if total_size_mb <= auto_download_threshold_mb: - self.logger.info( - f"Repo size ({total_size_mb:.2f} MB) under threshold " - f"({auto_download_threshold_mb} MB), downloading full repo" - ) - return None, None - else: - raise RepoTooLargeError( - f"Repo size ({total_size_mb:.2f} MB) exceeds threshold " - f"({auto_download_threshold_mb} MB). Use a selective download " - "method via `files`, `allow_patterns` or `ignore_patterns`, " - "or increase `auto_download_threshold_mb` and try again." - ) - - except Exception as e: - self.logger.warning( - f"Could not determine repo size: {e}. Proceeding with " - f"pattern-based download." - ) - return allow_patterns, ignore_patterns - def _download_single_file( self, filename: str, @@ -248,7 +217,7 @@ def _download_single_file( "repo_type": self.repo_type, "filename": filename, "token": self.token, - **kwargs, # User kwargs override defaults + **kwargs, } # Set cache_dir only if local_dir not specified @@ -256,10 +225,7 @@ def _download_single_file( hf_kwargs["cache_dir"] = str(self.cache_dir) # Ensure string conversion for Path-like arguments - if "local_dir" in hf_kwargs and hf_kwargs["local_dir"] is not None: - hf_kwargs["local_dir"] = str(hf_kwargs["local_dir"]) - if "cache_dir" in hf_kwargs and hf_kwargs["cache_dir"] is not None: - hf_kwargs["cache_dir"] = str(hf_kwargs["cache_dir"]) + self._ensure_str_paths(hf_kwargs) file_path = hf_hub_download(**hf_kwargs) self._snapshot_path = Path(file_path).parent @@ -303,7 +269,6 @@ def _download_snapshot( } # Set cache_dir only if local_dir not specified and cache_dir wasn't passed in - # by user if ( "local_dir" not in snapshot_kwargs and "cache_dir" not in snapshot_kwargs @@ -311,15 +276,8 @@ def _download_snapshot( ): snapshot_kwargs["cache_dir"] = str(self.cache_dir) - # if allow_patterns or ignore_patterns are strings, convert to list - for pattern_key in ["allow_patterns", "ignore_patterns"]: - if ( - pattern_key in snapshot_kwargs - and snapshot_kwargs[pattern_key] is not None - ): - patterns = snapshot_kwargs[pattern_key] - if isinstance(patterns, str): - snapshot_kwargs[pattern_key] = [patterns] + # Convert string patterns to lists + self._normalize_patterns(snapshot_kwargs) snapshot_path = snapshot_download(**snapshot_kwargs) self.snapshot_path = Path(snapshot_path) @@ -327,7 +285,6 @@ def _download_snapshot( def download( self, - *, files: list[str] | str | None = None, force_full_download: bool = False, auto_download_threshold_mb: float = 100.0, @@ -351,42 +308,48 @@ def download( :return: Path to downloaded content (file or directory). """ - # Handle single file download + dataset_size_mb = self._get_dataset_size_mb() + if dataset_size_mb <= auto_download_threshold_mb or force_full_download: + self.logger.info( + f"Dataset size ({dataset_size_mb:.2f} MB) is below the auto-download " + f"threshold of {auto_download_threshold_mb} MB. Downloading entire " + "repo." + ) + files = None + kwargs.pop("allow_patterns", None) + kwargs.pop("ignore_patterns", None) + elif ( + not files + and not kwargs.get("allow_patterns") + and not kwargs.get("ignore_patterns") + ): + excess_size_mb = dataset_size_mb - auto_download_threshold_mb + raise RepoTooLargeError( + f"Dataset size ({dataset_size_mb:.2f} MB) exceeds the " + f"auto-download threshold of {auto_download_threshold_mb} MB by " + f"{excess_size_mb:.2f} MB. To download the dataset, either " + "specify specific files or patterns to download, " + "set force_full_download=True or increase the " + "`auto_download_threshold_mb`." + ) + # Handle specific file downloads if files is not None: - if isinstance(files, str): - files = [files] - - if len(files) == 1: + if isinstance(files, str) or (isinstance(files, list) and len(files) == 1): + # Single file + filename = files if isinstance(files, str) else files[0] + self.logger.info(f"Preparing to download single file: {filename}") return self._download_single_file( - filename=files[0], - dry_run=dry_run, - **kwargs, + filename=filename, dry_run=dry_run, **kwargs ) - else: - # Multiple specific files - use filtered snapshot_download - self.logger.info(f"Downloading specific files: {files}") + elif isinstance(files, list) and len(files) > 1: + # Multiple files - use snapshot_download with allow_patterns if kwargs.get("allow_patterns") is not None: self.logger.warning( - "`allow_patterns` will be overridden by `files` argument" + "Both 'files' and 'allow_patterns' were provided. " + "'files' will take precedence." ) kwargs["allow_patterns"] = files - # Check repo size and adjust download strategy if needed - allow_patterns, ignore_patterns = self._check_repo_size_and_decide_strategy( - auto_download_threshold_mb=auto_download_threshold_mb, - force_full_download=force_full_download, - allow_patterns=kwargs.get("allow_patterns"), - ignore_patterns=kwargs.get("ignore_patterns"), - **kwargs, - ) - - # Update kwargs with determined patterns - if allow_patterns is not None: - kwargs["allow_patterns"] = allow_patterns - if ignore_patterns is not None: - kwargs["ignore_patterns"] = ignore_patterns - - # Execute snapshot download return self._download_snapshot(dry_run=dry_run, **kwargs) @abstractmethod diff --git a/tfbpapi/tests/test_AbstractHfAPI.py b/tfbpapi/tests/test_AbstractHfAPI.py new file mode 100644 index 0000000..6f28eea --- /dev/null +++ b/tfbpapi/tests/test_AbstractHfAPI.py @@ -0,0 +1,435 @@ +import tempfile +from collections.abc import Mapping +from pathlib import Path +from typing import Any +from unittest.mock import Mock, patch + +import pytest +from requests import HTTPError + +from tfbpapi.AbstractHfAPI import AbstractHfAPI, RepoTooLargeError + + +class TestHfAPI(AbstractHfAPI): + """Concrete implementation of AbstractHfAPI for testing.""" + + def parse_datacard(self, *args: Any, **kwargs: Any) -> Mapping[str, Any]: + """Test implementation of parse_datacard.""" + return {"test": "datacard"} + + def query(self, *args: Any, **kwargs: Any) -> Any: + """Test implementation of query.""" + return {"test": "query"} + + +@pytest.fixture +def mock_hf_hub_download(): + """Mock hf_hub_download to return a fake path.""" + with patch("tfbpapi.AbstractHfAPI.hf_hub_download") as mock: + mock.return_value = "/fake/path/to/file.txt" + yield mock + + +@pytest.fixture +def mock_snapshot_download(): + """Mock snapshot_download to return a fake path.""" + with patch("tfbpapi.AbstractHfAPI.snapshot_download") as mock: + mock.return_value = "/fake/path/to/snapshot" + yield mock + + +@pytest.fixture +def mock_repo_info(): + """Mock repo_info to return fake repo information.""" + with patch("tfbpapi.AbstractHfAPI.repo_info") as mock: + # Create a mock with siblings attribute + mock_info = Mock() + mock_info.siblings = [ + Mock(size=1024 * 1024), # 1MB file + Mock(size=512 * 1024), # 512KB file + Mock(size=None), # File with no size + ] + mock.return_value = mock_info + yield mock + + +@pytest.fixture +def mock_requests_get(): + """Mock requests.get for dataset size API calls.""" + with patch("tfbpapi.AbstractHfAPI.requests.get") as mock: + # Create a mock response + mock_response = Mock() + mock_response.json.return_value = { + "size": { + "dataset": { + "num_bytes_original_files": 10 * 1024 * 1024, # 10MB + "size_determination_complete": True, + } + }, + "partial": False, + } + mock_response.raise_for_status.return_value = None + mock.return_value = mock_response + yield mock + + +@pytest.fixture +def mock_dataset_size_call(): + """Mock the _get_dataset_size call to prevent real API calls during init.""" + with patch.object(AbstractHfAPI, "_get_dataset_size") as mock: + yield mock + + +@pytest.fixture +def temp_cache_dir(): + """Create a temporary directory for cache testing.""" + with tempfile.TemporaryDirectory() as temp_dir: + yield Path(temp_dir) + + +class TestAbstractHfAPI: + """Test cases for AbstractHfAPI.""" + + def test_init_basic(self, temp_cache_dir, mock_dataset_size_call): + """Test basic initialization.""" + api = TestHfAPI( + repo_id="test/repo", + repo_type="dataset", + token="test-token", + cache_dir=temp_cache_dir, + ) + + assert api.repo_id == "test/repo" + assert api.repo_type == "dataset" + assert api.token == "test-token" + assert api.cache_dir == temp_cache_dir + + def test_init_with_env_vars( + self, temp_cache_dir, monkeypatch, mock_dataset_size_call + ): + """Test initialization with environment variables.""" + monkeypatch.setenv("HF_TOKEN", "env-token") + monkeypatch.setenv("HF_CACHE_DIR", str(temp_cache_dir)) + + api = TestHfAPI(repo_id="test/repo") + + assert api.token == "env-token" + assert api.cache_dir == temp_cache_dir + + def test_init_user_overrides_env(self, temp_cache_dir, monkeypatch): + """Test that user parameters override environment variables.""" + monkeypatch.setenv("HF_TOKEN", "env-token") + + api = TestHfAPI( + repo_id="test/repo", token="user-token", cache_dir=temp_cache_dir + ) + + assert api.token == "user-token" + assert api.cache_dir == temp_cache_dir + + def test_cache_dir_setter_valid(self, temp_cache_dir): + """Test cache_dir setter with valid directory.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + + new_cache_dir = temp_cache_dir / "new_cache" + new_cache_dir.mkdir() + + api.cache_dir = new_cache_dir + assert api.cache_dir == new_cache_dir + + def test_cache_dir_setter_invalid(self, temp_cache_dir): + """Test cache_dir setter with invalid directory.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + + invalid_dir = temp_cache_dir / "nonexistent" + + with pytest.raises( + FileNotFoundError, match="Cache directory .* does not exist" + ): + api.cache_dir = invalid_dir + + @patch("tfbpapi.AbstractHfAPI.requests.get") + def test_get_dataset_size_success(self, mock_get, temp_cache_dir): + """Test successful dataset size retrieval.""" + mock_response = Mock() + mock_response.json.return_value = { + "size": {"dataset": {"num_bytes": 1024}}, + "partial": False, + } + mock_response.raise_for_status.return_value = None + mock_get.return_value = mock_response + + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + api._get_dataset_size() + + assert api.size is not None + assert not api.size.get("partial", True) + + @patch("tfbpapi.AbstractHfAPI.requests.get") + def test_get_dataset_size_partial(self, mock_get, temp_cache_dir): + """Test dataset size retrieval with partial results.""" + mock_response = Mock() + mock_response.json.return_value = { + "size": {"dataset": {"num_bytes": 1024}}, + "partial": True, + } + mock_response.raise_for_status.return_value = None + mock_get.return_value = mock_response + + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + api._get_dataset_size() + + assert api.size["partial"] is True # type: ignore[index] + assert "size_warning" in api.size["size"]["dataset"] # type: ignore[index] + + def test_repo_id_setter_success(self, mock_requests_get, temp_cache_dir): + """Test successful repo_id setting.""" + api = TestHfAPI(repo_id="initial/repo", cache_dir=temp_cache_dir) + + api.repo_id = "new/repo" + assert api.repo_id == "new/repo" + mock_requests_get.assert_called() + + @patch("tfbpapi.AbstractHfAPI.requests.get") + def test_repo_id_setter_failure(self, mock_get, temp_cache_dir): + """Test repo_id setting with API failure.""" + mock_get.side_effect = HTTPError("Repository not found") + + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + + # Should not raise, but should log error + api.repo_id = "nonexistent/repo" + assert api.repo_id == "nonexistent/repo" + + def test_get_dataset_size_mb(self, temp_cache_dir): + """Test dataset size calculation in MB.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + + # Test with no size data + assert api._get_dataset_size_mb() == float("inf") + + # Test with size data + api.size = { + "size": {"dataset": {"num_bytes_original_files": 2 * 1024 * 1024}} # 2MB + } + assert api._get_dataset_size_mb() == 2.0 + + def test_build_auth_headers(self, temp_cache_dir): + """Test authentication header building.""" + # Without token + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + api.token = None + assert api._build_auth_headers() == {} + + # With token + api.token = "test-token" + headers = api._build_auth_headers() + assert headers == {"Authorization": "Bearer test-token"} + + def test_ensure_str_paths(self, temp_cache_dir): + """Test path string conversion.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + + kwargs = { + "local_dir": Path("/some/path"), + "cache_dir": Path("/cache/path"), + "other_param": "unchanged", + } + + api._ensure_str_paths(kwargs) + + assert kwargs["local_dir"] == "/some/path" + assert kwargs["cache_dir"] == "/cache/path" + assert kwargs["other_param"] == "unchanged" + + def test_normalize_patterns(self, temp_cache_dir): + """Test pattern normalization.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + + kwargs = { + "allow_patterns": "*.txt", + "ignore_patterns": ["*.log", "*.tmp"], + "other_param": "unchanged", + } + + api._normalize_patterns(kwargs) + + assert kwargs["allow_patterns"] == ["*.txt"] + assert kwargs["ignore_patterns"] == ["*.log", "*.tmp"] + assert kwargs["other_param"] == "unchanged" + + def test_download_single_file(self, mock_hf_hub_download, temp_cache_dir): + """Test single file download.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + + result = api._download_single_file("test.txt") + + assert result == Path("/fake/path/to/file.txt") + mock_hf_hub_download.assert_called_once() + + # Check that correct arguments were passed + call_args = mock_hf_hub_download.call_args[1] + assert call_args["repo_id"] == "test/repo" + assert call_args["filename"] == "test.txt" + + def test_download_single_file_dry_run(self, mock_hf_hub_download, temp_cache_dir): + """Test single file download with dry run.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + + result = api._download_single_file("test.txt", dry_run=True) + + assert result == Path("dry_run_path") + mock_hf_hub_download.assert_not_called() + + def test_download_snapshot(self, mock_snapshot_download, temp_cache_dir): + """Test snapshot download.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + + result = api._download_snapshot(allow_patterns=["*.txt"]) + + assert result == Path("/fake/path/to/snapshot") + mock_snapshot_download.assert_called_once() + + def test_download_snapshot_dry_run(self, mock_snapshot_download, temp_cache_dir): + """Test snapshot download with dry run.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + + result = api._download_snapshot(dry_run=True, allow_patterns=["*.txt"]) + + assert result == Path("dry_run_path") + mock_snapshot_download.assert_not_called() + + def test_download_single_file_string(self, temp_cache_dir): + """Test download with single file as string.""" + # Create API instance by bypassing problematic initialization + api = TestHfAPI.__new__(TestHfAPI) # Create without calling __init__ + + # Manually set the required attributes + api._repo_id = "test/repo" + api.repo_type = "dataset" + api.token = None + api._cache_dir = temp_cache_dir + api.size = { + "size": {"dataset": {"num_bytes_original_files": 1024}} + } # Small size + api.logger = Mock() # Mock logger to avoid issues + + with patch("tfbpapi.AbstractHfAPI.hf_hub_download") as mock_hf_download: + # Configure the mock to return a fake path + mock_hf_download.return_value = "/fake/path/to/file.txt" + + result = api.download(files="test.txt", auto_download_threshold_mb=0) + + assert result == Path("/fake/path/to/file.txt") + mock_hf_download.assert_called_once() + + # Verify the call arguments + call_args = mock_hf_download.call_args[1] + assert call_args["repo_id"] == "test/repo" + assert call_args["filename"] == "test.txt" + + def test_download_single_file_list( + self, mock_hf_hub_download, temp_cache_dir, mock_dataset_size_call + ): + """Test download with single file as list.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + api.size = { + "size": {"dataset": {"num_bytes_original_files": 1024}} + } # Small size + + result = api.download(files=["test.txt"], auto_download_threshold_mb=0) + + assert result == Path("/fake/path/to/file.txt") + mock_hf_hub_download.assert_called_once() + + def test_download_multiple_files(self, mock_snapshot_download, temp_cache_dir): + """Test download with multiple files.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + api.size = { + "size": {"dataset": {"num_bytes_original_files": 1024}} + } # Small size + + result = api.download(files=["test1.txt", "test2.txt"]) + + assert result == Path("/fake/path/to/snapshot") + mock_snapshot_download.assert_called_once() + + def test_download_force_full(self, mock_snapshot_download, temp_cache_dir): + """Test download with force_full_download=True.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + api.size = { + "size": {"dataset": {"num_bytes_original_files": 1000 * 1024 * 1024}} + } # Large size + + result = api.download(force_full_download=True) + + assert result == Path("/fake/path/to/snapshot") + mock_snapshot_download.assert_called_once() + + def test_download_repo_too_large(self, temp_cache_dir): + """Test download with repo too large error.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + api.size = { + "size": {"dataset": {"num_bytes_original_files": 1000 * 1024 * 1024}} + } # 1GB + + with pytest.raises(RepoTooLargeError, match="Dataset size .* exceeds"): + api.download(auto_download_threshold_mb=10) + + def test_download_small_repo_auto(self, mock_snapshot_download, temp_cache_dir): + """Test download with small repo under threshold.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + api.size = { + "size": {"dataset": {"num_bytes_original_files": 5 * 1024 * 1024}} + } # 5MB + + result = api.download(auto_download_threshold_mb=10) + + assert result == Path("/fake/path/to/snapshot") + mock_snapshot_download.assert_called_once() + + def test_snapshot_path_property(self, temp_cache_dir): + """Test snapshot_path property getter and setter.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + + # Initially None + assert api.snapshot_path is None + + # Set to path + test_path = "/some/path" + api.snapshot_path = Path(test_path) + assert api.snapshot_path == Path(test_path) + + # Set to None + api.snapshot_path = None + assert api.snapshot_path is None + + def test_size_property(self, temp_cache_dir): + """Test size property getter and setter.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + + # Initially None + assert api.size is None + + # Set size data + size_data = {"size": {"dataset": {"num_bytes": 1024}}} + api.size = size_data + assert api.size == size_data + + def test_abstract_methods_implemented(self, temp_cache_dir): + """Test that abstract methods are properly implemented.""" + api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) + + # Test parse_datacard + result = api.parse_datacard() + assert result == {"test": "datacard"} + + # Test query + result = api.query() + assert result == {"test": "query"} + + def test_repo_too_large_error(self): + """Test RepoTooLargeError exception.""" + error = RepoTooLargeError("Test error message") + assert str(error) == "Test error message" + assert isinstance(error, ValueError) From e7fb52e3942d59a031f3668c40196144e72b203e Mon Sep 17 00:00:00 2001 From: chasem Date: Mon, 8 Sep 2025 14:41:46 -0500 Subject: [PATCH 28/49] removing windows-2019 from ci --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c340f25..8007bd3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,6 @@ jobs: ubuntu-24.04, macos-13, windows-2022, - windows-2019, ] python-version: ["3.11"] runs-on: ${{ matrix.os }} From 177d245ad8e765ed1c70c7852ebf9b80c01a3416 Mon Sep 17 00:00:00 2001 From: chasem Date: Wed, 10 Sep 2025 18:02:14 -0500 Subject: [PATCH 29/49] working on adding rank response functionality --- .pre-commit-config.yaml | 2 +- docs/AbstractAPI.md | 1 + docs/AbstractHfAPI.md | 1 + docs/AbstractRecordsAndFilesAPI.md | 1 + docs/AbstractRecordsOnlyAPI.md | 1 + docs/BindingAPI.md | 1 + docs/BindingConcatenatedAPI.md | 1 + docs/BindingManualQCAPI.md | 1 + docs/Cache.md | 1 + docs/CallingCardsBackgroundAPI.md | 1 + docs/DataSourceAPI.md | 1 + docs/ExpressionAPI.md | 1 + docs/ExpressionManualQCAPI.md | 2 + docs/FileFormatAPI.md | 1 + docs/GenomicFeatureAPI.md | 1 + docs/HfCacheManager.md | 1 + docs/ParamsDict.md | 1 + docs/PromoterSetAPI.md | 1 + docs/PromoterSetSigAPI.md | 1 + docs/RegulatorAPI.md | 1 + docs/index.md | 89 + docs/metric_arrays.md | 1 + docs/rank_transforms.md | 17 + docs/tutorials/database_interface.ipynb | 2266 +++++++++++++++++++ mkdocs.yml | 199 +- pyproject.toml | 8 + tfbpapi/HfQueryAPI.py | 363 +++ tfbpapi/HfRankResponse.py | 482 ++++ tfbpapi/IncrementalAnalysisDB.py | 340 +++ tfbpapi/tests/test_HfQueryAPI.py | 530 +++++ tfbpapi/tests/test_HfRankResponse.py | 505 +++++ tfbpapi/tests/test_IncrementalAnalysisDB.py | 341 +++ 32 files changed, 5110 insertions(+), 53 deletions(-) create mode 100644 docs/AbstractAPI.md create mode 100644 docs/AbstractHfAPI.md create mode 100644 docs/AbstractRecordsAndFilesAPI.md create mode 100644 docs/AbstractRecordsOnlyAPI.md create mode 100644 docs/BindingAPI.md create mode 100644 docs/BindingConcatenatedAPI.md create mode 100644 docs/BindingManualQCAPI.md create mode 100644 docs/Cache.md create mode 100644 docs/CallingCardsBackgroundAPI.md create mode 100644 docs/DataSourceAPI.md create mode 100644 docs/ExpressionAPI.md create mode 100644 docs/ExpressionManualQCAPI.md create mode 100644 docs/FileFormatAPI.md create mode 100644 docs/GenomicFeatureAPI.md create mode 100644 docs/HfCacheManager.md create mode 100644 docs/ParamsDict.md create mode 100644 docs/PromoterSetAPI.md create mode 100644 docs/PromoterSetSigAPI.md create mode 100644 docs/RegulatorAPI.md create mode 100644 docs/index.md create mode 100644 docs/metric_arrays.md create mode 100644 docs/rank_transforms.md create mode 100644 docs/tutorials/database_interface.ipynb create mode 100644 tfbpapi/HfQueryAPI.py create mode 100644 tfbpapi/HfRankResponse.py create mode 100644 tfbpapi/IncrementalAnalysisDB.py create mode 100644 tfbpapi/tests/test_HfQueryAPI.py create mode 100644 tfbpapi/tests/test_HfRankResponse.py create mode 100644 tfbpapi/tests/test_IncrementalAnalysisDB.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e8284d5..e45b42f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,4 +1,4 @@ -exclude: ^docs/|devcontainer.json|.*/snapshots/ +exclude: ^docs/|devcontainer.json|.*/snapshots/|mkdocs.yml default_stages: [commit] default_language_version: diff --git a/docs/AbstractAPI.md b/docs/AbstractAPI.md new file mode 100644 index 0000000..ada18a1 --- /dev/null +++ b/docs/AbstractAPI.md @@ -0,0 +1 @@ +::: tfbpapi.AbstractAPI.AbstractAPI diff --git a/docs/AbstractHfAPI.md b/docs/AbstractHfAPI.md new file mode 100644 index 0000000..45dea40 --- /dev/null +++ b/docs/AbstractHfAPI.md @@ -0,0 +1 @@ +::: tfbpapi.AbstractHfAPI.AbstractHfAPI diff --git a/docs/AbstractRecordsAndFilesAPI.md b/docs/AbstractRecordsAndFilesAPI.md new file mode 100644 index 0000000..395c787 --- /dev/null +++ b/docs/AbstractRecordsAndFilesAPI.md @@ -0,0 +1 @@ +::: tfbpapi.AbstractRecordsAndFilesAPI.AbstractRecordsAndFilesAPI diff --git a/docs/AbstractRecordsOnlyAPI.md b/docs/AbstractRecordsOnlyAPI.md new file mode 100644 index 0000000..fc0726a --- /dev/null +++ b/docs/AbstractRecordsOnlyAPI.md @@ -0,0 +1 @@ +::: tfbpapi.AbstractRecordsOnlyAPI.AbstractRecordsOnlyAPI diff --git a/docs/BindingAPI.md b/docs/BindingAPI.md new file mode 100644 index 0000000..7f4c555 --- /dev/null +++ b/docs/BindingAPI.md @@ -0,0 +1 @@ +::: tfbpapi.BindingAPI.BindingAPI diff --git a/docs/BindingConcatenatedAPI.md b/docs/BindingConcatenatedAPI.md new file mode 100644 index 0000000..4d68aea --- /dev/null +++ b/docs/BindingConcatenatedAPI.md @@ -0,0 +1 @@ +::: tfbpapi.BindingConcatenatedAPI.BindingConcatenatedAPI diff --git a/docs/BindingManualQCAPI.md b/docs/BindingManualQCAPI.md new file mode 100644 index 0000000..a29d3ca --- /dev/null +++ b/docs/BindingManualQCAPI.md @@ -0,0 +1 @@ +::: tfbpapi.BindingManualQCAPI.BindingManualQCAPI diff --git a/docs/Cache.md b/docs/Cache.md new file mode 100644 index 0000000..3c2ed52 --- /dev/null +++ b/docs/Cache.md @@ -0,0 +1 @@ +::: tfbpapi.Cache.Cache diff --git a/docs/CallingCardsBackgroundAPI.md b/docs/CallingCardsBackgroundAPI.md new file mode 100644 index 0000000..5f5c24a --- /dev/null +++ b/docs/CallingCardsBackgroundAPI.md @@ -0,0 +1 @@ +::: tfbpapi.CallingCardsBackgroundAPI.CallingCardsBackgroundAPI diff --git a/docs/DataSourceAPI.md b/docs/DataSourceAPI.md new file mode 100644 index 0000000..b50cc38 --- /dev/null +++ b/docs/DataSourceAPI.md @@ -0,0 +1 @@ +::: tfbpapi.DataSourceAPI.DataSourceAPI diff --git a/docs/ExpressionAPI.md b/docs/ExpressionAPI.md new file mode 100644 index 0000000..9397868 --- /dev/null +++ b/docs/ExpressionAPI.md @@ -0,0 +1 @@ +::: tfbpapi.ExpressionAPI.ExpressionAPI diff --git a/docs/ExpressionManualQCAPI.md b/docs/ExpressionManualQCAPI.md new file mode 100644 index 0000000..094b037 --- /dev/null +++ b/docs/ExpressionManualQCAPI.md @@ -0,0 +1,2 @@ +::: tfbpapi.ExpressionManualQCAPI.ExpressionManualQCAPI + diff --git a/docs/FileFormatAPI.md b/docs/FileFormatAPI.md new file mode 100644 index 0000000..12adc28 --- /dev/null +++ b/docs/FileFormatAPI.md @@ -0,0 +1 @@ +::: tfbpapi.FileFormatAPI.FileFormatAPI diff --git a/docs/GenomicFeatureAPI.md b/docs/GenomicFeatureAPI.md new file mode 100644 index 0000000..c66339c --- /dev/null +++ b/docs/GenomicFeatureAPI.md @@ -0,0 +1 @@ +::: tfbpapi.GenomicFeatureAPI.GenomicFeatureAPI diff --git a/docs/HfCacheManager.md b/docs/HfCacheManager.md new file mode 100644 index 0000000..89d5c77 --- /dev/null +++ b/docs/HfCacheManager.md @@ -0,0 +1 @@ +::: tfbpapi.HfCacheManager.HfCacheManager diff --git a/docs/ParamsDict.md b/docs/ParamsDict.md new file mode 100644 index 0000000..057048a --- /dev/null +++ b/docs/ParamsDict.md @@ -0,0 +1 @@ +::: tfbpapi.ParamsDict.ParamsDict diff --git a/docs/PromoterSetAPI.md b/docs/PromoterSetAPI.md new file mode 100644 index 0000000..b2c311c --- /dev/null +++ b/docs/PromoterSetAPI.md @@ -0,0 +1 @@ +::: tfbpapi.PromoterSetAPI.PromoterSetAPI diff --git a/docs/PromoterSetSigAPI.md b/docs/PromoterSetSigAPI.md new file mode 100644 index 0000000..f07120c --- /dev/null +++ b/docs/PromoterSetSigAPI.md @@ -0,0 +1 @@ +::: tfbpapi.PromoterSetSigAPI.PromoterSetSigAPI diff --git a/docs/RegulatorAPI.md b/docs/RegulatorAPI.md new file mode 100644 index 0000000..0217a92 --- /dev/null +++ b/docs/RegulatorAPI.md @@ -0,0 +1 @@ +::: tfbpapi.RegulatorAPI.RegulatorAPI diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..b58e762 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,89 @@ +# tfbpapi Documentation + +## Development Commands + +### Testing +- Run tests: `poetry run pytest` +- Run specific test: `poetry run pytest tfbpapi/tests/test_[module_name].py` +- Run tests with coverage: `poetry run pytest --cov=tfbpapi` + +### Linting and Formatting +- Run all pre-commit checks: `poetry run pre-commit run --all-files` +- Format code with Black: `poetry run black tfbpapi/` +- Sort imports with isort: `poetry run isort tfbpapi/` +- Type check with mypy: `poetry run mypy tfbpapi/` +- Lint with flake8: `poetry run flake8 tfbpapi/` + +### Installation +- Install dependencies: `poetry install` +- Install pre-commit hooks: `poetry run pre-commit install` + +## Architecture + +This is a Python package for interacting with django.tfbindingandmodeling.com APIs. The codebase follows an object-oriented architecture with abstract base classes providing common functionality. + +### Core Architecture Classes + +- **AbstractAPI** (`tfbpapi/AbstractAPI.py`): Base class for all API clients with token authentication, caching (via `Cache` class), parameter validation (via `ParamsDict` class), and abstract CRUD methods. + +- **AbstractRecordsAndFilesAPI** (`tfbpapi/AbstractRecordsAndFilesAPI.py`): Extends `AbstractAPI` for endpoints that serve both record metadata and associated data files. Handles tarball extraction, CSV parsing, and file caching. + +- **AbstractRecordsOnlyAPI** (`tfbpapi/AbstractRecordsOnlyAPI.py`): Extends `AbstractAPI` for endpoints that only serve record metadata without file storage. + +- **AbstractHfAPI** (`tfbpapi/AbstractHfAPI.py`): Abstract base for Hugging Face Hub integration, providing repository management functionality. + +### Concrete API Classes + +All concrete API classes inherit from either `AbstractRecordsAndFilesAPI` or `AbstractRecordsOnlyAPI`: + +- `BindingAPI`, `ExpressionAPI`, `PromoterSetAPI` - Record and file APIs +- `DataSourceAPI`, `RegulatorAPI`, `GenomicFeatureAPI` - Records only APIs + +### Utility Classes + +- **Cache** (`tfbpapi/Cache.py`): TTL-based caching for API responses +- **ParamsDict** (`tfbpapi/ParamsDict.py`): Parameter validation against allowed keys +- **HfCacheManager** (`tfbpapi/HfCacheManager.py`): Hugging Face cache cleanup utilities + +### Data Processing Utilities + +- **metric_arrays** (`tfbpapi/metric_arrays.py`): Array-based metric calculations +- **rank_transforms** (`tfbpapi/rank_transforms.py`): Statistical rank transformation functions + +## Configuration + +- Uses Poetry for dependency management +- Python 3.11 required +- Black formatter with 88-character line length +- Pre-commit hooks include Black, isort, flake8, mypy, and various file checks +- pytest with snapshot testing support +- Environment variables: `BASE_URL`, `TOKEN`, `HF_TOKEN`, `HF_CACHE_DIR` + +## Testing Patterns + +- Tests use pytest with async support (`pytest-asyncio`) +- Snapshot testing with `pytest-snapshot` for API response validation +- Test fixtures in `tfbpapi/tests/conftest.py` +- Mock HTTP responses using `aioresponses` and `responses` libraries + +### mkdocs + +#### Commands + +After building the environment with poetry, you can use `poetry run` or a poetry shell +to execute the following: + +* `mkdocs new [dir-name]` - Create a new project. +* `mkdocs serve` - Start the live-reloading docs server. +* `mkdocs build` - Build the documentation site. +* `mkdocs -h` - Print help message and exit. + +#### Project layout + + mkdocs.yml # The configuration file. + docs/ + index.md # The documentation homepage. + ... # Other markdown pages, images and other files. + +To update the gh-pages documentation, use `poetry run mkdocs gh-deply` + diff --git a/docs/metric_arrays.md b/docs/metric_arrays.md new file mode 100644 index 0000000..0b78beb --- /dev/null +++ b/docs/metric_arrays.md @@ -0,0 +1 @@ +::: tfbpapi.metric_arrays.metric_arrays diff --git a/docs/rank_transforms.md b/docs/rank_transforms.md new file mode 100644 index 0000000..a3b55d4 --- /dev/null +++ b/docs/rank_transforms.md @@ -0,0 +1,17 @@ +

Rank Transforms

+ +

Shifted Negative Log Ranks

+ +::: tfbpapi.rank_transforms.shifted_negative_log_ranks + +

Stable Rank

+ +::: tfbpapi.rank_transforms.stable_rank + +

Rank by p-value

+ +::: tfbpapi.rank_transforms.rank_by_pvalue + +

Transform

+ +::: tfbpapi.rank_transforms.transform \ No newline at end of file diff --git a/docs/tutorials/database_interface.ipynb b/docs/tutorials/database_interface.ipynb new file mode 100644 index 0000000..79e41f7 --- /dev/null +++ b/docs/tutorials/database_interface.ipynb @@ -0,0 +1,2266 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# The Database Interface Classes\n", + "\n", + "For each API endpoint exposed in the Django app, there is a corresponding class that\n", + "provide methods to execute CRUD operations asynchronously.\n", + "\n", + "There are two types of API endpoints -- those that contain only records data, and \n", + "those that store both records and pointers to files.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Connecting to the Database\n", + "\n", + "The database currently runs on HTCF service partition. This is a single\n", + "node with 8 CPU and 30 GB that is meant for long running low resource jobs.\n", + "The components that need to run are a postgres database, a redis instance and the\n", + "django app. As long as these components are running on the service partition, \n", + "you can connect via an ssh tunnel with:\n", + "\n", + "```bash\n", + "ssh username@login.htcf.wustl.edu -N -L 8001:n240:8000\n", + "```\n", + "\n", + "where the `8001:n240:8000` takes the form of `local_port:cluster_node:app_port`. The\n", + "django app will always be served on port `8000`, and `n240` is the only service\n", + "partition node. You may choose a different local port.\n", + "\n", + "If you do this and cannot connect, let me know and I'll check the status of the jobs \n", + "on the cluster.\n", + "\n", + "### Database username and password\n", + "\n", + "Once you have a tunnel, you can access the database frontend at `127.0.0.1:8001`\n", + "(or a different local port, if you changed that number). If you haven't already\n", + "signed up, you'll need to click the 'sign up' button and follow\n", + "the instructions. The e-mail server is not hooked up at the moment, so when it says\n", + "\"see the e-mail\", send a slack message and let me know. I'll give you a link to\n", + "complete the sign up process. After that, you can just use the \"sign in\" button.\n", + "\n", + "For computational tasks, including generating rank response data, celery workers must\n", + "be launched on the HTCF general partition. There is currently a script that is meant to\n", + "monitor the redis queue and launch/kill these workers automatically, but this\n", + "functionality is new and largely untested. You can monitor the workers/tasks if you\n", + "create another tunnel with:\n", + "\n", + "```bash\n", + "ssh username@login.htcf.wustl.edu -N -L 8002:n240:5555\n", + "```\n", + "You'd access this dashboard at `127.0.0.1:5555`\n", + "\n", + "The login is currently:\n", + "\n", + "```raw\n", + "username: \"flower\"\n", + "password: \"daisy\"\n", + "```\n", + "(yes, really -- the security comes from the fact that you need to login with HTCF)\n", + "\n", + "### Configuring the Database Interface Classes\n", + "\n", + "The database classes expect the following environmental variables to be set. \n", + "\n", + "```raw\n", + "BASE_URL='http://127.0.0.1:8001'\n", + "TOKEN=''\n", + "BINDING_URL='http://127.0.0.1:8001/api/binding'\n", + "BINDINGMANUALQC_URL='http://127.0.0.1:8001/api/bindingmanualqc'\n", + "CALLINGCARDSBACKGROUND_URL='http://127.0.0.1:8001/api/callingcardsbackground'\n", + "DATASOURCE_URL='http://127.0.0.1:8001/api/datasource'\n", + "EXPRESSION_URL='http://127.0.0.1:8001/api/expression'\n", + "EXPRESSIONMANUALQC_URL='http://127.0.0.1:8001/api/expressionmanualqc'\n", + "FILEFORMAT_URL='http://127.0.0.1:8001/api/fileformat'\n", + "GENOMICFEATURE_URL='http://127.0.0.1:8001/api/genomicfeature'\n", + "PROMOTERSET_URL='http://127.0.0.1:8001/api/promoterset'\n", + "PROMOTERSETSIG_URL='http://127.0.0.1:8001/api/promotersetsig'\n", + "REGULATOR_URL='http://127.0.0.1:8001/api/regulator'\n", + "```\n", + "\n", + "This can be achieved in the package during development with a `.env` file at the\n", + "top most level of the package. The `.env` file is loaded in the package `__init__.py`.\n", + "\n", + "If you are importing `yeastdnnexplorer` into a different environment, then you'll \n", + "need to add the package `dotenv` and execute `load_dotenv(dotenv_path=env_path)`. If\n", + "the `.env` file is in the same `PWD` in which you execute that command, there is no\n", + "need to specify a path.\n", + "\n", + "### Token Authentication\n", + "\n", + "Once you have a username and password to the database, you can retrieve your token. \n", + "Make sure that you put this token, at least, in a `.env` file, and make sure that \n", + "`.env` file is in your `.gitignore`.\n", + "\n", + "Alternatively, you could retrieve and store in memory the token at the beginning of \n", + "each session -- this is more secure if you are not using a `.env` file. \n", + "\n", + "The `.env` file is already in the `yeastddnexplorer` `.gitignore`\n", + "\n", + "```bash\n", + "curl -X 'POST' \\\n", + " 'http://127.0.0.1:8001/auth-token/' \\\n", + " -H 'accept: application/json' \\\n", + " -H 'Content-Type: application/json' \\\n", + " -d '{\n", + " \"username\": \"username\",\n", + " \"password\": \"password\"\n", + "}'\n", + "```\n", + "\n", + "Or with python:\n", + "\n", + "```python\n", + "import requests\n", + "\n", + "url = \"http://127.0.0.1:8001/auth-token/\"\n", + "headers = {\n", + " \"accept\": \"application/json\",\n", + " \"Content-Type\": \"application/json\",\n", + "}\n", + "data = {\n", + " \"username\": \"username\",\n", + " \"password\": \"password\",\n", + "}\n", + "\n", + "response = requests.post(url, json=data, headers=headers)\n", + "print(response.text)\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Using the Interface Classes" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from yeastdnnexplorer.interface import *\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Records Only Endpoints\n", + "\n", + "The records only endpoints are:\n", + "\n", + "- BindingManualQC\n", + "\n", + "- DataSource\n", + "\n", + "- ExpressionManualQC\n", + "\n", + "- FileFormat\n", + "\n", + "- GenomicFeature\n", + "\n", + "- PromoterSetSig\n", + "\n", + "- Regulator\n", + "\n", + "When the `read()` method is called on the corresponding API classes, a dataframe will\n", + "be returned in the response.\n", + "\n", + "All of the `read()` methods, for both types of API endpoints, return the result of\n", + "a callable. By default, the callable returns a dictionary with two keys: `metadata` and\n", + "`data`. For response only tables, the `metadata` value will be the records from the\n", + "database as a pandas dataframe and the `data` will be `None.\n", + "\n", + "### Example -- RegulatorAPI" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
iduploadermodifierregulator_locus_tagregulator_symbolupload_datemodified_dateunder_developmentnotesgenomicfeature
01chasemchasemYAL051WOAF12024-07-012024-07-01T12:47:18.619129-05:00Falsenone24
12chasemchasemYBL103CRTG32024-07-012024-07-01T12:47:19.667722-05:00Falsenone140
23chasemchasemYBL066CSEF12024-07-012024-07-01T12:47:20.523161-05:00Falsenone186
34chasemchasemYBL054WTOD62024-07-012024-07-01T12:47:21.309606-05:00Falsenone199
45chasemchasemYBL052CSAS32024-07-012024-07-01T12:47:22.161007-05:00Falsenone201
.................................
18091810chasemchasemYOR262WGPN22024-07-012024-07-01T14:14:36.164403-05:00Falsenone6387
18101811chasemchasemYPR190CRPC822024-07-012024-07-01T14:14:38.921261-05:00Falsenone7070
18111812chasemchasemYPL228WCET12024-07-012024-07-01T14:15:51.518999-05:00Falsenone6603
18121813chasemchasemYKL049CCSE42024-07-012024-07-01T14:15:56.555122-05:00Falsenone4083
18131814chasemchasemYMR168CCEP32024-07-012024-07-01T14:22:14.060524-05:00Falsenone5258
\n", + "

1814 rows × 10 columns

\n", + "
" + ], + "text/plain": [ + " id uploader modifier regulator_locus_tag regulator_symbol upload_date \\\n", + "0 1 chasem chasem YAL051W OAF1 2024-07-01 \n", + "1 2 chasem chasem YBL103C RTG3 2024-07-01 \n", + "2 3 chasem chasem YBL066C SEF1 2024-07-01 \n", + "3 4 chasem chasem YBL054W TOD6 2024-07-01 \n", + "4 5 chasem chasem YBL052C SAS3 2024-07-01 \n", + "... ... ... ... ... ... ... \n", + "1809 1810 chasem chasem YOR262W GPN2 2024-07-01 \n", + "1810 1811 chasem chasem YPR190C RPC82 2024-07-01 \n", + "1811 1812 chasem chasem YPL228W CET1 2024-07-01 \n", + "1812 1813 chasem chasem YKL049C CSE4 2024-07-01 \n", + "1813 1814 chasem chasem YMR168C CEP3 2024-07-01 \n", + "\n", + " modified_date under_development notes \\\n", + "0 2024-07-01T12:47:18.619129-05:00 False none \n", + "1 2024-07-01T12:47:19.667722-05:00 False none \n", + "2 2024-07-01T12:47:20.523161-05:00 False none \n", + "3 2024-07-01T12:47:21.309606-05:00 False none \n", + "4 2024-07-01T12:47:22.161007-05:00 False none \n", + "... ... ... ... \n", + "1809 2024-07-01T14:14:36.164403-05:00 False none \n", + "1810 2024-07-01T14:14:38.921261-05:00 False none \n", + "1811 2024-07-01T14:15:51.518999-05:00 False none \n", + "1812 2024-07-01T14:15:56.555122-05:00 False none \n", + "1813 2024-07-01T14:22:14.060524-05:00 False none \n", + "\n", + " genomicfeature \n", + "0 24 \n", + "1 140 \n", + "2 186 \n", + "3 199 \n", + "4 201 \n", + "... ... \n", + "1809 6387 \n", + "1810 7070 \n", + "1811 6603 \n", + "1812 4083 \n", + "1813 5258 \n", + "\n", + "[1814 rows x 10 columns]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "regulator = RegulatorAPI()\n", + "\n", + "result = await regulator.read()\n", + "result.get(\"metadata\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Record and File Endpoints\n", + "\n", + "The record and file endpoints are the following:\n", + "\n", + "- CallingCardsBackground\n", + "\n", + "- Expression\n", + "\n", + "- PromoterSet\n", + "\n", + "- PromoterSetSig\n", + "\n", + "- RankResponse *\n", + "\n", + "The default `read()` method is the same as the Records only Endpoint API classes.\n", + "However, there is an additional argument, `retrieve_files` which if set to `True`\n", + "will retrieve the file for which each record provides metadata. The return value of\n", + "`read()` is again a callable, and by default the `data` key will store a dictionary\n", + "where the keys correspond to the `id` column in the `metadata`.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
iduploadermodifiersingle_bindingcomposite_bindingfileformatbackgroundupload_datemodified_datefilepromotersourceregulator_symbolregulator_locus_tagconditionrank_recalldata_usablebackground_name
01chasemchasem1.0NaN1NaN2024-07-012024-07-01T13:51:48.611781-05:00https://yeastregulatorydb-htcf-data.s3.amazona...NaNharbison_chipOAF1YAL051WYPDunreviewedunreviewedNaN
12chasemchasem2.0NaN1NaN2024-07-012024-07-01T13:51:49.643452-05:00https://yeastregulatorydb-htcf-data.s3.amazona...NaNharbison_chipPDR3YBL005WYPDunreviewedunreviewedNaN
23chasemchasem3.0NaN1NaN2024-07-012024-07-01T13:51:50.744384-05:00https://yeastregulatorydb-htcf-data.s3.amazona...NaNharbison_chipHIR1YBL008WYPDunreviewedunreviewedNaN
34chasemchasem4.0NaN1NaN2024-07-012024-07-01T13:51:51.507918-05:00https://yeastregulatorydb-htcf-data.s3.amazona...NaNharbison_chipHAP3YBL021CYPDunreviewedunreviewedNaN
45chasemchasem5.0NaN1NaN2024-07-012024-07-01T13:51:52.277595-05:00https://yeastregulatorydb-htcf-data.s3.amazona...NaNharbison_chipTOD6YBL054WYPDunreviewedunreviewedNaN
.........................................................
22377011adminadminNaN145.051.02024-07-302024-07-30T16:39:36.457965-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4.0mitra_ccCBF1YJR060WNaNunreviewedpassad1
22387012adminadminNaN146.051.02024-07-302024-07-30T16:39:36.848168-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4.0mitra_ccGCN4YEL009CNaNunreviewedpassad1
22397013adminadminNaN147.051.02024-07-302024-07-30T16:39:37.234144-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4.0mitra_ccOAF1YAL051WNaNunreviewedpassad1
22407014adminadminNaN148.051.02024-07-302024-07-30T16:39:38.547155-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4.0mitra_ccYOX1YML027WNaNunreviewedpassad1
22417015adminadminNaN149.051.02024-07-302024-07-30T16:39:39.713590-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4.0mitra_ccLEU3YLR451WNaNunreviewedpassad1
\n", + "

2242 rows × 18 columns

\n", + "
" + ], + "text/plain": [ + " id uploader modifier single_binding composite_binding fileformat \\\n", + "0 1 chasem chasem 1.0 NaN 1 \n", + "1 2 chasem chasem 2.0 NaN 1 \n", + "2 3 chasem chasem 3.0 NaN 1 \n", + "3 4 chasem chasem 4.0 NaN 1 \n", + "4 5 chasem chasem 5.0 NaN 1 \n", + "... ... ... ... ... ... ... \n", + "2237 7011 admin admin NaN 145.0 5 \n", + "2238 7012 admin admin NaN 146.0 5 \n", + "2239 7013 admin admin NaN 147.0 5 \n", + "2240 7014 admin admin NaN 148.0 5 \n", + "2241 7015 admin admin NaN 149.0 5 \n", + "\n", + " background upload_date modified_date \\\n", + "0 NaN 2024-07-01 2024-07-01T13:51:48.611781-05:00 \n", + "1 NaN 2024-07-01 2024-07-01T13:51:49.643452-05:00 \n", + "2 NaN 2024-07-01 2024-07-01T13:51:50.744384-05:00 \n", + "3 NaN 2024-07-01 2024-07-01T13:51:51.507918-05:00 \n", + "4 NaN 2024-07-01 2024-07-01T13:51:52.277595-05:00 \n", + "... ... ... ... \n", + "2237 1.0 2024-07-30 2024-07-30T16:39:36.457965-05:00 \n", + "2238 1.0 2024-07-30 2024-07-30T16:39:36.848168-05:00 \n", + "2239 1.0 2024-07-30 2024-07-30T16:39:37.234144-05:00 \n", + "2240 1.0 2024-07-30 2024-07-30T16:39:38.547155-05:00 \n", + "2241 1.0 2024-07-30 2024-07-30T16:39:39.713590-05:00 \n", + "\n", + " file promoter \\\n", + "0 https://yeastregulatorydb-htcf-data.s3.amazona... NaN \n", + "1 https://yeastregulatorydb-htcf-data.s3.amazona... NaN \n", + "2 https://yeastregulatorydb-htcf-data.s3.amazona... NaN \n", + "3 https://yeastregulatorydb-htcf-data.s3.amazona... NaN \n", + "4 https://yeastregulatorydb-htcf-data.s3.amazona... NaN \n", + "... ... ... \n", + "2237 https://yeastregulatorydb-htcf-data.s3.amazona... 4.0 \n", + "2238 https://yeastregulatorydb-htcf-data.s3.amazona... 4.0 \n", + "2239 https://yeastregulatorydb-htcf-data.s3.amazona... 4.0 \n", + "2240 https://yeastregulatorydb-htcf-data.s3.amazona... 4.0 \n", + "2241 https://yeastregulatorydb-htcf-data.s3.amazona... 4.0 \n", + "\n", + " source regulator_symbol regulator_locus_tag condition \\\n", + "0 harbison_chip OAF1 YAL051W YPD \n", + "1 harbison_chip PDR3 YBL005W YPD \n", + "2 harbison_chip HIR1 YBL008W YPD \n", + "3 harbison_chip HAP3 YBL021C YPD \n", + "4 harbison_chip TOD6 YBL054W YPD \n", + "... ... ... ... ... \n", + "2237 mitra_cc CBF1 YJR060W NaN \n", + "2238 mitra_cc GCN4 YEL009C NaN \n", + "2239 mitra_cc OAF1 YAL051W NaN \n", + "2240 mitra_cc YOX1 YML027W NaN \n", + "2241 mitra_cc LEU3 YLR451W NaN \n", + "\n", + " rank_recall data_usable background_name \n", + "0 unreviewed unreviewed NaN \n", + "1 unreviewed unreviewed NaN \n", + "2 unreviewed unreviewed NaN \n", + "3 unreviewed unreviewed NaN \n", + "4 unreviewed unreviewed NaN \n", + "... ... ... ... \n", + "2237 unreviewed pass ad1 \n", + "2238 unreviewed pass ad1 \n", + "2239 unreviewed pass ad1 \n", + "2240 unreviewed pass ad1 \n", + "2241 unreviewed pass ad1 \n", + "\n", + "[2242 rows x 18 columns]" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# First, retrieve only the records -- you'll want to filter these results down before\n", + "# retrieving the files most likely\n", + "pss_api = PromoterSetSigAPI()\n", + "result = await pss_api.read()\n", + "result.get(\"metadata\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Filtering\n", + "\n", + "All API classes have a `params` attribute which stores the filtering parameters\n", + "which will be applied to the HTTP requests." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "pss_api.push_params({\"regulator_symbol\": \"GZF3\",\n", + " \"workflow\": \"nf_core_callingcards_1_0_0\",\n", + " \"data_usable\": \"pass\"})" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "ParamsDict({'regulator_symbol': 'GZF3', 'workflow': 'nf_core_callingcards_1_0_0', 'data_usable': 'pass'})" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pss_api.params" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Retrieving files from a Records and Files Object\n", + "\n", + "To retrieve files from a Records and Files endpoint object, do the following:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
iduploadermodifiersingle_bindingcomposite_bindingfileformatbackgroundupload_datemodified_datefilepromotersourceregulator_symbolregulator_locus_tagconditionrank_recalldata_usablebackground_name
06577adminadmin1837NaN512024-07-012024-07-01T16:43:50.145871-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccGZF3YJL110Cunknownpasspassad1
16580adminadmin1841NaN512024-07-012024-07-01T16:43:50.968078-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccGZF3YJL110Cunknownpasspassad1
26642adminadmin1902NaN512024-07-012024-07-01T16:43:54.969507-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccGZF3YJL110Cunknownpasspassad1
36651adminadmin1911NaN512024-07-012024-07-01T16:43:55.326651-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccGZF3YJL110Cunknownpasspassad1
46717adminadmin1960NaN512024-07-012024-07-01T16:44:00.500038-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccGZF3YJL110Cunknownpasspassad1
\n", + "
" + ], + "text/plain": [ + " id uploader modifier single_binding composite_binding fileformat \\\n", + "0 6577 admin admin 1837 NaN 5 \n", + "1 6580 admin admin 1841 NaN 5 \n", + "2 6642 admin admin 1902 NaN 5 \n", + "3 6651 admin admin 1911 NaN 5 \n", + "4 6717 admin admin 1960 NaN 5 \n", + "\n", + " background upload_date modified_date \\\n", + "0 1 2024-07-01 2024-07-01T16:43:50.145871-05:00 \n", + "1 1 2024-07-01 2024-07-01T16:43:50.968078-05:00 \n", + "2 1 2024-07-01 2024-07-01T16:43:54.969507-05:00 \n", + "3 1 2024-07-01 2024-07-01T16:43:55.326651-05:00 \n", + "4 1 2024-07-01 2024-07-01T16:44:00.500038-05:00 \n", + "\n", + " file promoter source \\\n", + "0 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + "1 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + "2 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + "3 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + "4 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + "\n", + " regulator_symbol regulator_locus_tag condition rank_recall data_usable \\\n", + "0 GZF3 YJL110C unknown pass pass \n", + "1 GZF3 YJL110C unknown pass pass \n", + "2 GZF3 YJL110C unknown pass pass \n", + "3 GZF3 YJL110C unknown pass pass \n", + "4 GZF3 YJL110C unknown pass pass \n", + "\n", + " background_name \n", + "0 ad1 \n", + "1 ad1 \n", + "2 ad1 \n", + "3 ad1 \n", + "4 ad1 " + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# note that retrieve_files is set to True\n", + "result = await pss_api.read(retrieve_files = True)\n", + "\n", + "# the metadata slot is the same as before\n", + "result.get(\"metadata\")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "# but now the data slot is a dictionary where the `id` are keys and the values\n", + "# are the files parsed into pandas dataframes\n", + "result.get(\"data\").get(\"6568\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Filtering on multiple items\n", + "\n", + "Some filters, and eventually all, will accept multiple arguments as a comma\n", + "separated string without spaces. For example:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "regulator_symbol: GZF3,RTG3, workflow: nf_core_callingcards_1_0_0, data_usable: pass\n" + ] + } + ], + "source": [ + "pss_api.push_params({\"regulator_symbol\": \"GZF3,RTG3\"})\n", + "\n", + "print(pss_api.params)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Parameters can be removed one by one" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "regulator_symbol: GZF3,RTG3, workflow: nf_core_callingcards_1_0_0, data_usable: pass\n" + ] + } + ], + "source": [ + "print(pss_api.params)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "regulator_symbol: GZF3,RTG3, workflow: nf_core_callingcards_1_0_0\n" + ] + } + ], + "source": [ + "pss_api.pop_params('data_usable')\n", + "\n", + "print(pss_api.params)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "or cleared entirely" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "pss_api.pop_params(None)\n", + "\n", + "print(pss_api.params)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Another example with Expression\n", + "\n", + "This is used to get an expression_id to use in the RankResponseAPI() below" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
iduploadermodifierregulator_idregulator_locus_tagregulator_symbolsource_nameassayupload_datemodified_date...replicatecontrolmechanismrestrictiontimefilenotesregulatorsourcepromotersetsig_processing
02516chasemchasem3578YJL110CGZF3mcisaac_oeoverexpression2024-07-012024-07-01T13:36:13.814201-05:00...1undefinedgevN15.0https://yeastregulatorydb-htcf-data.s3.amazona...strain_id:SMY156n ; date:201501011357False
12510chasemchasem3578YJL110CGZF3mcisaac_oeoverexpression2024-07-012024-07-01T13:36:08.810276-05:00...1undefinedgevP15.0https://yeastregulatorydb-htcf-data.s3.amazona...strain_id:SMY156 ; date:201501011357False
\n", + "

2 rows × 22 columns

\n", + "
" + ], + "text/plain": [ + " id uploader modifier regulator_id regulator_locus_tag regulator_symbol \\\n", + "0 2516 chasem chasem 3578 YJL110C GZF3 \n", + "1 2510 chasem chasem 3578 YJL110C GZF3 \n", + "\n", + " source_name assay upload_date modified_date \\\n", + "0 mcisaac_oe overexpression 2024-07-01 2024-07-01T13:36:13.814201-05:00 \n", + "1 mcisaac_oe overexpression 2024-07-01 2024-07-01T13:36:08.810276-05:00 \n", + "\n", + " ... replicate control mechanism restriction time \\\n", + "0 ... 1 undefined gev N 15.0 \n", + "1 ... 1 undefined gev P 15.0 \n", + "\n", + " file \\\n", + "0 https://yeastregulatorydb-htcf-data.s3.amazona... \n", + "1 https://yeastregulatorydb-htcf-data.s3.amazona... \n", + "\n", + " notes regulator source \\\n", + "0 strain_id:SMY156n ; date:20150101 135 7 \n", + "1 strain_id:SMY156 ; date:20150101 135 7 \n", + "\n", + " promotersetsig_processing \n", + "0 False \n", + "1 False \n", + "\n", + "[2 rows x 22 columns]" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "expression = ExpressionAPI()\n", + "\n", + "expression.push_params({\"regulator_symbol\": \"GZF3\",\n", + " \"lab\": \"mcisaac\",\n", + " \"time\": \"15\"})\n", + "\n", + "expression_res = await expression.read()\n", + "\n", + "expression_res.get(\"metadata\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Rank Response\n", + "\n", + "The rank response endpoint is slightly different than the others. It is implemented\n", + "asynchronously on the database side, and will run many tasks simultaneously. As such,\n", + "it uses `submit()` and `retrieve()` methods.\n", + "\n", + "Additionally, it is a POST request and all parameters are passed in a json list.\n", + "If you include, for a given dictionary, multiple items to promoetersetsig_ids and/or\n", + "expression_ids, those datasets will be aggregated prior to calculating the rank\n", + "response. The current parameters that may be passed for each are:\n", + "\n", + "- promotersetsig_ids: a list of promotersetsig_ids. If more than 1, then the data\n", + "will be aggregated prior to calcuating rank response\n", + "- expression_ids: a list of expression_ids. If more than 1, then the data\n", + "will be aggregated prior to calcuating rank response\n", + "- expression_effect_colname: name of the column to use for the rank response effect\n", + "- expression_effect_threshold: The threshold to use on abs(effect) to label responsive/\n", + "unresponsive genes\n", + "- expression_pvalue_threshold: the threshold to use below which to label responsive/\n", + "unresponsive genes\n", + "- rank_bin_size: the size of the bins by which rank response is summarized.\n", + "- rank_by_binding_effect: if this is \"true\", then rank by the binding effect first\n", + "rather than pvalue. This is used for harbison_chip and mcisaac_oe\n", + "- summarize_by_rank_bin: if this is set to false, the unsummarized rank response\n", + "(merged binding/response) is returned" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "rr_api = RankResponseAPI()\n", + "\n", + "data = [\n", + " {\n", + " \"promotersetsig_ids\": [\"5555\"],\n", + " \"expression_ids\": [\"2510\"],\n", + " \"rank_by_binding_effect\": \"true\",\n", + " }\n", + "]\n", + "\n", + "group_id = await rr_api.submit(post_dict=data)\n", + "\n", + "result = await rr_api.retrieve(group_id)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
regulator_symbolpromotersetsig_idsexpression_idsn_responsivetotal_expression_genesid
0GZF3555525108106175.0a46a15db-8318-476e-80aa-af5588909eef
\n", + "
" + ], + "text/plain": [ + " regulator_symbol promotersetsig_ids expression_ids n_responsive \\\n", + "0 GZF3 5555 2510 810 \n", + "\n", + " total_expression_genes id \n", + "0 6175.0 a46a15db-8318-476e-80aa-af5588909eef " + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "result.get(\"metadata\")" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
rank_binn_responsive_in_rankrandomn_successesresponse_ratiopvalueci_lowerci_upper
0520.13117420.4000000.1312110.0527450.853367
11030.13117450.5000000.0055100.1870860.812914
21540.13117490.6000000.0000270.3228700.836636
32000.13117490.4500000.0004900.2305780.684722
42520.131174110.4400000.0001490.2440240.650718
53010.131174120.4000000.0002240.2265580.593965
63510.131174130.3714290.0002960.2147320.550769
74000.131174130.3250000.0012780.1857290.491295
84510.131174140.3111110.0013630.1816590.466491
95000.131174140.2800000.0049460.1623110.424905
105510.131174150.2727270.0043780.1613800.409619
116000.131174150.2500000.0115260.1471860.378596
\n", + "
" + ], + "text/plain": [ + " rank_bin n_responsive_in_rank random n_successes response_ratio \\\n", + "0 5 2 0.131174 2 0.400000 \n", + "1 10 3 0.131174 5 0.500000 \n", + "2 15 4 0.131174 9 0.600000 \n", + "3 20 0 0.131174 9 0.450000 \n", + "4 25 2 0.131174 11 0.440000 \n", + "5 30 1 0.131174 12 0.400000 \n", + "6 35 1 0.131174 13 0.371429 \n", + "7 40 0 0.131174 13 0.325000 \n", + "8 45 1 0.131174 14 0.311111 \n", + "9 50 0 0.131174 14 0.280000 \n", + "10 55 1 0.131174 15 0.272727 \n", + "11 60 0 0.131174 15 0.250000 \n", + "\n", + " pvalue ci_lower ci_upper \n", + "0 0.131211 0.052745 0.853367 \n", + "1 0.005510 0.187086 0.812914 \n", + "2 0.000027 0.322870 0.836636 \n", + "3 0.000490 0.230578 0.684722 \n", + "4 0.000149 0.244024 0.650718 \n", + "5 0.000224 0.226558 0.593965 \n", + "6 0.000296 0.214732 0.550769 \n", + "7 0.001278 0.185729 0.491295 \n", + "8 0.001363 0.181659 0.466491 \n", + "9 0.004946 0.162311 0.424905 \n", + "10 0.004378 0.161380 0.409619 \n", + "11 0.011526 0.147186 0.378596 " + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "result.get(\"data\").get(result.get(\"metadata\").id[0])" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "df = result.get(\"data\").get(result.get(\"metadata\").id[0]).iloc[1:50,:]\n", + "\n", + "# Plot\n", + "plt.figure(figsize=(10, 6))\n", + "plt.plot(df['rank_bin'], df['response_ratio'], marker='o')\n", + "plt.title('GZF3 - promotersetsig_id 6577. McIsaac 15 2510')\n", + "plt.xlabel('Rank Bin')\n", + "plt.ylabel('Response Ratio')\n", + "plt.title('Response Ratio vs Rank Bin')\n", + "plt.grid(True)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# CallingCards Aggregated Data\n", + "\n", + "For regulators which have data generated in the Brentlab and processed through the\n", + "nf-core/callingcards:1.0.0 workflow, if that data has been manually (or eventually \n", + "automatically) QC reviewed,\n", + "*and if there are at least 2 samples which are labeled as data_usable*, then there will\n", + "exist a BindingConcatenated record to which both a BindingManualQC record *and* a\n", + "PromoterSetSig record are foreign keyed.\n", + "\n", + "To view the set of samples for which there is aggregated data, you may do the following:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "pss_api.pop_params(None)\n", + "\n", + "pss_api.push_params({\"source_name\": \"brent_nf_cc\", \"aggregated\": \"true\"})\n", + "\n", + "callingcards_aggregated_meta_res = await pss_api.read()\n", + "\n", + "callingcards_aggregated_meta_df = callingcards_aggregated_meta_res.get(\"metadata\")" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
iduploadermodifiersingle_bindingcomposite_bindingfileformatbackgroundupload_datemodified_datefilepromotersourceregulator_symbolregulator_locus_tagrank_recalldata_usablebackground_name
06867adminadminNaN6512024-07-062024-07-06T11:08:33.125644-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccWTM1YOR230Wpasspassad1
16868adminadminNaN3512024-07-062024-07-06T11:08:33.125640-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccMIG2YGL209Wpasspassad1
26869adminadminNaN1512024-07-062024-07-06T11:08:33.119818-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccCAT8YMR280Cpasspassad1
36870adminadminNaN5512024-07-062024-07-06T11:08:33.125605-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccPDR1YGL013Cpasspassad1
46871adminadminNaN4512024-07-062024-07-06T11:08:33.129564-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccPHO4YFR034Cpasspassad1
......................................................
666933adminadminNaN67512024-07-062024-07-06T11:08:45.567814-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccABF2YMR072Wfailpassad1
676934adminadminNaN68512024-07-062024-07-06T11:08:45.921894-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccUSV1YPL230Wpasspassad1
686935adminadminNaN69512024-07-062024-07-06T11:08:46.166036-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccMGA1YGR249Wpasspassad1
696936adminadminNaN70512024-07-062024-07-06T11:08:46.246482-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccCIN5YOR028Cpasspassad1
706937adminadminNaN71512024-07-062024-07-06T11:08:47.987665-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccROX1YPR065Wpasspassad1
\n", + "

71 rows × 17 columns

\n", + "
" + ], + "text/plain": [ + " id uploader modifier single_binding composite_binding fileformat \\\n", + "0 6867 admin admin NaN 6 5 \n", + "1 6868 admin admin NaN 3 5 \n", + "2 6869 admin admin NaN 1 5 \n", + "3 6870 admin admin NaN 5 5 \n", + "4 6871 admin admin NaN 4 5 \n", + ".. ... ... ... ... ... ... \n", + "66 6933 admin admin NaN 67 5 \n", + "67 6934 admin admin NaN 68 5 \n", + "68 6935 admin admin NaN 69 5 \n", + "69 6936 admin admin NaN 70 5 \n", + "70 6937 admin admin NaN 71 5 \n", + "\n", + " background upload_date modified_date \\\n", + "0 1 2024-07-06 2024-07-06T11:08:33.125644-05:00 \n", + "1 1 2024-07-06 2024-07-06T11:08:33.125640-05:00 \n", + "2 1 2024-07-06 2024-07-06T11:08:33.119818-05:00 \n", + "3 1 2024-07-06 2024-07-06T11:08:33.125605-05:00 \n", + "4 1 2024-07-06 2024-07-06T11:08:33.129564-05:00 \n", + ".. ... ... ... \n", + "66 1 2024-07-06 2024-07-06T11:08:45.567814-05:00 \n", + "67 1 2024-07-06 2024-07-06T11:08:45.921894-05:00 \n", + "68 1 2024-07-06 2024-07-06T11:08:46.166036-05:00 \n", + "69 1 2024-07-06 2024-07-06T11:08:46.246482-05:00 \n", + "70 1 2024-07-06 2024-07-06T11:08:47.987665-05:00 \n", + "\n", + " file promoter source \\\n", + "0 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + "1 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + "2 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + "3 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + "4 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + ".. ... ... ... \n", + "66 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + "67 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + "68 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + "69 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + "70 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + "\n", + " regulator_symbol regulator_locus_tag rank_recall data_usable \\\n", + "0 WTM1 YOR230W pass pass \n", + "1 MIG2 YGL209W pass pass \n", + "2 CAT8 YMR280C pass pass \n", + "3 PDR1 YGL013C pass pass \n", + "4 PHO4 YFR034C pass pass \n", + ".. ... ... ... ... \n", + "66 ABF2 YMR072W fail pass \n", + "67 USV1 YPL230W pass pass \n", + "68 MGA1 YGR249W pass pass \n", + "69 CIN5 YOR028C pass pass \n", + "70 ROX1 YPR065W pass pass \n", + "\n", + " background_name \n", + "0 ad1 \n", + "1 ad1 \n", + "2 ad1 \n", + "3 ad1 \n", + "4 ad1 \n", + ".. ... \n", + "66 ad1 \n", + "67 ad1 \n", + "68 ad1 \n", + "69 ad1 \n", + "70 ad1 \n", + "\n", + "[71 rows x 17 columns]" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "callingcards_aggregated_meta_df" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
iduploadermodifiersingle_bindingcomposite_bindingfileformatbackgroundupload_datemodified_datefilepromotersourceregulator_symbolregulator_locus_tagrank_recalldata_usablebackground_name
06873adminadminNaN7512024-07-062024-07-06T11:08:33.117009-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccGZF3YJL110Cpasspassad1
\n", + "
" + ], + "text/plain": [ + " id uploader modifier single_binding composite_binding fileformat \\\n", + "0 6873 admin admin NaN 7 5 \n", + "\n", + " background upload_date modified_date \\\n", + "0 1 2024-07-06 2024-07-06T11:08:33.117009-05:00 \n", + "\n", + " file promoter source \\\n", + "0 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", + "\n", + " regulator_symbol regulator_locus_tag rank_recall data_usable background_name \n", + "0 GZF3 YJL110C pass pass ad1 " + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pss_api.push_params({\"regulator_symbol\": \"GZF3\"})\n", + "\n", + "callingcards_res = await pss_api.read()\n", + "\n", + "gzf3_callingcards_res = callingcards_res.get(\"metadata\")\n", + "\n", + "gzf3_callingcards_res\n", + "\n", + "#gzf3_callingcards_res[~gzf3_callingcards_res.composite_binding_id.isna()]" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "data = [\n", + " {\n", + " \"promotersetsig_ids\": [\"6873\"],\n", + " \"expression_ids\": [\"2510\"],\n", + " }\n", + "]\n", + "\n", + "group_id = await rr_api.submit(post_dict=data)\n", + "\n", + "agg_result = await rr_api.retrieve(group_id)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA04AAAIjCAYAAAA0vUuxAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAB/eUlEQVR4nO3deVxU9f7H8ffMsIzIJiKCS4pbhrikhlGZlmuZZd3Ka1lm2820utpqG9mv275velutvJXV9Wq2mHtloqhoibuIO4iCAi5sM+f3hzGJLDPgwAzwej4ePHLO+Z5zPjN8Q95+v+d7TIZhGAIAAAAAVMjs6QIAAAAAwNsRnAAAAADACYITAAAAADhBcAIAAAAAJwhOAAAAAOAEwQkAAAAAnCA4AQAAAIATBCcAAAAAcILgBAAAAABOEJwAAA3S0qVLZTKZtHTpUk+XUifccsstCgwMrPHr7Ny5UyaTSdOnT6/xawFAVRCcAKAapk+fLpPJ5Pjy8fFRy5Ytdcstt2jfvn2eLq9OKvmFueTLbDYrLCxMl112mRITE6t93nfffbde/BJel/vcU089VeZ7GxUVpSuuuEIrVqzwdHkA4BIfTxcAAHXZ008/rejoaOXn52vFihWaPn26li1bppSUFFmtVk+XVyeNGjVKl19+uWw2m7Zu3ap3331Xl1xyiVatWqWuXbtW+XzvvvuuwsPDdcstt5TafvHFF+vEiRPy8/NzU+W1oy73ualTpyowMFB2u1179uzR+++/r4svvlhJSUnq0aOHJKlNmzY6ceKEfH19PVssAJyG4AQAZ+Cyyy5T7969JUm33367wsPD9cILL+jbb7/V9ddf7+Hq6qaePXtq9OjRjtd9+/bVZZddpqlTp+rdd99123XMZrPXB43y1OU+d+211yo8PNzxesSIEYqNjdXXX3/tCE4mk6lOfl8A1H9M1QMAN+rbt68kKTU1tdT2zZs369prr1VYWJisVqt69+6tb7/9tlSboqIiTZkyRR07dpTValXTpk110UUXacGCBY42JfeZ7NixQ0OGDFHjxo3VokULPf300zIMo9T5jh07pvvvv1+tW7eWv7+/zj77bL388stl2plMJk2YMEGzZ89WbGys/P391aVLF82bN69Uu7y8PP3zn/9U27Zt5e/vr4iICA0aNEjJycml2q1cuVJDhw5VSEiIAgIC1K9fP/3222/V+0BV8Wf68ccf69JLL1VERIT8/f0VExOjqVOnlmrTtm1bbdiwQT///LNjmlj//v0lVXyP09dff61evXqpUaNGCg8P1+jRo51OhVu9erVMJpM++eSTMvt++uknmUwmfffdd5Jc/xxdVd7nU1hYqCeffFK9evVSSEiIGjdurL59+2rJkiWlji2ZHvnyyy/rvffeU/v27eXv76/zzjtPq1atcnrtdevWqVmzZurfv7+OHj1a5dojIyMlST4+f/07bnn3OJX0+3379mnEiBEKDAxUs2bN9MADD8hms1X5ugBQHYw4AYAb7dy5U5LUpEkTx7YNGzbowgsvVMuWLfXII4+ocePG+uqrrzRixAj997//1dVXXy3p5H0gzz33nG6//XbFxcUpNzdXq1evVnJysgYNGuQ4n81m09ChQ3X++efrxRdf1Lx585SQkKDi4mI9/fTTkiTDMHTllVdqyZIluu2229SjRw/99NNPevDBB7Vv3z699tprpepetmyZZs2apbvvvltBQUF688039be//U27d+9W06ZNJUl33XWXvvnmG02YMEExMTHKysrSsmXLtGnTJvXs2VOStHjxYl122WXq1auXEhISZDabHQHn119/VVxcnFs+U+nktK8uXbroyiuvlI+Pj+bOnau7775bdrtd48ePlyS9/vrruueeexQYGKjHHntMktS8efMKrzV9+nSNHTtW5513np577jkdOHBAb7zxhn777TetXbtWoaGh5R7Xu3dvtWvXTl999ZXGjBlTat/MmTPVpEkTDRkyxOXP8Uw/n9zcXH3wwQcaNWqU7rjjDuXl5enDDz/UkCFDSk2LK/H5558rLy9P//jHP2QymfTiiy/qmmuu0Y4dOyqcMrdq1SoNGTJEvXv31pw5c9SoUSOntWZnZ0uS7Ha79u3bp//7v/+T1Wp1aaTMZrNpyJAh6tOnj15++WUtXLhQr7zyitq3b69x48Y5PR4AzpgBAKiyjz/+2JBkLFy40Dh48KCxZ88e45tvvjGaNWtm+Pv7G3v27HG0HTBggNG1a1cjPz/fsc1utxsXXHCB0bFjR8e27t27G8OGDav0umPGjDEkGffcc0+pcw0bNszw8/MzDh48aBiGYcyePduQZDzzzDOljr/22msNk8lkbN++3bFNkuHn51dq2++//25IMt566y3HtpCQEGP8+PEV1ma3242OHTsaQ4YMMex2u2P78ePHjejoaGPQoEGVvre0tDRDkjFlyhTj4MGDRkZGhvHrr78a5513niHJ+Prrr0u1P378eJlzDBkyxGjXrl2pbV26dDH69etXpu2SJUsMScaSJUsMwzCMwsJCIyIiwoiNjTVOnDjhaPfdd98Zkownn3yy0vonT55s+Pr6GtnZ2Y5tBQUFRmhoqHHrrbc6tjn7HCtSlT5XXFxsFBQUlDr+8OHDRvPmzUvVUvKZN23atFTdc+bMMSQZc+fOdWwbM2aM0bhxY8MwDGPZsmVGcHCwMWzYsFL9uiIJCQmGpDJfoaGhxrx580q1Lanp448/LnVtScbTTz9dqu25555r9OrVy+n1AcAdmKoHAGdg4MCBatasmVq3bq1rr71WjRs31rfffqtWrVpJOvkv7IsXL9b111+vvLw8HTp0SIcOHVJWVpaGDBmibdu2OaaBhYaGasOGDdq2bZvT606YMMHx55KpdoWFhVq4cKEk6YcffpDFYtG9995b6rj7779fhmHoxx9/LPM+2rdv73jdrVs3BQcHa8eOHY5toaGhWrlypfbv319uTevWrdO2bdt0ww03KCsry/Fejx07pgEDBuiXX36R3W53+t4SEhLUrFkzRUZGqm/fvtq0aZNeeeUVXXvttaXanTrCkZOTo0OHDqlfv37asWOHcnJynF7ndKtXr1ZmZqbuvvvuUvfYDBs2TJ07d9b3339f6fEjR45UUVGRZs2a5dg2f/58HTlyRCNHjnRsc/Y5OuOsz0mSxWJxLHpht9uVnZ2t4uJi9e7du9wpgSNHjiw1YlUy/e/U73+JJUuWaMiQIRowYIBmzZolf39/l2v/73//qwULFmj+/Pn6+OOP1alTJ/3tb3/T8uXLXTr+rrvuKvW6b9++5dYIADWBqXoAcAbeeecdderUSTk5Ofroo4/0yy+/lPpFcvv27TIMQ0888YSeeOKJcs+RmZmpli1b6umnn9ZVV12lTp06KTY2VkOHDtVNN92kbt26lWpvNpvVrl27Uts6deok6a9pW7t27VKLFi0UFBRUqt0555zj2H+qs846q0xdTZo00eHDhx2vX3zxRY0ZM0atW7dWr169dPnll+vmm2921FIS+E6fqnaqnJycMlPuTnfnnXfquuuuU35+vhYvXqw333yz3PtYfvvtNyUkJCgxMVHHjx8vc52QkJBKr3O6ks/k7LPPLrOvc+fOWrZsWaXHd+/eXZ07d9bMmTN12223STo5TS88PFyXXnqpo52zz9EZZ32uxCeffKJXXnlFmzdvVlFRkWN7dHR0mbanf/9Lvkenfv8lKT8/X8OGDVOvXr301Vdflbo3yRUXX3xxqcUhrr32WnXs2FH33HOP1qxZU+mxVqtVzZo1K1Pn6TUCQE0hOAHAGYiLi3OscDZixAhddNFFuuGGG7RlyxbHssuS9MADDzjucTldhw4dJJ38pTI1NVVz5szR/Pnz9cEHH+i1117TtGnTdPvtt9fo+7BYLOVuN05ZSOL6669X37599b///U/z58/XSy+9pBdeeEGzZs3SZZdd5nivL730Upl7aEq48gDVjh07auDAgZKkK664QhaLRY888oguueQSx2edmpqqAQMGqHPnznr11VfVunVr+fn56YcfftBrr73m0shWTRg5cqT+9a9/6dChQwoKCtK3336rUaNGlQoYzj5HZ5z1OUmaMWOGbrnlFo0YMUIPPvigIiIiZLFY9Nxzz5VZZENy7fsvSf7+/rr88ss1Z84czZs3T1dccYXLn015AgMD1adPH82ZM0fHjh1T48aNK2xbUY0AUFuYqgcAblLyi+n+/fv19ttvS5JjFMHX11cDBw4s9+vUUaGwsDCNHTtWX3zxhfbs2aNu3brpqaeeKnUdu91eZnrS1q1bJZ1cRU46+Syc/fv3Ky8vr1S7zZs3O/ZXR1RUlO6++27Nnj1baWlpatq0qf71r39JkmOqX3BwcIXvtTrP5nnssccUFBSkxx9/3LFt7ty5Kigo0Lfffqt//OMfuvzyyzVw4MByFygwmUwuXafkM9myZUuZfVu2bHHpMxs5cqSKi4v13//+Vz/++KNyc3P197//vUy7yj7Hqiivz0nSN998o3bt2mnWrFm66aabNGTIEA0cOFD5+flVvsapTCaT/vOf/2jAgAG67rrryqxIWB3FxcWSVK1V+QCgNhGcAMCN+vfvr7i4OL3++uvKz89XRESE+vfvr3//+99KT08v0/7gwYOOP2dlZZXaFxgYqA4dOqigoKDMcaf+kmwYht5++235+vpqwIABkuR4gOyp7STptddek8lkcmlk41Q2m63MfUMRERFq0aKFo75evXqpffv2evnll8v9JfjU91oVoaGh+sc//qGffvpJ69atk/TX6MOpIyI5OTn6+OOPyxzfuHFjHTlyxOl1evfurYiICE2bNq3UZ/7jjz9q06ZNGjZsmNNznHPOOeratatmzpypmTNnKioqShdffLFjvyufY1Wd3uek8j+flStXKjExsVrXOJWfn59mzZql8847T8OHD1dSUlK1z5Wdna3ly5crMjJSERERZ1wbANQkpuoBgJs9+OCDuu666zR9+nTdddddeuedd3TRRRepa9euuuOOO9SuXTsdOHBAiYmJ2rt3r37//XdJUkxMjPr3769evXopLCxMq1evdixbfSqr1ap58+ZpzJgx6tOnj3788Ud9//33evTRRx33gAwfPlyXXHKJHnvsMe3cuVPdu3fX/PnzNWfOHP3zn/8stRCEK/Ly8tSqVStde+216t69uwIDA7Vw4UKtWrVKr7zyiqST91598MEHuuyyy9SlSxeNHTtWLVu21L59+7RkyRIFBwdr7ty51fpM77vvPr3++ut6/vnn9eWXX2rw4MHy8/PT8OHD9Y9//ENHjx7V+++/r4iIiDIBtVevXpo6daqeeeYZdejQQREREaXuOSrh6+urF154QWPHjlW/fv00atQox3Lkbdu21cSJE12qdeTIkXryySdltVp12223yWz+698oXfkcq+P0PnfFFVdo1qxZuvrqqzVs2DClpaVp2rRpiomJccvITqNGjfTdd9/p0ksv1WWXXaaff/5ZsbGxTo/75ptvFBgYKMMwtH//fn344Yc6fPiwpk2b5vLIIAB4jOcW9AOAuqtkaehVq1aV2Wez2Yz27dsb7du3N4qLiw3DMIzU1FTj5ptvNiIjIw1fX1+jZcuWxhVXXGF88803juOeeeYZIy4uzggNDTUaNWpkdO7c2fjXv/5lFBYWOtqULAmdmppqDB482AgICDCaN29uJCQkGDabrVQdeXl5xsSJE40WLVoYvr6+RseOHY2XXnqp1FLhhnFyOfLylsdu06aNMWbMGMMwTi6r/eCDDxrdu3c3goKCjMaNGxvdu3c33n333TLHrV271rjmmmuMpk2bGv7+/kabNm2M66+/3li0aFGln2nJMtQvvfRSuftvueUWw2KxOJZN//bbb41u3boZVqvVaNu2rfHCCy8YH330kSHJSEtLcxyXkZFhDBs2zAgKCjIkOZYmP3058hIzZ840zj33XMPf398ICwszbrzxRmPv3r2V1n6qbdu2OZbbXrZsWal9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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "agg_df = agg_result.get(\"data\").get(agg_result.get(\"metadata\").id[0]).iloc[1:50,:]\n", + "\n", + "# Plot\n", + "plt.figure(figsize=(10, 6))\n", + "plt.plot(agg_df['rank_bin'], agg_df['response_ratio'], marker='o')\n", + "plt.title('Aggregated GZF3. McIsaac 15 2510')\n", + "plt.xlabel('Rank Bin')\n", + "plt.ylabel('Response Ratio')\n", + "plt.title('Response Ratio vs Rank Bin')\n", + "plt.grid(True)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Caveats\n", + "\n", + "1. I have written the scripts to automatically check the redis queue for work and to \n", + " both launch celery worker nodes, and kill them when they are finished. But, though\n", + " they work if I run them manually, they have not worked when scheduled through a\n", + " cronjob. I'll work with Brian and Eric next week to figure out why.\n", + "\n", + "1. I haven't tested each of the endpoint APIs individually. Help is welcome." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/mkdocs.yml b/mkdocs.yml index a28f581..c367973 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,45 +1,141 @@ site_name: tfbpapi site_description: "A collection of objects and functions to work with calling cards sequencing tools" site_author: "ben mueller , chase mateusiak , michael brent " -site_url: "https://brentlab.github.io/tfbpapi/" +site_url: "https://brentlab.github.io/tfbpapi" repo_url: "https://github.com/brentlab/tfbpapi" -repo_name: "tfbpapi" -edit_uri: "edit/master/docs/" +repo_name: "brentlab/tfbpapi" +edit_uri: "edit/main/docs/" watch: ['tfbpapi', 'docs'] theme: name: material + palette: + # Palette toggle for light mode + - media: "(prefers-color-scheme: light)" + scheme: default + primary: indigo + accent: indigo + toggle: + icon: material/brightness-7 + name: Switch to dark mode + # Palette toggle for dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: indigo + accent: indigo + toggle: + icon: material/brightness-4 + name: Switch to light mode + features: + - navigation.tabs + - navigation.sections + - navigation.expand + - navigation.path + - navigation.top + - search.highlight + - search.share + - search.suggest + - content.code.copy + - content.code.select + - content.code.annotate + - content.action.edit + - content.action.view + icon: + repo: fontawesome/brands/github + edit: material/pencil + view: material/eye plugins: -- search -- autorefs -- section-index -- mkdocs-jupyter: + - search: + separator: '[\s\-,:!=\[\]()"`/]+|\.(?!\d)|&[lg]t;|(?!\b)(?=[A-Z][a-z])' + - autorefs + - section-index + - mkdocs-jupyter: remove_tag_config: - remove_input_tags: - - hide - remove_output_tags: - - hide -- mkdocstrings: - handlers: - python: - paths: [tfbpapi] # search packages in the src folder - merge_init_into_class: True - options: - docstring_style: 'sphinx' + remove_input_tags: + - hide + remove_output_tags: + - hide + execute: false + allow_errors: false + - mkdocstrings: + handlers: + python: + paths: [.] + import: + - https://docs.python.org/3/objects.inv + - https://numpy.org/doc/stable/objects.inv + - https://pandas.pydata.org/docs/objects.inv + options: + docstring_style: sphinx + show_source: true + show_root_heading: true + show_root_toc_entry: true + show_symbol_type_heading: true + show_symbol_type_toc: true + signature_crossrefs: true markdown_extensions: + - abbr + - admonition + - attr_list + - def_list + - footnotes + - md_in_html - smarty + - tables - toc: - permalink: True + permalink: true + title: On this page - sane_lists - pymdownx.arithmatex: generic: true + - pymdownx.betterem: + smart_enable: all + - pymdownx.caret + - pymdownx.details + - pymdownx.emoji: + emoji_generator: !!python/name:material.extensions.emoji.to_svg + emoji_index: !!python/name:material.extensions.emoji.twemoji + - pymdownx.highlight: + anchor_linenums: true + line_spans: __span + pygments_lang_class: true + - pymdownx.inlinehilite + - pymdownx.keys + - pymdownx.magiclink: + normalize_issue_symbols: true + repo_url_shorthand: true + user: brentlab + repo: tfbpapi + - pymdownx.mark + - pymdownx.smartsymbols + - pymdownx.snippets: + auto_append: + - includes/mkdocs.md - pymdownx.superfences: custom_fences: - name: mermaid class: mermaid - format: "!!python/name:pymdownx.superfences.fence_code_format" + format: !!python/name:pymdownx.superfences.fence_code_format + - pymdownx.tabbed: + alternate_style: true + combine_header_slug: true + slugify: !!python/object/apply:pymdownx.slugs.slugify + kwds: + case: lower + - pymdownx.tasklist: + custom_checkbox: true + - pymdownx.tilde + +extra: + social: + - icon: fontawesome/brands/github + link: https://github.com/brentlab/tfbpapi + name: GitHub Repository + version: + provider: mike + default: latest extra_javascript: - javascripts/mathjax.js @@ -47,36 +143,35 @@ extra_javascript: - https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js - js/init-mermaid.js +extra_css: + - stylesheets/extra.css + nav: -- Home: index.md -- Tutorials: - - Database Interface: tutorials/database_interface.ipynb - - LassoCV: tutorials/lassoCV.ipynb - - Interactor Modeling Workflow: tutorials/interactor_modeling_workflow.ipynb -- API: - - Models: - - Overview: ml_models/index.md - - SigmoidModel: ml_models/SigmoidModel.md - - Lasso Modeling: ml_models/lasso_modeling.md - - Database Interface: - - Records Only Classes: - - interface/BindingManualQCAPI.md - - interface/DataSourceAPI.md - - interface/DtoAPI.md - - interface/ExpressionManualQCAPI.md - - interface/FileFormatAPI.md - - interface/GenomicFeatureAPI.md - - interface/RegulatorAPI.md - - Records and Files Classes: - - BindingAPI: interface/BindingAPI.md - - BindingConcatenatedAPI: interface/BindingConcatenatedAPI.md - - CallingCardsBackgroundAPI: interface/CallingCardsBackgroundAPI.md - - ExpressionAPI: interface/ExpressionAPI.md - - PromoterSetAPI: interface/PromoterSetAPI.md - - PromoterSetSigAPI: interface/PromoterSetSigAPI.md - - Developer Classes: - - interface/AbstractAPI.md - - interface/AbstractRecordsAndFilesAPI.md - - interface/AbstractRecordsOnlyAPI.md - - interface/Cache.md - - interface/ParamsDict.md + - Home: index.md + - Tutorials: + - Database tfbpapi: tutorials/database_tfbpapi.ipynb + - API: + - Database tfbpapi: + - Records Only Classes: + - BindingManualQCAPI.md + - DataSourceAPI.md + - DtoAPI.md + - ExpressionManualQCAPI.md + - FileFormatAPI.md + - GenomicFeatureAPI.md + - RegulatorAPI.md + - Records and Files Classes: + - BindingAPI: BindingAPI.md + - BindingConcatenatedAPI: BindingConcatenatedAPI.md + - CallingCardsBackgroundAPI: CallingCardsBackgroundAPI.md + - ExpressionAPI: ExpressionAPI.md + - PromoterSetAPI: PromoterSetAPI.md + - PromoterSetSigAPI: PromoterSetSigAPI.md + - Developer Classes: + - AbstractAPI.md + - AbstractRecordsAndFilesAPI.md + - AbstractRecordsOnlyAPI.md + - AbstractHfAPI.md + - HfCacheManager.md + - Cache.md + - ParamsDict.md diff --git a/pyproject.toml b/pyproject.toml index 56c6f30..916be3d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,6 +20,8 @@ dotenv = "^0.9.9" pandas = "^2.3.1" huggingface-hub = "^0.34.4" types-requests = "^2.32.4.20250809" +datasets = "^4.0.0" +duckdb = "^1.3.2" [tool.poetry.group.dev.dependencies] @@ -27,6 +29,12 @@ pytest = "^8.3.5" pytest-snapshot = "^0.9.0" pytest-asyncio = "^0.26.0" types-requests = "^2.32.4.20250809" +mkdocs = "^1.6.1" +mkdocs-material = "^9.6.19" +mkdocs-autorefs = "^1.4.3" +mkdocs-section-index = "^0.3.10" +mkdocs-jupyter = "^0.25.1" +mkdocstrings = {extras = ["python"], version = "^0.30.0"} [tool.pytest.ini_options] diff --git a/tfbpapi/HfQueryAPI.py b/tfbpapi/HfQueryAPI.py new file mode 100644 index 0000000..5907071 --- /dev/null +++ b/tfbpapi/HfQueryAPI.py @@ -0,0 +1,363 @@ +from pathlib import Path +from typing import Any, Literal + +import duckdb +import pandas as pd +from datasets import Dataset, DatasetDict, load_dataset +from huggingface_hub import DatasetCard + +from .AbstractHfAPI import AbstractHfAPI + + +class HfQueryAPI(AbstractHfAPI): + """ + Concrete implementation of AbstractHfAPI with DuckDB query capabilities. + + This class provides seamless querying of Hugging Face datasets using SQL via DuckDB. + It automatically handles dataset downloading, parsing, and provides a simple query + interface. + + """ + + def __init__( + self, + repo_id: str, + repo_type: Literal["model", "dataset", "space"] = "dataset", + token: str | None = None, + cache_dir: str | Path | None = None, + auto_download_threshold_mb: float = 100.0, + auto_parse_datacard: bool = True, + ): + """ + Initialize the HF Query API client. + + :param repo_id: The repo identifier on HF (e.g., "user/dataset") + :param repo_type: One of {"model", "dataset", "space"}. Defaults to "dataset" + :param token: Optional HF token for private repos + :param cache_dir: HF cache directory for downloads + :param auto_download_threshold_mb: Auto-download threshold in MB + :param auto_parse_datacard: Whether to automatically parse datacard on init + + """ + super().__init__(repo_id, repo_type, token, cache_dir) + self.auto_download_threshold_mb = auto_download_threshold_mb + self._datasets: dict[str, Any] = {} + self._loaded_datasets: dict[str, Dataset | DatasetDict] = {} + self._duckdb_conn = duckdb.connect(":memory:") + self._table_filters: dict[str, str] = {} + + if auto_parse_datacard: + try: + self.datasets = self.parse_datacard() + except Exception as e: + self.logger.warning(f"Failed to auto-parse datacard: {e}") + self._datasets = {} + + @property + def datasets(self) -> dict[str, Any]: + """Parsed dataset configurations from the datacard.""" + return self._datasets + + @datasets.setter + def datasets(self, value: dict[str, Any]) -> None: + """Set the parsed datasets and update available tables.""" + self._datasets = value + self._update_available_tables() + + @property + def available_tables(self) -> list[str]: + """List of available table names for querying.""" + return list(self._datasets.keys()) + + def parse_datacard(self) -> dict[str, Any]: + """ + Parse the dataset card into a standardized format. + + :return: Dict mapping config names to their metadata + + """ + try: + card = DatasetCard.load(self.repo_id, self.repo_type) + data_dict = card.data.to_dict() + except Exception as e: + self.logger.error(f"Failed to load dataset card: {e}") + return {} + + parsed_datasets = {} + + for config in data_dict.get("configs", []): + config_name = config["config_name"] + + # Extract features for filtering/querying + features = {} + if "dataset_info" in config and "features" in config["dataset_info"]: + for feature in config["dataset_info"]["features"]: + features[feature["name"]] = { + "dtype": feature["dtype"], + "description": feature.get("description", ""), + } + + # Extract file paths + data_files = [] + for file_info in config.get("data_files", []): + data_files.append( + { + "path": file_info["path"], + "split": file_info.get("split", "train"), + } + ) + + parsed_datasets[config_name] = { + "features": features, + "data_files": data_files, + "config": config, + "loaded": False, + } + + return parsed_datasets + + def _update_available_tables(self) -> None: + """Update the logger with information about available tables.""" + if self._datasets: + self.logger.info(f"Available tables: {', '.join(self.available_tables)}") + + def _ensure_dataset_loaded(self, table_name: str) -> Dataset | DatasetDict: + """ + Ensure a dataset is loaded and available for querying. + + :param table_name: Name of the dataset configuration + :return: The loaded dataset + :raises ValueError: If table_name is not found + + """ + if table_name not in self._datasets: + raise ValueError( + f"Table '{table_name}' not found. " + f"Available tables: {self.available_tables}" + ) + + if table_name in self._loaded_datasets: + return self._loaded_datasets[table_name] + + # Download the dataset if not already downloaded + if not self.snapshot_path: + self.logger.info(f"Downloading dataset for table '{table_name}'...") + self.download(auto_download_threshold_mb=self.auto_download_threshold_mb) + + # Load the specific dataset configuration + try: + self.logger.info(f"Loading dataset configuration '{table_name}'...") + dataset = load_dataset( + str(self.snapshot_path), name=table_name, keep_in_memory=False + ) + self._loaded_datasets[table_name] = dataset + self._datasets[table_name]["loaded"] = True + + # Register with DuckDB + self._register_dataset_with_duckdb(table_name, dataset) + + return dataset + except Exception as e: + self.logger.error(f"Failed to load dataset '{table_name}': {e}") + raise + + def _register_dataset_with_duckdb( + self, table_name: str, dataset: Dataset | DatasetDict + ) -> None: + """Register a dataset with DuckDB for SQL querying.""" + try: + if isinstance(dataset, DatasetDict): + # Register each split as a separate view + for split_name, split_dataset in dataset.items(): + view_name = ( + f"{table_name}_{split_name}" + if split_name != "train" + else table_name + ) + df = split_dataset.to_pandas() + self._duckdb_conn.register(view_name, df) + self.logger.debug( + f"Registered view '{view_name}' with {len(df)} rows" + ) + else: + # Single dataset + df = dataset.to_pandas() + self._duckdb_conn.register(table_name, df) + self.logger.debug( + f"Registered table '{table_name}' with {len(df)} rows" + ) + except Exception as e: + self.logger.error( + f"Failed to register dataset '{table_name}' with DuckDB: {e}" + ) + raise + + def query(self, sql: str, table_name: str | None = None) -> pd.DataFrame: + """ + Execute a SQL query against the dataset. + + :param sql: SQL query string + :param table_name: Optional table name to ensure is loaded. If not provided, + attempts to infer from the SQL query + :return: Query results as a pandas DataFrame + + """ + # If table_name not provided, try to infer from available tables + if table_name is None: + sql_lower = sql.lower() + for available_table in self.available_tables: + if available_table in sql_lower: + table_name = available_table + break + + # If we found a table name, ensure it's loaded + if table_name: + self._ensure_dataset_loaded(table_name) + elif not self._loaded_datasets: + # If no datasets are loaded and we couldn't infer, + # try to load the first one + if self.available_tables: + self._ensure_dataset_loaded(self.available_tables[0]) + + # Apply any table filters to the query + modified_sql = self._apply_table_filters(sql) + + try: + result = self._duckdb_conn.execute(modified_sql).fetchdf() + self.logger.debug(f"Query returned {len(result)} rows") + return result + except Exception as e: + self.logger.error(f"Query failed: {e}") + if modified_sql != sql: + self.logger.debug(f"Original query: {sql}") + self.logger.debug(f"Modified query: {modified_sql}") + raise + + def describe_table(self, table_name: str) -> pd.DataFrame: + """ + Get information about a table's structure. + + :param table_name: Name of the table to describe + :return: DataFrame with column information + + """ + self._ensure_dataset_loaded(table_name) + return self.query(f"DESCRIBE {table_name}") + + def sample(self, table_name: str, n: int = 5) -> pd.DataFrame: + """ + Get a sample of rows from a table. + + :param table_name: Name of the table to sample + :param n: Number of rows to return + :return: Sample DataFrame + + """ + return self.query(f"SELECT * FROM {table_name} LIMIT {n}", table_name) + + def count(self, table_name: str) -> int: + """ + Get the number of rows in a table. + + :param table_name: Name of the table to count + :return: Number of rows + + """ + result = self.query(f"SELECT COUNT(*) as count FROM {table_name}", table_name) + return result.iloc[0]["count"] + + def get_columns(self, table_name: str) -> list[str]: + """ + Get column names for a table. + + :param table_name: Name of the table + :return: List of column names + + """ + if table_name not in self._datasets: + raise ValueError(f"Table '{table_name}' not found") + + return list(self._datasets[table_name]["features"].keys()) + + def _apply_table_filters(self, sql: str) -> str: + """ + Apply table filters to a SQL query by modifying table references. + + :param sql: Original SQL query + :return: Modified SQL query with filters applied + + """ + if not self._table_filters: + return sql + + modified_sql = sql + + # Apply filters by replacing table references with filtered subqueries + for table_name, filter_condition in self._table_filters.items(): + import re + + # Simple pattern to match table references in FROM and JOIN clauses + pattern = rf"\b{re.escape(table_name)}\b" + replacement = f"(SELECT * FROM {table_name} WHERE {filter_condition})" + + # Only replace if we find the table name in the SQL + if re.search(pattern, modified_sql, re.IGNORECASE): + # Check if it's already wrapped in a filtered subquery to + # avoid double-wrapping + if not re.search( + rf"\(SELECT.*FROM\s+{re.escape(table_name)}\s+WHERE", + modified_sql, + re.IGNORECASE, + ): + modified_sql = re.sub( + pattern, replacement, modified_sql, flags=re.IGNORECASE + ) + + return modified_sql + + def get_table_filter(self, table_name: str) -> str | None: + """ + Get the current filter for a table. + + :param table_name: Name of the table + :return: Current filter SQL condition or None if no filter is set + + """ + return self._table_filters.get(table_name) + + def set_table_filter(self, table_name: str, filter_condition: str) -> None: + """ + Set a filter condition for a table that will be applied to all queries. + + :param table_name: Name of the table + :param filter_condition: SQL WHERE condition (without the WHERE keyword) + + """ + self._table_filters[table_name] = filter_condition + self.logger.debug(f"Set filter for table '{table_name}': {filter_condition}") + + def remove_table_filter(self, table_name: str) -> None: + """ + Remove any filter condition for a table. + + :param table_name: Name of the table + + """ + removed_filter = self._table_filters.pop(table_name, None) + if removed_filter: + self.logger.debug( + f"Removed filter for table '{table_name}': {removed_filter}" + ) + + def close(self) -> None: + """Close the DuckDB connection.""" + if self._duckdb_conn: + self._duckdb_conn.close() + + def __enter__(self): + """Context manager entry.""" + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + """Context manager exit.""" + self.close() diff --git a/tfbpapi/HfRankResponse.py b/tfbpapi/HfRankResponse.py new file mode 100644 index 0000000..b52f6f9 --- /dev/null +++ b/tfbpapi/HfRankResponse.py @@ -0,0 +1,482 @@ +import logging + +import duckdb +import pandas as pd + +from .IncrementalAnalysisDB import IncrementalAnalysisDB + + +class HfRankResponse: + """ + A class to provide an API to compute and analyze "rank response", which is defined + as the cumulative number of responsive targets (e.g., genes) binned by their binding + rank score for each regulator sample pair of binding and perturbation response data. + + Handles multiple dataset comparisons and stores all results in a shared database. + + """ + + def __init__(self, db: IncrementalAnalysisDB): + """ + Initialize RankResponse analyzer with database connection. + + :param db: IncrementalAnalysisDB instance for storing results + + """ + self.db = db + self.logger = logging.getLogger(__name__) + + def compute( + self, + ranking_api, + response_api, + ranking_table: str, + response_table: str, + ranking_score_column: str, + response_column: str, + comparison_id: str | None = None, + regulator_column: str = "regulator_locus_tag", + target_column: str = "target_locus_tag", + bin_size: int = 5, + force_recompute: bool = False, + responsive_condition: str | None = None, + ) -> pd.DataFrame: + """ + Compute rank response for a specific dataset comparison. + + :param ranking_api: API instance for ranking/binding data + :param response_api: API instance for response/perturbation data + :param ranking_table: Name of ranking table in ranking_api + :param response_table: Name of response table in response_api + :param ranking_score_column: Column name for ranking scores + :param response_column: Column name for response values + :param comparison_id: Unique identifier for this comparison (auto-generated if + None) + :param regulator_column: Column name for regulator identifiers + :param target_column: Column name for target identifiers + :param bin_size: Size of ranking bins + :param force_recompute: Whether to recompute existing results + :param responsive_condition: SQL condition to define responsive (default: IS NOT + NULL) + :return: DataFrame with computed results + + """ + # Generate comparison ID if not provided + if comparison_id is None: + comparison_id = f"{ranking_table}_vs_{response_table}" + + table_name = f"rank_response_{comparison_id}" + + # Get all regulators from ranking data + ranking_api._ensure_dataset_loaded(ranking_table) + all_regulators = ranking_api.query( + f"SELECT DISTINCT {regulator_column} FROM {ranking_table}" + )[regulator_column].tolist() + + # Check which regulators already have results + if not force_recompute and self.db.table_exists(table_name): + existing_regulators = set( + self.db.query(f"SELECT DISTINCT {regulator_column} FROM {table_name}")[ + regulator_column + ].tolist() + ) + + new_regulators = [ + reg for reg in all_regulators if reg not in existing_regulators + ] + self.logger.info( + f"Found {len(existing_regulators)} existing regulators, " + f"{len(new_regulators)} new ones" + ) + else: + new_regulators = all_regulators + self.logger.info(f"Computing analysis for {len(new_regulators)} regulators") + + if not new_regulators: + self.logger.info("No new regulators to analyze") + return self.db.query( + f"SELECT * FROM {table_name} ORDER BY " + f"{regulator_column}, {target_column}" + ) + + # Apply filters to focus on new regulators + # For analytical queries, escape and format the values safely + escaped_values = [ + f"'{reg.replace(chr(39), chr(39)+chr(39))}'" for reg in new_regulators + ] + regulator_filter = f"{regulator_column} IN ({', '.join(escaped_values)})" + + # Temporarily add filters for new regulators only + original_ranking_filter = ranking_api.get_table_filter(ranking_table) + original_response_filter = response_api.get_table_filter(response_table) + + new_ranking_filter = regulator_filter + new_response_filter = regulator_filter + + if original_ranking_filter: + new_ranking_filter = f"({original_ranking_filter}) AND ({regulator_filter})" + if original_response_filter: + new_response_filter = ( + f"({original_response_filter}) AND ({regulator_filter})" + ) + + ranking_api.set_table_filter(ranking_table, new_ranking_filter) + response_api.set_table_filter(response_table, new_response_filter) + + try: + # Load filtered data from both APIs + ranking_api._ensure_dataset_loaded(ranking_table) + response_api._ensure_dataset_loaded(response_table) + + ranking_conn = ranking_api._duckdb_conn + response_conn = response_api._duckdb_conn + + # Create temporary connection for analysis + temp_conn = duckdb.connect(":memory:") + + try: + # Get filtered data + ranking_df = ranking_conn.execute( + f"SELECT * FROM {ranking_table}" + ).fetchdf() + response_df = response_conn.execute( + f"SELECT * FROM {response_table}" + ).fetchdf() + + temp_conn.register("ranking_data", ranking_df) + temp_conn.register("response_data", response_df) + + # Execute intermediate analysis SQL + # Set default responsive condition if not provided + if responsive_condition is None: + responsive_condition = f"b.{response_column} IS NOT NULL" + else: + # Replace column references in the condition + responsive_condition = responsive_condition.replace( + response_column, f"b.{response_column}" + ) + + intermediate_sql = f""" + WITH binned_data AS ( + SELECT + a.{regulator_column}, + a.{target_column}, + a.{ranking_score_column}, + b.{response_column}, + CASE WHEN {responsive_condition} THEN 1 ELSE 0 END + AS responsive, + CEILING(ROW_NUMBER() OVER ( + PARTITION BY a.{regulator_column} + ORDER BY a.{regulator_column}, a.{target_column} + ) / {bin_size}.0) * {bin_size} AS bin_label + FROM ranking_data AS a + LEFT JOIN response_data AS b + ON a.{regulator_column} = b.{regulator_column} + AND a.{target_column} = b.{target_column} + ) + SELECT + {regulator_column}, + {target_column}, + {ranking_score_column}, + {response_column}, + responsive, + bin_label, + SUM(responsive) OVER ( + PARTITION BY {regulator_column} + ORDER BY bin_label + RANGE UNBOUNDED PRECEDING + ) AS cumulative_responsive + FROM binned_data + ORDER BY {regulator_column}, bin_label, {target_column} + """ + + new_results = temp_conn.execute(intermediate_sql).fetchdf() + + finally: + temp_conn.close() + + # Save new intermediate results + if len(new_results) > 0: + self.db.append_results( + new_results, + table_name, + analysis_type="response_rate_intermediate", + parameters={ + "ranking_table": ranking_table, + "response_table": response_table, + "ranking_score_column": ranking_score_column, + "response_column": response_column, + "bin_size": bin_size, + "result_type": "intermediate", + }, + description=( + f"Added intermediate data for {len(new_regulators)} " + "new regulators" + ), + deduplicate_on=[regulator_column, target_column], + ) + + self.logger.info( + f"Saved {len(new_results)} intermediate records to database" + ) + + # Return complete results from database + return self.db.query( + f"SELECT * FROM {table_name} ORDER BY {regulator_column}, bin_label, " + f"{target_column}" + ) + + finally: + # Restore original filters + if original_ranking_filter: + ranking_api.set_table_filter(ranking_table, original_ranking_filter) + else: + ranking_api.remove_table_filter(ranking_table) + + if original_response_filter: + response_api.set_table_filter(response_table, original_response_filter) + else: + response_api.remove_table_filter(response_table) + + def get_comparisons(self) -> list[str]: + """ + Get list of all computed comparisons. + + :return: List of comparison identifiers + + """ + tables = self.db.list_tables() + rank_response_tables = [ + table + for table in tables + if table.startswith("rank_response_") and table != "rank_response_metadata" + ] + return [table.replace("rank_response_", "") for table in rank_response_tables] + + def get_bin_summary( + self, + comparison_id: str, + regulator_column: str = "regulator_locus_tag", + bin_size: int = 5, + regulators_filter: list[str] | None = None, + ) -> pd.DataFrame: + """ + Generate bin-level summary for a specific comparison. + + :param comparison_id: Identifier for the comparison to summarize + :param regulator_column: Column name for regulator identifiers + :param bin_size: Bin size used in analysis + :param regulators_filter: Optional list of regulators to include + :return: DataFrame with bin summary results + + """ + intermediate_table_name = f"rank_response_{comparison_id}" + + if not self.db.table_exists(intermediate_table_name): + raise ValueError( + f"Intermediate table '{intermediate_table_name}' does not exist. " + "Run compute() first." + ) + + # Build WHERE clause for regulator filter + where_clause = "" + if regulators_filter: + # For analytical queries, escape and format the values safely + escaped_values = [ + f"'{reg.replace(chr(39), chr(39)+chr(39))}'" + for reg in regulators_filter + ] + where_clause = f"WHERE {regulator_column} IN ({', '.join(escaped_values)})" + + # Generate summary from intermediate data + summary_sql = f""" + SELECT + {regulator_column}, + bin_label, + COUNT(*) as records_in_bin, + SUM(responsive) as responsive_in_bin, + MAX(cumulative_responsive) as cumulative_responsive, + MAX(cumulative_responsive) / bin_label as response_rate + FROM {intermediate_table_name} + {where_clause} + GROUP BY {regulator_column}, bin_label + ORDER BY {regulator_column}, bin_label + """ + + summary_results = self.db.query(summary_sql) + + self.logger.info( + f"Generated summary for {len(summary_results)} regulator-bin combinations" + ) + return summary_results + + def get_regulator_summary( + self, + comparison_id: str, + regulator_column: str = "regulator_locus_tag", + max_bin_label: int | None = None, + ) -> pd.DataFrame: + """ + Generate regulator-level performance summary for a comparison. + + :param comparison_id: Identifier for the comparison + :param regulator_column: Column name for regulator identifiers + :param max_bin_label: Maximum bin label to consider (e.g., 20 for top 20 + targets) + :return: DataFrame with regulator-level summary statistics + + """ + intermediate_table_name = f"rank_response_{comparison_id}" + + if not self.db.table_exists(intermediate_table_name): + raise ValueError( + f"Intermediate table '{intermediate_table_name}' does not exist." + ) + + where_clause = "" + if max_bin_label: + where_clause = f"WHERE bin_label <= {max_bin_label}" + + regulator_summary_sql = f""" + SELECT + {regulator_column}, + COUNT(*) as total_targets, + SUM(responsive) as total_responsive, + COUNT(DISTINCT bin_label) as num_bins, + MAX(cumulative_responsive) as max_cumulative_responsive, + MAX(bin_label) as max_bin_label, + MAX(cumulative_responsive) / MAX(bin_label) + as overall_response_rate, + AVG(CASE WHEN bin_label <= 5 THEN responsive ELSE NULL END) + as top5_response_rate, + AVG(CASE WHEN bin_label <= 10 THEN responsive ELSE NULL END) + as top10_response_rate, + AVG(CASE WHEN bin_label <= 20 THEN responsive ELSE NULL END) + as top20_response_rate + FROM {intermediate_table_name} + {where_clause} + GROUP BY {regulator_column} + ORDER BY overall_response_rate DESC + """ + + return self.db.query(regulator_summary_sql) + + def summarize( + self, + comparison_id: str, + summary_type: str = "bin", + regulator_column: str = "regulator_locus_tag", + bin_size: int = 5, + regulators_filter: list[str] | None = None, + max_bin_label: int | None = None, + ) -> pd.DataFrame: + """ + Generate summary for a specific comparison. + + :param comparison_id: Identifier for the comparison to summarize + :param summary_type: Type of summary ('bin' or 'regulator') + :param regulator_column: Column name for regulator identifiers + :param bin_size: Bin size used in analysis + :param regulators_filter: Optional list of regulators to include + :param max_bin_label: Maximum bin label to consider (for regulator summaries) + :return: DataFrame with summary results + + """ + if summary_type == "bin": + return self.get_bin_summary( + comparison_id=comparison_id, + regulator_column=regulator_column, + bin_size=bin_size, + regulators_filter=regulators_filter, + ) + elif summary_type == "regulator": + return self.get_regulator_summary( + comparison_id=comparison_id, + regulator_column=regulator_column, + max_bin_label=max_bin_label, + ) + else: + raise ValueError(f"Unknown summary type: {summary_type}") + + def query(self, sql: str) -> pd.DataFrame: + """ + Execute custom SQL query on the database. + + :param sql: SQL query to execute + :return: DataFrame with query results + + """ + return self.db.query(sql) + + def get_comparison_data( + self, + comparison_id: str, + regulator_filter: list[str] | None = None, + limit: int | None = None, + ) -> pd.DataFrame: + """ + Get raw data for a specific comparison. + + :param comparison_id: Identifier for the comparison + :param regulator_filter: Optional list of regulators to filter + :param limit: Optional limit on number of records + :return: DataFrame with raw comparison data + + """ + table_name = f"rank_response_{comparison_id}" + + if not self.db.table_exists(table_name): + raise ValueError(f"No results found for comparison '{comparison_id}'") + + filters = {} + if regulator_filter: + filters["regulator_locus_tag"] = regulator_filter + + return self.db.get_results(table_name, filters=filters, limit=limit) + + def compare_across_datasets( + self, + comparison_ids: list[str], + regulator_column: str = "regulator_locus_tag", + metric_columns: list[str] = ["overall_response_rate", "top10_response_rate"], + ) -> pd.DataFrame: + """ + Compare regulator performance across multiple dataset comparisons. + + :param comparison_ids: List of comparison identifiers to compare + :param regulator_column: Column name for regulator identifiers + :param metric_columns: Performance metrics to compare + :return: DataFrame with cross-comparison results + + """ + comparison_data = [] + + for comp_id in comparison_ids: + summary = self.summarize(comp_id, summary_type="regulator") + summary["comparison_id"] = comp_id + comparison_data.append( + summary[[regulator_column, "comparison_id"] + metric_columns] + ) + + if not comparison_data: + return pd.DataFrame() + + # Combine all comparisons + combined = pd.concat(comparison_data, ignore_index=True) + + # Pivot to have comparisons as columns + result_dfs = [] + for metric in metric_columns: + pivot = combined.pivot( + index=regulator_column, columns="comparison_id", values=metric + ) + pivot.columns = [f"{metric}_{col}" for col in pivot.columns] + result_dfs.append(pivot) + + if len(result_dfs) == 1: + return result_dfs[0].reset_index() + else: + final_result = result_dfs[0] + for df in result_dfs[1:]: + final_result = final_result.join(df) + return final_result.reset_index() diff --git a/tfbpapi/IncrementalAnalysisDB.py b/tfbpapi/IncrementalAnalysisDB.py new file mode 100644 index 0000000..9e8d53e --- /dev/null +++ b/tfbpapi/IncrementalAnalysisDB.py @@ -0,0 +1,340 @@ +import logging +from pathlib import Path +from typing import Any + +import duckdb +import pandas as pd + + +class IncrementalAnalysisDB: + """ + Class for managing incremental analysis results in DuckDB. + + Supports appending new results, updating existing ones, and maintaining analysis + metadata for tracking what's been computed. + + """ + + def __init__(self, db_path: str): + """ + Initialize connection to persistent DuckDB database. + + :param db_path: Path to the DuckDB database file + + """ + self.db_path = db_path + Path(db_path).parent.mkdir(parents=True, exist_ok=True) + self.conn = duckdb.connect(db_path) + self.logger = logging.getLogger(__name__) + + # Create metadata table to track analyses + self._ensure_metadata_table() + + def _ensure_metadata_table(self): + """Create metadata table if it doesn't exist.""" + self.conn.execute( + """ + CREATE TABLE IF NOT EXISTS analysis_metadata ( + table_name VARCHAR PRIMARY KEY, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + total_records INTEGER, + analysis_type VARCHAR, + parameters JSON, + description TEXT + ) + """ + ) + + def append_results( + self, + new_results: pd.DataFrame, + table_name: str, + analysis_type: str = "response_rate", + parameters: dict | None = None, + description: str | None = None, + deduplicate_on: list[str] | None = None, + ) -> int: + """ + Append new analysis results to an existing table. + + :param new_results: DataFrame with new results to append + :param table_name: Name of the target table + :param analysis_type: Type of analysis for metadata + :param parameters: Parameters used in the analysis + :param description: Description of the analysis + :param deduplicate_on: Column names to deduplicate on + :return: Number of records added + + """ + if new_results.empty: + self._update_metadata(table_name, 0, analysis_type, parameters, description) + return 0 + + # Handle deduplication if specified + if deduplicate_on and self.table_exists(table_name): + existing_data = self.get_results(table_name) + if not existing_data.empty: + # Remove duplicates based on specified columns + merged = pd.merge( + new_results, + existing_data[deduplicate_on], + on=deduplicate_on, + how="left", + indicator=True, + ) + new_results = merged[merged["_merge"] == "left_only"].drop( + "_merge", axis=1 + ) + + # Insert new data + if not new_results.empty: + self.conn.register("new_data", new_results) + if self.table_exists(table_name): + self.conn.execute(f"INSERT INTO {table_name} SELECT * FROM new_data") + else: + self.conn.execute( + f"CREATE TABLE {table_name} AS SELECT * FROM new_data" + ) + self.conn.unregister("new_data") + + records_added = len(new_results) + self._update_metadata( + table_name, records_added, analysis_type, parameters, description + ) + + return records_added + + def update_results( + self, updated_data: pd.DataFrame, table_name: str, key_columns: list[str] + ) -> int: + """ + Update existing records in a table. + + :param updated_data: DataFrame with updated values + :param table_name: Name of the target table + :param key_columns: Columns to match records on + :return: Number of records updated + + """ + if not self.table_exists(table_name) or updated_data.empty: + return 0 + + records_updated = 0 + self.conn.register("update_data", updated_data) + + # Build SET clause for non-key columns + non_key_columns = [ + col for col in updated_data.columns if col not in key_columns + ] + set_clause = ", ".join( + [f"{col} = update_data.{col}" for col in non_key_columns] + ) + + # Build WHERE clause for key columns + where_clause = " AND ".join( + [f"{table_name}.{col} = update_data.{col}" for col in key_columns] + ) + + update_query = f""" + UPDATE {table_name} + SET {set_clause} + FROM update_data + WHERE {where_clause} + """ + + self.conn.execute(update_query) + records_updated = len(updated_data) + + self.conn.unregister("update_data") + self._update_metadata_timestamp(table_name) + + return records_updated + + def query(self, sql: str) -> pd.DataFrame: + """ + Execute a SQL query and return results as DataFrame. + + :param sql: SQL query to execute + :return: DataFrame with query results + + """ + return self.conn.execute(sql).fetchdf() + + def get_results( + self, + table_name: str, + filters: dict[str, Any] | None = None, + limit: int | None = None, + ) -> pd.DataFrame: + """ + Retrieve results from a table. + + :param table_name: Name of the table to query + :param filters: Optional filters to apply + :param limit: Optional limit on number of records + :return: DataFrame with results + + """ + if not self.table_exists(table_name): + raise ValueError(f"Table {table_name} does not exist") + + query = f"SELECT * FROM {table_name}" + + if filters: + where_conditions = [] + for column, values in filters.items(): + if isinstance(values, list): + values_str = ", ".join( + [f"'{v}'" if isinstance(v, str) else str(v) for v in values] + ) + where_conditions.append(f"{column} IN ({values_str})") + else: + if isinstance(values, str): + where_conditions.append(f"{column} = '{values}'") + else: + where_conditions.append(f"{column} = {values}") + + if where_conditions: + query += " WHERE " + " AND ".join(where_conditions) + + if limit: + query += f" LIMIT {limit}" + + return self.conn.execute(query).fetchdf() + + def table_exists(self, table_name: str) -> bool: + """Check if a table exists in the database.""" + result = self.conn.execute( + """ + SELECT table_name FROM information_schema.tables + WHERE table_name = ? AND table_schema = 'main' + """, + [table_name], + ).fetchall() + return len(result) > 0 + + def drop_table(self, table_name: str) -> None: + """Drop a table and its metadata.""" + if self.table_exists(table_name): + self.conn.execute(f"DROP TABLE {table_name}") + self.conn.execute( + "DELETE FROM analysis_metadata WHERE table_name = ?", [table_name] + ) + + def get_table_info(self, table_name: str) -> dict[str, Any]: + """Get metadata information about a table.""" + if not self.table_exists(table_name): + raise ValueError(f"Table {table_name} does not exist") + + result = self.conn.execute( + """ + SELECT * FROM analysis_metadata WHERE table_name = ? + """, + [table_name], + ).fetchdf() + + if result.empty: + raise ValueError(f"No metadata found for table {table_name}") + + return result.iloc[0].to_dict() + + def list_tables(self) -> list[str]: + """List all tables in the database.""" + result = self.conn.execute( + """ + SELECT table_name FROM information_schema.tables + WHERE table_schema = 'main' + """ + ).fetchall() + return [row[0] for row in result] + + def get_table_schema(self, table_name: str) -> list[dict[str, str]]: + """Get schema information for a table.""" + if not self.table_exists(table_name): + raise ValueError(f"Table {table_name} does not exist") + + result = self.conn.execute(f"DESCRIBE {table_name}").fetchall() + # TODO: fix the mypy ignore/typing + return [ + { + "column_name": row[0], + "column_type": row[1], + "null": row[2], + "key": row[3] if len(row) > 3 else None, # type: ignore + "default": row[4] if len(row) > 4 else None, # type: ignore + "extra": row[5] if len(row) > 5 else None, # type: ignore + } + for row in result + ] + + def close(self) -> None: + """Close the database connection.""" + if hasattr(self, "conn"): + self.conn.close() + + def __enter__(self): + """Context manager entry.""" + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + """Context manager exit.""" + self.close() + + def _update_metadata( + self, + table_name: str, + records_added: int, + analysis_type: str, + parameters: dict | None, + description: str | None, + ) -> None: + """Update or insert metadata for a table.""" + import json + + # Check if metadata exists + existing = self.conn.execute( + """ + SELECT total_records FROM analysis_metadata WHERE table_name = ? + """, + [table_name], + ).fetchall() + + if existing: + # Update existing metadata + new_total = existing[0][0] + records_added + self.conn.execute( + """ + UPDATE analysis_metadata + SET last_updated = CURRENT_TIMESTAMP, total_records = ? + WHERE table_name = ? + """, + [new_total, table_name], + ) + else: + # Insert new metadata + self.conn.execute( + """ + INSERT INTO analysis_metadata + (table_name, total_records, analysis_type, parameters, description) + VALUES (?, ?, ?, ?, ?) + """, + [ + table_name, + records_added, + analysis_type, + json.dumps(parameters) if parameters else None, + description, + ], + ) + + def _update_metadata_timestamp(self, table_name: str) -> None: + """Update the last_updated timestamp for a table.""" + self.conn.execute( + """ + UPDATE analysis_metadata + SET last_updated = CURRENT_TIMESTAMP + WHERE table_name = ? + """, + [table_name], + ) diff --git a/tfbpapi/tests/test_HfQueryAPI.py b/tfbpapi/tests/test_HfQueryAPI.py new file mode 100644 index 0000000..1aeeaea --- /dev/null +++ b/tfbpapi/tests/test_HfQueryAPI.py @@ -0,0 +1,530 @@ +import tempfile +from pathlib import Path +from unittest.mock import Mock, patch + +import pandas as pd +import pytest +from datasets import Dataset, DatasetDict + +from tfbpapi.HfQueryAPI import HfQueryAPI + + +@pytest.fixture +def mock_dataset_card(): + """Mock DatasetCard.load to return fake card data.""" + with patch("tfbpapi.HfQueryAPI.DatasetCard.load") as mock: + mock_card = Mock() + mock_card.data.to_dict.return_value = { + "configs": [ + { + "config_name": "default", + "dataset_info": { + "features": [ + { + "name": "text", + "dtype": "string", + "description": "Input text", + }, + { + "name": "label", + "dtype": "int64", + "description": "Classification label", + }, + ] + }, + "data_files": [ + {"path": "data/train.parquet", "split": "train"}, + {"path": "data/test.parquet", "split": "test"}, + ], + } + ] + } + mock.return_value = mock_card + yield mock + + +@pytest.fixture +def mock_load_dataset(): + """Mock load_dataset to return fake dataset.""" + with patch("tfbpapi.HfQueryAPI.load_dataset") as mock: + # Create mock dataset with sample data + mock_dataset_dict = DatasetDict( + { + "train": Dataset.from_pandas( + pd.DataFrame( + {"text": ["hello", "world", "test"], "label": [0, 1, 0]} + ) + ), + "test": Dataset.from_pandas( + pd.DataFrame({"text": ["sample", "data"], "label": [1, 0]}) + ), + } + ) + mock.return_value = mock_dataset_dict + yield mock + + +@pytest.fixture +def mock_duckdb(): + """Mock DuckDB connection.""" + with patch("tfbpapi.HfQueryAPI.duckdb.connect") as mock_connect: + mock_conn = Mock() + mock_result = Mock() + mock_result.fetchdf.return_value = pd.DataFrame({"count": [3]}) + mock_conn.execute.return_value = mock_result + mock_connect.return_value = mock_conn + yield mock_conn + + +@pytest.fixture +def mock_hf_api_init(): + """Mock AbstractHfAPI initialization to prevent real API calls.""" + # Instead of mocking the __init__, we'll mock the methods that cause issues + with patch( + "tfbpapi.AbstractHfAPI.AbstractHfAPI._get_dataset_size" + ) as mock_get_size: + mock_get_size.return_value = None + yield mock_get_size + + +@pytest.fixture +def temp_cache_dir(): + """Create a temporary directory for cache testing.""" + with tempfile.TemporaryDirectory() as temp_dir: + yield Path(temp_dir) + + +class TestHfQueryAPI: + """Test cases for HfQueryAPI.""" + + def test_init_with_auto_parse( + self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir + ): + """Test initialization with auto_parse_datacard=True.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + # Verify initialization + assert api.auto_download_threshold_mb == 100.0 + assert api._datasets == { + "default": { + "features": { + "text": {"dtype": "string", "description": "Input text"}, + "label": {"dtype": "int64", "description": "Classification label"}, + }, + "data_files": [ + {"path": "data/train.parquet", "split": "train"}, + {"path": "data/test.parquet", "split": "test"}, + ], + "config": { + "config_name": "default", + "dataset_info": { + "features": [ + { + "name": "text", + "dtype": "string", + "description": "Input text", + }, + { + "name": "label", + "dtype": "int64", + "description": "Classification label", + }, + ] + }, + "data_files": [ + {"path": "data/train.parquet", "split": "train"}, + {"path": "data/test.parquet", "split": "test"}, + ], + }, + "loaded": False, + } + } + mock_dataset_card.assert_called_once() + + def test_init_without_auto_parse( + self, mock_hf_api_init, mock_duckdb, temp_cache_dir + ): + """Test initialization with auto_parse_datacard=False.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=False + ) + + assert api._datasets == {} + + def test_init_parse_datacard_failure( + self, mock_hf_api_init, mock_duckdb, temp_cache_dir + ): + """Test initialization when parse_datacard fails.""" + with patch("tfbpapi.HfQueryAPI.DatasetCard.load") as mock_card: + mock_card.side_effect = Exception("Failed to load") + + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + assert api._datasets == {} + # Logger warning should have been called during init + + def test_datasets_property( + self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir + ): + """Test datasets property getter and setter.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + # Test getter + assert "default" in api.datasets + + # Test setter + new_datasets = {"custom": {"features": {}, "data_files": [], "loaded": False}} + api.datasets = new_datasets + assert api.datasets == new_datasets + + def test_available_tables( + self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir + ): + """Test available_tables property.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + assert api.available_tables == ["default"] + + def test_parse_datacard_success( + self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir + ): + """Test successful datacard parsing.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=False + ) + + result = api.parse_datacard() + + assert "default" in result + assert "features" in result["default"] + assert "text" in result["default"]["features"] + assert "label" in result["default"]["features"] + + def test_parse_datacard_failure( + self, mock_hf_api_init, mock_duckdb, temp_cache_dir + ): + """Test datacard parsing failure.""" + with patch("tfbpapi.HfQueryAPI.DatasetCard.load") as mock_card: + mock_card.side_effect = Exception("Load failed") + + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=False + ) + + result = api.parse_datacard() + + assert result == {} + + def test_ensure_dataset_loaded_not_found( + self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir + ): + """Test _ensure_dataset_loaded with non-existent table.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + with pytest.raises(ValueError, match="Table 'nonexistent' not found"): + api._ensure_dataset_loaded("nonexistent") + + def test_ensure_dataset_loaded_already_loaded( + self, + mock_hf_api_init, + mock_dataset_card, + mock_load_dataset, + mock_duckdb, + temp_cache_dir, + ): + """Test _ensure_dataset_loaded when dataset already loaded.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + # Pre-load a dataset + mock_dataset = Mock() + api._loaded_datasets["default"] = mock_dataset + + result = api._ensure_dataset_loaded("default") + assert result == mock_dataset + mock_load_dataset.assert_not_called() + + def test_ensure_dataset_loaded_download_and_load( + self, + mock_hf_api_init, + mock_dataset_card, + mock_load_dataset, + mock_duckdb, + temp_cache_dir, + ): + """Test _ensure_dataset_loaded with download and load.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + # Mock download method - ensure snapshot_path is None initially + api.snapshot_path = None + mock_download = Mock() + mock_download.return_value = Path("/fake/snapshot") + api.download = mock_download # type: ignore + + # Set side effect to simulate download setting snapshot_path + def download_side_effect(**kwargs): + api.snapshot_path = Path("/fake/snapshot") + return Path("/fake/snapshot") + + mock_download.side_effect = download_side_effect + + result = api._ensure_dataset_loaded("default") + + assert result == mock_load_dataset.return_value + mock_download.assert_called_once_with(auto_download_threshold_mb=100.0) + mock_load_dataset.assert_called_once() + assert api._datasets["default"]["loaded"] is True + + def test_query_with_table_name( + self, + mock_hf_api_init, + mock_dataset_card, + mock_load_dataset, + mock_duckdb, + temp_cache_dir, + ): + """Test query with explicit table name.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + # Mock the dataset loading + api.snapshot_path = Path("/fake/snapshot") + + result = api.query("SELECT * FROM default", table_name="default") + + assert isinstance(result, pd.DataFrame) + mock_duckdb.execute.assert_called_once_with("SELECT * FROM default") + + def test_query_infer_table_name( + self, + mock_hf_api_init, + mock_dataset_card, + mock_load_dataset, + mock_duckdb, + temp_cache_dir, + ): + """Test query with table name inference.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + # Mock the dataset loading + api.snapshot_path = Path("/fake/snapshot") + + result = api.query("SELECT * FROM default") + + assert isinstance(result, pd.DataFrame) + mock_duckdb.execute.assert_called_once_with("SELECT * FROM default") + + def test_describe_table( + self, + mock_hf_api_init, + mock_dataset_card, + mock_load_dataset, + mock_duckdb, + temp_cache_dir, + ): + """Test describe_table method.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + # Mock the dataset loading + api.snapshot_path = Path("/fake/snapshot") + + result = api.describe_table("default") + + assert isinstance(result, pd.DataFrame) + mock_duckdb.execute.assert_called_with("DESCRIBE default") + + def test_sample( + self, + mock_hf_api_init, + mock_dataset_card, + mock_load_dataset, + mock_duckdb, + temp_cache_dir, + ): + """Test sample method.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + # Mock the dataset loading + api.snapshot_path = Path("/fake/snapshot") + + result = api.sample("default", n=3) + + assert isinstance(result, pd.DataFrame) + mock_duckdb.execute.assert_called_with("SELECT * FROM default LIMIT 3") + + def test_count( + self, + mock_hf_api_init, + mock_dataset_card, + mock_load_dataset, + mock_duckdb, + temp_cache_dir, + ): + """Test count method.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + # Mock the dataset loading + api.snapshot_path = Path("/fake/snapshot") + + result = api.count("default") + + assert result == 3 + mock_duckdb.execute.assert_called_with("SELECT COUNT(*) as count FROM default") + + def test_get_columns( + self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir + ): + """Test get_columns method.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + result = api.get_columns("default") + assert result == ["text", "label"] + + def test_get_columns_not_found( + self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir + ): + """Test get_columns with non-existent table.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + with pytest.raises(ValueError, match="Table 'nonexistent' not found"): + api.get_columns("nonexistent") + + def test_context_manager( + self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir + ): + """Test context manager functionality.""" + with HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) as api: + assert api is not None + + # Verify close was called + mock_duckdb.close.assert_called_once() + + def test_close( + self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir + ): + """Test close method.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + api.close() + mock_duckdb.close.assert_called_once() + + def test_table_filters_basic( + self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir + ): + """Test basic table filter functionality.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + # Test setting and getting filters + assert api.get_table_filter("default") is None + + api.set_table_filter("default", "text = 'test'") + assert api.get_table_filter("default") == "text = 'test'" + + # Test removing filters + api.remove_table_filter("default") + assert api.get_table_filter("default") is None + + def test_table_filters_query_modification( + self, + mock_hf_api_init, + mock_dataset_card, + mock_load_dataset, + mock_duckdb, + temp_cache_dir, + ): + """Test that table filters modify queries correctly.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + # Mock the dataset loading + api.snapshot_path = Path("/fake/snapshot") + + # Set a filter + api.set_table_filter("default", "label = 1") + + # Execute a query + api.query("SELECT * FROM default", table_name="default") + + # Verify the query was modified to include the filter + expected_sql = "SELECT * FROM (SELECT * FROM default WHERE label = 1)" + mock_duckdb.execute.assert_called_once_with(expected_sql) + + def test_table_filters_no_modification_when_no_filters( + self, + mock_hf_api_init, + mock_dataset_card, + mock_load_dataset, + mock_duckdb, + temp_cache_dir, + ): + """Test that queries are not modified when no filters are set.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + # Mock the dataset loading + api.snapshot_path = Path("/fake/snapshot") + + # Execute a query without any filters + original_sql = "SELECT * FROM default" + api.query(original_sql, table_name="default") + + # Verify the query was not modified + mock_duckdb.execute.assert_called_once_with(original_sql) + + def test_apply_table_filters_method( + self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir + ): + """Test the _apply_table_filters method directly.""" + api = HfQueryAPI( + repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True + ) + + # Test with no filters + sql = "SELECT * FROM default" + assert api._apply_table_filters(sql) == sql + + # Test with filter applied + api.set_table_filter("default", "text LIKE '%test%'") + modified_sql = api._apply_table_filters(sql) + expected = "SELECT * FROM (SELECT * FROM default WHERE text LIKE '%test%')" + assert modified_sql == expected + + # Test that already filtered queries don't get double-wrapped + already_filtered = "SELECT * FROM (SELECT * FROM default WHERE existing = 1)" + result = api._apply_table_filters(already_filtered) + # Should not modify since it already contains a filtered subquery + assert result == already_filtered diff --git a/tfbpapi/tests/test_HfRankResponse.py b/tfbpapi/tests/test_HfRankResponse.py new file mode 100644 index 0000000..922af97 --- /dev/null +++ b/tfbpapi/tests/test_HfRankResponse.py @@ -0,0 +1,505 @@ +import tempfile +from pathlib import Path +from unittest.mock import MagicMock, patch + +import pandas as pd +import pytest + +from tfbpapi.HfRankResponse import HfRankResponse +from tfbpapi.IncrementalAnalysisDB import IncrementalAnalysisDB + + +@pytest.fixture +def temp_db_path(): + """Create temporary database path for testing.""" + with tempfile.TemporaryDirectory() as temp_dir: + db_path = Path(temp_dir) / "test.db" + yield str(db_path) + + +@pytest.fixture +def analysis_db(temp_db_path): + """Create IncrementalAnalysisDB instance for testing.""" + return IncrementalAnalysisDB(temp_db_path) + + +@pytest.fixture +def rank_response(analysis_db): + """Create HfRankResponse instance for testing.""" + return HfRankResponse(analysis_db) + + +@pytest.fixture +def mock_ranking_api(): + """Create mock ranking API.""" + api = MagicMock() + api.get_table_filter.return_value = None + api.query.return_value = pd.DataFrame( + { + "regulator_locus_tag": ["REG1", "REG2", "REG3"], + } + ) + api._duckdb_conn.execute.return_value.fetchdf.return_value = pd.DataFrame( + { + "regulator_locus_tag": ["REG1", "REG1", "REG2", "REG2", "REG3"], + "target_locus_tag": ["TGT1", "TGT2", "TGT1", "TGT3", "TGT1"], + "binding_score": [10.5, 8.2, 9.1, 7.8, 6.5], + } + ) + return api + + +@pytest.fixture +def mock_response_api(): + """Create mock response API.""" + api = MagicMock() + api.get_table_filter.return_value = None + api._duckdb_conn.execute.return_value.fetchdf.return_value = pd.DataFrame( + { + "regulator_locus_tag": ["REG1", "REG1", "REG2"], + "target_locus_tag": ["TGT1", "TGT2", "TGT1"], + "log2fc": [2.1, -1.5, 1.8], + } + ) + return api + + +@pytest.fixture +def sample_computed_data(): + """Create sample computed rank response data.""" + return pd.DataFrame( + { + "regulator_locus_tag": ["REG1", "REG1", "REG1", "REG2", "REG2", "REG3"], + "target_locus_tag": ["TGT1", "TGT2", "TGT3", "TGT1", "TGT2", "TGT1"], + "binding_score": [10.5, 8.2, 7.1, 9.1, 7.8, 6.5], + "log2fc": [2.1, -1.5, None, 1.8, None, None], + "responsive": [1, 1, 0, 1, 0, 0], + "bin_label": [5, 5, 10, 5, 10, 5], + "cumulative_responsive": [2, 2, 2, 1, 1, 0], + } + ) + + +class TestHfRankResponse: + + def test_init(self, analysis_db): + """Test HfRankResponse initialization.""" + rr = HfRankResponse(analysis_db) + assert rr.db == analysis_db + assert rr.logger is not None + + def test_get_comparisons_empty(self, rank_response): + """Test getting comparisons when none exist.""" + comparisons = rank_response.get_comparisons() + assert comparisons == [] + + def test_get_comparisons_with_data(self, rank_response, sample_computed_data): + """Test getting comparisons with existing data.""" + # Add some test data + rank_response.db.append_results( + sample_computed_data, "rank_response_test_comparison_1" + ) + rank_response.db.append_results( + sample_computed_data, "rank_response_test_comparison_2" + ) + + comparisons = rank_response.get_comparisons() + assert "test_comparison_1" in comparisons + assert "test_comparison_2" in comparisons + assert len(comparisons) == 2 + + @patch("tfbpapi.HfRankResponse.duckdb.connect") + def test_compute_new_comparison( + self, + mock_duckdb_connect, + rank_response, + mock_ranking_api, + mock_response_api, + sample_computed_data, + ): + """Test computing a new comparison.""" + # Mock the temporary connection + mock_temp_conn = MagicMock() + mock_duckdb_connect.return_value = mock_temp_conn + mock_temp_conn.execute.return_value.fetchdf.return_value = sample_computed_data + + result = rank_response.compute( + ranking_api=mock_ranking_api, + response_api=mock_response_api, + ranking_table="binding_data", + response_table="expression_data", + ranking_score_column="binding_score", + response_column="log2fc", + comparison_id="test_comp", + bin_size=5, + ) + + # Verify APIs were called correctly + mock_ranking_api._ensure_dataset_loaded.assert_called() + mock_response_api._ensure_dataset_loaded.assert_called() + + # Verify data was stored + assert rank_response.db.table_exists("rank_response_test_comp") + + # Verify result + assert not result.empty + assert "regulator_locus_tag" in result.columns + + def test_compute_auto_generated_comparison_id( + self, rank_response, mock_ranking_api, mock_response_api + ): + """Test compute with auto-generated comparison ID.""" + with patch("tfbpapi.HfRankResponse.duckdb.connect") as mock_duckdb: + mock_temp_conn = MagicMock() + mock_duckdb.return_value = mock_temp_conn + mock_temp_conn.execute.return_value.fetchdf.return_value = pd.DataFrame( + { + "regulator_locus_tag": ["REG1"], + "target_locus_tag": ["TGT1"], + "binding_score": [10.0], + "log2fc": [2.0], + "responsive": [1], + "bin_label": [5], + "cumulative_responsive": [1], + } + ) + + rank_response.compute( + ranking_api=mock_ranking_api, + response_api=mock_response_api, + ranking_table="binding_table", + response_table="expression_table", + ranking_score_column="binding_score", + response_column="log2fc", + ) + + # Should create table with auto-generated ID + assert rank_response.db.table_exists( + "rank_response_binding_table_vs_expression_table" + ) + + def test_compute_incremental_update( + self, rank_response, mock_ranking_api, mock_response_api, sample_computed_data + ): + """Test incremental computation with existing data.""" + # First, add some existing data + rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") + + # Mock APIs to return new regulators + mock_ranking_api.query.return_value = pd.DataFrame( + { + "regulator_locus_tag": ["REG1", "REG2", "REG3", "REG4"], # REG4 is new + } + ) + + with patch("tfbpapi.HfRankResponse.duckdb.connect") as mock_duckdb: + mock_temp_conn = MagicMock() + mock_duckdb.return_value = mock_temp_conn + # Return data for new regulator only + mock_temp_conn.execute.return_value.fetchdf.return_value = pd.DataFrame( + { + "regulator_locus_tag": ["REG4"], + "target_locus_tag": ["TGT1"], + "binding_score": [5.0], + "log2fc": [1.0], + "responsive": [1], + "bin_label": [5], + "cumulative_responsive": [1], + } + ) + + result = rank_response.compute( + ranking_api=mock_ranking_api, + response_api=mock_response_api, + ranking_table="binding_data", + response_table="expression_data", + ranking_score_column="binding_score", + response_column="log2fc", + comparison_id="test_comp", + ) + + # Should have data for all regulators now + regulators = set(result["regulator_locus_tag"].unique()) + assert "REG1" in regulators + assert "REG4" in regulators + + def test_get_bin_summary(self, rank_response, sample_computed_data): + """Test generating bin-level summary.""" + # Add test data + rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") + + summary = rank_response.get_bin_summary("test_comp") + + assert not summary.empty + assert "regulator_locus_tag" in summary.columns + assert "bin_label" in summary.columns + assert "records_in_bin" in summary.columns + assert "responsive_in_bin" in summary.columns + assert "cumulative_responsive" in summary.columns + assert "response_rate" in summary.columns + + def test_get_bin_summary_with_filter(self, rank_response, sample_computed_data): + """Test bin summary with regulator filter.""" + rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") + + summary = rank_response.get_bin_summary( + "test_comp", regulators_filter=["REG1", "REG2"] + ) + + assert not summary.empty + regulators = set(summary["regulator_locus_tag"].unique()) + assert regulators.issubset({"REG1", "REG2"}) + assert "REG3" not in regulators + + def test_get_regulator_summary(self, rank_response, sample_computed_data): + """Test generating regulator-level summary.""" + rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") + + summary = rank_response.get_regulator_summary("test_comp") + + assert not summary.empty + assert "regulator_locus_tag" in summary.columns + assert "total_targets" in summary.columns + assert "total_responsive" in summary.columns + assert "overall_response_rate" in summary.columns + assert "top5_response_rate" in summary.columns + assert "top10_response_rate" in summary.columns + assert "top20_response_rate" in summary.columns + + def test_get_regulator_summary_with_max_bin( + self, rank_response, sample_computed_data + ): + """Test regulator summary with max bin limit.""" + rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") + + summary = rank_response.get_regulator_summary("test_comp", max_bin_label=5) + + assert not summary.empty + # Should only include data from bins <= 5 + + def test_summarize_bin_type(self, rank_response, sample_computed_data): + """Test summarize method with bin type.""" + rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") + + summary = rank_response.summarize("test_comp", summary_type="bin") + + assert not summary.empty + assert "bin_label" in summary.columns + + def test_summarize_regulator_type(self, rank_response, sample_computed_data): + """Test summarize method with regulator type.""" + rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") + + summary = rank_response.summarize("test_comp", summary_type="regulator") + + assert not summary.empty + assert "overall_response_rate" in summary.columns + + def test_summarize_invalid_type(self, rank_response, sample_computed_data): + """Test summarize with invalid summary type.""" + rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") + + with pytest.raises(ValueError, match="Unknown summary type"): + rank_response.summarize("test_comp", summary_type="invalid") + + def test_query_method(self, rank_response, sample_computed_data): + """Test direct SQL query method.""" + rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") + + result = rank_response.query( + "SELECT COUNT(*) as count FROM rank_response_test_comp" + ) + + assert not result.empty + assert result.iloc[0]["count"] == len(sample_computed_data) + + def test_get_comparison_data(self, rank_response, sample_computed_data): + """Test getting raw comparison data.""" + rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") + + data = rank_response.get_comparison_data("test_comp") + + assert not data.empty + assert len(data) == len(sample_computed_data) + + def test_get_comparison_data_with_filter(self, rank_response, sample_computed_data): + """Test getting comparison data with regulator filter.""" + rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") + + data = rank_response.get_comparison_data("test_comp", regulator_filter=["REG1"]) + + assert not data.empty + assert all(data["regulator_locus_tag"] == "REG1") + + def test_get_comparison_data_with_limit(self, rank_response, sample_computed_data): + """Test getting comparison data with limit.""" + rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") + + data = rank_response.get_comparison_data("test_comp", limit=3) + + assert len(data) == 3 + + def test_compare_across_datasets(self, rank_response, sample_computed_data): + """Test comparing across multiple datasets.""" + # Add data for multiple comparisons + rank_response.db.append_results(sample_computed_data, "rank_response_comp1") + rank_response.db.append_results(sample_computed_data, "rank_response_comp2") + + comparison = rank_response.compare_across_datasets(["comp1", "comp2"]) + + assert not comparison.empty + assert "regulator_locus_tag" in comparison.columns + # Should have columns for each comparison and metric + assert any("overall_response_rate_" in col for col in comparison.columns) + + def test_compare_across_datasets_empty(self, rank_response): + """Test comparing across datasets with no data.""" + comparison = rank_response.compare_across_datasets([]) + + assert comparison.empty + + def test_compare_across_datasets_custom_metrics( + self, rank_response, sample_computed_data + ): + """Test comparing with custom metric columns.""" + rank_response.db.append_results(sample_computed_data, "rank_response_comp1") + + comparison = rank_response.compare_across_datasets( + ["comp1"], metric_columns=["top5_response_rate"] + ) + + assert not comparison.empty + assert any("top5_response_rate_" in col for col in comparison.columns) + + def test_nonexistent_comparison_error(self, rank_response): + """Test error handling for nonexistent comparisons.""" + with pytest.raises(ValueError, match="No results found"): + rank_response.get_comparison_data("nonexistent") + + with pytest.raises(ValueError, match="does not exist"): + rank_response.get_bin_summary("nonexistent") + + with pytest.raises(ValueError, match="does not exist"): + rank_response.get_regulator_summary("nonexistent") + + def test_compute_with_existing_filters( + self, rank_response, mock_ranking_api, mock_response_api + ): + """Test compute when APIs already have filters set.""" + # Set existing filters + mock_ranking_api.get_table_filter.return_value = "existing_filter = 'value'" + mock_response_api.get_table_filter.return_value = "another_filter = 'value'" + + with patch("tfbpapi.HfRankResponse.duckdb.connect") as mock_duckdb: + mock_temp_conn = MagicMock() + mock_duckdb.return_value = mock_temp_conn + mock_temp_conn.execute.return_value.fetchdf.return_value = pd.DataFrame( + { + "regulator_locus_tag": ["REG1"], + "target_locus_tag": ["TGT1"], + "binding_score": [10.0], + "log2fc": [2.0], + "responsive": [1], + "bin_label": [5], + "cumulative_responsive": [1], + } + ) + + rank_response.compute( + ranking_api=mock_ranking_api, + response_api=mock_response_api, + ranking_table="binding_data", + response_table="expression_data", + ranking_score_column="binding_score", + response_column="log2fc", + comparison_id="with_filters", + ) + + # Verify filters were combined with AND + calls = mock_ranking_api.set_table_filter.call_args_list + assert len(calls) > 0 + combined_filter = calls[0][0][1] # Second argument of first call + assert "existing_filter = 'value'" in combined_filter + assert "AND" in combined_filter + + @patch("tfbpapi.HfRankResponse.logging.getLogger") + def test_logging_setup(self, mock_get_logger, analysis_db): + """Test that logging is properly configured.""" + mock_logger = MagicMock() + mock_get_logger.return_value = mock_logger + + rr = HfRankResponse(analysis_db) + + mock_get_logger.assert_called_once_with("tfbpapi.HfRankResponse") + assert rr.logger == mock_logger + + def test_compute_with_custom_responsive_condition( + self, rank_response, mock_ranking_api, mock_response_api + ): + """Test compute with custom responsive condition.""" + with patch("tfbpapi.HfRankResponse.duckdb.connect") as mock_duckdb: + mock_temp_conn = MagicMock() + mock_duckdb.return_value = mock_temp_conn + mock_temp_conn.execute.return_value.fetchdf.return_value = pd.DataFrame( + { + "regulator_locus_tag": ["REG1"], + "target_locus_tag": ["TGT1"], + "binding_score": [10.0], + "log2fc": [2.0], + "responsive": [1], + "bin_label": [5], + "cumulative_responsive": [1], + } + ) + + rank_response.compute( + ranking_api=mock_ranking_api, + response_api=mock_response_api, + ranking_table="binding_data", + response_table="expression_data", + ranking_score_column="binding_score", + response_column="log2fc", + comparison_id="custom_responsive", + responsive_condition="log2fc IS NOT NULL AND log2fc != 0", + ) + + # Verify the SQL contains the custom responsive condition + sql_calls = mock_temp_conn.execute.call_args_list + assert len(sql_calls) > 0 + executed_sql = sql_calls[0][0][0] + assert "b.log2fc IS NOT NULL AND b.log2fc != 0" in executed_sql + + def test_compute_with_default_responsive_condition( + self, rank_response, mock_ranking_api, mock_response_api + ): + """Test compute with default responsive condition (IS NOT NULL).""" + with patch("tfbpapi.HfRankResponse.duckdb.connect") as mock_duckdb: + mock_temp_conn = MagicMock() + mock_duckdb.return_value = mock_temp_conn + mock_temp_conn.execute.return_value.fetchdf.return_value = pd.DataFrame( + { + "regulator_locus_tag": ["REG1"], + "target_locus_tag": ["TGT1"], + "binding_score": [10.0], + "log2fc": [2.0], + "responsive": [1], + "bin_label": [5], + "cumulative_responsive": [1], + } + ) + + rank_response.compute( + ranking_api=mock_ranking_api, + response_api=mock_response_api, + ranking_table="binding_data", + response_table="expression_data", + ranking_score_column="binding_score", + response_column="log2fc", + comparison_id="default_responsive", + ) + + # Verify the SQL contains the default responsive condition + sql_calls = mock_temp_conn.execute.call_args_list + assert len(sql_calls) > 0 + executed_sql = sql_calls[0][0][0] + assert "b.log2fc IS NOT NULL" in executed_sql diff --git a/tfbpapi/tests/test_IncrementalAnalysisDB.py b/tfbpapi/tests/test_IncrementalAnalysisDB.py new file mode 100644 index 0000000..0fbb1e1 --- /dev/null +++ b/tfbpapi/tests/test_IncrementalAnalysisDB.py @@ -0,0 +1,341 @@ +import json +import tempfile +from pathlib import Path +from unittest.mock import MagicMock, patch + +import pandas as pd +import pytest + +from tfbpapi.IncrementalAnalysisDB import IncrementalAnalysisDB + + +@pytest.fixture +def temp_db_path(): + """Create temporary database path for testing.""" + with tempfile.TemporaryDirectory() as temp_dir: + db_path = Path(temp_dir) / "test.db" + yield str(db_path) + + +@pytest.fixture +def sample_dataframe(): + """Create sample DataFrame for testing.""" + return pd.DataFrame( + {"id": [1, 2, 3], "name": ["A", "B", "C"], "value": [10.5, 20.3, 15.7]} + ) + + +@pytest.fixture +def analysis_db(temp_db_path): + """Create IncrementalAnalysisDB instance for testing.""" + return IncrementalAnalysisDB(temp_db_path) + + +class TestIncrementalAnalysisDB: + + def test_init_creates_database_and_metadata_table(self, temp_db_path): + """Test that initialization creates the database file and metadata table.""" + db = IncrementalAnalysisDB(temp_db_path) + + # Check database file exists + assert Path(temp_db_path).exists() + + # Check metadata table exists + result = db.conn.execute( + """ + SELECT table_name FROM information_schema.tables + WHERE table_name='analysis_metadata' AND table_schema='main' + """ + ).fetchall() + assert len(result) == 1 + + db.conn.close() + + def test_init_creates_parent_directories(self): + """Test that initialization creates parent directories if they don't exist.""" + with tempfile.TemporaryDirectory() as temp_dir: + nested_path = Path(temp_dir) / "nested" / "path" / "test.db" + db = IncrementalAnalysisDB(str(nested_path)) + + assert nested_path.parent.exists() + assert nested_path.exists() + db.conn.close() + + def test_append_results_new_table(self, analysis_db, sample_dataframe): + """Test appending results to a new table.""" + records_added = analysis_db.append_results( + new_results=sample_dataframe, + table_name="test_table", + analysis_type="test_analysis", + parameters={"param1": "value1"}, + description="Test description", + ) + + assert records_added == 3 + + # Check data was inserted + result = analysis_db.conn.execute("SELECT * FROM test_table").fetchdf() + pd.testing.assert_frame_equal(result, sample_dataframe) + + # Check metadata was inserted + metadata = analysis_db.conn.execute( + """ + SELECT * FROM analysis_metadata WHERE table_name = 'test_table' + """ + ).fetchdf() + + assert len(metadata) == 1 + assert metadata.iloc[0]["analysis_type"] == "test_analysis" + assert metadata.iloc[0]["total_records"] == 3 + assert json.loads(metadata.iloc[0]["parameters"]) == {"param1": "value1"} + assert metadata.iloc[0]["description"] == "Test description" + + def test_append_results_existing_table(self, analysis_db, sample_dataframe): + """Test appending results to an existing table.""" + # First append + analysis_db.append_results(sample_dataframe, "test_table") + + # Second append with new data + new_data = pd.DataFrame( + {"id": [4, 5], "name": ["D", "E"], "value": [25.1, 30.9]} + ) + + records_added = analysis_db.append_results(new_data, "test_table") + assert records_added == 2 + + # Check total records + result = analysis_db.conn.execute( + "SELECT COUNT(*) as count FROM test_table" + ).fetchdf() + assert result.iloc[0]["count"] == 5 + + # Check metadata updated + metadata = analysis_db.conn.execute( + """ + SELECT total_records FROM analysis_metadata WHERE table_name = 'test_table' + """ + ).fetchdf() + assert metadata.iloc[0]["total_records"] == 5 + + def test_append_results_with_deduplication(self, analysis_db): + """Test appending results with deduplication.""" + initial_data = pd.DataFrame( + {"id": [1, 2, 3], "name": ["A", "B", "C"], "value": [10.5, 20.3, 15.7]} + ) + + analysis_db.append_results(initial_data, "test_table", deduplicate_on=["id"]) + + # Append data with some duplicates + new_data = pd.DataFrame( + { + "id": [2, 3, 4], # 2 and 3 are duplicates + "name": ["B2", "C2", "D"], + "value": [20.3, 15.7, 25.1], + } + ) + + records_added = analysis_db.append_results( + new_data, "test_table", deduplicate_on=["id"] + ) + + # Only record with id=4 should be added + assert records_added == 1 + + # Check total records + result = analysis_db.conn.execute( + "SELECT COUNT(*) as count FROM test_table" + ).fetchdf() + assert result.iloc[0]["count"] == 4 + + def test_update_results(self, analysis_db, sample_dataframe): + """Test updating existing results.""" + # First insert data + analysis_db.append_results(sample_dataframe, "test_table") + + # Update data + updated_data = pd.DataFrame( + {"id": [1, 2], "name": ["A_updated", "B_updated"], "value": [100.5, 200.3]} + ) + + records_updated = analysis_db.update_results( + updated_data, "test_table", key_columns=["id"] + ) + + assert records_updated == 2 + + # Check data was updated + result = analysis_db.conn.execute( + """ + SELECT * FROM test_table WHERE id IN (1, 2) ORDER BY id + """ + ).fetchdf() + + assert result.iloc[0]["name"] == "A_updated" + assert result.iloc[1]["name"] == "B_updated" + + def test_get_results(self, analysis_db, sample_dataframe): + """Test retrieving results.""" + analysis_db.append_results(sample_dataframe, "test_table") + + # Get all results + result = analysis_db.get_results("test_table") + pd.testing.assert_frame_equal(result, sample_dataframe) + + # Get results with filter + filtered_result = analysis_db.get_results("test_table", filters={"id": [1, 2]}) + + expected = sample_dataframe[sample_dataframe["id"].isin([1, 2])] + pd.testing.assert_frame_equal(filtered_result.reset_index(drop=True), expected) + + def test_get_results_with_limit(self, analysis_db, sample_dataframe): + """Test retrieving results with limit.""" + analysis_db.append_results(sample_dataframe, "test_table") + + result = analysis_db.get_results("test_table", limit=2) + assert len(result) == 2 + + def test_query_method(self, analysis_db, sample_dataframe): + """Test direct SQL query execution.""" + analysis_db.append_results(sample_dataframe, "test_table") + + # Test basic query + result = analysis_db.query("SELECT * FROM test_table") + assert len(result) == len(sample_dataframe) + pd.testing.assert_frame_equal(result, sample_dataframe) + + # Test query with WHERE clause + result = analysis_db.query("SELECT * FROM test_table WHERE id = 1") + assert len(result) == 1 + assert result.iloc[0]["id"] == 1 + + # Test query with aggregation + result = analysis_db.query("SELECT COUNT(*) as count FROM test_table") + assert result.iloc[0]["count"] == len(sample_dataframe) + + # Test query with complex SQL + result = analysis_db.query( + """ + SELECT name, AVG(value) as avg_value + FROM test_table + GROUP BY name + ORDER BY name + """ + ) + assert len(result) == 3 # Should have 3 distinct names + assert "avg_value" in result.columns + + def test_table_exists(self, analysis_db, sample_dataframe): + """Test checking if table exists.""" + assert not analysis_db.table_exists("test_table") + + analysis_db.append_results(sample_dataframe, "test_table") + assert analysis_db.table_exists("test_table") + + def test_drop_table(self, analysis_db, sample_dataframe): + """Test dropping a table.""" + analysis_db.append_results(sample_dataframe, "test_table") + assert analysis_db.table_exists("test_table") + + analysis_db.drop_table("test_table") + assert not analysis_db.table_exists("test_table") + + # Check metadata was also removed + metadata = analysis_db.conn.execute( + """ + SELECT * FROM analysis_metadata WHERE table_name = 'test_table' + """ + ).fetchdf() + assert len(metadata) == 0 + + def test_get_table_info(self, analysis_db, sample_dataframe): + """Test getting table information.""" + analysis_db.append_results( + sample_dataframe, + "test_table", + analysis_type="test_analysis", + parameters={"param1": "value1"}, + description="Test description", + ) + + info = analysis_db.get_table_info("test_table") + + assert info["table_name"] == "test_table" + assert info["total_records"] == 3 + assert info["analysis_type"] == "test_analysis" + assert json.loads(info["parameters"]) == {"param1": "value1"} + assert info["description"] == "Test description" + + def test_list_tables(self, analysis_db, sample_dataframe): + """Test listing all tables.""" + # Initially should be empty (except metadata table) + tables = analysis_db.list_tables() + assert "analysis_metadata" in tables + + # Add some tables + analysis_db.append_results(sample_dataframe, "table1") + analysis_db.append_results(sample_dataframe, "table2") + + tables = analysis_db.list_tables() + assert "table1" in tables + assert "table2" in tables + assert "analysis_metadata" in tables + + def test_get_table_schema(self, analysis_db, sample_dataframe): + """Test getting table schema.""" + analysis_db.append_results(sample_dataframe, "test_table") + + schema = analysis_db.get_table_schema("test_table") + + # Should have columns from sample dataframe + column_names = [col["column_name"] for col in schema] + assert "id" in column_names + assert "name" in column_names + assert "value" in column_names + + def test_close_connection(self, analysis_db): + """Test closing database connection.""" + analysis_db.close() + + # Connection should be closed + with pytest.raises(Exception): + analysis_db.conn.execute("SELECT 1") + + def test_context_manager(self, temp_db_path, sample_dataframe): + """Test using IncrementalAnalysisDB as context manager.""" + with IncrementalAnalysisDB(temp_db_path) as db: + db.append_results(sample_dataframe, "test_table") + assert db.table_exists("test_table") + + # Connection should be closed after context exit + with pytest.raises(Exception): + db.conn.execute("SELECT 1") + + @patch("tfbpapi.IncrementalAnalysisDB.logging.getLogger") + def test_logging_setup(self, mock_get_logger, temp_db_path): + """Test that logging is properly configured.""" + mock_logger = MagicMock() + mock_get_logger.return_value = mock_logger + + db = IncrementalAnalysisDB(temp_db_path) + + mock_get_logger.assert_called_once_with("tfbpapi.IncrementalAnalysisDB") + assert db.logger == mock_logger + db.conn.close() + + def test_error_handling_nonexistent_table(self, analysis_db): + """Test error handling for operations on nonexistent tables.""" + with pytest.raises(Exception): + analysis_db.get_results("nonexistent_table") + + with pytest.raises(Exception): + analysis_db.get_table_info("nonexistent_table") + + def test_empty_dataframe_append(self, analysis_db): + """Test appending empty DataFrame.""" + empty_df = pd.DataFrame() + + records_added = analysis_db.append_results(empty_df, "empty_table") + assert records_added == 0 + + # Table should not be created for empty DataFrame + assert not analysis_db.table_exists("empty_table") From 74b4f8c86e6f5707217e9252ab588879e5ff752a Mon Sep 17 00:00:00 2001 From: chasem Date: Wed, 17 Sep 2025 20:23:04 -0500 Subject: [PATCH 30/49] this is getting closer to what im after. datainfo is close to finished. documentation works and is updated --- docs/AbstractAPI.md | 1 - docs/AbstractHfAPI.md | 1 - docs/AbstractRecordsAndFilesAPI.md | 1 - docs/AbstractRecordsOnlyAPI.md | 1 - docs/BindingAPI.md | 1 - docs/BindingConcatenatedAPI.md | 1 - docs/BindingManualQCAPI.md | 1 - docs/Cache.md | 1 - docs/CallingCardsBackgroundAPI.md | 1 - docs/DataSourceAPI.md | 1 - docs/ExpressionAPI.md | 1 - docs/ExpressionManualQCAPI.md | 2 - docs/FileFormatAPI.md | 1 - docs/GenomicFeatureAPI.md | 1 - docs/HfCacheManager.md | 2 +- docs/HfQueryAPI.md | 1 + docs/HfRankResponse.md | 1 + docs/IncrementalAnalysisDB.md | 1 + docs/ParamsDict.md | 1 - docs/PromoterSetAPI.md | 1 - docs/PromoterSetSigAPI.md | 1 - docs/RegulatorAPI.md | 1 - docs/datainfo.md | 133 + docs/errors.md | 1 + docs/huggingface_datacard.md | 315 +++ docs/index.md | 49 +- docs/metric_arrays.md | 1 - docs/rank_transforms.md | 17 - docs/tutorials/datacard_tutorial.ipynb | 759 ++++++ mkdocs.yml | 44 +- pyproject.toml | 2 +- tfbpapi/AbstractAPI.py | 230 -- tfbpapi/AbstractHfAPI.py | 373 --- tfbpapi/AbstractRecordsAndFilesAPI.py | 314 --- tfbpapi/AbstractRecordsOnlyAPI.py | 82 - tfbpapi/BindingAPI.py | 62 - tfbpapi/BindingConcatenatedAPI.py | 62 - tfbpapi/BindingManualQCAPI.py | 106 - tfbpapi/Cache.py | 29 - tfbpapi/CallingCardsBackgroundAPI.py | 56 - tfbpapi/DataSourceAPI.py | 48 - tfbpapi/DtoAPI.py | 295 --- tfbpapi/ExpressionAPI.py | 66 - tfbpapi/ExpressionManualQCAPI.py | 103 - tfbpapi/FileFormatAPI.py | 57 - tfbpapi/GenomicFeatureAPI.py | 60 - tfbpapi/HfQueryAPI.py | 2163 +++++++++++++++-- tfbpapi/ParamsDict.py | 156 -- tfbpapi/PromoterSetAPI.py | 46 - tfbpapi/PromoterSetSigAPI.py | 68 - tfbpapi/RankResponseAPI.py | 286 --- tfbpapi/RegulatorAPI.py | 53 - tfbpapi/UnivariateModelsAPI.py | 202 -- tfbpapi/__init__.py | 39 - tfbpapi/datainfo/__init__.py | 23 + tfbpapi/datainfo/datacard.py | 339 +++ tfbpapi/datainfo/fetchers.py | 224 ++ tfbpapi/datainfo/models.py | 250 ++ tfbpapi/errors.py | 214 ++ tfbpapi/metric_arrays.py | 162 -- tfbpapi/rank_transforms.py | 154 -- tfbpapi/tests/data/cache_info.pkl | Bin 123975 -> 0 bytes tfbpapi/tests/datainfo/__init__.py | 1 + tfbpapi/tests/datainfo/conftest.py | 302 +++ tfbpapi/tests/datainfo/test_datacard.py | 459 ++++ tfbpapi/tests/datainfo/test_fetchers.py | 422 ++++ tfbpapi/tests/datainfo/test_models.py | 481 ++++ tfbpapi/tests/snapshots/__init__.py | 0 .../promotersetsig_records_and_files.tar.gz | Bin 590168 -> 0 bytes .../tests/snapshots/snap_test_AbstractAPI.py | 20 - .../snap_test_AbstractRecordsAndFilesAPI.py | 15 - .../cache_get_after_delete | 1 - .../test_cache_operations/cache_get_after_set | 1 - .../test_cache_operations/cache_list | 1 - .../pop_params_after_all_removed | 1 - .../pop_params_after_one_removed | 1 - .../test_push_params/push_params | 1 - tfbpapi/tests/test_AbstractAPI.py | 94 - tfbpapi/tests/test_AbstractHfAPI.py | 435 ---- .../tests/test_AbstractRecordsAndFilesAPI.py | 284 --- tfbpapi/tests/test_AbstractRecordsOnlyAPI.py | 71 - tfbpapi/tests/test_Cache.py | 66 - tfbpapi/tests/test_HfQueryAPI.py | 530 ---- tfbpapi/tests/test_ParamsDict.py | 96 - tfbpapi/tests/test_metric_arrays.py | 194 -- tfbpapi/tests/test_rank_transforms.py | 80 - 86 files changed, 5956 insertions(+), 5267 deletions(-) delete mode 100644 docs/AbstractAPI.md delete mode 100644 docs/AbstractHfAPI.md delete mode 100644 docs/AbstractRecordsAndFilesAPI.md delete mode 100644 docs/AbstractRecordsOnlyAPI.md delete mode 100644 docs/BindingAPI.md delete mode 100644 docs/BindingConcatenatedAPI.md delete mode 100644 docs/BindingManualQCAPI.md delete mode 100644 docs/Cache.md delete mode 100644 docs/CallingCardsBackgroundAPI.md delete mode 100644 docs/DataSourceAPI.md delete mode 100644 docs/ExpressionAPI.md delete mode 100644 docs/ExpressionManualQCAPI.md delete mode 100644 docs/FileFormatAPI.md delete mode 100644 docs/GenomicFeatureAPI.md create mode 100644 docs/HfQueryAPI.md create mode 100644 docs/HfRankResponse.md create mode 100644 docs/IncrementalAnalysisDB.md delete mode 100644 docs/ParamsDict.md delete mode 100644 docs/PromoterSetAPI.md delete mode 100644 docs/PromoterSetSigAPI.md delete mode 100644 docs/RegulatorAPI.md create mode 100644 docs/datainfo.md create mode 100644 docs/errors.md create mode 100644 docs/huggingface_datacard.md delete mode 100644 docs/metric_arrays.md delete mode 100644 docs/rank_transforms.md create mode 100644 docs/tutorials/datacard_tutorial.ipynb delete mode 100644 tfbpapi/AbstractAPI.py delete mode 100644 tfbpapi/AbstractHfAPI.py delete mode 100644 tfbpapi/AbstractRecordsAndFilesAPI.py delete mode 100644 tfbpapi/AbstractRecordsOnlyAPI.py delete mode 100644 tfbpapi/BindingAPI.py delete mode 100644 tfbpapi/BindingConcatenatedAPI.py delete mode 100644 tfbpapi/BindingManualQCAPI.py delete mode 100644 tfbpapi/Cache.py delete mode 100644 tfbpapi/CallingCardsBackgroundAPI.py delete mode 100644 tfbpapi/DataSourceAPI.py delete mode 100644 tfbpapi/DtoAPI.py delete mode 100644 tfbpapi/ExpressionAPI.py delete mode 100644 tfbpapi/ExpressionManualQCAPI.py delete mode 100644 tfbpapi/FileFormatAPI.py delete mode 100644 tfbpapi/GenomicFeatureAPI.py delete mode 100644 tfbpapi/ParamsDict.py delete mode 100644 tfbpapi/PromoterSetAPI.py delete mode 100644 tfbpapi/PromoterSetSigAPI.py delete mode 100644 tfbpapi/RankResponseAPI.py delete mode 100644 tfbpapi/RegulatorAPI.py delete mode 100644 tfbpapi/UnivariateModelsAPI.py create mode 100644 tfbpapi/datainfo/__init__.py create mode 100644 tfbpapi/datainfo/datacard.py create mode 100644 tfbpapi/datainfo/fetchers.py create mode 100644 tfbpapi/datainfo/models.py create mode 100644 tfbpapi/errors.py delete mode 100644 tfbpapi/metric_arrays.py delete mode 100644 tfbpapi/rank_transforms.py delete mode 100644 tfbpapi/tests/data/cache_info.pkl create mode 100644 tfbpapi/tests/datainfo/__init__.py create mode 100644 tfbpapi/tests/datainfo/conftest.py create mode 100644 tfbpapi/tests/datainfo/test_datacard.py create mode 100644 tfbpapi/tests/datainfo/test_fetchers.py create mode 100644 tfbpapi/tests/datainfo/test_models.py delete mode 100644 tfbpapi/tests/snapshots/__init__.py delete mode 100644 tfbpapi/tests/snapshots/promotersetsig_records_and_files.tar.gz delete mode 100644 tfbpapi/tests/snapshots/snap_test_AbstractAPI.py delete mode 100644 tfbpapi/tests/snapshots/snap_test_AbstractRecordsAndFilesAPI.py delete mode 100644 tfbpapi/tests/snapshots/test_AbstractAPI/test_cache_operations/cache_get_after_delete delete mode 100644 tfbpapi/tests/snapshots/test_AbstractAPI/test_cache_operations/cache_get_after_set delete mode 100644 tfbpapi/tests/snapshots/test_AbstractAPI/test_cache_operations/cache_list delete mode 100644 tfbpapi/tests/snapshots/test_AbstractAPI/test_pop_params/pop_params_after_all_removed delete mode 100644 tfbpapi/tests/snapshots/test_AbstractAPI/test_pop_params/pop_params_after_one_removed delete mode 100644 tfbpapi/tests/snapshots/test_AbstractAPI/test_push_params/push_params delete mode 100644 tfbpapi/tests/test_AbstractAPI.py delete mode 100644 tfbpapi/tests/test_AbstractHfAPI.py delete mode 100644 tfbpapi/tests/test_AbstractRecordsAndFilesAPI.py delete mode 100644 tfbpapi/tests/test_AbstractRecordsOnlyAPI.py delete mode 100644 tfbpapi/tests/test_Cache.py delete mode 100644 tfbpapi/tests/test_HfQueryAPI.py delete mode 100644 tfbpapi/tests/test_ParamsDict.py delete mode 100644 tfbpapi/tests/test_metric_arrays.py delete mode 100644 tfbpapi/tests/test_rank_transforms.py diff --git a/docs/AbstractAPI.md b/docs/AbstractAPI.md deleted file mode 100644 index ada18a1..0000000 --- a/docs/AbstractAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.AbstractAPI.AbstractAPI diff --git a/docs/AbstractHfAPI.md b/docs/AbstractHfAPI.md deleted file mode 100644 index 45dea40..0000000 --- a/docs/AbstractHfAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.AbstractHfAPI.AbstractHfAPI diff --git a/docs/AbstractRecordsAndFilesAPI.md b/docs/AbstractRecordsAndFilesAPI.md deleted file mode 100644 index 395c787..0000000 --- a/docs/AbstractRecordsAndFilesAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.AbstractRecordsAndFilesAPI.AbstractRecordsAndFilesAPI diff --git a/docs/AbstractRecordsOnlyAPI.md b/docs/AbstractRecordsOnlyAPI.md deleted file mode 100644 index fc0726a..0000000 --- a/docs/AbstractRecordsOnlyAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.AbstractRecordsOnlyAPI.AbstractRecordsOnlyAPI diff --git a/docs/BindingAPI.md b/docs/BindingAPI.md deleted file mode 100644 index 7f4c555..0000000 --- a/docs/BindingAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.BindingAPI.BindingAPI diff --git a/docs/BindingConcatenatedAPI.md b/docs/BindingConcatenatedAPI.md deleted file mode 100644 index 4d68aea..0000000 --- a/docs/BindingConcatenatedAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.BindingConcatenatedAPI.BindingConcatenatedAPI diff --git a/docs/BindingManualQCAPI.md b/docs/BindingManualQCAPI.md deleted file mode 100644 index a29d3ca..0000000 --- a/docs/BindingManualQCAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.BindingManualQCAPI.BindingManualQCAPI diff --git a/docs/Cache.md b/docs/Cache.md deleted file mode 100644 index 3c2ed52..0000000 --- a/docs/Cache.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.Cache.Cache diff --git a/docs/CallingCardsBackgroundAPI.md b/docs/CallingCardsBackgroundAPI.md deleted file mode 100644 index 5f5c24a..0000000 --- a/docs/CallingCardsBackgroundAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.CallingCardsBackgroundAPI.CallingCardsBackgroundAPI diff --git a/docs/DataSourceAPI.md b/docs/DataSourceAPI.md deleted file mode 100644 index b50cc38..0000000 --- a/docs/DataSourceAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.DataSourceAPI.DataSourceAPI diff --git a/docs/ExpressionAPI.md b/docs/ExpressionAPI.md deleted file mode 100644 index 9397868..0000000 --- a/docs/ExpressionAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.ExpressionAPI.ExpressionAPI diff --git a/docs/ExpressionManualQCAPI.md b/docs/ExpressionManualQCAPI.md deleted file mode 100644 index 094b037..0000000 --- a/docs/ExpressionManualQCAPI.md +++ /dev/null @@ -1,2 +0,0 @@ -::: tfbpapi.ExpressionManualQCAPI.ExpressionManualQCAPI - diff --git a/docs/FileFormatAPI.md b/docs/FileFormatAPI.md deleted file mode 100644 index 12adc28..0000000 --- a/docs/FileFormatAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.FileFormatAPI.FileFormatAPI diff --git a/docs/GenomicFeatureAPI.md b/docs/GenomicFeatureAPI.md deleted file mode 100644 index c66339c..0000000 --- a/docs/GenomicFeatureAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.GenomicFeatureAPI.GenomicFeatureAPI diff --git a/docs/HfCacheManager.md b/docs/HfCacheManager.md index 89d5c77..6aa53a3 100644 --- a/docs/HfCacheManager.md +++ b/docs/HfCacheManager.md @@ -1 +1 @@ -::: tfbpapi.HfCacheManager.HfCacheManager +::: tfbpapi.HfCacheManager.HFCacheManager diff --git a/docs/HfQueryAPI.md b/docs/HfQueryAPI.md new file mode 100644 index 0000000..357ea30 --- /dev/null +++ b/docs/HfQueryAPI.md @@ -0,0 +1 @@ +::: tfbpapi.HfQueryAPI.HfQueryAPI \ No newline at end of file diff --git a/docs/HfRankResponse.md b/docs/HfRankResponse.md new file mode 100644 index 0000000..e2ffd53 --- /dev/null +++ b/docs/HfRankResponse.md @@ -0,0 +1 @@ +::: tfbpapi.HfRankResponse.HfRankResponse \ No newline at end of file diff --git a/docs/IncrementalAnalysisDB.md b/docs/IncrementalAnalysisDB.md new file mode 100644 index 0000000..7dc07d7 --- /dev/null +++ b/docs/IncrementalAnalysisDB.md @@ -0,0 +1 @@ +::: tfbpapi.IncrementalAnalysisDB.IncrementalAnalysisDB \ No newline at end of file diff --git a/docs/ParamsDict.md b/docs/ParamsDict.md deleted file mode 100644 index 057048a..0000000 --- a/docs/ParamsDict.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.ParamsDict.ParamsDict diff --git a/docs/PromoterSetAPI.md b/docs/PromoterSetAPI.md deleted file mode 100644 index b2c311c..0000000 --- a/docs/PromoterSetAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.PromoterSetAPI.PromoterSetAPI diff --git a/docs/PromoterSetSigAPI.md b/docs/PromoterSetSigAPI.md deleted file mode 100644 index f07120c..0000000 --- a/docs/PromoterSetSigAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.PromoterSetSigAPI.PromoterSetSigAPI diff --git a/docs/RegulatorAPI.md b/docs/RegulatorAPI.md deleted file mode 100644 index 0217a92..0000000 --- a/docs/RegulatorAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.RegulatorAPI.RegulatorAPI diff --git a/docs/datainfo.md b/docs/datainfo.md new file mode 100644 index 0000000..e159e7b --- /dev/null +++ b/docs/datainfo.md @@ -0,0 +1,133 @@ +# DataInfo Package + +The `datainfo` package provides dataset information management for HuggingFace datasets. It enables exploration of dataset metadata, structure, and relationships without loading actual genomic data. + +## Overview + +The datainfo package consists of three main components: + +- **DataCard**: High-level interface for exploring dataset metadata +- **Fetchers**: Low-level components for retrieving data from HuggingFace Hub +- **Models**: Pydantic models for validation and type safety + +## Main Interface + +### DataCard +::: tfbpapi.datainfo.datacard.DataCard + options: + show_root_heading: true + show_source: true + +The `DataCard` class is the primary interface for exploring HuggingFace datasets. It provides methods to: + +- Discover dataset configurations and types +- Explore feature schemas and data types +- Understand metadata relationships +- Extract field values and experimental conditions +- Navigate partitioned dataset structures + +## Data Models + +### Core Models +::: tfbpapi.datainfo.models.DatasetCard + options: + show_root_heading: true + +::: tfbpapi.datainfo.models.DatasetConfig + options: + show_root_heading: true + +::: tfbpapi.datainfo.models.FeatureInfo + options: + show_root_heading: true + +### Dataset Types +::: tfbpapi.datainfo.models.DatasetType + options: + show_root_heading: true + +### Relationship Models +::: tfbpapi.datainfo.models.MetadataRelationship + options: + show_root_heading: true + +::: tfbpapi.datainfo.models.ExtractedMetadata + options: + show_root_heading: true + +## Data Fetchers + +### HuggingFace Integration +::: tfbpapi.datainfo.fetchers.HfDataCardFetcher + options: + show_root_heading: true + +::: tfbpapi.datainfo.fetchers.HfRepoStructureFetcher + options: + show_root_heading: true + +::: tfbpapi.datainfo.fetchers.HfSizeInfoFetcher + options: + show_root_heading: true + +## Usage Examples + +### Basic Dataset Exploration + +```python +from tfbpapi.datainfo import DataCard + +# Initialize DataCard for a repository +card = DataCard('BrentLab/rossi_2021') + +# Get repository overview +repo_info = card.get_repository_info() +print(f"Dataset: {repo_info['pretty_name']}") +print(f"Configurations: {repo_info['num_configs']}") + +# Explore configurations +for config in card.configs: + print(f"{config.config_name}: {config.dataset_type.value}") +``` + +### Understanding Dataset Structure + +```python +# Get detailed config information +config_info = card.explore_config('metadata') +print(f"Features: {config_info['num_features']}") + +# Check for partitioned data +if 'partitioning' in config_info: + partition_info = config_info['partitioning'] + print(f"Partitioned by: {partition_info['partition_by']}") +``` + +### Metadata Relationships + +```python +# Discover metadata relationships +relationships = card.get_metadata_relationships() +for rel in relationships: + print(f"{rel.data_config} -> {rel.metadata_config} ({rel.relationship_type})") +``` + +## Integration with HfQueryAPI + +The datainfo package is designed to work seamlessly with `HfQueryAPI` for efficient data loading: + +```python +from tfbpapi import HfQueryAPI +from tfbpapi.datainfo import DataCard + +# Explore dataset structure first +card = DataCard('BrentLab/rossi_2021') +config_info = card.explore_config('genome_map') + +# Use insights to load data efficiently +query_api = HfQueryAPI('BrentLab/rossi_2021') +data = query_api.get_pandas('genome_map', + filters={'run_accession': 'SRR123456'}) +``` + +For a complete tutorial, see the [DataCard Tutorial](tutorials/datacard_tutorial.ipynb). \ No newline at end of file diff --git a/docs/errors.md b/docs/errors.md new file mode 100644 index 0000000..cfbf1df --- /dev/null +++ b/docs/errors.md @@ -0,0 +1 @@ +::: tfbpapi.errors \ No newline at end of file diff --git a/docs/huggingface_datacard.md b/docs/huggingface_datacard.md new file mode 100644 index 0000000..916d6d4 --- /dev/null +++ b/docs/huggingface_datacard.md @@ -0,0 +1,315 @@ +# HuggingFace Dataset Card Format + +This document describes the expected YAML metadata format for HuggingFace dataset repositories used with the tfbpapi package. The metadata is defined in the repository's README.md file frontmatter and provides structured information about the dataset configuration and contents. + +## Required Top-Level Fields + +### Basic Metadata +```yaml +license: mit # Dataset license +language: # Languages (usually 'en' for scientific data) +- en +tags: # Descriptive tags for discoverability +- biology +- genomics +- yeast +- transcription-factors +pretty_name: "Dataset Name" # Human-readable dataset name +size_categories: # Dataset size category +- 100KRank Transforms - -

Shifted Negative Log Ranks

- -::: tfbpapi.rank_transforms.shifted_negative_log_ranks - -

Stable Rank

- -::: tfbpapi.rank_transforms.stable_rank - -

Rank by p-value

- -::: tfbpapi.rank_transforms.rank_by_pvalue - -

Transform

- -::: tfbpapi.rank_transforms.transform \ No newline at end of file diff --git a/docs/tutorials/datacard_tutorial.ipynb b/docs/tutorials/datacard_tutorial.ipynb new file mode 100644 index 0000000..071397b --- /dev/null +++ b/docs/tutorials/datacard_tutorial.ipynb @@ -0,0 +1,759 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# DataCard Tutorial: Exploring HuggingFace Genomics Datasets\n", + "\n", + "The `DataCard` class provides an easy-to-use interface for exploring HuggingFace dataset metadata without loading the actual genomic data. This is particularly useful for:\n", + "\n", + "- Understanding dataset structure and available configurations\n", + "- Exploring experimental conditions and regulators\n", + "- Discovering metadata relationships\n", + "- Planning data analysis workflows\n", + "\n", + "In this tutorial, we'll explore the **BrentLab/rossi_2021** dataset, which contains ChIP-exo data for transcription factor binding in yeast." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1. Getting Started\n", + "\n", + "First, let's import the DataCard class and initialize it with our target dataset." + ] + }, + { + "cell_type": "code", + "execution_count": 132, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Repository: BrentLab/rossi_2021\n" + ] + } + ], + "source": [ + "from tfbpapi.datainfo import DataCard\n", + "\n", + "# Initialize DataCard with the Rossi 2021 dataset\n", + "# try this with mahendrawada_2025, which is more complex\n", + "card = DataCard('BrentLab/rossi_2021')\n", + "\n", + "print(f\"Repository: {card.repo_id}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2. Repository Overview\n", + "\n", + "Let's start by getting a high-level overview of the dataset." + ] + }, + { + "cell_type": "code", + "execution_count": 133, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Repository Information:\n", + "========================================\n", + "repo_id : BrentLab/rossi_2021\n", + "pretty_name : Rossi ChIP-exo 2021\n", + "license : mit\n", + "tags : ['transcription-factor', 'binding', 'chipexo', 'genomics', 'biology']\n", + "language : ['en']\n", + "size_categories : None\n", + "num_configs : 2\n", + "dataset_types : ['metadata', 'genome_map']\n", + "total_files : 1237\n", + "last_modified : 2025-09-18T00:59:17+00:00\n", + "has_default_config : True\n" + ] + } + ], + "source": [ + "# Get repository information\n", + "repo_info = card.get_repository_info()\n", + "\n", + "print(\"Repository Information:\")\n", + "print(\"=\" * 40)\n", + "for key, value in repo_info.items():\n", + " print(f\"{key:20}: {value}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 134, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dataset Summary:\n", + "==================================================\n", + "Dataset: Rossi ChIP-exo 2021\n", + "Repository: BrentLab/rossi_2021\n", + "License: mit\n", + "Configurations: 2\n", + "Dataset Types: metadata, genome_map\n", + "Tags: transcription-factor, binding, chipexo, genomics, biology\n", + "\n", + "Configurations:\n", + " - metadata: metadata (default)\n", + " Metadata describing the tagged regulator in each experiment\n", + " - genome_map: genome_map\n", + " ChIP-exo 5' tag coverage data partitioned by sample accession\n" + ] + } + ], + "source": [ + "# Get a human-readable summary\n", + "print(\"Dataset Summary:\")\n", + "print(\"=\" * 50)\n", + "print(card.summary())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3. Exploring Configurations\n", + "\n", + "Datasets can have multiple configurations representing different types of data. Let's explore what's available in this dataset." + ] + }, + { + "cell_type": "code", + "execution_count": 135, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of configurations: 2\n", + "\n", + "Configuration details:\n", + "\n", + "• metadata:\n", + " Type: metadata\n", + " Default: True\n", + " Description: Metadata describing the tagged regulator in each experiment\n", + " Features: 4\n", + "\n", + "• genome_map:\n", + " Type: genome_map\n", + " Default: False\n", + " Description: ChIP-exo 5' tag coverage data partitioned by sample accession\n", + " Features: 3\n" + ] + } + ], + "source": [ + "# List all configurations\n", + "print(f\"Number of configurations: {len(card.configs)}\")\n", + "print(\"\\nConfiguration details:\")\n", + "\n", + "for config in card.configs:\n", + " print(f\"\\n• {config.config_name}:\")\n", + " print(f\" Type: {config.dataset_type.value}\")\n", + " print(f\" Default: {config.default}\")\n", + " print(f\" Description: {config.description}\")\n", + " print(f\" Features: {len(config.dataset_info.features)}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Understanding Dataset Types\n", + "\n", + "The Rossi 2021 dataset contains two types of configurations:\n", + "\n", + "- **`metadata`**: Experimental metadata describing each ChIP-exo sample\n", + "- **`genome_map`**: Position-level ChIP-exo tag coverage data\n", + "\n", + "Let's explore each configuration in detail." + ] + }, + { + "cell_type": "code", + "execution_count": 136, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Metadata Configuration Details:\n", + "========================================\n", + "Config name: metadata\n", + "Dataset type: metadata\n", + "Number of features: 4\n", + "Default config: True\n", + "\n", + "Features in metadata config:\n", + " • regulator_locus_tag (string ): Systematic gene name (ORF identifier) of the transcription factor\n", + " • regulator_symbol (string ): Standard gene symbol of the transcription factor\n", + " • run_accession (string ): GEO run accession identifier for the sample\n", + " • yeastepigenome_id (string ): Sample identifier used by yeastepigenome.org\n" + ] + } + ], + "source": [ + "# Explore the metadata configuration\n", + "metadata_info = card.explore_config('metadata')\n", + "\n", + "print(\"Metadata Configuration Details:\")\n", + "print(\"=\" * 40)\n", + "print(f\"Config name: {metadata_info['config_name']}\")\n", + "print(f\"Dataset type: {metadata_info['dataset_type']}\")\n", + "print(f\"Number of features: {metadata_info['num_features']}\")\n", + "print(f\"Default config: {metadata_info['is_default']}\")\n", + "\n", + "print(\"\\nFeatures in metadata config:\")\n", + "for feature in metadata_info['features']:\n", + " print(f\" • {feature['name']:20} ({feature['dtype']:10}): {feature['description']}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 137, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Genome Map Configuration Details:\n", + "========================================\n", + "Config name: genome_map\n", + "Dataset type: genome_map\n", + "Number of features: 3\n", + "\n", + "Features in genome_map config:\n", + " • chr (string ): Chromosome name (e.g., chrI, chrII, etc.)\n", + " • pos (int32 ): Genomic position of the 5' tag\n", + " • pileup (int32 ): Depth of coverage (number of 5' tags) at this genomic position\n", + "\n", + "Partitioning Information:\n", + " Enabled: True\n", + " Partition by: ['run_accession']\n", + " Path template: genome_map/accession={run_accession}/*.parquet\n" + ] + } + ], + "source": [ + "# Explore the genome_map configuration\n", + "genome_map_info = card.explore_config('genome_map')\n", + "\n", + "print(\"Genome Map Configuration Details:\")\n", + "print(\"=\" * 40)\n", + "print(f\"Config name: {genome_map_info['config_name']}\")\n", + "print(f\"Dataset type: {genome_map_info['dataset_type']}\")\n", + "print(f\"Number of features: {genome_map_info['num_features']}\")\n", + "\n", + "print(\"\\nFeatures in genome_map config:\")\n", + "for feature in genome_map_info['features']:\n", + " print(f\" • {feature['name']:15} ({feature['dtype']:10}): {feature['description']}\")\n", + "\n", + "# Check if this config has partitioning\n", + "if 'partitioning' in genome_map_info:\n", + " print(\"\\nPartitioning Information:\")\n", + " partitioning = genome_map_info['partitioning']\n", + " print(f\" Enabled: {partitioning['enabled']}\")\n", + " print(f\" Partition by: {partitioning['partition_by']}\")\n", + " print(f\" Path template: {partitioning['path_template']}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4. Understanding Data Relationships\n", + "\n", + "The DataCard can help you understand how different configurations relate to each other, particularly metadata relationships." + ] + }, + { + "cell_type": "code", + "execution_count": 138, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 1 metadata relationships:\n", + "\n", + "Relationship details:\n", + " • genome_map -> metadata\n", + " Type: explicit\n", + " (Metadata config explicitly specifies which data configs it applies to)\n" + ] + } + ], + "source": [ + "# Explore metadata relationships\n", + "relationships = card.get_metadata_relationships()\n", + "\n", + "print(f\"Found {len(relationships)} metadata relationships:\")\n", + "print(\"\\nRelationship details:\")\n", + "\n", + "for rel in relationships:\n", + " print(f\" • {rel.data_config} -> {rel.metadata_config}\")\n", + " print(f\" Type: {rel.relationship_type}\")\n", + "\n", + " if rel.relationship_type == \"explicit\":\n", + " print(\" (Metadata config explicitly specifies which data configs it applies to)\")\n", + " elif rel.relationship_type == \"embedded\":\n", + " print(\" (Metadata is embedded within the data config itself)\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 5. Exploring Dataset Contents\n", + "\n", + "Now let's explore what experimental data is available in this dataset." + ] + }, + { + "cell_type": "code", + "execution_count": 139, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Metadata configurations: ['metadata']\n", + "Data configurations: ['genome_map']\n", + "\n", + "Default configuration: metadata\n" + ] + } + ], + "source": [ + "# Get different config types\n", + "from tfbpapi.datainfo.models import DatasetType\n", + "\n", + "# Find metadata configs\n", + "metadata_configs = card.get_configs_by_type(DatasetType.METADATA)\n", + "print(f\"Metadata configurations: {[c.config_name for c in metadata_configs]}\")\n", + "\n", + "# Find data configs\n", + "data_configs = card.get_configs_by_type(DatasetType.GENOME_MAP)\n", + "print(f\"Data configurations: {[c.config_name for c in data_configs]}\")\n", + "\n", + "# Get the default config\n", + "default_config = card.dataset_card.get_default_config()\n", + "if default_config:\n", + " print(f\"\\nDefault configuration: {default_config.config_name}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Extracting Field Values\n", + "\n", + "For exploration purposes, we can extract unique values from specific fields. This is particularly useful for understanding what experimental conditions or regulators are available." + ] + }, + { + "cell_type": "code", + "execution_count": 140, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 0 unique run accessions:\n", + "No accessions found (might require partition-based extraction)\n" + ] + } + ], + "source": [ + "# Try to extract run accession information\n", + "try:\n", + " accessions = card.get_field_values('metadata', 'run_accession')\n", + " print(f\"Found {len(accessions)} unique run accessions:\")\n", + " if accessions:\n", + " sample_accessions = sorted(list(accessions))[:5]\n", + " print(f\"Sample accessions: {sample_accessions}...\")\n", + " else:\n", + " print(\"No accessions found (might require partition-based extraction)\")\n", + "except Exception as e:\n", + " print(f\"Could not extract accession values: {e}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 6. Working with Partitioned Data\n", + "\n", + "Many genomics datasets are partitioned for efficient storage and querying. Let's explore how partitioning works in this dataset." + ] + }, + { + "cell_type": "code", + "execution_count": 141, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Partitioning Details:\n", + "==============================\n", + "Enabled: True\n", + "Partition columns: ['run_accession']\n", + "Path template: genome_map/accession={run_accession}/*.parquet\n", + "\n", + "This means:\n", + "• The genome map data is split into separate files for each run_accession\n", + "• Files are organized as: genome_map/accession={run_accession}/*.parquet\n", + "• This allows efficient querying of specific experimental runs\n" + ] + } + ], + "source": [ + "# Check partitioning details for the genome_map config\n", + "genome_map_config = card.get_config('genome_map')\n", + "\n", + "if genome_map_config and genome_map_config.dataset_info.partitioning:\n", + " part_info = genome_map_config.dataset_info.partitioning\n", + "\n", + " print(\"Partitioning Details:\")\n", + " print(\"=\" * 30)\n", + " print(f\"Enabled: {part_info.enabled}\")\n", + " print(f\"Partition columns: {part_info.partition_by}\")\n", + " print(f\"Path template: {part_info.path_template}\")\n", + "\n", + " print(\"\\nThis means:\")\n", + " print(\"• The genome map data is split into separate files for each run_accession\")\n", + " print(\"• Files are organized as: genome_map/accession={run_accession}/*.parquet\")\n", + " print(\"• This allows efficient querying of specific experimental runs\")\n", + "else:\n", + " print(\"No partitioning information found.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 7. Understanding Data Files\n", + "\n", + "Let's examine how the data files are organized within each configuration." + ] + }, + { + "cell_type": "code", + "execution_count": 142, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Data files for 'metadata' config:\n", + "----------------------------------------\n", + " File 1:\n", + " Split: train\n", + " Path: rossi_2021_metadata.parquet\n", + "\n", + "Data files for 'genome_map' config:\n", + "----------------------------------------\n", + " File 1:\n", + " Split: train\n", + " Path: genome_map/*/*.parquet\n", + " - This is a glob pattern that matches multiple files\n" + ] + } + ], + "source": [ + "# Examine data files for each configuration\n", + "for config in card.configs:\n", + " print(f\"\\nData files for '{config.config_name}' config:\")\n", + " print(\"-\" * 40)\n", + "\n", + " for i, data_file in enumerate(config.data_files):\n", + " print(f\" File {i+1}:\")\n", + " print(f\" Split: {data_file.split}\")\n", + " print(f\" Path: {data_file.path}\")\n", + "\n", + " # Explain path patterns\n", + " if '*' in data_file.path:\n", + " print(f\" - This is a glob pattern that matches multiple files\")\n", + " if '=' in data_file.path:\n", + " print(f\" - This uses partitioned directory structure\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 8. Practical Use Cases\n", + "\n", + "Here are some common scenarios where DataCard is useful:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Use Case 1: Finding Datasets with Specific Data Types" + ] + }, + { + "cell_type": "code", + "execution_count": 143, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Available dataset types: ['metadata', 'genome_map']\n", + "\n", + "Has genome-wide binding data: True\n" + ] + } + ], + "source": [ + "# Check what types of data are available\n", + "available_types = [config.dataset_type.value for config in card.configs]\n", + "print(f\"Available dataset types: {available_types}\")\n", + "\n", + "# Check if this dataset has genome-wide binding data\n", + "has_genome_map = any(config.dataset_type == DatasetType.GENOME_MAP for config in card.configs)\n", + "print(f\"\\nHas genome-wide binding data: {has_genome_map}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Use Case 2: Understanding Data Schema Before Loading" + ] + }, + { + "cell_type": "code", + "execution_count": 144, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Genome Map Data Schema:\n", + "==============================\n", + "Column: chr\n", + " Type: string\n", + " Description: Chromosome name (e.g., chrI, chrII, etc.)\n", + "\n", + "Column: pos\n", + " Type: int32\n", + " Description: Genomic position of the 5' tag\n", + "\n", + "Column: pileup\n", + " Type: int32\n", + " Description: Depth of coverage (number of 5' tags) at this genomic position\n", + "\n", + "This tells us:\n", + "• 'chr' column contains chromosome names (string)\n", + "• 'pos' column contains genomic positions (int32)\n", + "• 'pileup' column contains tag counts (int32)\n", + "• Data represents 5' tag coverage from ChIP-exo experiments\n" + ] + } + ], + "source": [ + "# Before loading large genome map data, understand its structure\n", + "genome_config = card.get_config('genome_map')\n", + "\n", + "if genome_config:\n", + " print(\"Genome Map Data Schema:\")\n", + " print(\"=\" * 30)\n", + "\n", + " for feature in genome_config.dataset_info.features:\n", + " print(f\"Column: {feature.name}\")\n", + " print(f\" Type: {feature.dtype}\")\n", + " print(f\" Description: {feature.description}\")\n", + " print()\n", + "\n", + " print(\"This tells us:\")\n", + " print(\"• 'chr' column contains chromosome names (string)\")\n", + " print(\"• 'pos' column contains genomic positions (int32)\")\n", + " print(\"• 'pileup' column contains tag counts (int32)\")\n", + " print(\"• Data represents 5' tag coverage from ChIP-exo experiments\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Use Case 3: Planning Efficient Data Access" + ] + }, + { + "cell_type": "code", + "execution_count": 145, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Data Access Strategy:\n", + "=========================\n", + "• Data is partitioned by 'run_accession'\n", + "• To load data for a specific experiment, filter by run_accession\n", + "• This avoids loading data from all experiments\n", + "• Path pattern: genome_map/accession={run_accession}/*.parquet\n", + "\n", + "Example workflow:\n", + "1. Use metadata config to find interesting run_accessions\n", + "2. Load only genome_map data for those specific accessions\n", + "3. Analyze position-level binding data for selected experiments\n" + ] + } + ], + "source": [ + "# Understanding partitioning helps plan efficient queries\n", + "if genome_config and genome_config.dataset_info.partitioning:\n", + " print(\"Data Access Strategy:\")\n", + " print(\"=\" * 25)\n", + " print(\"• Data is partitioned by 'run_accession'\")\n", + " print(\"• To load data for a specific experiment, filter by run_accession\")\n", + " print(\"• This avoids loading data from all experiments\")\n", + " print(\"• Path pattern: genome_map/accession={run_accession}/*.parquet\")\n", + "\n", + " print(\"\\nExample workflow:\")\n", + " print(\"1. Use metadata config to find interesting run_accessions\")\n", + " print(\"2. Load only genome_map data for those specific accessions\")\n", + " print(\"3. Analyze position-level binding data for selected experiments\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 9. Error Handling and Troubleshooting\n", + "\n", + "The DataCard class includes validation and error handling. Here are some common scenarios:" + ] + }, + { + "cell_type": "code", + "execution_count": 146, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Non-existent config result: None\n", + "Error accessing non-existent field: Field 'nonexistent_field' not found in config 'metadata'\n", + "Config 'some_config' not found in this dataset\n" + ] + } + ], + "source": [ + "# Handling missing configurations\n", + "missing_config = card.get_config('nonexistent_config')\n", + "print(f\"Non-existent config result: {missing_config}\")\n", + "\n", + "# Handling missing fields\n", + "try:\n", + " invalid_field = card.get_field_values('metadata', 'nonexistent_field')\n", + "except Exception as e:\n", + " print(f\"Error accessing non-existent field: {e}\")\n", + "\n", + "# Checking if config exists before using\n", + "config_name = 'some_config'\n", + "if card.get_config(config_name):\n", + " print(f\"Config '{config_name}' exists\")\n", + "else:\n", + " print(f\"Config '{config_name}' not found in this dataset\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 10. Summary and Next Steps\n", + "\n", + "The DataCard class provides a powerful way to explore HuggingFace genomics datasets before committing to loading large amounts of data. \n", + "\n", + "### Key Takeaways:\n", + "\n", + "1. **Dataset Structure**: The Rossi 2021 dataset contains both experimental metadata and genome-wide ChIP-exo binding data\n", + "2. **Partitioning**: Data is efficiently partitioned by experimental run for fast access\n", + "3. **Metadata Relationships**: The system automatically understands how metadata relates to data configs\n", + "4. **Schema Discovery**: You can understand data types and structure before loading\n", + "\n", + "### Next Steps:\n", + "\n", + "- Use `HfQueryAPI` to load specific subsets of the data based on your exploration\n", + "- Apply filters based on experimental conditions discovered through DataCard\n", + "- Combine multiple datasets that have compatible schemas\n", + "\n", + "### Example Integration with HfQueryAPI:\n", + "\n", + "```python\n", + "from tfbpapi import HfQueryAPI\n", + "\n", + "# After exploring with DataCard, load specific data\n", + "query_api = HfQueryAPI('BrentLab/rossi_2021')\n", + "\n", + "# Load metadata for planning\n", + "metadata_df = query_api.get_pandas('metadata')\n", + "\n", + "# Load genome map data for specific experiments\n", + "# (using partition filters based on DataCard exploration)\n", + "genome_data = query_api.get_pandas('genome_map', \n", + " filters={'run_accession': 'SRR123456'})\n", + "```" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "tfbpapi-py3.11 (3.11.9)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/mkdocs.yml b/mkdocs.yml index c367973..85960a2 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,5 +1,5 @@ site_name: tfbpapi -site_description: "A collection of objects and functions to work with calling cards sequencing tools" +site_description: "Python API for querying and analyzing genomic datasets from HuggingFace Hub" site_author: "ben mueller , chase mateusiak , michael brent " site_url: "https://brentlab.github.io/tfbpapi" repo_url: "https://github.com/brentlab/tfbpapi" @@ -62,7 +62,7 @@ plugins: handlers: python: paths: [.] - import: + inventories: - https://docs.python.org/3/objects.inv - https://numpy.org/doc/stable/objects.inv - https://pandas.pydata.org/docs/objects.inv @@ -149,29 +149,17 @@ extra_css: nav: - Home: index.md - Tutorials: - - Database tfbpapi: tutorials/database_tfbpapi.ipynb - - API: - - Database tfbpapi: - - Records Only Classes: - - BindingManualQCAPI.md - - DataSourceAPI.md - - DtoAPI.md - - ExpressionManualQCAPI.md - - FileFormatAPI.md - - GenomicFeatureAPI.md - - RegulatorAPI.md - - Records and Files Classes: - - BindingAPI: BindingAPI.md - - BindingConcatenatedAPI: BindingConcatenatedAPI.md - - CallingCardsBackgroundAPI: CallingCardsBackgroundAPI.md - - ExpressionAPI: ExpressionAPI.md - - PromoterSetAPI: PromoterSetAPI.md - - PromoterSetSigAPI: PromoterSetSigAPI.md - - Developer Classes: - - AbstractAPI.md - - AbstractRecordsAndFilesAPI.md - - AbstractRecordsOnlyAPI.md - - AbstractHfAPI.md - - HfCacheManager.md - - Cache.md - - ParamsDict.md + - "DataCard: Exploring Datasets": tutorials/datacard_tutorial.ipynb + - API Reference: + - Core Components: + - HfQueryAPI: HfQueryAPI.md + - HfRankResponse: HfRankResponse.md + - IncrementalAnalysisDB: IncrementalAnalysisDB.md + - Cache Management: + - HfCacheManager: HfCacheManager.md + - Dataset Information: + - DataInfo Package: datainfo.md + - Error Handling: + - Custom Exceptions: errors.md + - HuggingFace Configuration: + - HuggingFace Dataset Card Format: huggingface_datacard.md diff --git a/pyproject.toml b/pyproject.toml index 916be3d..72a4b01 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,11 +22,11 @@ huggingface-hub = "^0.34.4" types-requests = "^2.32.4.20250809" datasets = "^4.0.0" duckdb = "^1.3.2" +pydantic = "^2.11.9" [tool.poetry.group.dev.dependencies] pytest = "^8.3.5" -pytest-snapshot = "^0.9.0" pytest-asyncio = "^0.26.0" types-requests = "^2.32.4.20250809" mkdocs = "^1.6.1" diff --git a/tfbpapi/AbstractAPI.py b/tfbpapi/AbstractAPI.py deleted file mode 100644 index 39bd10a..0000000 --- a/tfbpapi/AbstractAPI.py +++ /dev/null @@ -1,230 +0,0 @@ -import logging -import os -from abc import ABC, abstractmethod -from collections.abc import Coroutine -from typing import Any - -import pandas as pd -import requests - -from tfbpapi.Cache import Cache -from tfbpapi.ParamsDict import ParamsDict - - -class AbstractAPI(ABC): - """ - Abstract base class for creating API clients that require token authentication. - - This class provides a template for connecting to a cache for caching API responses, - validating parameters against a list of valid keys, and provides an interface for - CRUD operations. - - """ - - def __init__( - self, - url: str = "", - token: str = "", - **kwargs, - ): - """ - Initialize the API client. - - :param url: The API endpoint URL. Defaults to the `BASE_URL` - environment variable. - :param token: The authentication token. Defaults to the `TOKEN` - environment variable. - :param valid_param_keys: A list of valid parameter keys for the API. - :param params: A ParamsDict object containing parameters for the API request. - :param cache: a Cache object for caching API responses. - :param kwargs: Additional keyword arguments that may be passed on to the - ParamsDict and Cache constructors. - - """ - self.logger = logging.getLogger(self.__class__.__name__) - self._token = token or os.getenv("TOKEN", "") - self.url = url or os.getenv("BASE_URL", "") - self.params = ParamsDict( - params=kwargs.pop("params", {}), - valid_keys=kwargs.pop("valid_keys", []), - ) - self.cache = Cache( - maxsize=kwargs.pop("maxsize", 100), ttl=kwargs.pop("ttl", 300) - ) - - @property - def header(self) -> dict[str, str]: - """The HTTP authorization header.""" - return { - "Authorization": f"token {self.token}", - "Content-Type": "application/json", - } - - @property - def url(self) -> str: - """The URL for the API.""" - return self._url # type: ignore - - @url.setter - def url(self, value: str) -> None: - if not value: - self._url = None - elif hasattr(self, "token") and self.token: - # validate the URL with the new token - self._is_valid_url(value) - self._url = value - else: - self.logger.warning("No token provided: URL un-validated") - self._url = value - - @property - def token(self) -> str: - """The authentication token for the API.""" - return self._token - - @token.setter - def token(self, value: str) -> None: - self._token = value - # validate the URL with the new token - if hasattr(self, "url") and self.url: - self.logger.info("Validating URL with new token") - self._is_valid_url(self.url) - - @property - def cache(self) -> Cache: - """The cache object for caching API responses.""" - return self._cache - - @cache.setter - def cache(self, value: Cache) -> None: - self._cache = value - - @property - def params(self) -> ParamsDict: - """The ParamsDict object containing parameters for the API request.""" - return self._params - - @params.setter - def params(self, value: ParamsDict) -> None: - self._params = value - - def push_params(self, params: dict[str, Any]) -> None: - """Adds or updates parameters in the ParamsDict.""" - try: - self.params.update(params) - except KeyError as e: - self.logger.error(f"Error updating parameters: {e}") - - def pop_params(self, keys: list[str] | None = None) -> None: - """Removes parameters from the ParamsDict.""" - if keys is None: - self.params.clear() - return - if keys is not None and not isinstance(keys, list): - keys = [keys] - for key in keys: - del self.params[key] - - @abstractmethod - def create(self, data: dict[str, Any], **kwargs) -> Any: - """Placeholder for the create method.""" - raise NotImplementedError( - f"`create()` is not implemented for {self.__class__.__name__}" - ) - - @abstractmethod - def read(self, **kwargs) -> Any: - """Placeholder for the read method.""" - raise NotImplementedError( - f"`read()` is not implemented for {self.__class__.__name__}" - ) - - @abstractmethod - def update(self, df: pd.DataFrame, **kwargs) -> Any: - """Placeholder for the update method.""" - raise NotImplementedError( - f"`update()` is not implemented for {self.__class__.__name__}" - ) - - @abstractmethod - def delete(self, id: str, **kwargs) -> Any: - """Placeholder for the delete method.""" - raise NotImplementedError( - f"`delete()` is not implemented for {self.__class__.__name__}" - ) - - @abstractmethod - def submit(self, post_dict: dict[str, Any], **kwargs) -> Any: - """Placeholder for the submit method.""" - raise NotImplementedError( - f"`submit()` is not implemented for {self.__class__.__name__}" - ) - - @abstractmethod - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Coroutine[Any, Any, Any]: - """Placeholder for the retrieve method.""" - raise NotImplementedError( - f"`retrieve()` is not implemented for {self.__class__.__name__}" - ) - - def _is_valid_url(self, url: str) -> None: - """ - Confirms that the URL is valid and the header authorization is appropriate. - - :param url: The URL to validate. - :type url: str - :raises ValueError: If the URL is invalid or the token is not set. - - """ - try: - # note that with allow_redirect=True the result can be a 300 status code - # which is not an error, and then another request to the redirected URL - response = requests.head(url, headers=self.header, allow_redirects=True) - if response.status_code != 200: - raise ValueError("Invalid URL or token provided. Check both.") - except requests.RequestException as e: - raise AttributeError(f"Error validating URL: {e}") from e - except AttributeError as e: - self.logger.error(f"Error validating URL: {e}") - - def _cache_get(self, key: str, default: Any = None) -> Any: - """ - Get a value from the cache if configured. - - :param key: The key to retrieve from the cache. - :type key: str - :param default: The default value to return if the key is not found. - :type default: any, optional - :return: The value from the cache or the default value. - :rtype: any - - """ - return self.cache.get(key, default=default) - - def _cache_set(self, key: str, value: Any) -> None: - """ - Set a value in the cache if configured. - - :param key: The key to set in the cache. - :type key: str - :param value: The value to set in the cache. - :type value: any - - """ - self.cache.set(key, value) - - def _cache_list(self) -> list[str]: - """List keys in the cache if configured.""" - return self.cache.list() - - def _cache_delete(self, key: str) -> None: - """ - Delete a key from the cache if configured. - - :param key: The key to delete from the cache. - :type key: str - - """ - self.cache.delete(key) diff --git a/tfbpapi/AbstractHfAPI.py b/tfbpapi/AbstractHfAPI.py deleted file mode 100644 index 58e0884..0000000 --- a/tfbpapi/AbstractHfAPI.py +++ /dev/null @@ -1,373 +0,0 @@ -import logging -import os -from abc import ABC, abstractmethod -from collections.abc import Mapping -from pathlib import Path -from typing import Any, Literal - -import requests -from huggingface_hub import hf_hub_download, snapshot_download -from huggingface_hub.constants import HF_HUB_CACHE -from requests import HTTPError - -# Constants -MB_TO_BYTES = 1024 * 1024 - - -class RepoTooLargeError(ValueError): - """Raised when repository exceeds auto-download threshold.""" - - -class AbstractHfAPI(ABC): - """Abstract base class for creating Hugging Face API clients.""" - - def __init__( - self, - repo_id: str, - repo_type: Literal["model", "dataset", "space"] = "dataset", - token: str | None = None, - cache_dir: str | Path | None = None, - ): - """ - Initialize the HF-backed API client. - - :param repo_id: The repo identifier on HF (e.g., "user/dataset"). Eg, - "BrentLab/yeast_genome_resources" - :param token: Optional. Not necessary for public repos. May be set via the - HF_TOKEN environment variable. - :param repo_type: One of {"model", "dataset", "space"}. Defaults to "dataset". - :param cache_dir: HF cache_dir for hf_hub_download and snapshot_download (see - huggingface_hub docs). May be passed via the HF_CACHE_DIR environmental - variable. If not set, the default HF cache directory is used. - :raises FileNotFoundError: If the specified cache_dir does not exist. - - """ - self.logger = logging.getLogger(self.__class__.__name__) - - self.token = token or os.getenv("HF_TOKEN", None) - self.repo_id = repo_id - self.repo_type = repo_type - self.cache_dir = Path( - cache_dir if cache_dir else os.getenv("HF_CACHE_DIR", HF_HUB_CACHE) - ) - - @property - def token(self) -> str | None: - return self._token - - @token.setter - def token(self, value: str | None) -> None: - # TODO: if a token is provided, then validate that it works. Only necessary - # if token is not None of course - self._token = value - - @property - def repo_id(self) -> str: - return self._repo_id - - @repo_id.setter - def repo_id(self, value: str) -> None: - """ - Set the repo_id. - - This setter also calls _get_dataset_size to fetch size info and validate that - the repo exists and is accessible. No error is raised if the repo is not - accessible, but an error is logged. - - """ - self._repo_id = value - try: - self._get_dataset_size(self._repo_id) - except (HTTPError, ValueError) as e: - self.logger.error(f"Could not reach {value}: {e}") - - @property - def cache_dir(self) -> Path: - return self._cache_dir - - @cache_dir.setter - def cache_dir(self, value: str | Path) -> None: - """ - Set the cache directory for huggingface_hub downloads. - - :raises FileNotFoundError: If the specified directory does not exist. - - """ - path = Path(value) - if not path.exists(): - raise FileNotFoundError(f"Cache directory {path} does not exist") - self._cache_dir = path - - @property - def size(self) -> dict[str, Any] | None: - """ - Size information from the HF Dataset Server API. - - This reaches the /size endpoint. See - https://github.com/huggingface/dataset-viewer/blob/8f0ae65f0ff64791111d37a725af437c3c752daf/docs/source/size.md - - """ - return getattr(self, "_size", None) - - @size.setter - def size(self, value: dict[str, Any]) -> None: - self._size = value - - @property - def snapshot_path(self) -> Path | None: - """Path to the last downloaded snapshot (if any).""" - return getattr(self, "_snapshot_path", None) - - @snapshot_path.setter - def snapshot_path(self, value: str | Path | None) -> None: - self._snapshot_path = None if value is None else Path(value) - - def _get_dataset_size_mb(self) -> float: - """Get dataset size in MB, returning inf if not available.""" - if not self.size: - return float("inf") - return ( - self.size.get("size", {}) - .get("dataset", {}) - .get("num_bytes_original_files", float("inf")) - / MB_TO_BYTES - ) - - def _ensure_str_paths(self, kwargs: dict[str, Any]) -> None: - """Ensure Path-like arguments are converted to strings.""" - for key in ["local_dir", "cache_dir"]: - if key in kwargs and kwargs[key] is not None: - kwargs[key] = str(kwargs[key]) - - def _build_auth_headers(self) -> dict[str, str]: - """Build authentication headers if token is available.""" - return {"Authorization": f"Bearer {self.token}"} if self.token else {} - - def _normalize_patterns(self, kwargs: dict[str, Any]) -> None: - """Convert string patterns to lists.""" - for pattern_key in ["allow_patterns", "ignore_patterns"]: - if pattern_key in kwargs and kwargs[pattern_key] is not None: - patterns = kwargs[pattern_key] - if isinstance(patterns, str): - kwargs[pattern_key] = [patterns] - - def _get_dataset_size(self, repo_id: str | None = None) -> None: - """ - Get dataset size information from HuggingFace Dataset Server API. - - :returns: Dict containing size information with additional metadata about - completeness - :raises requests.HTTPError: If the API request fails - :raises ValueError: If the dataset doesn't exist or isn't accessible - - """ - repo_id = repo_id or self.repo_id - url = f"https://datasets-server.huggingface.co/size?dataset={repo_id}" - - response = requests.get(url, headers=self._build_auth_headers()) - response.raise_for_status() - - data = response.json() - - # Check if size determination was partial - is_partial = data.get("partial", False) - - if is_partial: - self.logger.warning( - f"Size information for {repo_id} is incomplete. " - "The dataset is too large for complete size determination. " - "Reported numbers may be lower than actual size." - ) - - # Add metadata about completeness to the response - if "size" in data and "dataset" in data["size"]: - data["size"]["dataset"]["size_determination_complete"] = not is_partial - data["size"]["dataset"]["size_warning"] = ( - "Partial size only - actual dataset may be larger" - if is_partial - else "Complete size information" - ) - - self.size = data - - def _download_single_file( - self, - filename: str, - dry_run: bool = False, - **kwargs, - ) -> Path: - """ - Download a single file using hf_hub_download. - - :param filename: File to download - :param dry_run: If True, log what would be downloaded without downloading - :param kwargs: Additional arguments passed directly to hf_hub_download - :return: Path to the downloaded file - - """ - self.logger.info(f"Downloading single file: {filename}") - - if dry_run: - self.logger.info(f"[DRY RUN] Would download {filename} from {self.repo_id}") - return Path("dry_run_path") - - # Build base arguments - hf_kwargs = { - "repo_id": self.repo_id, - "repo_type": self.repo_type, - "filename": filename, - "token": self.token, - **kwargs, - } - - # Set cache_dir only if local_dir not specified - if "local_dir" not in hf_kwargs and self.cache_dir is not None: - hf_kwargs["cache_dir"] = str(self.cache_dir) - - # Ensure string conversion for Path-like arguments - self._ensure_str_paths(hf_kwargs) - - file_path = hf_hub_download(**hf_kwargs) - self._snapshot_path = Path(file_path).parent - return Path(file_path) - - def _download_snapshot( - self, - dry_run: bool = False, - **kwargs, - ) -> Path: - """ - Download repository snapshot using snapshot_download. - - :param dry_run: If True, log what would be downloaded without downloading - :param kwargs: Additional arguments passed directly to snapshot_download - :return: Path to the downloaded snapshot - - """ - # Log download plan - if dry_run: - self.logger.info(f"[DRY RUN] Would download from {self.repo_id}:") - self.logger.info(f" - allow_patterns: {kwargs.get('allow_patterns')}") - self.logger.info(f" - ignore_patterns: {kwargs.get('ignore_patterns')}") - return Path("dry_run_path") - - # Execute snapshot download - self.logger.info( - f"Downloading repo snapshot with patterns - " - f"allow: {kwargs.get('allow_patterns')}, " - f"ignore: {kwargs.get('ignore_patterns')}" - ) - - # Build base arguments - # note that kwargs passed into this method will override defaults, - # including repo_id, etc - snapshot_kwargs = { - "repo_id": self.repo_id, - "repo_type": self.repo_type, - "token": self.token, - **kwargs, - } - - # Set cache_dir only if local_dir not specified and cache_dir wasn't passed in - if ( - "local_dir" not in snapshot_kwargs - and "cache_dir" not in snapshot_kwargs - and self.cache_dir is not None - ): - snapshot_kwargs["cache_dir"] = str(self.cache_dir) - - # Convert string patterns to lists - self._normalize_patterns(snapshot_kwargs) - - snapshot_path = snapshot_download(**snapshot_kwargs) - self.snapshot_path = Path(snapshot_path) - return self.snapshot_path - - def download( - self, - files: list[str] | str | None = None, - force_full_download: bool = False, - auto_download_threshold_mb: float = 100.0, - dry_run: bool = False, - **kwargs, - ) -> Path: - """ - Download dataset by file, patterns or if the dataset is small enough, the entire - repo. - - :param files: Specific file(s) to download. If provided, uses hf_hub_download - :param force_full_download: If True, always download entire repo regardless of - size - :param auto_download_threshold_mb: Auto-download full repo if estimated size < - this (MB) - :param dry_run: If True, log what would be downloaded without actually - downloading - :param kwargs: Additional arguments passed to hf_hub_download or - snapshot_download. Common args: revision, local_dir, cache_dir, - local_files_only, allow_patterns, ignore_patterns, etc. - :return: Path to downloaded content (file or directory). - - """ - dataset_size_mb = self._get_dataset_size_mb() - if dataset_size_mb <= auto_download_threshold_mb or force_full_download: - self.logger.info( - f"Dataset size ({dataset_size_mb:.2f} MB) is below the auto-download " - f"threshold of {auto_download_threshold_mb} MB. Downloading entire " - "repo." - ) - files = None - kwargs.pop("allow_patterns", None) - kwargs.pop("ignore_patterns", None) - elif ( - not files - and not kwargs.get("allow_patterns") - and not kwargs.get("ignore_patterns") - ): - excess_size_mb = dataset_size_mb - auto_download_threshold_mb - raise RepoTooLargeError( - f"Dataset size ({dataset_size_mb:.2f} MB) exceeds the " - f"auto-download threshold of {auto_download_threshold_mb} MB by " - f"{excess_size_mb:.2f} MB. To download the dataset, either " - "specify specific files or patterns to download, " - "set force_full_download=True or increase the " - "`auto_download_threshold_mb`." - ) - # Handle specific file downloads - if files is not None: - if isinstance(files, str) or (isinstance(files, list) and len(files) == 1): - # Single file - filename = files if isinstance(files, str) else files[0] - self.logger.info(f"Preparing to download single file: {filename}") - return self._download_single_file( - filename=filename, dry_run=dry_run, **kwargs - ) - elif isinstance(files, list) and len(files) > 1: - # Multiple files - use snapshot_download with allow_patterns - if kwargs.get("allow_patterns") is not None: - self.logger.warning( - "Both 'files' and 'allow_patterns' were provided. " - "'files' will take precedence." - ) - kwargs["allow_patterns"] = files - - return self._download_snapshot(dry_run=dry_run, **kwargs) - - @abstractmethod - def parse_datacard(self, *args: Any, **kwargs: Any) -> Mapping[str, Any]: - """ - Abstract method to parse a datacard from the downloaded content. - - Must be implemented by subclasses. - - """ - raise NotImplementedError("Subclasses must implement this method.") - - @abstractmethod - def query(self, *args: Any, **kwargs: Any) -> Any: - """ - Abstract method to query the API. - - Must be implemented by subclasses. - - """ - raise NotImplementedError("Subclasses must implement this method.") diff --git a/tfbpapi/AbstractRecordsAndFilesAPI.py b/tfbpapi/AbstractRecordsAndFilesAPI.py deleted file mode 100644 index 87f99ad..0000000 --- a/tfbpapi/AbstractRecordsAndFilesAPI.py +++ /dev/null @@ -1,314 +0,0 @@ -import csv -import gzip -import os -import tarfile -import tempfile -from collections.abc import Callable -from io import BytesIO -from typing import Any - -import aiohttp -import pandas as pd - -from tfbpapi.AbstractAPI import AbstractAPI - - -class AbstractRecordsAndFilesAPI(AbstractAPI): - """ - Abstract class to interact with both the records and the data stored in the `file` - field. - - The return for this class must be records, against the `/export` - endpoint when `retrieve_files` is False. When `retrieve_files` is True, the cache - should be checked first. If the file doesn't exist there, it should be retrieved - from the database against the `/record_table_and_files` endpoint. The file should - be a tarball with the metadata.csv and the file associated with the record, - where the file is named according to the `id` field in metadata.csv. Data files - should be `.csv.gz`. - - """ - - def __init__(self, **kwargs): - """ - Initialize the AbstractRecordsAndFilesAPI object. This will serve as an - interface to an endpoint that can serve both records and files, and cache the - file/retrieve from the cache if it exists. - - :param kwargs: parameters to pass to AbstractAPI. - - """ - self.export_url_suffix = kwargs.pop("export_url_suffix", "export") - self.export_files_url_suffix = kwargs.pop( - "export_files_url_suffix", "record_table_and_files" - ) - super().__init__(**kwargs) - - @property - def export_url_suffix(self) -> str: - """The URL suffix for exporting records.""" - return self._export_url_suffix - - @export_url_suffix.setter - def export_url_suffix(self, value: str) -> None: - self._export_url_suffix = value - - @property - def export_files_url_suffix(self) -> str: - """The URL suffix for exporting files.""" - return self._export_files_url_suffix - - @export_files_url_suffix.setter - def export_files_url_suffix(self, value: str) -> None: - self._export_files_url_suffix = value - - def _detect_delimiter(self, file_path: str, sample_size: int = 1024) -> str: - """ - Detect the delimiter of a CSV file. - - :param file_path: The path to the CSV file. - :type file_path: str - :param sample_size: The number of bytes to read from the file to detect the - delimiter. Defaults to 1024. - :type sample_size: int - :return: The delimiter of the CSV file. - :rtype: str - :raises FileNotFoundError: If the file does not exist. - :raises gzip.BadGzipFile: If the file is not a valid gzip file. - :raises _csv.Error: If the CSV sniffer cannot determine the delimiter. - - """ - try: - # by default, open() uses newline=False, which opens the file - # in universal newline mode and translates all new line characters - # to '\n' - file = ( - gzip.open(file_path, "rt") - if file_path.endswith(".gz") - else open(file_path) - ) - except FileNotFoundError as exc: - raise FileNotFoundError(f"File {file_path} not found.") from exc - - sample = file.read(sample_size) - - # In order to avoid errors in the csv sniffer, attempt to find the - # last newline character in the string - last_newline_index = sample.rfind("\n") - # if a newline character is found, trim the sample to the last newline - if last_newline_index != -1: - # Trim to the last complete line - sample = sample[:last_newline_index] - - sniffer = csv.Sniffer() - dialect = sniffer.sniff(sample) - delimiter = dialect.delimiter - - file.close() - - return delimiter - - async def read( - self, - callback: Callable[ - [pd.DataFrame, dict[str, Any] | None, Any], Any - ] = lambda metadata, data, cache, **kwargs: ( - {"metadata": metadata, "data": data} - ), - retrieve_files: bool = False, - **kwargs, - ) -> Any: - """ - Retrieve data from the endpoint according to the `retrieve_files` parameter. If - `retrieve_files` is False, the records will be returned as a dataframe. If - `retrieve_files` is True, the files associated with the records will be - retrieved either from the local cache or from the database. Note that a user can - select which effect_colname and pvalue_colname is used for a genomicfile (see - database documentation for more details). If one or both of those are present in - the params, and retrieve_file is true, then that column name is added to the - cache_key. Eg if record 1 is being retrieved from mcisaac data with - effect_colname "log2_raio", then the cache_key for that data will be - "1_log2_ratio". The default effect colname, which is set by the database, will - be stored with only the record id as the cache_key. - - :param callback: The function to call with the metadata. Signature must - include `metadata`, `data`, and `cache`. - :type callback: Callable[[pd.DataFrame, dict[str, Any] | None, Any], Any] - :param retrieve_files: Boolean. Whether to retrieve the files associated with - the records. Defaults to False. - :type retrieve_files: bool - :param kwargs: The following kwargs are used by the read() function. Any - others are passed onto the callback function - - timeout: The timeout for the GET request. Defaults to 120. - - :return: The result of the callback function. - :rtype: Any - - :raises ValueError: If the callback function does not have the correct - signature. - :raises aiohttp.ClientError: If there is an error in the GET request. - :raises pd.errors.ParserError: If there is an error reading the request - - """ - if not callable(callback) or {"metadata", "data", "cache"} - set( - callback.__code__.co_varnames - ): - raise ValueError( - "The callback must be a callable function with `metadata`, `data`, ", - "and `cache` as parameters.", - ) - - export_url = f"{self.url.rstrip('/')}/{self.export_url_suffix}" - self.logger.debug("read() export_url: %s", export_url) - - timeout = aiohttp.ClientTimeout(kwargs.pop("timeout", 120)) - async with aiohttp.ClientSession(timeout=timeout) as session: - try: - async with session.get( - export_url, headers=self.header, params=self.params - ) as response: - response.raise_for_status() - content = await response.content.read() - with gzip.GzipFile(fileobj=BytesIO(content)) as f: - records_df = pd.read_csv(f) - - if not retrieve_files: - return callback(records_df, None, self.cache, **kwargs) - else: - data_list = await self._retrieve_files(session, records_df) - return callback( - records_df, - data_list, - self.cache, - **kwargs, - ) - - except aiohttp.ClientError as e: - self.logger.error(f"Error in GET request: {e}") - raise - except pd.errors.ParserError as e: - self.logger.error(f"Error reading request content: {e}") - raise - - async def _retrieve_files( - self, session: aiohttp.ClientSession, records_df: pd.DataFrame - ) -> dict[str, pd.DataFrame]: - """ - Retrieve files associated with the records either from the local cache or from - the database. - - :param session: The aiohttp ClientSession. - :type session: aiohttp.ClientSession - :param records_df: The DataFrame containing the records. - :type records_df: pd.DataFrame - :return: A dictionary where the keys are record IDs and the values are - DataFrames of the associated files. - :rtype: dict[str, pd.DataFrame] - - """ - data_list = {} - for record_id in records_df["id"]: - data_list[str(record_id)] = await self._retrieve_file(session, record_id) - return data_list - - async def _retrieve_file( - self, session: aiohttp.ClientSession, record_id: int - ) -> pd.DataFrame: - """ - Retrieve a file associated with a record either from the local cache or from the - database. - - :param session: The aiohttp ClientSession. - :type session: aiohttp.ClientSession - :param record_id: The ID of the record. - :type record_id: int - :return: A DataFrame containing the file's data. - :rtype: pd.DataFrame - :raises FileNotFoundError: If the file is not found in the tar archive. - :raises ValueError: If the delimiter is not supported. - - """ - export_files_url = f"{self.url.rstrip('/')}/{self.export_files_url_suffix}" - self.logger.debug("_retrieve_file() export_url: %s", export_files_url) - - # set key for local cache - cache_key = str(record_id) - if "effect_colname" in self.params: - cache_key += f"_{self.params['effect_colname']}" - if "pvalue_colname" in self.params: - cache_key += f"_{self.params['pvalue_colname']}" - cached_data = self._cache_get(cache_key) - if cached_data is not None: - self.logger.info(f"cache_key {cache_key} retrieved from cache.") - return pd.read_json(BytesIO(cached_data.encode())) - else: - self.logger.debug(f"cache_key {cache_key} not found in cache.") - - try: - header = self.header.copy() - header["Content-Type"] = "application/gzip" - retrieve_files_params = self.params.copy() - retrieve_files_params.update({"id": record_id}) - async with session.get( - export_files_url, - headers=header, - params=retrieve_files_params, - timeout=120, - ) as response: - response.raise_for_status() - tar_data = await response.read() - - # Create a temporary file for the tarball - tar_file = tempfile.NamedTemporaryFile(delete=False, suffix=".tar.gz") - try: - tar_file.write(tar_data) - tar_file.flush() - tar_file.seek(0) - - # Create a temporary directory for extraction - with tempfile.TemporaryDirectory() as extract_dir: - # Open the tar file and log its contents - with tarfile.open(fileobj=tar_file, mode="r:gz") as tar: - tar_members = tar.getmembers() - self.logger.debug( - f"Tar file contains: " - f"{[member.name for member in tar_members]}", - ) - - # Find the specific file to extract - csv_filename = f"{record_id}.csv.gz" - member = next( - (m for m in tar_members if m.name == csv_filename), None - ) - if member is None: - raise FileNotFoundError( - f"{csv_filename} not found in tar archive" - ) - - # Extract only the specific member - tar.extract(member, path=extract_dir) - - # Read the extracted CSV file - csv_path = os.path.join(extract_dir, csv_filename) - self.logger.debug(f"Extracted file: {csv_path}") - - delimiter = self._detect_delimiter(csv_path) - - # raise an error if the delimiter is not a "," or a "\t" - if delimiter not in [",", "\t"]: - raise ValueError( - f"Delimiter {delimiter} is not supported. " - "Supported delimiters are ',' and '\\t'." - ) - - df = pd.read_csv(csv_path, delimiter=delimiter) - - # Store the data in the cache - self.logger.debug(f"Storing {cache_key} in cache.") - self._cache_set(cache_key, df.to_json()) - finally: - os.unlink(tar_file.name) - - return df - except Exception as e: - self.logger.error(f"Error retrieving file for cache_key {cache_key}: {e}") - raise diff --git a/tfbpapi/AbstractRecordsOnlyAPI.py b/tfbpapi/AbstractRecordsOnlyAPI.py deleted file mode 100644 index 1751ec7..0000000 --- a/tfbpapi/AbstractRecordsOnlyAPI.py +++ /dev/null @@ -1,82 +0,0 @@ -import gzip -import logging -from collections.abc import Callable -from io import BytesIO -from typing import Any - -import aiohttp -import pandas as pd - -from tfbpapi.AbstractAPI import AbstractAPI - - -class AbstractRecordsOnlyAPI(AbstractAPI): - """Abstract class for CRUD operations on records-only (no file storage) - endpoints.""" - - def __init__(self, **kwargs): - """ - Initialize the RecordsOnlyAPI object. - - :param kwargs: Additional parameters to pass to AbstractAPI. - - """ - self.logger = logging.getLogger(__name__) - super().__init__(**kwargs) - - async def read( - self, - callback: Callable[ - [pd.DataFrame, dict[str, Any] | None, Any], Any - ] = lambda metadata, data, cache, **kwargs: { - "metadata": metadata, - "data": data, - }, - export_url_suffix="export", - **kwargs, - ) -> Any: - """ - Retrieve data from the endpoint. The data will be returned as a dataframe. The - callback function must take metadata, data, and cache as parameters. - - :param callback: The function to call with the data. Signature must - include `metadata`, `data`, and `cache` as parameters. - :param export_url_suffix: The URL suffix for the export endpoint. This will - return a response object with a csv file. - :param kwargs: This can be used to pass "params" to the request to use in place - of `self.params`. If those are passed, they will be popped off and then - the remaining kwargs will be passed to the callback function - - """ - if not callable(callback) or {"metadata", "data", "cache"} - set( - callback.__code__.co_varnames - ): - raise ValueError( - "The callback must be a callable function with `metadata`,", - "`data`, and `cache` as parameters.", - ) - - export_url = f"{self.url.rstrip('/')}/{export_url_suffix}" - self.logger.debug("read() export_url: %s", export_url) - - async with aiohttp.ClientSession() as session: - try: - # note that the url and the export suffix are joined such that - # the url is stripped of any trailing slashes and the export suffix is - # added without a leading slash - async with session.get( - export_url, - headers=self.header, - params=kwargs.pop("params", self.params), - ) as response: - response.raise_for_status() - content = await response.content.read() - with gzip.GzipFile(fileobj=BytesIO(content)) as f: - records_df = pd.read_csv(f) - return callback(records_df, None, self.cache, **kwargs) - except aiohttp.ClientError as e: - self.logger.error(f"Error in GET request: {e}") - raise - except pd.errors.ParserError as e: - self.logger.error(f"Error reading request content: {e}") - raise diff --git a/tfbpapi/BindingAPI.py b/tfbpapi/BindingAPI.py deleted file mode 100644 index b766b37..0000000 --- a/tfbpapi/BindingAPI.py +++ /dev/null @@ -1,62 +0,0 @@ -import os -from typing import Any - -import pandas as pd - -from tfbpapi.AbstractRecordsAndFilesAPI import ( - AbstractRecordsAndFilesAPI, -) - - -class BindingAPI(AbstractRecordsAndFilesAPI): - """Class to interact with the BindingAPI endpoint.""" - - def __init__(self, **kwargs) -> None: - """ - Initialize the BindingAPI object. - - :param kwargs: parameters to pass through AbstractRecordsAndFilesAPI to - AbstractAPI. - - """ - valid_param_keys = kwargs.pop( - "valid_param_keys", - [ - "id", - "regulator", - "regulator_locus_tag", - "regulator_symbol", - "batch", - "replicate", - "source", - "source_name", - "source_orig_id", - "strain", - "condition", - "lab", - "assay", - "workflow", - "data_usable", - ], - ) - - url = kwargs.pop("url", os.getenv("BINDING_URL", None)) - - super().__init__(url=url, valid_keys=valid_param_keys, **kwargs) - - def create(self, data: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The BindingAPI does not support create.") - - def update(self, df: pd.DataFrame, **kwargs) -> Any: - raise NotImplementedError("The BindingAPI does not support update.") - - def delete(self, id: str, **kwargs) -> Any: - raise NotImplementedError("The BindingAPI does not support delete.") - - def submit(self, post_dict: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The BindingAPI does not support submit.") - - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Any: - raise NotImplementedError("The BindingAPI does not support retrieve.") diff --git a/tfbpapi/BindingConcatenatedAPI.py b/tfbpapi/BindingConcatenatedAPI.py deleted file mode 100644 index 1ad6aff..0000000 --- a/tfbpapi/BindingConcatenatedAPI.py +++ /dev/null @@ -1,62 +0,0 @@ -import os -from typing import Any - -import pandas as pd - -from tfbpapi.AbstractRecordsAndFilesAPI import ( - AbstractRecordsAndFilesAPI, -) - - -class BindingConcatenatedAPI(AbstractRecordsAndFilesAPI): - """Class to interact with the BindingConcatenatedAPI endpoint.""" - - def __init__(self, **kwargs) -> None: - """ - Initialize the BindingConcatenatedAPI object. - - :param kwargs: parameters to pass through AbstractRecordsAndFilesAPI to - AbstractAPI. - - """ - valid_param_keys = kwargs.pop( - "valid_param_keys", - [ - "id", - "regulator", - "regulator_locus_tag", - "regulator_symbol", - "batch", - "replicate", - "source", - "strain", - "condition", - "lab", - "assay", - "workflow", - "data_usable", - ], - ) - - url = kwargs.pop("url", os.getenv("BINDINGCONCATENATED_URL", None)) - - super().__init__(url=url, valid_keys=valid_param_keys, **kwargs) - - def create(self, data: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The BindingConcatenatedAPI does not support create.") - - def update(self, df: pd.DataFrame, **kwargs) -> Any: - raise NotImplementedError("The BindingConcatenatedAPI does not support update.") - - def delete(self, id: str, **kwargs) -> Any: - raise NotImplementedError("The BindingConcatenatedAPI does not support delete.") - - def submit(self, post_dict: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The BindingConcatenatedAPI does not support submit.") - - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Any: - raise NotImplementedError( - "The BindingConcatenatedAPI does not support retrieve." - ) diff --git a/tfbpapi/BindingManualQCAPI.py b/tfbpapi/BindingManualQCAPI.py deleted file mode 100644 index 02b6a21..0000000 --- a/tfbpapi/BindingManualQCAPI.py +++ /dev/null @@ -1,106 +0,0 @@ -import os -from typing import Any - -import pandas as pd -import requests - -from tfbpapi.AbstractRecordsOnlyAPI import AbstractRecordsOnlyAPI - - -class BindingManualQCAPI(AbstractRecordsOnlyAPI): - """A class to interact with the BindingManualQCAPI endpoint.""" - - def __init__(self, **kwargs): - """ - Initialize the BindingManualQCAPI object. - - :param kwargs: parameters to pass to AbstractAPI via AbstractRecordsOnlyAPI. - - """ - valid_param_keys = kwargs.pop( - "valid_param_keys", - [ - "id", - "binding", - "best_datatype", - "data_usable", - "passing_replicate", - "rank_recall", - "regulator", - "regulator_locus_tag", - "regulator_symbol", - "batch", - "source", - ], - ) - - url = kwargs.pop("url", os.getenv("BINDINGMANUALQC_URL", None)) - if not url: - raise AttributeError( - "url must be provided or the environmental variable ", - "`BINDINGMANUALQC_URL` must be set", - ) - - self.bulk_update_url_suffix = kwargs.pop( - "bulk_update_url_suffix", "bulk-update" - ) - - super().__init__(url=url, valid_param_keys=valid_param_keys, **kwargs) - - @property - def bulk_update_url_suffix(self) -> str: - """The URL suffix for updating multiple records in the same request.""" - return self._bulk_update_url_suffix - - @bulk_update_url_suffix.setter - def bulk_update_url_suffix(self, value: str) -> None: - self._bulk_update_url_suffix = value - - def update(self, df: pd.DataFrame, **kwargs: Any) -> requests.Response: - """ - Update the records in the database. - - :param df: The DataFrame containing the records to update. - :type df: pd.DataFrame - :param kwargs: Additional fields to include in the payload. - :type kwargs: Any - :return: The response from the POST request. - :rtype: requests.Response - :raises requests.RequestException: If the request fails. - - """ - bulk_update_url = ( - f"{self.url.rstrip('/')}/{self.bulk_update_url_suffix.rstrip('/')}/" - ) - - self.logger.debug("bulk_update_url: %s", bulk_update_url) - - # Include additional fields in the payload if provided - payload = {"data": df.to_dict(orient="records")} - payload.update(kwargs) - - try: - response = requests.post( - bulk_update_url, - headers=self.header, - json=payload, - ) - response.raise_for_status() - return response - except requests.RequestException as e: - self.logger.error(f"Error in POST request: {e}") - raise - - def create(self, data: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The BindingManualQCAPI does not support create.") - - def delete(self, id: str, **kwargs) -> Any: - raise NotImplementedError("The BindingManualQCAPI does not support delete.") - - def submit(self, post_dict: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The BindingManualQCAPI does not support submit.") - - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Any: - raise NotImplementedError("The BindingManualQCAPI does not support retrieve.") diff --git a/tfbpapi/Cache.py b/tfbpapi/Cache.py deleted file mode 100644 index 366604d..0000000 --- a/tfbpapi/Cache.py +++ /dev/null @@ -1,29 +0,0 @@ -import logging -from typing import Any - -from cachetools import TTLCache # type: ignore - - -class Cache: - """A caching class that uses cachetools for TTL caching with an LRU eviction - policy.""" - - def __init__(self, maxsize: int = 100, ttl: int = 300): - self.ttl_cache = TTLCache(maxsize=maxsize, ttl=ttl) - self.logger = logging.getLogger(__name__) - - def get(self, key: str, default: Any = None) -> Any: - """Get a value from the cache.""" - return self.ttl_cache.get(key, default) - - def set(self, key: str, value: Any) -> None: - """Set a value in the cache.""" - self.ttl_cache[key] = value - - def list(self) -> list[str]: - """List all keys in the cache.""" - return list(self.ttl_cache.keys()) - - def delete(self, key: str) -> None: - """Delete a key from the cache.""" - self.ttl_cache.pop(key, None) diff --git a/tfbpapi/CallingCardsBackgroundAPI.py b/tfbpapi/CallingCardsBackgroundAPI.py deleted file mode 100644 index f5b7668..0000000 --- a/tfbpapi/CallingCardsBackgroundAPI.py +++ /dev/null @@ -1,56 +0,0 @@ -import os -from typing import Any - -import pandas as pd - -from tfbpapi.AbstractRecordsAndFilesAPI import ( - AbstractRecordsAndFilesAPI, -) - - -class CallingCardsBackgroundAPI(AbstractRecordsAndFilesAPI): - """Class to interact with the CallingCardsBackgroundAPI endpoint.""" - - def __init__(self, **kwargs) -> None: - """ - Initialize the CallingCardsBackgroundAPI object. - - :param kwargs: parameters to pass through AbstractRecordsAndFilesAPI to - AbstractAPI. - - """ - valid_param_keys = kwargs.pop( - "valid_param_keys", - ["id", "name"], - ) - - url = kwargs.pop("url", os.getenv("CALLINGCARDSBACKGROUND_URL", None)) - - super().__init__(url=url, valid_keys=valid_param_keys, **kwargs) - - def create(self, data: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError( - "The CallingCardsBackgroundAPI does not support create." - ) - - def update(self, df: pd.DataFrame, **kwargs) -> Any: - raise NotImplementedError( - "The CallingCardsBackgroundAPI does not support update." - ) - - def delete(self, id: str, **kwargs) -> Any: - raise NotImplementedError( - "The CallingCardsBackgroundAPI does not support delete." - ) - - def submit(self, post_dict: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError( - "The CallingCardsBackgroundAPI does not support submit." - ) - - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Any: - raise NotImplementedError( - "The CallingCardsBackgroundAPI does not support retrieve." - ) diff --git a/tfbpapi/DataSourceAPI.py b/tfbpapi/DataSourceAPI.py deleted file mode 100644 index 0d00785..0000000 --- a/tfbpapi/DataSourceAPI.py +++ /dev/null @@ -1,48 +0,0 @@ -import os -from typing import Any - -import pandas as pd - -from tfbpapi.AbstractRecordsOnlyAPI import AbstractRecordsOnlyAPI - - -class DataSourceAPI(AbstractRecordsOnlyAPI): - """A class to interact with the DataSourceAPI endpoint.""" - - def __init__(self, **kwargs): - """ - Initialize the DataSourceAPI object. - - :param kwargs: parameters to pass to AbstractAPI via AbstractRecordsOnlyAPI. - - """ - valid_param_keys = kwargs.pop( - "valid_param_keys", - ["id", "fileformat_id", "fileformat", "lab", "assay", "workflow"], - ) - - url = kwargs.pop("url", os.getenv("DATASOURCE_URL", None)) - if not url: - raise AttributeError( - "url must be provided or the environmental variable ", - "`DATASOURCE_URL` must be set", - ) - - super().__init__(url=url, valid_keys=valid_param_keys, **kwargs) - - def create(self, data: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The DataSourceAPI does not support create.") - - def update(self, df: pd.DataFrame, **kwargs) -> Any: - raise NotImplementedError("The DataSourceAPI does not support update.") - - def delete(self, id: str, **kwargs) -> Any: - raise NotImplementedError("The DataSourceAPI does not support delete.") - - def submit(self, post_dict: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The DataSourceAPI does not support submit.") - - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Any: - raise NotImplementedError("The DataSourceAPI does not support retrieve.") diff --git a/tfbpapi/DtoAPI.py b/tfbpapi/DtoAPI.py deleted file mode 100644 index b8780b6..0000000 --- a/tfbpapi/DtoAPI.py +++ /dev/null @@ -1,295 +0,0 @@ -import asyncio -import json -import os -import time -from typing import Any - -import aiohttp -import pandas as pd -import requests - -from tfbpapi.AbstractRecordsOnlyAPI import AbstractRecordsOnlyAPI - - -class DtoAPI(AbstractRecordsOnlyAPI): - """ - A class to interact with the DTO API. - - Retrieves dto data from the database. - - """ - - def __init__(self, **kwargs) -> None: - """ - Initialize the DTO object. This will serve as an interface to the DTO endpoint - of both the database and the application cache. - - :param url: The URL of the DTO API - :param kwargs: Additional parameters to pass to AbstractAPI. - - """ - - self.bulk_update_url_suffix = kwargs.pop( - "bulk_update_url_suffix", "bulk-update" - ) - - super().__init__( - url=kwargs.pop("url", os.getenv("DTO_URL", "")), - **kwargs, - ) - - async def read(self, *args, **kwargs) -> Any: - """ - Override the read() method to use a custom callback that parses metadata. - - :param callback: The function to call with the metadata. Defaults to parsing - metadata. - :type callback: Callable[[pd.DataFrame, dict[str, Any] | None, Any], Any] - :return: The result of the callback function. - :rtype: Any - - """ - - # Define the default callback - def dto_callback(metadata, data, cache, **kwargs): - return {"metadata": self.parse_metadata(metadata), "data": data} - - # Explicitly set the callback argument to dto_callback - kwargs["callback"] = dto_callback - - # Call the superclass method with updated kwargs - return await super().read(*args, **kwargs) - - async def submit( - self, - post_dict: dict[str, Any], - **kwargs, - ) -> Any: - """ - Submit a DTO task to the DTO API. - - :param post_dict: The dictionary to submit to the DTO API. The typing needs to - be adjusted -- it can take a list of dictionaries to submit a batch. - :return: The group_task_id of the submitted task. - - """ - # make a post request with the post_dict to dto_url - dto_url = f"{self.url.rstrip('/')}/submit/" - self.logger.debug("dto_url: %s", dto_url) - - async with aiohttp.ClientSession() as session: - async with session.post( - dto_url, headers=self.header, json=post_dict - ) as response: - try: - response.raise_for_status() - except aiohttp.ClientResponseError as e: - self.logger.error( - "Failed to submit DTO task: Status %s, Reason %s", - e.status, - e.message, - ) - raise - result = await response.json() - try: - return result["group_task_id"] - except KeyError: - self.logger.error( - "Expected 'group_task_id' in response: %s", json.dumps(result) - ) - raise - - async def retrieve( - self, - group_task_id: str, - timeout: int = 300, - polling_interval: int = 2, - **kwargs, - ) -> dict[str, pd.DataFrame]: - """ - Periodically check the task status and retrieve the result when the task - completes. - - :param group_task_id: The task ID to retrieve results for. - :param timeout: The maximum time to wait for the task to complete (in seconds). - :param polling_interval: The time to wait between status checks (in seconds). - :return: Records from the DTO API of the successfully completed task. - - """ - # Start time for timeout check - start_time = time.time() - - # Task status URL - status_url = f"{self.url.rstrip('/')}/status/" - - while True: - async with aiohttp.ClientSession() as session: - # Send a GET request to check the task status - async with session.get( - status_url, - headers=self.header, - params={"group_task_id": group_task_id}, - ) as response: - response.raise_for_status() # Raise an error for bad status codes - status_response = await response.json() - - # Check if the task is complete - if status_response.get("status") == "SUCCESS": - - if error_tasks := status_response.get("error_tasks"): - self.logger.error( - f"Tasks {group_task_id} failed: {error_tasks}" - ) - if success_tasks := status_response.get("success_pks"): - params = {"id": ",".join(str(pk) for pk in success_tasks)} - return await self.read(params=params) - elif status_response.get("status") == "FAILURE": - raise Exception( - f"Task {group_task_id} failed: {status_response}" - ) - - # Check if we have reached the timeout - elapsed_time = time.time() - start_time - if elapsed_time > timeout: - raise TimeoutError( - f"Task {group_task_id} did not " - "complete within {timeout} seconds." - ) - - # Wait for the specified polling interval before checking again - await asyncio.sleep(polling_interval) - - def create(self, data: dict[str, Any], **kwargs) -> requests.Response: - raise NotImplementedError("The DTO does not support create.") - - def update(self, df: pd.DataFrame, **kwargs: Any) -> requests.Response: - """ - Update the records in the database. - - :param df: The DataFrame containing the records to update. - :type df: pd.DataFrame - :param kwargs: Additional fields to include in the payload. - :type kwargs: Any - :return: The response from the POST request. - :rtype: requests.Response - :raises requests.RequestException: If the request fails. - - """ - bulk_update_url = ( - f"{self.url.rstrip('/')}/{self.bulk_update_url_suffix.rstrip('/')}/" - ) - - self.logger.debug("bulk_update_url: %s", bulk_update_url) - - # Include additional fields in the payload if provided - payload = {"data": df.to_dict(orient="records")} - payload.update(kwargs) - - try: - response = requests.post( - bulk_update_url, - headers=self.header, - json=payload, - ) - response.raise_for_status() - return response - except requests.RequestException as e: - self.logger.error(f"Error in POST request: {e}") - raise - - def delete(self, id: str, **kwargs) -> Any: - """ - Delete a DTO record from the database. - - :param id: The ID of the DTO record to delete. - :return: A dictionary with a status message indicating success or failure. - - """ - # Include the Authorization header with the token - headers = kwargs.get("headers", {}) - headers["Authorization"] = f"Token {self.token}" - - # Make the DELETE request with the updated headers - response = requests.delete(f"{self.url}/{id}/", headers=headers, **kwargs) - - if response.status_code == 204: - return {"status": "success", "message": "DTO deleted successfully."} - - # Raise an error if the response indicates failure - response.raise_for_status() - - def parse_metadata(self, metadata: pd.DataFrame) -> pd.DataFrame: - """ - Parse the metadata from the DTO API. - - :param metadata: The metadata DataFrame to parse. - :return: The parsed metadata DataFrame. - :raises KeyError: If the metadata DataFrame is missing required columns. - - """ - if metadata.empty: - self.logger.warning("Metadata is empty") - return metadata - - output_columns = [ - "id", - "promotersetsig", - "expression", - "regulator_symbol", - "binding_source", - "expression_source", - "passing_fdr", - "passing_pvalue", - ] - - # required columns are "result" and output_columns - missing_req_columns = [ - col for col in ["result"] + output_columns if col not in metadata.columns - ] - if missing_req_columns: - raise KeyError( - "Metadata is missing required columns: " - "{', '.join(missing_req_columns)}" - ) - - dto_results_list = [] - - # Check and rename keys, logging a warning if a key is missing - keys_to_rename = { - "rank1": "binding_rank_threshold", - "rank2": "perturbation_rank_threshold", - "set1_len": "binding_set_size", - "set2_len": "perturbation_set_size", - } - - for _, row in metadata.iterrows(): - dto_results = json.loads(row.result.replace("'", '"')) - - for old_key, new_key in keys_to_rename.items(): - if old_key in dto_results: - dto_results[new_key] = dto_results.pop(old_key) - else: - self.logger.warning( - f"Key '{old_key}' missing in row with id '{row.id}'." - ) - - dto_results["id"] = row.id - dto_results["promotersetsig"] = row.promotersetsig - dto_results["expression"] = row.expression - dto_results["regulator_symbol"] = row.regulator_symbol - dto_results["binding_source"] = row.binding_source - dto_results["expression_source"] = row.expression_source - dto_results["passing_fdr"] = row.passing_fdr - dto_results["passing_pvalue"] = row.passing_pvalue - - dto_results_list.append(dto_results) - - # Create DataFrame - result_df = pd.DataFrame(dto_results_list) - - # Reorder columns: output_columns first, followed by others - reordered_columns = output_columns + [ - col for col in result_df.columns if col not in output_columns - ] - - return result_df.loc[:, reordered_columns] diff --git a/tfbpapi/ExpressionAPI.py b/tfbpapi/ExpressionAPI.py deleted file mode 100644 index c61e1f7..0000000 --- a/tfbpapi/ExpressionAPI.py +++ /dev/null @@ -1,66 +0,0 @@ -import os -from typing import Any - -import pandas as pd - -from tfbpapi.AbstractRecordsAndFilesAPI import ( - AbstractRecordsAndFilesAPI, -) - - -class ExpressionAPI(AbstractRecordsAndFilesAPI): - """Class to interact with the ExpressionAPI endpoint.""" - - def __init__(self, **kwargs) -> None: - """ - Initialize the ExpressionAPI object. - - :param kwargs: parameters to pass through AbstractRecordsAndFilesAPI to - AbstractAPI. - - """ - valid_param_keys = kwargs.pop( - "valid_param_keys", - [ - "id", - "regulator", - "regulator_locus_tag", - "regulator_symbol", - "batch", - "control", - "mechanism", - "restriction", - "time", - "strain", - "source", - "source_name", - "source_time", - "lab", - "assay", - "workflow", - "effect_colname", - "pvalue_colname", - "preferred_replicate", - ], - ) - - url = kwargs.pop("url", os.getenv("EXPRESSION_URL", None)) - - super().__init__(url=url, valid_keys=valid_param_keys, **kwargs) - - def create(self, data: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The ExpressionAPI does not support create.") - - def update(self, df: pd.DataFrame, **kwargs) -> Any: - raise NotImplementedError("The ExpressionAPI does not support update.") - - def delete(self, id: str, **kwargs) -> Any: - raise NotImplementedError("The ExpressionAPI does not support delete.") - - def submit(self, post_dict: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The ExpressionAPI does not support submit.") - - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Any: - raise NotImplementedError("The ExpressionAPI does not support retrieve.") diff --git a/tfbpapi/ExpressionManualQCAPI.py b/tfbpapi/ExpressionManualQCAPI.py deleted file mode 100644 index 15ff863..0000000 --- a/tfbpapi/ExpressionManualQCAPI.py +++ /dev/null @@ -1,103 +0,0 @@ -import os -from typing import Any - -import pandas as pd -import requests - -from tfbpapi.AbstractRecordsOnlyAPI import AbstractRecordsOnlyAPI - - -class ExpressionManualQCAPI(AbstractRecordsOnlyAPI): - """A class to interact with the ExpressionManualQCAPI endpoint.""" - - def __init__(self, **kwargs): - """ - Initialize the ExpressionManualQCAPI object. - - :param kwargs: parameters to pass to AbstractAPI via AbstractRecordsOnlyAPI. - - """ - valid_param_keys = kwargs.pop( - "valid_param_keys", - [ - "id", - "expression", - "strain_verified", - "regulator_locus_tag", - "regulator_symbol", - "batch", - "replicate", - "control", - "mechanism", - "restriction", - "time", - "source", - "lab", - "assay", - "workflow", - ], - ) - - url = kwargs.pop("url", os.getenv("EXPRESSIONMANUALQC_URL", None)) - if not url: - raise AttributeError( - "url must be provided or the environmental variable ", - "`EXPRESSIONMANUALQC_URL` must be set", - ) - - self.bulk_update_url_suffix = kwargs.pop( - "bulk_update_url_suffix", "bulk-update" - ) - - super().__init__(url=url, valid_keys=valid_param_keys, **kwargs) - - def create(self, data: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The ExpressionManualQCAPI does not support create.") - - def update(self, df: pd.DataFrame, **kwargs: Any) -> requests.Response: - """ - Update the records in the database. - - :param df: The DataFrame containing the records to update. - :type df: pd.DataFrame - :param kwargs: Additional fields to include in the payload. - :type kwargs: Any - :return: The response from the POST request. - :rtype: requests.Response - :raises requests.RequestException: If the request fails. - - """ - bulk_update_url = ( - f"{self.url.rstrip('/')}/{self.bulk_update_url_suffix.rstrip('/')}/" - ) - - self.logger.debug("bulk_update_url: %s", bulk_update_url) - - # Include additional fields in the payload if provided - payload = {"data": df.to_dict(orient="records")} - payload.update(kwargs) - - try: - response = requests.post( - bulk_update_url, - headers=self.header, - json=payload, - ) - response.raise_for_status() - return response - except requests.RequestException as e: - self.logger.error(f"Error in POST request: {e}") - raise - - def delete(self, id: str, **kwargs) -> Any: - raise NotImplementedError("The ExpressionManualQCAPI does not support delete.") - - def submit(self, post_dict: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The ExpressionManualQCAPI does not support submit.") - - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Any: - raise NotImplementedError( - "The ExpressionManualQCAPI does not support retrieve." - ) diff --git a/tfbpapi/FileFormatAPI.py b/tfbpapi/FileFormatAPI.py deleted file mode 100644 index bccdcc1..0000000 --- a/tfbpapi/FileFormatAPI.py +++ /dev/null @@ -1,57 +0,0 @@ -import os -from typing import Any - -import pandas as pd - -from tfbpapi.AbstractRecordsOnlyAPI import AbstractRecordsOnlyAPI - - -class FileFormatAPI(AbstractRecordsOnlyAPI): - """A class to interact with the FileFormatAPI endpoint.""" - - def __init__(self, **kwargs): - """ - Initialize the FileFormatAPI object. - - :param kwargs: parameters to pass to AbstractAPI via AbstractRecordsOnlyAPI. - - """ - valid_param_keys = kwargs.pop( - "valid_param_keys", - [ - "fileformat", - "fields", - "separator", - "feature_identifier_col", - "effect_col", - "default_effect_threshold", - "pval_col", - "default_pvalue_threshold", - ], - ) - - url = kwargs.pop("url", os.getenv("FILEFORMAT_URL", None)) - if not url: - raise AttributeError( - "url must be provided or the environmental variable ", - "`FILEFORMAT_URL` must be set", - ) - - super().__init__(url=url, valid_keys=valid_param_keys, **kwargs) - - def create(self, data: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The FileFormatAPI does not support create.") - - def update(self, df: pd.DataFrame, **kwargs) -> Any: - raise NotImplementedError("The FileFormatAPI does not support update.") - - def delete(self, id: str, **kwargs) -> Any: - raise NotImplementedError("The FileFormatAPI does not support delete.") - - def submit(self, post_dict: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The FileFormatAPI does not support submit.") - - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Any: - raise NotImplementedError("The FileFormatAPI does not support retrieve.") diff --git a/tfbpapi/GenomicFeatureAPI.py b/tfbpapi/GenomicFeatureAPI.py deleted file mode 100644 index 499cb6c..0000000 --- a/tfbpapi/GenomicFeatureAPI.py +++ /dev/null @@ -1,60 +0,0 @@ -import os -from typing import Any - -import pandas as pd - -from tfbpapi.AbstractRecordsOnlyAPI import AbstractRecordsOnlyAPI - - -class GenomicFeatureAPI(AbstractRecordsOnlyAPI): - """A class to interact with the GenomicFeatureAPI endpoint.""" - - def __init__(self, **kwargs): - """ - Initialize the GenomicFeatureAPI object. - - :param kwargs: parameters to pass to AbstractAPI via AbstractRecordsOnlyAPI. - - """ - valid_param_keys = kwargs.pop( - "valid_param_keys", - [ - "id", - "chr", - "start", - "end", - "strand", - "type", - "locus_tag", - "symbol", - "source", - "alias", - "note", - ], - ) - - url = kwargs.pop("url", os.getenv("GENOMICFEATURE_URL", None)) - if not url: - raise AttributeError( - "url must be provided or the environmental variable ", - "`GENOMICFEATURE_URL` must be set", - ) - - super().__init__(url=url, valid_keys=valid_param_keys, **kwargs) - - def create(self, data: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The GenomicFeatureAPI does not support create.") - - def update(self, df: pd.DataFrame, **kwargs) -> Any: - raise NotImplementedError("The GenomicFeatureAPI does not support update.") - - def delete(self, id: str, **kwargs) -> Any: - raise NotImplementedError("The GenomicFeatureAPI does not support delete.") - - def submit(self, post_dict: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The GenomicFeatureAPI does not support submit.") - - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Any: - raise NotImplementedError("The GenomicFeatureAPI does not support retrieve.") diff --git a/tfbpapi/HfQueryAPI.py b/tfbpapi/HfQueryAPI.py index 5907071..98c380a 100644 --- a/tfbpapi/HfQueryAPI.py +++ b/tfbpapi/HfQueryAPI.py @@ -1,23 +1,21 @@ +import logging +import os +import re from pathlib import Path from typing import Any, Literal import duckdb import pandas as pd from datasets import Dataset, DatasetDict, load_dataset -from huggingface_hub import DatasetCard +from huggingface_hub import hf_hub_download, snapshot_download +from huggingface_hub.constants import HF_HUB_CACHE -from .AbstractHfAPI import AbstractHfAPI +from .errors import RepoTooLargeError +from .HfCacheManager import HFCacheManager -class HfQueryAPI(AbstractHfAPI): - """ - Concrete implementation of AbstractHfAPI with DuckDB query capabilities. - - This class provides seamless querying of Hugging Face datasets using SQL via DuckDB. - It automatically handles dataset downloading, parsing, and provides a simple query - interface. - - """ +class HfQueryAPI: + """Hugging Face API client with intelligent downloading and SQL querying.""" def __init__( self, @@ -27,147 +25,704 @@ def __init__( cache_dir: str | Path | None = None, auto_download_threshold_mb: float = 100.0, auto_parse_datacard: bool = True, + enable_cache_management: bool = True, + cache_auto_cleanup: bool = False, + cache_max_age_days: int = 30, + cache_max_size: str = "10GB", ): """ Initialize the HF Query API client. - :param repo_id: The repo identifier on HF (e.g., "user/dataset") - :param repo_type: One of {"model", "dataset", "space"}. Defaults to "dataset" - :param token: Optional HF token for private repos - :param cache_dir: HF cache directory for downloads - :param auto_download_threshold_mb: Auto-download threshold in MB - :param auto_parse_datacard: Whether to automatically parse datacard on init + :param repo_id: Repository identifier (e.g., "user/dataset") + :param repo_type: Type of repository ("dataset", "model", "space") + :param token: HuggingFace token for authentication + :param cache_dir: HF cache_dir for downloads + :param auto_download_threshold_mb: Threshold in MB for auto full download + :param auto_parse_datacard: Whether to auto-parse the datacard on init + :param enable_cache_management: Enable integrated cache management features + :param cache_auto_cleanup: Enable automatic cache cleanup + :param cache_max_age_days: Maximum age in days for cache entries + :param cache_max_size: Maximum total cache size (e.g., "10GB") """ - super().__init__(repo_id, repo_type, token, cache_dir) + self.logger = logging.getLogger(self.__class__.__name__) + + # Initialize data info manager with new architecture + self.data_info = HfDataInfoManager( + repo_id=repo_id, repo_type=repo_type, token=token + ) + + self.cache_dir = Path( + cache_dir if cache_dir else os.getenv("HF_CACHE_DIR", HF_HUB_CACHE) + ) self.auto_download_threshold_mb = auto_download_threshold_mb - self._datasets: dict[str, Any] = {} self._loaded_datasets: dict[str, Dataset | DatasetDict] = {} self._duckdb_conn = duckdb.connect(":memory:") self._table_filters: dict[str, str] = {} + self._partition_cache: dict[str, set[str]] = {} # Track downloaded partitions + + # Initialize cache management + self._cache_manager = None + if enable_cache_management: + self._cache_manager = HFCacheManager(logger=self.logger) + self._cache_auto_cleanup = cache_auto_cleanup + self._cache_max_age_days = cache_max_age_days + self._cache_max_size = cache_max_size if auto_parse_datacard: try: - self.datasets = self.parse_datacard() + self.data_info.parse_datacard() except Exception as e: self.logger.warning(f"Failed to auto-parse datacard: {e}") - self._datasets = {} + self.data_info.clear() + + @property + def repo_id(self) -> str: + return self.data_info.repo_id + + @property + def repo_type(self) -> str: + return self.data_info.repo_type + + @property + def token(self) -> str | None: + return self.data_info.token @property def datasets(self) -> dict[str, Any]: """Parsed dataset configurations from the datacard.""" - return self._datasets + # Convert TableConfig objects to legacy dictionary format for backward compatibility + result = {} + for name, table_config in self.data_info.datasets.items(): + if table_config: + # Convert FeatureInfo objects to legacy format + features = {} + for feat_name, feat_info in table_config.features.items(): + features[feat_name] = { + "dtype": feat_info.dtype, + "description": feat_info.description, + } + + # Convert DataFileInfo objects to legacy format + data_files = [] + for file_info in table_config.data_files: + data_files.append( + {"path": file_info.path, "split": file_info.split} + ) + + result[name] = { + "features": features, + "data_files": data_files, + "config": table_config.config, + "loaded": table_config.downloaded, # Use "loaded" for backward compatibility + "is_partitioned": table_config.is_partitioned, + } + return result @datasets.setter def datasets(self, value: dict[str, Any]) -> None: - """Set the parsed datasets and update available tables.""" - self._datasets = value - self._update_available_tables() + """Set dataset configurations (for backward compatibility).""" + # Clear existing data and register new tables + self.data_info.clear() + # Convert legacy format to TableConfig objects if needed + from .datainfo.models import TableConfig + + table_configs = {} + for name, config in value.items(): + if isinstance(config, TableConfig): + table_configs[name] = config + else: + # Convert from legacy dict format + table_configs[name] = TableConfig( + name=name, + features=config.get("features", {}), + data_files=config.get("data_files", []), + config=config, + downloaded=config.get("loaded", False), # "loaded" was the old name + is_partitioned=config.get("is_partitioned", False), + partition_info=None, + ) + self.data_info._registry.register_tables(table_configs) @property def available_tables(self) -> list[str]: """List of available table names for querying.""" - return list(self._datasets.keys()) + return self.data_info.available_tables + + @property + def cache_manager(self) -> HFCacheManager | None: + """Access to the integrated cache manager for advanced cache operations.""" + return self._cache_manager + + @property + def cache_dir(self) -> Path: + return self._cache_dir + + @cache_dir.setter + def cache_dir(self, value: str | Path) -> None: + """Set the cache directory for huggingface_hub downloads.""" + path = Path(value) + if not path.exists(): + raise FileNotFoundError(f"Cache directory {path} does not exist") + self._cache_dir = path + + @property + def size(self) -> dict[str, Any] | None: + """Size information from the HF Dataset Server API.""" + size_info = self.data_info.get_dataset_size() + if size_info: + return { + "total": size_info.total_bytes, + "total_mb": size_info.total_mb, + "configs": size_info.config_sizes, + } + return None - def parse_datacard(self) -> dict[str, Any]: + @size.setter + def size(self, value: dict[str, Any]) -> None: + """Set size information (for backward compatibility).""" + # Convert legacy format to DatasetSize object + from .datainfo.models import DatasetSize + + dataset_size = DatasetSize.from_hf_size_response(value) + self.data_info._dataset_size = dataset_size + + @property + def snapshot_path(self) -> Path | None: + """Path to the last downloaded snapshot (if any).""" + return getattr(self, "_snapshot_path", None) + + @snapshot_path.setter + def snapshot_path(self, value: str | Path | None) -> None: + self._snapshot_path = None if value is None else Path(value) + + def _build_auth_headers(self) -> dict[str, str]: + """Build authentication headers if token is available.""" + return ( + {"Authorization": f"Bearer {self.data_info.token}"} + if self.data_info.token + else {} + ) + + def _normalize_patterns(self, kwargs: dict[str, Any]) -> None: + """Convert string patterns to lists.""" + for pattern_key in ["allow_patterns", "ignore_patterns"]: + if pattern_key in kwargs and kwargs[pattern_key] is not None: + patterns = kwargs[pattern_key] + if isinstance(patterns, str): + kwargs[pattern_key] = [patterns] + + def download_partitions( + self, table_name: str, partition_values: set[str] | None = None + ) -> Path: """ - Parse the dataset card into a standardized format. + Download specific partitions using the path_template from partitioning metadata. - :return: Dict mapping config names to their metadata + :param table_name: Name of the dataset table + :param partition_values: Specific partition values to download (None for all) + :return: Path to downloaded data """ - try: - card = DatasetCard.load(self.repo_id, self.repo_type) - data_dict = card.data.to_dict() - except Exception as e: - self.logger.error(f"Failed to load dataset card: {e}") - return {} + table_config = self.data_info.get_table_or_raise(table_name) - parsed_datasets = {} + if not table_config.is_partitioned: + raise ValueError(f"Table {table_name} is not configured as partitioned") - for config in data_dict.get("configs", []): - config_name = config["config_name"] + partition_info = self.data_info.get_partition_info(table_name) + if not partition_info: + raise ValueError(f"Table {table_name} missing partition information") - # Extract features for filtering/querying - features = {} - if "dataset_info" in config and "features" in config["dataset_info"]: - for feature in config["dataset_info"]["features"]: - features[feature["name"]] = { - "dtype": feature["dtype"], - "description": feature.get("description", ""), - } + path_template = partition_info.get("path_template") + if not path_template: + raise ValueError( + f"Table {table_name} missing required path_template in partitioning config" + ) - # Extract file paths - data_files = [] - for file_info in config.get("data_files", []): - data_files.append( - { - "path": file_info["path"], - "split": file_info.get("split", "train"), - } - ) + partition_columns = partition_info.get("partition_by", []) + + if partition_values and partition_columns: + # Download specific partitions using path template + patterns = [] + for partition_value in partition_values: + # For single-column partitioning, substitute the first column + if len(partition_columns) == 1: + column = partition_columns[0] + pattern = path_template.replace(f"{{{column}}}", partition_value) + patterns.append(pattern) + else: + # For multi-column partitioning, we'd need more sophisticated logic + # For now, create a wildcard pattern for the specific value + # This is a simplified approach - real implementation would need + # to handle multi-dimensional partition filtering + pattern = path_template + for column in partition_columns: + if f"{{{column}}}" in pattern: + pattern = pattern.replace(f"{{{column}}}", partition_value) + break + patterns.append(pattern) + + self.logger.info( + f"Downloading partitions for {table_name}: {partition_values}" + ) + self.logger.debug(f"Using patterns: {patterns}") + return self.download(allow_patterns=patterns) + else: + # Download all partitions - use the original data files paths + patterns = table_config.get_file_paths() - parsed_datasets[config_name] = { - "features": features, - "data_files": data_files, - "config": config, - "loaded": False, - } + self.logger.info(f"Downloading all partitions for {table_name}") + return self.download(allow_patterns=patterns) - return parsed_datasets + def download( + self, + files: list[str] | str | None = None, + force_full_download: bool = False, + auto_download_threshold_mb: float = 100.0, + dry_run: bool = False, + **kwargs, + ) -> Path: + """Download dataset with intelligent partitioning support.""" + dataset_size_mb = self.data_info.get_dataset_size_mb() + + if dataset_size_mb <= auto_download_threshold_mb or force_full_download: + self.logger.info( + f"Dataset size ({dataset_size_mb:.2f} MB) is below threshold. " + "Downloading entire repo." + ) + files = None + kwargs.pop("allow_patterns", None) + kwargs.pop("ignore_patterns", None) + elif ( + not files + and not kwargs.get("allow_patterns") + and not kwargs.get("ignore_patterns") + ): + excess_size_mb = dataset_size_mb - auto_download_threshold_mb + raise RepoTooLargeError( + f"Dataset size ({dataset_size_mb:.2f} MB) exceeds threshold by " + f"{excess_size_mb:.2f} MB. Specify files, patterns, or set " + "force_full_download=True." + ) + + # Handle specific file downloads + if files is not None: + if isinstance(files, str) or (isinstance(files, list) and len(files) == 1): + filename = files if isinstance(files, str) else files[0] + return self._download_single_file(filename, dry_run=dry_run, **kwargs) + elif isinstance(files, list) and len(files) > 1: + if kwargs.get("allow_patterns") is not None: + self.logger.warning( + "Both 'files' and 'allow_patterns' provided. Using 'files'." + ) + kwargs["allow_patterns"] = files + + return self._download_snapshot(dry_run=dry_run, **kwargs) + + def _download_single_file( + self, filename: str, dry_run: bool = False, **kwargs + ) -> Path: + """Download a single file using hf_hub_download.""" + self.logger.info(f"Downloading single file: {filename}") + + if dry_run: + self.logger.info(f"[DRY RUN] Would download {filename} from {self.repo_id}") + return Path("dry_run_path") + + hf_kwargs = { + "repo_id": self.repo_id, + "repo_type": self.repo_type, + "filename": filename, + "token": self.token, + **kwargs, + } + + if "local_dir" not in hf_kwargs and self.cache_dir is not None: + hf_kwargs["cache_dir"] = str(self.cache_dir) + + for key in ["local_dir", "cache_dir"]: + if key in hf_kwargs and hf_kwargs[key] is not None: + hf_kwargs[key] = str(hf_kwargs[key]) + file_path = hf_hub_download(**hf_kwargs) + self.snapshot_path = Path(file_path).parent + return Path(file_path) + + def _download_snapshot(self, dry_run: bool = False, **kwargs) -> Path: + """Download repository snapshot using snapshot_download.""" + if dry_run: + self.logger.info(f"[DRY RUN] Would download from {self.repo_id}:") + self.logger.info(f" - allow_patterns: {kwargs.get('allow_patterns')}") + self.logger.info(f" - ignore_patterns: {kwargs.get('ignore_patterns')}") + return Path("dry_run_path") + + self.logger.info( + f"Downloading repo snapshot - " + f"allow: {kwargs.get('allow_patterns')}, " + f"ignore: {kwargs.get('ignore_patterns')}" + ) + + snapshot_kwargs = { + "repo_id": self.repo_id, + "repo_type": self.repo_type, + "token": self.token, + **kwargs, + } + + if ( + "local_dir" not in snapshot_kwargs + and "cache_dir" not in snapshot_kwargs + and self.cache_dir is not None + ): + snapshot_kwargs["cache_dir"] = str(self.cache_dir) + + self._normalize_patterns(snapshot_kwargs) + snapshot_path = snapshot_download(**snapshot_kwargs) + self.snapshot_path = Path(snapshot_path) + return self.snapshot_path def _update_available_tables(self) -> None: """Update the logger with information about available tables.""" - if self._datasets: + if self.data_info.available_tables: self.logger.info(f"Available tables: {', '.join(self.available_tables)}") - def _ensure_dataset_loaded(self, table_name: str) -> Dataset | DatasetDict: + def _extract_table_references(self, sql: str) -> set[str]: """ - Ensure a dataset is loaded and available for querying. + Extract all table references from a SQL query. - :param table_name: Name of the dataset configuration - :return: The loaded dataset - :raises ValueError: If table_name is not found + Handles FROM clauses, JOINs, subqueries, and direct parquet file references. + + :param sql: SQL query string + :return: Set of table names/file references found in the query """ - if table_name not in self._datasets: - raise ValueError( - f"Table '{table_name}' not found. " - f"Available tables: {self.available_tables}" + table_refs = set() + + # Remove comments and normalize whitespace + sql_clean = re.sub(r"--.*?\n", " ", sql, flags=re.DOTALL) + sql_clean = re.sub(r"/\*.*?\*/", " ", sql_clean, flags=re.DOTALL) + sql_clean = re.sub(r"\s+", " ", sql_clean.strip()) + + # Pattern to match table references in FROM and JOIN clauses + # This handles: FROM table, FROM read_parquet('file.parquet'), JOIN table ON... + from_pattern = r""" + (?:FROM|JOIN)\s+ # FROM or JOIN keyword + (?: + read_parquet\s*\(\s*['"]([^'"]+)['"][\s)]* # read_parquet('file.parquet') + | + ([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*) # table.name or tablename ) + (?:\s+(?:AS\s+)?[a-zA-Z_][a-zA-Z0-9_]*)? # Optional alias + (?:\s+ON\s+.*?(?=\s+(?:FROM|JOIN|WHERE|GROUP|ORDER|LIMIT|$)))? # Optional ON clause for JOINs + """ + for match in re.finditer(from_pattern, sql_clean, re.IGNORECASE | re.VERBOSE): + parquet_file = match.group(1) + table_name = match.group(2) + + if parquet_file: + # Extract just the filename without extension for parquet files + file_ref = Path(parquet_file).stem + table_refs.add(file_ref) + elif table_name: + # Clean table name (remove schema prefix if present) + clean_name = table_name.split(".")[-1] + table_refs.add(clean_name) + + # Also check for simple table name patterns in case the regex missed something + simple_pattern = r"\b(?:FROM|JOIN)\s+([a-zA-Z_][a-zA-Z0-9_]*)\b" + for match in re.finditer(simple_pattern, sql_clean, re.IGNORECASE): + table_name = match.group(1).lower() + # Filter out SQL keywords and function names + if table_name not in { + "select", + "where", + "group", + "order", + "having", + "limit", + "offset", + "union", + "intersect", + "except", + "read_parquet", + "read_csv", + "read_json", + }: + table_refs.add(table_name) + + return table_refs + + def _resolve_table_to_files(self, table_ref: str) -> list[str]: + """ + Resolve a table reference to specific files that need to be downloaded. + + :param table_ref: Table name or file reference from SQL + :return: List of file paths/patterns needed for this table + + """ + return self.data_info.resolve_table_to_files(table_ref) + + def _ensure_tables_available(self, sql: str) -> set[str]: + """ + Ensure all tables referenced in SQL are available, downloading if necessary. + + :param sql: SQL query string + :return: Set of table names that were processed + + """ + table_refs = self._extract_table_references(sql) + processed_tables = set() + + for table_ref in table_refs: + if self.data_info.has_table(table_ref): + # Table is already known from dataset card + if not self.data_info.is_table_downloaded(table_ref): + self._ensure_dataset_loaded(table_ref, sql) + processed_tables.add(table_ref) + else: + # Try to discover as standalone file + if self._try_discover_standalone_table(table_ref): + processed_tables.add(table_ref) + else: + # File doesn't exist locally, try to download it + files_needed = self._resolve_table_to_files(table_ref) + if self._try_download_specific_files(files_needed, table_ref): + processed_tables.add(table_ref) + else: + self.logger.warning( + f"Could not locate or download table: {table_ref}" + ) + + return processed_tables + + def _try_download_specific_files(self, files: list[str], table_name: str) -> bool: + """ + Attempt to download specific files for a table. + + :param files: List of file paths/patterns to download + :param table_name: Name of the table these files represent + :return: True if download was successful + + """ + try: + # Try downloading specific files + for file_path in files: + try: + # First check if file exists in repo + downloaded_file_path = self._download_single_file( + file_path, dry_run=False + ) + if downloaded_file_path and downloaded_file_path.exists(): + # Register the file as a table if it's a parquet file + if file_path.endswith(".parquet"): + self._register_parquet_as_table( + downloaded_file_path, table_name + ) + return True + except Exception as e: + self.logger.debug(f"Failed to download {file_path}: {e}") + continue + + # If individual file downloads failed, try pattern-based download + if files: + try: + self.download(allow_patterns=files, force_full_download=False) + # After download, try to discover the table again + return self._try_discover_standalone_table(table_name) + except RepoTooLargeError: + self.logger.error( + f"Repository too large to download files for table: {table_name}" + ) + return False + except Exception as e: + self.logger.error( + f"Failed to download files for table {table_name}: {e}" + ) + return False + + return False + except Exception as e: + self.logger.error(f"Error downloading files for table {table_name}: {e}") + return False + + def _register_parquet_as_table(self, parquet_path: Path, table_name: str) -> None: + """Register a parquet file directly as a DuckDB table.""" + create_view_sql = f""" + CREATE OR REPLACE VIEW {table_name} AS + SELECT * FROM read_parquet('{parquet_path}') + """ + self._duckdb_conn.execute(create_view_sql) + + # Add to datasets registry via DataInfo + self.data_info.add_standalone_table(table_name, parquet_path, downloaded=True) + + def _try_discover_standalone_table(self, table_name: str) -> bool: + """ + Try to discover a standalone parquet file as a table. + + :param table_name: The name of the table to discover + :return: True if the table was discovered and registered + + """ + if not self.snapshot_path: + return False + + # Look for parquet file with matching name + parquet_file = self.snapshot_path / f"{table_name}.parquet" + if parquet_file.exists(): + # Register the standalone parquet file with DataInfo + self.data_info.add_standalone_table( + table_name, parquet_file, downloaded=True + ) + + # Register directly with DuckDB + create_view_sql = f""" + CREATE OR REPLACE VIEW {table_name} AS + SELECT * FROM read_parquet('{parquet_file}') + """ + self._duckdb_conn.execute(create_view_sql) + self.logger.debug( + f"Registered standalone parquet file as table: {table_name}" + ) + return True + + return False + + def _ensure_dataset_loaded( + self, table_name: str, sql: str | None = None + ) -> Dataset | DatasetDict | None: + """ + Ensure a dataset is loaded, with intelligent partition downloading. + + :param table_name: Name of the dataset configuration + :param sql: Optional SQL query to determine required partitions + :return: The loaded dataset (None for partitioned datasets that are loaded + directly into DuckDB) + + """ + if not self.data_info.has_table(table_name): + # Try to discover the table as a standalone parquet file + if self._try_discover_standalone_table(table_name): + self.logger.info(f"Discovered standalone table: {table_name}") + else: + raise ValueError( + f"Table '{table_name}' not found. " + f"Available tables: {self.available_tables}" + ) + + dataset_info = self.data_info.get_table_info(table_name) + + # Check if we need to download partitions + if dataset_info and dataset_info.is_partitioned and sql: + required_partitions = self._get_required_partitions(sql, table_name) + + if required_partitions: + # Check if we have these partitions cached + cached_partitions = self._partition_cache.get(table_name, set()) + missing_partitions = required_partitions - cached_partitions + + if missing_partitions: + self.logger.info( + f"Downloading missing partitions: {missing_partitions}" + ) + self._download_partitions(table_name, missing_partitions) + self._partition_cache.setdefault(table_name, set()).update( + missing_partitions + ) + + # Check if dataset is already loaded if table_name in self._loaded_datasets: return self._loaded_datasets[table_name] - # Download the dataset if not already downloaded + # Check if standalone table is already registered + if dataset_info and dataset_info.downloaded: + return None # Standalone tables are registered directly with DuckDB + + # Download if needed if not self.snapshot_path: - self.logger.info(f"Downloading dataset for table '{table_name}'...") - self.download(auto_download_threshold_mb=self.auto_download_threshold_mb) + if dataset_info and dataset_info.is_partitioned and sql: + required_partitions = self._get_required_partitions(sql, table_name) + self._download_partitions(table_name, required_partitions) + else: + self.logger.info(f"Downloading dataset for table '{table_name}'...") + self.download( + auto_download_threshold_mb=self.auto_download_threshold_mb + ) - # Load the specific dataset configuration + # Load the dataset try: self.logger.info(f"Loading dataset configuration '{table_name}'...") - dataset = load_dataset( - str(self.snapshot_path), name=table_name, keep_in_memory=False - ) - self._loaded_datasets[table_name] = dataset - self._datasets[table_name]["loaded"] = True - - # Register with DuckDB - self._register_dataset_with_duckdb(table_name, dataset) - return dataset + if dataset_info and dataset_info.is_partitioned: + # For partitioned datasets, load directly into DuckDB + self._load_partitioned_dataset(table_name) + # Mark as downloaded but don't store in _loaded_datasets since it's in DuckDB + self.data_info.mark_table_downloaded(table_name, True) + return None + else: + # Standard dataset loading + dataset = load_dataset( + str(self.snapshot_path), name=table_name, keep_in_memory=False + ) + self._loaded_datasets[table_name] = dataset + self._register_dataset_with_duckdb(table_name, dataset) + return dataset except Exception as e: self.logger.error(f"Failed to load dataset '{table_name}': {e}") raise + def _load_partitioned_dataset(self, table_name: str) -> None: + """ + Load a partitioned dataset directly into DuckDB without using Hugging Face + datasets. + + This is more efficient for partitioned datasets like genome_map. + + """ + # Find parquet files in the snapshot + if not self.snapshot_path: + raise ValueError("No snapshot path available") + + # Look for parquet files matching the dataset pattern + parquet_files: list[Path] = [] + for file_info in self.data_info.get_table_data_files(table_name): + pattern = file_info["path"] + if "*" in pattern: + # Convert pattern to actual file search + search_pattern = pattern.replace("*", "**") + parquet_files.extend(self.snapshot_path.glob(search_pattern)) + + if not parquet_files: + # Fallback: find all parquet files + parquet_files = list(self.snapshot_path.rglob("*.parquet")) + + if parquet_files: + # Register parquet files directly with DuckDB + file_paths = [str(f) for f in parquet_files] + self.logger.info( + f"Registering {len(file_paths)} parquet files for {table_name}" + ) + + # Create a view that reads from all parquet files + files_str = "', '".join(file_paths) + create_view_sql = f""" + CREATE OR REPLACE VIEW {table_name} AS + SELECT * FROM read_parquet(['{files_str}']) + """ + self._duckdb_conn.execute(create_view_sql) + self.logger.debug( + f"Created view '{table_name}' from {len(file_paths)} parquet files" + ) + else: + raise ValueError( + f"No parquet files found for partitioned dataset {table_name}" + ) + def _register_dataset_with_duckdb( self, table_name: str, dataset: Dataset | DatasetDict ) -> None: - """Register a dataset with DuckDB for SQL querying.""" + """Register a standard dataset with DuckDB for SQL querying.""" try: if isinstance(dataset, DatasetDict): - # Register each split as a separate view for split_name, split_dataset in dataset.items(): view_name = ( f"{table_name}_{split_name}" @@ -180,7 +735,6 @@ def _register_dataset_with_duckdb( f"Registered view '{view_name}' with {len(df)} rows" ) else: - # Single dataset df = dataset.to_pandas() self._duckdb_conn.register(table_name, df) self.logger.debug( @@ -194,32 +748,26 @@ def _register_dataset_with_duckdb( def query(self, sql: str, table_name: str | None = None) -> pd.DataFrame: """ - Execute a SQL query against the dataset. + Execute a SQL query with intelligent table discovery and loading. :param sql: SQL query string - :param table_name: Optional table name to ensure is loaded. If not provided, - attempts to infer from the SQL query + :param table_name: Optional table name to ensure is loaded (legacy parameter) :return: Query results as a pandas DataFrame """ - # If table_name not provided, try to infer from available tables - if table_name is None: - sql_lower = sql.lower() - for available_table in self.available_tables: - if available_table in sql_lower: - table_name = available_table - break + # Use intelligent table discovery to ensure all referenced tables are available + processed_tables = self._ensure_tables_available(sql) - # If we found a table name, ensure it's loaded - if table_name: - self._ensure_dataset_loaded(table_name) - elif not self._loaded_datasets: - # If no datasets are loaded and we couldn't infer, - # try to load the first one - if self.available_tables: - self._ensure_dataset_loaded(self.available_tables[0]) - - # Apply any table filters to the query + # Legacy support: if table_name is provided, ensure it's also available + if table_name and table_name not in processed_tables: + try: + self._ensure_dataset_loaded(table_name, sql) + except ValueError: + # Try to discover as standalone table + if not self._try_discover_standalone_table(table_name): + self.logger.warning(f"Could not load specified table: {table_name}") + + # Apply table filters modified_sql = self._apply_table_filters(sql) try: @@ -231,123 +779,1386 @@ def query(self, sql: str, table_name: str | None = None) -> pd.DataFrame: if modified_sql != sql: self.logger.debug(f"Original query: {sql}") self.logger.debug(f"Modified query: {modified_sql}") + + # If query failed, provide helpful information about available tables + if self.data_info.available_tables: + self.logger.info( + f"Available tables: {', '.join(self.data_info.available_tables)}" + ) + raise - def describe_table(self, table_name: str) -> pd.DataFrame: - """ - Get information about a table's structure. + def _apply_table_filters(self, sql: str) -> str: + """Apply table filters to a SQL query.""" + if not self._table_filters: + return sql - :param table_name: Name of the table to describe - :return: DataFrame with column information + modified_sql = sql + for table_name, filter_condition in self._table_filters.items(): + pattern = rf"\b{re.escape(table_name)}\b" + replacement = f"(SELECT * FROM {table_name} WHERE {filter_condition})" - """ + if re.search(pattern, modified_sql, re.IGNORECASE): + if not re.search( + rf"\(SELECT.*FROM\s+{re.escape(table_name)}\s+WHERE", + modified_sql, + re.IGNORECASE, + ): + modified_sql = re.sub( + pattern, replacement, modified_sql, flags=re.IGNORECASE + ) + + return modified_sql + + def get_table_filter(self, table_name: str) -> str | None: + """Get the current filter for a table.""" + return self._table_filters.get(table_name) + + def set_table_filter(self, table_name: str, filter_condition: str) -> None: + """Set a filter condition for a table.""" + self._table_filters[table_name] = filter_condition + self.logger.debug(f"Set filter for table '{table_name}': {filter_condition}") + + def remove_table_filter(self, table_name: str) -> None: + """Remove any filter condition for a table.""" + removed_filter = self._table_filters.pop(table_name, None) + if removed_filter: + self.logger.debug( + f"Removed filter for table '{table_name}': {removed_filter}" + ) + + # Standard methods + def describe_table(self, table_name: str) -> pd.DataFrame: + """Get information about a table's structure.""" self._ensure_dataset_loaded(table_name) return self.query(f"DESCRIBE {table_name}") def sample(self, table_name: str, n: int = 5) -> pd.DataFrame: + """Get a sample of rows from a table.""" + return self.query(f"SELECT * FROM {table_name} LIMIT {n}", table_name) + + def count(self, table_name: str) -> int: + """Get the number of rows in a table.""" + result = self.query(f"SELECT COUNT(*) as count FROM {table_name}", table_name) + return result.iloc[0]["count"] + + def get_columns(self, table_name: str) -> list[str]: + """Get column names for a table.""" + if not self.data_info.has_table(table_name): + raise ValueError(f"Table '{table_name}' not found") + return list(self.data_info.get_table_features(table_name).keys()) + + # ============== Cache Management Methods ============== + + def get_cache_info(self) -> dict[str, Any]: """ - Get a sample of rows from a table. + Get comprehensive cache information including current repo details. + + :return: Dictionary with cache stats, repo info, and recommendations + """ + if not self._cache_manager: + self.logger.debug("Cache management is disabled") + return {"cache_management": "disabled"} + + self.logger.info("Retrieving comprehensive cache information") - :param table_name: Name of the table to sample - :param n: Number of rows to return - :return: Sample DataFrame + from huggingface_hub import scan_cache_dir + try: + cache_info = scan_cache_dir() + self.logger.debug( + f"Scanned cache directory: {cache_info.size_on_disk_str} in {len(cache_info.repos)} repos" + ) + except Exception as e: + self.logger.error(f"Failed to scan cache directory: {e}") + return {"error": f"Failed to scan cache: {e}"} + + # Find current repo in cache + current_repo_info = None + for repo in cache_info.repos: + if ( + repo.repo_id == self.repo_id + and repo.repo_type.lower() == self.repo_type + ): + current_repo_info = repo + break + + result = { + "cache_management": "enabled", + "cache_directory": str(self.cache_dir), + "total_cache_size": cache_info.size_on_disk_str, + "total_cache_size_bytes": cache_info.size_on_disk, + "total_repos_cached": len(cache_info.repos), + "current_repo": { + "repo_id": self.repo_id, + "repo_type": self.repo_type, + "cached": current_repo_info is not None, + "cache_info": None, + }, + "cache_policies": { + "auto_cleanup": getattr(self, "_cache_auto_cleanup", False), + "max_age_days": getattr(self, "_cache_max_age_days", 30), + "max_size": getattr(self, "_cache_max_size", "10GB"), + }, + "recommendations": [], + } + + if current_repo_info: + result["current_repo"]["cache_info"] = { + "size_on_disk": current_repo_info.size_on_disk_str, + "size_bytes": current_repo_info.size_on_disk, + "nb_files": current_repo_info.nb_files, + "last_accessed": current_repo_info.last_accessed, + "last_modified": current_repo_info.last_modified, + "revisions_count": len(current_repo_info.revisions), + "revisions": [ + { + "commit_hash": rev.commit_hash[:8], + "size_on_disk": rev.size_on_disk_str, + "last_modified": rev.last_modified, + "files_count": len(rev.files), + } + for rev in sorted( + current_repo_info.revisions, + key=lambda r: r.last_modified, + reverse=True, + ) + ], + } + + # Add recommendations with logging + max_size_bytes = self._cache_manager._parse_size_string( + getattr(self, "_cache_max_size", "10GB") + ) + if cache_info.size_on_disk > max_size_bytes: + recommendation = ( + f"Cache size ({cache_info.size_on_disk_str}) exceeds configured limit " + f"({getattr(self, '_cache_max_size', '10GB')}). Consider running cache cleanup." + ) + result["recommendations"].append(recommendation) + self.logger.warning(f"Cache size warning: {recommendation}") + + if len(cache_info.repos) > 50: # Arbitrary threshold + recommendation = ( + f"Large number of cached repos ({len(cache_info.repos)}). " + "Consider cleaning unused repositories." + ) + result["recommendations"].append(recommendation) + self.logger.info(f"Cache optimization suggestion: {recommendation}") + + if current_repo_info: + self.logger.info( + f"Current repo {self.repo_id} found in cache: " + f"{current_repo_info.size_on_disk_str}, {len(current_repo_info.revisions)} revisions" + ) + else: + self.logger.debug(f"Current repo {self.repo_id} not found in cache") + + self.logger.info( + f"Cache info summary: {cache_info.size_on_disk_str} total, " + f"{len(cache_info.repos)} repos, {len(result['recommendations'])} recommendations" + ) + + return result + + def get_repo_cache_info(self, repo_id: str | None = None) -> dict[str, Any]: """ - return self.query(f"SELECT * FROM {table_name} LIMIT {n}", table_name) + Get detailed cache information for a specific repository. - def count(self, table_name: str) -> int: + :param repo_id: Repository ID (defaults to current repo) + :return: Dictionary with detailed repo cache information + """ + if not self._cache_manager: + self.logger.debug("Cache management is disabled") + return {"cache_management": "disabled"} + + target_repo_id = repo_id or self.repo_id + self.logger.info( + f"Retrieving detailed cache information for repo: {target_repo_id}" + ) + + from huggingface_hub import scan_cache_dir + + try: + cache_info = scan_cache_dir() + except Exception as e: + self.logger.error(f"Failed to scan cache directory: {e}") + return {"error": f"Failed to scan cache: {e}"} + + # Find the specified repo + target_repo = None + for repo in cache_info.repos: + if repo.repo_id == target_repo_id: + target_repo = repo + break + + if not target_repo: + self.logger.info(f"Repository {target_repo_id} not found in cache") + return { + "repo_id": target_repo_id, + "cached": False, + "message": "Repository not found in cache", + } + + return { + "repo_id": target_repo_id, + "repo_type": target_repo.repo_type, + "cached": True, + "size_on_disk": target_repo.size_on_disk_str, + "size_bytes": target_repo.size_on_disk, + "files_count": target_repo.nb_files, + "last_accessed": target_repo.last_accessed, + "last_modified": target_repo.last_modified, + "revisions_count": len(target_repo.revisions), + "revisions": [ + { + "commit_hash": rev.commit_hash, + "short_hash": rev.commit_hash[:8], + "size_on_disk": rev.size_on_disk_str, + "size_bytes": rev.size_on_disk, + "last_modified": rev.last_modified, + "files_count": len(rev.files), + "files": [ + { + "name": file.file_name, + "size": file.size_on_disk, + "blob_last_accessed": file.blob_last_accessed, + "blob_last_modified": file.blob_last_modified, + } + for file in sorted(rev.files, key=lambda f: f.file_name) + ], + } + for rev in sorted( + target_repo.revisions, key=lambda r: r.last_modified, reverse=True + ) + ], + } + + self.logger.info( + f"Found repo {target_repo_id} in cache: {target_repo.size_on_disk_str}, " + f"{target_repo.nb_files} files, {len(target_repo.revisions)} revisions" + ) + + return result + + def check_cached_files(self, table_name: str | None = None) -> dict[str, Any]: """ - Get the number of rows in a table. + Check which files for current dataset/table are cached locally. - :param table_name: Name of the table to count - :return: Number of rows + :param table_name: Specific table to check (defaults to all tables) + :return: Dictionary with file cache status + """ + if not self._cache_manager: + self.logger.debug("Cache management is disabled") + return {"cache_management": "disabled"} + + if table_name: + self.logger.info(f"Checking cache status for table: {table_name}") + else: + self.logger.info(f"Checking cache status for all tables in {self.repo_id}") + + from huggingface_hub import _CACHED_NO_EXIST, try_to_load_from_cache + + result = { + "repo_id": self.repo_id, + "cache_directory": str(self.cache_dir), + "tables": {}, + } + + # Check specific table or all tables + tables_to_check = [table_name] if table_name else self.available_tables + self.logger.debug(f"Checking {len(tables_to_check)} tables: {tables_to_check}") + + for table in tables_to_check: + if not self.data_info.has_table(table): + self.logger.warning(f"Table {table} not found in dataset configuration") + result["tables"][table] = { + "exists": False, + "message": "Table not found in dataset configuration", + } + continue + + table_config = self.data_info.get_table_info(table) + if not table_config: + continue + + file_status = {} + for file_info in table_config.data_files: + file_path = file_info.path + + # Check if file is cached + cached_path = try_to_load_from_cache( + repo_id=self.repo_id, + filename=file_path, + repo_type=self.repo_type, + ) + if isinstance(cached_path, str): + # File is cached + file_status[file_path] = { + "cached": True, + "local_path": cached_path, + "split": file_info.split, + } + elif cached_path is _CACHED_NO_EXIST: + # Non-existence is cached (file doesn't exist on Hub) + file_status[file_path] = { + "cached": False, + "exists_on_hub": False, + "split": file_info.split, + } + else: + # File is not cached + file_status[file_path] = { + "cached": False, + "exists_on_hub": True, # Assumed + "split": file_info.split, + } + + cached_count = sum( + 1 for f in file_status.values() if f.get("cached", False) + ) + total_files = len(table_config.data_files) + + result["tables"][table] = { + "exists": True, + "files": file_status, + "total_files": total_files, + "cached_files": cached_count, + "is_partitioned": table_config.is_partitioned, + } + + self.logger.info( + f"Table {table}: {cached_count}/{total_files} files cached " + f"({cached_count/total_files*100:.1f}%)" + ) + + total_tables = len( + [t for t in result["tables"].values() if t.get("exists", False)] + ) + self.logger.info(f"Cache check complete: processed {total_tables} tables") + + return result + + def cleanup_cache( + self, + strategy: str = "auto", + max_age_days: int | None = None, + target_size: str | None = None, + keep_current_repo: bool = True, + dry_run: bool = True, + ) -> dict[str, Any]: """ - result = self.query(f"SELECT COUNT(*) as count FROM {table_name}", table_name) - return result.iloc[0]["count"] + Clean up the HuggingFace Hub cache using various strategies. + + :param strategy: Cleanup strategy - "auto", "age", "size", "unused" + :param max_age_days: Maximum age for cache entries (for "age" strategy) + :param target_size: Target cache size (for "size" strategy, e.g., "5GB") + :param keep_current_repo: Whether to preserve current repo from cleanup + :param dry_run: If True, show what would be deleted without executing + :return: Dictionary with cleanup results and summary + """ + if not self._cache_manager: + self.logger.warning("Cache management is disabled, cannot perform cleanup") + return { + "cache_management": "disabled", + "message": "Cache management is not enabled", + } - def get_columns(self, table_name: str) -> list[str]: + # Use instance defaults if not specified + max_age_days = max_age_days or getattr(self, "_cache_max_age_days", 30) + target_size = target_size or getattr(self, "_cache_max_size", "10GB") + + self.logger.info( + f"Starting cache cleanup: strategy={strategy}, max_age={max_age_days}d, " + f"target_size={target_size}, dry_run={dry_run}, keep_current_repo={keep_current_repo}" + ) + + result = { + "strategy": strategy, + "dry_run": dry_run, + "keep_current_repo": keep_current_repo, + "strategies_executed": [], + "total_freed_bytes": 0, + "total_freed_str": "0B", + } + + try: + if strategy == "auto": + # Multi-strategy automated cleanup + self.logger.info("Executing automated multi-strategy cleanup") + strategies = self._cache_manager.auto_clean_cache( + max_age_days=max_age_days, + max_total_size=target_size, + keep_latest_per_repo=2, + dry_run=dry_run, + ) + total_freed = sum(s.expected_freed_size for s in strategies) + result["strategies_executed"] = [ + { + "type": "auto_cleanup", + "freed_bytes": total_freed, + "freed_str": self._cache_manager._format_bytes(total_freed), + "details": f"Executed {len(strategies)} cleanup strategies", + } + ] + result["total_freed_bytes"] = total_freed + self.logger.info( + f"Auto cleanup {'would free' if dry_run else 'freed'} " + f"{self._cache_manager._format_bytes(total_freed)} using {len(strategies)} strategies" + ) + + elif strategy == "age": + # Age-based cleanup + self.logger.info( + f"Executing age-based cleanup (older than {max_age_days} days)" + ) + delete_strategy = self._cache_manager.clean_cache_by_age( + max_age_days=max_age_days, dry_run=dry_run + ) + result["strategies_executed"] = [ + { + "type": "age_based", + "freed_bytes": delete_strategy.expected_freed_size, + "freed_str": delete_strategy.expected_freed_size_str, + "max_age_days": max_age_days, + } + ] + result["total_freed_bytes"] = delete_strategy.expected_freed_size + self.logger.info( + f"Age-based cleanup {'would free' if dry_run else 'freed'} " + f"{delete_strategy.expected_freed_size_str}" + ) + + elif strategy == "size": + # Size-based cleanup + self.logger.info( + f"Executing size-based cleanup (target: {target_size})" + ) + delete_strategy = self._cache_manager.clean_cache_by_size( + target_size=target_size, strategy="oldest_first", dry_run=dry_run + ) + result["strategies_executed"] = [ + { + "type": "size_based", + "freed_bytes": delete_strategy.expected_freed_size, + "freed_str": delete_strategy.expected_freed_size_str, + "target_size": target_size, + } + ] + result["total_freed_bytes"] = delete_strategy.expected_freed_size + self.logger.info( + f"Size-based cleanup {'would free' if dry_run else 'freed'} " + f"{delete_strategy.expected_freed_size_str} to reach target {target_size}" + ) + + elif strategy == "unused": + # Clean unused revisions + self.logger.info( + "Executing unused revisions cleanup (keeping 2 latest per repo)" + ) + delete_strategy = self._cache_manager.clean_unused_revisions( + keep_latest=2, dry_run=dry_run + ) + result["strategies_executed"] = [ + { + "type": "unused_revisions", + "freed_bytes": delete_strategy.expected_freed_size, + "freed_str": delete_strategy.expected_freed_size_str, + "keep_latest": 2, + } + ] + result["total_freed_bytes"] = delete_strategy.expected_freed_size + self.logger.info( + f"Unused revisions cleanup {'would free' if dry_run else 'freed'} " + f"{delete_strategy.expected_freed_size_str}" + ) + + else: + self.logger.error(f"Unknown cleanup strategy: {strategy}") + return { + "error": f"Unknown cleanup strategy: {strategy}", + "available_strategies": ["auto", "age", "size", "unused"], + } + + result["total_freed_str"] = self._cache_manager._format_bytes( + result["total_freed_bytes"] + ) + + # Add current repo protection info + if keep_current_repo: + result["current_repo_protected"] = { + "repo_id": self.repo_id, + "message": "Current repository was protected from cleanup", + } + self.logger.debug( + f"Current repository {self.repo_id} protected from cleanup" + ) + + # Final summary logging + self.logger.info( + f"Cache cleanup completed: {strategy} strategy, " + f"{'would free' if dry_run else 'freed'} {result['total_freed_str']}" + ) + + return result + + except Exception as e: + self.logger.error(f"Cache cleanup failed: {e}") + return { + "error": f"Cache cleanup failed: {str(e)}", + "strategy": strategy, + "dry_run": dry_run, + } + + def auto_cleanup_cache_if_needed(self) -> dict[str, Any]: """ - Get column names for a table. + Automatically clean cache if configured policies are exceeded. - :param table_name: Name of the table - :return: List of column names + This method is called automatically during operations if auto_cleanup is enabled. + :return: Dictionary with cleanup results or None if no cleanup was needed """ - if table_name not in self._datasets: - raise ValueError(f"Table '{table_name}' not found") + if not self._cache_manager or not getattr(self, "_cache_auto_cleanup", False): + self.logger.debug("Auto-cleanup is disabled") + return {"auto_cleanup": "disabled"} - return list(self._datasets[table_name]["features"].keys()) + self.logger.debug("Checking if auto-cleanup is needed") - def _apply_table_filters(self, sql: str) -> str: + from huggingface_hub import scan_cache_dir + + try: + cache_info = scan_cache_dir() + except Exception as e: + self.logger.error(f"Failed to scan cache for auto-cleanup: {e}") + return {"auto_cleanup": "error", "error": str(e)} + max_size_bytes = self._cache_manager._parse_size_string( + getattr(self, "_cache_max_size", "10GB") + ) + + cleanup_needed = cache_info.size_on_disk > max_size_bytes + + if not cleanup_needed: + self.logger.debug( + f"Auto-cleanup not needed: {cache_info.size_on_disk_str} " + f"< {getattr(self, '_cache_max_size', '10GB')}" + ) + return { + "auto_cleanup": "enabled", + "cleanup_needed": False, + "current_size": cache_info.size_on_disk_str, + "max_size": getattr(self, "_cache_max_size", "10GB"), + } + + self.logger.info( + f"Auto-cleanup triggered: cache size ({cache_info.size_on_disk_str}) " + f"exceeds limit ({getattr(self, '_cache_max_size', '10GB')})" + ) + + cleanup_result = self.cleanup_cache( + strategy="auto", + dry_run=False, # Execute cleanup + keep_current_repo=True, + ) + + cleanup_result.update( + { + "auto_cleanup": "enabled", + "triggered": True, + "reason": "cache_size_exceeded", + "previous_size": cache_info.size_on_disk_str, + } + ) + + return cleanup_result + + def suggest_cache_cleanup(self) -> dict[str, Any]: + """ + Analyze cache and provide cleanup recommendations without executing. + + :return: Dictionary with analysis and recommendations """ - Apply table filters to a SQL query by modifying table references. + if not self._cache_manager: + return {"cache_management": "disabled"} + + from datetime import datetime, timedelta + + from huggingface_hub import scan_cache_dir + + cache_info = scan_cache_dir() + suggestions = { + "cache_analysis": { + "total_size": cache_info.size_on_disk_str, + "total_repos": len(cache_info.repos), + "recommendations": [], + }, + "cleanup_strategies": {}, + } + + # Analyze size + max_size_bytes = self._cache_manager._parse_size_string( + getattr(self, "_cache_max_size", "10GB") + ) + if cache_info.size_on_disk > max_size_bytes: + suggestions["cache_analysis"]["recommendations"].append( + { + "type": "size_exceeded", + "message": f"Cache size ({cache_info.size_on_disk_str}) exceeds " + f"configured limit ({getattr(self, '_cache_max_size', '10GB')})", + "suggested_action": "Run cleanup_cache(strategy='size')", + } + ) + + # Analyze age + cutoff_date = datetime.now() - timedelta( + days=getattr(self, "_cache_max_age_days", 30) + ) + old_repos = [] + for repo in cache_info.repos: + for revision in repo.revisions: + if datetime.fromtimestamp(revision.last_modified) < cutoff_date: + old_repos.append((repo.repo_id, revision.commit_hash[:8])) + + if old_repos: + suggestions["cache_analysis"]["recommendations"].append( + { + "type": "old_revisions", + "message": f"Found {len(old_repos)} old revisions " + f"(older than {getattr(self, '_cache_max_age_days', 30)} days)", + "suggested_action": "Run cleanup_cache(strategy='age')", + } + ) - :param sql: Original SQL query - :return: Modified SQL query with filters applied + # Analyze unused revisions + repos_with_multiple_revisions = [ + repo for repo in cache_info.repos if len(repo.revisions) > 2 + ] + if repos_with_multiple_revisions: + suggestions["cache_analysis"]["recommendations"].append( + { + "type": "multiple_revisions", + "message": f"Found {len(repos_with_multiple_revisions)} repos " + "with multiple cached revisions", + "suggested_action": "Run cleanup_cache(strategy='unused')", + } + ) + + # Dry run different strategies to show potential savings + try: + age_cleanup = self._cache_manager.clean_cache_by_age( + max_age_days=getattr(self, "_cache_max_age_days", 30), dry_run=True + ) + suggestions["cleanup_strategies"]["age_based"] = { + "description": f"Remove revisions older than {getattr(self, '_cache_max_age_days', 30)} days", + "potential_savings": age_cleanup.expected_freed_size_str, + "potential_savings_bytes": age_cleanup.expected_freed_size, + } + + size_cleanup = self._cache_manager.clean_cache_by_size( + target_size=getattr(self, "_cache_max_size", "10GB"), + strategy="oldest_first", + dry_run=True, + ) + suggestions["cleanup_strategies"]["size_based"] = { + "description": f"Reduce cache to {getattr(self, '_cache_max_size', '10GB')}", + "potential_savings": size_cleanup.expected_freed_size_str, + "potential_savings_bytes": size_cleanup.expected_freed_size, + } + + unused_cleanup = self._cache_manager.clean_unused_revisions( + keep_latest=2, dry_run=True + ) + suggestions["cleanup_strategies"]["unused_revisions"] = { + "description": "Remove unused revisions (keep 2 latest per repo)", + "potential_savings": unused_cleanup.expected_freed_size_str, + "potential_savings_bytes": unused_cleanup.expected_freed_size, + } + except Exception as e: + suggestions["error"] = f"Failed to analyze cleanup strategies: {e}" + + return suggestions + + def warm_cache( + self, + repo_ids: list[str] | None = None, + tables: list[str] | None = None, + include_current_repo: bool = True, + dry_run: bool = False, + ) -> dict[str, Any]: """ - if not self._table_filters: - return sql + Pre-download (warm) cache with specified repositories or tables. - modified_sql = sql + :param repo_ids: List of repository IDs to pre-download + :param tables: List of table names from current repo to pre-download + :param include_current_repo: Whether to include current repo if repo_ids specified + :param dry_run: If True, show what would be downloaded without executing + :return: Dictionary with warming results + """ + if not self._cache_manager: + return {"cache_management": "disabled"} + + result = { + "cache_warming": "enabled", + "dry_run": dry_run, + "operations": [], + "total_downloaded": 0, + "errors": [], + } + + # Handle table-specific warming for current repo + if tables: + repo_result = { + "repo_id": self.repo_id, + "type": "table_specific", + "tables": {}, + "success": True, + } - # Apply filters by replacing table references with filtered subqueries - for table_name, filter_condition in self._table_filters.items(): - import re + for table_name in tables: + try: + if not self.data_info.has_table(table_name): + repo_result["tables"][table_name] = { + "status": "error", + "message": "Table not found in dataset configuration", + } + continue + + if not dry_run: + # Download files for this table + self._ensure_dataset_loaded(table_name) + repo_result["tables"][table_name] = { + "status": "downloaded", + "message": "Table files cached successfully", + } + result["total_downloaded"] += 1 + else: + repo_result["tables"][table_name] = { + "status": "would_download", + "message": "Would download table files", + } + + except Exception as e: + error_msg = f"Failed to warm cache for table {table_name}: {e}" + repo_result["tables"][table_name] = { + "status": "error", + "message": str(e), + } + result["errors"].append(error_msg) + repo_result["success"] = False + + result["operations"].append(repo_result) + + # Handle repository-specific warming + if repo_ids or (not tables and include_current_repo): + target_repos = repo_ids or [] + if include_current_repo and self.repo_id not in target_repos: + target_repos.append(self.repo_id) + + for repo_id in target_repos: + repo_result = { + "repo_id": repo_id, + "type": "full_repo", + "success": True, + "message": "", + } + + try: + if not dry_run: + if repo_id == self.repo_id: + # Use current API instance for current repo + downloaded_path = self.download() + repo_result["message"] = ( + f"Repository cached at {downloaded_path}" + ) + else: + # Create temporary API instance for other repos + temp_api = HfQueryAPI( + repo_id=repo_id, + repo_type=self.repo_type, + token=self.token, + cache_dir=self.cache_dir, + enable_cache_management=False, # Avoid recursive cache management + ) + downloaded_path = temp_api.download() + repo_result["message"] = ( + f"Repository cached at {downloaded_path}" + ) + + result["total_downloaded"] += 1 + else: + repo_result["message"] = f"Would download repository {repo_id}" + + except Exception as e: + error_msg = f"Failed to warm cache for repo {repo_id}: {e}" + repo_result["success"] = False + repo_result["message"] = str(e) + result["errors"].append(error_msg) + + result["operations"].append(repo_result) + + if not repo_ids and not tables and not include_current_repo: + result["message"] = "No repositories or tables specified for cache warming" + + return result + + def verify_cache_integrity(self) -> dict[str, Any]: + """ + Verify integrity of cached files and detect corruption. - # Simple pattern to match table references in FROM and JOIN clauses - pattern = rf"\b{re.escape(table_name)}\b" - replacement = f"(SELECT * FROM {table_name} WHERE {filter_condition})" + :return: Dictionary with verification results + """ + if not self._cache_manager: + return {"cache_management": "disabled"} + + import os + + from huggingface_hub import scan_cache_dir + + cache_info = scan_cache_dir() + result = { + "cache_verification": "enabled", + "total_repos_scanned": len(cache_info.repos), + "issues_found": [], + "healthy_repos": [], + "summary": { + "healthy": 0, + "corrupted": 0, + "missing_files": 0, + "symlink_issues": 0, + }, + } + + for repo in cache_info.repos: + repo_issues = [] + + # Check if snapshots directory exists + if not repo.repo_path.exists(): + repo_issues.append( + { + "type": "missing_repo_directory", + "message": f"Repository directory does not exist: {repo.repo_path}", + } + ) + result["summary"]["corrupted"] += 1 + continue + + # Check each revision + for revision in repo.revisions: + if not revision.snapshot_path.exists(): + repo_issues.append( + { + "type": "missing_snapshot", + "revision": revision.commit_hash[:8], + "message": f"Snapshot directory missing: {revision.snapshot_path}", + } + ) + continue + + # Check each file in the revision + for file_info in revision.files: + if not file_info.file_path.exists(): + repo_issues.append( + { + "type": "missing_file", + "revision": revision.commit_hash[:8], + "file": file_info.file_name, + "message": f"File missing: {file_info.file_path}", + } + ) + continue + + # Check if it's a symlink and target exists + if file_info.file_path.is_symlink(): + if not file_info.blob_path.exists(): + repo_issues.append( + { + "type": "broken_symlink", + "revision": revision.commit_hash[:8], + "file": file_info.file_name, + "message": f"Symlink target missing: {file_info.blob_path}", + } + ) + continue + + # Basic file size check + try: + actual_size = os.path.getsize(file_info.file_path) + if actual_size != file_info.size_on_disk: + repo_issues.append( + { + "type": "size_mismatch", + "revision": revision.commit_hash[:8], + "file": file_info.file_name, + "expected_size": file_info.size_on_disk, + "actual_size": actual_size, + "message": f"File size mismatch in {file_info.file_name}", + } + ) + except (OSError, IOError) as e: + repo_issues.append( + { + "type": "access_error", + "revision": revision.commit_hash[:8], + "file": file_info.file_name, + "message": f"Cannot access file: {e}", + } + ) + + # Categorize the repo + if repo_issues: + result["issues_found"].append( + { + "repo_id": repo.repo_id, + "repo_type": repo.repo_type, + "issues": repo_issues, + "issues_count": len(repo_issues), + } + ) - # Only replace if we find the table name in the SQL - if re.search(pattern, modified_sql, re.IGNORECASE): - # Check if it's already wrapped in a filtered subquery to - # avoid double-wrapping - if not re.search( - rf"\(SELECT.*FROM\s+{re.escape(table_name)}\s+WHERE", - modified_sql, - re.IGNORECASE, + # Update summary + if any( + issue["type"] in ["missing_repo_directory", "missing_snapshot"] + for issue in repo_issues ): - modified_sql = re.sub( - pattern, replacement, modified_sql, flags=re.IGNORECASE - ) + result["summary"]["corrupted"] += 1 + elif any(issue["type"] == "missing_file" for issue in repo_issues): + result["summary"]["missing_files"] += 1 + elif any(issue["type"] == "broken_symlink" for issue in repo_issues): + result["summary"]["symlink_issues"] += 1 + else: + result["summary"]["corrupted"] += 1 + else: + result["healthy_repos"].append( + { + "repo_id": repo.repo_id, + "repo_type": repo.repo_type, + "size": repo.size_on_disk_str, + } + ) + result["summary"]["healthy"] += 1 + + # Overall health assessment + total_repos = len(cache_info.repos) + if total_repos > 0: + health_percentage = (result["summary"]["healthy"] / total_repos) * 100 + result["overall_health"] = { + "percentage": round(health_percentage, 1), + "status": ( + "healthy" + if health_percentage > 95 + else "warning" if health_percentage > 80 else "critical" + ), + "recommendation": self._get_health_recommendation(health_percentage), + } - return modified_sql + return result - def get_table_filter(self, table_name: str) -> str | None: + def _get_health_recommendation(self, health_percentage: float) -> str: + """Get recommendation based on cache health percentage.""" + if health_percentage > 95: + return "Cache is in excellent condition" + elif health_percentage > 80: + return "Cache has minor issues. Consider running cleanup to remove problematic entries" + else: + return "Cache has significant issues. Run cleanup_cache() or consider clearing the cache entirely" + + def migrate_cache(self, new_cache_dir: str | Path) -> dict[str, Any]: + """ + Migrate cache to a new directory location. + + :param new_cache_dir: Target directory for cache migration + :return: Dictionary with migration results + """ + if not self._cache_manager: + return {"cache_management": "disabled"} + + import shutil + from pathlib import Path + + new_cache_path = Path(new_cache_dir) + current_cache_path = self.cache_dir + + result = { + "cache_migration": "enabled", + "source": str(current_cache_path), + "destination": str(new_cache_path), + "success": False, + "files_migrated": 0, + "errors": [], + } + + try: + # Validate target directory + if new_cache_path.exists() and list(new_cache_path.iterdir()): + return { + **result, + "error": f"Target directory {new_cache_path} is not empty", + "suggestion": "Choose an empty directory or clear the target directory", + } + + # Create target directory if needed + new_cache_path.mkdir(parents=True, exist_ok=True) + + # Get current cache info + from huggingface_hub import scan_cache_dir + + cache_info = scan_cache_dir() + + if not cache_info.repos: + result.update( + { + "success": True, + "message": "No cached repositories to migrate", + } + ) + # Update cache directory + self.cache_dir = new_cache_path + return result + + # Migrate each repository + migrated_repos = [] + for repo in cache_info.repos: + try: + # Create repo structure in new location + repo_name = repo.repo_path.name + new_repo_path = new_cache_path / repo_name + + # Copy entire repository directory + shutil.copytree(repo.repo_path, new_repo_path, symlinks=True) + migrated_repos.append(repo.repo_id) + result["files_migrated"] += repo.nb_files + + except Exception as e: + error_msg = f"Failed to migrate repo {repo.repo_id}: {e}" + result["errors"].append(error_msg) + + if migrated_repos and not result["errors"]: + # Migration successful, update cache directory + self.cache_dir = new_cache_path + result.update( + { + "success": True, + "migrated_repos": migrated_repos, + "repos_count": len(migrated_repos), + "message": f"Successfully migrated {len(migrated_repos)} repositories", + } + ) + elif migrated_repos: + result.update( + { + "success": True, # Partial success + "migrated_repos": migrated_repos, + "repos_count": len(migrated_repos), + "message": f"Partially migrated {len(migrated_repos)} repositories with {len(result['errors'])} errors", + } + ) + else: + result.update( + { + "success": False, + "message": "Migration failed completely", + } + ) + + except Exception as e: + result.update( + { + "success": False, + "error": f"Migration failed: {str(e)}", + } + ) + + return result + + # ============== Cache Configuration Management ============== + + def configure_cache_policies( + self, + auto_cleanup: bool | None = None, + max_age_days: int | None = None, + max_size: str | None = None, + save_to_env: bool = False, + ) -> dict[str, Any]: + """ + Configure cache management policies for this instance. + + :param auto_cleanup: Enable/disable automatic cache cleanup + :param max_age_days: Maximum age in days for cache entries + :param max_size: Maximum total cache size (e.g., "10GB") + :param save_to_env: Save configuration to environment variables + :return: Dictionary with updated configuration """ - Get the current filter for a table. + if not self._cache_manager: + return {"cache_management": "disabled"} + + # Update instance configuration + if auto_cleanup is not None: + self._cache_auto_cleanup = auto_cleanup + if max_age_days is not None: + self._cache_max_age_days = max_age_days + if max_size is not None: + self._cache_max_size = max_size + + config = { + "cache_management": "enabled", + "configuration_updated": True, + "policies": { + "auto_cleanup": getattr(self, "_cache_auto_cleanup", False), + "max_age_days": getattr(self, "_cache_max_age_days", 30), + "max_size": getattr(self, "_cache_max_size", "10GB"), + }, + "env_variables_updated": False, + } + + # Save to environment variables if requested + if save_to_env: + import os + + env_vars = {} + + if auto_cleanup is not None: + env_var = "TFBPAPI_CACHE_AUTO_CLEANUP" + os.environ[env_var] = str(auto_cleanup).lower() + env_vars[env_var] = str(auto_cleanup).lower() + + if max_age_days is not None: + env_var = "TFBPAPI_CACHE_MAX_AGE_DAYS" + os.environ[env_var] = str(max_age_days) + env_vars[env_var] = str(max_age_days) + + if max_size is not None: + env_var = "TFBPAPI_CACHE_MAX_SIZE" + os.environ[env_var] = max_size + env_vars[env_var] = max_size + + config.update( + { + "env_variables_updated": True, + "env_variables": env_vars, + "note": "Environment variables set for current session. " + "Add them to your shell profile for persistence.", + } + ) - :param table_name: Name of the table - :return: Current filter SQL condition or None if no filter is set + return config + def get_cache_configuration(self) -> dict[str, Any]: """ - return self._table_filters.get(table_name) + Get current cache configuration including environment variables. - def set_table_filter(self, table_name: str, filter_condition: str) -> None: + :return: Dictionary with comprehensive cache configuration """ - Set a filter condition for a table that will be applied to all queries. + import os + + config = { + "cache_management": "enabled" if self._cache_manager else "disabled", + "cache_directory": str(self.cache_dir), + "instance_config": {}, + "environment_config": {}, + "effective_config": {}, + } + + if not self._cache_manager: + return config + + # Instance configuration + config["instance_config"] = { + "auto_cleanup": getattr(self, "_cache_auto_cleanup", False), + "max_age_days": getattr(self, "_cache_max_age_days", 30), + "max_size": getattr(self, "_cache_max_size", "10GB"), + } + + # Environment configuration + env_config = {} + env_vars = { + "TFBPAPI_CACHE_AUTO_CLEANUP": "auto_cleanup", + "TFBPAPI_CACHE_MAX_AGE_DAYS": "max_age_days", + "TFBPAPI_CACHE_MAX_SIZE": "max_size", + "HF_CACHE_DIR": "cache_directory", + "HF_HUB_CACHE": "cache_directory_fallback", + } + + for env_var, config_key in env_vars.items(): + value = os.getenv(env_var) + if value: + env_config[config_key] = value + + config["environment_config"] = env_config + + # Effective configuration (environment overrides instance) + effective = config["instance_config"].copy() + + # Apply environment overrides + if "auto_cleanup" in env_config: + effective["auto_cleanup"] = env_config["auto_cleanup"].lower() in ( + "true", + "1", + "yes", + ) + if "max_age_days" in env_config: + try: + effective["max_age_days"] = int(env_config["max_age_days"]) + except ValueError: + pass + if "max_size" in env_config: + effective["max_size"] = env_config["max_size"] + + config["effective_config"] = effective - :param table_name: Name of the table - :param filter_condition: SQL WHERE condition (without the WHERE keyword) + return config + def reset_cache_configuration( + self, remove_env_vars: bool = False + ) -> dict[str, Any]: """ - self._table_filters[table_name] = filter_condition - self.logger.debug(f"Set filter for table '{table_name}': {filter_condition}") + Reset cache configuration to defaults. - def remove_table_filter(self, table_name: str) -> None: + :param remove_env_vars: Also remove related environment variables + :return: Dictionary with reset results """ - Remove any filter condition for a table. + if not self._cache_manager: + return {"cache_management": "disabled"} + + # Reset instance configuration to defaults + self._cache_auto_cleanup = False + self._cache_max_age_days = 30 + self._cache_max_size = "10GB" + + result = { + "cache_management": "enabled", + "configuration_reset": True, + "new_config": { + "auto_cleanup": False, + "max_age_days": 30, + "max_size": "10GB", + }, + "env_variables_removed": [], + } + + # Remove environment variables if requested + if remove_env_vars: + import os + + env_vars_to_remove = [ + "TFBPAPI_CACHE_AUTO_CLEANUP", + "TFBPAPI_CACHE_MAX_AGE_DAYS", + "TFBPAPI_CACHE_MAX_SIZE", + ] + + for env_var in env_vars_to_remove: + if env_var in os.environ: + del os.environ[env_var] + result["env_variables_removed"].append(env_var) + + if result["env_variables_removed"]: + result["note"] = ( + "Environment variables removed from current session. " + "Remove them from your shell profile for permanent effect." + ) - :param table_name: Name of the table + return result + def apply_cache_policy_from_env(self) -> dict[str, Any]: """ - removed_filter = self._table_filters.pop(table_name, None) - if removed_filter: - self.logger.debug( - f"Removed filter for table '{table_name}': {removed_filter}" - ) + Apply cache policies from environment variables. + + :return: Dictionary with applied configuration + """ + if not self._cache_manager: + return {"cache_management": "disabled"} + + import os + + applied_config = [] + + # Auto cleanup + auto_cleanup_env = os.getenv("TFBPAPI_CACHE_AUTO_CLEANUP") + if auto_cleanup_env: + self._cache_auto_cleanup = auto_cleanup_env.lower() in ("true", "1", "yes") + applied_config.append(f"auto_cleanup: {self._cache_auto_cleanup}") + + # Max age days + max_age_env = os.getenv("TFBPAPI_CACHE_MAX_AGE_DAYS") + if max_age_env: + try: + self._cache_max_age_days = int(max_age_env) + applied_config.append(f"max_age_days: {self._cache_max_age_days}") + except ValueError: + applied_config.append( + f"max_age_days: invalid value '{max_age_env}' (keeping default)" + ) + + # Max size + max_size_env = os.getenv("TFBPAPI_CACHE_MAX_SIZE") + if max_size_env: + self._cache_max_size = max_size_env + applied_config.append(f"max_size: {self._cache_max_size}") + + return { + "cache_management": "enabled", + "environment_config_applied": True, + "applied_settings": applied_config, + "current_config": { + "auto_cleanup": getattr(self, "_cache_auto_cleanup", False), + "max_age_days": getattr(self, "_cache_max_age_days", 30), + "max_size": getattr(self, "_cache_max_size", "10GB"), + }, + } + + def export_cache_configuration(self, format: str = "env") -> dict[str, Any]: + """ + Export current cache configuration in various formats. + + :param format: Export format - "env", "json", "yaml" + :return: Dictionary with exported configuration + """ + if not self._cache_manager: + return {"cache_management": "disabled"} + + current_config = { + "auto_cleanup": getattr(self, "_cache_auto_cleanup", False), + "max_age_days": getattr(self, "_cache_max_age_days", 30), + "max_size": getattr(self, "_cache_max_size", "10GB"), + "cache_directory": str(self.cache_dir), + } + + result = { + "cache_management": "enabled", + "format": format, + "configuration": current_config, + } + + if format == "env": + env_lines = [ + f"export TFBPAPI_CACHE_AUTO_CLEANUP={str(current_config['auto_cleanup']).lower()}", + f"export TFBPAPI_CACHE_MAX_AGE_DAYS={current_config['max_age_days']}", + f"export TFBPAPI_CACHE_MAX_SIZE={current_config['max_size']}", + f"export HF_CACHE_DIR={current_config['cache_directory']}", + ] + result["exported_config"] = "\n".join(env_lines) + result["usage"] = "Add these lines to your ~/.bashrc or ~/.zshrc file" + + elif format == "json": + import json + + result["exported_config"] = json.dumps(current_config, indent=2) + + elif format == "yaml": + yaml_lines = [ + "cache_configuration:", + f" auto_cleanup: {str(current_config['auto_cleanup']).lower()}", + f" max_age_days: {current_config['max_age_days']}", + f" max_size: \"{current_config['max_size']}\"", + f" cache_directory: \"{current_config['cache_directory']}\"", + ] + result["exported_config"] = "\n".join(yaml_lines) + + else: + result["error"] = f"Unsupported format: {format}" + result["supported_formats"] = ["env", "json", "yaml"] + + return result def close(self) -> None: """Close the DuckDB connection.""" diff --git a/tfbpapi/ParamsDict.py b/tfbpapi/ParamsDict.py deleted file mode 100644 index 19f7470..0000000 --- a/tfbpapi/ParamsDict.py +++ /dev/null @@ -1,156 +0,0 @@ -from typing import Any, Union - - -class ParamsDict(dict): - """ - A dictionary subclass that ensures all keys are strings and supports multiple key- - value assignments at once, with validation against a list of valid keys. - - This class is designed to be used for passing parameters to HTTP requests and - extends the base dictionary class, ensuring that insertion order is preserved. - - """ - - def __init__(self, params: dict[str, Any] = {}, valid_keys: list[str] = []) -> None: - """ - Initialize the ParamsDict with optional initial parameters and valid keys. - - :param params: A dictionary of initial parameters. All keys must be strings. - :type params: dict, optional - :param valid_keys: A list of valid keys for validation. - :type valid_keys: list of str, optional - :raises ValueError: If `params` is not a dictionary or if any of the keys - are not strings. - - """ - params = params or {} - valid_keys = valid_keys or [] - if not isinstance(params, dict): - raise ValueError("params must be a dictionary") - if len(params) > 0 and not all(isinstance(k, str) for k in params.keys()): - raise ValueError("params must be a dictionary with string keys") - super().__init__(params) - self._valid_keys = valid_keys - - def __setitem__(self, key: str | list[str], value: Any | list[Any]) -> None: - """ - Set a parameter value or multiple parameter values. - - :param key: The parameter key or a list of parameter keys. - :type key: str or list of str - :param value: The parameter value or a list of parameter values. - :type value: any or list of any - :raises ValueError: If the length of `key` and `value` lists do not match. - :raises KeyError: If `key` is not a string or a list of strings. - - """ - if isinstance(key, str): - self._validate_key(key) - super().__setitem__(key, value) - elif isinstance(key, list) and isinstance(value, list): - if len(key) != len(value): - raise ValueError("Length of keys and values must match") - for k, v in zip(key, value): - if not isinstance(k, str): - raise KeyError("All keys must be strings") - self._validate_key(k) - super().__setitem__(k, v) - else: - raise KeyError("Key must be a string or list of strings") - - def __getitem__(self, key: str | list[str]) -> Union[Any, "ParamsDict"]: - """ - Get a parameter value or a new ParamsDict with specified keys. - - :param key: The parameter key or a list of parameter keys. - :type key: str or list of str - :return: The parameter value or a new ParamsDict with the specified keys. - :rtype: any or ParamsDict - :raises KeyError: If `key` is not a string or a list of strings. - - """ - if isinstance(key, str): - return super().__getitem__(key) - elif isinstance(key, list): - return ParamsDict({k: dict.__getitem__(self, k) for k in key if k in self}) - else: - raise KeyError("Key must be a string or list of strings") - - def __delitem__(self, key: str) -> None: - """ - Delete a parameter by key. - - :param key: The parameter key. - :type key: str - :raises KeyError: If `key` is not a string. - - """ - if isinstance(key, str): - super().__delitem__(key) - else: - raise KeyError("Key must be a string") - - def __repr__(self) -> str: - """ - Return a string representation of the ParamsDict. - - :return: A string representation of the ParamsDict. - :rtype: str - - """ - return f"ParamsDict({super().__repr__()})" - - def __str__(self) -> str: - """ - Return a human-readable string representation of the ParamsDict. - - :return: A human-readable string representation of the ParamsDict. - :rtype: str - - """ - return ", ".join(f"{k}: {v}" for k, v in self.items()) - - def update(self, *args, **kwargs) -> None: - """Update the ParamsDict with the key/value pairs from other, overwriting - existing keys.""" - if args: - other = args[0] - if isinstance(other, dict): - [self._validate_key(k) for k in other.keys()] - for key, value in other.items(): - self.__setitem__(key, value) - else: - [self._validate_key(k) for k, _ in other] - for key, value in other: - self.__setitem__(key, value) - [self._validate_key(k) for k in kwargs.keys()] - for key, value in kwargs.items(): - self.__setitem__(key, value) - - def as_dict(self) -> dict: - """ - Convert the ParamsDict to a standard dictionary. - - :return: A standard dictionary with the same items as the ParamsDict. - :rtype: dict - - """ - return dict(self) - - def _validate_key(self, key: str) -> bool: - """Validate that the key is in the list of valid keys.""" - if self._valid_keys and key not in self._valid_keys: - raise KeyError(f"Invalid parameter key provided: {key}") - return True - - @property - def valid_keys(self) -> list[str]: - """Get the list of valid keys.""" - return self._valid_keys - - @valid_keys.setter - def valid_keys(self, keys: list[str]) -> None: - """Set the list of valid keys.""" - if not all(isinstance(k, str) for k in keys): - raise ValueError("valid_keys must be a list of strings") - self._valid_keys = keys diff --git a/tfbpapi/PromoterSetAPI.py b/tfbpapi/PromoterSetAPI.py deleted file mode 100644 index f747497..0000000 --- a/tfbpapi/PromoterSetAPI.py +++ /dev/null @@ -1,46 +0,0 @@ -import os -from typing import Any - -import pandas as pd - -from tfbpapi.AbstractRecordsAndFilesAPI import ( - AbstractRecordsAndFilesAPI, -) - - -class PromoterSetAPI(AbstractRecordsAndFilesAPI): - """Class to interact with the PromoterSetAPI endpoint.""" - - def __init__(self, **kwargs) -> None: - """ - Initialize the PromoterSetAPI object. - - :param kwargs: parameters to pass through AbstractRecordsAndFilesAPI to - AbstractAPI. - - """ - valid_param_keys = kwargs.pop( - "valid_param_keys", - ["id", "name"], - ) - - url = kwargs.pop("url", os.getenv("PROMOTERSET_URL", None)) - - super().__init__(url=url, valid_keys=valid_param_keys, **kwargs) - - def create(self, data: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The PromoterSetAPI does not support create.") - - def update(self, df: pd.DataFrame, **kwargs) -> Any: - raise NotImplementedError("The PromoterSetAPI does not support update.") - - def delete(self, id: str, **kwargs) -> Any: - raise NotImplementedError("The PromoterSetAPI does not support delete.") - - def submit(self, post_dict: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The PromoterSetAPI does not support submit.") - - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Any: - raise NotImplementedError("The PromoterSetAPI does not support retrieve.") diff --git a/tfbpapi/PromoterSetSigAPI.py b/tfbpapi/PromoterSetSigAPI.py deleted file mode 100644 index e75609e..0000000 --- a/tfbpapi/PromoterSetSigAPI.py +++ /dev/null @@ -1,68 +0,0 @@ -import os -from typing import Any - -import pandas as pd - -from tfbpapi.AbstractRecordsAndFilesAPI import ( - AbstractRecordsAndFilesAPI, -) - - -class PromoterSetSigAPI(AbstractRecordsAndFilesAPI): - """Class to interact with the PromoterSetSigAPI endpoint.""" - - def __init__(self, **kwargs) -> None: - """ - Initialize the PromoterSetSigAPI object. - - :param kwargs: parameters to pass through AbstractRecordsAndFilesAPI to - AbstractAPI. - - """ - valid_param_keys = kwargs.pop( - "valid_param_keys", - [ - "id", - "single_binding", - "composite_binding", - "promoter", - "promoter_name", - "background", - "background_name", - "regulator_locus_tag", - "regulator_symbol", - "batch", - "replicate", - "source", - "source_name", - "lab", - "assay", - "workflow", - "data_usable", - "aggregated", - "condition", - "deduplicate", - "preferred_replicate", - ], - ) - - url = kwargs.pop("url", os.getenv("PROMOTERSETSIG_URL", None)) - - super().__init__(url=url, valid_keys=valid_param_keys, **kwargs) - - def create(self, data: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The PromoterSetSigAPI does not support create.") - - def update(self, df: pd.DataFrame, **kwargs) -> Any: - raise NotImplementedError("The PromoterSetSigAPI does not support update.") - - def delete(self, id: str, **kwargs) -> Any: - raise NotImplementedError("The PromoterSetSigAPI does not support delete.") - - def submit(self, post_dict: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The PromoterSetSigAPI does not support submit.") - - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Any: - raise NotImplementedError("The PromoterSetSigAPI does not support retrieve.") diff --git a/tfbpapi/RankResponseAPI.py b/tfbpapi/RankResponseAPI.py deleted file mode 100644 index e12e0f2..0000000 --- a/tfbpapi/RankResponseAPI.py +++ /dev/null @@ -1,286 +0,0 @@ -import asyncio -import json -import os -import tarfile -import tempfile -import time -from typing import Any - -import aiohttp -import pandas as pd -from requests import Response, delete, post -from requests_toolbelt import MultipartEncoder - -from tfbpapi.AbstractRecordsAndFilesAPI import ( - AbstractRecordsAndFilesAPI, -) - - -class RankResponseAPI(AbstractRecordsAndFilesAPI): - """ - A class to interact with the Rank Response API. - - Retrieves rank response data from the database. - - """ - - def __init__(self, **kwargs) -> None: - """ - Initialize the RankResponseAPI object. This will serve as an interface to the - RankResponse endpoint of both the database and the application cache. - - :param url: The URL of the Rank Response API - :param kwargs: Additional parameters to pass to AbstractAPI. - - """ - super().__init__( - url=kwargs.pop("url", os.getenv("RANKRESPONSE_URL", "")), - **kwargs, - ) - - async def submit( - self, - post_dict: dict[str, Any], - **kwargs, - ) -> Any: - # make a post request with the post_dict to rankresponse_url - rankresponse_url = f"{self.url.rstrip('/')}/submit/" - self.logger.debug("rankresponse_url: %s", rankresponse_url) - - async with aiohttp.ClientSession() as session: - async with session.post( - rankresponse_url, headers=self.header, json=post_dict - ) as response: - response.raise_for_status() - result = await response.json() - try: - return result["group_task_id"] - except KeyError: - self.logger.error( - "Expected 'group_task_id' in response: %s", json.dumps(result) - ) - raise - - async def retrieve( - self, - group_task_id: str, - timeout: int = 300, - polling_interval: int = 2, - **kwargs, - ) -> dict[str, pd.DataFrame]: - """ - Periodically check the task status and retrieve the result when the task - completes. - - :param group_task_id: The task ID to retrieve results for. - :param timeout: The maximum time to wait for the task to complete (in seconds). - :param polling_interval: The time to wait between status checks (in seconds). - :return: Extracted files from the result tarball. - - """ - # Start time for timeout check - start_time = time.time() - - # Task status URL - status_url = f"{self.url.rstrip('/')}/status/" - - while True: - async with aiohttp.ClientSession() as session: - # Send a GET request to check the task status - async with session.get( - status_url, - headers=self.header, - params={"group_task_id": group_task_id}, - ) as response: - response.raise_for_status() # Raise an error for bad status codes - status_response = await response.json() - - # Check if the task is complete - if status_response.get("status") == "SUCCESS": - # Fetch and return the tarball - return await self._download_result(group_task_id) - elif status_response.get("status") == "FAILURE": - raise Exception( - f"Task {group_task_id} failed: {status_response}" - ) - - # Check if we have reached the timeout - elapsed_time = time.time() - start_time - if elapsed_time > timeout: - raise TimeoutError( - f"Task {group_task_id} did not " - "complete within {timeout} seconds." - ) - - # Wait for the specified polling interval before checking again - await asyncio.sleep(polling_interval) - - async def _download_result(self, group_task_id: str) -> Any: - """ - Download the result tarball after the task is successful. - - :param group_task_id: The group_task_id to download the results for. - :return: Extracted metadata and data from the tarball. - - """ - download_url = f"{self.url.rstrip('/')}/retrieve_task/" - - async with aiohttp.ClientSession() as session: - async with session.get( - download_url, - headers=self.header, - params={"group_task_id": group_task_id}, - ) as response: - response.raise_for_status() # Ensure request was successful - tar_data = await response.read() - - # Save tarball to a temporary file or return raw tar content - with tempfile.NamedTemporaryFile( - delete=False, suffix=".tar.gz" - ) as temp_file: - temp_file.write(tar_data) - temp_file.flush() - temp_file.seek(0) - - # Extract and return the content of the tarball - return self._extract_files(temp_file.name) - - def _extract_files(self, tar_path: str) -> dict[str, pd.DataFrame]: - """ - Extract metadata and associated files from a tarball. - - :param tar_path: The path to the tarball file. - :return: A tuple of metadata DataFrame and a dictionary of DataFrames for each - file. - - """ - with tarfile.open(tar_path, mode="r:gz") as tar: - tar_members = tar.getmembers() - - # Extract metadata.json - metadata_member = next( - (m for m in tar_members if m.name == "metadata.json"), None - ) - if metadata_member is None: - raise FileNotFoundError("metadata.json not found in tar archive") - - extracted_file = tar.extractfile(metadata_member) - if extracted_file is None: - raise FileNotFoundError("Failed to extract metadata.json") - - with extracted_file as f: - metadata_dict = json.load(f) - - metadata_df = pd.DataFrame(metadata_dict.values()) - metadata_df["id"] = metadata_dict.keys() - - # Extract CSV files - data = {} - for rr_id in metadata_df["id"]: - csv_filename = f"{rr_id}.csv.gz" - member = next((m for m in tar_members if m.name == csv_filename), None) - if member is None: - raise FileNotFoundError(f"{csv_filename} not found in tar archive") - - extracted_file = tar.extractfile(member) - if extracted_file is None: - raise FileNotFoundError(f"Failed to extract {csv_filename}") - - with extracted_file as f: - data[rr_id] = pd.read_csv(f, compression="gzip") - return {"metadata": metadata_df, "data": data} - - def create(self, data: dict[str, Any], **kwargs) -> Response: - """ - Create a new RankResponse record by uploading a gzipped CSV file. - - :param data: This should be the fields in the RankREsponse model, eg - "promotersetsig_id", "expression_id" and "parameters". - :param kwargs: Additional parameters to pass to the post. This must include a - DataFrame to upload as a CSV file with the keyword `df`, eg `df=my_df`. - - :return: The result of the post request. - - :raises ValueError: If a DataFrame is not provided in the keyword arguments. - :raises TypeError: If the DataFrame provided is not a pandas DataFrame. - - """ - # ensure that the url ends in a slash - rankresponse_url = f"{self.url.rstrip('/')}/" - df = kwargs.pop("df", None) - - if df is None: - raise ValueError( - "A DataFrame must be provided to create " - "a RankResponse via keyword `df`" - ) - if not isinstance(df, pd.DataFrame): - raise TypeError( - f"Expected a DataFrame for keyword `df`, got {type(df).__name__}" - ) - - # Create a temporary gzipped CSV file from the DataFrame - with tempfile.NamedTemporaryFile(suffix=".csv.gz") as temp_file: - df.to_csv(temp_file.name, compression="gzip", index=False) - - # Prepare the file and metadata for upload - with open(temp_file.name, "rb") as file: - multipart_data = MultipartEncoder( - fields={**data, "file": (temp_file.name, file, "application/gzip")} - ) - headers = {**self.header, "Content-Type": multipart_data.content_type} - - # Send the POST request with custom encoded multipart data - response = post(rankresponse_url, headers=headers, data=multipart_data) - - response.raise_for_status() - return response - - def update(self, df: pd.DataFrame, **kwargs) -> Any: - raise NotImplementedError("The RankResponseAPI does not support update.") - - def delete(self, id: str = "", **kwargs) -> Any: - """ - Delete one or more records from the database. - - :param id: The ID of the record to delete. However, you can also pass in - `ids` as a list of IDs to delete multiple records. This is why `id` is optional. - If neither `id` nor `ids` is provided, a ValueError is raised. - - :return: A dictionary with a status message indicating success or failure. - - :raises ValueError: If neither `id` nor `ids` is provided. - - """ - # Include the Authorization header with the token - headers = kwargs.get("headers", {}) - headers["Authorization"] = f"Token {self.token}" - - ids = kwargs.pop("ids", str(id)) - - # Determine if it's a single ID or multiple - if isinstance(ids, str) and str != "": - # Single ID deletion for backward compatibility - response = delete(f"{self.url}/{ids}/", headers=headers, **kwargs) - elif isinstance(ids, list) and ids: - # Bulk delete with a list of IDs - response = delete( - f"{self.url}/delete/", - headers=headers, - json={"ids": ids}, # Send the list of IDs in the request body - **kwargs, - ) - else: - raise ValueError( - "No ID(s) provided for deletion. Either pass a single ID with " - "`id` or a list of IDs with `ids = [1,2, ...]" - ) - - if response.status_code in [200, 204]: - return { - "status": "success", - "message": "RankResponse(s) deleted successfully.", - } - - # Raise an error if the response indicates failure - response.raise_for_status() diff --git a/tfbpapi/RegulatorAPI.py b/tfbpapi/RegulatorAPI.py deleted file mode 100644 index 675c002..0000000 --- a/tfbpapi/RegulatorAPI.py +++ /dev/null @@ -1,53 +0,0 @@ -import os -from typing import Any - -import pandas as pd - -from tfbpapi.AbstractRecordsOnlyAPI import AbstractRecordsOnlyAPI - - -class RegulatorAPI(AbstractRecordsOnlyAPI): - """A class to interact with the RegulatorAPI endpoint.""" - - def __init__(self, **kwargs): - """ - Initialize the RegulatorAPI object. - - :param kwargs: parameters to pass to AbstractAPI via AbstractRecordsOnlyAPI. - - """ - valid_param_keys = kwargs.pop( - "valid_param_keys", - [ - "id", - "regulator_locus_tag", - "regulator_symbol", - "under_development", - ], - ) - - url = kwargs.pop("url", os.getenv("REGULATOR_URL", None)) - if not url: - raise AttributeError( - "url must be provided or the environmental variable ", - "`REGULATOR_URL` must be set", - ) - - super().__init__(url=url, valid_keys=valid_param_keys, **kwargs) - - def create(self, data: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The RegulatorAPI does not support create.") - - def update(self, df: pd.DataFrame, **kwargs) -> Any: - raise NotImplementedError("The RegulatorAPI does not support update.") - - def delete(self, id: str, **kwargs) -> Any: - raise NotImplementedError("The RegulatorAPI does not support delete.") - - def submit(self, post_dict: dict[str, Any], **kwargs) -> Any: - raise NotImplementedError("The RegulatorAPI does not support submit.") - - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Any: - raise NotImplementedError("The RegulatorAPI does not support retrieve.") diff --git a/tfbpapi/UnivariateModelsAPI.py b/tfbpapi/UnivariateModelsAPI.py deleted file mode 100644 index 57cc303..0000000 --- a/tfbpapi/UnivariateModelsAPI.py +++ /dev/null @@ -1,202 +0,0 @@ -import asyncio -import json -import os -import time -from typing import Any - -import aiohttp -import pandas as pd -import requests - -from tfbpapi.AbstractRecordsOnlyAPI import AbstractRecordsOnlyAPI - - -class UnivariateModelsAPI(AbstractRecordsOnlyAPI): - """ - A class to interact with the UnivariateModels API. - - Retrieves univariatemodels data from the database. - - """ - - def __init__(self, **kwargs) -> None: - """ - Initialize the UnivariateModels object. This will serve as an interface to the - UnivariateModels endpoint of both the database and the application cache. - - :param url: The URL of the UnivariateModels API - :param kwargs: Additional parameters to pass to AbstractAPI. - - """ - - self.bulk_update_url_suffix = kwargs.pop( - "bulk_update_url_suffix", "bulk-update" - ) - - super().__init__( - url=kwargs.pop("url", os.getenv("UNIVARIATEMODELS_URL", "")), - **kwargs, - ) - - async def submit( - self, - post_dict: dict[str, Any], - **kwargs, - ) -> Any: - """ - Submit a UnivariateModels task to the UnivariateModels API. - - :param post_dict: The dictionary to submit to the UnivariateModels API. The - typing needs to be adjusted -- it can take a list of dictionaries to submit - a batch. - :return: The group_task_id of the submitted task. - - """ - # make a post request with the post_dict to univariatemodels_url - univariatemodels_url = f"{self.url.rstrip('/')}/submit/" - self.logger.debug("univariatemodels_url: %s", univariatemodels_url) - - async with aiohttp.ClientSession() as session: - async with session.post( - univariatemodels_url, headers=self.header, json=post_dict - ) as response: - try: - response.raise_for_status() - except aiohttp.ClientResponseError as e: - self.logger.error( - "Failed to submit UnivariateModels task: Status %s, Reason %s", - e.status, - e.message, - ) - raise - result = await response.json() - try: - return result["group_task_id"] - except KeyError: - self.logger.error( - "Expected 'group_task_id' in response: %s", json.dumps(result) - ) - raise - - async def retrieve( - self, - group_task_id: str, - timeout: int = 300, - polling_interval: int = 2, - **kwargs, - ) -> dict[str, pd.DataFrame]: - """ - Periodically check the task status and retrieve the result when the task - completes. - - :param group_task_id: The task ID to retrieve results for. - :param timeout: The maximum time to wait for the task to complete (in seconds). - :param polling_interval: The time to wait between status checks (in seconds). - :return: Records from the UnivariateModels API of the successfully completed - task. - - """ - # Start time for timeout check - start_time = time.time() - - # Task status URL - status_url = f"{self.url.rstrip('/')}/status/" - - while True: - async with aiohttp.ClientSession() as session: - # Send a GET request to check the task status - async with session.get( - status_url, - headers=self.header, - params={"group_task_id": group_task_id}, - ) as response: - response.raise_for_status() # Raise an error for bad status codes - status_response = await response.json() - - # Check if the task is complete - if status_response.get("status") == "SUCCESS": - - if error_tasks := status_response.get("error_tasks"): - self.logger.error( - f"Tasks {group_task_id} failed: {error_tasks}" - ) - if success_tasks := status_response.get("success_pks"): - params = {"id": ",".join(str(pk) for pk in success_tasks)} - return await self.read(params=params) - elif status_response.get("status") == "FAILURE": - raise Exception( - f"Task {group_task_id} failed: {status_response}" - ) - - # Check if we have reached the timeout - elapsed_time = time.time() - start_time - if elapsed_time > timeout: - raise TimeoutError( - f"Task {group_task_id} did not " - "complete within {timeout} seconds." - ) - - # Wait for the specified polling interval before checking again - await asyncio.sleep(polling_interval) - - def create(self, data: dict[str, Any], **kwargs) -> requests.Response: - raise NotImplementedError("The UnivariateModels does not support create.") - - def update(self, df: pd.DataFrame, **kwargs: Any) -> requests.Response: - """ - Update the records in the database. - - :param df: The DataFrame containing the records to update. - :type df: pd.DataFrame - :param kwargs: Additional fields to include in the payload. - :type kwargs: Any - :return: The response from the POST request. - :rtype: requests.Response - :raises requests.RequestException: If the request fails. - - """ - bulk_update_url = ( - f"{self.url.rstrip('/')}/{self.bulk_update_url_suffix.rstrip('/')}/" - ) - - self.logger.debug("bulk_update_url: %s", bulk_update_url) - - # Include additional fields in the payload if provided - payload = {"data": df.to_dict(orient="records")} - payload.update(kwargs) - - try: - response = requests.post( - bulk_update_url, - headers=self.header, - json=payload, - ) - response.raise_for_status() - return response - except requests.RequestException as e: - self.logger.error(f"Error in POST request: {e}") - raise - - def delete(self, id: str, **kwargs) -> Any: - """ - Delete a UnivariateModels record from the database. - - :param id: The ID of the UnivariateModels record to delete. - :return: A dictionary with a status message indicating success or failure. - - """ - # Include the Authorization header with the token - headers = kwargs.get("headers", {}) - headers["Authorization"] = f"Token {self.token}" - - # Make the DELETE request with the updated headers - response = requests.delete(f"{self.url}/{id}/", headers=headers, **kwargs) - - if response.status_code == 204: - return { - "status": "success", - "message": "UnivariateModels deleted successfully.", - } - - # Raise an error if the response indicates failure - response.raise_for_status() diff --git a/tfbpapi/__init__.py b/tfbpapi/__init__.py index 8c0f3be..e69de29 100644 --- a/tfbpapi/__init__.py +++ b/tfbpapi/__init__.py @@ -1,39 +0,0 @@ -from .BindingAPI import BindingAPI -from .BindingConcatenatedAPI import BindingConcatenatedAPI -from .BindingManualQCAPI import BindingManualQCAPI -from .CallingCardsBackgroundAPI import CallingCardsBackgroundAPI -from .DataSourceAPI import DataSourceAPI -from .DtoAPI import DtoAPI -from .ExpressionAPI import ExpressionAPI -from .ExpressionManualQCAPI import ExpressionManualQCAPI -from .FileFormatAPI import FileFormatAPI -from .GenomicFeatureAPI import GenomicFeatureAPI -from .metric_arrays import metric_arrays -from .PromoterSetAPI import PromoterSetAPI -from .PromoterSetSigAPI import PromoterSetSigAPI -from .rank_transforms import shifted_negative_log_ranks, stable_rank, transform -from .RankResponseAPI import RankResponseAPI -from .RegulatorAPI import RegulatorAPI -from .UnivariateModelsAPI import UnivariateModelsAPI - -__all__ = [ - "BindingAPI", - "BindingConcatenatedAPI", - "BindingManualQCAPI", - "CallingCardsBackgroundAPI", - "DataSourceAPI", - "DtoAPI", - "ExpressionAPI", - "ExpressionManualQCAPI", - "FileFormatAPI", - "GenomicFeatureAPI", - "metric_arrays", - "transform", - "PromoterSetAPI", - "PromoterSetSigAPI", - "RankResponseAPI", - "RegulatorAPI", - "stable_rank", - "shifted_negative_log_ranks", - "UnivariateModelsAPI", -] diff --git a/tfbpapi/datainfo/__init__.py b/tfbpapi/datainfo/__init__.py new file mode 100644 index 0000000..88452eb --- /dev/null +++ b/tfbpapi/datainfo/__init__.py @@ -0,0 +1,23 @@ +from .datacard import DataCard +from .fetchers import HfDataCardFetcher, HfRepoStructureFetcher, HfSizeInfoFetcher +from .models import ( + DatasetCard, + DatasetConfig, + DatasetType, + ExtractedMetadata, + FeatureInfo, + MetadataRelationship, +) + +__all__ = [ + "DataCard", + "HfDataCardFetcher", + "HfRepoStructureFetcher", + "HfSizeInfoFetcher", + "DatasetCard", + "DatasetConfig", + "DatasetType", + "ExtractedMetadata", + "FeatureInfo", + "MetadataRelationship", +] diff --git a/tfbpapi/datainfo/datacard.py b/tfbpapi/datainfo/datacard.py new file mode 100644 index 0000000..e59fc4c --- /dev/null +++ b/tfbpapi/datainfo/datacard.py @@ -0,0 +1,339 @@ +"""DataCard class for easy exploration of HuggingFace dataset metadata.""" + +import logging +from typing import Any, Dict, List, Optional, Set, Union + +from pydantic import ValidationError + +from ..errors import ( + DataCardError, + DataCardValidationError, + HfDataFetchError, +) +from .fetchers import HfDataCardFetcher, HfRepoStructureFetcher, HfSizeInfoFetcher +from .models import ( + DatasetCard, + DatasetConfig, + DatasetType, + ExtractedMetadata, + MetadataRelationship, +) + + +class DataCard: + """Easy-to-use interface for exploring HuggingFace dataset metadata. + + Provides methods to discover and explore dataset contents, configurations, + and metadata without loading the actual genomic data. + """ + + def __init__(self, repo_id: str, token: Optional[str] = None): + """Initialize DataCard for a repository. + + :param repo_id: HuggingFace repository identifier (e.g., "user/dataset") + :param token: Optional HuggingFace token for authentication + """ + self.repo_id = repo_id + self.token = token + self.logger = logging.getLogger(self.__class__.__name__) + + # Initialize fetchers + self._card_fetcher = HfDataCardFetcher(token=token) + self._structure_fetcher = HfRepoStructureFetcher(token=token) + self._size_fetcher = HfSizeInfoFetcher(token=token) + + # Cache for parsed card + self._dataset_card: Optional[DatasetCard] = None + self._metadata_cache: Dict[str, List[ExtractedMetadata]] = {} + + @property + def dataset_card(self) -> DatasetCard: + """Get the validated dataset card.""" + if self._dataset_card is None: + self._load_and_validate_card() + # this is here for type checking purposes. _load_and_validate_card() + # will either set the _dataset_card or raise an error + assert self._dataset_card is not None + return self._dataset_card + + def _load_and_validate_card(self) -> None: + """Load and validate the dataset card from HuggingFace.""" + try: + self.logger.debug(f"Loading dataset card for {self.repo_id}") + card_data = self._card_fetcher.fetch(self.repo_id) + + if not card_data: + raise DataCardValidationError( + f"No dataset card found for {self.repo_id}" + ) + + # Validate using Pydantic model + self._dataset_card = DatasetCard(**card_data) + self.logger.debug(f"Successfully validated dataset card for {self.repo_id}") + + except ValidationError as e: + # Create a more user-friendly error message + error_details = [] + for error in e.errors(): + field_path = " -> ".join(str(x) for x in error['loc']) + error_type = error['type'] + error_msg = error['msg'] + input_value = error.get('input', 'N/A') + + if 'dtype' in field_path and error_type == 'string_type': + error_details.append( + f"Field '{field_path}': Expected a simple data type string (like 'string', 'int64', 'float64') " + f"but got a complex structure. This might be a categorical field with class labels. " + f"Actual value: {input_value}" + ) + else: + error_details.append(f"Field '{field_path}': {error_msg} (got: {input_value})") + + detailed_msg = f"Dataset card validation failed for {self.repo_id}:\n" + "\n".join(f" - {detail}" for detail in error_details) + self.logger.error(detailed_msg) + raise DataCardValidationError(detailed_msg) from e + except HfDataFetchError as e: + raise DataCardError(f"Failed to fetch dataset card: {e}") from e + + @property + def configs(self) -> List[DatasetConfig]: + """Get all dataset configurations.""" + return self.dataset_card.configs + + def get_config(self, config_name: str) -> Optional[DatasetConfig]: + """Get a specific configuration by name.""" + return self.dataset_card.get_config_by_name(config_name) + + def get_configs_by_type( + self, dataset_type: Union[DatasetType, str] + ) -> List[DatasetConfig]: + """Get configurations by dataset type.""" + if isinstance(dataset_type, str): + dataset_type = DatasetType(dataset_type) + return self.dataset_card.get_configs_by_type(dataset_type) + + def get_regulators(self, config_name: Optional[str] = None) -> Set[str]: + """Get all regulators mentioned in the dataset. + + :param config_name: Optional specific config to search, otherwise searches all + :return: Set of regulator identifiers found + """ + raise NotImplementedError("Method not yet implemented") + + def get_experimental_conditions( + self, config_name: Optional[str] = None + ) -> Set[str]: + """Get all experimental conditions mentioned in the dataset. + + :param config_name: Optional specific config to search, otherwise searches all + :return: Set of experimental conditions found + """ + raise NotImplementedError("Method not yet implemented") + + def get_field_values(self, config_name: str, field_name: str) -> Set[str]: + """Get all unique values for a specific field in a configuration. + + :param config_name: Configuration name + :param field_name: Field name to extract values from + :return: Set of unique values + :raises DataCardError: If config or field not found + """ + config = self.get_config(config_name) + if not config: + raise DataCardError(f"Configuration '{config_name}' not found") + + # Check if field exists in the config + field_names = [f.name for f in config.dataset_info.features] + if field_name not in field_names: + raise DataCardError( + f"Field '{field_name}' not found in config '{config_name}'" + ) + + return self._extract_field_values(config, field_name) + + def _extract_field_values(self, config: DatasetConfig, field_name: str) -> Set[str]: + """Extract unique values for a field from various sources.""" + values = set() + + # Check cache first + cache_key = f"{config.config_name}:{field_name}" + if cache_key in self._metadata_cache: + cached_metadata = self._metadata_cache[cache_key] + for meta in cached_metadata: + if meta.field_name == field_name: + values.update(meta.values) + return values + + try: + # For partitioned datasets, extract from file structure + if ( + config.dataset_info.partitioning + and config.dataset_info.partitioning.enabled + ): + partition_values = self._extract_partition_values(config, field_name) + if partition_values: + values.update(partition_values) + + # For embedded metadata fields, we would need to query the actual data + # This is a placeholder - in practice, you might use the HF datasets server API + if config.metadata_fields and field_name in config.metadata_fields: + # Placeholder for actual data extraction + self.logger.debug( + f"Would extract embedded metadata for {field_name} in {config.config_name}" + ) + + except Exception as e: + self.logger.warning(f"Failed to extract values for {field_name}: {e}") + + return values + + def _extract_partition_values( + self, config: DatasetConfig, field_name: str + ) -> Set[str]: + """Extract values from partition structure.""" + if ( + not config.dataset_info.partitioning + or not config.dataset_info.partitioning.enabled + ): + return set() + + partition_columns = config.dataset_info.partitioning.partition_by or [] + if field_name not in partition_columns: + return set() + + try: + # Get partition values from repository structure + partition_values = self._structure_fetcher.get_partition_values( + self.repo_id, field_name + ) + return set(partition_values) + except HfDataFetchError: + self.logger.warning(f"Failed to extract partition values for {field_name}") + return set() + + def get_metadata_relationships(self) -> List[MetadataRelationship]: + """Get relationships between data configs and their metadata.""" + relationships = [] + data_configs = self.dataset_card.get_data_configs() + metadata_configs = self.dataset_card.get_metadata_configs() + + for data_config in data_configs: + # Check for explicit applies_to relationships + for meta_config in metadata_configs: + if ( + meta_config.applies_to + and data_config.config_name in meta_config.applies_to + ): + relationships.append( + MetadataRelationship( + data_config=data_config.config_name, + metadata_config=meta_config.config_name, + relationship_type="explicit", + ) + ) + continue + + + # Check for embedded metadata + if data_config.metadata_fields: + relationships.append( + MetadataRelationship( + data_config=data_config.config_name, + metadata_config=f"{data_config.config_name}_embedded", + relationship_type="embedded", + ) + ) + + return relationships + + + def get_repository_info(self) -> Dict[str, Any]: + """Get general repository information.""" + card = self.dataset_card + + try: + structure = self._structure_fetcher.fetch(self.repo_id) + total_files = structure.get("total_files", 0) + last_modified = structure.get("last_modified") + except HfDataFetchError: + total_files = None + last_modified = None + + return { + "repo_id": self.repo_id, + "pretty_name": card.pretty_name, + "license": card.license, + "tags": card.tags, + "language": card.language, + "size_categories": card.size_categories, + "num_configs": len(card.configs), + "dataset_types": [config.dataset_type.value for config in card.configs], + "total_files": total_files, + "last_modified": last_modified, + "has_default_config": self.dataset_card.get_default_config() is not None, + } + + def explore_config(self, config_name: str) -> Dict[str, Any]: + """Get detailed information about a specific configuration.""" + config = self.get_config(config_name) + if not config: + raise DataCardError(f"Configuration '{config_name}' not found") + + info: Dict[str, Any] = { + "config_name": config.config_name, + "description": config.description, + "dataset_type": config.dataset_type.value, + "is_default": config.default, + "num_features": len(config.dataset_info.features), + "features": [ + {"name": f.name, "dtype": f.dtype, "description": f.description} + for f in config.dataset_info.features + ], + "data_files": [ + {"split": df.split, "path": df.path} for df in config.data_files + ], + } + + # Add partitioning info if present + if config.dataset_info.partitioning: + info["partitioning"] = { + "enabled": config.dataset_info.partitioning.enabled, + "partition_by": config.dataset_info.partitioning.partition_by, + "path_template": config.dataset_info.partitioning.path_template, + } + + # Add metadata-specific fields + if config.applies_to: + info["applies_to"] = config.applies_to + + if config.metadata_fields: + info["metadata_fields"] = config.metadata_fields + + return info + + def summary(self) -> str: + """Get a human-readable summary of the dataset.""" + card = self.dataset_card + info = self.get_repository_info() + + lines = [ + f"Dataset: {card.pretty_name or self.repo_id}", + f"Repository: {self.repo_id}", + f"License: {card.license or 'Not specified'}", + f"Configurations: {len(card.configs)}", + f"Dataset Types: {', '.join(info['dataset_types'])}", + ] + + if card.tags: + lines.append(f"Tags: {', '.join(card.tags)}") + + # Add config summaries + lines.append("\nConfigurations:") + for config in card.configs: + default_mark = " (default)" if config.default else "" + lines.append( + f" - {config.config_name}: {config.dataset_type.value}{default_mark}" + ) + lines.append(f" {config.description}") + + return "\n".join(lines) diff --git a/tfbpapi/datainfo/fetchers.py b/tfbpapi/datainfo/fetchers.py new file mode 100644 index 0000000..76b33fc --- /dev/null +++ b/tfbpapi/datainfo/fetchers.py @@ -0,0 +1,224 @@ +"""Data fetchers for HuggingFace Hub integration.""" + +import logging +import os +import re +from typing import Any, Dict, List, Optional, Set + +import requests +from huggingface_hub import DatasetCard, repo_info +from requests import HTTPError + +from ..errors import HfDataFetchError + + +class HfDataCardFetcher: + """Handles fetching dataset cards from HuggingFace Hub.""" + + def __init__(self, token: Optional[str] = None): + """Initialize the fetcher. + + :param token: HuggingFace token for authentication + """ + self.logger = logging.getLogger(self.__class__.__name__) + self.token = token or os.getenv("HF_TOKEN") + + def fetch(self, repo_id: str, repo_type: str = "dataset") -> Dict[str, Any]: + """Fetch and return dataset card data. + + :param repo_id: Repository identifier (e.g., "user/dataset") + :param repo_type: Type of repository ("dataset", "model", "space") + :return: Dataset card data as dictionary + :raises HfDataFetchError: If fetching fails + """ + try: + self.logger.debug(f"Fetching dataset card for {repo_id}") + card = DatasetCard.load(repo_id, repo_type=repo_type, token=self.token) + + if not card.data: + self.logger.warning(f"Dataset card for {repo_id} has no data section") + return {} + + return card.data.to_dict() + + except Exception as e: + error_msg = f"Failed to fetch dataset card for {repo_id}: {e}" + self.logger.error(error_msg) + raise HfDataFetchError(error_msg) from e + + +class HfSizeInfoFetcher: + """Handles fetching size information from HuggingFace Dataset Server API.""" + + def __init__(self, token: Optional[str] = None): + """Initialize the fetcher. + + :param token: HuggingFace token for authentication + """ + self.logger = logging.getLogger(self.__class__.__name__) + self.token = token or os.getenv("HF_TOKEN") + self.base_url = "https://datasets-server.huggingface.co" + + def _build_headers(self) -> Dict[str, str]: + """Build request headers with authentication if available.""" + headers = {"User-Agent": "TFBP-API/1.0"} + if self.token: + headers["Authorization"] = f"Bearer {self.token}" + return headers + + def fetch(self, repo_id: str) -> Dict[str, Any]: + """Fetch dataset size information. + + :param repo_id: Repository identifier (e.g., "user/dataset") + :return: Size information as dictionary + :raises HfDataFetchError: If fetching fails + """ + url = f"{self.base_url}/size" + params = {"dataset": repo_id} + headers = self._build_headers() + + try: + self.logger.debug(f"Fetching size info for {repo_id}") + response = requests.get(url, params=params, headers=headers, timeout=30) + response.raise_for_status() + + data = response.json() + self.logger.debug(f"Size info fetched successfully for {repo_id}") + return data + + except HTTPError as e: + if e.response.status_code == 404: + error_msg = f"Dataset {repo_id} not found" + elif e.response.status_code == 403: + error_msg = ( + f"Access denied to dataset {repo_id} (check token permissions)" + ) + else: + error_msg = f"HTTP error fetching size for {repo_id}: {e}" + + self.logger.error(error_msg) + raise HfDataFetchError(error_msg) from e + + except requests.RequestException as e: + error_msg = f"Request failed fetching size for {repo_id}: {e}" + self.logger.error(error_msg) + raise HfDataFetchError(error_msg) from e + + except ValueError as e: + error_msg = f"Invalid JSON response fetching size for {repo_id}: {e}" + self.logger.error(error_msg) + raise HfDataFetchError(error_msg) from e + + +class HfRepoStructureFetcher: + """Handles fetching repository structure from HuggingFace Hub.""" + + def __init__(self, token: Optional[str] = None): + """Initialize the fetcher. + + :param token: HuggingFace token for authentication + """ + self.logger = logging.getLogger(self.__class__.__name__) + self.token = token or os.getenv("HF_TOKEN") + self._cached_structure: Dict[str, Dict[str, Any]] = {} + + def fetch(self, repo_id: str, force_refresh: bool = False) -> Dict[str, Any]: + """Fetch repository structure information. + + :param repo_id: Repository identifier (e.g., "user/dataset") + :param force_refresh: If True, bypass cache and fetch fresh data + :return: Repository structure information + :raises HfDataFetchError: If fetching fails + """ + # Check cache first unless force refresh is requested + if not force_refresh and repo_id in self._cached_structure: + self.logger.debug(f"Using cached repo structure for {repo_id}") + return self._cached_structure[repo_id] + + try: + self.logger.debug(f"Fetching repo structure for {repo_id}") + info = repo_info(repo_id=repo_id, repo_type="dataset", token=self.token) + + # Extract file structure + files = [] + partitions: Dict[str, set] = {} + + for sibling in info.siblings or []: + file_info = { + "path": sibling.rfilename, + "size": sibling.size, + "is_lfs": sibling.lfs is not None, + } + files.append(file_info) + + # Extract partition information from file paths + self._extract_partition_info(sibling.rfilename, partitions) + + result = { + "repo_id": repo_id, + "files": files, + "partitions": partitions, + "total_files": len(files), + "last_modified": ( + info.last_modified.isoformat() if info.last_modified else None + ), + } + + # Cache the result + self._cached_structure[repo_id] = result + return result + + except Exception as e: + error_msg = f"Failed to fetch repo structure for {repo_id}: {e}" + self.logger.error(error_msg) + raise HfDataFetchError(error_msg) from e + + def _extract_partition_info( + self, file_path: str, partitions: Dict[str, Set[str]] + ) -> None: + """Extract partition information from file paths. + + :param file_path: Path to analyze for partitions + :param partitions: Dictionary to update with partition info + """ + # Look for partition patterns like "column=value" in path + partition_pattern = r"([^/=]+)=([^/]+)" + matches = re.findall(partition_pattern, file_path) + + for column, value in matches: + if column not in partitions: + partitions[column] = set() + partitions[column].add(value) + + def get_partition_values(self, repo_id: str, partition_column: str, force_refresh: bool = False) -> List[str]: + """Get all values for a specific partition column. + + :param repo_id: Repository identifier + :param partition_column: Name of the partition column + :param force_refresh: If True, bypass cache and fetch fresh data + :return: List of unique partition values + :raises HfDataFetchError: If fetching fails + """ + structure = self.fetch(repo_id, force_refresh=force_refresh) + partition_values = structure.get("partitions", {}).get(partition_column, set()) + return sorted(list(partition_values)) + + def get_dataset_files( + self, repo_id: str, path_pattern: Optional[str] = None, force_refresh: bool = False + ) -> List[Dict[str, Any]]: + """Get dataset files, optionally filtered by path pattern. + + :param repo_id: Repository identifier + :param path_pattern: Optional regex pattern to filter files + :param force_refresh: If True, bypass cache and fetch fresh data + :return: List of matching files + :raises HfDataFetchError: If fetching fails + """ + structure = self.fetch(repo_id, force_refresh=force_refresh) + files = structure["files"] + + if path_pattern: + pattern = re.compile(path_pattern) + files = [f for f in files if pattern.search(f["path"])] + + return files diff --git a/tfbpapi/datainfo/models.py b/tfbpapi/datainfo/models.py new file mode 100644 index 0000000..2ad8383 --- /dev/null +++ b/tfbpapi/datainfo/models.py @@ -0,0 +1,250 @@ +"""Pydantic models for dataset card validation.""" + +from enum import Enum +from typing import Any, Dict, List, Optional, Set, Union + +from pydantic import BaseModel, ConfigDict, Field, field_validator + + +class DatasetType(str, Enum): + """Supported dataset types.""" + + GENOMIC_FEATURES = "genomic_features" + ANNOTATED_FEATURES = "annotated_features" + GENOME_MAP = "genome_map" + METADATA = "metadata" + + +class ClassLabelType(BaseModel): + """Categorical data type with class labels.""" + + names: List[str] = Field(..., description="List of possible class names") + + +class FeatureInfo(BaseModel): + """Information about a dataset feature/column.""" + + name: str = Field(..., description="Column name in the data") + dtype: Union[str, Dict[str, ClassLabelType]] = Field( + ..., + description="Data type (string, int64, float64, etc.) or categorical class labels", + ) + description: str = Field(..., description="Detailed description of the field") + + @field_validator("dtype", mode="before") + @classmethod + def validate_dtype(cls, v): + """Validate and normalize dtype field.""" + if isinstance(v, str): + return v + elif isinstance(v, dict): + # Handle class_label structure + if "class_label" in v: + # Convert to our ClassLabelType structure + class_label_data = v["class_label"] + if isinstance(class_label_data, dict) and "names" in class_label_data: + return {"class_label": ClassLabelType(**class_label_data)} + else: + raise ValueError( + f"Invalid class_label structure: expected dict with 'names' key, got {class_label_data}" + ) + else: + raise ValueError( + f"Unknown dtype structure: expected 'class_label' key in dict, got keys: {list(v.keys())}" + ) + else: + raise ValueError( + f"dtype must be a string or dict with class_label info, got {type(v)}: {v}" + ) + + def get_dtype_summary(self) -> str: + """Get a human-readable summary of the data type.""" + if isinstance(self.dtype, str): + return self.dtype + elif isinstance(self.dtype, dict) and "class_label" in self.dtype: + names = self.dtype["class_label"].names + return f"categorical ({len(names)} classes: {', '.join(names)})" + else: + return str(self.dtype) + + +class PartitioningInfo(BaseModel): + """Partitioning configuration for datasets.""" + + enabled: bool = Field(default=False, description="Whether partitioning is enabled") + partition_by: Optional[List[str]] = Field( + default=None, description="Partition column names" + ) + path_template: Optional[str] = Field( + default=None, description="Path template for partitioned files" + ) + + +class DataFileInfo(BaseModel): + """Information about data files.""" + + split: str = Field(default="train", description="Dataset split name") + path: str = Field(..., description="Path to data file(s)") + + +class DatasetInfo(BaseModel): + """Dataset structure information.""" + + features: List[FeatureInfo] = Field(..., description="Feature definitions") + partitioning: Optional[PartitioningInfo] = Field( + default=None, description="Partitioning configuration" + ) + + +class DatasetConfig(BaseModel): + """Configuration for a dataset within a repository.""" + + config_name: str = Field(..., description="Unique configuration identifier") + description: str = Field(..., description="Human-readable description") + dataset_type: DatasetType = Field(..., description="Type of dataset") + default: bool = Field( + default=False, description="Whether this is the default config" + ) + applies_to: Optional[List[str]] = Field( + default=None, description="Configs this metadata applies to" + ) + metadata_fields: Optional[List[str]] = Field( + default=None, description="Fields for embedded metadata extraction" + ) + data_files: List[DataFileInfo] = Field(..., description="Data file information") + dataset_info: DatasetInfo = Field(..., description="Dataset structure information") + + @field_validator("applies_to") + @classmethod + def applies_to_only_for_metadata(cls, v, info): + """Validate that applies_to is only used for metadata configs.""" + if v is not None: + dataset_type = info.data.get("dataset_type") + if dataset_type != DatasetType.METADATA: + raise ValueError( + "applies_to field is only valid for metadata dataset types" + ) + return v + + @field_validator("metadata_fields") + @classmethod + def metadata_fields_validation(cls, v): + """Validate metadata_fields usage.""" + if v is not None and len(v) == 0: + raise ValueError("metadata_fields cannot be empty list, use None instead") + return v + + +class BasicMetadata(BaseModel): + """Basic dataset metadata.""" + + license: Optional[str] = Field(default=None, description="Dataset license") + language: Optional[List[str]] = Field(default=None, description="Dataset languages") + tags: Optional[List[str]] = Field(default=None, description="Descriptive tags") + pretty_name: Optional[str] = Field( + default=None, description="Human-readable dataset name" + ) + size_categories: Optional[List[str]] = Field( + default=None, description="Dataset size categories" + ) + + +class DatasetCard(BaseModel): + """Complete dataset card model.""" + + configs: List[DatasetConfig] = Field(..., description="Dataset configurations") + license: Optional[str] = Field(default=None, description="Dataset license") + language: Optional[List[str]] = Field(default=None, description="Dataset languages") + tags: Optional[List[str]] = Field(default=None, description="Descriptive tags") + pretty_name: Optional[str] = Field( + default=None, description="Human-readable dataset name" + ) + size_categories: Optional[List[str]] = Field( + default=None, description="Dataset size categories" + ) + + @field_validator("configs") + @classmethod + def configs_not_empty(cls, v): + """Ensure at least one config is present.""" + if not v: + raise ValueError("At least one dataset configuration is required") + return v + + @field_validator("configs") + @classmethod + def unique_config_names(cls, v): + """Ensure config names are unique.""" + names = [config.config_name for config in v] + if len(names) != len(set(names)): + raise ValueError("Configuration names must be unique") + return v + + @field_validator("configs") + @classmethod + def at_most_one_default(cls, v): + """Ensure at most one config is marked as default.""" + defaults = [config for config in v if config.default] + if len(defaults) > 1: + raise ValueError("At most one configuration can be marked as default") + return v + + def get_config_by_name(self, name: str) -> Optional[DatasetConfig]: + """Get a configuration by name.""" + for config in self.configs: + if config.config_name == name: + return config + return None + + def get_configs_by_type(self, dataset_type: DatasetType) -> List[DatasetConfig]: + """Get all configurations of a specific type.""" + return [ + config for config in self.configs if config.dataset_type == dataset_type + ] + + def get_default_config(self) -> Optional[DatasetConfig]: + """Get the default configuration if one exists.""" + defaults = [config for config in self.configs if config.default] + return defaults[0] if defaults else None + + def get_data_configs(self) -> List[DatasetConfig]: + """Get all non-metadata configurations.""" + return [ + config + for config in self.configs + if config.dataset_type != DatasetType.METADATA + ] + + def get_metadata_configs(self) -> List[DatasetConfig]: + """Get all metadata configurations.""" + return [ + config + for config in self.configs + if config.dataset_type == DatasetType.METADATA + ] + + +class ExtractedMetadata(BaseModel): + """Metadata extracted from datasets.""" + + config_name: str = Field(..., description="Source configuration name") + field_name: str = Field( + ..., description="Field name the metadata was extracted from" + ) + values: Set[str] = Field(..., description="Unique values found") + extraction_method: str = Field(..., description="How the metadata was extracted") + + model_config = ConfigDict( + # Allow sets in JSON serialization + json_encoders={set: list} + ) + + +class MetadataRelationship(BaseModel): + """Relationship between a data config and its metadata.""" + + data_config: str = Field(..., description="Data configuration name") + metadata_config: str = Field(..., description="Metadata configuration name") + relationship_type: str = Field( + ..., description="Type of relationship (explicit, embedded)" + ) diff --git a/tfbpapi/errors.py b/tfbpapi/errors.py new file mode 100644 index 0000000..3c0fdfa --- /dev/null +++ b/tfbpapi/errors.py @@ -0,0 +1,214 @@ +"""Custom exception classes for dataset management.""" + +from typing import Any, Dict, Optional + + +class DatasetError(Exception): + """Base exception for all dataset-related errors.""" + + def __init__(self, message: str, details: Optional[Dict[str, Any]] = None): + super().__init__(message) + self.details = details or {} + + def __str__(self) -> str: + base_msg = super().__str__() + if self.details: + detail_str = ", ".join(f"{k}={v}" for k, v in self.details.items()) + return f"{base_msg} (Details: {detail_str})" + return base_msg + + +class RepoTooLargeError(DatasetError): + """Raised when repository exceeds auto-download threshold.""" + + def __init__(self, repo_id: str, size_mb: float, threshold_mb: float): + message = f"Repository '{repo_id}' is too large for auto-download: {size_mb:.2f}MB exceeds {threshold_mb}MB threshold" + super().__init__( + message, + details={ + "repo_id": repo_id, + "actual_size_mb": size_mb, + "threshold_mb": threshold_mb, + }, + ) + self.repo_id = repo_id + self.size_mb = size_mb + self.threshold_mb = threshold_mb + + +class DataCardParsingError(DatasetError): + """Raised when dataset card parsing fails.""" + + def __init__( + self, + message: str, + repo_id: Optional[str] = None, + config_name: Optional[str] = None, + original_error: Optional[Exception] = None, + ): + details = {} + if repo_id: + details["repo_id"] = repo_id + if config_name: + details["config_name"] = config_name + if original_error: + details["original_error"] = str(original_error) + + super().__init__(message, details) + self.repo_id = repo_id + self.config_name = config_name + self.original_error = original_error + + +class HfDataFetchError(DatasetError): + """Raised when HuggingFace API requests fail.""" + + def __init__( + self, + message: str, + repo_id: Optional[str] = None, + status_code: Optional[int] = None, + endpoint: Optional[str] = None, + ): + details = {} + if repo_id: + details["repo_id"] = repo_id + if status_code: + details["status_code"] = status_code + if endpoint: + details["endpoint"] = endpoint + + super().__init__(message, details) + self.repo_id = repo_id + self.status_code = status_code + self.endpoint = endpoint + + +class TableNotFoundError(DatasetError): + """Raised when requested table doesn't exist.""" + + def __init__(self, table_name: str, available_tables: Optional[list] = None): + available_str = ( + f"Available tables: {available_tables}" + if available_tables + else "No tables available" + ) + message = f"Table '{table_name}' not found. {available_str}" + + super().__init__( + message, + details={ + "requested_table": table_name, + "available_tables": available_tables or [], + }, + ) + self.table_name = table_name + self.available_tables = available_tables or [] + + +class MissingDatasetTypeError(DatasetError): + """Raised when dataset_type field is missing from config.""" + + def __init__(self, config_name: str, available_fields: Optional[list] = None): + fields_str = f"Available fields: {available_fields}" if available_fields else "" + message = ( + f"Missing 'dataset_type' field in config '{config_name}'. {fields_str}" + ) + + super().__init__( + message, + details={ + "config_name": config_name, + "available_fields": available_fields or [], + }, + ) + self.config_name = config_name + self.available_fields = available_fields or [] + + +class InvalidDatasetTypeError(DatasetError): + """Raised when dataset_type value is not recognized.""" + + def __init__(self, invalid_type: str, valid_types: Optional[list] = None): + valid_str = f"Valid types: {valid_types}" if valid_types else "" + message = f"Invalid dataset type '{invalid_type}'. {valid_str}" + + super().__init__( + message, + details={"invalid_type": invalid_type, "valid_types": valid_types or []}, + ) + self.invalid_type = invalid_type + self.valid_types = valid_types or [] + + +class ConfigNotFoundError(DatasetError): + """Raised when a requested config doesn't exist.""" + + def __init__( + self, + config_name: str, + repo_id: Optional[str] = None, + available_configs: Optional[list] = None, + ): + repo_str = f" in repository '{repo_id}'" if repo_id else "" + available_str = ( + f"Available configs: {available_configs}" if available_configs else "" + ) + message = f"Config '{config_name}' not found{repo_str}. {available_str}" + + super().__init__( + message, + details={ + "config_name": config_name, + "repo_id": repo_id, + "available_configs": available_configs or [], + }, + ) + self.config_name = config_name + self.repo_id = repo_id + self.available_configs = available_configs or [] + + +class DataCardError(DatasetError): + """Base exception for DataCard operations.""" + pass + + +class DataCardValidationError(DataCardError): + """Exception raised when dataset card validation fails.""" + + def __init__( + self, + message: str, + repo_id: Optional[str] = None, + validation_errors: Optional[list] = None, + ): + details = {} + if repo_id: + details["repo_id"] = repo_id + if validation_errors: + details["validation_errors"] = validation_errors + + super().__init__(message, details) + self.repo_id = repo_id + self.validation_errors = validation_errors or [] + + +class DataCardMetadataError(DataCardError): + """Exception raised when metadata extraction fails.""" + + def __init__( + self, + message: str, + config_name: Optional[str] = None, + field_name: Optional[str] = None, + ): + details = {} + if config_name: + details["config_name"] = config_name + if field_name: + details["field_name"] = field_name + + super().__init__(message, details) + self.config_name = config_name + self.field_name = field_name diff --git a/tfbpapi/metric_arrays.py b/tfbpapi/metric_arrays.py deleted file mode 100644 index 2bfaf14..0000000 --- a/tfbpapi/metric_arrays.py +++ /dev/null @@ -1,162 +0,0 @@ -import logging -from collections.abc import Callable - -import pandas as pd - -logger = logging.getLogger(__name__) - - -def metric_arrays( - res_dict: dict[str, pd.DataFrame | dict[str, pd.DataFrame]], - metrics_dict: dict[str, Callable], - rownames: str = "target_symbol", - colnames: str = "regulator_symbol", - row_dedup_func: Callable | None = None, - drop_incomplete_rows: bool = True, -) -> dict[str, pd.DataFrame]: - """ - Extract specified metrics from an AbstractRecordsAndFilesAPI instance's - read(retrieve_files=True) results object. - - :param res_dict: The output of an AbstractRecordsAndFiles instance. - :param metrics_dict: A dictionary where the keys are metrics and the values are - functions to apply to rows in the event that there are multiple rows with - the same rownames. Set to None to raise error if duplicate rownames are found. - :param rownames: Column name to use for row labels. - :param colnames: Column name to use for column labels. - :param drop_incomplete_rows: When True, drops rows and columns with all NaN values. - - :return: A dictionary where the metric is the key and the value is a DataFrame. - The column values are metric values, and the column names correspond - to `colnames` in the metadata DataFrame. - - :raises AttributeError: If the values in `colnames` or `rownames` are not unique - :raises KeyError: If the res_dict does not have keys 'metadata' and 'data' - :raises KeyError: If the data dictionary does not have the same keys as the 'id' - column - :raises ValueError: If the metadata does not have an 'id' column - :raises ValueError: If either the metadata or the data dictionary values are not - DataFrames - :raises ValueError: If the `colnames` is not in the res_dict metadata - :raises ValueError: If the `rownames` is not in the res_dict data - :raises ValueError: If the metrics are not in the data dictionary - - """ - - # Check required keys - if not all(k in res_dict for k in ["metadata", "data"]): - raise KeyError("res_dict must have keys 'metadata' and 'data'") - - metadata: pd.DataFrame = res_dict["metadata"] - - # Verify 'id' in metadata - if "id" not in metadata.columns: - raise ValueError("metadata must have an 'id' column") - - # Check for missing keys in 'data' - missing_keys = [k for k in metadata["id"] if str(k) not in res_dict["data"]] - if missing_keys: - raise KeyError( - f"Data dictionary must have the same keys as the 'id' " - f"column. Missing keys: {missing_keys}" - ) - - # Ensure all data dictionary values are DataFrames - if not all(isinstance(v, pd.DataFrame) for v in res_dict["data"].values()): - raise ValueError("All values in the data dictionary must be DataFrames") - - # Verify rownames in data and colnames in metadata - if colnames not in metadata.columns: - raise ValueError(f"colnames '{colnames}' not in metadata") - data_with_missing_rownames = [ - id for id, df in res_dict["data"].items() if rownames not in df.columns - ] - if data_with_missing_rownames: - raise ValueError( - f"rownames '{rownames}' not in data for ids: {data_with_missing_rownames}" - ) - - # Factorize unique row and column labels - row_labels = pd.Index( - {item for df in res_dict["data"].values() for item in df[rownames].unique()} - ) - - # Initialize output dictionary with NaN DataFrames for each metric - output_dict = { - m: pd.DataFrame(index=pd.Index(row_labels, name=rownames)) - for m in metrics_dict.keys() - } - - # Populate DataFrames with metric values - info_msgs = set() - for _, row in metadata.iterrows(): - try: - data = res_dict["data"][row["id"]] - except KeyError: - info_msgs.add("casting `id` to str to extract data from res_dict['data']") - data = res_dict["data"][str(row["id"])] - - for metric, row_dedup_func in metrics_dict.items(): - # Filter data to include only the rownames and metric columns - if metric not in data.columns: - raise ValueError( - f"Metric '{metric}' not found in data for id '{row['id']}'" - ) - - metric_data = data[[rownames, metric]] - - # Handle deduplication if row_dedup_func is provided - if row_dedup_func is not None: - metric_data = ( - metric_data.groupby(rownames)[metric] - .apply(row_dedup_func) - .reset_index() - ) - else: - # Ensure no duplicates exist if no deduplication function is provided - if metric_data[rownames].duplicated().any(): - raise ValueError( - f"Duplicate entries found for metric '{metric}' " - f"in id '{row['id']}' without dedup_func" - ) - - # test if row[colnames] is already in output_dict[metric]. If it is, add a - # replicate suffix and try again, Continue doing this until the column name - # is unique - colname = row[colnames] - suffix = 2 - while colname in output_dict[metric].columns: - colname = f"{row[colnames]}_rep{suffix}" - suffix += 1 - if suffix > 2: - info_msgs.add( - f"Column name '{row[colnames]}' already exists in " - f"output DataFrame for metric '{metric}'. " - f"Renaming to '{colname}'" - ) - # Join metric data with output DataFrame for the metric - output_dict[metric] = output_dict[metric].join( - metric_data.set_index(rownames).rename(columns={metric: colname}), - how="left", - ) - logger.info("; ".join(info_msgs)) - - # Drop incomplete rows and columns if drop_incomplete_rows is True - if drop_incomplete_rows: - for metric, df in output_dict.items(): - # Drop rows and columns where all values are NaN - initial_shape = df.shape - output_dict[metric] = df.dropna(axis=0) - final_shape = output_dict[metric].shape - - dropped_rows = initial_shape[0] - final_shape[0] - dropped_columns = initial_shape[1] - final_shape[1] - - if dropped_rows > 0 or dropped_columns > 0: - logger.warning( - f"{dropped_rows} rows and {dropped_columns} " - f"columns with incomplete " - f"records were dropped for metric '{metric}'." - ) - - return output_dict diff --git a/tfbpapi/rank_transforms.py b/tfbpapi/rank_transforms.py deleted file mode 100644 index 9e4c672..0000000 --- a/tfbpapi/rank_transforms.py +++ /dev/null @@ -1,154 +0,0 @@ -import numpy as np -from scipy.stats import rankdata - - -def shifted_negative_log_ranks(ranks: np.ndarray) -> np.ndarray: - """ - Transforms ranks to negative log10 values and shifts such that the lowest value is - 0. - - :param ranks: A vector of ranks - :return np.ndarray: A vector of negative log10 transformed ranks shifted such that - the lowest value is 0 - :raises ValueError: If the ranks are not numeric. - - """ - if not np.issubdtype(ranks.dtype, np.number): - raise ValueError("`ranks` must be a numeric") - max_rank = np.max(ranks) - log_max_rank = np.log10(max_rank) - return -1 * np.log10(ranks) + log_max_rank - - -def stable_rank( - pvalue_vector: np.ndarray, enrichment_vector: np.ndarray, method="average" -) -> np.ndarray: - """ - Ranks data by primary_column, breaking ties based on secondary_column. The expected - primary and secondary columns are 'pvalue' and 'enrichment', respectively. Then the - ranks are transformed to negative log10 values and shifted such that the lowest - value is 0 and the highest value is log10(min_rank). - - :param pvalue_vector: A vector of pvalues - :param enrichment_vector: A vector of enrichment values corresponding to the pvalues - :param method: The method to use for final ranking. Default is "average". - See `rankdata` - - :return np.ndarray: A vector of negative log10 transformed ranks shifted such that - the lowest value is 0 and the highest value is log10(min_rank) - :raises ValueError: If the primary or secondary column is not numeric. - - """ - - # Check if primary and secondary columns are numeric - if not np.issubdtype(pvalue_vector.dtype, np.number): - raise ValueError("`primary_vector` must be a numeric") - if not np.issubdtype(enrichment_vector.dtype, np.number): - raise ValueError("`secondary_vector` must be a numeric") - - # Step 1: Rank by primary_column - # note that this will now always be an integer, unlike average which could return - # decimal values making adding the secondary rank more difficult - primary_rank = rankdata(pvalue_vector, method="min") - - # Step 2: Identify ties in primary_rank - unique_ranks = np.unique(primary_rank) - - # Step 3: Adjust ranks within ties using secondary ranking - adjusted_primary_rank = primary_rank.astype( - float - ) # Convert to float for adjustments - - for unique_rank in unique_ranks: - # Get indices where primary_rank == unique_rank - tie_indices = np.where(primary_rank == unique_rank)[0] - - if len(tie_indices) > 1: # Only adjust if there are ties - # Rank within the tie group by secondary_column - # (descending if higher is better) - tie_secondary_values = enrichment_vector[tie_indices] - secondary_rank_within_ties = rankdata( - -tie_secondary_values, method="average" - ) - - # Calculate dynamic scale factor to ensure adjustments are < 1. Since the - # primary_rank is an integer, adding a number less than 1 will not affect - # rank relative to the other groups. - max_secondary_rank = np.max(secondary_rank_within_ties) - scale_factor = ( - 0.9 / max_secondary_rank - ) # Keep scale factor slightly below 1/max rank - - # multiple the secondary_rank_within_ties values by 0.1 and add this value - # to the adjusted_primary_rank_values. This will rank the tied primary - # values by the secondary values, but not affect the overall primary rank - # outside of the tie group - # think about this scale factor - adjusted_primary_rank[tie_indices] += ( - secondary_rank_within_ties * scale_factor - ) - - # Step 4: Final rank based on the adjusted primary ranks - final_ranks = rankdata(adjusted_primary_rank, method=method) - - return final_ranks - - -def rank_by_pvalue(pvalue_vector: np.ndarray, method="average") -> np.ndarray: - """ - This expects a vector of pvalues, returns a vector of ranks where the lowest pvalue - has the lowest rank. - - :param pvalue_vector: A vector of pvalues - :param enrichment_vector: A vector of enrichment values corresponding to the pvalues - :param method: The method to use for ranking. Default is "average". See `rankdata` - :return np.ndarray: A vector of negative log10 transformed ranks shifted such that - the lowest value is 0 and the highest value is log10(min_rank) - :raises ValueError: If the primary or secondary column is not numeric. - - """ - - # Check if primary and secondary columns are numeric - if not np.issubdtype(pvalue_vector.dtype, np.number): - raise ValueError("`primary_vector` must be a numeric") - - # Step 1: Rank by primary_column - # note that this will now always be an integer, unlike average which could return - # decimal values making adding the secondary rank more difficult - return rankdata(pvalue_vector, method=method) - - -def transform( - pvalue_vector: np.ndarray, - enrichment_vector: np.ndarray, - use_enrichment: bool = True, - negative_log_shift: bool = True, - **kwargs, -) -> np.ndarray: - """ - This calls the rank() function and then transforms the ranks to negative log10 - values and shifts to the right such that the lowest value (largest rank, least - important) is 0. - - :param pvalue_vector: A vector of pvalues - :param enrichment_vector: A vector of enrichment values corresponding to the pvalues - :param use_enrichment: Set to True to use the enrichment vector to break ties. - Default is True. If False, pvalues will be ranked directly with method="average' - :param negative_log_shift: Set to True to shift the ranks to the right such that the - lowest value (largest rank, least important) is 0. Default is True. - :param kwargs: Additional keyword arguments to pass to the rank() function (e.g. - method="min") - :return np.ndarray: A vector of negative log10 transformed ranks shifted such that - the lowest value is 0 and the highest value is log10(min_rank) - :raises ValueError: If the primary or secondary column is not numeric. - - """ - if use_enrichment: - ranks = stable_rank(pvalue_vector, enrichment_vector, **kwargs) - else: - ranks = rank_by_pvalue(pvalue_vector, **kwargs) - - if negative_log_shift: - return shifted_negative_log_ranks(ranks) - else: - return ranks diff --git a/tfbpapi/tests/data/cache_info.pkl b/tfbpapi/tests/data/cache_info.pkl deleted file mode 100644 index 7906d8e30dd63514636537b3083c3ee20c7995e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 123975 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 diff --git a/tfbpapi/tests/datainfo/__init__.py b/tfbpapi/tests/datainfo/__init__.py new file mode 100644 index 0000000..672d1e8 --- /dev/null +++ b/tfbpapi/tests/datainfo/__init__.py @@ -0,0 +1 @@ +"""Tests for the datainfo package.""" \ No newline at end of file diff --git a/tfbpapi/tests/datainfo/conftest.py b/tfbpapi/tests/datainfo/conftest.py new file mode 100644 index 0000000..51afb26 --- /dev/null +++ b/tfbpapi/tests/datainfo/conftest.py @@ -0,0 +1,302 @@ +"""Shared fixtures and test data for datainfo tests.""" + +import pytest +from unittest.mock import Mock + + +@pytest.fixture +def sample_dataset_card_data(): + """Sample dataset card data for testing.""" + return { + "license": "mit", + "language": ["en"], + "tags": ["biology", "genomics", "yeast"], + "pretty_name": "Test Genomics Dataset", + "size_categories": ["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≯!LT?|_H! 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 z z{JG>WI`Q=SNC*(T9^M}zIfhH{t%8}VY23Iv%m z*h_x>2{o#0qgBGBFo~F3BfzXQJPG}X+{N89q?o@@%sfOCK}p}(`lw{%hs@MCY%Hs3 zb|@i%rmf*AUpIanxyD~ZD+q$|Vj|RFHx(vAZ>9X=WxC>%^;Ra+>}V z$RVSpo|Q}XCTZ^#O72@$J@#S6L=-`r_VPp31Z>)v`XN9$nOm=e0FoQ?$0$^w0 zwOCdr*1ABY0&GsYZ^ZsI0nl7b_P@)I({cCwzILlQrwhC639zPfL-=FKUc30D*)I!h z@dBRAuqFRC*!^zcucFVHDbmCOmw6-Wv%jb9BD>!Dq zVZK-2gXcK!u}=|9ir^&Oa^)RFhs~Ils^}imq*SNLK7+P`>NJ$E-4V5}<2{(h0@eYuTabCu zzT5v<@xQKpGK9wV#6qna!czRx$3N1NhA5xJ2z$@U1*p904X+HC%85Zxm#$e%)q50aTP&Vm` z_2^4a5RK+dDuMtpXD)TTwWs6d`c6T^AgM{vB$Mo1>2@`#FBj9C+|i%Al)YkSyH-l{ zy#agf9x0KEE^x@MZ%sF|KGjjB4k$4+2AcNoza`*lP!nQd(|5dI-Ly(h;XTD`C%l6*1PPxu`;`sBmov`*8Air6wG!n%x3+fZxoieP=*o&80t3wtD zS4br76VmYIQ_xK&4<6s@1zUe`lddEa_R($kOs%0H8xxB8qNjzx(Mpobm2oaz#y{Kt zb7-)SHC+{xl7rfHU8*My(RHWjo7p61HFjA;if|qXmj+7(7K9vl|82L1xWPO`D>Hf^ zSImx#4D6haXkyGhR;Vm4y1PUA^->`dIjn)5uo4>?+cbl082npMBA2S&jolT$0{Jsd zc!JD`Vau*ObUjpxOcpC%N>N>!5n$EBf_5B^ux(K2grmw@TAX$i4vkRK=(^LZt#@Vt zX#)lbX*PLRKfr2|6cwRKlX?oV$dXauZ1D90o-j7`)g-I^V8vhEczCmR zZ0($QFFN$r^!j6_j@0D|aew2vPW^+!?%fzVW;KjdS|Ly5SlQw!irGJEV`kkLK&E1k zsV*yt6KeVIZ$ZF8FP~cH#c8R98Xn&)C^aK%pFJ0{A%U<78j+Kiq=V`4*XDRw{+v9a z48l5!h}lj)=fW>dXz=w^3|bN3!g1!)mn#T+)XI};(AyoHo}+qlpdHaAge|Gdu&LwFCt9vz2`qI$EP zna|#W!^}WzOLQ!VESKCI@X$5LazeHTGabPq`m9H2q(zs3(Z-i#S&0^f?KqW`?ix28 zY0#d9{hHjmXD-dz$k@(!xNS-~Y2V3=uSvM1QqcMBpf( z%T~}DdXVZ6$~89Uj47WFWSdoMp1Uxtw!%F!$jUObi5R#So90tuD_XY( zH$|?$&KZA@65P3TSDSLR`#)h@$JB3<#J2npaVEiI#-qPdURJ!3VDuAvE0Lg`ru#SI z)Wt_stD@ULTglRUav9<^7d0DzmR>2R1~KRv_W``(GJ(U74{jj$QzIW6)>eo?8*(-m_$#TF2=_%1)G4gUKf>#5=MN%n5V^q?ZzR46u&Dj) zHMFN9KJzUS7lMx5m&_mgGrz-H09E`qJ#N?!{)n)W>#O7<;mW?NG|o?-6ZOokj}W3s z?zng0{}B_}bBNHR3prTGh)HX%EbhC6E_Ab}W=O&2U2Elvbiw_y6~rP&ZT@8KigbNX zQgs(`hx#~$WkzGPdIuV`mL{m|TACxO(;XbTRbL8W%@8xE2% z;x!vm$8D=NJ81Gyt2K8nq`6FmGK#XL5HI=`OydKN)!46(SWYbTZ`=3F)q-vZ9`IVI z20{Rlr-LxyiV{XtAjAyymuHbQ>coOZd$acI;NlN8a(5V{`vt4l$t>6}HYuAsFuP^j zyvoNM#}QNeyFnRN*(JzIeUpKI@2rVkV?B5Y(XqYkINq~jvdN>2sqE`ducypk`I3P3 zIwv`{FGiBHU}Hv1FJ z{Jd$k7O%7(gu;?rsB?P)d(m!>jdlIcB@E$4{?OLA;L+;mbuzvBuCQUWS(hpgEdw7Of^?ja#@mF1FlSIBXe6q3q zZCmR|)}nS``QQG#;MjdnY*#hQ_xJy7>D?q?V_Q;-z!q}%&*rxZ&+t^bC7tOkwA>I( zHazrOU>fj?r1c4G(jMad(bngdd7%8)MGH{Ffdq$t?_ADMPcsDYo);yacm@UyEov7_ zOOn)K51v213o~9HB=V1Q*Nv4!eiJsm8XQMYGV$7Zjx1^WXFQ5~s^Lrft_w4^O~3w{ zPSMk1jxj;A&F>|wlveB7tSVMQNtv4y?+cD`mM6I#s<1Y10W9PoA&9W5vs*OP#|tz6 zRJ&hMblK2F-=-=ELX+|6!r*#n?DP~Sbh=-`rn;j;?URj9e1cOFmdihabCAVDO_$jL z!M*WC(^j}KA}nIfe|+MP1|eC?bvw;)HeVo{q;FcAi;hJ5e<#VgH^mYGDNWBNk!tHm z9ze~+=dZGxCR`6(jHWX@Cx zsbK1oi*cu19F$uDYeGxj;o`EJp*=yFv?g=>E(NIeMX7} znp4>dR>3`_C{oQH8E(Cs%Z{OEfi&CA#qHa;0M4Vep8p@2mQ}P}tw{ z;AAqg%Qab$0Gsv>tT-j%FrU5Z30(rKd9qGTP6y|-%y-KOH3hz; zA3jbX*!R=-aQoOha<<@vbvb-T3Lo>ZTT^5j3^XRUomn?1Rf{7F?tt!uYmqswFcP_Y zHQLtUM~9yjXUC;Ou1QCOKEc^;$8nl{mG2Dexzu_}Kq~2@p&;nI@T4ObzkqWAqu=cd{$aT8$RjMqt|&XE=YmsJXa+CHsn` zWu5yia1hwZnhmka8RS0f?GtwuJ_&Fo8wy2&C+*$~eN9;6ZAH>lzm@8-#H-262%*c4 zrhjgB_}-FW=PEz}hD2R<6#6*#Zu1u z$l~9nq0LDMnq53vp3it0V>_0F{n!O<01cz-Cv4(J!|Hb3;%W2%k%Rk2G;~LkJ2DbD ze0?JrCEC{Wk#^J{q~;X>OBI*Wi5N3kDpZFu3{J06L`OPV1i+e*7{i<2>X&@%(SQXk zO$#YYS?0JhEqxS(S?NR)w6Az21Wr14`a80?@?npf6INbk)EnzC|eOW_Ck%BEk(@}$L*%HAp((tQ7X$Rs7b^@7vsI#x56JIXX21JLS$w+ZO+|xRKfVM94km^^66qS@mF5MQ zbMbUbjn^z_+9uI?c})Lkgx6!O;aV8Whc%pCWS-fn+ManK;q)mnMQTE=KVVm#Fpn8*u6)|( zWoR6R#+oxD@gq#WhVJol9RmMq8t32*XbZXj#cJj!K%PgQ3(>gV#9%sQ4Za(gs1e$Sx|wx?WonD(Bz* zHd2v&aK;5_m~@$<909r}7KC4?{WY>{&ARdOO%yDC?jgW&*k7~^HvPh6sjDwb=-Z=U zCO3WNT{tN&UNjoIr_N2qUg`p$L(6KR2Py8(u#mmm?0al-(&b6aETvV6!E|dZubJeV z^kb}V3%&;pX;bJ2U>kQAV*F7dxUn?{xnm=FHwyK3Qmo^|4;=YyFdR}&rMT;$Cv}jKV?y(&4>n!DQ_PM0{ z*a_k5^5fiy3UNFcjsLG7D)3O^)qDS#k{v9olDtHRG816yz7FlMa3_1PZ}qF~TOap# zC=o%FJ?4tAJq$A1lHXhW^kjVmLDpvaW)XyKY)leL>tx<$7HAV+I*L0Inw)L5iw>OV zPac9wlJa>8KT*$`&=yg?c_}5q+c3@Y&SW~583oB~kKOtjSuc# z+}&`FPwe#6k)JqFT{uHj5ogl}i6hNL&p>!n*%yYiY4Md~J-FPS$(lzHfi&)^Z@~tf zr____j_K=9o9kctQNlSe6}L1jVj5Wu4A3hwq`6R4Z!#cuM6Fm3tCqy3pMOB#^P`oW z=^_Gj2nYg(H@MS1;w@Hgawe34=y=WPt0Z;4FH1TEouIucEg9ot^n>7+&>9r=#HC?O zv=24OO@_x*XK6U`{;tHo>~_WKo*gqDH%m#ja zTG}{Oj`(UH4QKJ=ot7uQQ(~OWl`AWti)+)SXWeq`>mTRzG1Ku{UE2X{AxRQTZFIOM zQZF zlGb{{fHW%ioVp*nv(b*lYpwcJ&AJK1>1$FwtxdcNHK-E&jDH}Y2 zjFIDI+0Srhi_uCa0-NB6l`^PQGTwA3AEieHDaHr!6qA-)0V6-kNYvCRVsWr5LZi+L zPsHtGWyCK_B4{U92&n`QnapdbL`It1Os(Y12-{?sN`1qA3hMQ+OV$3MBpSDJ#$BiW zw6ZP_Ll$-#7`FTCs{Xoi))*Rw2HC!<^4(t}T!!gR6K?B!l@AEn1l`z@{3`&}*Y|kg zWaRNJ;`YxZ^1>L+&p>_~HZlO}E%U!kXbg34PkC1cs$T!{b=9(>E!BXpqBL!zM~K}# zW9^qm8$qXaF^@JM|8m)|yjxTc*JcGv-0@Sddi%NsM0W^U!bMLFg}Jt?1!1<&l;}si zVr}yQBuVWh^EggCiG4^6B;ECaIw);Q2H@a0y+(i-{!#?tA(9?Wwp{S$qxg~>X8@!S zS-|g^^ke{2;{vBgl~lYZvKcVC_1GbWmr}!LPOfOrFn65-@fQ_cL;=$IsFBx?8iS#(X@IFJ*LvI6j0NCtWCl-T{SFvr(CbHG0L_3Q9FwjPfh_0t{aBawkj-!1{@YB~%W|T15I9s{^t{#v zr$>*vIB2NrN&eK1kUOfaEgng&&aXE5$8n;MaE4yN_!sl-+t&8>JUj&6g z^h1=5oQg2a5(4*R;U_@O1k57wt7@Vz3}OuhhxgsB`d*0OXFo zLjd9H?x|W$0olKKJB4;Ba;zBK{5!TFcu?G@Hrr_Gt90P@B{78Rl=}WTZ0}>C9}0{0SOW$mUw72i)9^(t zU|&|iA|@b_+1fq%fdri^lOM^KkU95u43gJW|FkT%hdKpCgW<# zO~y@mD>x$?)g>?Z*FlcFZH9&bVW=GJy_MJq|AaE-wbTyObL+ep*}&^`>6a6kibQ^OhxcgG|K!~MoY88#y-O4x2B4P z5HCM8FH@3*C{w&KzDlJfloe7#JT8I{o0tTYNJ^hyDcU5{eh)rR#;m{fara?86r1SL+jjrG7)X zDcNi>dMfLB#UW6!EU$p6=X2MIDfbJ`xab+dx&+(iRtex?HWk)EJlUl?P_Y88f=m=& zI{kJ>>Sas%rvn&TjIDo7S@F-V?^rJAfgt4G1bmW4*hb54mG9Lt)2bL3BGesoU^GXt z#Z6nRSEnUxE~LcxVCLPMRx;^gL`H);dbw3__&b2^0KQF>mBu&S&!hU>|G}eU=8=;4 z8ro*i=X3CjXP4l5ePx_IgYmB%`ar2{euFhA^5dpt2N-9)dF*yXZYQLOFI*0(n<MPc(0~D zK_Y*|cjBj2LMwQlWU~9fQLjx|!Fc2#Te=Dq?*qBM!g^FLH^8lkMR-te8v5N%dJ+04 z;6e^aM|%d@{3~$q-#cgS=aiJwLh)$v_ROSFaJMrpjj4X=9Q_HdNKiUx0mfcZO5Zs2 znoiEKu(W$qh;dkWc}LpcAEkit+q&~-&@w55w63COHoVg-OGKZ%xuIZQAs7J6#eDc7 z{k&YUrKp>(kSnx&dw9T%%6B0SlKYks#~ok<*3)oOfn#Y#nY<&uogreqFXAR27EX2$ z!e?eIZPc|-CzL@%@$#{PySkWi4j_c0mIngF4VB9ohOS_;0^_Reg6uay5HB|fzA@~2 zPcow`WQ!OB)k>tKc%`>;a8bX}*Pdg#{XFD@(nlZfA8OpW52b{l=Pt~5dF}Rm`paQ~ zOIEmgR*o%ZIbwsyiD4$8P{lB8WZjU({WMY%dM<)>kEDHSXm>8ca6G(y8HL@1U;ERn zZ{kF3O7vAbYG$-IR@i}z&y#yY6g4%@b$q7`y#fY(kP=}y#z9~$A(g(gH?xve2LU^4 zTitj8O2IY929%X1&>x%tq41vE!S6;p+{;%)EgP)iV-%1;D{)&^8gFyfBePy{2#F(02T&O&Ls9jQSHV zN=O%SWpB{67MoH_L~2@`2?|@I&5%{j3bA6OwhmClykELTqDONKrthaJ;045d+mV_K z7Ps}Jc1M?lMwQL6u-@*I2;!33#1@d%Aa%>QJ0jnPNyH;wJh}uGXZe^bNN*9wO+r_! zC~Bs-s@?l1E^27kPPlNLet2jD9x;mNm{KTFyCb&m&=(p%f)i;iv>L26(4ULOZhjZf zNP|zV7b)~ejzi+n9boad-SsHTJ$s}3QSS$zgw}-az*2*QY2)*Ht4qJ2{iBkLd&QTE z0;w>C!01WOy^ST5m;J08U@kw$CSQIOyMgsGEin#5MdU(~zByz2g>Cdp20(3=o{4H4*ctm?tixI zCdT1-&z|>ZXW$1sgYjj`bbofQyfUM}Bf(pC$VdM|hiks+Wij46ja{pWU1uyJ)JeP7 zELb{jiG5-AWu8LES|wJ4B&uZ>+wj!oKnY9N^VMc zAC)(QV%x|(%t`$A-xHFYUfsqcBC2*|^3%E0c*5;iSbAUhFyc?{(SvT^plqltlVpl^ z_#F820-(M;#~p8Rp)Wo)9Pg}l(8AfhwM$P2O+- zN^GuksOZVHk#FFR(1dc&S_}qH*)%6s$5OG4xlNMBKXm>x#g ztt}iL=-#1xFfe>c^@|&OvczrxM@iT8_cY|3F;Cp_&(N?kZW)@)5{BK@A3!|zFT(7m z^(P*t$WT-88eWsZ`RiZtiESeLXR5hiDQ$7kApo-X`dWQ)$6d@8ml!b}oVPOPvx z*;O6d!6MxmUg1OvnY`wl87fy#kEe@4xBw_ow;R@zR9@QyXoInH;5Vx9h|_-G+$Tuf zdr%Bj7H(x~I^iAH+ca8QU_`saNV-@G!u?|tH7LQOIH9dh34#>wOq5A%|^*)j-(fuqzV2cn6y(VCB{ z4=8UVX8s+#9<-H-j0=AL^_Pv3RRg6_UkwC@1wdl&qa3;)qOTpb*1=M^5 z5LAfbrKgJ^T|C083Deq8`5~ltD&b_m=VU>jpi_#hI=j4_8?n5BgIFoG$fi{<0u1lq z;i=~IDu4)&vx3SgHvsB?+%0r2ArF>5%0BUEZT|1W^@JCwwBP~O9j^$wvyuS=@xm}I zMd$?m98<;a$)BJmAi%Zxk zfV7xqlmqrL*Lc>)^A=#|9Xtb;Q4yH1T#o6Ab!14rqm@nrDRSF8tS* zBR6T%6*t}5#!+=pbWCyH3m=cXET2O0v_DRE3I$xJAC~3V>REo%)FevG~Gc9<@XoeGjWRK`zpo zbsV57@!qN*3g8o8ls7=@b2t}-W&4YN2ph@>?ZXLWQEG$1;~&poky2v?7hh8+-Nx$@ zF+Ap(G-9gx(4gym3&0zA?x{*R!*mNwGcqz^&x{4tddzDB42yc}h>}a+gI~wcjT8=X z)289%NVp3x+p%u>=d`JsUAZ|vf4401e}in=Q;!D=s?M=jkmIfEzl{Y=*R zU*?wl{t>ORD5)}KFJe`2Zc~CPS45x`N0~Yx6{^f#IEw;u;2&9DU!wFCL7rQL0u;gFI;z8@0E%W z*crOCm=m*PV^&JCO!pV1XT8bidJ%adCY09J@=Uy7cDQnI1|m}i%nrLTMYtc()n3<} z-Qif9Lm!c5_AhYX9dXha96owN=@g%-=+t3#!W@3$B1JKv=iUH&g?&&ukNSn=s(sfg z7OoA;34=8Gyy%S>el=8-K!PDHIG`-}U?d8a{qc=R-F{$(KM|fUD>K2&n{bt z3AlH|-;Q$pFLX)^E*4METsYyXv&CPI`tL}g_(Mrn`RYGe@H!~vML_3@1Wl^;@w)}` zpo<%tJ(yWr30OOGPhi8J2IGo(&qKK@Y9um0kQ79S8SvLrZI$yG&TOhb8%-NhPN*xA z9_3^PW#Y>$_^57@QMno;DN$|R*u&R~c8w9un-sTmH`yE4TlzWPt!`LUAUlVgYutXI z*L*h)BFhZ0+pfZS!@j;@-TwE{E%dL1eAK-ZW4LE5iWf;z8>}GYA4HsrP zJ|r8_Ld%=?`mrrVsv<0}42v+O>tyPf$=k;6Z-080lnKWdk|dLs-gCK)3vj^;uJmG` zu^$tBy4Ee8D$tF@$=5QpV&cF6VZWECu`!Z@cxUD=&67%kzTsTdVFb3pzF6pC_^($0 zm-SR}CWBGyr`k~81C+FhFl9$&>IlK;r43a2-!ER@uB%ye99x3f_r2j`IuE%Slj*O8 zWu+)Q#1-39|4WCt3B5?Axp66D7Iwj(s zt`P`3r6_0`_lsRepUIr7@1-L!*YRJJZ_CG&i#o-kt7E14=^E3nM1-<%;bG(C&<+D& zhbZ&!SOPMuU_S6$*G+P^q*|FLL@$o7dwvJCfDvBjo%N1af&^HTq{~Yauhy={PyyA- z?eA#mKJXdbuBqYBb&1vs;>MLj_8FG_V9EbNUQ0Uc=dJ#EMVMk;?0IAGC$r+?(BQVx zQpgXs{vbxM#qZU%XNFTQVHbR_@DR;Wp@xC}y)W3t$+f?g=$bTYHjsXaC3mfZzJMfs z@EJ!$74@P28ViO8M>iUSaFJ>Rqf2vNYP)9ya6)#NRilRK65ikDf~at4h+o=NbRoc? ze&Zml9_kHr95g_y$R1~96+B_n)3jb!=j%HlP;6}#Uv7UwhED_pDi}(E(o-S=pnQ-l z1*!4OqJ|&K(j-D7(O!V1x;*f&;V&o+BmCWn+y#441ooGpr!oS?t!ez_*TLLYfJTq- zFf%_;AQuSOOm(bqi=qT*+iOMvg>PbdTLv|aT>yCiGAa+3CG$GBSzX-^41@^Lj=Xhi z2MgrDbATKj1a6rXgoDH!UG@|6Y=ev&J$C3n`7QJsAxBl-v)X?7e#fqizXh>78iGeD z7Hcwt>^BO-(V*Z-xsTy<$%C7FVyCXe~e@=Vvws-`!KUP-An)vBaZj^ zWy=Pe9#Z2Ym_eBL0;p5An0b?4imIM14lQv!wMHcDuOUczVc}xc>o;B z3Z~-2^_u(e<|sI2A8pp2Y0l$VnIk3{DQNTa=$oya_kAi6hAy+0eVb%J2ULhkXUGH)nn3h?RRo5XH!HdJV0)ZEXu^JU>}a=e5bb_SGgrOk!|8;_;9kg_ zZEOs?hRX6Iek0%dSE(bZVMsV$BM^~(F|gQHW(5kES7?G z!>hPyVB_}C4q2LJJcl+6o#Xy>3_8lZW-C z5Z1L;y{Rw59Gs09-2`#$l}#@}p*8$$Xrq9@-Wz8enahNU?|@+P33;1Rv7`gfGn~08 zN{I&B=wYe#5ot@%8SaKa3aj_1h$`lIe#Je8tpMWed+bIrVHmL&WvG-A-`!J8A z1xh&uNt?of0HZ!`)rFF9fz1oGgk2~tKV+Yc&sM}cezPR%S&SN&mdbH}M`Z%~+5wp+ zSHA-1FKEl5qPXT%qxl&S=PKuIivKYPhedM~#1Oi<+PXqi|33nhbfBu0f1S%+ndOJz z?};ePn##J-g$u2}554jjeQMc`ZY)XhcbdpU)f##dTJvxQGu$r_FsPmwByfFyPR5Z* z=cVcCDzXgpp!=c2Q^xM2TyrSg_m#qRTIKgAl8#K^tyob|nR!;%#u^H)uUHSsqvj114r4$=kgECfOXSCZx9KG($53$J^M9IdmYL;~ph!*zkWXq) z#ON5EBfZ5$<#ll6hb7}s@O)xQY>2J#AZpQk3)L;&K8WyqS&)HJ)dOWF^@RT~U8RRo zKe!#Hpx`@TOeG5?S4(z<%nlovT+D?wLnO=6AG(qLH{!nog<5mgQL&1TrT>x7(f3#fA&7H$?< zvdXX(VEL?xlyt9vSI>C}KKB0IAu_XS%xuNQsmT)RiD7d5nf$motr2$JCJu4O60!|H zrb%oRf8}qm20`f#!=G+QZ8hvo0DPajW*c8)*}9FK;aG^LoY$JCc6Q{Jr-U6*CjSi<5bApH(8yaz#a=%a-a5F$p2P;7(mjv{I=i~NM* z5+?9&;g_{B_scA8%QmxcUx97Ngw8?IcA=BW`u?Cxl7a->V4|jyU#O~afJnBsF-vmGBX;6?3?3`9 znqxHN2WWgLVpf#Ki$>?KHaj0+PbfL6f-K@;yerF}@V|=KC^;HRQUpvK^9$FJ*dElF z55f|*aykh_;vHqssiNT*icji~sZMz#k1xpr@PG?|X%J|H#BlwU^!JXksjr}`KVxF>EQf)& z*>@Hdr+xX61t(D7g80UR#`%9Fol|(FP1A*A+qSb~n-g0zNhWqCwr$%^cFc)w+qP}% zpZEJu`=q<8s%zb=uLp`spGa-ZRmB?3;Zo2*8`gT2^Mf8(XAG??bH%a;R{ihAY2`s_t zs>PFzjpUZ*V3)^XmUZ;a-4J=Q>V@|Am>X4F?J+JV>8x&GF%Nj24?pN+zW5$JKT|tr z@)46!!hmWOhJK%IG?4p}_iim1e@<_W&es6rB7fN*h+wB5HDMc(BI3UTd=JTGiPJ#- zYW6godmyWYO^`(u$U}XX#&$&b!-*sbk{A4TWwynBoCWawm2BK&EOc)oRk8OIY!JDD z3cL1Q#-rRb0T;6jo?{0N8tS`QF&pqaygDdHFDATX*&Gh6x)S90zs13PFf1MPfzJTB z?TBM2M*R`}`tzO!R;e^#>ZzAidFs zB6s;I&Bw7=)RX)TLAv&omVV=5Ny!fU;>960- zQU;716sLe$vnX?h?z<$!{hO%2WZ*QwrE!zEj!Fn~Q$l;??9cR{&#-3!*W!R-VdbjS zBY$L70wJYGL!dkK6=}R?Lj5KQo#NIf%9Dra%7XJAdyWn*Q*m z64R>%vd34jsOX(_dph%L*;P7~h_>g~gH3tS!TtY!AtIkVog!-_HE*v1r#Jdn8pMME z_C_uCci2(ywRl9Bi?5Wj-Rj8|ss5;&uaBiyQwHB3#AkBC#9TPN8x?Eo-SA!pTDfSZ zgY;iwp+qg`JoX****`9yyZ|5!UC%E4Dx&%KyG2PRqcD>G(kxyd`wOuHLHEo733x z5t?0GkgzZAO;3)i_bsXa4Np|I9P~LN^v%wEa$%-+SRT4%wQ*@q22PzAuX-WImB% zO5NaPr}QRaMZ+L9vMU3R>T~{yny9lwBz=HV?%cC8@w1DHCgtGL?By9c&kYxGNHepD zl>*2}OXSOA=WGSOI*@r(rR$6+#BEHP9XGl}h&~}a9&#GX5~Yh=c^_yvOPkGJNK`ya z>5amgAjT@&-Sq34YvnY}ut$*DvEdTSL={i#WOV;z*|biz%lMq+W#Jszffg-fZj9uv zR%{m|KK@)+(l|qDup$m$iVZFyq|IZ-#deIGF#o_e3wyKWW}imMnRAe1#wV|P+)5Z5 zK%fA*QELYChYHy*D?f^TRQyv*u_?y0VuBVic#)0%lF-RC<1tw07B1f)iBN`LseEQ| zTnsW=D_w$Mj=KHIycNXEicr|atT=y(*LzO-M=*8$c8Y6O$lO``S$@J0Jn{{C5xWys z0;u=a07m0vdI>FJGUAO!2RK7hJ2|Zi9-Q=q)^-%4-FiksXxAY48=Z_-Z{LC4WG;Ad zN{0|~p=P%_*r}?tfAUNP%fxUpIw6M0PbgF}GjodM%oiw@7l=a7F)`9YAj+*|z5GKb zT_ad|iczI)Q9Tk?siZ@6?@!EtpZK{5s>n*E5`s}TNYN7paATk%q)t>oS(3QPyJ%rY z=LfN~`4RI|P$LRet&T-^=N`}pmAe%ACh=cHGNKFD>8(CNmtv`*y3eGbth|z5WN{;6 zxR3nSD;6}_#h*(K)?;GW&IvfB)vis7Wa)QG6|}=O?BjRwqP-uOhuj%n8cVH$mu>V|NV7rwau3-){?deERXt zkw3rBU@p*U2ld_iS(C(Le@>^KIeP4o(TbJ8L*r4+Y;4N;Mw1z%@%^?^x)|1FAK^?# zZvLUyTUr?bz)q_b_N3=k8T-bVZ|&*l;L8|Lf(tB8xNYki?rNKI*Q-s@22(EHWUZZ> zGZoYxRfR)j2{yga)ouZV_`^@kC>p%g9_XNtw_rIp^U)v{=7%ZAR+JRvfh@7N{A|Gi zU|z7Xodfl9w*N*!sB4^$jdhSuSrcZI?g&GvGfJ2Ig1`3gPw?l+xdB09qRr8P?y~1m z`Z-hFx^HR>f{7 zndqFmJ*o-n>KT{1dEc=i-+Zzdk+YwdUn$O3Od5uFPFrKv)hk<3sDPPiniBoVA%WY8 zXk4ofWv9tt&ZwwEod zkm|$oT~v1M0HhrP4*2NkdVI6A&_@Q-N1%_+b3xp}$;N&MiOi-TcyYf5OsU~oQwV5G z*XP-}t3MOtcQ5g6`R%XV8eCv;hvhP|nkq0gLnlIfT&XM_6HbOaB8(xG-%(Xv5W%HP z3|E!;W0D_19&+pMF(g!OxeHuM=i^AOu~zZ~4)Rfq<^lxueT%d-FTkP*It$O?%L(q@ z9x%!md|@l=n`Kor91GXs6ykO^W5z{%{eAmnqnU8Pxpd_zH(1c%){P*7K7+viO`G-U ze`Nd;Kd3eOaayH;t*RU!)Q$_f_qR^j!nO7q=-IvDh}X+%7AF`^x)OFr-&=BxltD6_ z`CCxX6ZLM!4@{J?s@ripb?|c}7q0=eV@;rb*9y0=1<3cZpOPHq8{bW1W5Fj!lMGDp z@Un5Yf7P6E{yn3zCoHzg;P*C>t5nS2e_Eqw>SCwHl z5bg@x;_u#ged#%PK}rZx4w<62fNvk!JFvjci8sX|7HidF70@MQ+pzww8St)rcw407 z=oLS4b>eX|{eH0)Rl7isEpw%%a^7C;4J+Hus`d$B*fjSKM6!fE{r@ltI*7u|3Cv2I zk_)5=O7V=9_ge9`ND)lJ1}nqiC~j&RO@WHX!&Z9v@i)bzFd5L2MC}IyL%mi0qNRA` z6RxIoll-K~7qE4*wrxlXP5U{VSd!VS$tCl9Ozd6Gq|6nGQm|h`Iw3AydQk$Qi&JZ@ z@EI(rg_Z6D{AC-h|9|`1i%qcNP@dIR2l7wJ8S@vCTH}SoaJ3*+TsXSKc`zdx*Gi3x z`NwX)++%)u8UxKVm#K{z7Iy?#O@dt8Hhc68y|+%*w#>_*5>>I2zWKHz{Ddgy7Kw^J>v$H>svvXhPkG zYr0Sznn^FaOUpK5h;oFSDOs&AhY_D(W(%l*PhhX*lCO}<*btKFD@N;QBd79Sb}zvEoB48n^TiiBLTFL$u$E=d}9e&zLm`+(3mNE|oZ zqV&^<9%`K>RGE%@j!#rEHh`NLz0$#hif-gor0m?g%xi1npy;4?zSu@6abgGvE$QRZ zO&~DQBAt-;+lp&S3V1#cK#8$YMx3PaWl5MMy&La}@6glx1A{JydjHyyT>q=gXvq7S z?zr@Cs{C9*;c`{1sePR}XWcoK8o&9X;HcBr0u9Z$gq|Hn3YK?+TBSzG4Mo#m(FNHj zlH*pzMzUsHr)gXjb=cV?ZQ6VbQ8{gLn!iv+Nqs^RXOX>>-+mx<)*+x!$JDM7lPiec zNl2tuMuwM1P*P|&jDSnu3mRfb2bHVPJ!0EPSxn6WJ8=*s(1Yv+vh`J6Ok6xE7vH?fh! zuYq3l+=t6Pe|ni3_cEd^gygknURl+30tlp;W9S$-D?h<)4a)R5al%uc#&!gBqoa1g zdLcykCDCfPLaSZJl8kw)iJT4>?w%W6RdWPB$06>hfjMs{^jItfax(51(t%Of@~;4UGr(A)=h+$1 z*fMLXnghy*IgGCVTCu%ElIS*_J(K~O6JjB4fMP@fJA~S}09C1LO#3_$!{BPO%Ls-% zUB~Qum%DG=;M~gy_HY^ud=(i{^T|Kj&G4H`z&oFexH>JqfkQQvA42_eS)RZ!%_BmF z9oOg-@H)|Ef8f~Q&05|DLa^3LcA{(0k&mOZ;mDF<7w5tLQ4mO=M;B)xKp%&IyQ%_a zJ8?Wk^D7{zo^dDGxVH(yyOT2d#Mv`qNm;qPc`j?_T*t>m97nSN?!JUaaG~OH1p`2< zQ9pcNh%aG7XPf;6+^kg5>4_9ph*3lUUmBtOGVH`H5kr!u-i{bSKpKiC8HHAl&+k4| zo;N8}1*yi9uD~90A6rZ-$3|b2xCJum557w*KS^AgCB~W+o?KVXDn!&UZa`e15)Cb4 zXs?=X)xdM-yI!*9j?`wiqpIt)gu%o`lw$zC)_b;PD0+c#YhyF+=`1{g zDyw?VxM(U3g>80CFJsp^r~7Q2c(%L^`mjqf>6#vRT*I?G?9QZUTa-G-lnjTsj+u=V^il+= z4Gpqf!D`}PiVuOpo30mMFEqHVHb%uEf`HUD~*{fyqA?Mcz7ZOsM`zqxGY0d<_ z`t8UwX^W7i`dOt{nhZc&Wc)Jt@tdycC-UETq?bo;)OW7LClFQefhf_|2+F2}7t4QL?6Rhb8JQj3l{2D^5el*A z{v*l|6qK_X!6o(p(Rb3Al=)I)&#EB~Bc}brXeZ@&Ug)nfidu?HZ;zCs3dRX=wxD+1 zc0ny<@|~~4y%Oa$0zW~x5{=b~&NVCJ&Cx)|&G;Akhg8$@CT9a&GRFgYSvF_}!IC!| zSkZS`>xov}dQbD2{N&UkzeN>mPhD^)@#uCpe_cw;dgHt&%n3l z0^Cl-dx*rQt3~3x-4NPF#c_ASK_4e`FsZSxKrILX@r$VPNw#K&cNJy90}4?w{;t;CGCZr4OGw;p@>N9rJ4fcgDIq!=6dE-w=ONq;HB=m z{#%fWQiQ3;&!}v~_w7Hl<}>(VfVgbyySC+E&{-(Nw!$8#E(3*J7I4T2wQ5V+<{$g) z3dK?C(XPjQK+PX%$*c)cTwe=)ejV^_G4NVr*imWGJ}Jys%XjcKiY3Hs@%J3tumRYL z65#+Oa>eMMw2c$rMLJMV65c+}uY20BN06U0Nb=?J37A+l^Qs=_V4*#%_W&GVr z|8iRxIqAHurZHU%hfKmchU$c+`^eR_Zwv$n8YO7S}*jAjg^cWoL14+D>kiO zh&|0Uz8%F7is#Ft#5NMDe@tb1iseJWvX<`sVDVjG z+7>2`=9p=+kMX_yiEss$PJxz1aF3y4r8L^<% zA?eQ^Qys@`2{p&o@J`TYL)<|;NJFw?_##aHM;iu}d~b2zg7lf? z3BZpmWn2nQz~^OKk@!PNFj)rov&rJ2?gSe{NrKKw82O*xt7!zI{6>D{pxgoUJ8GZA z8C$sm79}c!hg*3m~1p9Nm=!(VXlh-s{!|c zM5jTOd3svhc9S>xXgD}<6`O7PFDJj--vOV6n1H%Cy^bcm!MSeT-prmccQ(Nb{m3=Z zG^MH}26>Qqq2AaHET|tpIlzls>TI8u>y-_)X_DnyYwC>KlkV66+rOIedA1pI0cqZF zLwt<4t3;Fff8yHM4wR5PS><30+u{J%ebuor@Up+cRje6+$NRUo+U9AlBFpw7s*-H& zE(|JJT{YDLBIacl&9W2vPjd|MaTL2$y0q=uZ@3r9Q`CTJ6E%E zfa$|V(HeXYQ`2O}a-C3ppOE5083(Ur?z@QyTo)}j_Ct>LCfQ!SfU$(v6*J5H!oHu- z+Cav@-lI$`m(~hsU+0NM(z1fTxnn%#WwJQcpn@Dz+2Hmw^S*`*cTaWN8rMrpt0X>w zV+-rC-rHk3><wl;%A80@kGJH_gAX%0B`tGyz#zMd}dxG;qu^khW!F3 zA7sBo=5v)fRXbdA=-L?oQ5?Rj;(khFF7T$>zp&F#_>k*{+b24e8!h2S6HICvyqk)f zy4^y~^YBpa@aGTj-E$6_P^i+mb#hD^m4CD=uLr~`eI25%iRbM)<`OioVU?1Pa|}oZ za+dk-crnO@nr!opN5WVf{{jpQy$dd137)6*f#Nlstic~k1~vpQE4BQ{8k&&n&K>;a zhIo=-VwkUjbjw}&m!Su0Ta?oeIDCor>z&2x(7N#QVmtPfPF$%C9OP(dKWV_a7qA7X zGYrWvcrL*pYCw6(72iM{_}ln7?SM=N@H|s<{3ItOkLN}&giGq(gatlm!^`Jl z=ws(vyH8?;=vb$z1*lcY8TM-=-v3R&ti&*>X-C+7&SYI;PW}qN%piRU5|m%qcGQ{k z8MmFb+DEW8qOVI+wifHpnAd#jP zp5BZyv1HFWt{UMM>=z+sP1q<)Z#zL_p0 z<26;o3MfNsH>)xldhcxW|E1BSWCwfDc=cANA`CZy%Oj#Rz_ zyqg#ZM&2dXZ=!?2Np0i!WswfSfnQx9LHggRI!VdKNpZ+oV*Rh(@#uNt@4aKt2TKzy|Y3(`eWDdO}!}kSrtCZ3&_W( z@1oWnvb=)yRZQ#>`DI$mT1@229(Z%B*a1aO;Dh$MqVEuYEcIq$1Cmoa{g$nXF&D7^ z29_-ZzfL<%WjABPzy31`Ez-IpC_X=R{ZcmiZG=SidQfm!4joy(xCeAN7rN|7*FZr0 zu+CE3<303hW1pj|FQble+n-HySV}8hEi{q6ORN7 zXt$~1Vh6?k4!Wu{B$D@I<4V*huavu*j#Uy1yvTZT4T5h41jPSCy#5k~X z*klEBoDA>aFO|8_sgJ?wZmCY$sSOLr87(~tSDEhQW!u=Md;fot!sa2?HfBjiu-XlY zo#0QPF%CFnl9GBCrCU(GF2#VJYl77i4tn->k$>qUU9j5c@*_XAPLkC_3H&$aJrGP> zie+OFPxXvEzydFj*Tb%_T1s|sAB5|j4{dqWX5hc!k!V-TWCdc4+UPs~KsZE_Rk9YL zyc_*1^g{uUA?W~{uXRIhsJ<>|Oe#LvoO`8SGZhQLz#lT82AMw-h&+K47owpDMLfQH zdgE;X{h;IJ2$(zxA2Vu-I!%niGl+?^2}6N?=aEy0Hl4QKydG5=FQZ%sTOMz^kg*J`|{3a?F&wn6=cgfaX0`x+jH1hu72?fp7tj{)0S z{k$=dJhi3q)h}ib z01hI*AXU(PF`jl@*MfjzYO86M5?On0z@sW*W1llHQ1Xfy#qIn=$%S5poX~+s_2X2So5P8onD?v$@8n|><@_((m6r}3(cf;M z)_&K(b&}z3cU;w0{iDSCwTWQD!KOqU+B=tT*MdGYO5n9tL37i{9Zn@=NEGLg-Qz2h@aPjb}@P-!wBllGM@>(J5;*%>p3T8r4`E(wPgQfRc0s=iD%le+b2Z6LG}09 zksPFoIWreC1A&AVTf758pgrp7^74ILrTR?@04{&>=(hvY==$5X6zH2IG8-C)qr|5d zbY}S&OlHy}b#~|XS78a!H}KhJMTXUgb>z)LWQTW=w9Yy37GM+7bR$ybeX{s!`XPf@ zf?rQ*PH1u3KJgoVDbX`+yR%LGCS;cEj&5y-eQMfKcs32oiW~3&Pp%c>3jWs%q``!F zOd4rY3@vUh)mcE1=dCh4xbMq3O}C*<{lZ2rbQI7TtMDl)%^Hb;Vm$IU*i)b60BhV~ zPBHBdzCjr#;ITsZ6w{K_LPuJTJbt}Lf6Rm56(B9kQ};U@xKTvlOBAHhJhuD)7!GNg zM!x+UJA8X^&9V@fhMJ&|;)DFm{|l$n3PP zkK}ULPdgL)$9qD6Iw4yPdYW?Spc4tKrYU}I8$z7R$6>VwKmD}~w0unBV?0Z8N#ag& zE54OANdRc5YoQZ8l|@-~fpG_z(HlPLf+NH4YpHyzDlD>926PzZgYLzVBuB9o@UPJ{ zI4@6cVsVB`c^vVSGZ#(XQ+A$)={bG@L-%9`?N@y^ghh$Z8Hoz8&A$doc%;2~9I?}# zhz&7Zat(~m__~sCDf}|QpQPH_^IM?qVl8Tf^>L&`|q1c!1-QW~UMTSU~ddWJz_ zy{sAs)nxrBkKy_yFRUF;7zg1W<0FNJ13OnTlTr!MlNAvXd6A$jE0aL!?Y=cIMH@z zmhp}2A>RbOF=9SU1tX}GZ`4z3L8Y8`qbb8kx}Ac81anEkLpdD?e2E{fXT;MHo|ED! zs(UmK-0@HYRTjP(PiQ4^Iei~aI;x^PqdJrBKpXc*2-YigXvCk zvR~^4(a`+*56H;BjmURs5YK`)_u`&3&2rqbkdyu(m!L|>dHo6~H&Gj@eZCjmpgWfZ z10-pAIInBCWA1N1uln?zx5aB0yCRxBP5x*E{;I(GHY&_SHnlB$Ga^4q=?|U%+Jj~I zJeP2<0Umqg;MBLN*ZX2SdkZhjRrdNWR~=q*=lvUN@cpm*vnMN(B}AccgnJ1_S{JGn zQ^X3eoY|Wh@#T!HkQ(ZS^r7s zaycLlwBO&-l-2~FE=cvMW45tH zQcAs93F`0g%&t>8k*kpJ#~`A=`lu|EXG2z1M=HWkWOp=+q{$d3l2YkUVcy&@KdiAl zXmIFxZe#WEx3rLXVyBdP=d&Xm!1a{2Dgj)Jur6KDk+Cs+8LzkX5IVMMb+Gt}k|KAC zNNpC;uo|nQ4kI)9GePvb1d$$Q1h)jn2(Ci$a9g!SCqwX&^m1!0HX0}$S|_IrYcu@& zF$X@CLzVXng%(Mo(6$tVvTP)|xVY9s)cIF-7YMwXsB`)s*qVCB1Z)tnFX954@BNLp zCR-M)yIt=>>&Ut|M1gebdF!SrmmK?EL|#IeVBq5ZJr9NXsh&zl?k|Vy_pSFn4kx&L zs|Bedt08nBO^=C=o-XyF6=&HIbY!I~gC^55 z9k4S7KMS+%UG>WCx2VrXNP%hJ#=#TGSFVqGq%+3l=jIznc-*?*w#*kxSi4&VtR+pa zZ`gViU(;CYxn!H#aUusq=EvFkLMp~+WWW)e2A0wrtJUq{<=wlyW#ennHA0rdeOd4k zkMrxv<-6mlq}cl)kC&W-*Ucy!NzZIAkL*1ZX^j)y&LhJQ_naZ>@7mGEcIh-a*c;Xc zjHYYG+`WFPnC%0WQ>Jn6cz`V7 zU78yOQ`HWXS@BU?TL%#H)VYA1XV~^-ZU|dY=Fh?Xx%l}hb4*O@L8edJbWJ^t_AIzj z>6O!p1l^XI5Em%Bto+p!@QX(vYPtqII}Lm!u8gm1-OkcW!4Z9Jpv?6M*=gzhgLe-0 z%W~*zK_@A3nu$I)DuHgZkVHX`>eV?9(a{2&ylL3_eS?T|wUP4`h&5rQ?*Lxj(Jq}n z6-GeoPTu)CTdkF|y9$y*pNppv#Rs6jV=Zg!&BbGA-{VKI$>8j%JNYFubFL&-4=uZK zqAtK!IFXwAg-ZW$ZK(7iZ>g!LTdqphiA~v*na=XXDysRDQcx5c$&fOk4~17EN;9A= zYSf76x_u@5&TApnW;PDHQq?U}els<7?? YgTM)>F#ch07ho6Il67-!r=i}E%5Ns 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

?W~Gc*^Ru%3*ce|{EVR!)Pvo8K$ekY| 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 z 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 K2%zG7~yq3@~=SbuENeX6~z#xR@7J7ye{`&6)9v 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 diff --git a/tfbpapi/tests/snapshots/snap_test_AbstractAPI.py b/tfbpapi/tests/snapshots/snap_test_AbstractAPI.py deleted file mode 100644 index 8444992..0000000 --- a/tfbpapi/tests/snapshots/snap_test_AbstractAPI.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# snapshottest: v1 - https://goo.gl/zC4yUc -from __future__ import unicode_literals - -from snapshottest import Snapshot - - -snapshots = Snapshot() - -snapshots['test_cache_operations cache_get_after_delete'] = 'None' - -snapshots['test_cache_operations cache_get_after_set'] = 'test_value' - -snapshots['test_cache_operations cache_list'] = "['test_key']" - -snapshots['test_pop_params pop_params_after_all_removed'] = '{}' - -snapshots['test_pop_params pop_params_after_one_removed'] = '{"param2": "value2"}' - -snapshots['test_push_params push_params'] = '{"param1": "value1", "param2": "value2"}' diff --git a/tfbpapi/tests/snapshots/snap_test_AbstractRecordsAndFilesAPI.py b/tfbpapi/tests/snapshots/snap_test_AbstractRecordsAndFilesAPI.py deleted file mode 100644 index 807cb7d..0000000 --- a/tfbpapi/tests/snapshots/snap_test_AbstractRecordsAndFilesAPI.py +++ /dev/null @@ -1,15 +0,0 @@ -# snapshottest: v1 - https://goo.gl/zC4yUc - -from snapshottest import Snapshot - -snapshots = Snapshot() - -snapshots[ - "test_save_response_records_and_files 1" -] = """id,uploader_id,upload_date,modifier_id,modified_date,binding_id,promoter_id,background_id,fileformat_id,file -10690,1,2024-03-26,1,2024-03-26 14:28:43.825628+00:00,4079,4,6,5,promotersetsig/10690.csv.gz -10694,1,2024-03-26,1,2024-03-26 14:28:44.739775+00:00,4083,4,6,5,promotersetsig/10694.csv.gz -10754,1,2024-03-26,1,2024-03-26 14:29:01.837335+00:00,4143,4,6,5,promotersetsig/10754.csv.gz -10929,1,2024-03-26,1,2024-03-26 14:29:45.379790+00:00,4318,4,6,5,promotersetsig/10929.csv.gz -10939,1,2024-03-26,1,2024-03-26 14:29:47.853980+00:00,4327,4,6,5,promotersetsig/10939.csv.gz -""" diff --git a/tfbpapi/tests/snapshots/test_AbstractAPI/test_cache_operations/cache_get_after_delete b/tfbpapi/tests/snapshots/test_AbstractAPI/test_cache_operations/cache_get_after_delete deleted file mode 100644 index 4af1832..0000000 --- a/tfbpapi/tests/snapshots/test_AbstractAPI/test_cache_operations/cache_get_after_delete +++ /dev/null @@ -1 +0,0 @@ -None \ No newline at end of file diff --git a/tfbpapi/tests/snapshots/test_AbstractAPI/test_cache_operations/cache_get_after_set b/tfbpapi/tests/snapshots/test_AbstractAPI/test_cache_operations/cache_get_after_set deleted file mode 100644 index fff1c65..0000000 --- a/tfbpapi/tests/snapshots/test_AbstractAPI/test_cache_operations/cache_get_after_set +++ /dev/null @@ -1 +0,0 @@ -test_value \ No newline at end of file diff --git a/tfbpapi/tests/snapshots/test_AbstractAPI/test_cache_operations/cache_list b/tfbpapi/tests/snapshots/test_AbstractAPI/test_cache_operations/cache_list deleted file mode 100644 index 1950491..0000000 --- a/tfbpapi/tests/snapshots/test_AbstractAPI/test_cache_operations/cache_list +++ /dev/null @@ -1 +0,0 @@ -['test_key'] \ No newline at end of file diff --git a/tfbpapi/tests/snapshots/test_AbstractAPI/test_pop_params/pop_params_after_all_removed b/tfbpapi/tests/snapshots/test_AbstractAPI/test_pop_params/pop_params_after_all_removed deleted file mode 100644 index 9e26dfe..0000000 --- a/tfbpapi/tests/snapshots/test_AbstractAPI/test_pop_params/pop_params_after_all_removed +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/tfbpapi/tests/snapshots/test_AbstractAPI/test_pop_params/pop_params_after_one_removed b/tfbpapi/tests/snapshots/test_AbstractAPI/test_pop_params/pop_params_after_one_removed deleted file mode 100644 index cab5c0c..0000000 --- a/tfbpapi/tests/snapshots/test_AbstractAPI/test_pop_params/pop_params_after_one_removed +++ /dev/null @@ -1 +0,0 @@ -{"param2": "value2"} \ No newline at end of file diff --git a/tfbpapi/tests/snapshots/test_AbstractAPI/test_push_params/push_params b/tfbpapi/tests/snapshots/test_AbstractAPI/test_push_params/push_params deleted file mode 100644 index 21d59b6..0000000 --- a/tfbpapi/tests/snapshots/test_AbstractAPI/test_push_params/push_params +++ /dev/null @@ -1 +0,0 @@ -{"param1": "value1", "param2": "value2"} \ No newline at end of file diff --git a/tfbpapi/tests/test_AbstractAPI.py b/tfbpapi/tests/test_AbstractAPI.py deleted file mode 100644 index 84a643d..0000000 --- a/tfbpapi/tests/test_AbstractAPI.py +++ /dev/null @@ -1,94 +0,0 @@ -import json -from typing import Any - -import pytest -import responses - -from tfbpapi.AbstractAPI import AbstractAPI -from tfbpapi.ParamsDict import ParamsDict - - -class ConcreteAPI(AbstractAPI): - """Concrete implementation of AbstractAPI for testing purposes.""" - - def create(self, data: dict[str, Any], **kwargs) -> Any: - pass # Implement for testing if necessary - - def read(self, **kwargs) -> dict[str, Any]: - return {"id": id} # Mock implementation for testing - - def update(self, df: Any, **kwargs) -> Any: - pass # Implement for testing if necessary - - def delete(self, id: str, **kwargs) -> Any: - pass # Implement for testing if necessary - - def submit(self, post_dict: dict, **kwargs) -> Any: - pass # Implement for testing if necessary - - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Any: - pass # Implement for testing if necessary - - -@pytest.fixture -@responses.activate -def api_client(): - valid_url = "https://valid.url" - responses.add(responses.HEAD, valid_url, status=200) - return ConcreteAPI(url=valid_url, token="token") - - -def test_initialize(snapshot, api_client): - assert api_client.url == "https://valid.url" - assert api_client.token == "token" - assert isinstance(api_client.params, ParamsDict) - - -def test_push_params(snapshot, api_client): - params = {"param1": "value1", "param2": "value2"} - api_client.push_params(params) - # Serialize the dictionary to a JSON string for comparison - params_as_json = json.dumps(api_client.params.as_dict(), sort_keys=True) - snapshot.assert_match(params_as_json, "push_params") - - -def test_pop_params(snapshot, api_client): - params = {"param1": "value1", "param2": "value2"} - api_client.push_params(params) - api_client.pop_params(["param1"]) - params_as_json1 = json.dumps(api_client.params.as_dict(), sort_keys=True) - snapshot.assert_match(params_as_json1, "pop_params_after_one_removed") - api_client.pop_params() - params_as_json2 = json.dumps(api_client.params.as_dict(), sort_keys=True) - snapshot.assert_match(params_as_json2, "pop_params_after_all_removed") - - -@responses.activate -def test_is_valid_url(api_client): - invalid_url = "https://invalid.url" - - responses.add(responses.HEAD, invalid_url, status=404) - - with pytest.raises(ValueError): - api_client.url = invalid_url - - -def test_cache_operations(snapshot, api_client): - key = "test_key" - value = "test_value" - - api_client._cache_set(key, value) - snapshot.assert_match(str(api_client._cache_get(key)), "cache_get_after_set") - - keys = api_client._cache_list() - snapshot.assert_match(str(keys), "cache_list") - - api_client._cache_delete(key) - snapshot.assert_match(str(api_client._cache_get(key)), "cache_get_after_delete") - snapshot.assert_match(str(api_client._cache_get(key)), "cache_get_after_delete") - - -if __name__ == "__main__": - pytest.main() diff --git a/tfbpapi/tests/test_AbstractHfAPI.py b/tfbpapi/tests/test_AbstractHfAPI.py deleted file mode 100644 index 6f28eea..0000000 --- a/tfbpapi/tests/test_AbstractHfAPI.py +++ /dev/null @@ -1,435 +0,0 @@ -import tempfile -from collections.abc import Mapping -from pathlib import Path -from typing import Any -from unittest.mock import Mock, patch - -import pytest -from requests import HTTPError - -from tfbpapi.AbstractHfAPI import AbstractHfAPI, RepoTooLargeError - - -class TestHfAPI(AbstractHfAPI): - """Concrete implementation of AbstractHfAPI for testing.""" - - def parse_datacard(self, *args: Any, **kwargs: Any) -> Mapping[str, Any]: - """Test implementation of parse_datacard.""" - return {"test": "datacard"} - - def query(self, *args: Any, **kwargs: Any) -> Any: - """Test implementation of query.""" - return {"test": "query"} - - -@pytest.fixture -def mock_hf_hub_download(): - """Mock hf_hub_download to return a fake path.""" - with patch("tfbpapi.AbstractHfAPI.hf_hub_download") as mock: - mock.return_value = "/fake/path/to/file.txt" - yield mock - - -@pytest.fixture -def mock_snapshot_download(): - """Mock snapshot_download to return a fake path.""" - with patch("tfbpapi.AbstractHfAPI.snapshot_download") as mock: - mock.return_value = "/fake/path/to/snapshot" - yield mock - - -@pytest.fixture -def mock_repo_info(): - """Mock repo_info to return fake repo information.""" - with patch("tfbpapi.AbstractHfAPI.repo_info") as mock: - # Create a mock with siblings attribute - mock_info = Mock() - mock_info.siblings = [ - Mock(size=1024 * 1024), # 1MB file - Mock(size=512 * 1024), # 512KB file - Mock(size=None), # File with no size - ] - mock.return_value = mock_info - yield mock - - -@pytest.fixture -def mock_requests_get(): - """Mock requests.get for dataset size API calls.""" - with patch("tfbpapi.AbstractHfAPI.requests.get") as mock: - # Create a mock response - mock_response = Mock() - mock_response.json.return_value = { - "size": { - "dataset": { - "num_bytes_original_files": 10 * 1024 * 1024, # 10MB - "size_determination_complete": True, - } - }, - "partial": False, - } - mock_response.raise_for_status.return_value = None - mock.return_value = mock_response - yield mock - - -@pytest.fixture -def mock_dataset_size_call(): - """Mock the _get_dataset_size call to prevent real API calls during init.""" - with patch.object(AbstractHfAPI, "_get_dataset_size") as mock: - yield mock - - -@pytest.fixture -def temp_cache_dir(): - """Create a temporary directory for cache testing.""" - with tempfile.TemporaryDirectory() as temp_dir: - yield Path(temp_dir) - - -class TestAbstractHfAPI: - """Test cases for AbstractHfAPI.""" - - def test_init_basic(self, temp_cache_dir, mock_dataset_size_call): - """Test basic initialization.""" - api = TestHfAPI( - repo_id="test/repo", - repo_type="dataset", - token="test-token", - cache_dir=temp_cache_dir, - ) - - assert api.repo_id == "test/repo" - assert api.repo_type == "dataset" - assert api.token == "test-token" - assert api.cache_dir == temp_cache_dir - - def test_init_with_env_vars( - self, temp_cache_dir, monkeypatch, mock_dataset_size_call - ): - """Test initialization with environment variables.""" - monkeypatch.setenv("HF_TOKEN", "env-token") - monkeypatch.setenv("HF_CACHE_DIR", str(temp_cache_dir)) - - api = TestHfAPI(repo_id="test/repo") - - assert api.token == "env-token" - assert api.cache_dir == temp_cache_dir - - def test_init_user_overrides_env(self, temp_cache_dir, monkeypatch): - """Test that user parameters override environment variables.""" - monkeypatch.setenv("HF_TOKEN", "env-token") - - api = TestHfAPI( - repo_id="test/repo", token="user-token", cache_dir=temp_cache_dir - ) - - assert api.token == "user-token" - assert api.cache_dir == temp_cache_dir - - def test_cache_dir_setter_valid(self, temp_cache_dir): - """Test cache_dir setter with valid directory.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - - new_cache_dir = temp_cache_dir / "new_cache" - new_cache_dir.mkdir() - - api.cache_dir = new_cache_dir - assert api.cache_dir == new_cache_dir - - def test_cache_dir_setter_invalid(self, temp_cache_dir): - """Test cache_dir setter with invalid directory.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - - invalid_dir = temp_cache_dir / "nonexistent" - - with pytest.raises( - FileNotFoundError, match="Cache directory .* does not exist" - ): - api.cache_dir = invalid_dir - - @patch("tfbpapi.AbstractHfAPI.requests.get") - def test_get_dataset_size_success(self, mock_get, temp_cache_dir): - """Test successful dataset size retrieval.""" - mock_response = Mock() - mock_response.json.return_value = { - "size": {"dataset": {"num_bytes": 1024}}, - "partial": False, - } - mock_response.raise_for_status.return_value = None - mock_get.return_value = mock_response - - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - api._get_dataset_size() - - assert api.size is not None - assert not api.size.get("partial", True) - - @patch("tfbpapi.AbstractHfAPI.requests.get") - def test_get_dataset_size_partial(self, mock_get, temp_cache_dir): - """Test dataset size retrieval with partial results.""" - mock_response = Mock() - mock_response.json.return_value = { - "size": {"dataset": {"num_bytes": 1024}}, - "partial": True, - } - mock_response.raise_for_status.return_value = None - mock_get.return_value = mock_response - - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - api._get_dataset_size() - - assert api.size["partial"] is True # type: ignore[index] - assert "size_warning" in api.size["size"]["dataset"] # type: ignore[index] - - def test_repo_id_setter_success(self, mock_requests_get, temp_cache_dir): - """Test successful repo_id setting.""" - api = TestHfAPI(repo_id="initial/repo", cache_dir=temp_cache_dir) - - api.repo_id = "new/repo" - assert api.repo_id == "new/repo" - mock_requests_get.assert_called() - - @patch("tfbpapi.AbstractHfAPI.requests.get") - def test_repo_id_setter_failure(self, mock_get, temp_cache_dir): - """Test repo_id setting with API failure.""" - mock_get.side_effect = HTTPError("Repository not found") - - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - - # Should not raise, but should log error - api.repo_id = "nonexistent/repo" - assert api.repo_id == "nonexistent/repo" - - def test_get_dataset_size_mb(self, temp_cache_dir): - """Test dataset size calculation in MB.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - - # Test with no size data - assert api._get_dataset_size_mb() == float("inf") - - # Test with size data - api.size = { - "size": {"dataset": {"num_bytes_original_files": 2 * 1024 * 1024}} # 2MB - } - assert api._get_dataset_size_mb() == 2.0 - - def test_build_auth_headers(self, temp_cache_dir): - """Test authentication header building.""" - # Without token - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - api.token = None - assert api._build_auth_headers() == {} - - # With token - api.token = "test-token" - headers = api._build_auth_headers() - assert headers == {"Authorization": "Bearer test-token"} - - def test_ensure_str_paths(self, temp_cache_dir): - """Test path string conversion.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - - kwargs = { - "local_dir": Path("/some/path"), - "cache_dir": Path("/cache/path"), - "other_param": "unchanged", - } - - api._ensure_str_paths(kwargs) - - assert kwargs["local_dir"] == "/some/path" - assert kwargs["cache_dir"] == "/cache/path" - assert kwargs["other_param"] == "unchanged" - - def test_normalize_patterns(self, temp_cache_dir): - """Test pattern normalization.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - - kwargs = { - "allow_patterns": "*.txt", - "ignore_patterns": ["*.log", "*.tmp"], - "other_param": "unchanged", - } - - api._normalize_patterns(kwargs) - - assert kwargs["allow_patterns"] == ["*.txt"] - assert kwargs["ignore_patterns"] == ["*.log", "*.tmp"] - assert kwargs["other_param"] == "unchanged" - - def test_download_single_file(self, mock_hf_hub_download, temp_cache_dir): - """Test single file download.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - - result = api._download_single_file("test.txt") - - assert result == Path("/fake/path/to/file.txt") - mock_hf_hub_download.assert_called_once() - - # Check that correct arguments were passed - call_args = mock_hf_hub_download.call_args[1] - assert call_args["repo_id"] == "test/repo" - assert call_args["filename"] == "test.txt" - - def test_download_single_file_dry_run(self, mock_hf_hub_download, temp_cache_dir): - """Test single file download with dry run.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - - result = api._download_single_file("test.txt", dry_run=True) - - assert result == Path("dry_run_path") - mock_hf_hub_download.assert_not_called() - - def test_download_snapshot(self, mock_snapshot_download, temp_cache_dir): - """Test snapshot download.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - - result = api._download_snapshot(allow_patterns=["*.txt"]) - - assert result == Path("/fake/path/to/snapshot") - mock_snapshot_download.assert_called_once() - - def test_download_snapshot_dry_run(self, mock_snapshot_download, temp_cache_dir): - """Test snapshot download with dry run.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - - result = api._download_snapshot(dry_run=True, allow_patterns=["*.txt"]) - - assert result == Path("dry_run_path") - mock_snapshot_download.assert_not_called() - - def test_download_single_file_string(self, temp_cache_dir): - """Test download with single file as string.""" - # Create API instance by bypassing problematic initialization - api = TestHfAPI.__new__(TestHfAPI) # Create without calling __init__ - - # Manually set the required attributes - api._repo_id = "test/repo" - api.repo_type = "dataset" - api.token = None - api._cache_dir = temp_cache_dir - api.size = { - "size": {"dataset": {"num_bytes_original_files": 1024}} - } # Small size - api.logger = Mock() # Mock logger to avoid issues - - with patch("tfbpapi.AbstractHfAPI.hf_hub_download") as mock_hf_download: - # Configure the mock to return a fake path - mock_hf_download.return_value = "/fake/path/to/file.txt" - - result = api.download(files="test.txt", auto_download_threshold_mb=0) - - assert result == Path("/fake/path/to/file.txt") - mock_hf_download.assert_called_once() - - # Verify the call arguments - call_args = mock_hf_download.call_args[1] - assert call_args["repo_id"] == "test/repo" - assert call_args["filename"] == "test.txt" - - def test_download_single_file_list( - self, mock_hf_hub_download, temp_cache_dir, mock_dataset_size_call - ): - """Test download with single file as list.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - api.size = { - "size": {"dataset": {"num_bytes_original_files": 1024}} - } # Small size - - result = api.download(files=["test.txt"], auto_download_threshold_mb=0) - - assert result == Path("/fake/path/to/file.txt") - mock_hf_hub_download.assert_called_once() - - def test_download_multiple_files(self, mock_snapshot_download, temp_cache_dir): - """Test download with multiple files.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - api.size = { - "size": {"dataset": {"num_bytes_original_files": 1024}} - } # Small size - - result = api.download(files=["test1.txt", "test2.txt"]) - - assert result == Path("/fake/path/to/snapshot") - mock_snapshot_download.assert_called_once() - - def test_download_force_full(self, mock_snapshot_download, temp_cache_dir): - """Test download with force_full_download=True.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - api.size = { - "size": {"dataset": {"num_bytes_original_files": 1000 * 1024 * 1024}} - } # Large size - - result = api.download(force_full_download=True) - - assert result == Path("/fake/path/to/snapshot") - mock_snapshot_download.assert_called_once() - - def test_download_repo_too_large(self, temp_cache_dir): - """Test download with repo too large error.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - api.size = { - "size": {"dataset": {"num_bytes_original_files": 1000 * 1024 * 1024}} - } # 1GB - - with pytest.raises(RepoTooLargeError, match="Dataset size .* exceeds"): - api.download(auto_download_threshold_mb=10) - - def test_download_small_repo_auto(self, mock_snapshot_download, temp_cache_dir): - """Test download with small repo under threshold.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - api.size = { - "size": {"dataset": {"num_bytes_original_files": 5 * 1024 * 1024}} - } # 5MB - - result = api.download(auto_download_threshold_mb=10) - - assert result == Path("/fake/path/to/snapshot") - mock_snapshot_download.assert_called_once() - - def test_snapshot_path_property(self, temp_cache_dir): - """Test snapshot_path property getter and setter.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - - # Initially None - assert api.snapshot_path is None - - # Set to path - test_path = "/some/path" - api.snapshot_path = Path(test_path) - assert api.snapshot_path == Path(test_path) - - # Set to None - api.snapshot_path = None - assert api.snapshot_path is None - - def test_size_property(self, temp_cache_dir): - """Test size property getter and setter.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - - # Initially None - assert api.size is None - - # Set size data - size_data = {"size": {"dataset": {"num_bytes": 1024}}} - api.size = size_data - assert api.size == size_data - - def test_abstract_methods_implemented(self, temp_cache_dir): - """Test that abstract methods are properly implemented.""" - api = TestHfAPI(repo_id="test/repo", cache_dir=temp_cache_dir) - - # Test parse_datacard - result = api.parse_datacard() - assert result == {"test": "datacard"} - - # Test query - result = api.query() - assert result == {"test": "query"} - - def test_repo_too_large_error(self): - """Test RepoTooLargeError exception.""" - error = RepoTooLargeError("Test error message") - assert str(error) == "Test error message" - assert isinstance(error, ValueError) diff --git a/tfbpapi/tests/test_AbstractRecordsAndFilesAPI.py b/tfbpapi/tests/test_AbstractRecordsAndFilesAPI.py deleted file mode 100644 index 1c64a39..0000000 --- a/tfbpapi/tests/test_AbstractRecordsAndFilesAPI.py +++ /dev/null @@ -1,284 +0,0 @@ -import gzip -from io import BytesIO -from tempfile import NamedTemporaryFile -from typing import Any - -import pandas as pd -import pytest -import responses -from aioresponses import aioresponses - -from tfbpapi.AbstractRecordsAndFilesAPI import ( - AbstractRecordsAndFilesAPI, -) - -# The following test is commented out because it requires a running server -- this is -# how I retrieved the data for the tests below. The data is saved in the snapshot -# directory -# -# @pytest.mark.asyncio -# async def test_save_response_records_and_files(snapshot): -# async with aiohttp.ClientSession() as session: -# url = "http://127.0.0.1:8001/api/promotersetsig/export" -# async with session.get( -# url, -# headers={ -# "Authorization": f"token {os.getenv('TOKEN')}", -# "Content-Type": "application/json", -# }, -# params={ -# "regulator_symbol": "HAP5", -# "workflow": "nf_core_callingcards_dev", -# "data_usable": "pass", -# }, -# ) as response: -# response.raise_for_status() -# response_text = await response.text() -# snapshot.assert_match(response_text) -# assert response.status == 200 - - -# @pytest.mark.asyncio -# async def test_save_response_records_and_files(): -# async with aiohttp.ClientSession() as session: -# url = "http://127.0.0.1:8001/api/promotersetsig/record_table_and_files" -# async with session.get( -# url, -# headers={ -# "Authorization": f"token {os.getenv('TOKEN')}", -# "Content-Type": "application/gzip", -# }, -# params={ -# "regulator_symbol": "HAP5", -# "workflow": "nf_core_callingcards_dev", -# "data_usable": "pass", -# }, -# ) as response: -# response.raise_for_status() -# response_content = await response.read() -# with open("saved_response.tar.gz", "wb") as f: -# f.write(response_content) -# assert response.status == 200 - - -def promotersetsig_csv_gzip() -> bytes: - # Define the data as a dictionary - data = { - "id": [10690, 10694, 10754, 10929, 10939], - "uploader_id": [1, 1, 1, 1, 1], - "upload_date": ["2024-03-26"] * 5, - "modifier_id": [1, 1, 1, 1, 1], - "modified_date": [ - "2024-03-26 14:28:43.825628+00:00", - "2024-03-26 14:28:44.739775+00:00", - "2024-03-26 14:29:01.837335+00:00", - "2024-03-26 14:29:45.379790+00:00", - "2024-03-26 14:29:47.853980+00:00", - ], - "binding_id": [4079, 4083, 4143, 4318, 4327], - "promoter_id": [4, 4, 4, 4, 4], - "background_id": [6, 6, 6, 6, 6], - "fileformat_id": [5, 5, 5, 5, 5], - "file": [ - "promotersetsig/10690.csv.gz", - "promotersetsig/10694.csv.gz", - "promotersetsig/10754.csv.gz", - "promotersetsig/10929.csv.gz", - "promotersetsig/10939.csv.gz", - ], - } - - # Create a DataFrame - df = pd.DataFrame(data) - - # Convert the DataFrame to CSV and compress it using gzip - csv_buffer = BytesIO() - with gzip.GzipFile(fileobj=csv_buffer, mode="w") as gz: - df.to_csv(gz, index=False) - - # Get the gzipped data as bytes - return csv_buffer.getvalue() - - -class ConcreteRecordsAndFilesAPI(AbstractRecordsAndFilesAPI): - """Concrete implementation of AbstractRecordsAndFilesAPI for testing purposes.""" - - def create(self, data: dict[str, Any], **kwargs) -> Any: - pass - - def update(self, df: Any, **kwargs) -> Any: - pass - - def delete(self, id: str, **kwargs) -> Any: - pass - - def submit(self, post_dict: dict, **kwargs) -> Any: - pass # Implement for testing if necessary - - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Any: - pass # Implement for testing if necessary - - -@pytest.fixture -@responses.activate -def api_client(): - valid_url = "http://127.0.0.1:8001/api/promotersetsig" - responses.add(responses.HEAD, valid_url, status=200) - return ConcreteRecordsAndFilesAPI(url=valid_url, token="my_token") - - -@pytest.mark.asyncio -async def test_read_without_files(snapshot, api_client): - with aioresponses() as m: - # Mock the HTTP response with the saved snapshot response - m.get( - "http://127.0.0.1:8001/api/promotersetsig/export", - status=200, - body=promotersetsig_csv_gzip(), - headers={"Content-Type": "application/gzip"}, - ) - - result = await api_client.read() - assert isinstance(result.get("metadata"), pd.DataFrame) - assert result.get("metadata").shape == ( - 5, - 10, - ) - - -# chatGPT and I went through many iterations of trying to mock two endpoints at once. -# no success. the retrieve_files is untested outside of the tutorial notebook as a -# result -# -# @pytest.mark.asyncio -# async def test_read_with_responses(snapshot, api_client): -# with responses.RequestsMock() as rsps: -# # Mock the /export endpoint -# rsps.add( -# responses.GET, -# "http://127.0.0.1:8001/api/promotersetsig/export", -# body=promotersetsig_csv_gzip(), -# status=200, -# content_type="text/csv", -# ) - -# # Path to the tar.gz file -# tar_gz_file_path = os.path.join( -# os.path.dirname(__file__), -# "snapshots", -# "promotersetsig_records_and_files.tar.gz", -# ) - -# # Read the content of the tar.gz file -# with open(tar_gz_file_path, "rb") as tar_gz_file: -# tar_gz_content = tar_gz_file.read() - -# # Mock the /record_table_and_files endpoint -# rsps.add( -# responses.GET, -# "http://127.0.0.1:8001/api/promotersetsig/record_table_and_files", -# body=tar_gz_content, -# status=200, -# content_type="application/gzip", -# ) - -# # Helper function to create a mock ClientResponse -# async def create_mock_response(url, method, body, content_type, status): -# return MockClientResponse( -# method, URL(url), status, {"Content-Type": content_type}, body -# ) - -# # Patch aiohttp.ClientSession.get to use our mocked responses -# async def mock_get(self, url, **kwargs): -# if "export" in url: -# return await create_mock_response( -# url, -# "GET", -# promotersetsig_csv_gzip().encode(), -# "text/csv", -# 200, -# ) -# elif "record_table_and_files" in url: -# return await create_mock_response( -# url, -# "GET", -# tar_gz_content, -# "application/gzip", -# 200, -# ) -# else: -# raise ValueError("Unexpected URL") - -# with patch("aiohttp.ClientSession.get", new=mock_get): -# # Test the read method without retrieving files -# result = await api_client.read() -# assert isinstance(result.get("metadata"), pd.DataFrame) -# assert result.get("metadata").shape == (5, 10) - -# # Test the read method with retrieving files -# result = await api_client.read(retrieve_files=True) -# assert isinstance(result.get("metadata"), pd.DataFrame) -# assert result.get("metadata").shape == (5, 10) -# assert isinstance(result.get("data"), dict) -# assert len(result.get("data")) == 5 -# assert all(isinstance(v, pd.DataFrame) \ -# for v in result.get("data").values()) - -# test the _detect_delimiter method #### - - -def test_detect_delimiter_errors(api_client): - # test that a FileNotFound error is raised if the file does not exist - with pytest.raises(FileNotFoundError): - api_client._detect_delimiter("non_existent_file.csv") - - with NamedTemporaryFile(mode="w", suffix=".csv.gz") as tmpfile: - tmpfile.write("col1,col2,col3\nval1,val2,val3") - tmpfile.flush() - tmpfile_path = tmpfile.name - - with pytest.raises(gzip.BadGzipFile): - api_client._detect_delimiter(tmpfile_path) - - -def test_comma_delimiter(api_client): - with NamedTemporaryFile(mode="w", suffix=".csv") as tmpfile: - tmpfile.write("col1,col2,col3\nval1,val2,val3") - tmpfile.flush() - tmpfile_path = tmpfile.name - - delimiter = api_client._detect_delimiter(tmpfile_path) - assert delimiter == "," - - -def test_tab_delimiter(api_client): - with NamedTemporaryFile(mode="w", suffix=".csv") as tmpfile: - tmpfile.write("col1\tcol2\tcol3\nval1\tval2\tval3") - tmpfile.flush() - tmpfile_path = tmpfile.name - - delimiter = api_client._detect_delimiter(tmpfile_path) - assert delimiter == "\t" - - -def test_space_delimiter(api_client): - with NamedTemporaryFile(mode="w", suffix=".csv") as tmpfile: - tmpfile.write("col1 col2 col3\nval1 val2 val3") - tmpfile.flush() - tmpfile_path = tmpfile.name - - delimiter = api_client._detect_delimiter(tmpfile_path) - assert delimiter == " " - - -def test_gzipped_file(api_client): - with NamedTemporaryFile(suffix=".csv.gz") as tmpfile: - with gzip.open(tmpfile.name, "wt") as gzfile: - gzfile.write("col1,col2,col3\nval1,val2,val3") - gzfile.flush() - tmpfile_path = tmpfile.name - - delimiter = api_client._detect_delimiter(tmpfile_path) - assert delimiter == "," diff --git a/tfbpapi/tests/test_AbstractRecordsOnlyAPI.py b/tfbpapi/tests/test_AbstractRecordsOnlyAPI.py deleted file mode 100644 index 1def39a..0000000 --- a/tfbpapi/tests/test_AbstractRecordsOnlyAPI.py +++ /dev/null @@ -1,71 +0,0 @@ -import gzip -from typing import Any - -import pandas as pd -import pytest -import responses -from aioresponses import aioresponses - -from tfbpapi.AbstractRecordsOnlyAPI import AbstractRecordsOnlyAPI - - -class ConcreteAPI(AbstractRecordsOnlyAPI): - """Concrete implementation of AbstractRecordsOnlyAPI for testing purposes.""" - - def create(self, data: dict[str, Any], **kwargs) -> Any: - pass # Implement for testing if necessary - - def update(self, df: Any, **kwargs) -> Any: - pass # Implement for testing if necessary - - def delete(self, id: str, **kwargs) -> Any: - pass # Implement for testing if necessary - - def submit(self, post_dict: dict, **kwargs) -> Any: - pass # Implement for testing if necessary - - def retrieve( - self, group_task_id: str, timeout: int, polling_interval: int, **kwargs - ) -> Any: - pass # Implement for testing if necessary - - -@pytest.fixture -@responses.activate -def api_client(): - valid_url = "https://example.com/api/endpoint" - responses.add(responses.HEAD, valid_url, status=200) - return ConcreteAPI(url=valid_url, token="my_token") - - -@pytest.mark.asyncio -async def test_read(snapshot, api_client): - with aioresponses() as m: - # Mocking the response - mocked_csv = ( - "id,uploader_id,upload_date,modifier_id,modified_date,binding_id,promoter_id,background_id,fileformat_id,file\n" # noqa: E501 - "10690,1,2024-03-26,1,2024-03-26 14:28:43.825628+00:00,4079,4,6,5,promotersetsig/10690.csv.gz\n" # noqa: E501 - "10694,1,2024-03-26,1,2024-03-26 14:28:44.739775+00:00,4083,4,6,5,promotersetsig/10694.csv.gz\n" # noqa: E501 - "10754,1,2024-03-26,1,2024-03-26 14:29:01.837335+00:00,4143,4,6,5,promotersetsig/10754.csv.gz\n" # noqa: E501 - "10929,1,2024-03-26,1,2024-03-26 14:29:45.379790+00:00,4318,4,6,5,promotersetsig/10929.csv.gz\n" # noqa: E501 - "10939,1,2024-03-26,1,2024-03-26 14:29:47.853980+00:00,4327,4,6,5,promotersetsig/10939.csv.gz" # noqa: E501 - ) - - # Convert to bytes and gzip the content - gzipped_csv = gzip.compress(mocked_csv.encode("utf-8")) - - m.get( - "https://example.com/api/endpoint/export", - status=200, - body=gzipped_csv, - headers={"Content-Type": "application/gzip"}, - ) - - result = await api_client.read() - assert isinstance(result, dict) - assert isinstance(result.get("metadata"), pd.DataFrame) - assert result.get("metadata").shape == (5, 10) # type: ignore - - -if __name__ == "__main__": - pytest.main() diff --git a/tfbpapi/tests/test_Cache.py b/tfbpapi/tests/test_Cache.py deleted file mode 100644 index a84eb37..0000000 --- a/tfbpapi/tests/test_Cache.py +++ /dev/null @@ -1,66 +0,0 @@ -import time - -import pytest - -from tfbpapi.Cache import Cache - - -def test_cache_set_and_get(): - cache = Cache() - cache.set("key1", "value1") - assert cache.get("key1") == "value1" - assert cache.get("key2", "default_value") == "default_value" - - -def test_cache_list(): - cache = Cache() - cache.set("key1", "value1") - cache.set("key2", "value2") - keys = cache.list() - assert "key1" in keys - assert "key2" in keys - - -def test_cache_delete(): - cache = Cache() - cache.set("key1", "value1") - cache.set("key2", "value2") - cache.delete("key1") - assert cache.get("key1") is None - assert cache.get("key2") == "value2" - - -def test_cache_ttl(): - cache = Cache(ttl=1) # TTL set to 1 second - cache.set("key1", "value1") - time.sleep(1.5) # Wait for TTL to expire - assert cache.get("key1") is None # Should be None after TTL expiry - - -def test_cache_lru(): - cache = Cache(maxsize=2) - cache.set("key1", "value1") - cache.set("key2", "value2") - cache.set("key3", "value3") # This should evict "key1" if LRU works - assert cache.get("key1") is None - assert cache.get("key2") == "value2" - assert cache.get("key3") == "value3" - - -def test_separate_cache_instances(): - cache1 = Cache() - cache2 = Cache() - - cache1.set("key1", "value1") - cache2.set("key2", "value2") - - # Ensure they don't share state - assert cache1.get("key1") == "value1" - assert cache1.get("key2") is None - - assert cache2.get("key2") == "value2" - assert cache2.get("key1") is None - - -if __name__ == "__main__": - pytest.main() diff --git a/tfbpapi/tests/test_HfQueryAPI.py b/tfbpapi/tests/test_HfQueryAPI.py deleted file mode 100644 index 1aeeaea..0000000 --- a/tfbpapi/tests/test_HfQueryAPI.py +++ /dev/null @@ -1,530 +0,0 @@ -import tempfile -from pathlib import Path -from unittest.mock import Mock, patch - -import pandas as pd -import pytest -from datasets import Dataset, DatasetDict - -from tfbpapi.HfQueryAPI import HfQueryAPI - - -@pytest.fixture -def mock_dataset_card(): - """Mock DatasetCard.load to return fake card data.""" - with patch("tfbpapi.HfQueryAPI.DatasetCard.load") as mock: - mock_card = Mock() - mock_card.data.to_dict.return_value = { - "configs": [ - { - "config_name": "default", - "dataset_info": { - "features": [ - { - "name": "text", - "dtype": "string", - "description": "Input text", - }, - { - "name": "label", - "dtype": "int64", - "description": "Classification label", - }, - ] - }, - "data_files": [ - {"path": "data/train.parquet", "split": "train"}, - {"path": "data/test.parquet", "split": "test"}, - ], - } - ] - } - mock.return_value = mock_card - yield mock - - -@pytest.fixture -def mock_load_dataset(): - """Mock load_dataset to return fake dataset.""" - with patch("tfbpapi.HfQueryAPI.load_dataset") as mock: - # Create mock dataset with sample data - mock_dataset_dict = DatasetDict( - { - "train": Dataset.from_pandas( - pd.DataFrame( - {"text": ["hello", "world", "test"], "label": [0, 1, 0]} - ) - ), - "test": Dataset.from_pandas( - pd.DataFrame({"text": ["sample", "data"], "label": [1, 0]}) - ), - } - ) - mock.return_value = mock_dataset_dict - yield mock - - -@pytest.fixture -def mock_duckdb(): - """Mock DuckDB connection.""" - with patch("tfbpapi.HfQueryAPI.duckdb.connect") as mock_connect: - mock_conn = Mock() - mock_result = Mock() - mock_result.fetchdf.return_value = pd.DataFrame({"count": [3]}) - mock_conn.execute.return_value = mock_result - mock_connect.return_value = mock_conn - yield mock_conn - - -@pytest.fixture -def mock_hf_api_init(): - """Mock AbstractHfAPI initialization to prevent real API calls.""" - # Instead of mocking the __init__, we'll mock the methods that cause issues - with patch( - "tfbpapi.AbstractHfAPI.AbstractHfAPI._get_dataset_size" - ) as mock_get_size: - mock_get_size.return_value = None - yield mock_get_size - - -@pytest.fixture -def temp_cache_dir(): - """Create a temporary directory for cache testing.""" - with tempfile.TemporaryDirectory() as temp_dir: - yield Path(temp_dir) - - -class TestHfQueryAPI: - """Test cases for HfQueryAPI.""" - - def test_init_with_auto_parse( - self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir - ): - """Test initialization with auto_parse_datacard=True.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - # Verify initialization - assert api.auto_download_threshold_mb == 100.0 - assert api._datasets == { - "default": { - "features": { - "text": {"dtype": "string", "description": "Input text"}, - "label": {"dtype": "int64", "description": "Classification label"}, - }, - "data_files": [ - {"path": "data/train.parquet", "split": "train"}, - {"path": "data/test.parquet", "split": "test"}, - ], - "config": { - "config_name": "default", - "dataset_info": { - "features": [ - { - "name": "text", - "dtype": "string", - "description": "Input text", - }, - { - "name": "label", - "dtype": "int64", - "description": "Classification label", - }, - ] - }, - "data_files": [ - {"path": "data/train.parquet", "split": "train"}, - {"path": "data/test.parquet", "split": "test"}, - ], - }, - "loaded": False, - } - } - mock_dataset_card.assert_called_once() - - def test_init_without_auto_parse( - self, mock_hf_api_init, mock_duckdb, temp_cache_dir - ): - """Test initialization with auto_parse_datacard=False.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=False - ) - - assert api._datasets == {} - - def test_init_parse_datacard_failure( - self, mock_hf_api_init, mock_duckdb, temp_cache_dir - ): - """Test initialization when parse_datacard fails.""" - with patch("tfbpapi.HfQueryAPI.DatasetCard.load") as mock_card: - mock_card.side_effect = Exception("Failed to load") - - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - assert api._datasets == {} - # Logger warning should have been called during init - - def test_datasets_property( - self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir - ): - """Test datasets property getter and setter.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - # Test getter - assert "default" in api.datasets - - # Test setter - new_datasets = {"custom": {"features": {}, "data_files": [], "loaded": False}} - api.datasets = new_datasets - assert api.datasets == new_datasets - - def test_available_tables( - self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir - ): - """Test available_tables property.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - assert api.available_tables == ["default"] - - def test_parse_datacard_success( - self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir - ): - """Test successful datacard parsing.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=False - ) - - result = api.parse_datacard() - - assert "default" in result - assert "features" in result["default"] - assert "text" in result["default"]["features"] - assert "label" in result["default"]["features"] - - def test_parse_datacard_failure( - self, mock_hf_api_init, mock_duckdb, temp_cache_dir - ): - """Test datacard parsing failure.""" - with patch("tfbpapi.HfQueryAPI.DatasetCard.load") as mock_card: - mock_card.side_effect = Exception("Load failed") - - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=False - ) - - result = api.parse_datacard() - - assert result == {} - - def test_ensure_dataset_loaded_not_found( - self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir - ): - """Test _ensure_dataset_loaded with non-existent table.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - with pytest.raises(ValueError, match="Table 'nonexistent' not found"): - api._ensure_dataset_loaded("nonexistent") - - def test_ensure_dataset_loaded_already_loaded( - self, - mock_hf_api_init, - mock_dataset_card, - mock_load_dataset, - mock_duckdb, - temp_cache_dir, - ): - """Test _ensure_dataset_loaded when dataset already loaded.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - # Pre-load a dataset - mock_dataset = Mock() - api._loaded_datasets["default"] = mock_dataset - - result = api._ensure_dataset_loaded("default") - assert result == mock_dataset - mock_load_dataset.assert_not_called() - - def test_ensure_dataset_loaded_download_and_load( - self, - mock_hf_api_init, - mock_dataset_card, - mock_load_dataset, - mock_duckdb, - temp_cache_dir, - ): - """Test _ensure_dataset_loaded with download and load.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - # Mock download method - ensure snapshot_path is None initially - api.snapshot_path = None - mock_download = Mock() - mock_download.return_value = Path("/fake/snapshot") - api.download = mock_download # type: ignore - - # Set side effect to simulate download setting snapshot_path - def download_side_effect(**kwargs): - api.snapshot_path = Path("/fake/snapshot") - return Path("/fake/snapshot") - - mock_download.side_effect = download_side_effect - - result = api._ensure_dataset_loaded("default") - - assert result == mock_load_dataset.return_value - mock_download.assert_called_once_with(auto_download_threshold_mb=100.0) - mock_load_dataset.assert_called_once() - assert api._datasets["default"]["loaded"] is True - - def test_query_with_table_name( - self, - mock_hf_api_init, - mock_dataset_card, - mock_load_dataset, - mock_duckdb, - temp_cache_dir, - ): - """Test query with explicit table name.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - # Mock the dataset loading - api.snapshot_path = Path("/fake/snapshot") - - result = api.query("SELECT * FROM default", table_name="default") - - assert isinstance(result, pd.DataFrame) - mock_duckdb.execute.assert_called_once_with("SELECT * FROM default") - - def test_query_infer_table_name( - self, - mock_hf_api_init, - mock_dataset_card, - mock_load_dataset, - mock_duckdb, - temp_cache_dir, - ): - """Test query with table name inference.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - # Mock the dataset loading - api.snapshot_path = Path("/fake/snapshot") - - result = api.query("SELECT * FROM default") - - assert isinstance(result, pd.DataFrame) - mock_duckdb.execute.assert_called_once_with("SELECT * FROM default") - - def test_describe_table( - self, - mock_hf_api_init, - mock_dataset_card, - mock_load_dataset, - mock_duckdb, - temp_cache_dir, - ): - """Test describe_table method.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - # Mock the dataset loading - api.snapshot_path = Path("/fake/snapshot") - - result = api.describe_table("default") - - assert isinstance(result, pd.DataFrame) - mock_duckdb.execute.assert_called_with("DESCRIBE default") - - def test_sample( - self, - mock_hf_api_init, - mock_dataset_card, - mock_load_dataset, - mock_duckdb, - temp_cache_dir, - ): - """Test sample method.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - # Mock the dataset loading - api.snapshot_path = Path("/fake/snapshot") - - result = api.sample("default", n=3) - - assert isinstance(result, pd.DataFrame) - mock_duckdb.execute.assert_called_with("SELECT * FROM default LIMIT 3") - - def test_count( - self, - mock_hf_api_init, - mock_dataset_card, - mock_load_dataset, - mock_duckdb, - temp_cache_dir, - ): - """Test count method.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - # Mock the dataset loading - api.snapshot_path = Path("/fake/snapshot") - - result = api.count("default") - - assert result == 3 - mock_duckdb.execute.assert_called_with("SELECT COUNT(*) as count FROM default") - - def test_get_columns( - self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir - ): - """Test get_columns method.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - result = api.get_columns("default") - assert result == ["text", "label"] - - def test_get_columns_not_found( - self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir - ): - """Test get_columns with non-existent table.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - with pytest.raises(ValueError, match="Table 'nonexistent' not found"): - api.get_columns("nonexistent") - - def test_context_manager( - self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir - ): - """Test context manager functionality.""" - with HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) as api: - assert api is not None - - # Verify close was called - mock_duckdb.close.assert_called_once() - - def test_close( - self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir - ): - """Test close method.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - api.close() - mock_duckdb.close.assert_called_once() - - def test_table_filters_basic( - self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir - ): - """Test basic table filter functionality.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - # Test setting and getting filters - assert api.get_table_filter("default") is None - - api.set_table_filter("default", "text = 'test'") - assert api.get_table_filter("default") == "text = 'test'" - - # Test removing filters - api.remove_table_filter("default") - assert api.get_table_filter("default") is None - - def test_table_filters_query_modification( - self, - mock_hf_api_init, - mock_dataset_card, - mock_load_dataset, - mock_duckdb, - temp_cache_dir, - ): - """Test that table filters modify queries correctly.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - # Mock the dataset loading - api.snapshot_path = Path("/fake/snapshot") - - # Set a filter - api.set_table_filter("default", "label = 1") - - # Execute a query - api.query("SELECT * FROM default", table_name="default") - - # Verify the query was modified to include the filter - expected_sql = "SELECT * FROM (SELECT * FROM default WHERE label = 1)" - mock_duckdb.execute.assert_called_once_with(expected_sql) - - def test_table_filters_no_modification_when_no_filters( - self, - mock_hf_api_init, - mock_dataset_card, - mock_load_dataset, - mock_duckdb, - temp_cache_dir, - ): - """Test that queries are not modified when no filters are set.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - # Mock the dataset loading - api.snapshot_path = Path("/fake/snapshot") - - # Execute a query without any filters - original_sql = "SELECT * FROM default" - api.query(original_sql, table_name="default") - - # Verify the query was not modified - mock_duckdb.execute.assert_called_once_with(original_sql) - - def test_apply_table_filters_method( - self, mock_hf_api_init, mock_dataset_card, mock_duckdb, temp_cache_dir - ): - """Test the _apply_table_filters method directly.""" - api = HfQueryAPI( - repo_id="test/repo", cache_dir=temp_cache_dir, auto_parse_datacard=True - ) - - # Test with no filters - sql = "SELECT * FROM default" - assert api._apply_table_filters(sql) == sql - - # Test with filter applied - api.set_table_filter("default", "text LIKE '%test%'") - modified_sql = api._apply_table_filters(sql) - expected = "SELECT * FROM (SELECT * FROM default WHERE text LIKE '%test%')" - assert modified_sql == expected - - # Test that already filtered queries don't get double-wrapped - already_filtered = "SELECT * FROM (SELECT * FROM default WHERE existing = 1)" - result = api._apply_table_filters(already_filtered) - # Should not modify since it already contains a filtered subquery - assert result == already_filtered diff --git a/tfbpapi/tests/test_ParamsDict.py b/tfbpapi/tests/test_ParamsDict.py deleted file mode 100644 index cccaeda..0000000 --- a/tfbpapi/tests/test_ParamsDict.py +++ /dev/null @@ -1,96 +0,0 @@ -import pytest -import requests -import responses - -from tfbpapi.ParamsDict import ParamsDict - - -def test_initialization(): - params = ParamsDict({"b": 2, "a": 1}, valid_keys=["a", "b"]) - assert params == {"a": 1, "b": 2} - - -def test_getitem(): - params = ParamsDict({"a": 1, "b": 2}, valid_keys=["a", "b"]) - assert params["a"] == 1 - assert params[["a", "b"]] == ParamsDict({"a": 1, "b": 2}) - with pytest.raises(KeyError): - _ = params["123"] # Changed from 123 to '123' - - -def test_setitem(): - params = ParamsDict({"a": 1}, valid_keys=["a", "b", "c", "d"]) - params.update({"b": 2}) - assert params == {"a": 1, "b": 2} - - params[["c", "d"]] = [3, 4] - assert params == {"a": 1, "b": 2, "c": 3, "d": 4} - - with pytest.raises(ValueError): - params[["e", "f"]] = [5] - - with pytest.raises(KeyError): - params[123] = 5 # type: ignore - - with pytest.raises(KeyError): - params.update({"d": 4, "e": 5}) - - -def test_delitem(): - params = ParamsDict({"a": 1, "b": 2}, valid_keys=["a", "b"]) - del params["a"] - assert params == {"b": 2} - with pytest.raises(KeyError): - del params["123"] # Changed from 123 to '123' - - -def test_repr(): - params = ParamsDict({"a": 1, "b": 2}, valid_keys=["a", "b"]) - assert repr(params) == "ParamsDict({'a': 1, 'b': 2})" - - -def test_str(): - params = ParamsDict({"a": 1, "b": 2}, valid_keys=["a", "b"]) - assert str(params) == "a: 1, b: 2" - - -def test_len(): - params = ParamsDict({"a": 1, "b": 2}, valid_keys=["a", "b", "c"]) - assert len(params) == 2 - params["c"] = 3 - assert len(params) == 3 - - -def test_keys_values_items(): - params = ParamsDict({"a": 1, "b": 2}, valid_keys=["a", "b"]) - assert set(params.keys()) == {"a", "b"} - assert set(params.values()) == {1, 2} - assert set(params.items()) == {("a", 1), ("b", 2)} - - -def test_clear(): - params = ParamsDict({"a": 1, "b": 2}, valid_keys=["a", "b"]) - params.clear() - assert len(params) == 0 - - -def test_as_dict(): - params = ParamsDict({"a": 1, "b": 2}, valid_keys=["a", "b"]) - assert params.as_dict() == {"a": 1, "b": 2} - - -@responses.activate -def test_requests_integration(): - params = ParamsDict({"a": 1, "b": 2}, valid_keys=["a", "b"]) - - url = "https://httpbin.org/get" - responses.add(responses.GET, url, json={"args": {"a": "1", "b": "2"}}, status=200) - - response = requests.get(url, params=params) - assert response.status_code == 200 - response_json = response.json() - assert response_json["args"] == {"a": "1", "b": "2"} - - -if __name__ == "__main__": - pytest.main() diff --git a/tfbpapi/tests/test_metric_arrays.py b/tfbpapi/tests/test_metric_arrays.py deleted file mode 100644 index 45a8203..0000000 --- a/tfbpapi/tests/test_metric_arrays.py +++ /dev/null @@ -1,194 +0,0 @@ -import logging - -import numpy as np -import pandas as pd -import pytest - -from tfbpapi.metric_arrays import metric_arrays - - -def test_metric_arrays_expected_result(caplog): - res_dict = { - "metadata": pd.DataFrame( - { - "id": ["A", "B"], - "regulator_symbol": ["tf1", "tf2"], - } - ), - "data": { - "A": pd.DataFrame( - { - "target_symbol": ["gene1", "gene2"], - "metric1": [1.0, 2.0], - } - ), - "B": pd.DataFrame( - { - "target_symbol": ["gene2", "gene1"], - "metric1": [3.0, 4.0], - } - ), - }, - } - metrics_dict = {"metric1": np.mean} - - # Run function - with caplog.at_level(logging.WARNING): - output_dict = metric_arrays(res_dict, metrics_dict) - - # Check expected result for metric1 - # order based on the index of output_dict['metrics1'] since the ordering of - # the rows is random due to the set operation - expected_df = pd.DataFrame( - {"tf1": [1.0, 2.0], "tf2": [4.0, 3.0]}, - index=pd.Index(["gene1", "gene2"], name="target_symbol"), - ).reindex(output_dict["metric1"].index) - - pd.testing.assert_frame_equal(output_dict["metric1"], expected_df) - - # Check no warning since there are no incomplete rows or columns - assert "incomplete" not in caplog.text - - -def test_metric_arrays_missing_data(caplog): - res_dict = { - "metadata": pd.DataFrame( - { - "id": ["A", "B"], - "regulator_symbol": ["tf1", "tf2"], - } - ), - "data": { - "A": pd.DataFrame( - { - "target_symbol": ["gene1", "gene2"], - "metric1": [1.0, 2.0], - } - ), - "B": pd.DataFrame( - { - "target_symbol": ["gene1", "gene3"], - "metric1": [5.0, 3.0], - } - ), - }, - } - metrics_dict = {"metric1": np.mean} - - # Run function with incomplete row dropping - with caplog.at_level(logging.WARNING): - output_dict1 = metric_arrays(res_dict, metrics_dict, drop_incomplete_rows=False) - - # Check result for metric1 with "gene2" dropped due to missing data in B - # sort based on output_dict['metric1'] index since - # the ordering of the rows is random - expected_df1 = pd.DataFrame( - {"tf1": [1.0, 2.0, np.nan], "tf2": [5.0, np.nan, 3.0]}, - index=pd.Index(["gene1", "gene2", "gene3"], name="target_symbol"), - ).reindex(output_dict1["metric1"].index) - - pd.testing.assert_frame_equal(output_dict1["metric1"], expected_df1) - - # Run function with incomplete row dropping - with caplog.at_level(logging.WARNING): - output_dict2 = metric_arrays(res_dict, metrics_dict, drop_incomplete_rows=True) - - # Check result for metric1 with "gene2" dropped due to missing data in B - expected_df2 = pd.DataFrame( - {"tf1": [1.0], "tf2": [5.0]}, - index=pd.Index(["gene1"], name="target_symbol"), - ).reindex(output_dict2["metric1"].index) - - pd.testing.assert_frame_equal(output_dict2["metric1"], expected_df2) - - # Check warning for incomplete rows - assert "2 rows and 0 columns with incomplete records were dropped" in caplog.text - - -def test_metric_arrays_missing_keys(): - res_dict = { - "metadata": pd.DataFrame( - {"id": ["A"], "target_symbol": ["gene1"], "regulator_symbol": ["tf1"]} - ), - # Missing data for id "A" - "data": {}, - } - metrics_dict = {"metric1": np.mean} - - # Expect a KeyError for missing data keys - with pytest.raises(KeyError, match="Data dictionary must have the same keys"): - metric_arrays(res_dict, metrics_dict) - - -def test_metric_arrays_non_dataframe_value(): - res_dict = { - "metadata": pd.DataFrame( - {"id": ["A"], "target_symbol": ["gene1"], "regulator_symbol": ["tf1"]} - ), - "data": {"A": [1, 2, 3]}, # Invalid non-DataFrame entry - } - metrics_dict = {"metric1": np.mean} - - # Expect ValueError when data dictionary values are not DataFrames - with pytest.raises( - ValueError, match="All values in the data dictionary must be DataFrames" - ): - metric_arrays(res_dict, metrics_dict) - - -def test_metric_arrays_duplicate_rows_without_dedup_func(): - res_dict = { - "metadata": pd.DataFrame( - { - "id": ["A"], - "target_symbol": ["gene1"], - "regulator_symbol": ["tf1"], - } - ), - "data": { - "A": pd.DataFrame( - { - "target_symbol": ["gene1", "gene1"], - "metric1": [1.0, 2.0], - } - ), - }, - } - metrics_dict = {"metric1": None} # No deduplication function provided - - # Expect a ValueError due to duplicate rows without deduplication function - # - with pytest.raises( - ValueError, match="Duplicate entries found for metric 'metric1'" - ): - metric_arrays(res_dict, metrics_dict) # type: ignore - - -def test_metric_arrays_deduplication_function(): - res_dict = { - "metadata": pd.DataFrame( - { - "id": ["A"], - "target_symbol": ["gene1"], - "regulator_symbol": ["tf1"], - } - ), - "data": { - "A": pd.DataFrame( - { - "target_symbol": ["gene1", "gene1"], - "metric1": [1.0, 2.0], - } - ), - }, - } - metrics_dict = {"metric1": np.mean} # Deduplication function to average duplicates - - # Run function with deduplication - output_dict = metric_arrays(res_dict, metrics_dict) - - # Check that duplicates were averaged correctly - expected_df = pd.DataFrame( - {"tf1": [1.5]}, pd.Index(["gene1"], name="target_symbol") - ) - pd.testing.assert_frame_equal(output_dict["metric1"], expected_df) diff --git a/tfbpapi/tests/test_rank_transforms.py b/tfbpapi/tests/test_rank_transforms.py deleted file mode 100644 index 31dbeaa..0000000 --- a/tfbpapi/tests/test_rank_transforms.py +++ /dev/null @@ -1,80 +0,0 @@ -import numpy as np -from scipy.stats import rankdata - -from tfbpapi.rank_transforms import ( - shifted_negative_log_ranks, - transform, -) - - -def test_shifted_negative_log_ranks_basic(): - ranks = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) - expected_log_ranks = -1 * np.log10(ranks) + np.log10(np.max(ranks)) - - actual_log_ranks = shifted_negative_log_ranks(ranks) - np.testing.assert_array_almost_equal(actual_log_ranks, expected_log_ranks) - - -def test_shifted_negative_log_ranks_with_ties(): - ranks = np.array([1.0, 2.5, 2.5, 3.0, 4.0]) - expected_log_ranks = -1 * np.log10(ranks) + np.log10(np.max(ranks)) - - actual_log_ranks = shifted_negative_log_ranks(ranks) - np.testing.assert_array_almost_equal(actual_log_ranks, expected_log_ranks) - - -def test_negative_log_transform_basic(): - pvalues = np.array([0.01, 0.05, 0.01, 0.02, 0.05]) - enrichment = np.array([5.0, 3.0, 6.0, 4.0, 4.5]) - - # Expected ranks based on pvalue (primary) with enrichment (secondary) tie-breaking - expected_ranks = np.array([2.0, 5.0, 1.0, 3.0, 4.0]) - expected_log_ranks = -1 * np.log10(expected_ranks) + np.log10( - np.max(expected_ranks) - ) - - actual_log_ranks = transform(pvalues, enrichment) - np.testing.assert_array_almost_equal(actual_log_ranks, expected_log_ranks) - - -def test_all_ties_in_primary_column(): - pvalues = np.array([0.01, 0.01, 0.01, 0.01]) - enrichment = np.array([10.0, 20.0, 15.0, 5.0]) - - # With all pvalues tied, the ranking should depend solely - # on enrichment (higher is better) - expected_secondary_ranks = rankdata(-enrichment, method="average") - expected_log_ranks = -1 * np.log10(expected_secondary_ranks) + np.log10( - np.max(expected_secondary_ranks) - ) - - actual_log_ranks = transform(pvalues, enrichment) - np.testing.assert_array_almost_equal(actual_log_ranks, expected_log_ranks) - - -def test_no_ties_in_primary_column(): - pvalues = np.array([0.01, 0.02, 0.03, 0.04]) - enrichment = np.array([5.0, 10.0, 15.0, 20.0]) - - # With no ties in pvalue, the secondary column should have no effect - expected_ranks = rankdata(pvalues, method="average") - expected_log_ranks = -1 * np.log10(expected_ranks) + np.log10( - np.max(expected_ranks) - ) - - actual_log_ranks = transform(pvalues, enrichment) - np.testing.assert_array_almost_equal(actual_log_ranks, expected_log_ranks) - - -def test_tied_in_both_pvalue_and_enrichment(): - pvalues = np.array([0.01, 0.05, 0.01, 0.02, 0.05]) - enrichment = np.array([5.0, 3.0, 5.0, 4.0, 3.0]) - - # With ties in both primary and secondary columns - expected_ranks = np.array([1.5, 4.5, 1.5, 3.0, 4.5]) - expected_log_ranks = -1 * np.log10(expected_ranks) + np.log10( - np.max(expected_ranks) - ) - - actual_log_ranks = transform(pvalues, enrichment) - np.testing.assert_array_almost_equal(actual_log_ranks, expected_log_ranks) From 594eb6fa73a3dc518384775217aea1e9ddc02525 Mon Sep 17 00:00:00 2001 From: chasem Date: Wed, 17 Sep 2025 20:23:22 -0500 Subject: [PATCH 31/49] this is getting closer to what im after. datainfo is close to finished. documentation works and is updated --- tfbpapi/HfQueryAPI.py | 22 +- tfbpapi/datainfo/datacard.py | 79 +++--- tfbpapi/datainfo/fetchers.py | 64 +++-- tfbpapi/datainfo/models.py | 52 ++-- tfbpapi/errors.py | 33 +-- tfbpapi/tests/datainfo/__init__.py | 2 +- tfbpapi/tests/datainfo/conftest.py | 110 ++++--- tfbpapi/tests/datainfo/test_datacard.py | 363 +++++++++++++++++------- tfbpapi/tests/datainfo/test_fetchers.py | 71 +++-- tfbpapi/tests/datainfo/test_models.py | 114 +++++--- 10 files changed, 572 insertions(+), 338 deletions(-) diff --git a/tfbpapi/HfQueryAPI.py b/tfbpapi/HfQueryAPI.py index 98c380a..4eee06d 100644 --- a/tfbpapi/HfQueryAPI.py +++ b/tfbpapi/HfQueryAPI.py @@ -855,6 +855,7 @@ def get_cache_info(self) -> dict[str, Any]: Get comprehensive cache information including current repo details. :return: Dictionary with cache stats, repo info, and recommendations + """ if not self._cache_manager: self.logger.debug("Cache management is disabled") @@ -967,6 +968,7 @@ def get_repo_cache_info(self, repo_id: str | None = None) -> dict[str, Any]: :param repo_id: Repository ID (defaults to current repo) :return: Dictionary with detailed repo cache information + """ if not self._cache_manager: self.logger.debug("Cache management is disabled") @@ -1047,6 +1049,7 @@ def check_cached_files(self, table_name: str | None = None) -> dict[str, Any]: :param table_name: Specific table to check (defaults to all tables) :return: Dictionary with file cache status + """ if not self._cache_manager: self.logger.debug("Cache management is disabled") @@ -1157,6 +1160,7 @@ def cleanup_cache( :param keep_current_repo: Whether to preserve current repo from cleanup :param dry_run: If True, show what would be deleted without executing :return: Dictionary with cleanup results and summary + """ if not self._cache_manager: self.logger.warning("Cache management is disabled, cannot perform cleanup") @@ -1315,9 +1319,11 @@ def auto_cleanup_cache_if_needed(self) -> dict[str, Any]: """ Automatically clean cache if configured policies are exceeded. - This method is called automatically during operations if auto_cleanup is enabled. + This method is called automatically during operations if auto_cleanup is + enabled. :return: Dictionary with cleanup results or None if no cleanup was needed + """ if not self._cache_manager or not getattr(self, "_cache_auto_cleanup", False): self.logger.debug("Auto-cleanup is disabled") @@ -1377,6 +1383,7 @@ def suggest_cache_cleanup(self) -> dict[str, Any]: Analyze cache and provide cleanup recommendations without executing. :return: Dictionary with analysis and recommendations + """ if not self._cache_manager: return {"cache_management": "disabled"} @@ -1491,9 +1498,11 @@ def warm_cache( :param repo_ids: List of repository IDs to pre-download :param tables: List of table names from current repo to pre-download - :param include_current_repo: Whether to include current repo if repo_ids specified + :param include_current_repo: Whether to include current repo if repo_ids + specified :param dry_run: If True, show what would be downloaded without executing :return: Dictionary with warming results + """ if not self._cache_manager: return {"cache_management": "disabled"} @@ -1607,6 +1616,7 @@ def verify_cache_integrity(self) -> dict[str, Any]: Verify integrity of cached files and detect corruption. :return: Dictionary with verification results + """ if not self._cache_manager: return {"cache_management": "disabled"} @@ -1695,7 +1705,7 @@ def verify_cache_integrity(self) -> dict[str, Any]: "message": f"File size mismatch in {file_info.file_name}", } ) - except (OSError, IOError) as e: + except OSError as e: repo_issues.append( { "type": "access_error", @@ -1769,6 +1779,7 @@ def migrate_cache(self, new_cache_dir: str | Path) -> dict[str, Any]: :param new_cache_dir: Target directory for cache migration :return: Dictionary with migration results + """ if not self._cache_manager: return {"cache_management": "disabled"} @@ -1888,6 +1899,7 @@ def configure_cache_policies( :param max_size: Maximum total cache size (e.g., "10GB") :param save_to_env: Save configuration to environment variables :return: Dictionary with updated configuration + """ if not self._cache_manager: return {"cache_management": "disabled"} @@ -1948,6 +1960,7 @@ def get_cache_configuration(self) -> dict[str, Any]: Get current cache configuration including environment variables. :return: Dictionary with comprehensive cache configuration + """ import os @@ -2016,6 +2029,7 @@ def reset_cache_configuration( :param remove_env_vars: Also remove related environment variables :return: Dictionary with reset results + """ if not self._cache_manager: return {"cache_management": "disabled"} @@ -2064,6 +2078,7 @@ def apply_cache_policy_from_env(self) -> dict[str, Any]: Apply cache policies from environment variables. :return: Dictionary with applied configuration + """ if not self._cache_manager: return {"cache_management": "disabled"} @@ -2112,6 +2127,7 @@ def export_cache_configuration(self, format: str = "env") -> dict[str, Any]: :param format: Export format - "env", "json", "yaml" :return: Dictionary with exported configuration + """ if not self._cache_manager: return {"cache_management": "disabled"} diff --git a/tfbpapi/datainfo/datacard.py b/tfbpapi/datainfo/datacard.py index e59fc4c..c58cd2d 100644 --- a/tfbpapi/datainfo/datacard.py +++ b/tfbpapi/datainfo/datacard.py @@ -21,17 +21,21 @@ class DataCard: - """Easy-to-use interface for exploring HuggingFace dataset metadata. + """ + Easy-to-use interface for exploring HuggingFace dataset metadata. + + Provides methods to discover and explore dataset contents, configurations, and + metadata without loading the actual genomic data. - Provides methods to discover and explore dataset contents, configurations, - and metadata without loading the actual genomic data. """ - def __init__(self, repo_id: str, token: Optional[str] = None): - """Initialize DataCard for a repository. + def __init__(self, repo_id: str, token: str | None = None): + """ + Initialize DataCard for a repository. :param repo_id: HuggingFace repository identifier (e.g., "user/dataset") :param token: Optional HuggingFace token for authentication + """ self.repo_id = repo_id self.token = token @@ -43,8 +47,8 @@ def __init__(self, repo_id: str, token: Optional[str] = None): self._size_fetcher = HfSizeInfoFetcher(token=token) # Cache for parsed card - self._dataset_card: Optional[DatasetCard] = None - self._metadata_cache: Dict[str, List[ExtractedMetadata]] = {} + self._dataset_card: DatasetCard | None = None + self._metadata_cache: dict[str, list[ExtractedMetadata]] = {} @property def dataset_card(self) -> DatasetCard: @@ -75,68 +79,77 @@ def _load_and_validate_card(self) -> None: # Create a more user-friendly error message error_details = [] for error in e.errors(): - field_path = " -> ".join(str(x) for x in error['loc']) - error_type = error['type'] - error_msg = error['msg'] - input_value = error.get('input', 'N/A') + field_path = " -> ".join(str(x) for x in error["loc"]) + error_type = error["type"] + error_msg = error["msg"] + input_value = error.get("input", "N/A") - if 'dtype' in field_path and error_type == 'string_type': + if "dtype" in field_path and error_type == "string_type": error_details.append( f"Field '{field_path}': Expected a simple data type string (like 'string', 'int64', 'float64') " f"but got a complex structure. This might be a categorical field with class labels. " f"Actual value: {input_value}" ) else: - error_details.append(f"Field '{field_path}': {error_msg} (got: {input_value})") + error_details.append( + f"Field '{field_path}': {error_msg} (got: {input_value})" + ) - detailed_msg = f"Dataset card validation failed for {self.repo_id}:\n" + "\n".join(f" - {detail}" for detail in error_details) + detailed_msg = ( + f"Dataset card validation failed for {self.repo_id}:\n" + + "\n".join(f" - {detail}" for detail in error_details) + ) self.logger.error(detailed_msg) raise DataCardValidationError(detailed_msg) from e except HfDataFetchError as e: raise DataCardError(f"Failed to fetch dataset card: {e}") from e @property - def configs(self) -> List[DatasetConfig]: + def configs(self) -> list[DatasetConfig]: """Get all dataset configurations.""" return self.dataset_card.configs - def get_config(self, config_name: str) -> Optional[DatasetConfig]: + def get_config(self, config_name: str) -> DatasetConfig | None: """Get a specific configuration by name.""" return self.dataset_card.get_config_by_name(config_name) def get_configs_by_type( - self, dataset_type: Union[DatasetType, str] - ) -> List[DatasetConfig]: + self, dataset_type: DatasetType | str + ) -> list[DatasetConfig]: """Get configurations by dataset type.""" if isinstance(dataset_type, str): dataset_type = DatasetType(dataset_type) return self.dataset_card.get_configs_by_type(dataset_type) - def get_regulators(self, config_name: Optional[str] = None) -> Set[str]: - """Get all regulators mentioned in the dataset. + def get_regulators(self, config_name: str | None = None) -> set[str]: + """ + Get all regulators mentioned in the dataset. :param config_name: Optional specific config to search, otherwise searches all :return: Set of regulator identifiers found + """ raise NotImplementedError("Method not yet implemented") - def get_experimental_conditions( - self, config_name: Optional[str] = None - ) -> Set[str]: - """Get all experimental conditions mentioned in the dataset. + def get_experimental_conditions(self, config_name: str | None = None) -> set[str]: + """ + Get all experimental conditions mentioned in the dataset. :param config_name: Optional specific config to search, otherwise searches all :return: Set of experimental conditions found + """ raise NotImplementedError("Method not yet implemented") - def get_field_values(self, config_name: str, field_name: str) -> Set[str]: - """Get all unique values for a specific field in a configuration. + def get_field_values(self, config_name: str, field_name: str) -> set[str]: + """ + Get all unique values for a specific field in a configuration. :param config_name: Configuration name :param field_name: Field name to extract values from :return: Set of unique values :raises DataCardError: If config or field not found + """ config = self.get_config(config_name) if not config: @@ -151,7 +164,7 @@ def get_field_values(self, config_name: str, field_name: str) -> Set[str]: return self._extract_field_values(config, field_name) - def _extract_field_values(self, config: DatasetConfig, field_name: str) -> Set[str]: + def _extract_field_values(self, config: DatasetConfig, field_name: str) -> set[str]: """Extract unique values for a field from various sources.""" values = set() @@ -189,7 +202,7 @@ def _extract_field_values(self, config: DatasetConfig, field_name: str) -> Set[s def _extract_partition_values( self, config: DatasetConfig, field_name: str - ) -> Set[str]: + ) -> set[str]: """Extract values from partition structure.""" if ( not config.dataset_info.partitioning @@ -211,7 +224,7 @@ def _extract_partition_values( self.logger.warning(f"Failed to extract partition values for {field_name}") return set() - def get_metadata_relationships(self) -> List[MetadataRelationship]: + def get_metadata_relationships(self) -> list[MetadataRelationship]: """Get relationships between data configs and their metadata.""" relationships = [] data_configs = self.dataset_card.get_data_configs() @@ -233,7 +246,6 @@ def get_metadata_relationships(self) -> List[MetadataRelationship]: ) continue - # Check for embedded metadata if data_config.metadata_fields: relationships.append( @@ -246,8 +258,7 @@ def get_metadata_relationships(self) -> List[MetadataRelationship]: return relationships - - def get_repository_info(self) -> Dict[str, Any]: + def get_repository_info(self) -> dict[str, Any]: """Get general repository information.""" card = self.dataset_card @@ -273,13 +284,13 @@ def get_repository_info(self) -> Dict[str, Any]: "has_default_config": self.dataset_card.get_default_config() is not None, } - def explore_config(self, config_name: str) -> Dict[str, Any]: + def explore_config(self, config_name: str) -> dict[str, Any]: """Get detailed information about a specific configuration.""" config = self.get_config(config_name) if not config: raise DataCardError(f"Configuration '{config_name}' not found") - info: Dict[str, Any] = { + info: dict[str, Any] = { "config_name": config.config_name, "description": config.description, "dataset_type": config.dataset_type.value, diff --git a/tfbpapi/datainfo/fetchers.py b/tfbpapi/datainfo/fetchers.py index 76b33fc..e33ac05 100644 --- a/tfbpapi/datainfo/fetchers.py +++ b/tfbpapi/datainfo/fetchers.py @@ -15,21 +15,25 @@ class HfDataCardFetcher: """Handles fetching dataset cards from HuggingFace Hub.""" - def __init__(self, token: Optional[str] = None): - """Initialize the fetcher. + def __init__(self, token: str | None = None): + """ + Initialize the fetcher. :param token: HuggingFace token for authentication + """ self.logger = logging.getLogger(self.__class__.__name__) self.token = token or os.getenv("HF_TOKEN") - def fetch(self, repo_id: str, repo_type: str = "dataset") -> Dict[str, Any]: - """Fetch and return dataset card data. + def fetch(self, repo_id: str, repo_type: str = "dataset") -> dict[str, Any]: + """ + Fetch and return dataset card data. :param repo_id: Repository identifier (e.g., "user/dataset") :param repo_type: Type of repository ("dataset", "model", "space") :return: Dataset card data as dictionary :raises HfDataFetchError: If fetching fails + """ try: self.logger.debug(f"Fetching dataset card for {repo_id}") @@ -50,28 +54,32 @@ def fetch(self, repo_id: str, repo_type: str = "dataset") -> Dict[str, Any]: class HfSizeInfoFetcher: """Handles fetching size information from HuggingFace Dataset Server API.""" - def __init__(self, token: Optional[str] = None): - """Initialize the fetcher. + def __init__(self, token: str | None = None): + """ + Initialize the fetcher. :param token: HuggingFace token for authentication + """ self.logger = logging.getLogger(self.__class__.__name__) self.token = token or os.getenv("HF_TOKEN") self.base_url = "https://datasets-server.huggingface.co" - def _build_headers(self) -> Dict[str, str]: + def _build_headers(self) -> dict[str, str]: """Build request headers with authentication if available.""" headers = {"User-Agent": "TFBP-API/1.0"} if self.token: headers["Authorization"] = f"Bearer {self.token}" return headers - def fetch(self, repo_id: str) -> Dict[str, Any]: - """Fetch dataset size information. + def fetch(self, repo_id: str) -> dict[str, Any]: + """ + Fetch dataset size information. :param repo_id: Repository identifier (e.g., "user/dataset") :return: Size information as dictionary :raises HfDataFetchError: If fetching fails + """ url = f"{self.base_url}/size" params = {"dataset": repo_id} @@ -113,22 +121,26 @@ def fetch(self, repo_id: str) -> Dict[str, Any]: class HfRepoStructureFetcher: """Handles fetching repository structure from HuggingFace Hub.""" - def __init__(self, token: Optional[str] = None): - """Initialize the fetcher. + def __init__(self, token: str | None = None): + """ + Initialize the fetcher. :param token: HuggingFace token for authentication + """ self.logger = logging.getLogger(self.__class__.__name__) self.token = token or os.getenv("HF_TOKEN") - self._cached_structure: Dict[str, Dict[str, Any]] = {} + self._cached_structure: dict[str, dict[str, Any]] = {} - def fetch(self, repo_id: str, force_refresh: bool = False) -> Dict[str, Any]: - """Fetch repository structure information. + def fetch(self, repo_id: str, force_refresh: bool = False) -> dict[str, Any]: + """ + Fetch repository structure information. :param repo_id: Repository identifier (e.g., "user/dataset") :param force_refresh: If True, bypass cache and fetch fresh data :return: Repository structure information :raises HfDataFetchError: If fetching fails + """ # Check cache first unless force refresh is requested if not force_refresh and repo_id in self._cached_structure: @@ -141,7 +153,7 @@ def fetch(self, repo_id: str, force_refresh: bool = False) -> Dict[str, Any]: # Extract file structure files = [] - partitions: Dict[str, set] = {} + partitions: dict[str, set] = {} for sibling in info.siblings or []: file_info = { @@ -174,12 +186,14 @@ def fetch(self, repo_id: str, force_refresh: bool = False) -> Dict[str, Any]: raise HfDataFetchError(error_msg) from e def _extract_partition_info( - self, file_path: str, partitions: Dict[str, Set[str]] + self, file_path: str, partitions: dict[str, set[str]] ) -> None: - """Extract partition information from file paths. + """ + Extract partition information from file paths. :param file_path: Path to analyze for partitions :param partitions: Dictionary to update with partition info + """ # Look for partition patterns like "column=value" in path partition_pattern = r"([^/=]+)=([^/]+)" @@ -190,29 +204,35 @@ def _extract_partition_info( partitions[column] = set() partitions[column].add(value) - def get_partition_values(self, repo_id: str, partition_column: str, force_refresh: bool = False) -> List[str]: - """Get all values for a specific partition column. + def get_partition_values( + self, repo_id: str, partition_column: str, force_refresh: bool = False + ) -> list[str]: + """ + Get all values for a specific partition column. :param repo_id: Repository identifier :param partition_column: Name of the partition column :param force_refresh: If True, bypass cache and fetch fresh data :return: List of unique partition values :raises HfDataFetchError: If fetching fails + """ structure = self.fetch(repo_id, force_refresh=force_refresh) partition_values = structure.get("partitions", {}).get(partition_column, set()) return sorted(list(partition_values)) def get_dataset_files( - self, repo_id: str, path_pattern: Optional[str] = None, force_refresh: bool = False - ) -> List[Dict[str, Any]]: - """Get dataset files, optionally filtered by path pattern. + self, repo_id: str, path_pattern: str | None = None, force_refresh: bool = False + ) -> list[dict[str, Any]]: + """ + Get dataset files, optionally filtered by path pattern. :param repo_id: Repository identifier :param path_pattern: Optional regex pattern to filter files :param force_refresh: If True, bypass cache and fetch fresh data :return: List of matching files :raises HfDataFetchError: If fetching fails + """ structure = self.fetch(repo_id, force_refresh=force_refresh) files = structure["files"] diff --git a/tfbpapi/datainfo/models.py b/tfbpapi/datainfo/models.py index 2ad8383..980c9fa 100644 --- a/tfbpapi/datainfo/models.py +++ b/tfbpapi/datainfo/models.py @@ -18,14 +18,14 @@ class DatasetType(str, Enum): class ClassLabelType(BaseModel): """Categorical data type with class labels.""" - names: List[str] = Field(..., description="List of possible class names") + names: list[str] = Field(..., description="List of possible class names") class FeatureInfo(BaseModel): """Information about a dataset feature/column.""" name: str = Field(..., description="Column name in the data") - dtype: Union[str, Dict[str, ClassLabelType]] = Field( + dtype: str | dict[str, ClassLabelType] = Field( ..., description="Data type (string, int64, float64, etc.) or categorical class labels", ) @@ -72,10 +72,10 @@ class PartitioningInfo(BaseModel): """Partitioning configuration for datasets.""" enabled: bool = Field(default=False, description="Whether partitioning is enabled") - partition_by: Optional[List[str]] = Field( + partition_by: list[str] | None = Field( default=None, description="Partition column names" ) - path_template: Optional[str] = Field( + path_template: str | None = Field( default=None, description="Path template for partitioned files" ) @@ -90,8 +90,8 @@ class DataFileInfo(BaseModel): class DatasetInfo(BaseModel): """Dataset structure information.""" - features: List[FeatureInfo] = Field(..., description="Feature definitions") - partitioning: Optional[PartitioningInfo] = Field( + features: list[FeatureInfo] = Field(..., description="Feature definitions") + partitioning: PartitioningInfo | None = Field( default=None, description="Partitioning configuration" ) @@ -105,13 +105,13 @@ class DatasetConfig(BaseModel): default: bool = Field( default=False, description="Whether this is the default config" ) - applies_to: Optional[List[str]] = Field( + applies_to: list[str] | None = Field( default=None, description="Configs this metadata applies to" ) - metadata_fields: Optional[List[str]] = Field( + metadata_fields: list[str] | None = Field( default=None, description="Fields for embedded metadata extraction" ) - data_files: List[DataFileInfo] = Field(..., description="Data file information") + data_files: list[DataFileInfo] = Field(..., description="Data file information") dataset_info: DatasetInfo = Field(..., description="Dataset structure information") @field_validator("applies_to") @@ -138,13 +138,13 @@ def metadata_fields_validation(cls, v): class BasicMetadata(BaseModel): """Basic dataset metadata.""" - license: Optional[str] = Field(default=None, description="Dataset license") - language: Optional[List[str]] = Field(default=None, description="Dataset languages") - tags: Optional[List[str]] = Field(default=None, description="Descriptive tags") - pretty_name: Optional[str] = Field( + license: str | None = Field(default=None, description="Dataset license") + language: list[str] | None = Field(default=None, description="Dataset languages") + tags: list[str] | None = Field(default=None, description="Descriptive tags") + pretty_name: str | None = Field( default=None, description="Human-readable dataset name" ) - size_categories: Optional[List[str]] = Field( + size_categories: list[str] | None = Field( default=None, description="Dataset size categories" ) @@ -152,14 +152,14 @@ class BasicMetadata(BaseModel): class DatasetCard(BaseModel): """Complete dataset card model.""" - configs: List[DatasetConfig] = Field(..., description="Dataset configurations") - license: Optional[str] = Field(default=None, description="Dataset license") - language: Optional[List[str]] = Field(default=None, description="Dataset languages") - tags: Optional[List[str]] = Field(default=None, description="Descriptive tags") - pretty_name: Optional[str] = Field( + configs: list[DatasetConfig] = Field(..., description="Dataset configurations") + license: str | None = Field(default=None, description="Dataset license") + language: list[str] | None = Field(default=None, description="Dataset languages") + tags: list[str] | None = Field(default=None, description="Descriptive tags") + pretty_name: str | None = Field( default=None, description="Human-readable dataset name" ) - size_categories: Optional[List[str]] = Field( + size_categories: list[str] | None = Field( default=None, description="Dataset size categories" ) @@ -189,25 +189,25 @@ def at_most_one_default(cls, v): raise ValueError("At most one configuration can be marked as default") return v - def get_config_by_name(self, name: str) -> Optional[DatasetConfig]: + def get_config_by_name(self, name: str) -> DatasetConfig | None: """Get a configuration by name.""" for config in self.configs: if config.config_name == name: return config return None - def get_configs_by_type(self, dataset_type: DatasetType) -> List[DatasetConfig]: + def get_configs_by_type(self, dataset_type: DatasetType) -> list[DatasetConfig]: """Get all configurations of a specific type.""" return [ config for config in self.configs if config.dataset_type == dataset_type ] - def get_default_config(self) -> Optional[DatasetConfig]: + def get_default_config(self) -> DatasetConfig | None: """Get the default configuration if one exists.""" defaults = [config for config in self.configs if config.default] return defaults[0] if defaults else None - def get_data_configs(self) -> List[DatasetConfig]: + def get_data_configs(self) -> list[DatasetConfig]: """Get all non-metadata configurations.""" return [ config @@ -215,7 +215,7 @@ def get_data_configs(self) -> List[DatasetConfig]: if config.dataset_type != DatasetType.METADATA ] - def get_metadata_configs(self) -> List[DatasetConfig]: + def get_metadata_configs(self) -> list[DatasetConfig]: """Get all metadata configurations.""" return [ config @@ -231,7 +231,7 @@ class ExtractedMetadata(BaseModel): field_name: str = Field( ..., description="Field name the metadata was extracted from" ) - values: Set[str] = Field(..., description="Unique values found") + values: set[str] = Field(..., description="Unique values found") extraction_method: str = Field(..., description="How the metadata was extracted") model_config = ConfigDict( diff --git a/tfbpapi/errors.py b/tfbpapi/errors.py index 3c0fdfa..654d6fc 100644 --- a/tfbpapi/errors.py +++ b/tfbpapi/errors.py @@ -6,7 +6,7 @@ class DatasetError(Exception): """Base exception for all dataset-related errors.""" - def __init__(self, message: str, details: Optional[Dict[str, Any]] = None): + def __init__(self, message: str, details: dict[str, Any] | None = None): super().__init__(message) self.details = details or {} @@ -42,9 +42,9 @@ class DataCardParsingError(DatasetError): def __init__( self, message: str, - repo_id: Optional[str] = None, - config_name: Optional[str] = None, - original_error: Optional[Exception] = None, + repo_id: str | None = None, + config_name: str | None = None, + original_error: Exception | None = None, ): details = {} if repo_id: @@ -66,9 +66,9 @@ class HfDataFetchError(DatasetError): def __init__( self, message: str, - repo_id: Optional[str] = None, - status_code: Optional[int] = None, - endpoint: Optional[str] = None, + repo_id: str | None = None, + status_code: int | None = None, + endpoint: str | None = None, ): details = {} if repo_id: @@ -87,7 +87,7 @@ def __init__( class TableNotFoundError(DatasetError): """Raised when requested table doesn't exist.""" - def __init__(self, table_name: str, available_tables: Optional[list] = None): + def __init__(self, table_name: str, available_tables: list | None = None): available_str = ( f"Available tables: {available_tables}" if available_tables @@ -109,7 +109,7 @@ def __init__(self, table_name: str, available_tables: Optional[list] = None): class MissingDatasetTypeError(DatasetError): """Raised when dataset_type field is missing from config.""" - def __init__(self, config_name: str, available_fields: Optional[list] = None): + def __init__(self, config_name: str, available_fields: list | None = None): fields_str = f"Available fields: {available_fields}" if available_fields else "" message = ( f"Missing 'dataset_type' field in config '{config_name}'. {fields_str}" @@ -129,7 +129,7 @@ def __init__(self, config_name: str, available_fields: Optional[list] = None): class InvalidDatasetTypeError(DatasetError): """Raised when dataset_type value is not recognized.""" - def __init__(self, invalid_type: str, valid_types: Optional[list] = None): + def __init__(self, invalid_type: str, valid_types: list | None = None): valid_str = f"Valid types: {valid_types}" if valid_types else "" message = f"Invalid dataset type '{invalid_type}'. {valid_str}" @@ -147,8 +147,8 @@ class ConfigNotFoundError(DatasetError): def __init__( self, config_name: str, - repo_id: Optional[str] = None, - available_configs: Optional[list] = None, + repo_id: str | None = None, + available_configs: list | None = None, ): repo_str = f" in repository '{repo_id}'" if repo_id else "" available_str = ( @@ -171,6 +171,7 @@ def __init__( class DataCardError(DatasetError): """Base exception for DataCard operations.""" + pass @@ -180,8 +181,8 @@ class DataCardValidationError(DataCardError): def __init__( self, message: str, - repo_id: Optional[str] = None, - validation_errors: Optional[list] = None, + repo_id: str | None = None, + validation_errors: list | None = None, ): details = {} if repo_id: @@ -200,8 +201,8 @@ class DataCardMetadataError(DataCardError): def __init__( self, message: str, - config_name: Optional[str] = None, - field_name: Optional[str] = None, + config_name: str | None = None, + field_name: str | None = None, ): details = {} if config_name: diff --git a/tfbpapi/tests/datainfo/__init__.py b/tfbpapi/tests/datainfo/__init__.py index 672d1e8..e38de55 100644 --- a/tfbpapi/tests/datainfo/__init__.py +++ b/tfbpapi/tests/datainfo/__init__.py @@ -1 +1 @@ -"""Tests for the datainfo package.""" \ No newline at end of file +"""Tests for the datainfo package.""" diff --git a/tfbpapi/tests/datainfo/conftest.py b/tfbpapi/tests/datainfo/conftest.py index 51afb26..09aa58f 100644 --- a/tfbpapi/tests/datainfo/conftest.py +++ b/tfbpapi/tests/datainfo/conftest.py @@ -1,8 +1,9 @@ """Shared fixtures and test data for datainfo tests.""" -import pytest from unittest.mock import Mock +import pytest + @pytest.fixture def sample_dataset_card_data(): @@ -25,30 +26,30 @@ def sample_dataset_card_data(): { "name": "gene_id", "dtype": "string", - "description": "Systematic gene identifier" + "description": "Systematic gene identifier", }, { "name": "gene_symbol", "dtype": "string", - "description": "Standard gene symbol" + "description": "Standard gene symbol", }, { "name": "chromosome", "dtype": "string", - "description": "Chromosome identifier" + "description": "Chromosome identifier", }, { "name": "start", "dtype": "int64", - "description": "Gene start position" + "description": "Gene start position", }, { "name": "end", "dtype": "int64", - "description": "Gene end position" - } + "description": "Gene end position", + }, ] - } + }, }, { "config_name": "binding_data", @@ -61,54 +62,59 @@ def sample_dataset_card_data(): { "name": "regulator_symbol", "dtype": "string", - "description": "Transcription factor name" + "description": "Transcription factor name", }, { "name": "target_gene", "dtype": "string", - "description": "Target gene identifier" + "description": "Target gene identifier", }, { "name": "experimental_condition", "dtype": "string", - "description": "Experimental treatment condition" + "description": "Experimental treatment condition", }, { "name": "binding_score", "dtype": "float64", - "description": "Quantitative binding measurement" - } + "description": "Quantitative binding measurement", + }, ] - } + }, }, { "config_name": "genome_map_data", "description": "Genome-wide signal tracks", "dataset_type": "genome_map", - "data_files": [{"split": "train", "path": "tracks/regulator=*/experiment=*/*.parquet"}], + "data_files": [ + { + "split": "train", + "path": "tracks/regulator=*/experiment=*/*.parquet", + } + ], "dataset_info": { "features": [ { "name": "chr", "dtype": "string", - "description": "Chromosome identifier" + "description": "Chromosome identifier", }, { "name": "pos", "dtype": "int32", - "description": "Genomic position" + "description": "Genomic position", }, { "name": "signal", "dtype": "float32", - "description": "Signal intensity" - } + "description": "Signal intensity", + }, ], "partitioning": { "enabled": True, - "partition_by": ["regulator", "experiment"] - } - } + "partition_by": ["regulator", "experiment"], + }, + }, }, { "config_name": "experiment_metadata", @@ -121,22 +127,22 @@ def sample_dataset_card_data(): { "name": "sample_id", "dtype": "string", - "description": "Unique sample identifier" + "description": "Unique sample identifier", }, { "name": "experimental_condition", "dtype": "string", - "description": "Experimental treatment or condition" + "description": "Experimental treatment or condition", }, { "name": "publication_doi", "dtype": "string", - "description": "DOI of associated publication" - } + "description": "DOI of associated publication", + }, ] - } - } - ] + }, + }, + ], } @@ -155,10 +161,10 @@ def minimal_dataset_card_data(): { "name": "test_field", "dtype": "string", - "description": "Test field" + "description": "Test field", } ] - } + }, } ] } @@ -174,9 +180,7 @@ def invalid_dataset_card_data(): "description": "Invalid configuration", # Missing required dataset_type field "data_files": [{"split": "train", "path": "test.parquet"}], - "dataset_info": { - "features": [] # Empty features list - } + "dataset_info": {"features": []}, # Empty features list } ] } @@ -188,38 +192,27 @@ def sample_repo_structure(): return { "repo_id": "test/dataset", "files": [ - { - "path": "features.parquet", - "size": 2048000, - "is_lfs": True - }, - { - "path": "binding/part1.parquet", - "size": 1024000, - "is_lfs": True - }, + {"path": "features.parquet", "size": 2048000, "is_lfs": True}, + {"path": "binding/part1.parquet", "size": 1024000, "is_lfs": True}, { "path": "tracks/regulator=TF1/experiment=exp1/data.parquet", "size": 5120000, - "is_lfs": True + "is_lfs": True, }, { "path": "tracks/regulator=TF1/experiment=exp2/data.parquet", "size": 4096000, - "is_lfs": True + "is_lfs": True, }, { "path": "tracks/regulator=TF2/experiment=exp1/data.parquet", "size": 3072000, - "is_lfs": True - } + "is_lfs": True, + }, ], - "partitions": { - "regulator": {"TF1", "TF2"}, - "experiment": {"exp1", "exp2"} - }, + "partitions": {"regulator": {"TF1", "TF2"}, "experiment": {"exp1", "exp2"}}, "total_files": 5, - "last_modified": "2023-12-01T10:30:00Z" + "last_modified": "2023-12-01T10:30:00Z", } @@ -231,7 +224,7 @@ def sample_size_info(): "num_bytes": 15360000, "num_rows": 150000, "download_size": 12288000, - "dataset_size": 15360000 + "dataset_size": 15360000, } @@ -279,7 +272,7 @@ def sample_feature_info(): return { "name": "gene_symbol", "dtype": "string", - "description": "Standard gene symbol (e.g., HO, GAL1)" + "description": "Standard gene symbol (e.g., HO, GAL1)", } @@ -289,14 +282,11 @@ def sample_partitioning_info(): return { "enabled": True, "partition_by": ["regulator", "condition"], - "path_template": "data/regulator={regulator}/condition={condition}/*.parquet" + "path_template": "data/regulator={regulator}/condition={condition}/*.parquet", } @pytest.fixture def sample_data_file_info(): """Sample data file information.""" - return { - "split": "train", - "path": "genomic_features.parquet" - } \ No newline at end of file + return {"split": "train", "path": "genomic_features.parquet"} diff --git a/tfbpapi/tests/datainfo/test_datacard.py b/tfbpapi/tests/datainfo/test_datacard.py index 40429bd..59345b9 100644 --- a/tfbpapi/tests/datainfo/test_datacard.py +++ b/tfbpapi/tests/datainfo/test_datacard.py @@ -1,7 +1,8 @@ """Tests for the DataCard class.""" -import pytest from unittest.mock import Mock, patch + +import pytest from pydantic import ValidationError from tfbpapi.datainfo import DataCard @@ -16,10 +17,17 @@ class TestDataCard: """Test suite for DataCard class.""" - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_init(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, test_token): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_init( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + test_token, + ): """Test DataCard initialization.""" datacard = DataCard(test_repo_id, token=test_token) @@ -33,10 +41,12 @@ def test_init(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher mock_structure_fetcher.assert_called_once_with(token=test_token) mock_size_fetcher.assert_called_once_with(token=test_token) - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_init_without_token(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_init_without_token( + self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id + ): """Test DataCard initialization without token.""" datacard = DataCard(test_repo_id) @@ -48,10 +58,17 @@ def test_init_without_token(self, mock_size_fetcher, mock_structure_fetcher, moc mock_structure_fetcher.assert_called_once_with(token=None) mock_size_fetcher.assert_called_once_with(token=None) - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_load_and_validate_card_success(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_load_and_validate_card_success( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): """Test successful card loading and validation.""" # Setup mock mock_fetcher_instance = Mock() @@ -68,10 +85,12 @@ def test_load_and_validate_card_success(self, mock_size_fetcher, mock_structure_ assert card.pretty_name == "Test Genomics Dataset" mock_fetcher_instance.fetch.assert_called_once_with(test_repo_id) - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_load_card_no_data(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_load_card_no_data( + self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id + ): """Test handling when no dataset card is found.""" mock_fetcher_instance = Mock() mock_card_fetcher.return_value = mock_fetcher_instance @@ -82,10 +101,17 @@ def test_load_card_no_data(self, mock_size_fetcher, mock_structure_fetcher, mock with pytest.raises(DataCardValidationError, match="No dataset card found"): _ = datacard.dataset_card - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_load_card_validation_error(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, invalid_dataset_card_data): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_load_card_validation_error( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + invalid_dataset_card_data, + ): """Test handling of validation errors.""" mock_fetcher_instance = Mock() mock_card_fetcher.return_value = mock_fetcher_instance @@ -93,13 +119,17 @@ def test_load_card_validation_error(self, mock_size_fetcher, mock_structure_fetc datacard = DataCard(test_repo_id) - with pytest.raises(DataCardValidationError, match="Dataset card validation failed"): + with pytest.raises( + DataCardValidationError, match="Dataset card validation failed" + ): _ = datacard.dataset_card - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_load_card_fetch_error(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_load_card_fetch_error( + self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id + ): """Test handling of fetch errors.""" mock_fetcher_instance = Mock() mock_card_fetcher.return_value = mock_fetcher_instance @@ -110,10 +140,17 @@ def test_load_card_fetch_error(self, mock_size_fetcher, mock_structure_fetcher, with pytest.raises(DataCardError, match="Failed to fetch dataset card"): _ = datacard.dataset_card - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_configs_property(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_configs_property( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): """Test getting all configurations via property.""" mock_fetcher_instance = Mock() mock_card_fetcher.return_value = mock_fetcher_instance @@ -129,10 +166,17 @@ def test_configs_property(self, mock_size_fetcher, mock_structure_fetcher, mock_ assert "genome_map_data" in config_names assert "experiment_metadata" in config_names - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_get_config_by_name(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_get_config_by_name( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): """Test getting a specific configuration by name.""" mock_fetcher_instance = Mock() mock_card_fetcher.return_value = mock_fetcher_instance @@ -148,10 +192,17 @@ def test_get_config_by_name(self, mock_size_fetcher, mock_structure_fetcher, moc # Test non-existent config assert datacard.get_config("nonexistent") is None - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_get_configs_by_type(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_get_configs_by_type( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): """Test getting configurations by dataset type.""" mock_fetcher_instance = Mock() mock_card_fetcher.return_value = mock_fetcher_instance @@ -174,10 +225,17 @@ def test_get_configs_by_type(self, mock_size_fetcher, mock_structure_fetcher, mo assert len(genome_map_configs) == 1 assert genome_map_configs[0].config_name == "genome_map_data" - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_get_field_values_success(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_get_field_values_success( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): """Test getting field values for a specific config and field.""" mock_fetcher_instance = Mock() mock_card_fetcher.return_value = mock_fetcher_instance @@ -190,10 +248,17 @@ def test_get_field_values_success(self, mock_size_fetcher, mock_structure_fetche # Since _extract_field_values returns empty set by default, we expect empty set assert isinstance(values, set) - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_get_field_values_config_not_found(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_get_field_values_config_not_found( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): """Test error when config not found.""" mock_fetcher_instance = Mock() mock_card_fetcher.return_value = mock_fetcher_instance @@ -201,13 +266,22 @@ def test_get_field_values_config_not_found(self, mock_size_fetcher, mock_structu datacard = DataCard(test_repo_id) - with pytest.raises(DataCardError, match="Configuration 'nonexistent' not found"): + with pytest.raises( + DataCardError, match="Configuration 'nonexistent' not found" + ): datacard.get_field_values("nonexistent", "some_field") - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_get_field_values_field_not_found(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_get_field_values_field_not_found( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): """Test error when field not found.""" mock_fetcher_instance = Mock() mock_card_fetcher.return_value = mock_fetcher_instance @@ -218,10 +292,17 @@ def test_get_field_values_field_not_found(self, mock_size_fetcher, mock_structur with pytest.raises(DataCardError, match="Field 'nonexistent' not found"): datacard.get_field_values("binding_data", "nonexistent") - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_get_metadata_relationships(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_get_metadata_relationships( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): """Test getting metadata relationships.""" mock_fetcher_instance = Mock() mock_card_fetcher.return_value = mock_fetcher_instance @@ -243,10 +324,18 @@ def test_get_metadata_relationships(self, mock_size_fetcher, mock_structure_fetc assert embedded_rels[0].data_config == "binding_data" assert embedded_rels[0].metadata_config == "binding_data_embedded" - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_get_repository_info_success(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data, sample_repo_structure): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_get_repository_info_success( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + sample_repo_structure, + ): """Test getting repository information.""" mock_card_fetcher_instance = Mock() mock_structure_fetcher_instance = Mock() @@ -272,10 +361,17 @@ def test_get_repository_info_success(self, mock_size_fetcher, mock_structure_fet assert info["last_modified"] == "2023-12-01T10:30:00Z" assert info["has_default_config"] is True - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_get_repository_info_fetch_error(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_get_repository_info_fetch_error( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): """Test getting repository info when structure fetch fails.""" mock_card_fetcher_instance = Mock() mock_structure_fetcher_instance = Mock() @@ -283,7 +379,9 @@ def test_get_repository_info_fetch_error(self, mock_size_fetcher, mock_structure mock_structure_fetcher.return_value = mock_structure_fetcher_instance mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data - mock_structure_fetcher_instance.fetch.side_effect = HfDataFetchError("Structure fetch failed") + mock_structure_fetcher_instance.fetch.side_effect = HfDataFetchError( + "Structure fetch failed" + ) datacard = DataCard(test_repo_id) @@ -293,10 +391,17 @@ def test_get_repository_info_fetch_error(self, mock_size_fetcher, mock_structure assert info["total_files"] is None assert info["last_modified"] is None - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_explore_config(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_explore_config( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): """Test exploring a specific configuration.""" mock_fetcher_instance = Mock() mock_card_fetcher.return_value = mock_fetcher_instance @@ -313,22 +418,35 @@ def test_explore_config(self, mock_size_fetcher, mock_structure_fetcher, mock_ca assert config_info["is_default"] is False assert config_info["num_features"] == 4 assert len(config_info["features"]) == 4 - assert config_info["metadata_fields"] == ["regulator_symbol", "experimental_condition"] + assert config_info["metadata_fields"] == [ + "regulator_symbol", + "experimental_condition", + ] # Test config with partitioning partitioned_config_info = datacard.explore_config("genome_map_data") assert "partitioning" in partitioned_config_info assert partitioned_config_info["partitioning"]["enabled"] is True - assert partitioned_config_info["partitioning"]["partition_by"] == ["regulator", "experiment"] + assert partitioned_config_info["partitioning"]["partition_by"] == [ + "regulator", + "experiment", + ] # Test metadata config with applies_to metadata_config_info = datacard.explore_config("experiment_metadata") assert metadata_config_info["applies_to"] == ["binding_data"] - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_explore_config_not_found(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_explore_config_not_found( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): """Test exploring a non-existent configuration.""" mock_fetcher_instance = Mock() mock_card_fetcher.return_value = mock_fetcher_instance @@ -336,13 +454,23 @@ def test_explore_config_not_found(self, mock_size_fetcher, mock_structure_fetche datacard = DataCard(test_repo_id) - with pytest.raises(DataCardError, match="Configuration 'nonexistent' not found"): + with pytest.raises( + DataCardError, match="Configuration 'nonexistent' not found" + ): datacard.explore_config("nonexistent") - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_summary(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data, sample_repo_structure): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_summary( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + sample_repo_structure, + ): """Test getting a summary of the dataset.""" mock_card_fetcher_instance = Mock() mock_structure_fetcher_instance = Mock() @@ -366,10 +494,17 @@ def test_summary(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetc assert "experiment_metadata" in summary assert "(default)" in summary # genomic_features is marked as default - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_extract_partition_values(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_extract_partition_values( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): """Test extracting partition values.""" mock_card_fetcher_instance = Mock() mock_structure_fetcher_instance = Mock() @@ -377,7 +512,11 @@ def test_extract_partition_values(self, mock_size_fetcher, mock_structure_fetche mock_structure_fetcher.return_value = mock_structure_fetcher_instance mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data - mock_structure_fetcher_instance.get_partition_values.return_value = ["TF1", "TF2", "TF3"] + mock_structure_fetcher_instance.get_partition_values.return_value = [ + "TF1", + "TF2", + "TF3", + ] datacard = DataCard(test_repo_id) @@ -388,12 +527,21 @@ def test_extract_partition_values(self, mock_size_fetcher, mock_structure_fetche values = datacard._extract_partition_values(config, "regulator") assert values == {"TF1", "TF2", "TF3"} - mock_structure_fetcher_instance.get_partition_values.assert_called_once_with(test_repo_id, "regulator") - - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_extract_partition_values_no_partitioning(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data): + mock_structure_fetcher_instance.get_partition_values.assert_called_once_with( + test_repo_id, "regulator" + ) + + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_extract_partition_values_no_partitioning( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): """Test extracting partition values when partitioning is disabled.""" mock_card_fetcher_instance = Mock() mock_structure_fetcher_instance = Mock() @@ -413,10 +561,17 @@ def test_extract_partition_values_no_partitioning(self, mock_size_fetcher, mock_ assert values == set() mock_structure_fetcher_instance.get_partition_values.assert_not_called() - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_extract_partition_values_field_not_in_partitions(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_extract_partition_values_field_not_in_partitions( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): """Test extracting partition values when field is not a partition column.""" mock_card_fetcher_instance = Mock() mock_structure_fetcher_instance = Mock() @@ -436,10 +591,17 @@ def test_extract_partition_values_field_not_in_partitions(self, mock_size_fetche assert values == set() mock_structure_fetcher_instance.get_partition_values.assert_not_called() - @patch('tfbpapi.datainfo.datacard.HfDataCardFetcher') - @patch('tfbpapi.datainfo.datacard.HfRepoStructureFetcher') - @patch('tfbpapi.datainfo.datacard.HfSizeInfoFetcher') - def test_extract_partition_values_fetch_error(self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id, sample_dataset_card_data): + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_extract_partition_values_fetch_error( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): """Test extracting partition values when fetch fails.""" mock_card_fetcher_instance = Mock() mock_structure_fetcher_instance = Mock() @@ -447,7 +609,9 @@ def test_extract_partition_values_fetch_error(self, mock_size_fetcher, mock_stru mock_structure_fetcher.return_value = mock_structure_fetcher_instance mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data - mock_structure_fetcher_instance.get_partition_values.side_effect = HfDataFetchError("Fetch failed") + mock_structure_fetcher_instance.get_partition_values.side_effect = ( + HfDataFetchError("Fetch failed") + ) datacard = DataCard(test_repo_id) @@ -456,4 +620,3 @@ def test_extract_partition_values_fetch_error(self, mock_size_fetcher, mock_stru # Should return empty set on error assert values == set() - diff --git a/tfbpapi/tests/datainfo/test_fetchers.py b/tfbpapi/tests/datainfo/test_fetchers.py index 234878a..a0a04fe 100644 --- a/tfbpapi/tests/datainfo/test_fetchers.py +++ b/tfbpapi/tests/datainfo/test_fetchers.py @@ -1,7 +1,8 @@ """Tests for datainfo fetcher classes.""" -import pytest from unittest.mock import Mock, patch + +import pytest import requests from requests import HTTPError @@ -23,18 +24,20 @@ def test_init_with_token(self, test_token): def test_init_without_token(self): """Test initialization without token.""" - with patch.dict('os.environ', {}, clear=True): + with patch.dict("os.environ", {}, clear=True): fetcher = HfDataCardFetcher() assert fetcher.token is None def test_init_with_env_token(self, test_token): """Test initialization with environment token.""" - with patch.dict('os.environ', {'HF_TOKEN': test_token}): + with patch.dict("os.environ", {"HF_TOKEN": test_token}): fetcher = HfDataCardFetcher() assert fetcher.token == test_token - @patch('tfbpapi.datainfo.fetchers.DatasetCard') - def test_fetch_success(self, mock_dataset_card, test_repo_id, sample_dataset_card_data): + @patch("tfbpapi.datainfo.fetchers.DatasetCard") + def test_fetch_success( + self, mock_dataset_card, test_repo_id, sample_dataset_card_data + ): """Test successful dataset card fetch.""" # Setup mock mock_card = Mock() @@ -49,7 +52,7 @@ def test_fetch_success(self, mock_dataset_card, test_repo_id, sample_dataset_car test_repo_id, repo_type="dataset", token="test_token" ) - @patch('tfbpapi.datainfo.fetchers.DatasetCard') + @patch("tfbpapi.datainfo.fetchers.DatasetCard") def test_fetch_no_data_section(self, mock_dataset_card, test_repo_id): """Test fetch when dataset card has no data section.""" # Setup mock with no data @@ -62,7 +65,7 @@ def test_fetch_no_data_section(self, mock_dataset_card, test_repo_id): assert result == {} - @patch('tfbpapi.datainfo.fetchers.DatasetCard') + @patch("tfbpapi.datainfo.fetchers.DatasetCard") def test_fetch_exception(self, mock_dataset_card, test_repo_id): """Test fetch when DatasetCard.load raises exception.""" mock_dataset_card.load.side_effect = Exception("API Error") @@ -74,7 +77,7 @@ def test_fetch_exception(self, mock_dataset_card, test_repo_id): def test_fetch_different_repo_types(self, sample_dataset_card_data): """Test fetch with different repository types.""" - with patch('tfbpapi.datainfo.fetchers.DatasetCard') as mock_dataset_card: + with patch("tfbpapi.datainfo.fetchers.DatasetCard") as mock_dataset_card: mock_card = Mock() mock_card.data.to_dict.return_value = sample_dataset_card_data mock_dataset_card.load.return_value = mock_card @@ -119,7 +122,7 @@ def test_build_headers_without_token(self): assert headers["User-Agent"] == "TFBP-API/1.0" assert "Authorization" not in headers - @patch('tfbpapi.datainfo.fetchers.requests.get') + @patch("tfbpapi.datainfo.fetchers.requests.get") def test_fetch_success(self, mock_get, test_repo_id, sample_size_info): """Test successful size info fetch.""" # Setup mock response @@ -139,7 +142,7 @@ def test_fetch_success(self, mock_get, test_repo_id, sample_size_info): assert call_args[1]["headers"]["Authorization"] == "Bearer test_token" assert call_args[1]["timeout"] == 30 - @patch('tfbpapi.datainfo.fetchers.requests.get') + @patch("tfbpapi.datainfo.fetchers.requests.get") def test_fetch_404_error(self, mock_get, test_repo_id): """Test fetch with 404 error.""" # Setup mock 404 response @@ -153,7 +156,7 @@ def test_fetch_404_error(self, mock_get, test_repo_id): with pytest.raises(HfDataFetchError, match="Dataset .* not found"): fetcher.fetch(test_repo_id) - @patch('tfbpapi.datainfo.fetchers.requests.get') + @patch("tfbpapi.datainfo.fetchers.requests.get") def test_fetch_403_error(self, mock_get, test_repo_id): """Test fetch with 403 error.""" # Setup mock 403 response @@ -164,10 +167,12 @@ def test_fetch_403_error(self, mock_get, test_repo_id): fetcher = HfSizeInfoFetcher() - with pytest.raises(HfDataFetchError, match="Access denied.*check token permissions"): + with pytest.raises( + HfDataFetchError, match="Access denied.*check token permissions" + ): fetcher.fetch(test_repo_id) - @patch('tfbpapi.datainfo.fetchers.requests.get') + @patch("tfbpapi.datainfo.fetchers.requests.get") def test_fetch_other_http_error(self, mock_get, test_repo_id): """Test fetch with other HTTP error.""" # Setup mock 500 response @@ -181,7 +186,7 @@ def test_fetch_other_http_error(self, mock_get, test_repo_id): with pytest.raises(HfDataFetchError, match="HTTP error fetching size"): fetcher.fetch(test_repo_id) - @patch('tfbpapi.datainfo.fetchers.requests.get') + @patch("tfbpapi.datainfo.fetchers.requests.get") def test_fetch_request_exception(self, mock_get, test_repo_id): """Test fetch with request exception.""" mock_get.side_effect = requests.RequestException("Network error") @@ -191,7 +196,7 @@ def test_fetch_request_exception(self, mock_get, test_repo_id): with pytest.raises(HfDataFetchError, match="Request failed fetching size"): fetcher.fetch(test_repo_id) - @patch('tfbpapi.datainfo.fetchers.requests.get') + @patch("tfbpapi.datainfo.fetchers.requests.get") def test_fetch_json_decode_error(self, mock_get, test_repo_id): """Test fetch with JSON decode error.""" # Setup mock response with invalid JSON @@ -214,7 +219,7 @@ def test_init(self, test_token): assert fetcher.token == test_token assert fetcher._cached_structure == {} - @patch('tfbpapi.datainfo.fetchers.repo_info') + @patch("tfbpapi.datainfo.fetchers.repo_info") def test_fetch_success(self, mock_repo_info, test_repo_id, sample_repo_structure): """Test successful repository structure fetch.""" # Setup mock repo info @@ -222,7 +227,11 @@ def test_fetch_success(self, mock_repo_info, test_repo_id, sample_repo_structure mock_info.siblings = [ Mock(rfilename="features.parquet", size=2048000, lfs=Mock()), Mock(rfilename="binding/part1.parquet", size=1024000, lfs=Mock()), - Mock(rfilename="tracks/regulator=TF1/experiment=exp1/data.parquet", size=5120000, lfs=Mock()), + Mock( + rfilename="tracks/regulator=TF1/experiment=exp1/data.parquet", + size=5120000, + lfs=Mock(), + ), ] mock_info.last_modified.isoformat.return_value = "2023-12-01T10:30:00Z" mock_repo_info.return_value = mock_info @@ -240,7 +249,7 @@ def test_fetch_success(self, mock_repo_info, test_repo_id, sample_repo_structure repo_id=test_repo_id, repo_type="dataset", token="test_token" ) - @patch('tfbpapi.datainfo.fetchers.repo_info') + @patch("tfbpapi.datainfo.fetchers.repo_info") def test_fetch_with_caching(self, mock_repo_info, test_repo_id): """Test fetch with caching behavior.""" # Setup mock @@ -264,7 +273,7 @@ def test_fetch_with_caching(self, mock_repo_info, test_repo_id): result3 = fetcher.fetch(test_repo_id, force_refresh=True) assert mock_repo_info.call_count == 2 - @patch('tfbpapi.datainfo.fetchers.repo_info') + @patch("tfbpapi.datainfo.fetchers.repo_info") def test_fetch_siblings_none(self, mock_repo_info, test_repo_id): """Test fetch when siblings is None.""" # Setup mock with None siblings @@ -280,7 +289,7 @@ def test_fetch_siblings_none(self, mock_repo_info, test_repo_id): assert result["files"] == [] assert result["partitions"] == {} - @patch('tfbpapi.datainfo.fetchers.repo_info') + @patch("tfbpapi.datainfo.fetchers.repo_info") def test_fetch_exception(self, mock_repo_info, test_repo_id): """Test fetch when repo_info raises exception.""" mock_repo_info.side_effect = Exception("API Error") @@ -296,14 +305,18 @@ def test_extract_partition_info(self): partitions = {} # Test normal partition pattern - fetcher._extract_partition_info("data/regulator=TF1/condition=control/file.parquet", partitions) + fetcher._extract_partition_info( + "data/regulator=TF1/condition=control/file.parquet", partitions + ) assert "regulator" in partitions assert "TF1" in partitions["regulator"] assert "condition" in partitions assert "control" in partitions["condition"] # Test multiple values for same partition - fetcher._extract_partition_info("data/regulator=TF2/condition=treatment/file.parquet", partitions) + fetcher._extract_partition_info( + "data/regulator=TF2/condition=treatment/file.parquet", partitions + ) assert len(partitions["regulator"]) == 2 assert "TF2" in partitions["regulator"] assert "treatment" in partitions["condition"] @@ -313,7 +326,7 @@ def test_extract_partition_info(self): # partitions dict should remain unchanged assert len(partitions) == 2 - @patch('tfbpapi.datainfo.fetchers.repo_info') + @patch("tfbpapi.datainfo.fetchers.repo_info") def test_get_partition_values_success(self, mock_repo_info, test_repo_id): """Test getting partition values for a specific column.""" # Setup mock with partitioned files @@ -331,7 +344,7 @@ def test_get_partition_values_success(self, mock_repo_info, test_repo_id): assert values == ["TF1", "TF2", "TF3"] # Should be sorted - @patch('tfbpapi.datainfo.fetchers.repo_info') + @patch("tfbpapi.datainfo.fetchers.repo_info") def test_get_partition_values_no_partitions(self, mock_repo_info, test_repo_id): """Test getting partition values when no partitions exist.""" # Setup mock with no partitioned files @@ -347,7 +360,7 @@ def test_get_partition_values_no_partitions(self, mock_repo_info, test_repo_id): assert values == [] - @patch('tfbpapi.datainfo.fetchers.repo_info') + @patch("tfbpapi.datainfo.fetchers.repo_info") def test_get_dataset_files_all(self, mock_repo_info, test_repo_id): """Test getting all dataset files.""" # Setup mock @@ -371,7 +384,7 @@ def test_get_dataset_files_all(self, mock_repo_info, test_repo_id): assert files[1]["size"] == 2000 assert files[1]["is_lfs"] is True - @patch('tfbpapi.datainfo.fetchers.repo_info') + @patch("tfbpapi.datainfo.fetchers.repo_info") def test_get_dataset_files_with_pattern(self, mock_repo_info, test_repo_id): """Test getting dataset files with path pattern filter.""" # Setup mock @@ -394,7 +407,7 @@ def test_get_dataset_files_uses_cache(self): """Test that get_dataset_files uses fetch caching.""" fetcher = HfRepoStructureFetcher() - with patch.object(fetcher, 'fetch') as mock_fetch: + with patch.object(fetcher, "fetch") as mock_fetch: mock_fetch.return_value = {"files": []} # First call @@ -409,7 +422,7 @@ def test_get_partition_values_uses_cache(self): """Test that get_partition_values uses fetch caching.""" fetcher = HfRepoStructureFetcher() - with patch.object(fetcher, 'fetch') as mock_fetch: + with patch.object(fetcher, "fetch") as mock_fetch: mock_fetch.return_value = {"partitions": {"regulator": {"TF1", "TF2"}}} # First call @@ -419,4 +432,4 @@ def test_get_partition_values_uses_cache(self): # Second call with force_refresh fetcher.get_partition_values("test/repo", "regulator", force_refresh=True) - mock_fetch.assert_called_with("test/repo", force_refresh=True) \ No newline at end of file + mock_fetch.assert_called_with("test/repo", force_refresh=True) diff --git a/tfbpapi/tests/datainfo/test_models.py b/tfbpapi/tests/datainfo/test_models.py index 1513e46..618a392 100644 --- a/tfbpapi/tests/datainfo/test_models.py +++ b/tfbpapi/tests/datainfo/test_models.py @@ -4,15 +4,15 @@ from pydantic import ValidationError from tfbpapi.datainfo.models import ( - DatasetType, - FeatureInfo, - PartitioningInfo, DataFileInfo, - DatasetInfo, - DatasetConfig, DatasetCard, + DatasetConfig, + DatasetInfo, + DatasetType, ExtractedMetadata, + FeatureInfo, MetadataRelationship, + PartitioningInfo, ) @@ -74,12 +74,8 @@ def test_feature_info_categorical_dtype(self): """Test FeatureInfo with categorical dtype.""" categorical_feature_data = { "name": "mechanism", - "dtype": { - "class_label": { - "names": ["GEV", "ZEV"] - } - }, - "description": "Induction system" + "dtype": {"class_label": {"names": ["GEV", "ZEV"]}}, + "description": "Induction system", } feature = FeatureInfo(**categorical_feature_data) @@ -92,9 +88,7 @@ def test_feature_info_categorical_dtype(self): def test_feature_info_simple_string_dtype(self): """Test FeatureInfo with simple string dtype.""" feature = FeatureInfo( - name="test_field", - dtype="string", - description="Test field" + name="test_field", dtype="string", description="Test field" ) assert feature.dtype == "string" assert feature.get_dtype_summary() == "string" @@ -105,7 +99,7 @@ def test_feature_info_invalid_categorical_dtype(self): FeatureInfo( name="test_field", dtype={"class_label": "invalid"}, # Should be dict with names - description="Test field" + description="Test field", ) def test_feature_info_unknown_dtype_structure(self): @@ -114,7 +108,7 @@ def test_feature_info_unknown_dtype_structure(self): FeatureInfo( name="test_field", dtype={"unknown_key": "value"}, - description="Test field" + description="Test field", ) @@ -133,7 +127,10 @@ def test_enabled_partitioning_info(self, sample_partitioning_info): partitioning = PartitioningInfo(**sample_partitioning_info) assert partitioning.enabled is True assert partitioning.partition_by == ["regulator", "condition"] - assert partitioning.path_template is not None and "regulator={regulator}" in partitioning.path_template + assert ( + partitioning.path_template is not None + and "regulator={regulator}" in partitioning.path_template + ) def test_partial_partitioning_info(self): """Test partitioning with only some fields set.""" @@ -174,14 +171,19 @@ def test_minimal_dataset_info(self, sample_feature_info): assert len(dataset_info.features) == 1 assert dataset_info.partitioning is None - def test_dataset_info_with_partitioning(self, sample_feature_info, sample_partitioning_info): + def test_dataset_info_with_partitioning( + self, sample_feature_info, sample_partitioning_info + ): """Test DatasetInfo with partitioning.""" features = [FeatureInfo(**sample_feature_info)] partitioning = PartitioningInfo(**sample_partitioning_info) dataset_info = DatasetInfo(features=features, partitioning=partitioning) assert len(dataset_info.features) == 1 - assert dataset_info.partitioning is not None and dataset_info.partitioning.enabled is True + assert ( + dataset_info.partitioning is not None + and dataset_info.partitioning.enabled is True + ) def test_dataset_info_empty_features_error(self): """Test that empty features list is allowed.""" @@ -204,7 +206,7 @@ def test_minimal_dataset_config(self, sample_feature_info, sample_data_file_info description="Test configuration", dataset_type=DatasetType.GENOMIC_FEATURES, data_files=data_files, - dataset_info=dataset_info + dataset_info=dataset_info, ) assert config.config_name == "test_config" @@ -213,7 +215,9 @@ def test_minimal_dataset_config(self, sample_feature_info, sample_data_file_info assert config.applies_to is None assert config.metadata_fields is None - def test_dataset_config_with_applies_to_metadata(self, sample_feature_info, sample_data_file_info): + def test_dataset_config_with_applies_to_metadata( + self, sample_feature_info, sample_data_file_info + ): """Test DatasetConfig with applies_to for metadata types.""" features = [FeatureInfo(**sample_feature_info)] data_files = [DataFileInfo(**sample_data_file_info)] @@ -225,44 +229,55 @@ def test_dataset_config_with_applies_to_metadata(self, sample_feature_info, samp dataset_type=DatasetType.METADATA, applies_to=["data_config1", "data_config2"], data_files=data_files, - dataset_info=dataset_info + dataset_info=dataset_info, ) assert config.applies_to == ["data_config1", "data_config2"] - def test_dataset_config_applies_to_validation_error(self, sample_feature_info, sample_data_file_info): + def test_dataset_config_applies_to_validation_error( + self, sample_feature_info, sample_data_file_info + ): """Test that applies_to is only valid for metadata types.""" features = [FeatureInfo(**sample_feature_info)] data_files = [DataFileInfo(**sample_data_file_info)] dataset_info = DatasetInfo(features=features) - with pytest.raises(ValidationError, match="applies_to field is only valid for metadata dataset types"): + with pytest.raises( + ValidationError, + match="applies_to field is only valid for metadata dataset types", + ): DatasetConfig( config_name="invalid_config", description="Invalid configuration", dataset_type=DatasetType.GENOMIC_FEATURES, # Not a metadata type applies_to=["some_config"], # This should cause validation error data_files=data_files, - dataset_info=dataset_info + dataset_info=dataset_info, ) - def test_dataset_config_empty_metadata_fields_error(self, sample_feature_info, sample_data_file_info): + def test_dataset_config_empty_metadata_fields_error( + self, sample_feature_info, sample_data_file_info + ): """Test that empty metadata_fields list raises error.""" features = [FeatureInfo(**sample_feature_info)] data_files = [DataFileInfo(**sample_data_file_info)] dataset_info = DatasetInfo(features=features) - with pytest.raises(ValidationError, match="metadata_fields cannot be empty list"): + with pytest.raises( + ValidationError, match="metadata_fields cannot be empty list" + ): DatasetConfig( config_name="test_config", description="Test configuration", dataset_type=DatasetType.ANNOTATED_FEATURES, metadata_fields=[], # Empty list should cause error data_files=data_files, - dataset_info=dataset_info + dataset_info=dataset_info, ) - def test_dataset_config_with_metadata_fields(self, sample_feature_info, sample_data_file_info): + def test_dataset_config_with_metadata_fields( + self, sample_feature_info, sample_data_file_info + ): """Test DatasetConfig with valid metadata_fields.""" features = [FeatureInfo(**sample_feature_info)] data_files = [DataFileInfo(**sample_data_file_info)] @@ -274,7 +289,7 @@ def test_dataset_config_with_metadata_fields(self, sample_feature_info, sample_d dataset_type=DatasetType.ANNOTATED_FEATURES, metadata_fields=["field1", "field2"], data_files=data_files, - dataset_info=dataset_info + dataset_info=dataset_info, ) assert config.metadata_fields == ["field1", "field2"] @@ -301,10 +316,14 @@ def test_full_dataset_card(self, sample_dataset_card_data): def test_empty_configs_error(self): """Test that empty configs list raises error.""" - with pytest.raises(ValidationError, match="At least one dataset configuration is required"): + with pytest.raises( + ValidationError, match="At least one dataset configuration is required" + ): DatasetCard(configs=[]) - def test_duplicate_config_names_error(self, sample_feature_info, sample_data_file_info): + def test_duplicate_config_names_error( + self, sample_feature_info, sample_data_file_info + ): """Test that duplicate config names raise error.""" features = [FeatureInfo(**sample_feature_info)] data_files = [DataFileInfo(**sample_data_file_info)] @@ -315,7 +334,7 @@ def test_duplicate_config_names_error(self, sample_feature_info, sample_data_fil description="First config", dataset_type=DatasetType.GENOMIC_FEATURES, data_files=data_files, - dataset_info=dataset_info + dataset_info=dataset_info, ) config2 = DatasetConfig( @@ -323,13 +342,15 @@ def test_duplicate_config_names_error(self, sample_feature_info, sample_data_fil description="Second config", dataset_type=DatasetType.ANNOTATED_FEATURES, data_files=data_files, - dataset_info=dataset_info + dataset_info=dataset_info, ) with pytest.raises(ValidationError, match="Configuration names must be unique"): DatasetCard(configs=[config1, config2]) - def test_multiple_default_configs_error(self, sample_feature_info, sample_data_file_info): + def test_multiple_default_configs_error( + self, sample_feature_info, sample_data_file_info + ): """Test that multiple default configs raise error.""" features = [FeatureInfo(**sample_feature_info)] data_files = [DataFileInfo(**sample_data_file_info)] @@ -341,7 +362,7 @@ def test_multiple_default_configs_error(self, sample_feature_info, sample_data_f dataset_type=DatasetType.GENOMIC_FEATURES, default=True, data_files=data_files, - dataset_info=dataset_info + dataset_info=dataset_info, ) config2 = DatasetConfig( @@ -350,10 +371,12 @@ def test_multiple_default_configs_error(self, sample_feature_info, sample_data_f dataset_type=DatasetType.ANNOTATED_FEATURES, default=True, # Another default data_files=data_files, - dataset_info=dataset_info + dataset_info=dataset_info, ) - with pytest.raises(ValidationError, match="At most one configuration can be marked as default"): + with pytest.raises( + ValidationError, match="At most one configuration can be marked as default" + ): DatasetCard(configs=[config1, config2]) def test_get_config_by_name(self, sample_dataset_card_data): @@ -418,7 +441,7 @@ def test_extracted_metadata_creation(self): config_name="test_config", field_name="regulator_symbol", values={"TF1", "TF2", "TF3"}, - extraction_method="partition_values" + extraction_method="partition_values", ) assert metadata.config_name == "test_config" @@ -432,7 +455,7 @@ def test_extracted_metadata_serialization(self): config_name="test_config", field_name="condition", values={"control", "treatment"}, - extraction_method="embedded" + extraction_method="embedded", ) # Test basic serialization (sets remain as sets in model_dump) @@ -454,7 +477,7 @@ def test_metadata_relationship_creation(self): relationship = MetadataRelationship( data_config="binding_data", metadata_config="experiment_metadata", - relationship_type="explicit" + relationship_type="explicit", ) assert relationship.data_config == "binding_data" @@ -465,17 +488,14 @@ def test_metadata_relationship_types(self): """Test different relationship types.""" # Test explicit relationship explicit = MetadataRelationship( - data_config="data1", - metadata_config="meta1", - relationship_type="explicit" + data_config="data1", metadata_config="meta1", relationship_type="explicit" ) assert explicit.relationship_type == "explicit" - # Test embedded relationship embedded = MetadataRelationship( data_config="data3", metadata_config="data3_embedded", - relationship_type="embedded" + relationship_type="embedded", ) - assert embedded.relationship_type == "embedded" \ No newline at end of file + assert embedded.relationship_type == "embedded" From ac55a4bf8e4bcc331715b1a94120df8ba63da8c5 Mon Sep 17 00:00:00 2001 From: chasem Date: Sat, 20 Sep 2025 16:47:13 -0500 Subject: [PATCH 32/49] working on hfqueryapi --- docs/HfCacheManager.md | 2 +- docs/tutorials/cache_manager_tutorial.ipynb | 1535 +++++++++++++ docs/tutorials/database_interface.ipynb | 2266 ------------------ docs/tutorials/datacard_tutorial.ipynb | 150 +- docs/tutorials/hfqueryapi_tutorial.ipynb | 2214 ++++++++++++++++++ mkdocs.yml | 2 + tfbpapi/HfCacheManager.py | 240 +- tfbpapi/HfQueryAPI.py | 2298 ++----------------- tfbpapi/constants.py | 11 + tfbpapi/datainfo/fetchers.py | 10 +- tfbpapi/errors.py | 13 +- tfbpapi/tests/test_HfCacheManager.py | 538 ++++- 12 files changed, 4829 insertions(+), 4450 deletions(-) create mode 100644 docs/tutorials/cache_manager_tutorial.ipynb delete mode 100644 docs/tutorials/database_interface.ipynb create mode 100644 docs/tutorials/hfqueryapi_tutorial.ipynb create mode 100644 tfbpapi/constants.py diff --git a/docs/HfCacheManager.md b/docs/HfCacheManager.md index 6aa53a3..89d5c77 100644 --- a/docs/HfCacheManager.md +++ b/docs/HfCacheManager.md @@ -1 +1 @@ -::: tfbpapi.HfCacheManager.HFCacheManager +::: tfbpapi.HfCacheManager.HfCacheManager diff --git a/docs/tutorials/cache_manager_tutorial.ipynb b/docs/tutorials/cache_manager_tutorial.ipynb new file mode 100644 index 0000000..a043417 --- /dev/null +++ b/docs/tutorials/cache_manager_tutorial.ipynb @@ -0,0 +1,1535 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# HfCacheManager Tutorial: Intelligent Cache Management for HuggingFace Datasets\n", + "\n", + "The `HfCacheManager` class provides sophisticated cache management capabilities for HuggingFace genomics datasets. It extends DataCard functionality with intelligent caching strategies and automated cache cleanup tools.\n", + "\n", + "This tutorial covers:\n", + "- Setting up HfCacheManager for cache management\n", + "- Understanding the 3-case metadata caching strategy\n", + "- Automated cache cleanup by age, size, and revision\n", + "- Cache monitoring and diagnostics\n", + "- Best practices for efficient cache management\n", + "- Integration with data loading workflows\n", + "\n", + "**Prerequisites**: Basic familiarity with DataCard (see datacard_tutorial.ipynb) and HuggingFace datasets." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1. Setting Up HfCacheManager\n", + "\n", + "The HfCacheManager extends DataCard with cache management capabilities. Unlike DataCard which focuses on dataset exploration, HfCacheManager adds intelligent caching and cleanup features." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "HfCacheManager initialized for: BrentLab/hackett_2020\n", + "DuckDB connection: Active\n", + "Logger configured: Yes\n", + "Current HF cache size: 4.6G\n", + "Cached repositories: 9\n" + ] + } + ], + "source": [ + "import duckdb\n", + "import logging\n", + "from tfbpapi.HfCacheManager import HfCacheManager\n", + "from huggingface_hub import scan_cache_dir\n", + "\n", + "# Set up logging to see cache management activities\n", + "logging.basicConfig(level=logging.INFO)\n", + "logger = logging.getLogger(__name__)\n", + "\n", + "# Create DuckDB connection for metadata caching\n", + "conn = duckdb.connect(':memory:')\n", + "\n", + "# Initialize HfCacheManager\n", + "cache_manager = HfCacheManager(\n", + " repo_id='BrentLab/mahendrawada_2025',\n", + " duckdb_conn=conn,\n", + " logger=logger\n", + ")\n", + "\n", + "print(f\"HfCacheManager initialized for: {cache_manager.repo_id}\")\n", + "print(f\"DuckDB connection: {'Active' if conn else 'None'}\")\n", + "print(f\"Logger configured: {'Yes' if logger else 'No'}\")\n", + "\n", + "# Show current cache status -- NOTE: this is from huggingface_hub,\n", + "# not from HfCacheManager\n", + "cache_info = scan_cache_dir()\n", + "print(f\"Current HF cache size: {cache_info.size_on_disk_str}\")\n", + "print(f\"Cached repositories: {len(cache_info.repos)}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "HFCacheInfo(size_on_disk=4576042428, repos=frozenset({CachedRepoInfo(repo_id='BrentLab/mahendrawada_2025', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025'), size_on_disk=1997004, nb_files=9, revisions=frozenset({CachedRevisionInfo(commit_hash='8bea431be57e21633be4174df291540b1fae212a', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/8bea431be57e21633be4174df291540b1fae212a'), size_on_disk=18603, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/8bea431be57e21633be4174df291540b1fae212a/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/6a2a5a6a8006e386c0e47a2a98151442046d3d41'), size_on_disk=18603, blob_last_accessed=1758400385.1923163, blob_last_modified=1758155849.9076133)}), refs=frozenset({'main'}), last_modified=1758155849.9076133), CachedRevisionInfo(commit_hash='d602c1651f3117dd8d3c7443440eb819b7aa2ec2', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2'), size_on_disk=1963211, files=frozenset({CachedFileInfo(file_name='rnaseq_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2/rnaseq_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/4411789f50aab449658c100f2758ceb410022a0a30f956fd5fe41152915580f9'), size_on_disk=286605, blob_last_accessed=1757439452.1947446, blob_last_modified=1756858618.0563447), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/1bf59ab7ca7d0595586675ff09c4767b04037f29'), size_on_disk=15355, blob_last_accessed=1758055881.95639, blob_last_modified=1757439229.7199314), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756858617.9613454, blob_last_modified=1756858618.033345), CachedFileInfo(file_name='parse_mahendrawada.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2/scripts/parse_mahendrawada.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/c1130d6f8e06f94b1441f73821e3ae727663d0fa'), size_on_disk=6546, blob_last_accessed=1757439229.7259312, blob_last_modified=1757439229.7949307), CachedFileInfo(file_name='chec_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2/chec_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/dd3e967d8c54c6056dc9fd6bdb921c3eed9d10df3a449273bd08faeb1b936765'), size_on_disk=1220601, blob_last_accessed=1757439235.362876, blob_last_modified=1756858618.0613449), CachedFileInfo(file_name='features_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2/features_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/618ea89d880c8a1b4c6d05cc3c13a70cbb05784d10bf2b6c4fc954705203096c'), size_on_disk=431436, blob_last_accessed=1757439459.1206765, blob_last_modified=1756858618.1383443), CachedFileInfo(file_name='checksums.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2/checksums.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/bee0d3fd2c2bd4f07ec16489cdba8d5b4b6a2ae2'), size_on_disk=207, blob_last_accessed=1757439229.7169313, blob_last_modified=1757439229.7729309)}), refs=frozenset(), last_modified=1757439229.7949307), CachedRevisionInfo(commit_hash='66e0cf1fc85e136990b8230d6fa80b64c1091c7c', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/66e0cf1fc85e136990b8230d6fa80b64c1091c7c'), size_on_disk=1956293, files=frozenset({CachedFileInfo(file_name='features_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/66e0cf1fc85e136990b8230d6fa80b64c1091c7c/features_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/618ea89d880c8a1b4c6d05cc3c13a70cbb05784d10bf2b6c4fc954705203096c'), size_on_disk=431436, blob_last_accessed=1757439459.1206765, blob_last_modified=1756858618.1383443), CachedFileInfo(file_name='rnaseq_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/66e0cf1fc85e136990b8230d6fa80b64c1091c7c/rnaseq_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/4411789f50aab449658c100f2758ceb410022a0a30f956fd5fe41152915580f9'), size_on_disk=286605, blob_last_accessed=1757439452.1947446, blob_last_modified=1756858618.0563447), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/66e0cf1fc85e136990b8230d6fa80b64c1091c7c/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756858617.9613454, blob_last_modified=1756858618.033345), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/66e0cf1fc85e136990b8230d6fa80b64c1091c7c/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/2ababbde8810cb0f65bfbceb4f2b5b4b65e491c1'), size_on_disk=15190, blob_last_accessed=1756858550.24473, blob_last_modified=1756858550.24373), CachedFileInfo(file_name='chec_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/66e0cf1fc85e136990b8230d6fa80b64c1091c7c/chec_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/dd3e967d8c54c6056dc9fd6bdb921c3eed9d10df3a449273bd08faeb1b936765'), size_on_disk=1220601, blob_last_accessed=1757439235.362876, blob_last_modified=1756858618.0613449)}), refs=frozenset(), last_modified=1756858618.1383443)}), last_accessed=1758400385.1923163, last_modified=1758155849.9076133), CachedRepoInfo(repo_id='BrentLab/yeast_genome_resources', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources'), size_on_disk=114475, nb_files=7, revisions=frozenset({CachedRevisionInfo(commit_hash='15fdb72f8c31ae58f3e3ce7c90be279383743a2f', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/15fdb72f8c31ae58f3e3ce7c90be279383743a2f'), size_on_disk=88406, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/15fdb72f8c31ae58f3e3ce7c90be279383743a2f/features/chr=chrII/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/d64638adb2290412a962abffe0fbd0546ff139b29064bbf313b0a768449454a5'), size_on_disk=82276, blob_last_accessed=1755812630.656903, blob_last_modified=1755812631.0999012), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/15fdb72f8c31ae58f3e3ce7c90be279383743a2f/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/645da8028fd6abe86ee305f76eab78cdf29be364'), size_on_disk=6130, blob_last_accessed=1755819093.2316637, blob_last_modified=1755819093.2306638)}), refs=frozenset(), last_modified=1755819093.2306638), CachedRevisionInfo(commit_hash='42beb28478e27c5d4c5bb2308b12100e6489ef07', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/42beb28478e27c5d4c5bb2308b12100e6489ef07'), size_on_disk=6125, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/42beb28478e27c5d4c5bb2308b12100e6489ef07/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/ab6cc475062a2767a41b2aaa006faf4d9d2d60d6'), size_on_disk=6125, blob_last_accessed=1758155946.5559895, blob_last_modified=1758155946.5549896)}), refs=frozenset({'main'}), last_modified=1758155946.5549896), CachedRevisionInfo(commit_hash='25b47a191e40728efc2437d906e84317f922b36b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/25b47a191e40728efc2437d906e84317f922b36b'), size_on_disk=5426, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/25b47a191e40728efc2437d906e84317f922b36b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/9cdf7b342b249f25f2c2cbe46a2e0a26ded4f692'), size_on_disk=5426, blob_last_accessed=1755815405.3308983, blob_last_modified=1755815405.3298984)}), refs=frozenset(), last_modified=1755815405.3298984), CachedRevisionInfo(commit_hash='aae6e5ea8139e23940d11b4d7e77684c78bb7f6b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/aae6e5ea8139e23940d11b4d7e77684c78bb7f6b'), size_on_disk=4585, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/aae6e5ea8139e23940d11b4d7e77684c78bb7f6b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/4320f8f18620d988a5fc32872a671ace406fd0a2'), size_on_disk=4585, blob_last_accessed=1755814342.1923234, blob_last_modified=1755814342.1913235)}), refs=frozenset(), last_modified=1755814342.1913235), CachedRevisionInfo(commit_hash='7441b9a8c858332e730e7ddb9d255460ae698626', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/7441b9a8c858332e730e7ddb9d255460ae698626'), size_on_disk=87714, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/7441b9a8c858332e730e7ddb9d255460ae698626/features/chr=chrII/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/d64638adb2290412a962abffe0fbd0546ff139b29064bbf313b0a768449454a5'), size_on_disk=82276, blob_last_accessed=1755812630.656903, blob_last_modified=1755812631.0999012), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/7441b9a8c858332e730e7ddb9d255460ae698626/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/e63c2087b2e56dc15e8ac6cf5693c2391127b732'), size_on_disk=5438, blob_last_accessed=1755816785.70087, blob_last_modified=1755816785.6988702)}), refs=frozenset(), last_modified=1755816785.6988702), CachedRevisionInfo(commit_hash='a5ecb243ba27ca4b4d6ad1b60cc160c033ca2be6', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/a5ecb243ba27ca4b4d6ad1b60cc160c033ca2be6'), size_on_disk=82276, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/a5ecb243ba27ca4b4d6ad1b60cc160c033ca2be6/features/chr=chrII/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/d64638adb2290412a962abffe0fbd0546ff139b29064bbf313b0a768449454a5'), size_on_disk=82276, blob_last_accessed=1755812630.656903, blob_last_modified=1755812631.0999012)}), refs=frozenset(), last_modified=1755812631.0999012), CachedRevisionInfo(commit_hash='6607f7be76546563db0a68a9365c0a858bb5f3a1', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/6607f7be76546563db0a68a9365c0a858bb5f3a1'), size_on_disk=4495, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/6607f7be76546563db0a68a9365c0a858bb5f3a1/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/a31c4647c502767c22b5fe725ec8de58686a02d4'), size_on_disk=4495, blob_last_accessed=1755813454.8008156, blob_last_modified=1755813454.7998157)}), refs=frozenset(), last_modified=1755813454.7998157)}), last_accessed=1758155946.5559895, last_modified=1758155946.5549896), CachedRepoInfo(repo_id='BrentLab/barkai_compendium', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium'), size_on_disk=3579068970, nb_files=504, revisions=frozenset({CachedRevisionInfo(commit_hash='a987ef37c72fcd07b18828d320bc4305480daade', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade'), size_on_disk=3579068970, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417769/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e640018be0b2de04e0ed77c7586c413990876e91348531f43511c20da5fe279a'), size_on_disk=11568261, blob_last_accessed=1756926331.59533, blob_last_modified=1756926333.0913236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417930/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62f17648a07c7675d6a101e3d12efeae247c742e37eaacf96f95e03dceedefde'), size_on_disk=20574235, blob_last_accessed=1756926361.2242014, blob_last_modified=1756926362.5701957), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417693/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/98a5478f5bd4feb033ac69510f2bf785c87777377b7b8b6dce24b8f89dbd6a03'), size_on_disk=18580692, blob_last_accessed=1756926319.7013817, blob_last_modified=1756926321.9783719), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417686/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6a42f24b250baf0d88696bc123f01494a400b3f03f2ff2a1fc828f9295721a16'), size_on_disk=12070748, blob_last_accessed=1756926318.2063882, blob_last_modified=1756926319.627382), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417618/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d2ce91297551bc97b4fca46007f80c8846e5f9e9c78d9c1d7ded02fc31b3ef38'), size_on_disk=9239111, blob_last_accessed=1756926302.3654573, blob_last_modified=1756926304.3714485), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417724/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e5beee12039dfe6b4dae180a31ac0897dea725754554db43e2df9f99da94db58'), size_on_disk=4535402, blob_last_accessed=1756926324.69836, blob_last_modified=1756926325.9703546), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417794/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f15be566c029c52cf46f6258e589529afb3a002f010710a54d04dd6151f8b684'), size_on_disk=10205023, blob_last_accessed=1756926335.9063113, blob_last_modified=1756926337.151306), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417788/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fd6f44471f1b86568bc059f25e5ccec7bda2b85b9f8be536aeaf2305fe015f5d'), size_on_disk=9112879, blob_last_accessed=1756926334.546317, blob_last_modified=1756926335.971311), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4331f53e52f628f99a9695fa70339b08eb69a011cc8a3cbdb08dfa8e6c5d3ddd'), size_on_disk=10779086, blob_last_accessed=1756926376.8381338, blob_last_modified=1756926377.9141293), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417777/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bd953fd8a3ee2a5c90382c1c9adc8091fbca801aa87f07a409b6dd5a9704421f'), size_on_disk=1267333, blob_last_accessed=1756926332.9623241, blob_last_modified=1756926333.6353211), CachedFileInfo(file_name='genome_map.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b8f0ed936cb43c4649126e9b7596cef0ab626a2d'), size_on_disk=142786, blob_last_accessed=1756926300.410466, blob_last_modified=1756926300.4954655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380950/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f508a5da82832b920577592b853da2c2d76ebf0adca587e10b63507f46dc5066'), size_on_disk=3452513, blob_last_accessed=1756926368.7781687, blob_last_modified=1756926369.7081647), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417846/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a3dc440aac6a69d75ec73242a30e59d8183023a2677f1576b5e73d037d6e6654'), size_on_disk=2680468, blob_last_accessed=1756926346.0562673, blob_last_modified=1756926346.7392642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417662/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/90008c75533e5a395bf92b2a4382cfb8d6c27af4921615cac22bc1965375efb5'), size_on_disk=12701661, blob_last_accessed=1756926313.0204108, blob_last_modified=1756926314.158406), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417781/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9dd7989f90f8762246868ccd7d11c9ce876b2b33099efc3a4c5bf1bc7cbcca80'), size_on_disk=6923944, blob_last_accessed=1756926333.4783218, blob_last_modified=1756926335.3743136), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417689/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e27d1d153e5b135a09ec5db80bb05ace721fd60ac90f0693ac34ca3160fdfbdd'), size_on_disk=5015020, blob_last_accessed=1756926319.2973835, blob_last_modified=1756926321.220375), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417909/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c5de46a74c16aee32dfb2be89a50dceaa5704c0c550076dcec483d1c21d983af'), size_on_disk=5969725, blob_last_accessed=1756926357.3362184, blob_last_modified=1756926358.5162132), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417856/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3dc4e868f31759c9ac3d43f40097f5ed2f0efb6307e3bc1ccbed70c50a16ee4e'), size_on_disk=6126482, blob_last_accessed=1756926347.3832614, blob_last_modified=1756926348.3162575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417734/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/10d4c6d9acc5b56370fc32062262ae3871008e20e960c6b352ac27a4e229c905'), size_on_disk=1495194, blob_last_accessed=1756926325.9453547, blob_last_modified=1756926326.745351), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417669/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8eaf08a45dbd72787c5250403f3d3079db9902e77527c7ca438abc740fdfb753'), size_on_disk=7376929, blob_last_accessed=1756926314.6724036, blob_last_modified=1756926316.0383978), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381044/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/89a79f2c5f6258380ff4776af49db6119ac0c04342686705cfde94feebd7c9ca'), size_on_disk=3576881, blob_last_accessed=1756926776.065759, blob_last_modified=1756926778.8987432), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380932/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f214422d02adb2b0a1037eb424d8574123cf27d8a5cd1418feb5733b0249f60c'), size_on_disk=6251591, blob_last_accessed=1756926365.6181824, blob_last_modified=1756926366.5551784), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417937/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e432985b09ba72eab54fc008b87b20a718dbae96d920a61b47de7e16d214227c'), size_on_disk=6445266, blob_last_accessed=1756926362.6341953, blob_last_modified=1756926363.6641908), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417754/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0d840353f1148a08928dfb3177a0abfe635c406fc77d6d9958743585ade6b872'), size_on_disk=1101891, blob_last_accessed=1756926329.758338, blob_last_modified=1756926330.215336), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417757/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6c9c5dbd0513759ace8599b2542a1f99fe0c30feb318665ebdb55bfb111c8c67'), size_on_disk=5834465, blob_last_accessed=1756926330.011337, blob_last_modified=1756926330.7623336), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380964/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3a63df07de544662fb04d5388f34b0c8068efbc19f464b673d8a860a57b5b422'), size_on_disk=956844, blob_last_accessed=1756926370.333162, blob_last_modified=1756926370.8741596), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380926/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fd708dfb8084d767fde32c52acb58e785083ce7becbd2e160277f31460719346'), size_on_disk=6579161, blob_last_accessed=1756926364.8451858, blob_last_modified=1756926365.5441828), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417874/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6674c316eab3b03bbbe3502565efff7650445f5e1453933336d74938e42a14bf'), size_on_disk=6201897, blob_last_accessed=1756926350.5772476, blob_last_modified=1756926351.5142436), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417610/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a93c708ade83c1c4521589a51835a1dcc98972ab900e0eefdb8ce34e64394bf4'), size_on_disk=2566580, blob_last_accessed=1756926301.328462, blob_last_modified=1756926302.4814568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417793/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8a51bd4d953e697022a85f9b527afd45126988ba4bbb796d71c9e5f6c8fffc44'), size_on_disk=10797388, blob_last_accessed=1756926335.4883132, blob_last_modified=1756926336.7013078), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381050/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f3c3bac37a1d4ec633edf53ba305c970eb3de412fd54dfbaf0cdf89f39574334'), size_on_disk=1518412, blob_last_accessed=1756926777.1687527, blob_last_modified=1756926778.028748), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381016/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0e597280417f8f8b5b476bc9d66033afc9412759f5e2f9bd9f3367932cc6f03f'), size_on_disk=820782, blob_last_accessed=1756926377.9791288, blob_last_modified=1756926378.4041271), CachedFileInfo(file_name='GSE178430_metadata.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE178430_metadata.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9673758f13ec096ec4a89642bbe34d60975f5f05'), size_on_disk=61, blob_last_accessed=1756926300.2374666, blob_last_modified=1756926300.2994664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417670/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5e36b63ef1fbb55f821ed2c3d61f0d0bf86cf00846e86ff9dbe6f4d9597f9dc8'), size_on_disk=10881106, blob_last_accessed=1756926314.7704034, blob_last_modified=1756926315.980398), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380944/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7331bfc92dc902a721083ac4ea027481c90ce204d7dca7c493fec8f284db0155'), size_on_disk=8432479, blob_last_accessed=1756926367.299175, blob_last_modified=1756926368.6141694), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380928/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/251ffcb8e06f2ac0db0791065248327a9d1eeef33a07f5c6b25946e04b6c46d8'), size_on_disk=2289693, blob_last_accessed=1756926365.037185, blob_last_modified=1756926366.1961799), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380992/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dbc6b1de202e7fb25701c02607facc3e8c332550907ca0b70ba177533a311d6d'), size_on_disk=2551903, blob_last_accessed=1756926374.2281451, blob_last_modified=1756926375.0341415), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381068/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/16e4412527449a3cb9d2da129c3309a71b2ec9b33a7a39cb9bb31d41ce5d2e04'), size_on_disk=14909067, blob_last_accessed=1756926780.5687337, blob_last_modified=1756926782.3187242), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380961/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fc4356aaa2a30217145b4aecc4f3fdd2575fcd696a2603fea3d8112998f770c7'), size_on_disk=798599, blob_last_accessed=1756926370.1791627, blob_last_modified=1756926370.589161), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417612/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ee40f7af39e729ee79ef18d6777bbd632b7cc7811041a32d54f4a02b42638464'), size_on_disk=3509045, blob_last_accessed=1756926301.5874608, blob_last_modified=1756926302.6274562), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380933/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/07c22e11c56ce5ecb6c22c910c3d3a0d9b54fd341bef0513b4bf42a9c53ec6a0'), size_on_disk=10138164, blob_last_accessed=1756926365.8761814, blob_last_modified=1756926366.7471776), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380955/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/369e0f1de51ea197e928618e562613eeed2ed59ce5c1389ce07ff6dd96d391b4'), size_on_disk=3596907, blob_last_accessed=1756926369.4561658, blob_last_modified=1756926370.2661622), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380934/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d264f2a91c87696f6a07f573f1d921c91e1d53e3afca59610ba4703b3683b617'), size_on_disk=4601537, blob_last_accessed=1756926366.1091802, blob_last_modified=1756926366.9191768), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417755/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f4ffef985aed69a80417b5179ef9923bd73378a93a97f2dff128b1f7716b4599'), size_on_disk=8437424, blob_last_accessed=1756926329.777338, blob_last_modified=1756926331.2423315), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380967/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3abcaaa4db306d580dc27882172ced53d591b8aa69a1805bd2a9eea0448ff941'), size_on_disk=1245882, blob_last_accessed=1756926370.6731606, blob_last_modified=1756926371.1021585), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417903/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3ddfaef9dc57d0f63b8613408aba3af803dabd5ee87a1584edd9391b2b41bec3'), size_on_disk=11218813, blob_last_accessed=1756926355.9432244, blob_last_modified=1756926357.2232187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417888/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/961a35161b04a0e892f46a0a90b6d4776d7fe07ef82fc7016c95b1a885c4274f'), size_on_disk=9651505, blob_last_accessed=1756926353.298236, blob_last_modified=1756926354.65023), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417666/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/38008595447ab722c1889d0e19babf74299822ac7733504a4f09e1af1e4ad1ac'), size_on_disk=7712422, blob_last_accessed=1756926313.945407, blob_last_modified=1756926315.073402), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417837/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/04d099809ca78bd87dcb9921398681ac64e827439049a37e97a71ba0828debfb'), size_on_disk=8348990, blob_last_accessed=1756926344.4012744, blob_last_modified=1756926345.6152692), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380939/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dc6382d9dba7855504a6c7fa2a4adc44f564e25ff5c54eedd082c3cbe0a520a0'), size_on_disk=9235430, blob_last_accessed=1756926366.630178, blob_last_modified=1756926367.5861738), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417667/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef18f0ec136ef4b0facd217b32604556a7c9f514d75e26d2be17eb8da9b74a14'), size_on_disk=7979245, blob_last_accessed=1756926314.2284057, blob_last_modified=1756926315.734399), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417814/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9f70b1469a6d0ad8758aa7a44e754e07ccc401906f8af403a72075d763919535'), size_on_disk=7618977, blob_last_accessed=1756926339.5142956, blob_last_modified=1756926341.0872889), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381058/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9dc97b5892fcc8af593c03fad2040396b9b10626a655319ad40b38acd75e1d7b'), size_on_disk=11534270, blob_last_accessed=1756926778.543745, blob_last_modified=1756926779.7317386), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417730/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f2554716083cfa1bb54c2098315b92e1b3537eb265da884c00ba7e7d4c050da'), size_on_disk=14980864, blob_last_accessed=1756926325.5143564, blob_last_modified=1756926326.803351), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bbfeb8571ee8915d793698c2116cef9f2ea61573c66b4ae7066078271c6172d6'), size_on_disk=5132301, blob_last_accessed=1756926362.6441953, blob_last_modified=1756926363.7271905), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417830/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/21e2dbb49f07cb7c2d7277f5bf1a442e216a2991677c3c4ece5c51316795ea77'), size_on_disk=12760016, blob_last_accessed=1756926342.9432807, blob_last_modified=1756926344.1602755), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417614/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dac699f09872b0c58b742c692b341f0b00b7d83284cec5ef838c276a6f48908c'), size_on_disk=1759475, blob_last_accessed=1756926301.7274601, blob_last_modified=1756926302.4774568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381026/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e41f2d7a1b084a404e7f5e9e003b27f88665a983ec73859f7eb42cd8bfa2bdd3'), size_on_disk=2857045, blob_last_accessed=1756926378.898125, blob_last_modified=1756926379.7281213), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417721/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5457a24eab864fb369ae0e6fbdb302876e2a6c65ae83444f18bc01ad08ecfc0c'), size_on_disk=2073045, blob_last_accessed=1756926324.3243616, blob_last_modified=1756926324.9983587), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417843/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6569e23f0eb660e9f7111b9cdd69e7ae797ba265647ee44e4cbf2d8babdeca9e'), size_on_disk=2706587, blob_last_accessed=1756926345.3402703, blob_last_modified=1756926346.125267), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417752/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/efc9f1ecf4a7b06b23da46fb8c7d1879b67a9d6603ed753d09e2028396ec05c5'), size_on_disk=3420764, blob_last_accessed=1756926329.26534, blob_last_modified=1756926330.0613368), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380988/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/27b6e71986cf2b7da20b64dbda0ba47e08753f7184ddd57ed288f11df88d21e1'), size_on_disk=16063760, blob_last_accessed=1756926373.7421472, blob_last_modified=1756926374.932142), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381054/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1a9f9c650973937d852d2de54c0d73336d1516184d29e203563fd9bf8d7fefa5'), size_on_disk=3346485, blob_last_accessed=1756926778.1077476, blob_last_modified=1756926778.8637433), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417665/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7c9b3d448e53fcac9334df092cc003f438cbc462e6785ac3d61202bd65a7125f'), size_on_disk=7481605, blob_last_accessed=1756926313.490409, blob_last_modified=1756926314.7744033), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417945/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6689ba375c993d00d1f0e716d3bc39604d68df654a67b2518c56f4d54c7f4ca6'), size_on_disk=11289854, blob_last_accessed=1756926364.0191894, blob_last_modified=1756926364.9011855), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380981/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/198a0b08846a11e72de25e0e9c44f495ef88c9260de2e036d240715004c04953'), size_on_disk=8535317, blob_last_accessed=1756926372.5771523, blob_last_modified=1756926373.8291469), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381040/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a7c622a8ae1f5083eef26ecbcc2444055a9265b674ae80002e5468c94c0eb070'), size_on_disk=9682671, blob_last_accessed=1756926774.782766, blob_last_modified=1756926776.7967548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381019/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3023e878b223b00b77d3411a6d20195b85aa7e61a8445fe725ff694c77816053'), size_on_disk=1022843, blob_last_accessed=1756926378.3191273, blob_last_modified=1756926378.864125), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381032/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/335a75b123ae38273f1b1a10f16e9547eec356f68a1d3078926f011d3332e2b5'), size_on_disk=656385, blob_last_accessed=1756926379.6781216, blob_last_modified=1756926775.4077625), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380979/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef743a2a6a8fe44b62963b981ef0429676e7854243ab07d450d9c3ab569c7e8e'), size_on_disk=3735979, blob_last_accessed=1756926372.2201538, blob_last_modified=1756926373.6791475), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417867/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f4783b00d4674df92d84f9a304fb9d143c7b9e4754e3efcb9c072a49a9b9298b'), size_on_disk=8536709, blob_last_accessed=1756926349.3882527, blob_last_modified=1756926350.2882488), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417644/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0c788a7ba70689a796af823df5cc14504dfdc854617952459367364c4498cdeb'), size_on_disk=949200, blob_last_accessed=1756926309.758425, blob_last_modified=1756926310.6444213), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417899/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b4349f94cb0679468e9c28a9439c1a71c88d7288adc3bdad0ca31a2590f1d4e0'), size_on_disk=7910137, blob_last_accessed=1756926355.3832269, blob_last_modified=1756926356.3022227), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380957/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5a2362d35ac90fb1926051e6313886a649074b0022b128741a22a5ee24bb095d'), size_on_disk=5388789, blob_last_accessed=1756926369.5311654, blob_last_modified=1756926370.8571596), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417876/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0713329434bfbd55f1998400d9d44c1d8c7fec6cb1b646c0fdb926f1046f9b47'), size_on_disk=11246388, blob_last_accessed=1756926350.971246, blob_last_modified=1756926352.0472412), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417636/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4a3b2c7076107dcc47a87179cecab9e2051350c94e951a7aadc2ff0cf6fdbaca'), size_on_disk=43479381, blob_last_accessed=1756926307.6494343, blob_last_modified=1756926311.4484177), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380968/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/549ef4e39a437adb99efecdc9b05fee3ca4b31c878b99f7a4cf1398f405ca049'), size_on_disk=3575926, blob_last_accessed=1756926370.7441602, blob_last_modified=1756926371.5941565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380925/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdf190d30101829153acfd62edd4d689ac65a7d737f0e80978c7d460819b5caa'), size_on_disk=9220100, blob_last_accessed=1756926364.6101868, blob_last_modified=1756926366.1331801), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381046/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a96c1819b1010c40fc4d92418e407cc5b5f48f9e24883d4b4cdf3e02d23b688a'), size_on_disk=3071378, blob_last_accessed=1756926776.6277559, blob_last_modified=1756926777.855749), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380974/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ea9eb19e91fedadd9e46d5d7dc9169995065af5b6b8e823f1667117be9d737d0'), size_on_disk=8889742, blob_last_accessed=1756926371.5481567, blob_last_modified=1756926372.2801535), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380931/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/be287270314fed3dded81e0c24a66d8bd991b219fd6a157f1aeaa1690262e563'), size_on_disk=4324179, blob_last_accessed=1756926365.1611843, blob_last_modified=1756926366.1241803), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380940/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4f59a7f9287c427bed27910f08a041e460eff2490c21e2fbdaa5cc9348f5921e'), size_on_disk=6062382, blob_last_accessed=1756926366.8141773, blob_last_modified=1756926368.3051708), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417635/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d2d1c241467bd5c55cf059f793b4236c99d48e59bec853d1052ff6c644e50d1b'), size_on_disk=3044373, blob_last_accessed=1756926307.2014363, blob_last_modified=1756926308.391431), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417855/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ac2218ccb9f80664ce377351ea1dc84bf3f70b36006a86cffdb761a40ddab791'), size_on_disk=1981158, blob_last_accessed=1756926347.3992615, blob_last_modified=1756926348.3062575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417653/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/59233342d3c3b71fce95d5295cf7c1e5ef3f4254dbc120229cb83b6c8115c759'), size_on_disk=12208357, blob_last_accessed=1756926311.136419, blob_last_modified=1756926312.3904135), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380977/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/74237b16fd38090785d203d96bae72e1f5ce919d423f793cfb9d3b86623358e6'), size_on_disk=11114601, blob_last_accessed=1756926371.6651561, blob_last_modified=1756926373.12615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417894/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/12f262a12783317d135ffc5e1d281a3d43b746a82ed8053c15de824b94bd5ef8'), size_on_disk=6449687, blob_last_accessed=1756926354.4772308, blob_last_modified=1756926355.4742265), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417771/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8fe5513ae86b28ecca3d4c70bdb73097f578dd31b4d08a072960b61550f582fa'), size_on_disk=3970195, blob_last_accessed=1756926331.9313285, blob_last_modified=1756926332.8643246), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381070/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/32c8f4c4efdbe6e137add61b0e31c27ae6a9eb5e8b463e9e4410a27cc51a57d3'), size_on_disk=29864184, blob_last_accessed=1756926780.7487328, blob_last_modified=1756926783.3167186), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417764/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8a30e54d96d5b83981e37a0d8f6cae9e6584819a76a200f2730d9de32e282d02'), size_on_disk=2641764, blob_last_accessed=1756926331.099332, blob_last_modified=1756926332.088328), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417749/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8460c24102627d611c8918a1620078ee1bdf9929c44cdca863cd0dbb23f9cf1f'), size_on_disk=4938551, blob_last_accessed=1756926328.814342, blob_last_modified=1756926329.7143383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417924/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f2053fcaa7f21b1703e3491e167b457c29759b2fe9535f0cb2bfa257a1b86cea'), size_on_disk=6988370, blob_last_accessed=1756926359.940207, blob_last_modified=1756926360.8152032), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417732/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f99cdb10c12a75e1d020125d68b5c5302263eaef050b5c6b0032e27a0ac9a23c'), size_on_disk=685742, blob_last_accessed=1756926325.7463555, blob_last_modified=1756926326.326353), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417631/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b4b8c985528935ec46c6c7e78482af1ed26238137fc63b2196c6631e2d3796ab'), size_on_disk=27651513, blob_last_accessed=1756926306.2814403, blob_last_modified=1756926309.1994276), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380929/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aeddf7cf3aee11ee08c90d305e66fc682e675959c171ecfaa591bc1d9015ebbc'), size_on_disk=6656759, blob_last_accessed=1756926365.1011846, blob_last_modified=1756926366.0181806), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380975/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ea018f4882a60f5fb07a4bbae51bd494e44e6df9b215d9da5e405687a567dcbf'), size_on_disk=8369150, blob_last_accessed=1756926371.6051564, blob_last_modified=1756926372.7471516), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417727/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/414c702fdf3dd97c536f3237d2c38e0ef5ad8d08bb7c991ee252e16d57b201b6'), size_on_disk=2636905, blob_last_accessed=1756926325.0683584, blob_last_modified=1756926325.8093553), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417673/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/535d074f70f4553e24f3171fe5cfbd1821635ce7ca6ef93f8665876de620d6c9'), size_on_disk=6559693, blob_last_accessed=1756926315.1844015, blob_last_modified=1756926317.0733933), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417659/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/20f21bb1ba86fdbee1918776793b1ff2275be49933c72eaba90b8be8582f7692'), size_on_disk=15311232, blob_last_accessed=1756926312.2364142, blob_last_modified=1756926313.8474073), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417826/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3532f474125bbdc0445e46fe8be83801ca9c079afb1dedd7cd95e25994c50d17'), size_on_disk=11492099, blob_last_accessed=1756926341.9132853, blob_last_modified=1756926343.2502794), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417609/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e3d8451242fe9ecf9f82e2ac1a406144840a6182178228e6e1fa67e6a04a318a'), size_on_disk=3176651, blob_last_accessed=1756926300.651465, blob_last_modified=1756926302.0864584), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380942/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a958d997c1aff07ec07304485ee818280bc7714dd9c485ea73ef7fda81f2fe63'), size_on_disk=11407010, blob_last_accessed=1756926367.109176, blob_last_modified=1756926368.6211693), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380982/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d1e44b0b82ea22fb6c513ce02b49facf5f38b64b2baa91b24942fc6858125e37'), size_on_disk=6977161, blob_last_accessed=1756926372.8241513, blob_last_modified=1756926373.4951482), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417792/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/196cd0cf279f6bc85ca0dbeb1f089721f63eb167644d999b45d8cfe9a1f52acf'), size_on_disk=11982413, blob_last_accessed=1756926335.4593132, blob_last_modified=1756926336.651308), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417681/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6e4ea424e9eab4dc3ea304621992e1db158565a8ce30374980b1084a653a0132'), size_on_disk=7689435, blob_last_accessed=1756926316.934394, blob_last_modified=1756926318.231388), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417859/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e4c5915df6cb5bb450e1948d8c43bbe031d09ec345688ab51f2d8ea60f3be09f'), size_on_disk=12709203, blob_last_accessed=1756926347.8872592, blob_last_modified=1756926348.9972544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417692/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/82405dc0a21bd225cc6a9e813e62d4dc0522719abf1e677c954a6ec38763b306'), size_on_disk=4873514, blob_last_accessed=1756926319.6253822, blob_last_modified=1756926320.6443777), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417875/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8dc3634dae23cad7f562d15903341a67d3048929bd2198e53f71529b00a3508a'), size_on_disk=11490812, blob_last_accessed=1756926350.6762471, blob_last_modified=1756926351.7832425), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381042/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/80a0c18e8d146043e9c95c6a86552d28e28267a489b3e5c6f2a11319cf47b877'), size_on_disk=4477936, blob_last_accessed=1756926775.660761, blob_last_modified=1756926776.9177542), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381053/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6ca1dc55841bbb6d8d8a83d1a87d00570c1eea7cf1963db8cb04ddd427711fb6'), size_on_disk=10277654, blob_last_accessed=1756926777.9197485, blob_last_modified=1756926779.9187374), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417746/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/432442dbdd5578acedcbf6eddc14cfe0d460facc7b0dea857225fbd4e6a4242c'), size_on_disk=2726090, blob_last_accessed=1756926328.6353428, blob_last_modified=1756926329.1983404), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417628/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/291f0e4d2d0fa1656de1583bd9e131fd4d2fc1934d0bb70a6774f7e7f90e62ae'), size_on_disk=15444530, blob_last_accessed=1756926305.1314452, blob_last_modified=1756926307.1334364), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417944/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/01e0e606e4e8b2203e6497ffc8c231bce8672a43d83098ba838c8edfdb052c27'), size_on_disk=4563261, blob_last_accessed=1756926363.89319, blob_last_modified=1756926364.6951864), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417883/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8e23cc97e00adfd244a989f83702f4ce7a998ba7998dd95908a1b4ec256bc054'), size_on_disk=10709227, blob_last_accessed=1756926352.4542394, blob_last_modified=1756926353.675234), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380980/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e36f58da696f0827c72fed8647c2a45a0499fdc5d1859386dbf025388f07e16d'), size_on_disk=2288305, blob_last_accessed=1756926372.371153, blob_last_modified=1756926373.2051497), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381035/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/410edf5a6097a77bcec6880ec2618b4aec4bc10c44607163fe2b27aca0a05117'), size_on_disk=1451911, blob_last_accessed=1756926379.8691208, blob_last_modified=1756926380.5191178), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417706/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4f1ea5592a855f3bf5739b0caae1c69c7606276d125c3dfe8e98379d12bedf1b'), size_on_disk=1154142, blob_last_accessed=1756926322.9053679, blob_last_modified=1756926323.5173652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417655/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/25d98ba17be9b354b0ec7a0e513a6ff9d4593dd1cadf269f28bcea1ca9c71565'), size_on_disk=5461224, blob_last_accessed=1756926311.5504172, blob_last_modified=1756926312.9474113), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417790/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f88bed27521de8285bbb92cf95242820116e6fa2c23ab578e6608a6a25e0c04e'), size_on_disk=10836858, blob_last_accessed=1756926335.078315, blob_last_modified=1756926336.1843102), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417679/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bc48687cfd5c03c1a71787981180295dd3a27cf4d2c46bd26bb68cdf8b35925a'), size_on_disk=17346576, blob_last_accessed=1756926316.1203973, blob_last_modified=1756926317.84439), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380954/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/338886924ec42501a99a4b842e18fdbed28200d52daaf43e00ee601a147e0188'), size_on_disk=4903852, blob_last_accessed=1756926369.2841666, blob_last_modified=1756926370.101163), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417696/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2e1bce702520ca07d9037bd4de4d21bb30ef6b7fdf7de9d75ea232acc036a72e'), size_on_disk=31303382, blob_last_accessed=1756926320.7203774, blob_last_modified=1756926777.0657532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417627/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4027ab6ecd21364ca365bbe7192797ed924c817e6edc841d750111b2e3f6f45a'), size_on_disk=21089243, blob_last_accessed=1756926305.0144458, blob_last_modified=1756926309.6264257), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417763/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/91d8a58ead35fc52f7c2649ce9509a92236a20ca8c26f5f2ef4ba1a38e64afb1'), size_on_disk=230495, blob_last_accessed=1756926330.8943331, blob_last_modified=1756926331.4653306), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417902/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2d72fc510cd348af7611ca170b7804b228b74bd38e7ff5e437375c1cc926fa95'), size_on_disk=8952511, blob_last_accessed=1756926355.9322243, blob_last_modified=1756926357.2422187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417939/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a0fc7804b026f95809f33087deb75f00beeb9635475b62864d3e58e948361bfa'), size_on_disk=9099632, blob_last_accessed=1756926362.9701939, blob_last_modified=1756926363.9411898), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381038/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/415f62b8333a02df8a87ba85e1d0de30a6cc9fdb07f7c8dc02d30f81ed4c14ef'), size_on_disk=2424198, blob_last_accessed=1756926379.8751206, blob_last_modified=1756926380.4921181), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417783/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9603c95097c451e616c54d52d3474b866453409905ea16365d019e77c6ca31b3'), size_on_disk=4176778, blob_last_accessed=1756926333.6983209, blob_last_modified=1756926334.4773176), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417947/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/50bb9cc74904c9d3b6cc3ec8bc12c7ad1fb89204be6f89a863e703a94d224512'), size_on_disk=4023877, blob_last_accessed=1756926364.0161893, blob_last_modified=1756926364.9561853), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417823/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1887333f71d11fe7c43748662f58d9ba3fb12f3f6ca30a61d7e1c7a0c95f7064'), size_on_disk=9444476, blob_last_accessed=1756926341.6372864, blob_last_modified=1756926342.8442812), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417678/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/825d9bcd10c008b68cc84e92f20608c0d52770ac028bbc4a9219f1cd210ab8c4'), size_on_disk=29320273, blob_last_accessed=1756926316.0503976, blob_last_modified=1756926318.3993874), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417860/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/72e751d246e9b938ce9ab4380e2a1114ecd3c36d5829e3298f391c1edfefae24'), size_on_disk=3996315, blob_last_accessed=1756926347.9312592, blob_last_modified=1756926348.819255), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417932/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f040d4f594fe69143a7c3f04f0498be2a30fb9b5e78d42ec464fee65efcc42dd'), size_on_disk=9231914, blob_last_accessed=1756926361.4262006, blob_last_modified=1756926362.5771956), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417731/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/39ec5969bc8f3acceb3aa30b72f0c0101785a36da340f3983ad00d8cc80160a4'), size_on_disk=1557745, blob_last_accessed=1756926325.658356, blob_last_modified=1756926326.2043536), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417759/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b2dad4cf4b03c846adf9d5229aeab340e58034c751bc68b37bb0c8402a7c071b'), size_on_disk=1372312, blob_last_accessed=1756926330.1243365, blob_last_modified=1756926331.1623318), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417791/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8d2eb1bdd1a5cae023abb271e5858cf111879953cb0d6267f4ebf21a3d30c4ad'), size_on_disk=5232904, blob_last_accessed=1756926335.2033143, blob_last_modified=1756926336.417309), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417919/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f5504428a85399cb212c74235abf9cec068dba03136a87ea79eae6d63a3cf430'), size_on_disk=7011211, blob_last_accessed=1756926358.6892123, blob_last_modified=1756926359.8312075), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417911/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3ca8b1b14564fbc90b44995a61a34d5a67482eb5fd867ce4f9f2196de2b73539'), size_on_disk=3051177, blob_last_accessed=1756926357.404218, blob_last_modified=1756926358.3022141), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381027/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ba9aba2cc5b9bd9e83599ccba5920f028ac9c6936436dac50f45c606f3abd841'), size_on_disk=6537548, blob_last_accessed=1756926379.0151243, blob_last_modified=1756926775.5467618), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417779/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/918376b5cb0b63e3e531ac990854e5a7b461e7251f5ed562c5a92cbf6a5a0141'), size_on_disk=9459105, blob_last_accessed=1756926333.1593232, blob_last_modified=1756926334.2033186), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417926/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/303d44a38fbb646cf211ce1bd03fe2d24bb004f385c63d05ab8e707c5b283f0c'), size_on_disk=10766909, blob_last_accessed=1756926360.6072042, blob_last_modified=1756926361.6751995), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380993/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d568d29486ad3ec6f67f87b78cacb8b7102c591f979ec2bdb17ff75bde789670'), size_on_disk=3031778, blob_last_accessed=1756926374.496144, blob_last_modified=1756926375.2661407), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417857/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/64f710e0b8a62bb6eddf3508f133b6b82af8bd21dd07f2319af6d07981085b1e'), size_on_disk=11109477, blob_last_accessed=1756926347.471261, blob_last_modified=1756926349.6202517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417929/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/85b194132215311994e48908799e31357d1094663a232e8170a4edcfa5a11eff'), size_on_disk=9155231, blob_last_accessed=1756926360.9862025, blob_last_modified=1756926361.9231985), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417923/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a32971eb62fe5c4c08298b1bdd97a916d8ef8f5898aee42aaf3fbaa8069e92d1'), size_on_disk=10004280, blob_last_accessed=1756926359.919207, blob_last_modified=1756926362.0851977), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417890/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3eca80a3220fd0294fdfca83740ba70a20af764d28833494948705c4d542ded3'), size_on_disk=9018889, blob_last_accessed=1756926353.7652338, blob_last_modified=1756926354.7252295), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417739/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1c9e3fcff1dc467673efe70177cb4d678d7eff7b5b2bb3ded247d7a458b466dc'), size_on_disk=21974051, blob_last_accessed=1756926326.8623507, blob_last_modified=1756926328.7393425), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bd149efcdaa899981b965dc424d16fb66668a7666a059c6bc5043572331394c2'), size_on_disk=4513644, blob_last_accessed=1756926377.1751323, blob_last_modified=1756926378.1161282), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417865/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/348bfc1685f79e9ec7f559e96b61b8c43628d611378b6c4ead8bffea44415f1d'), size_on_disk=9233047, blob_last_accessed=1756926348.8982549, blob_last_modified=1756926349.9182506), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380995/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e0cae334f1cb71be0be1e8ceb5fc6ad23d22e4db20cb64b1314c3d080f2bb5f7'), size_on_disk=6315860, blob_last_accessed=1756926374.931142, blob_last_modified=1756926375.7271385), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417941/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/30188c510d2d7041e1525e304edc8f25b569a1771cf00844cb27300a052441e8'), size_on_disk=7032333, blob_last_accessed=1756926363.2551925, blob_last_modified=1756926364.2891881), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417824/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/65d997db1339fc5a8380a61fcf1ab1a6d28fb9142fc7d78c89236dc218b91ea3'), size_on_disk=8218856, blob_last_accessed=1756926341.7612858, blob_last_modified=1756926343.0512803), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417626/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1981a2a1e321acc5c35bbeb9b61a12636c27f28d6e216de20093afdbee9f8689'), size_on_disk=12046611, blob_last_accessed=1756926304.4464483, blob_last_modified=1756926306.2084405), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417832/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/268a3b7a91ccc3f3909042a711454c336b1887705de99442bda022bcbc26a01e'), size_on_disk=7655304, blob_last_accessed=1756926343.2332795, blob_last_modified=1756926344.3042748), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380946/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62fe0084bf7a8f9f484e76366f203e1c68862a211d5d501c08e19b8f28678d6b'), size_on_disk=8090103, blob_last_accessed=1756926367.6871734, blob_last_modified=1756926368.7681687), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417722/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f29055ed874a3a40735526808a6739884efecd8d7c5e7e9eb1fcaa8e495ade3'), size_on_disk=345818, blob_last_accessed=1756926324.5753605, blob_last_modified=1756926325.2483575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417633/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0b38824aeb2fac57eca256e3652539deb4bc221eeb5afc3701a997885f9ad9d0'), size_on_disk=19311587, blob_last_accessed=1756926306.8324378, blob_last_modified=1756926308.847429), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417821/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c29db68a3430cf8cb01716d90a92105387777ed1361992eb9501271c35359cfb'), size_on_disk=6597050, blob_last_accessed=1756926341.1692884, blob_last_modified=1756926342.2702837), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417690/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/283af9b17d776b75f6ff6ad1f426d6d0e0e5aa1f2089a20910edcb6763191b69'), size_on_disk=12265448, blob_last_accessed=1756926319.171384, blob_last_modified=1756926321.4153743), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381069/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aa8f6186814bf973f8b4448a0c4b692c31594e03905c75d5691b982d6ccde97d'), size_on_disk=13705106, blob_last_accessed=1756926780.5807338, blob_last_modified=1756926782.319724), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381011/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/839fea471c67e08e0b68ae6344432c76da559e67c505c3f6120b20db2f1f753e'), size_on_disk=4378212, blob_last_accessed=1756926377.2971318, blob_last_modified=1756926378.0891285), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380952/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3811d6e6194d8b88c84fc0931cba844da23677e76e1e9a25e2890df968a7e529'), size_on_disk=1003172, blob_last_accessed=1756926368.8351684, blob_last_modified=1756926369.4641657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417660/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c394d7b0cfca3f3782dca86380e1a55903111d32efde98173af8a0b726ef5e7d'), size_on_disk=6441490, blob_last_accessed=1756926312.4744132, blob_last_modified=1756926313.4024093), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417811/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/da92b1c1579a2295da085050305367bd02a47fd52a74d1ec2a9f227b5e808b11'), size_on_disk=6127159, blob_last_accessed=1756926338.8782983, blob_last_modified=1756926340.1172931), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417829/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/63a9af789279180a07d8e8afe07bb0e9b8aa32c4340e01347a6f7ee809198308'), size_on_disk=5677879, blob_last_accessed=1756926342.904281, blob_last_modified=1756926343.7332773), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417716/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cd98cf97346b62e4f1efcace598c9c273d9ad9b8d8692e35f50b11d46f7ed8a6'), size_on_disk=5100666, blob_last_accessed=1756926323.8513637, blob_last_modified=1756926324.8793592), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381039/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5f95b6de8d0d73589c612313eaed6ce1e6adf734e795719f23df6f54cbbedc69'), size_on_disk=1656495, blob_last_accessed=1756926379.8751206, blob_last_modified=1756926380.283119), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417872/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7d71ca12594ab85797fca8c1b88947cb0f3ece93596f1a4c5adc04c11c0abf68'), size_on_disk=8522283, blob_last_accessed=1756926350.1742494, blob_last_modified=1756926351.180245), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417695/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d6ae8d39c2949235dd4d82e7a0e56d9f20c20ba5f1b7a445fbba1f0df29c88b7'), size_on_disk=7710772, blob_last_accessed=1756926320.4913783, blob_last_modified=1756926321.959372), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417887/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/58707813ec63af2eb1a72b0c8317b4701b4777df8f828f2e08a0cbb1ccfa534f'), size_on_disk=9080847, blob_last_accessed=1756926353.0712368, blob_last_modified=1756926354.412231), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417709/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/395b1dd171ab9759e06a22580672bef65aac2acd7d548e38098d207c80071d89'), size_on_disk=717131, blob_last_accessed=1756926322.9983675, blob_last_modified=1756926323.6373646), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381023/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/22e3e0f4cd1f2c755ec1ac13b99d2d6c98d0158319478f2deca4e89aefc5e88e'), size_on_disk=3860195, blob_last_accessed=1756926378.5651264, blob_last_modified=1756926379.6101217), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417949/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6123bc7bd312e6e37fabadee561c7dacad32d26bada92c9785c783f94fc50a37'), size_on_disk=1320707, blob_last_accessed=1756926364.4151876, blob_last_modified=1756926365.0891848), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417625/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/96b04f28e74e25bd1502a4ea9aca8047bfa8462e61cced0d726af4a336e1a585'), size_on_disk=17384720, blob_last_accessed=1756926304.4014485, blob_last_modified=1756926306.2074406), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417853/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5995033a04f017892b30c025a1527f9b6420bf1e7636c466cdd8d1462cf228c8'), size_on_disk=2186899, blob_last_accessed=1756926346.9012635, blob_last_modified=1756926347.8172596), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417713/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c5d75f4a32cadceee844da0c5ce703483f575fb927990a1615ee37205137d986'), size_on_disk=524444, blob_last_accessed=1756926323.533365, blob_last_modified=1756926324.045363), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417921/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/05fb4ff0001b88b711eee9f29ae30642d940667f7166121c9782f464057880f5'), size_on_disk=7891276, blob_last_accessed=1756926359.3782094, blob_last_modified=1756926361.2442014), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417611/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c82d882f6538d65316dd01e26a82af13e41717952c79e20dae6c3f1d9724ab60'), size_on_disk=3720891, blob_last_accessed=1756926301.4614613, blob_last_modified=1756926302.2704577), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417684/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eee25a9a91c54d0404db884532d1b85555f2e1de0c664abcbda4343f9d729842'), size_on_disk=10310520, blob_last_accessed=1756926317.7173905, blob_last_modified=1756926319.3473833), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417772/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a21b204a2da7654dd5bf4e7965a3c1e19159c16052232030787175a830ff0233'), size_on_disk=8204405, blob_last_accessed=1756926332.1323278, blob_last_modified=1756926333.453322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417725/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4195b9ed6506437a735fd8061cd1b79211c99169c4319c4f72b801a501f2b0f9'), size_on_disk=2233032, blob_last_accessed=1756926324.958359, blob_last_modified=1756926325.594356), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417820/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9d36f1c17151e3203186142abff734c0182377d5287b3f27d00917608501abb3'), size_on_disk=8116588, blob_last_accessed=1756926340.9562893, blob_last_modified=1756926342.0902843), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417869/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8dd08ab28007f90a664a6fbde23298ae2bb0ded917d5d75b1275c41e3eb5f7dd'), size_on_disk=16604892, blob_last_accessed=1756926349.6872516, blob_last_modified=1756926352.7202382), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/059cc8a8b1f7918ab1b6499d3d9843100b3b37fe6dcd57dfe9ae512c6030cad6'), size_on_disk=2109635, blob_last_accessed=1756926376.7021344, blob_last_modified=1756926377.469131), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380996/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/09e663ce2a60df5635f4e4ea77d6350e7752a9bb5e81fbdfd818869ac53ca805'), size_on_disk=13309208, blob_last_accessed=1756926375.0511415, blob_last_modified=1756926376.0221374), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417654/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/67d658902a2b91d0d36345be06dc00c85af1b88ba40c37deaa091705d97b3ffc'), size_on_disk=6323092, blob_last_accessed=1756926311.166419, blob_last_modified=1756926314.4344046), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417885/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6d5faeda491268a8ec88393883d743aac6cd14d4837698164ca32ca1052da36f'), size_on_disk=7668243, blob_last_accessed=1756926352.806238, blob_last_modified=1756926354.1422322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417707/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/89192f5007f730a90c823219ea107c799935f5e824963090abad813603160fa8'), size_on_disk=2615872, blob_last_accessed=1756926322.9183679, blob_last_modified=1756926323.9473634), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380987/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2eb045d9f88b952eed8f8354e77c682bc3003bd2e9eacb0eb0c2df5ca44a34cc'), size_on_disk=4463796, blob_last_accessed=1756926373.559148, blob_last_modified=1756926374.4241443), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417617/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9ad746ec09ed51ed497d87f3dd64b11916b70e6833ce66141bc8feb6ba73d1d3'), size_on_disk=23082279, blob_last_accessed=1756926302.1754582, blob_last_modified=1756926303.6944516), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8168980e59c50149bfb9866a1e8855c235f2882b988df99331c9247f65e9813e'), size_on_disk=2289686, blob_last_accessed=1756926376.4721353, blob_last_modified=1756926377.2151322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417917/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2118491b5ba6b4af1c0d394eec3139b0258cf3a4e42b4d1a02ab177247852081'), size_on_disk=14019020, blob_last_accessed=1756926358.4622135, blob_last_modified=1756926359.8632073), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381036/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ff76dabe83d2948240dd846fcc2a969380b7bd9f8d2eff2c71c299f0b14134cd'), size_on_disk=2593393, blob_last_accessed=1756926379.8661208, blob_last_modified=1756926380.5611176), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380984/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a15ab5e5a0ead07dc2a530a1e4abb714027a95ce090c7a0eda5ec2bb714d903f'), size_on_disk=8070704, blob_last_accessed=1756926373.2251494, blob_last_modified=1756926374.1601455), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417726/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/739e474fc54ffee0c7e6300d3abb1f83d33d48c78d137ed848f837fa186b9308'), size_on_disk=2430088, blob_last_accessed=1756926325.0663583, blob_last_modified=1756926325.6803558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417712/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/59cbfa83b9abe13f60d6a80b35afe857e3ffd761366133f60505312a2b5a72e2'), size_on_disk=641243, blob_last_accessed=1756926323.2163665, blob_last_modified=1756926323.7173643), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417934/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5816750a355a277e532d46ffa5159f8a2ec1ce66686fb12f07ccab4b4f7b3c98'), size_on_disk=7324082, blob_last_accessed=1756926361.995198, blob_last_modified=1756926363.89619), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417774/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ad00f794fb29d2a609375d63b09170f396f055ae392cd05a31d5f8fb5ede7a33'), size_on_disk=2914273, blob_last_accessed=1756926332.1763275, blob_last_modified=1756926333.217323), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380997/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/add73ab9723ef88a47192fa169b89aaf17c16a0841fc9e53c1c5d73cfdc2ad50'), size_on_disk=15536494, blob_last_accessed=1756926375.1291413, blob_last_modified=1756926377.6141305), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417661/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c6effb515a20066646b7863cb86e9dc012e14187e432ba7458cd6af18662bfd6'), size_on_disk=22122748, blob_last_accessed=1756926312.9054115, blob_last_modified=1756926315.6813993), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381037/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5cef560daffa3a5b1e841a312937eca595ced0bbdb3e6c641b13359a0340fea1'), size_on_disk=2090047, blob_last_accessed=1756926379.8821206, blob_last_modified=1756926380.4171183), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380976/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d27d3efdccd104ca208d69fe9d94fe9011cb6212b34739679b1c8f550c5e9df2'), size_on_disk=12889551, blob_last_accessed=1756926371.6141565, blob_last_modified=1756926372.8021512), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381066/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ae017c72cf3b91ea3fdd40f63a126f640e7049340baca502ff3228c6ee40a7b4'), size_on_disk=13139272, blob_last_accessed=1756926780.000737, blob_last_modified=1756926782.6217225), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417668/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b426513aab4e071a24b7d27100a4da4da8c71c8e9b3e82163808b0ec6a42b8d0'), size_on_disk=6011682, blob_last_accessed=1756926314.5374043, blob_last_modified=1756926315.4354005), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381022/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/980ed07a51dfad581fc2f7bce44031c92445822ba7e994cbfc9b5508cdf97c85'), size_on_disk=2103246, blob_last_accessed=1756926378.5151265, blob_last_modified=1756926378.9441247), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417862/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cea0cfbd611f8c6482ec33f8ea32f304a481d1c1225d3dacea432dd359fc976b'), size_on_disk=5244392, blob_last_accessed=1756926348.381257, blob_last_modified=1756926349.6162517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417868/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/52e3f224a0147264d4ce477d26f4e11ff0745c328eb3e2ac44209dba1c8c55eb'), size_on_disk=5039768, blob_last_accessed=1756926349.6852515, blob_last_modified=1756926350.6082475), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417845/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ca2b89837de406b10d72533a3e3d5457b3512b704f7cea395fb0e1b88828393e'), size_on_disk=4648586, blob_last_accessed=1756926345.6932688, blob_last_modified=1756926346.5862648), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417895/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2daca9b311da1afa40f578c6df7dc4b0039dd4857663c98cd87c665a547b7144'), size_on_disk=11915424, blob_last_accessed=1756926354.7232296, blob_last_modified=1756926355.8262248), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417908/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/128e54326b088977000f8caaba7d09e324281ed7fe0e3e5f2779783f0a82f293'), size_on_disk=3614805, blob_last_accessed=1756926357.1272192, blob_last_modified=1756926358.2542143), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417744/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/faf407f0b251cf5f58c8ac7becee4b995327fd9a7d6599c3045fde8a78e1e811'), size_on_disk=2548979, blob_last_accessed=1756926327.7843466, blob_last_modified=1756926328.7443423), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417736/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/671a67d7275810840cafc9db32e43385c4ba61b007d702e7eb5cb244a0e4afa9'), size_on_disk=13855124, blob_last_accessed=1756926326.1693537, blob_last_modified=1756926327.261349), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381030/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e5baa8386faa327b26d27d6c8e6b265dddac1d55565e81d724a1980b93d871a3'), size_on_disk=1867415, blob_last_accessed=1756926379.2021236, blob_last_modified=1756926379.6681216), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417841/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cb829be078dd8b533a90c2c548dfb2ce0455026be54c36572337e0a1ddeeb2cf'), size_on_disk=5791736, blob_last_accessed=1756926345.138271, blob_last_modified=1756926345.9902675), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380989/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3c8b646955209386a1d97882163b7c9816463d2dea68fbed2cc960817ffeb332'), size_on_disk=12022144, blob_last_accessed=1756926373.7761471, blob_last_modified=1756926375.1511412), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417634/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/529d984782684991b2eb7be2d345b5a4e8a6e08d3dbe30ad34495d8380464e6e'), size_on_disk=16306189, blob_last_accessed=1756926306.810438, blob_last_modified=1756926310.239423), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417789/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6bd6339b995a5730b6b65ec65f680b57195469ebee9f2e15de6e604b069d4b0e'), size_on_disk=9612750, blob_last_accessed=1756926334.6493168, blob_last_modified=1756926335.8183117), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417604/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e6dd3abdf75462f1f5d16f19383b98a8a4c817dc3b13c7cea2b4bea611c40c62'), size_on_disk=961320, blob_last_accessed=1756926300.390466, blob_last_modified=1756926301.3534617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417621/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/354c654ace9bc123bcd9009c56af518b98f1a547a7ccd79f348d0e533607a41e'), size_on_disk=20457307, blob_last_accessed=1756926302.5744565, blob_last_modified=1756926307.7874336), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/225bf60f7c9796682210dc48d07a30d3dabab8ebc13abb7a84834279fa131020'), size_on_disk=5307353, blob_last_accessed=1756926376.2401364, blob_last_modified=1756926377.3751316), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417622/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/28eed3b67a6206b453d8adb775edf2bea32cfd54ba5b37d51787f67280e0317f'), size_on_disk=20411775, blob_last_accessed=1756926302.674456, blob_last_modified=1756926304.8564465), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417646/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2b3d78fc052de8647b1ba6e83245b5f375b4e6c10952feff795855160458a1d5'), size_on_disk=396843, blob_last_accessed=1756926310.1544235, blob_last_modified=1756926311.0694194), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417786/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdd4770c9b3e18e86f9a46ff924e3a562e37b632d6f0e8ad11627956a3416e09'), size_on_disk=11354740, blob_last_accessed=1756926334.3243182, blob_last_modified=1756926335.4083135), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417704/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/477a83e4a5eace957a036c5ec65ef4539442c078058c1a44a877763cb850c86d'), size_on_disk=459034, blob_last_accessed=1756926322.1393712, blob_last_modified=1756926322.8993678), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417718/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3ba23a0e7c5b3a455e95fbf6a22b51687d5330c19207241bdfd189e3776f5ba3'), size_on_disk=3304954, blob_last_accessed=1756926324.0233629, blob_last_modified=1756926324.986359), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417683/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6131abcd72bd4c6a8627685c4df8a295af50e824f52a5cde156adae7cd694bba'), size_on_disk=15545733, blob_last_accessed=1756926317.382392, blob_last_modified=1756926319.0483847), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417918/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9f1cf3832969364b6cee1c817d3583a554b765bdbc1ff3f5b29692b7ea98ee7e'), size_on_disk=6936136, blob_last_accessed=1756926358.5802128, blob_last_modified=1756926360.9132028), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417922/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fba17c65fa210922e57c533f5146b4237677149a5022fb836c1cd52dc89855b2'), size_on_disk=10194446, blob_last_accessed=1756926359.5812085, blob_last_modified=1756926360.610204), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380951/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b9b68679dd2da94e4aa4852ae3de3ee5b823c1fb1483f4ece3eee5d947014785'), size_on_disk=1154119, blob_last_accessed=1756926368.8281684, blob_last_modified=1756926369.410166), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417801/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4bbb1b3faac1a4c15eaac8c81260f4f18493d2a082915cbe4c480ce76a94140e'), size_on_disk=4600965, blob_last_accessed=1756926337.090306, blob_last_modified=1756926337.9153025), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380947/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3d6062f97d18f4945c36590355a312df2a16206c26626392cfb44d9613aea49d'), size_on_disk=9201129, blob_last_accessed=1756926368.5321698, blob_last_modified=1756926369.383166), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417737/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2e1d5b2306dd24257d4ba261baa42b324c9f4f8ca3a1d30e0f920c4f4a5f99ba'), size_on_disk=1342511, blob_last_accessed=1756926326.2713532, blob_last_modified=1756926326.9043505), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381057/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4e6c617d22a655d32847df8b5334f4fb7236a15b8bf6e30aaabcbb0a4c55782f'), size_on_disk=17494867, blob_last_accessed=1756926778.5417452, blob_last_modified=1756926780.4997342), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380936/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5a931bc1181001314c3822051c493ba7a8b87b5bb26b8866783612f2bcf49c62'), size_on_disk=3467608, blob_last_accessed=1756926366.19118, blob_last_modified=1756926367.2261755), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381017/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1d9b2dbb5079eca871182c2dce1d681b943f1362072c18e255a4bd6d1068b840'), size_on_disk=2436549, blob_last_accessed=1756926378.161128, blob_last_modified=1756926378.7931254), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380971/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0147090dd5a74f558b67151503ff635423a8af661b56d1faf06bb50a59adfb3a'), size_on_disk=1276600, blob_last_accessed=1756926370.9411592, blob_last_modified=1756926371.462157), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381071/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cf15be48b47b6c369c6a4290b783b9b22b01d6cb89d04c738c275241548b811b'), size_on_disk=2543373, blob_last_accessed=1756926781.2297301, blob_last_modified=1756926782.309724), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417676/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/53c35af59283469a22148b2841cb8574faf80d59162a33d269560283c9228af2'), size_on_disk=12006227, blob_last_accessed=1756926315.8083987, blob_last_modified=1756926316.8563943), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417800/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8e563a7d64bf06e7fe26c2817271f626273eed4c7dd19db0b9fd8e14c25f73b3'), size_on_disk=9904697, blob_last_accessed=1756926336.7953074, blob_last_modified=1756926338.958298), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417893/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f3b9f2c711cb806350320b93dba528efd6433d0ca10c39e5e2dd6aa89a408bd'), size_on_disk=9896637, blob_last_accessed=1756926354.2372317, blob_last_modified=1756926355.535226), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417805/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/661f37ed294c22f6c3b02d5c1ea25794f5e33090c246185e891ee82153e2e5f2'), size_on_disk=3071166, blob_last_accessed=1756926337.8643029, blob_last_modified=1756926339.3292964), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417884/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ff7d3efa4a231f5130be299d8d0ff67fe4973856e4db18e8a6c53d47ba6b28a7'), size_on_disk=11684836, blob_last_accessed=1756926352.791238, blob_last_modified=1756926354.0932324), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417642/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/591f688d0d9f98350a1d636e6f93ed37563f3ea9765ca6b633490533b0cf32d4'), size_on_disk=11498542, blob_last_accessed=1756926309.2804272, blob_last_modified=1756926310.8714201), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417671/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bced5a56092fea7de5754bebc33caaab643f27236f8d9020e7bc6b30b9045d6e'), size_on_disk=5347180, blob_last_accessed=1756926314.7704034, blob_last_modified=1756926315.8293986), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417879/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6714b3f5155dd540962a7541625f45e14e6a93b4c01e913790b70818e50c821b'), size_on_disk=8601872, blob_last_accessed=1756926351.4612439, blob_last_modified=1756926352.8182378), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fb5958ec4e10f5c7e19ebc55b24f26012e6d8ccf'), size_on_disk=8004, blob_last_accessed=1756926300.2594666, blob_last_modified=1756926300.3214662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417815/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ee8a37cd03dfe154008718f2a0f7fd95b3fdf468f99b1fc73ca515433fa45930'), size_on_disk=6563669, blob_last_accessed=1756926339.6062953, blob_last_modified=1756926341.8062856), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417912/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4451877e599fa65e1f00f67b5fcd41fe74719d8bcb19ddacac419fb347c77cf5'), size_on_disk=4943909, blob_last_accessed=1756926357.7152166, blob_last_modified=1756926358.6302128), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380973/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/343eb57057b5af866386fb4c02d0c41b888e206f5473ed7b4041200cfb93dbd5'), size_on_disk=1511721, blob_last_accessed=1756926371.3611574, blob_last_modified=1756926372.4931526), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417700/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1240fbb2497074bb7ff98b1bc6399b8a8ad5d320246285abfb6738e27e2bbdeb'), size_on_disk=19312447, blob_last_accessed=1756926321.6763732, blob_last_modified=1756926322.8383682), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417877/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b170370a220db5677b9c6193707d32d1d14b9082ca574b9562c4ac8ea4c72c26'), size_on_disk=10449263, blob_last_accessed=1756926351.0392456, blob_last_modified=1756926352.32624), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381060/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1bb05aa71fe55db65fc3868b131854c1e6666dca8cc370874f517aa0756d21ac'), size_on_disk=9137992, blob_last_accessed=1756926778.9637427, blob_last_modified=1756926780.2307358), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417785/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/209799965b03585e1143e881d87c5cea80d37e06637d2fc3f7e1ad2eaadfd3bc'), size_on_disk=11557515, blob_last_accessed=1756926334.1813188, blob_last_modified=1756926335.1353147), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417762/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c8b34d14be6dd44af03432d758f705cf81e86a46831c6deb4b0944248709630a'), size_on_disk=3024339, blob_last_accessed=1756926330.943333, blob_last_modified=1756926331.59933), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417699/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0c64ecbe6b245299fe04c04239d792fbae0f1b288562fe0c7912a24de56f0ad5'), size_on_disk=5707915, blob_last_accessed=1756926321.6203735, blob_last_modified=1756926322.8483682), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417838/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0dcd4dc268902c8e0845a1ee81938a81a00412014d5b198a41a523cc3450dfc4'), size_on_disk=10178872, blob_last_accessed=1756926344.5452738, blob_last_modified=1756926345.3902702), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417702/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/41123195fa339a4ffabf6d686a284c343d0519089a18b828818c92ec43a26eae'), size_on_disk=6213776, blob_last_accessed=1756926322.0263717, blob_last_modified=1756926323.0323672), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4c4a265f9e3e8d373550ea58ca1cde6094c823f090336974fde3e7cee117fd99'), size_on_disk=1770007, blob_last_accessed=1756926376.6771345, blob_last_modified=1756926377.1011326), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417608/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d225f561536fca4d073d2418a32efc3df68e9666a3403c7d8b1ee0a520861322'), size_on_disk=873305, blob_last_accessed=1756926300.603465, blob_last_modified=1756926301.521461), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417778/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/54e8ea03e214967eb4a1c7b50a086c4d7083d2c9c95dd0419e545fb0e8f18422'), size_on_disk=9345257, blob_last_accessed=1756926333.1513233, blob_last_modified=1756926334.0903192), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417624/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/28c1de2a4bd6b421cd05c3bb211ffde83e4b830127f106ec7be42ad288e26ef6'), size_on_disk=16758887, blob_last_accessed=1756926303.7884512, blob_last_modified=1756926305.6184433), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417641/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/355d8669b1d4c5576d8aa900582800fdf1819360f0db91d5e0299342b49884fb'), size_on_disk=6322689, blob_last_accessed=1756926309.3414268, blob_last_modified=1756926310.7974205), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380949/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/65090ef93158269c98bc2e747e505cea9d460f0959d0b2957e58543cd1944163'), size_on_disk=2110872, blob_last_accessed=1756926368.705169, blob_last_modified=1756926369.142167), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417658/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b6cf4c024743be14fcd5730f0ddbb960dbcd83d07adf8668639f9a1cdf195368'), size_on_disk=8754108, blob_last_accessed=1756926311.5764172, blob_last_modified=1756926313.1134105), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381063/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1e4361a7d20f8e92e8efc238365e0ed06eb021243864321c93d4638fa21634c4'), size_on_disk=10916323, blob_last_accessed=1756926779.3497405, blob_last_modified=1756926780.685733), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417697/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/198a3cd31740f347f0308db2914df4fc0fc303b4a9f040fbaf79fb5ca0170e7f'), size_on_disk=5521438, blob_last_accessed=1756926321.4153743, blob_last_modified=1756926322.4743698), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417809/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/486100d34df42ce97476d9753f8d57edb5f53596eaeca2d0cf81f5f95a4f4e12'), size_on_disk=7376702, blob_last_accessed=1756926338.5872996, blob_last_modified=1756926339.9172938), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417711/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e30dbc09cd2dfb4722d60d0833123542e9c5096f70b97867cd15d466aa7b927a'), size_on_disk=96859, blob_last_accessed=1756926323.1863666, blob_last_modified=1756926323.7823641), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417925/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8cdb783613764a35fd3a73db89463db6876951d443f5651a9a46877a53917f3f'), size_on_disk=912848, blob_last_accessed=1756926360.409205, blob_last_modified=1756926361.1532018), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417640/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/411d9146359847b5805450f67cbde4f1cbd51e3c2fae4bbcd6295db7426cb2ca'), size_on_disk=7000528, blob_last_accessed=1756926308.9124289, blob_last_modified=1756926310.0344238), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380953/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5b3410430d33f3123b935982be90037f2d0a8e657f6b0b70a7042846d5ddd1b0'), size_on_disk=1192961, blob_last_accessed=1756926368.8401685, blob_last_modified=1756926369.627165), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417657/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62398c518588ad11a939f95736cf0ed01a39890567cd4a8f05e9c9ba1155f9c1'), size_on_disk=10144285, blob_last_accessed=1756926311.5634172, blob_last_modified=1756926313.1174104), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381014/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5c5106dfb24bcfb1436aeb87b8218df873417ca2809aed71ca58e9b48ffacd29'), size_on_disk=1425306, blob_last_accessed=1756926377.5811305, blob_last_modified=1756926378.2571278), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380994/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7c5f237d9c32233a005e39386360f02d9e1932f9ac267ac3458f96aaf59e01ef'), size_on_disk=2496053, blob_last_accessed=1756926374.8051426, blob_last_modified=1756926375.3471403), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417742/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1a87c492aab479dd0c013fbe80e006f18ba80de751da600b95babcb3f6e88dad'), size_on_disk=2054520, blob_last_accessed=1756926327.3293486, blob_last_modified=1756926328.4213438), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380999/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/acb2ee4711dab5800afb7ccbea72c8c80268b0a848723e7bd8ecf197decb982d'), size_on_disk=2605563, blob_last_accessed=1756926375.2281408, blob_last_modified=1756926376.1321368), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417729/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8383a0e86c2bfe15c46045980d6768eb4e7e3c4c157ff1ae7ce20144e245ca80'), size_on_disk=3058876, blob_last_accessed=1756926325.3113573, blob_last_modified=1756926326.103354), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381048/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/53cd017f084781da16e69ae01b406ac0d8824a4d991e39b22c428e664a3808f2'), size_on_disk=2181901, blob_last_accessed=1756926776.9907537, blob_last_modified=1756926777.7067497), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417740/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e91586589c967e7bcbd521989874b21c024da79365d0d9916ffc66b774043e01'), size_on_disk=11875677, blob_last_accessed=1756926326.8903506, blob_last_modified=1756926327.6843472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417835/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6245fc8d220aad556115d4b831f72ae6a9beac48ed6e2aba811284aa0ccab4f7'), size_on_disk=7888215, blob_last_accessed=1756926343.795277, blob_last_modified=1756926344.942272), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417705/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/92b4f5af6793c98b854bcca83c86ac0cb41ec4e40ec23d3ba9c4ff287852c63c'), size_on_disk=332184, blob_last_accessed=1756926322.5923693, blob_last_modified=1756926323.1503668), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381059/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/59daa71f55c9c0da6f8d56fc2497491477312fbdbcb9e92d4951874f2ed72686'), size_on_disk=13124154, blob_last_accessed=1756926778.926743, blob_last_modified=1756926781.8157268), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417852/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/43431a444b2761ed9b6fa4c7e6edfc8da31b64ff933408c517bc771e9f3e94dc'), size_on_disk=6967416, blob_last_accessed=1756926346.8272638, blob_last_modified=1756926347.8462594), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417748/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/db4c67c001a1f688f70918f0e25d3ddd996392b33b9f1144a14551bc0026d036'), size_on_disk=3246481, blob_last_accessed=1756926328.8123422, blob_last_modified=1756926329.6943383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417892/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7ac6b85018422c74c894467b7b53b1c2dc7c5298fc31654bdea5ca70493e0a87'), size_on_disk=8194725, blob_last_accessed=1756926354.176232, blob_last_modified=1756926354.9712286), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417906/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/34d3381dd45e579dcc298e91cc95174c4219cf548401b8f2392cf746fd508c5b'), size_on_disk=13795318, blob_last_accessed=1756926356.3642225, blob_last_modified=1756926357.640217), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380927/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/38e67ea2fef57610ff63c0c298a6db65ae1c0a159f2b1e61940f2e65306b8fa9'), size_on_disk=7297320, blob_last_accessed=1756926364.969185, blob_last_modified=1756926365.7781818), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417753/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a8969c75211310735ad33d5af57327f2babc86276adc7cefcda254cc9fc9baa5'), size_on_disk=6433095, blob_last_accessed=1756926329.577339, blob_last_modified=1756926330.4023352), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417817/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ccf81aa26021a009a212ef8f8bee71c12f5c8d0a2b97ffdb2c4d86d13c8b8133'), size_on_disk=7521086, blob_last_accessed=1756926340.0802932, blob_last_modified=1756926341.3222878), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417816/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/86256a1b98c6d44750fd21ac9ea099262f1f0ec03d182b65741d669fb80f289e'), size_on_disk=10618044, blob_last_accessed=1756926339.7852945, blob_last_modified=1756926341.8372855), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381041/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a53caa1961d9245d82db10989b8a8d601760ccf26f67666d5558937830c51e31'), size_on_disk=6621865, blob_last_accessed=1756926774.784766, blob_last_modified=1756926775.9747593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417878/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d3d105cc45280a5ea88c2bba754c0c5849d0cec7112a0926e9ed0c946e7f5cfd'), size_on_disk=8838404, blob_last_accessed=1756926351.2492447, blob_last_modified=1756926352.7352383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381043/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f6ded8e6496dcfdd499edc382d19da2071f25daebbb250b375b9a2706e9d8211'), size_on_disk=9139336, blob_last_accessed=1756926775.6057615, blob_last_modified=1756926777.1057532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417645/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/761eba472193b23cbf7735946e08aa1c4db5b1abd5125f1aad22ddabbf872f04'), size_on_disk=2080044, blob_last_accessed=1756926309.765425, blob_last_modified=1756926311.5084174), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417623/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/562a43ff381a2ca177fb1cc3dbe47fc2cf7cade495bad511ca7ed75aefca7a98'), size_on_disk=19049552, blob_last_accessed=1756926302.696456, blob_last_modified=1756926305.4934437), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417897/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/309aab637d037638082524649ef7b0828885a98e786c64af01072d68968b7d66'), size_on_disk=7857749, blob_last_accessed=1756926354.8022292, blob_last_modified=1756926355.9902241), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417873/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d282d59c3c191bd7b7fa7251108224d0ac3929bc98f96a2c43f283f8a5cd046a'), size_on_disk=7609540, blob_last_accessed=1756926350.3852484, blob_last_modified=1756926351.373244), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417825/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/efc4971b63b21d6aee96e17d9e9b2cc2e3fd34608cc3722e36032c594fc03e48'), size_on_disk=8550531, blob_last_accessed=1756926341.8732853, blob_last_modified=1756926345.0782714), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417927/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8e639025209cff8d34b7e35a5fa371282bb725173225097a39f8f2101a49deed'), size_on_disk=7241057, blob_last_accessed=1756926360.6762037, blob_last_modified=1756926362.1171975), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417913/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/35c1a160b767f9f5870216ed33a1dc24804690772a5baee58f623c6dc0632127'), size_on_disk=9135350, blob_last_accessed=1756926357.9462156, blob_last_modified=1756926359.3142097), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381064/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8569620c4d2efb8dd163aa69d8edf8f855b96eba637e754a126a88e6f0c73192'), size_on_disk=20598404, blob_last_accessed=1756926779.800738, blob_last_modified=1756926781.1587305), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381061/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d44d57e9c03bb6e6e750809eea88657c2960ed251b8acaa1352a07f4cb346a82'), size_on_disk=3789431, blob_last_accessed=1756926779.0527422, blob_last_modified=1756926779.8977375), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380958/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ef3849004b6c72205ea1b298cf6a586f1114061ca94925229eda191d4666885'), size_on_disk=3288900, blob_last_accessed=1756926369.617165, blob_last_modified=1756926370.303162), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381049/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c571bcc5f87a79c7cd38b9614ee83b0d5cc742094e17a7990c829394fca193be'), size_on_disk=3753119, blob_last_accessed=1756926777.145753, blob_last_modified=1756926778.2097468), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417915/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9fbfc50c3e1f756169d25442880ba33e5cf237e3c83a0d674b64ace47e8d3322'), size_on_disk=13801295, blob_last_accessed=1756926358.318214, blob_last_modified=1756926359.4592092), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381024/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5182750a0c8af8253cc98ecad8715519463d5c18adbe067488dfef91198c0d80'), size_on_disk=603678, blob_last_accessed=1756926378.7151258, blob_last_modified=1756926379.128124), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381052/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/652334151cb539b888d9cfcb60d9711d80bd74ec65cfa3799de10b2970ab9c09'), size_on_disk=5788579, blob_last_accessed=1756926777.7767494, blob_last_modified=1756926779.283741), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381065/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/99cc73bc4c0908da04c01c7c59eff995a478ffc023c31fb970c7bb9789b2d70b'), size_on_disk=14673323, blob_last_accessed=1756926780.0087368, blob_last_modified=1756926781.5847282), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417848/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eec4ffff5d145c0ee0850d5e68787ab90e6510cffa5803579db44e3a575822dc'), size_on_disk=13556826, blob_last_accessed=1756926346.1882668, blob_last_modified=1756926347.2852619), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380943/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/987771cb978dbc949eff5044071149a00223c6771efcc870850e3c8cbe0648c3'), size_on_disk=13397144, blob_last_accessed=1756926367.295175, blob_last_modified=1756926368.7601688), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417680/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aa9e972e7427b1db5556008756651933c8792891339607dee2ee231733072d1c'), size_on_disk=18443288, blob_last_accessed=1756926316.5853953, blob_last_modified=1756926321.4263742), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417750/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aedd94fe4b42547108a807242052397b6dfbfda0eb4e56a9f2e267b312251147'), size_on_disk=1531584, blob_last_accessed=1756926329.1973405, blob_last_modified=1756926329.9403372), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417813/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f7d5239d0d39e171aad1e84964f8cdc3c7a325fe8c38b6829e114cb3997e658'), size_on_disk=10238322, blob_last_accessed=1756926339.4142962, blob_last_modified=1756926340.8722897), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417933/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4fe347d88754646d39d93dd8a78e857d04d1903f7163a21495038b087d5f6b8a'), size_on_disk=12389872, blob_last_accessed=1756926361.7481992, blob_last_modified=1756926362.9061942), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417741/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/734d5947cb26e877b6ae9e9adaf84575974a4fe2fb464ba11c4320906f17a42a'), size_on_disk=4566889, blob_last_accessed=1756926326.96635, blob_last_modified=1756926328.5653431), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380930/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/734e9bc8119c7a507e9cf84f9926b1f9d06ccc8b0062a9b4dc36631f6f1bc6d1'), size_on_disk=4604638, blob_last_accessed=1756926365.1301844, blob_last_modified=1756926365.9251812), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380956/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/15047f69259f2cbc2cb08949d07ac6fde505bade6a0e3bb3912b183a48a4b288'), size_on_disk=2282680, blob_last_accessed=1756926369.4771657, blob_last_modified=1756926370.1431627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381045/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdc8f9e6447280a7fc713c2a8f2157eb9076a5c01a7eb8437176e9ee30d6d4ab'), size_on_disk=4939421, blob_last_accessed=1756926776.3437574, blob_last_modified=1756926777.2507522), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417807/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ebc732f5d06d745bb63af3c7f5966722972fa722ef9d0194d71cf6d9485fda83'), size_on_disk=14049230, blob_last_accessed=1756926338.268301, blob_last_modified=1756926339.5272956), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417898/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ced2c80d4f9d2aa28260b885a20b1c90b41d8eb54ee92f418ed592b97b954a9e'), size_on_disk=6422129, blob_last_accessed=1756926355.1262279, blob_last_modified=1756926356.0962236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417910/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/694d86a1e469836a53e1ce5dba0d64b77b8f68590a2034e2a1ef5310457dc7fb'), size_on_disk=4494010, blob_last_accessed=1756926357.3112185, blob_last_modified=1756926358.3942137), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417701/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a0dd9587c7525a610a3794cf41f6db24a6ba22a12ed852dc27381a68db3e8fca'), size_on_disk=8781514, blob_last_accessed=1756926321.6273735, blob_last_modified=1756926323.1263669), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417871/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e44cb883893b5e30baa752dfbd773b31795f2072a7ec4f473371f435989cad42'), size_on_disk=6264896, blob_last_accessed=1756926349.9852502, blob_last_modified=1756926350.937246), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417847/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4dd39704bc9c1d60a8271f16206887171ee92abde023c53501c1560533a688c1'), size_on_disk=4755120, blob_last_accessed=1756926346.125267, blob_last_modified=1756926346.8372638), CachedFileInfo(file_name='GSE209631_metadata.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE209631_metadata.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fe31b19357a717d3d51d1d201620d37fca79b1c8'), size_on_disk=61, blob_last_accessed=1756926300.2214668, blob_last_modified=1756926300.2814665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417728/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2be09708238c50a31b4e9b334c6202c26eaa6a950ebf3de4287651999d45097e'), size_on_disk=3385901, blob_last_accessed=1756926325.0603585, blob_last_modified=1756926325.869355), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417703/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e82b21d919b84770e2d141eaea09aff82ef57c3ff3dbd15c588c1b8cff212c9e'), size_on_disk=1462841, blob_last_accessed=1756926322.0533717, blob_last_modified=1756926322.898368), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417840/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7e32bef3c0f7f7520382325aba12f5cf9405c22ebce8554b1d877fe24d2da52b'), size_on_disk=9679209, blob_last_accessed=1756926345.0092719, blob_last_modified=1756926346.0542672), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417720/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/961772a081bde1795af5caea9d7562e2b1f889981bd3c3f1c792ad9093a758d7'), size_on_disk=659095, blob_last_accessed=1756926324.1233625, blob_last_modified=1756926324.6243603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417643/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/66dfa3feea4410ba34f5d5d37c7b01e1fa2cdd4ff38f9dfb10d20d55e40e449f'), size_on_disk=1012185, blob_last_accessed=1756926309.326427, blob_last_modified=1756926310.0784237), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417839/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a04fc093d2a4fe873de135165bd62a38a1cb9b29860881ed973022ff128dc6d8'), size_on_disk=6789775, blob_last_accessed=1756926344.5502737, blob_last_modified=1756926346.4242656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417606/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f76c81d98337c9e127bf5516af0dd135458fd622c01f2b5c9fd85ce6edfa4c7f'), size_on_disk=9502783, blob_last_accessed=1756926300.5794652, blob_last_modified=1756926301.6394606), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417882/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ac2bd9710196abc4469d4d78ccbde6ddc67e9ef39ade7d50c32b0d951866e653'), size_on_disk=10076041, blob_last_accessed=1756926352.116241, blob_last_modified=1756926353.223236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417733/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/542261ed8bf189205b611485f1a878f28cae5c90e452ba9cad687eacfdfc2814'), size_on_disk=2645461, blob_last_accessed=1756926325.8883548, blob_last_modified=1756926327.3873484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417776/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/01c4a97f92cd5e2919759d52d083eb824408a2fd7ade9413a060c6b20d233963'), size_on_disk=2759569, blob_last_accessed=1756926332.8593245, blob_last_modified=1756926333.629321), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380963/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c0a30240a0a99347e114928a15a24268495fd37480fde6822f503438bdf3ed48'), size_on_disk=579777, blob_last_accessed=1756926370.2681623, blob_last_modified=1756926370.81716), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417858/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2d7539fcb6297592fe3755ccc88e122441925fbaf9f5600d4daa88639630fd8b'), size_on_disk=1021831, blob_last_accessed=1756926347.8042595, blob_last_modified=1756926348.5072565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417866/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b0424cd7567723443472cb266e090a30e2b64ee6b09bb1b94d8dcfdb9f0b0c43'), size_on_disk=9685580, blob_last_accessed=1756926349.109254, blob_last_modified=1756926350.493248), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756926300.2174668, blob_last_modified=1756926300.2804666), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ad0e80a28e4cd2027893adee5afceab851576ed2637c1a8e94d0af2adae6ace1'), size_on_disk=5088900, blob_last_accessed=1756926375.41114, blob_last_modified=1756926376.3821359), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417842/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c763016dcbf1b53df840e3ea3c1b45234e2d9e797114000efaf692e2c394d487'), size_on_disk=9967694, blob_last_accessed=1756926345.1472712, blob_last_modified=1756926347.1812623), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417656/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/03f8707a323ef7563c8ec2d6352d24c6454d043c4bbbca417680df5c7db208f8'), size_on_disk=10029839, blob_last_accessed=1756926311.588417, blob_last_modified=1756926312.8104117), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417738/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c3d82a351044aa8cd5d43979369088ccf2f563022d4f7aa3683ef20923202770'), size_on_disk=19772535, blob_last_accessed=1756926326.3913527, blob_last_modified=1756926328.6913426), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417844/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ae034bb207abc0c649fb60aae78e798d46647f53b4e0352c6510fb7c8339a234'), size_on_disk=5408445, blob_last_accessed=1756926345.5212696, blob_last_modified=1756926346.4342656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380972/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/91a8106ca7ac2babcabd1549072b2b730909e1386f827e732428bff2bb5251cb'), size_on_disk=6712599, blob_last_accessed=1756926371.1721582, blob_last_modified=1756926372.0711544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417942/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f3a5b1eb0f954ddf66a8dfa1a38465726d2251730d3898865535269a43c7fa4a'), size_on_disk=6911573, blob_last_accessed=1756926363.415192, blob_last_modified=1756926364.3521879), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380978/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a1d811dc3a4bfd44683ddf8bd0401a71d4941497c5cac8d8040a597fc5d26587'), size_on_disk=5902739, blob_last_accessed=1756926372.160154, blob_last_modified=1756926373.09715), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417602/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/26ae91a914e9ad095615c88ac3c62c99e28ae2687ccd66007f13ac8fbbffa3fe'), size_on_disk=3717181, blob_last_accessed=1756926448.0367467, blob_last_modified=1756926301.5714607), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380966/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f27a802d18bf78781f3d168370e0c0e241a130d04a5871d7416442be3ce3e40f'), size_on_disk=4241927, blob_last_accessed=1756926370.3881617, blob_last_modified=1756926371.272158), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417831/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/53d810292e5a513a8ab543fe18783f8d493cedbadc7c97400dc103a4675954df'), size_on_disk=6767934, blob_last_accessed=1756926343.12528, blob_last_modified=1756926344.4612741), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417891/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cabef83ee373e37d528b127d813f1535faede4b98c5524d83e9906df83558b07'), size_on_disk=12596609, blob_last_accessed=1756926353.9052331, blob_last_modified=1756926355.2992272), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417946/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef43c84affe53bbdefcd70cb97a4a4d1e483824b8b6f91e3360a11094609f815'), size_on_disk=4853893, blob_last_accessed=1756926364.0141892, blob_last_modified=1756926365.0591848), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417798/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0ac62fa7573f4b3c756fd9c280be4b97ec0c7c756e8c76307d4fab56d29e1c08'), size_on_disk=10509245, blob_last_accessed=1756926336.5193086, blob_last_modified=1756926337.800303), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417664/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/91fc5dce8497d1fc5466fb5ac31e44a20b295d8f97825500f256f6830ea2f983'), size_on_disk=8203399, blob_last_accessed=1756926313.1894102, blob_last_modified=1756926314.6624038), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417796/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/52d8262071c9c9de8cd8fb52be9e67acf125b8ce67033749fb92989f0d1f9345'), size_on_disk=10184069, blob_last_accessed=1756926336.2803097, blob_last_modified=1756926337.4333048), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417851/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2d8a571fae50598143744a1f13d40cec2ea8c2198e87aae17f96d7c8a00460d0'), size_on_disk=11051527, blob_last_accessed=1756926346.6472647, blob_last_modified=1756926347.6582603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417745/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ab84f2e8e76311cfe45a4b525b239186e835cab37f1e5222fdc35c286064e5e'), size_on_disk=1270105, blob_last_accessed=1756926328.596343, blob_last_modified=1756926329.1233408), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380959/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4693c1876e880fee1ec277fd5ae9127aafa9f40d41a3d5ed482dfcfa8cef55f1'), size_on_disk=873902, blob_last_accessed=1756926369.7061646, blob_last_modified=1756926370.1731627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417896/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/71e393229e9071bad31abe5dc3d41bd5d719a5dcb68077827861b4b4e9a49a85'), size_on_disk=6055777, blob_last_accessed=1756926354.7882292, blob_last_modified=1756926355.8442247), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417652/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d38c028233981eb275c44995eafa73172388a9e0e704fe278b0cedbab49d429f'), size_on_disk=12947754, blob_last_accessed=1756926311.0344195, blob_last_modified=1756926312.1414146), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417827/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8bfd1221f930e86ffd6150c82aae85f00a23fff33c672e40f47d671f5a2e4d17'), size_on_disk=7291599, blob_last_accessed=1756926342.156284, blob_last_modified=1756926343.1502798), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417818/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3b30217b127ff88f165762c5054c392d9489b9007ba7ca8b86f18b44f9988e4d'), size_on_disk=8388230, blob_last_accessed=1756926340.2372925, blob_last_modified=1756926341.5632868), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417663/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cc7010c3ed70c3083d49d88c0f95dbe8f6eff873d4b5848eeae06a22df71a479'), size_on_disk=8938116, blob_last_accessed=1756926313.18441, blob_last_modified=1756926314.5674043), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417715/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ac5effc2ec07a8fb49bac4d77b7d4129a18ae2180807874ea02d1c252a3b68bd'), size_on_disk=520286, blob_last_accessed=1756926323.8693635, blob_last_modified=1756926324.477361), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381028/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8d40caa48e7b9d32d2a0548732ef91c01ede9d0f4f6d74f045817e818c52c808'), size_on_disk=18141950, blob_last_accessed=1756926378.9691246, blob_last_modified=1756926778.2687466), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417694/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/058b507f75cb01bb5912e14e7bbb4e25a1de945e578323bce9f920f31a9ced82'), size_on_disk=11980395, blob_last_accessed=1756926320.09838, blob_last_modified=1756926321.5623736), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417685/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/32edfbf9226b7ef1060ebe3e6986a1803d60408ccda969528cf4a59c5785c722'), size_on_disk=16967633, blob_last_accessed=1756926317.9163895, blob_last_modified=1756926319.5263824), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417828/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eb4987a8ef94e1691c74c1d828d7d9313da4691b577118a73524def2a90c6d0b'), size_on_disk=4827634, blob_last_accessed=1756926342.3472834, blob_last_modified=1756926343.2782793), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380937/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e31f54581c4a4a47033065033f3f9266913edad654543dd07b20d0ffe54847dd'), size_on_disk=2691574, blob_last_accessed=1756926366.2071798, blob_last_modified=1756926367.1951756), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417916/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2abad8555b89fbb5009e8f2a95e553a5ce579d1841ae4e71eec7b137f3282ef1'), size_on_disk=9236466, blob_last_accessed=1756926358.371214, blob_last_modified=1756926359.0582108), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417768/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8bb8e088eb5123049e418c868ab42d94fb48946f934a8313f4cf283e3bfa09ba'), size_on_disk=4124375, blob_last_accessed=1756926331.3153312, blob_last_modified=1756926332.2313273), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417770/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0e7437c90c616b78ccc0963e0388385028b70d66fbe73e0ceb4e30e2d0239816'), size_on_disk=8252889, blob_last_accessed=1756926331.6563299, blob_last_modified=1756926333.0863235), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fc6783c3340c869bc3838a9a9d5abb8bca67361feeef12c42b6cf478d73bfd66'), size_on_disk=9020794, blob_last_accessed=1756926366.2571797, blob_last_modified=1756926367.2561753), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417850/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c08fca103aa3dd1944dcf5c73b67397263842dc687b4101c8ab2c1a63d50205a'), size_on_disk=1182282, blob_last_accessed=1756926346.5122652, blob_last_modified=1756926347.3082619), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417863/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4aabb9d1a4b105c7669deb0f13cd635ab7433ed1ae02f5785aad014d604f1504'), size_on_disk=6900487, blob_last_accessed=1756926348.4532568, blob_last_modified=1756926349.2742534), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380991/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dd86db1779581d5853d4d6a0fa2aeacdb00f92c832683ad788743f17db625db4'), size_on_disk=2640507, blob_last_accessed=1756926374.030146, blob_last_modified=1756926374.8661423), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381018/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/878bf028aca9daeebe7ed0f8ee522580d512771ec4a5e35bfc62ac675176ad15'), size_on_disk=701951, blob_last_accessed=1756926378.2281277, blob_last_modified=1756926378.643126), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417773/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d7980069a740f581dbcfd9416372452203d636d8ad4c9352c3b2d6483cd27379'), size_on_disk=5178664, blob_last_accessed=1756926332.1683276, blob_last_modified=1756926333.411322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417904/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cf9194917da2f4c6c3f0411256b2604e8c14959373d7431047de2e23cffbb9bf'), size_on_disk=7269877, blob_last_accessed=1756926356.062224, blob_last_modified=1756926358.1762147), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417787/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/17f33e06b6bd77c0bbd3141b53fc2d9550b0c54e99b18fe79ddad931e3a283a9'), size_on_disk=12355842, blob_last_accessed=1756926334.5003173, blob_last_modified=1756926336.2953095), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417881/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bf380975f31e362e8964000688bff364809bbe2e8e3efa9ceaa71253ca6beefb'), size_on_disk=8717095, blob_last_accessed=1756926351.848242, blob_last_modified=1756926352.9732373), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417650/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c4d103888f768ff3cb0c68f4b747cb8d8aad8058b75174160cfc42b2504407f1'), size_on_disk=1337049, blob_last_accessed=1756926310.89842, blob_last_modified=1756926311.4924176), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/43f12fb4826be4c7a3dd64800fc72cbcab2a9ce5bed7f7b5b9aee974deb7c06d'), size_on_disk=1116877, blob_last_accessed=1756926376.094137, blob_last_modified=1756926376.6491346), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380945/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/51efeee4439e81d920c4d9e860de321d90c72703600044e52215c774e1540650'), size_on_disk=6902367, blob_last_accessed=1756926367.356175, blob_last_modified=1756926368.6011696), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417795/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b995a48f28ae3baa145438a39c9fed73fa8689eb31e07ff02a3bc6bc827eb76e'), size_on_disk=8032882, blob_last_accessed=1756926336.1053104, blob_last_modified=1756926336.9993064), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381025/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aea8c4d9f3783191d3e89f4566811f01089f3b0b76b736c5167bda54693b0bc4'), size_on_disk=339954, blob_last_accessed=1756926378.7781255, blob_last_modified=1756926379.125124), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417649/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6baf4bcd82d9fe55b8dcdc7602c0d39d20954675ef4c5484331ff25dac8ad3f4'), size_on_disk=2672976, blob_last_accessed=1756926310.8234205, blob_last_modified=1756926311.4704177), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417784/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fd3dd53cf34f48c235e4f771378f42a5605446bbc62d108a5637a042513fd75d'), size_on_disk=4826642, blob_last_accessed=1756926333.7233207, blob_last_modified=1756926334.576317), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c103eef2a9834296fb1328b7012b5b9184c5a3f131817dfca257843e863d34b2'), size_on_disk=9829417, blob_last_accessed=1756926362.1531975, blob_last_modified=1756926363.3531923), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381020/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c85f953439e9f6918c7f7c8194bcbbebe0ef8a96070119813025a42bde8c08fe'), size_on_disk=897644, blob_last_accessed=1756926378.3451273, blob_last_modified=1756926378.7061257), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417861/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef65c45e0e86b7d04e274c39408b332cc94879c0804f16acf7c537b14bb6498c'), size_on_disk=10436399, blob_last_accessed=1756926348.3832572, blob_last_modified=1756926349.6192517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417651/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b95b6be3a7a6a5b4af3bbf15c5a4a6b3893f3030ef6d1dcfe79e6c6554e3d3cb'), size_on_disk=13551797, blob_last_accessed=1756926310.9544199, blob_last_modified=1756926314.6814036), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380970/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bdec4a51127eaf09abb5d25d7f3a732afc3b561105fd18ba1df44d04e6dd90a2'), size_on_disk=3545790, blob_last_accessed=1756926370.9301593, blob_last_modified=1756926371.5451567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417775/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2ca9ba5e1394191206945f41168d555519fe3963d4c39f251c1ead57f2a323f4'), size_on_disk=1059891, blob_last_accessed=1756926332.3723266, blob_last_modified=1756926332.773325), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381029/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6431cec7b7404a0773d8715761e2b161f01ebd8471950e71e17dad8976d83f3c'), size_on_disk=7229691, blob_last_accessed=1756926379.0071244, blob_last_modified=1756926776.565756), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380960/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/60771297311bf96c3213c2a96321062a06067c91d2bba2fce69a7a6d7a0c980e'), size_on_disk=560607, blob_last_accessed=1756926369.7811644, blob_last_modified=1756926370.3121622), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417907/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b24926bd36cf33ea9107043b9a47db8239ad6208745c56002513225e8cb80a6e'), size_on_disk=7594380, blob_last_accessed=1756926356.7332208, blob_last_modified=1756926357.882216), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417717/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/689842dffacf7efa4ba6cd9b4314d195e89f9105eb71675f27019a9e25be63db'), size_on_disk=102777, blob_last_accessed=1756926323.9603631, blob_last_modified=1756926324.5793605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417889/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/295f9efb0d78e3a116ce4022a7830297570446e2017a3fcd3b3d423c0c20664b'), size_on_disk=9617979, blob_last_accessed=1756926353.3102357, blob_last_modified=1756926354.7322295), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417803/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1fd8b542fb1b0e18bc5346c50ad85da440ff6190a987467ec6876ed3ec51a4e8'), size_on_disk=4928668, blob_last_accessed=1756926337.5433042, blob_last_modified=1756926338.7182992), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417638/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c8cface311cd55cd1542ed94d609da72cd2b3087422c28ad26c60e1d81a3e6bd'), size_on_disk=993699, blob_last_accessed=1756926308.4724307, blob_last_modified=1756926309.2634273), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381067/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/da9c5ab3c0f970f9c2af4c0f5f0e9a95d47769e8096ff6b9d76092becbc8517e'), size_on_disk=9837433, blob_last_accessed=1756926780.2977352, blob_last_modified=1756926781.5777283), CachedFileInfo(file_name='GSE178430_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE178430_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/be2c4af55a502d7fa22abb28afe6db347950327bde1bc7e97dbb68fdb0c59f3d'), size_on_disk=9398, blob_last_accessed=1756926861.9192877, blob_last_modified=1756926300.5344653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380998/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6808b8e1fa74b807321826b2c54182a3b6e257f0a223be0a6a94caf9463959e9'), size_on_disk=16847111, blob_last_accessed=1756926375.2231407, blob_last_modified=1756926376.572135), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417928/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/97e74e01fd641bfef68b6206a3c62ab41b5c28d8ea8369ce4198df1b3387b266'), size_on_disk=2449472, blob_last_accessed=1756926360.9072027, blob_last_modified=1756926361.3542008), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381056/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/382194ca9758ddc18f059a649b13c5a21ead3a7f1fa5e012065315047a037a58'), size_on_disk=2351268, blob_last_accessed=1756926778.362746, blob_last_modified=1756926778.9827425), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380985/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2ec986412ef2a9e6e59f4ea3dc278b27ab3975b5025d00bd1cd38fe1867b0fea'), size_on_disk=7208882, blob_last_accessed=1756926373.2251494, blob_last_modified=1756926373.9591463), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417940/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/270559ca1db4605de241e8642461b4d7304d124e4fc624f7d8a8a59867301a4c'), size_on_disk=2328150, blob_last_accessed=1756926363.182193, blob_last_modified=1756926363.9021897), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417639/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/43c5fb0a4f049fa5895f8167928d047cc4954aa37db1fc8a9892fe9f99c401bf'), size_on_disk=782319, blob_last_accessed=1756926308.7694294, blob_last_modified=1756926309.6284256), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417674/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5970db11c3efe2bba4eb50d07480cb0b46ed74dfc3ddf12d611ecd61f1d5fb68'), size_on_disk=4619941, blob_last_accessed=1756926315.5114, blob_last_modified=1756926317.630391), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417691/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/48de5a2968610e5ab855522d27f535d40e1973c1835ec32fcfa5bcd91a0795dc'), size_on_disk=14481663, blob_last_accessed=1756926319.447383, blob_last_modified=1756926321.2093751), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417708/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b58038866c334daaf757a086f99c3a5c5962b8563769ee0a3205bcdcbb0144ec'), size_on_disk=250863, blob_last_accessed=1756926323.0003674, blob_last_modified=1756926323.366366), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381051/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ba017d941936d755013757711f5de4661acd187ef79524662d85485ba6588f34'), size_on_disk=8199472, blob_last_accessed=1756926777.322752, blob_last_modified=1756926778.4757454), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417808/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0251ae27bd961d6c962253c555963b42148092cdaa2616a78f6872cf1a4e5e4d'), size_on_disk=12900784, blob_last_accessed=1756926338.3973005, blob_last_modified=1756926339.439296), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380948/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6ac219aa7f5be77c21bde60c80ccad59edf825e2a009d6a4b7fc8bd4d8be83d4'), size_on_disk=6046323, blob_last_accessed=1756926368.6691692, blob_last_modified=1756926369.5471654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417901/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/69432386b347f5b364c02d270b38891358fa2068465354d1ada84d5450ea5bf0'), size_on_disk=9484337, blob_last_accessed=1756926355.6292257, blob_last_modified=1756926357.0582194), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417804/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/16ee23ae4dbe8a4d968a614f5e6de65028b6edce012231a50eb776d65f949d34'), size_on_disk=2107502, blob_last_accessed=1756926337.7883031, blob_last_modified=1756926338.5083), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380969/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/10410488c74f35a493cef2ffc4a64b28b336be1fb32a2b6dac692e0d7fbd4271'), size_on_disk=1973405, blob_last_accessed=1756926371.041159, blob_last_modified=1756926372.1191542), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380941/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9a23f03fee8d60d752bd0bc1e59e9205abea4f3a78e3272b9a03447f076383d5'), size_on_disk=3514837, blob_last_accessed=1756926366.9851766, blob_last_modified=1756926368.7591689), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381031/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b4705ac4577c8e7b116d1a1b677967f69412b0e4716e0de7fa7ee97bc9be4d74'), size_on_disk=4551217, blob_last_accessed=1756926379.2931232, blob_last_modified=1756926776.2707577), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417943/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0e4191a403f5070636bcb281bd80d39e11c70fb502fa2b18150032d58f2e0740'), size_on_disk=5396199, blob_last_accessed=1756926363.7271905, blob_last_modified=1756926364.542187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417747/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7011e7ff4b64ca9c57bdab93cfcfc52938e7b0574b48417feb4f6185f6c118b9'), size_on_disk=4791771, blob_last_accessed=1756926328.7593424, blob_last_modified=1756926329.9543371), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417948/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d43ea802a452aa9d57004381e1688222e61bd313b107077c1c663555547d1d44'), size_on_disk=1748124, blob_last_accessed=1756926364.3661878, blob_last_modified=1756926365.038185), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417620/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b973f1a83f111c4e114a40d3731ff24779cafcb7b71523fe8571cf2ed849b316'), size_on_disk=20780203, blob_last_accessed=1756926302.5494566, blob_last_modified=1756926306.7334383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417687/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fa4f79173f5a5ed278c7ef80d9fb220aa9c615de97bae7963969e27ecf0e587c'), size_on_disk=13352339, blob_last_accessed=1756926318.3003879, blob_last_modified=1756926320.3913789), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381062/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/76151e17f242c5108010debea961b0b13f3821be2fcb77fd923a36cc0e672695'), size_on_disk=4880117, blob_last_accessed=1756926779.3357406, blob_last_modified=1756926780.5167341), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417743/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d4eb93049648498d2fdc1f125574429a4dfdec65e626ad9fa16bdf0d0cf599d8'), size_on_disk=2280534, blob_last_accessed=1756926327.462348, blob_last_modified=1756926329.1343408), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417880/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b08db0a2399fc4e5b6f59c19b7af05b1294d0fac10b2c336b627234fa828f1be'), size_on_disk=9818886, blob_last_accessed=1756926351.5782433, blob_last_modified=1756926353.246236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381055/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0aad0d3724742845da7f0afae0c18ab5c992bec76c02e0ffda057f00d0894d35'), size_on_disk=3355008, blob_last_accessed=1756926778.3377461, blob_last_modified=1756926779.263741), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417819/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a1ba6808d672dbc9c58c26364c5cd9ed0b8a3041e6714844846648d3cd1aa050'), size_on_disk=3244517, blob_last_accessed=1756926340.7022905, blob_last_modified=1756926341.6952863), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417886/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62fe87e7519370c4ac46e6d9d0abf314a8f92d9a5d6804ebddae0ed095641219'), size_on_disk=7968977, blob_last_accessed=1756926352.9472373, blob_last_modified=1756926353.7992337), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417637/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ca1a954c7b2aca56529daf9a89249ad4e6ec2373e29ac7c19d2af1533afc4263'), size_on_disk=2776397, blob_last_accessed=1756926307.8804333, blob_last_modified=1756926309.1794276), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380990/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4e04122d2830db35f2b82b29efaca846fe10d7638c88c3e4d3cf78bf10cf245a'), size_on_disk=2093813, blob_last_accessed=1756926373.9261465, blob_last_modified=1756926375.1611412), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417714/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d6abeb724a8c8b23217f3465284564da5baffee59ee4e3af582e0a3705ae32c6'), size_on_disk=262443, blob_last_accessed=1756926323.756364, blob_last_modified=1756926324.256362), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417799/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cba2fb579c7706a7ff38fad3c7c649000560c1052e681adea986c5cc02c02cfb'), size_on_disk=5688906, blob_last_accessed=1756926336.7133079, blob_last_modified=1756926337.7073035), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417782/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d6624acaa7c4ec21ae02d6d1322b8669a148308095e714b41bff868483fe2fa9'), size_on_disk=11742567, blob_last_accessed=1756926333.5273216, blob_last_modified=1756926334.824316), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381033/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5fff567b96a4649d787e93ef8cdca2d8ce977b7f2eb25d6f78e50bc30c798bce'), size_on_disk=1712156, blob_last_accessed=1756926379.7391212, blob_last_modified=1756926380.3661187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417648/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fb706eb700e8fc003822b897accd3501360d737fd1a9ffcf26c3e9d9b25a5e35'), size_on_disk=1285054, blob_last_accessed=1756926310.3104227, blob_last_modified=1756926311.0174196), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417854/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d52c8b14e197b179a9b43fbc89bb3205d09d458161ee49ac15a62b7c1f7a39fe'), size_on_disk=6203801, blob_last_accessed=1756926347.311262, blob_last_modified=1756926348.387257), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/64a119c8028083521d3872e2daca1545f2f8753eb8acfc751bf0c6d25803a3f8'), size_on_disk=2003211, blob_last_accessed=1756926376.794134, blob_last_modified=1756926377.2761319), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417836/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4fb964c54f4a37387c7844902d6171011a9a81efff244f365efcb156f630158f'), size_on_disk=3312893, blob_last_accessed=1756926344.2532752, blob_last_modified=1756926345.0702715), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417677/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cfcdac65879c0c26771c1cd1c4a656d4db20e6adb2ed3ad833cf23cd35a9d848'), size_on_disk=13135194, blob_last_accessed=1756926315.9083984, blob_last_modified=1756926318.1283886), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417616/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0b0f3c9481fd0e254abd544ba803de0c9da9b3a686451975f981d02fc481148d'), size_on_disk=15058562, blob_last_accessed=1756926301.8254597, blob_last_modified=1756926305.0234458), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417723/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f397cef2d9c491fb034826727c63b3c224f6ed791c9b4dcf884c63c55c9961c'), size_on_disk=2800893, blob_last_accessed=1756926324.70836, blob_last_modified=1756926325.4453568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417812/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9e8445f413d26051897f0fe3e7566b116a9a55062b78201990de334f17aecb31'), size_on_disk=19905089, blob_last_accessed=1756926339.0272977, blob_last_modified=1756926340.6232908), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380962/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/72be01aa42db736ffac8236c23c16edea00bdf5da88c3114fc94d9eb3daa4ee9'), size_on_disk=1460258, blob_last_accessed=1756926370.2361624, blob_last_modified=1756926370.6811604), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380965/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6b384b53be751cec9056e74c2c8ce9fe2e532cfbf6a38e43bb09ce74ec817f3a'), size_on_disk=12974487, blob_last_accessed=1756926370.3761618, blob_last_modified=1756926371.4361572), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417629/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/29c9c86be94792d797005b106ab00d066634bea44fbf17e81738a00cbffa264b'), size_on_disk=22750780, blob_last_accessed=1756926305.5594435, blob_last_modified=1756926307.5804346), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417822/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62cbbb0172158a2c0776000136424032a794757ca9e9f971e5f445deb20e8579'), size_on_disk=6040431, blob_last_accessed=1756926341.3882875, blob_last_modified=1756926342.8302813), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4c942b91ac018330aeb4da512a3e47ae4531a434b6e9933cf3e2bb476a77f9ca'), size_on_disk=6312758, blob_last_accessed=1756926366.1041803, blob_last_modified=1756926367.0141764), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417615/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3b8b3c4c686238c7c642151bbdcb2ba7796de56c1922e558a1e7f08507dc5abb'), size_on_disk=2866472, blob_last_accessed=1756926301.8314598, blob_last_modified=1756926302.5744565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417756/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ede027f2c13a1e6669becf6194ace85af5800d5fe77db16ec47bfb3abaa735d3'), size_on_disk=8620421, blob_last_accessed=1756926329.968337, blob_last_modified=1756926331.0303326), CachedFileInfo(file_name='GSE209631_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE209631_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/be49eef90682fbd55a5e60ada8b4c1c554e1b5a2ec1209996269db020d3bb074'), size_on_disk=4258, blob_last_accessed=1756926300.173467, blob_last_modified=1756926300.594465), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417905/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b9d291469b7a414bab157f45bfd3a1f7ffaaf931de25fec9eb18012a18b70939'), size_on_disk=4418829, blob_last_accessed=1756926356.1612234, blob_last_modified=1756926357.2652185), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417936/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8ba89583b8c822b64968df9eca8273d12270429f0f820d6027f13c66504c383d'), size_on_disk=4552381, blob_last_accessed=1756926362.205197, blob_last_modified=1756926363.171193), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381021/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3e80794742d10b6d120e70c4e4f1d7f77123ddf8aa18499f686b4abc8eadd325'), size_on_disk=1524611, blob_last_accessed=1756926378.410127, blob_last_modified=1756926378.897125), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417675/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/79072181ebce12769f2f0a1971977d6691dde1841b53d7b54cf9d4f47f53b3dc'), size_on_disk=6478433, blob_last_accessed=1756926315.763399, blob_last_modified=1756926317.2643924), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417630/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/26fdbadf7c1252a8fd73ac85ea7430d942d2d11ddc43ea1b2590aaa008e08337'), size_on_disk=3015575, blob_last_accessed=1756926305.7104428, blob_last_modified=1756926306.7214384), CachedFileInfo(file_name='GSE222268_metadata.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE222268_metadata.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4459325dc2deeb9f00d803b89cd8fdc823ed383f'), size_on_disk=61, blob_last_accessed=1756926300.2714665, blob_last_modified=1756926300.3444662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417719/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cc0a365d1d79f0469571216394f0cb211b9096f2a567e0a347a18f0c496bac06'), size_on_disk=2076369, blob_last_accessed=1756926324.010363, blob_last_modified=1756926324.951359), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417849/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/23b6606ffcf116cf066b103bf1b6b4fb33c40e544e20e4565fa44c5bbcea4bdd'), size_on_disk=5689527, blob_last_accessed=1756926346.584265, blob_last_modified=1756926347.3892615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381034/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/112a8e705f7536fa06c630165d3c409f87a5784ad626b891cc2b3919e24bdd14'), size_on_disk=250813, blob_last_accessed=1756926379.795121, blob_last_modified=1756926380.1791193), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417710/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9837ecd3769eaaf407d939ff09d6ae74d832630b27353251942bd262cd70fc35'), size_on_disk=330457, blob_last_accessed=1756926323.098367, blob_last_modified=1756926323.797364), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417607/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/623d085888c300fba1d05a9018ec3d4c31912f2b91a872ef34feda3322889b70'), size_on_disk=6809356, blob_last_accessed=1756926300.611465, blob_last_modified=1756926301.75846), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417766/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/39da4f16b82cc0b5019d8526f2e3eed49de9760e319f8e839db621bf4c1ab021'), size_on_disk=6482957, blob_last_accessed=1756926331.2123318, blob_last_modified=1756926332.1133277), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417920/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/00a869703f03e3df3f0f51a7d93d52dd7eac5174ed2a1b752207460e71c7b41a'), size_on_disk=14880236, blob_last_accessed=1756926359.1242106, blob_last_modified=1756926360.409205), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417698/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d240b652ebca64784e037cc8ffbda424e2c92f368c5bb9c730930f48f29facc2'), size_on_disk=1855826, blob_last_accessed=1756926321.2993748, blob_last_modified=1756926322.0703714), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417672/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f0741c46f1460c30d4d9b88ca1f7c0ca77a040cf25a34d82fab4c777a921ac26'), size_on_disk=18268758, blob_last_accessed=1756926314.9764023, blob_last_modified=1756926316.5143957), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417864/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/15d259c52e80a2d2d9c20274ef940237c90001d32f2aecb80779335a708222ff'), size_on_disk=3706383, blob_last_accessed=1756926348.5722563, blob_last_modified=1756926350.0962496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3fe2828766efd936f02efae2da3e068ef9b28ea374e594e09850e3e8ff0d8164'), size_on_disk=4421540, blob_last_accessed=1756926375.7961383, blob_last_modified=1756926376.7581341), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417780/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4f1b7c3ded6441c8e906c3494ae63750d25303ee6f8d8e05120dc11ce8d79080'), size_on_disk=9025914, blob_last_accessed=1756926333.2923226, blob_last_modified=1756926334.387318), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417931/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7e70415ed0dfb1eb42e46789c905ccf2b593dc192d907d939d87acd59cd963e1'), size_on_disk=4991063, blob_last_accessed=1756926361.306201, blob_last_modified=1756926363.1161933), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417751/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9a5733130de69115a731511dac8bf577fc5609a7f9649106ae9a12ffae7011ba'), size_on_disk=1660869, blob_last_accessed=1756926329.2023404, blob_last_modified=1756926329.9043374), CachedFileInfo(file_name='GSE222268_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE222268_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e0f1d5b82fd97798d0c02d992c49c0e83d8dcf82e07e001284e78d9656355481'), size_on_disk=4411, blob_last_accessed=1756926300.1784668, blob_last_modified=1756926300.5344653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381013/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c831ca7cc9cfd2cc2ccd92a0168b2e14dedf2bde9e29780dfaf6d281b31e9462'), size_on_disk=4648281, blob_last_accessed=1756926377.4421313, blob_last_modified=1756926378.3451273), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417900/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/45d5a0bbf39e542f22aa4e36186673d1246533dc513f006312a26286496f6ead'), size_on_disk=11235417, blob_last_accessed=1756926355.535226, blob_last_modified=1756926356.6632211), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417758/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/54f8e8d84e5b8b9457aaa143829fcfd5a4715d0fb184478ce659cd6c6efe7be1'), size_on_disk=4176901, blob_last_accessed=1756926330.0203369, blob_last_modified=1756926331.1383321), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380983/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eec33f1158aa017b073fcb16d6400cd800eb79e8c0b78b5e2f831d7413e4a736'), size_on_disk=4478939, blob_last_accessed=1756926372.869151, blob_last_modified=1756926373.6721475), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381047/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dda6f6ca669a0e5c9d34b548305cbeb6b82f1b70086e06d3604e7e096fd7fb25'), size_on_disk=6799004, blob_last_accessed=1756926776.8777544, blob_last_modified=1756926778.4717455), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417682/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/83b3100cfd1dc6918855c4a711ff607b36f40971655733855a7df353b4d5fca5'), size_on_disk=14037100, blob_last_accessed=1756926317.1663928, blob_last_modified=1756926319.1013844), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417605/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f689b77eccd8d95d70fb616f4e2aa03a41201aec2a8c3c86849b7b145cb0f796'), size_on_disk=1787545, blob_last_accessed=1756926300.5184655, blob_last_modified=1756926301.74346), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417806/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2befcad73b2aa8170ceaf8c249ceba45e87037771e86c9bb4bd7addc5815bbec'), size_on_disk=5276603, blob_last_accessed=1756926337.9863024, blob_last_modified=1756926338.8112986), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417802/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ea68aebb727f77d846a145d43ec16fa2496e9c05d49a8791aa131b8e3e536452'), size_on_disk=4881005, blob_last_accessed=1756926337.2223055, blob_last_modified=1756926338.1273017), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417632/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/23c561fde2e6c2973ec1913373c343a5c49d6cc0dbb44ff21eb5aff87cd77d3a'), size_on_disk=18481828, blob_last_accessed=1756926306.2764404, blob_last_modified=1756926308.5744302), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417765/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bd2b5a1b1593db002ea9c159d4c40d1d8addddbf124207a7b32b0bb43e2e4d72'), size_on_disk=2696437, blob_last_accessed=1756926331.2103317, blob_last_modified=1756926332.046328), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f00bc431128183affc8964f9610f3e9486a043bcea5b31b3ebbcbd9b0070a448'), size_on_disk=6096918, blob_last_accessed=1756926377.3431315, blob_last_modified=1756926378.2571278), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417688/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f21ccaca0f6782b6ce2b70b50c9dc93487b6aaec03db6c30d9cb0c9fc01dda42'), size_on_disk=16023102, blob_last_accessed=1756926318.5463867, blob_last_modified=1756926320.0283804), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417647/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8c01c928abe00b5afa6a2292820695797391b3671cc110b9026f5fc5cb8564ae'), size_on_disk=2891716, blob_last_accessed=1756926310.1614234, blob_last_modified=1756926310.93942), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417613/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/695d3095619f3a313d25c506b9d0493f171896f1439bcf3bd1cd932d1f6fed5d'), size_on_disk=2812565, blob_last_accessed=1756926301.6434605, blob_last_modified=1756926302.4814568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417810/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b807197acb33acdd1b70eddedd59dc4d831e271d231d6c9bb6f1281fb4d5813f'), size_on_disk=9808564, blob_last_accessed=1756926338.8082986, blob_last_modified=1756926339.7202947), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417834/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a69c65a4d32399355863fa14d4a1e92ecff2c92498e122e8e77796de16d42948'), size_on_disk=8537233, blob_last_accessed=1756926343.3672788, blob_last_modified=1756926345.2462707), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417870/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ca1bc9825b47ee9ac59e53bc6ada3b1a9e8aba1b57c57c57947afae7926054b'), size_on_disk=10710319, blob_last_accessed=1756926349.7052515, blob_last_modified=1756926350.7552469), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417833/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/48792db399d7f0aab6765109e8c01abd61a2da93e5d0bdf7c325bcffe01fe113'), size_on_disk=8139822, blob_last_accessed=1756926343.3262792, blob_last_modified=1756926344.480274), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381015/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/345c83c5ab13d230542683014d3d6073ba5a348e3063fea325843981c6c6e7c1'), size_on_disk=3807314, blob_last_accessed=1756926377.67913, blob_last_modified=1756926378.4971266), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417767/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8d0afed33cd85298c709656da4b210e3fe87aa4d6822208d744200816d59b95b'), size_on_disk=3739401, blob_last_accessed=1756926331.2333317, blob_last_modified=1756926331.8623288), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417760/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0500e5835116a61477f2d98554d0dd97e8d932a6b564f709c40c4884f2b49052'), size_on_disk=2234621, blob_last_accessed=1756926330.2873356, blob_last_modified=1756926330.8273335), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417735/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/634551506800bc76183ce179c8c8f324cb6f0f577f6cf0b629b74ce4f1e995d5'), size_on_disk=19171195, blob_last_accessed=1756926326.0393543, blob_last_modified=1756926329.4873393), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417914/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d062dbf660e1ef240ff8ef99ff2d486d9caaabda14575e6365ce6f725fa91ccf'), size_on_disk=8599250, blob_last_accessed=1756926358.2422144, blob_last_modified=1756926360.3412054), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417797/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/12ee278356fa75b0ad555fe06376c78b71630324b7b89aa28abe960c187d256f'), size_on_disk=14391201, blob_last_accessed=1756926336.3743093, blob_last_modified=1756926338.322301), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417619/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0a4d4159941caa34a5e079821d49047d1434077926b2933c27d384e04ed2e5b5'), size_on_disk=9949802, blob_last_accessed=1756926302.5744565, blob_last_modified=1756926304.05045), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417761/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ba86b2041414034d38b95ccbf3c65957642c56b56914240999986ebc76b9e193'), size_on_disk=1635118, blob_last_accessed=1756926330.478335, blob_last_modified=1756926331.0933323), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381000/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/503f854145f3a220aa936072c3e66410d34914fef948992399a4addbfc977186'), size_on_disk=3904727, blob_last_accessed=1756926375.3401403, blob_last_modified=1756926376.5601351), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417603/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdfb1fd9b21341104e6423c684b0ae8a52ed1701eb3e232babb98340f953ea5b'), size_on_disk=4460026, blob_last_accessed=1756926300.366466, blob_last_modified=1756926301.2574623), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380986/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f23d3060df5cb96778fbdf10fff0155c931e1a1e8cc077c7f1582542d54827f5'), size_on_disk=5858046, blob_last_accessed=1756926373.285149, blob_last_modified=1756926374.5971434)}), refs=frozenset({'main'}), last_modified=1756926783.3167186)}), last_accessed=1756926861.9192877, last_modified=1756926783.3167186), CachedRepoInfo(repo_id='BrentLab/rossi_2021', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021'), size_on_disk=213725645, nb_files=47, revisions=frozenset({CachedRevisionInfo(commit_hash='6b29baae54286d5fbdd1c70f499c03319badad51', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51'), size_on_disk=213707016, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466110/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/d5283e08e898b4f59837f7ff2064f79c8a828994f868c637894f4e07df36daa5'), size_on_disk=13123795, blob_last_accessed=1757598690.8318212, blob_last_modified=1757104407.6787057), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466116/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/587a7b86dffe3d7ade9adeeb94029ea1de54e61fb2c2445dc527979b7f1c24ac'), size_on_disk=5596311, blob_last_accessed=1757598690.988821, blob_last_modified=1757104407.3507075), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/04bacc0f8c03ee000ba3b69320522d0b1be909686474b6e8a126173a0ceae8c4'), size_on_disk=15547870, blob_last_accessed=1757598690.8528214, blob_last_modified=1756944357.1972551), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466142/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/3dc044b5cc1b7bc8070d8de4b7b870524b296f26b4f43b22dce2dcbf161d4c74'), size_on_disk=1739520, blob_last_accessed=1757598691.2588208, blob_last_modified=1757104415.5896657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466112/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8f3441964a046d3c148b41f335948a085d26884e1a890e515711a2d1b48a40be'), size_on_disk=4883601, blob_last_accessed=1757598690.9128213, blob_last_modified=1757104408.5027015), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466125/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/0bba21490ea0a6297031b0ff3219fc0158d9bbcf15099906c86a770b1a1312fc'), size_on_disk=2624759, blob_last_accessed=1757598691.081821, blob_last_modified=1757104409.4426968), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466139/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/25c7d3ffbacca76fd998edffe0c762e220f448140019aa2d389facff5117ce48'), size_on_disk=176811, blob_last_accessed=1757598691.2478209, blob_last_modified=1757104412.6266806), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466108/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/56875f81a3cc7c1d40b3042b2c443058eb0f9a2b7e7ebc2e1704c098be612628'), size_on_disk=1765819, blob_last_accessed=1757598690.8238213, blob_last_modified=1757104407.1947083), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466117/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/49065d4cee74c20f34303405e34e284e29f19082b76fa883a38aeab24cadf674'), size_on_disk=1742958, blob_last_accessed=1757598690.995821, blob_last_modified=1757104411.8276846), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466150/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/80cd2d325e5d7a50c6ecbf79a052f45c54a93b9423e90735bf8989aada9bb9eb'), size_on_disk=3234303, blob_last_accessed=1757598691.3798206, blob_last_modified=1757104417.837654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466111/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/ba431fc9a05f40581e450429d467226a8e3c49195de355437f9044bb0fdfe524'), size_on_disk=5749460, blob_last_accessed=1757598690.9028213, blob_last_modified=1757104404.9707196), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466128/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/f631797d2bc13a580f570637d94a0b9ea37deb2fd7ef92e32c66fd9014260525'), size_on_disk=1059587, blob_last_accessed=1757598691.118821, blob_last_modified=1757104410.6996903), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466140/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/25f98a891820b5a91c11629ed5615d40ffe1fe4a6cba1b2c6642067d76b5be87'), size_on_disk=68174, blob_last_accessed=1757598691.2588208, blob_last_modified=1757104415.6456654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466141/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/e9efb807ce35fc3e43b1ff0d27b80646c7d137c587abd9d6247393e44d4f30a0'), size_on_disk=986450, blob_last_accessed=1757598691.2628207, blob_last_modified=1757104413.129678), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/09f6dce90ec0e64d8f18614091dd18ad2e86213a'), size_on_disk=4403, blob_last_accessed=1757598690.987821, blob_last_modified=1757104409.9196944), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466149/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/ae4d5237fde214a92d3267fbe11f6f1f88569cc7f3f7ccb40fed200871bf33b1'), size_on_disk=10933582, blob_last_accessed=1757598691.3518207, blob_last_modified=1757104415.1316679), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466114/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/2c07f671cca471d6ad3e10e071cd1b976a89a45ff32920913d8fc36dbeaa1678'), size_on_disk=10992797, blob_last_accessed=1757598690.9498212, blob_last_modified=1757104406.3287127), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466136/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/f5e7fd644d30ae0c8e2c72bc341155bfe5df2ce102b68b08ff4d243470026fcc'), size_on_disk=343033, blob_last_accessed=1757598691.1958208, blob_last_modified=1757104411.7116852), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466144/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/57b3d2c7917bfa29312cc51c921beff914858c61762e28a658eb29d8eaf3348b'), size_on_disk=918271, blob_last_accessed=1757598691.2758207, blob_last_modified=1757104413.554676), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466124/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/39ef32412cae257b724c3b797ea482eeffb5b29afef4246d997ea525a2124054'), size_on_disk=7162800, blob_last_accessed=1757598691.075821, blob_last_modified=1757104409.5966961), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466113/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/acec3b041a74ab45dbeb03e7f183c2b3d16479aadd64a81f80a74aa42d56368d'), size_on_disk=5866774, blob_last_accessed=1757598690.921821, blob_last_modified=1757104409.0226989), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466129/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/b290d4c9436756b444d0da1af1229b0a2a6e877fd968c5d01b73efa96e62cff8'), size_on_disk=6955953, blob_last_accessed=1757598691.196821, blob_last_modified=1757104410.78769), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466122/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/6362da9e07f02d4c9dc05f15f638ca1b1bf0de5989da4ee8c6f0768f2453f98f'), size_on_disk=1249990, blob_last_accessed=1757598691.0398211, blob_last_modified=1757104411.9016843), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1757598690.9228213, blob_last_modified=1757104403.6017265), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466118/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/192057e6004dd5cd303e8b1abf54cb72005e211c315665e28eec7b617a84d95a'), size_on_disk=728586, blob_last_accessed=1757598691.0068212, blob_last_modified=1757104407.0917087), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466130/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/73db821736e3e47c09da250e369fa06812e02e5abe7a4a3ceab5d1020422b66c'), size_on_disk=1920920, blob_last_accessed=1757598691.1328208, blob_last_modified=1757104410.9306893), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466107/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/2f924bc58d82a21b621b051addd1913c77369afaa738171ef0962548fc1a1021'), size_on_disk=5670198, blob_last_accessed=1757598690.8608212, blob_last_modified=1757104405.0417192), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466126/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/34292b90d99b5a5be542c7dd10b11336605330639d92e93b092e567a56fe7d93'), size_on_disk=2850164, blob_last_accessed=1757598691.086821, blob_last_modified=1757104410.0976934), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466143/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/54bd4af6ed8caf52a5810598d96c9b235266542586a692112cb247a209c2649f'), size_on_disk=10801408, blob_last_accessed=1757598691.2688208, blob_last_modified=1757104413.3726768), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466135/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/d44d381db0b8c0cb4960796e48d3d16e3d766aff87cbc686789a0c5799ba0a98'), size_on_disk=5204256, blob_last_accessed=1757598691.180821, blob_last_modified=1757104412.4376817), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466115/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/c08ce1194dd98cb028a6038944317e70ac1545e32b21dd323eafa57b16611e6e'), size_on_disk=3503431, blob_last_accessed=1757598690.9468212, blob_last_modified=1757104405.9427147), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466121/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/78d0f377f8a756b28f2f5296932368c9c6b764d2972dc93aa6dd354591d29860'), size_on_disk=7053621, blob_last_accessed=1757598691.0328212, blob_last_modified=1757104408.5097015), CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1757101035.6211069, blob_last_modified=1756944310.9395182), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466127/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/1565c5175e05c322f160529cb4bb74d5e8468458f150f228d6aadf2b670c3b4a'), size_on_disk=11670434, blob_last_accessed=1757598691.092821, blob_last_modified=1757104411.0776885), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466123/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/57b7ca5d419676590538573f295b99db05a952e20cb4c6b2a1618974611d5051'), size_on_disk=3672899, blob_last_accessed=1757598691.073821, blob_last_modified=1757104409.590696), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466134/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/0b23434181eb1fc864e858cfca82d2d8fed1524a8f7dbea3bf576f6f452cf08b'), size_on_disk=12398132, blob_last_accessed=1757598691.169821, blob_last_modified=1757104412.552681), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466133/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/b6d6913e14b52d5d51dab2f254815da89e03cebe75e80356b27cf18e31eb048f'), size_on_disk=5090722, blob_last_accessed=1757598691.169821, blob_last_modified=1757104412.180683), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466131/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/bdce0842b5568e406a799dbe36b4b9645a30fa58eb0a707f24ed3bb3900fbf67'), size_on_disk=11759994, blob_last_accessed=1757598691.157821, blob_last_modified=1757104411.1146884), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466132/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/cc41509e63473d565341ed36fd6881fecfd537e59c003f4779d4c4ec7b49cb54'), size_on_disk=1906184, blob_last_accessed=1757598691.161821, blob_last_modified=1757104413.4366765), CachedFileInfo(file_name='genome_map.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/51978dc2125afb40a41a29672f8ae750308d9a63'), size_on_disk=5311747, blob_last_accessed=1757598690.9468212, blob_last_modified=1757104406.477712), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466109/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/6588704c47423e671054df9cb2318c045138b0018f2eefff61ef91a25848eb58'), size_on_disk=5259413, blob_last_accessed=1757598690.8298213, blob_last_modified=1757104404.9987195), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466119/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/0157d9136c2c2bb5640866449adf4bab8c62b3d7f7fb4262981908c77986f89e'), size_on_disk=12516152, blob_last_accessed=1757598691.0068212, blob_last_modified=1757104412.3166823), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466120/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/4220a31e6a7be79341828d41da77ef77fb5a8026980d4445fec18c57b5229c1f'), size_on_disk=3644577, blob_last_accessed=1757598691.034821, blob_last_modified=1757104408.3467023)}), refs=frozenset(), last_modified=1757104417.837654), CachedRevisionInfo(commit_hash='6aac54b4b3fbb414561a8b6cc7acf87880c3c3c2', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6aac54b4b3fbb414561a8b6cc7acf87880c3c3c2'), size_on_disk=4665, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6aac54b4b3fbb414561a8b6cc7acf87880c3c3c2/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/14e1e02ad3804c0a4146a52509460e9cbb759d93'), size_on_disk=4665, blob_last_accessed=1758306843.4906857, blob_last_modified=1758156479.1516545)}), refs=frozenset(), last_modified=1758156479.1516545), CachedRevisionInfo(commit_hash='1e07e46fd0b5348243487ac4a573b570a2a3946b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1e07e46fd0b5348243487ac4a573b570a2a3946b'), size_on_disk=4683, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1e07e46fd0b5348243487ac4a573b570a2a3946b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/bb690d71edd44885609ed660d926f3cf2c4c473e'), size_on_disk=4683, blob_last_accessed=1758306349.7238934, blob_last_modified=1758156991.1235294)}), refs=frozenset({'main'}), last_modified=1758156991.1235294), CachedRevisionInfo(commit_hash='22ebbfcf62a7ed665fb8eceaea53b58b45a1709e', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/22ebbfcf62a7ed665fb8eceaea53b58b45a1709e'), size_on_disk=4602, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/22ebbfcf62a7ed665fb8eceaea53b58b45a1709e/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/e03b8d10ba59c549907a309ad8343133b7b902fc'), size_on_disk=4602, blob_last_accessed=1757613581.062093, blob_last_modified=1757613581.0610929)}), refs=frozenset(), last_modified=1757613581.0610929), CachedRevisionInfo(commit_hash='1acaa5629922414e8efe23a5d023bd44965824c8', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1acaa5629922414e8efe23a5d023bd44965824c8'), size_on_disk=4683, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1acaa5629922414e8efe23a5d023bd44965824c8/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/bb690d71edd44885609ed660d926f3cf2c4c473e'), size_on_disk=4683, blob_last_accessed=1758306349.7238934, blob_last_modified=1758156991.1235294)}), refs=frozenset(), last_modified=1758156991.1235294), CachedRevisionInfo(commit_hash='664d95cdd482d753bc139d26119061c2fa6eaa76', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/664d95cdd482d753bc139d26119061c2fa6eaa76'), size_on_disk=4679, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/664d95cdd482d753bc139d26119061c2fa6eaa76/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/ff6f75db42c0690f0e0285e56c1aa95bc5cf1300'), size_on_disk=4679, blob_last_accessed=1758157110.1705706, blob_last_modified=1758157110.1695707)}), refs=frozenset(), last_modified=1758157110.1695707), CachedRevisionInfo(commit_hash='3ce77eb2070d7bb43049ba26bb462fded0ff7863', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/3ce77eb2070d7bb43049ba26bb462fded0ff7863'), size_on_disk=15562566, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/3ce77eb2070d7bb43049ba26bb462fded0ff7863/genome_map/accession=SRR11466106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/04bacc0f8c03ee000ba3b69320522d0b1be909686474b6e8a126173a0ceae8c4'), size_on_disk=15547870, blob_last_accessed=1757598690.8528214, blob_last_modified=1756944357.1972551), CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/3ce77eb2070d7bb43049ba26bb462fded0ff7863/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1757101035.6211069, blob_last_modified=1756944310.9395182)}), refs=frozenset(), last_modified=1756944357.1972551)}), last_accessed=1758306843.4906857, last_modified=1758157110.1695707), CachedRepoInfo(repo_id='BrentLab/harbison_2004', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004'), size_on_disk=12305, nb_files=2, revisions=frozenset({CachedRevisionInfo(commit_hash='2916ad316cc0d8a1ce2e7f35d3707b831b203e87', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/2916ad316cc0d8a1ce2e7f35d3707b831b203e87'), size_on_disk=7160, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/2916ad316cc0d8a1ce2e7f35d3707b831b203e87/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/7e3ec66f5479346ca9d082545ff0b6a960fb60d5'), size_on_disk=7160, blob_last_accessed=1758155946.7689884, blob_last_modified=1758155946.7669883)}), refs=frozenset({'main'}), last_modified=1758155946.7669883), CachedRevisionInfo(commit_hash='073cdeb1b541c28b1a97df9dbc6be6af1d9c23b6', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/073cdeb1b541c28b1a97df9dbc6be6af1d9c23b6'), size_on_disk=5145, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/073cdeb1b541c28b1a97df9dbc6be6af1d9c23b6/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/f7a6cf3dd4477f08e508f38665350e8d11589963'), size_on_disk=5145, blob_last_accessed=1755876214.0380862, blob_last_modified=1755876188.1642818)}), refs=frozenset(), last_modified=1755876188.1642818)}), last_accessed=1758155946.7689884, last_modified=1758155946.7669883), CachedRepoInfo(repo_id='BrentLab/hughes_2006', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006'), size_on_disk=11943331, nb_files=8, revisions=frozenset({CachedRevisionInfo(commit_hash='cc20720c4fe7de9151a051aafbd41e6de2b4cd84', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84'), size_on_disk=11937630, files=frozenset({CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756856028.69735, blob_last_modified=1756856028.7793496), CachedFileInfo(file_name='overexpression.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/overexpression.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/9b8cc1feede780efe0e8537555ed1cbbf067e7fa9aa7a7edc74e5a343c64a443'), size_on_disk=6337781, blob_last_accessed=1756856028.536351, blob_last_modified=1756856029.230347), CachedFileInfo(file_name='metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/e27a67137876123c68c229705cfbb8e06ed3ee76a3a868b66d80d624f8d03671'), size_on_disk=7942, blob_last_accessed=1756856028.533351, blob_last_modified=1756856028.9083488), CachedFileInfo(file_name='checksum.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/checksum.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/973b686313e32c96b6e0ef1dbc84020857c073ef'), size_on_disk=159, blob_last_accessed=1756856028.5883508, blob_last_modified=1756856028.6513503), CachedFileInfo(file_name='parse_hughes_2006.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/scripts/parse_hughes_2006.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/0c2645bdc0ab7f6be38a428a0a92752e9d3f88d5'), size_on_disk=9255, blob_last_accessed=1756856028.5803509, blob_last_modified=1756856028.6393504), CachedFileInfo(file_name='knockout.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/knockout.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/5e000e2e9401011c9ecc81655c94d55b7f9469ed856216fa21cef2883a904c93'), size_on_disk=5571518, blob_last_accessed=1756856029.2493467, blob_last_modified=1756856029.2143471), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/842b551fb3d1ba1e6f1daa35157a0c7c616f7655'), size_on_disk=8514, blob_last_accessed=1756855276.4871445, blob_last_modified=1756855276.4861445)}), refs=frozenset({'main'}), last_modified=1756856029.230347), CachedRevisionInfo(commit_hash='51ae7addf429c955a97934a15b242ff8a2ccd653', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/51ae7addf429c955a97934a15b242ff8a2ccd653'), size_on_disk=5701, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/51ae7addf429c955a97934a15b242ff8a2ccd653/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/f799807a7bf81229b0d063e6c939339c1f5c90f4'), size_on_disk=5701, blob_last_accessed=1756855057.8437808, blob_last_modified=1756855057.8417807)}), refs=frozenset(), last_modified=1756855057.8417807)}), last_accessed=1756856029.2493467, last_modified=1756856029.230347), CachedRepoInfo(repo_id='BrentLab/hackett_2020', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020'), size_on_disk=407271569, nb_files=17, revisions=frozenset({CachedRevisionInfo(commit_hash='60b3ecf6a06e709f0397b327ece5c31016303b5c', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/60b3ecf6a06e709f0397b327ece5c31016303b5c'), size_on_disk=9543, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/60b3ecf6a06e709f0397b327ece5c31016303b5c/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/6f54100c2bb84a6bc059c17e0954dad0ea451666'), size_on_disk=9543, blob_last_accessed=1758400469.288775, blob_last_modified=1758155372.1328666)}), refs=frozenset({'main'}), last_modified=1758155372.1328666), CachedRevisionInfo(commit_hash='b9d39535e175295d9cba23489a49fafebbac5ca0', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0'), size_on_disk=404565563, files=frozenset({CachedFileInfo(file_name='hackett_2020.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/hackett_2020.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4f47951c65c799b5c74ee3782b49c16cbb038d50'), size_on_disk=55, blob_last_accessed=1757105553.3228161, blob_last_modified=1756843395.5719483), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756843395.5059488, blob_last_modified=1756843395.5679483), CachedFileInfo(file_name='parse_mcisaac_data.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/scripts/parse_mcisaac_data.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0'), size_on_disk=4691, blob_last_accessed=1756843395.7579472, blob_last_modified=1756843395.820947), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/03e579e00c632cdb393c0ffafd9d15b069390e5c'), size_on_disk=9066, blob_last_accessed=1757439136.0598524, blob_last_modified=1757105945.6476595), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1757541699.4460278, blob_last_modified=1756843405.225893)}), refs=frozenset(), last_modified=1757105945.6476595), CachedRevisionInfo(commit_hash='2c196866d4f057cb5cf0adf1fe35f2c712c61025', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025'), size_on_disk=404565376, files=frozenset({CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756843395.5059488, blob_last_modified=1756843395.5679483), CachedFileInfo(file_name='hackett_2020.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/hackett_2020.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4f47951c65c799b5c74ee3782b49c16cbb038d50'), size_on_disk=55, blob_last_accessed=1757105553.3228161, blob_last_modified=1756843395.5719483), CachedFileInfo(file_name='parse_mcisaac_data.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/scripts/parse_mcisaac_data.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0'), size_on_disk=4691, blob_last_accessed=1756843395.7579472, blob_last_modified=1756843395.820947), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1757541699.4460278, blob_last_modified=1756843405.225893), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c3e72ccb1b8deba4bbfd18abe6081de7ec3914d9'), size_on_disk=8879, blob_last_accessed=1757105552.290822, blob_last_modified=1757104206.2667632)}), refs=frozenset(), last_modified=1757104206.2667632), CachedRevisionInfo(commit_hash='1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43'), size_on_disk=2678425, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=20/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/97adc0bb2b436c325555d82bb7f0ffb0e250d73092fcbf03ac0c00fa2b762e51'), size_on_disk=351379, blob_last_accessed=1756145253.7895415, blob_last_modified=1756145254.6265287), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=10/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4a2eb1051cad96c5b39a014f19b092f042b03700cc1b1a40c2de9e7edd73edf9'), size_on_disk=352201, blob_last_accessed=1756145253.7915416, blob_last_modified=1756145254.6375287), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=0/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/409962e1914ac9e9d631e4ae73e04b167822d0915d8d8d4a87703d4c7281ffb9'), size_on_disk=209695, blob_last_accessed=1756145254.7305272, blob_last_modified=1756145254.608529), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=15/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/51dfb68ed2518f0dd8ab4d4070d4896b6eca912ecb7e4aa773d857e3096020b5'), size_on_disk=351273, blob_last_accessed=1756145217.972108, blob_last_modified=1756142799.7054222), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=90/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/ebd06af83cc386d9094ce6b8d795b155c76c47c96da1a458743348264f1638ae'), size_on_disk=349917, blob_last_accessed=1756145253.7845416, blob_last_modified=1756145254.7235274), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/45cb0687c3ea72e7b32ad17dd799bc38ad02f80b'), size_on_disk=16034, blob_last_accessed=1756138543.6766906, blob_last_modified=1756138543.6746907), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=5/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/ed331d9d2b27a0958b12368c7b5d2d3383b111d3d1c39ed0e77b78561dc78b6a'), size_on_disk=348626, blob_last_accessed=1756145253.7845416, blob_last_modified=1756145254.6395288), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=45/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/db8595cd20dd7440c890c46025cf0643da8ec6be3e15bc18a8759b3fecdc02af'), size_on_disk=349256, blob_last_accessed=1756145253.7795417, blob_last_modified=1756145254.6225288), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=30/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/a85bd6b418d9644d9adaa1269c27f97469a4aaee51af63cf1aa041f62cd8ba2c'), size_on_disk=350044, blob_last_accessed=1756145253.7915416, blob_last_modified=1756145254.6385286)}), refs=frozenset(), last_modified=1756145254.7235274), CachedRevisionInfo(commit_hash='7192b0e2275ba5aa3ffb3752b87f1c3595f2331a', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a'), size_on_disk=404565656, files=frozenset({CachedFileInfo(file_name='hackett_2020.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/hackett_2020.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4f47951c65c799b5c74ee3782b49c16cbb038d50'), size_on_disk=55, blob_last_accessed=1757105553.3228161, blob_last_modified=1756843395.5719483), CachedFileInfo(file_name='parse_mcisaac_data.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/scripts/parse_mcisaac_data.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0'), size_on_disk=4691, blob_last_accessed=1756843395.7579472, blob_last_modified=1756843395.820947), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756843395.5059488, blob_last_modified=1756843395.5679483), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/f3899cd97da03a3543db10d76c15ee0442bb136e'), size_on_disk=9159, blob_last_accessed=1758145252.5929751, blob_last_modified=1757541416.40237), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1757541699.4460278, blob_last_modified=1756843405.225893)}), refs=frozenset(), last_modified=1757541416.40237)}), last_accessed=1758400469.288775, last_modified=1758155372.1328666), CachedRepoInfo(repo_id='BrentLab/hu_2007_reimand_2010', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010'), size_on_disk=42682732, nb_files=4, revisions=frozenset({CachedRevisionInfo(commit_hash='31ccd35daf785420014a1346a7db064dd306d4f8', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8'), size_on_disk=42682732, files=frozenset({CachedFileInfo(file_name='hu_2007_reimand_2010.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/hu_2007_reimand_2010.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/eb9be5a1f01907a81eb2ab4fbbee36ff8c50186f'), size_on_disk=63, blob_last_accessed=1756844741.7997973, blob_last_modified=1756844741.8557973), CachedFileInfo(file_name='hu_2007_reimand_2010.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/hu_2007_reimand_2010.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/47a15f3e23f8cb50af3217d00c6439d29a7422b5190116e4453a5f78cb22540d'), size_on_disk=42677516, blob_last_accessed=1756844742.1787965, blob_last_modified=1756844742.1477966), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756844741.8097973, blob_last_modified=1756844741.8767972), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/886f4614dfd1ef33709d462908ae18608c13e8ba'), size_on_disk=2692, blob_last_accessed=1756844742.0937967, blob_last_modified=1756844742.1537964)}), refs=frozenset({'main'}), last_modified=1756844742.1537964)}), last_accessed=1756844742.1787965, last_modified=1756844742.1537964), CachedRepoInfo(repo_id='BrentLab/kemmeren_2014', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014'), size_on_disk=319226397, nb_files=3, revisions=frozenset({CachedRevisionInfo(commit_hash='a6c9a954a1def1774b68582f117ad1abc5864a07', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07'), size_on_disk=319226397, files=frozenset({CachedFileInfo(file_name='kemmeren_2014.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07/kemmeren_2014.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/3c463017444bd7690959c0d6fff0d0ebe2faa7b916dd9c44e305156f6c98b863'), size_on_disk=319218929, blob_last_accessed=1756832531.471449, blob_last_modified=1756832530.9644527), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756832529.5094638, blob_last_modified=1756832529.5434635), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/a29a64128c10ef9691ee773131972c61f720ba07'), size_on_disk=5007, blob_last_accessed=1756832529.5094638, blob_last_modified=1756832529.5404634)}), refs=frozenset({'main'}), last_modified=1756832530.9644527)}), last_accessed=1756832531.471449, last_modified=1756832530.9644527)}), warnings=[CorruptedCacheException(\"Snapshots dir doesn't exist in cached repo: /home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots\")])" + ] + }, + "execution_count": 76, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "cache_info" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2. Understanding the 3-Case Metadata Caching Strategy\n", + "\n", + "HfCacheManager implements an intelligent 3-case strategy for metadata access that minimizes downloads and maximizes performance:\n", + "\n", + "1. **DuckDB Check**: First check if metadata already exists in the DuckDB database\n", + "2. **Cache Load**: If not in DuckDB, try to load from local HuggingFace cache \n", + "3. **Download**: If not cached, download from HuggingFace Hub\n", + "\n", + "This strategy is implemented in the internal `_get_metadata_for_config()` method and automatically used when loading data with HfQueryAPI." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Demonstrating the 3-Case Strategy\n", + "\n", + "Let's see how the caching strategy works by examining what metadata tables would be created and checking the DuckDB state." + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DuckDB Metadata Tables (Case 1):\n", + "===================================\n", + " No metadata tables found in DuckDB\n", + " → Would proceed to Case 2 (check HF cache) or Case 3 (download)\n", + "\n", + "The 3-case strategy ensures:\n", + "• Fast access: DuckDB queries are nearly instantaneous\n", + "• Minimal downloads: Reuse locally cached files when possible\n", + "• Automatic fallback: Download only when necessary\n", + "• Transparent operation: Works automatically with HfQueryAPI\n" + ] + } + ], + "source": [ + "# Check current DuckDB state (Case 1 check)\n", + "tables = conn.execute(\n", + " \"SELECT table_name FROM information_schema.tables WHERE table_name LIKE 'metadata_%'\"\n", + ").fetchall()\n", + "\n", + "print(\"DuckDB Metadata Tables (Case 1):\")\n", + "print(\"=\" * 35)\n", + "if tables:\n", + " for table in tables:\n", + " count = conn.execute(f\"SELECT COUNT(*) FROM {table[0]}\").fetchone()[0]\n", + " print(f\" • {table[0]}: {count} rows\")\n", + "else:\n", + " print(\" No metadata tables found in DuckDB\")\n", + " print(\" → Would proceed to Case 2 (check HF cache) or Case 3 (download)\")\n", + "\n", + "print(f\"\\nThe 3-case strategy ensures:\")\n", + "print(\"• Fast access: DuckDB queries are nearly instantaneous\")\n", + "print(\"• Minimal downloads: Reuse locally cached files when possible\") \n", + "print(\"• Automatic fallback: Download only when necessary\")\n", + "print(\"• Transparent operation: Works automatically with HfQueryAPI\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Checking HuggingFace Cache Status (Case 2)\n", + "\n", + "The second case checks if files are already cached locally by HuggingFace. Let's examine the cache state for our target repository." + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "HuggingFace Cache Status (Case 2):\n", + "========================================\n", + "✓ Repository BrentLab/hackett_2020 found in cache\n", + " Size: 407.3M\n", + " Revisions: 5\n", + " Files: 17\n", + " Latest revision: 60b3ecf6\n", + " Last accessed: 1758155372.1328666\n", + "\n", + " → Case 2 would succeed: Load from local cache\n", + "\n", + "Cache efficiency: Using local files avoids re-downloading 407.3M\n" + ] + } + ], + "source": [ + "# Check if target repository is in HuggingFace cache\n", + "cache_info = scan_cache_dir()\n", + "target_repo = None\n", + "\n", + "for repo in cache_info.repos:\n", + " if repo.repo_id == cache_manager.repo_id:\n", + " target_repo = repo\n", + " break\n", + "\n", + "print(\"HuggingFace Cache Status (Case 2):\")\n", + "print(\"=\" * 40)\n", + "\n", + "if target_repo:\n", + " print(f\"✓ Repository {cache_manager.repo_id} found in cache\")\n", + " print(f\" Size: {target_repo.size_on_disk_str}\")\n", + " print(f\" Revisions: {len(target_repo.revisions)}\")\n", + " print(f\" Files: {target_repo.nb_files}\")\n", + " \n", + " # Show latest revision info\n", + " if target_repo.revisions:\n", + " latest_rev = max(target_repo.revisions, key=lambda r: r.last_modified)\n", + " print(f\" Latest revision: {latest_rev.commit_hash[:8]}\")\n", + " print(f\" Last accessed: {latest_rev.last_modified}\")\n", + " \n", + " print(\"\\n → Case 2 would succeed: Load from local cache\")\n", + "else:\n", + " print(f\"✗ Repository {cache_manager.repo_id} not found in cache\")\n", + " print(\" → Would proceed to Case 3: Download from HuggingFace Hub\")\n", + "\n", + "print(f\"\\nCache efficiency: Using local files avoids re-downloading {target_repo.size_on_disk_str if target_repo else 'unknown size'}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3. Cache Management and Cleanup\n", + "\n", + "HfCacheManager's primary value is in providing sophisticated cache management. Let's explore the different cleanup strategies available." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Cache Overview and Current Status\n", + "\n", + "Before cleaning, let's understand what we're working with." + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Current HuggingFace Cache Overview:\n", + "========================================\n", + "Total cache size: 4.6G\n", + "Number of repositories: 9\n", + "\n", + "Largest repositories (top 5):\n", + " • BrentLab/barkai_compendium: 3.6G (1 revisions)\n", + " • BrentLab/hackett_2020: 407.3M (5 revisions)\n", + " • BrentLab/kemmeren_2014: 319.2M (1 revisions)\n", + " • BrentLab/rossi_2021: 213.7M (7 revisions)\n", + " • BrentLab/hu_2007_reimand_2010: 42.7M (1 revisions)\n", + " ... and 4 more repositories\n", + "\n", + "Total revisions across all repos: 29\n", + "Revisions older than 30 days: 0\n", + "Recent revisions (≤30 days): 29\n" + ] + } + ], + "source": [ + "# Get comprehensive cache overview\n", + "cache_info = scan_cache_dir()\n", + "\n", + "print(\"Current HuggingFace Cache Overview:\")\n", + "print(\"=\" * 40)\n", + "print(f\"Total cache size: {cache_info.size_on_disk_str}\")\n", + "print(f\"Number of repositories: {len(cache_info.repos)}\")\n", + "\n", + "# Analyze cache by repository size\n", + "repo_sizes = []\n", + "for repo in cache_info.repos:\n", + " repo_sizes.append((repo.repo_id, repo.size_on_disk, repo.size_on_disk_str, len(repo.revisions)))\n", + "\n", + "# Sort by size (largest first)\n", + "repo_sizes.sort(key=lambda x: x[1], reverse=True)\n", + "\n", + "print(f\"\\nLargest repositories (top 5):\")\n", + "for repo_id, size_bytes, size_str, revisions in repo_sizes[:5]:\n", + " print(f\" • {repo_id}: {size_str} ({revisions} revisions)\")\n", + "\n", + "if len(repo_sizes) > 5:\n", + " print(f\" ... and {len(repo_sizes) - 5} more repositories\")\n", + "\n", + "# Calculate total revisions\n", + "total_revisions = sum(len(repo.revisions) for repo in cache_info.repos)\n", + "print(f\"\\nTotal revisions across all repos: {total_revisions}\")\n", + "\n", + "# Show age distribution\n", + "from datetime import datetime\n", + "now = datetime.now().timestamp()\n", + "old_revisions = 0\n", + "for repo in cache_info.repos:\n", + " for rev in repo.revisions:\n", + " age_days = (now - rev.last_modified) / (24 * 3600)\n", + " if age_days > 30:\n", + " old_revisions += 1\n", + "\n", + "print(f\"Revisions older than 30 days: {old_revisions}\")\n", + "print(f\"Recent revisions (≤30 days): {total_revisions - old_revisions}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Querying Loaded Metadata\n", + "\n", + "Once metadata is loaded into DuckDB, we can query it using SQL." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Internal Cache Management Methods\n", + "\n", + "HfCacheManager provides several internal methods that work behind the scenes. Let's explore what these methods do and how they integrate with the caching strategy." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4. Working with Specific Metadata Configurations\n", + "\n", + "You can also retrieve metadata for specific configurations rather than all at once." + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "HfCacheManager Internal Methods:\n", + "===================================\n", + "\n", + "1. _get_metadata_for_config(config)\n", + " → Implements the 3-case strategy for a specific configuration\n", + " → Returns detailed result with strategy used and success status\n", + "\n", + "2. _check_metadata_exists_in_duckdb(table_name)\n", + " → Case 1: Checks if metadata table already exists in DuckDB\n", + " → Fast check using information_schema.tables\n", + "\n", + "3. _load_metadata_from_cache(config, table_name)\n", + " → Case 2: Attempts to load from local HuggingFace cache\n", + " → Uses try_to_load_from_cache() to find cached files\n", + "\n", + "4. _download_and_load_metadata(config, table_name)\n", + " → Case 3: Downloads from HuggingFace Hub if not cached\n", + " → Uses snapshot_download() for efficient file retrieval\n", + "\n", + "5. _create_duckdb_table_from_files(file_paths, table_name)\n", + " → Creates DuckDB views from parquet files\n", + " → Handles both single files and multiple files efficiently\n", + "\n", + "6. _extract_embedded_metadata_field(data_table, field, metadata_table)\n", + " → Extracts metadata fields from data tables\n", + " → Creates separate queryable metadata views\n", + "\n", + "These methods work together to provide:\n", + "• Transparent caching that 'just works'\n", + "• Minimal network usage through intelligent fallbacks\n", + "• Fast metadata access via DuckDB views\n", + "• Automatic handling of different file structures\n" + ] + } + ], + "source": [ + "# Demonstrate understanding of internal cache methods\n", + "print(\"HfCacheManager Internal Methods:\")\n", + "print(\"=\" * 35)\n", + "\n", + "print(\"\\n1. _get_metadata_for_config(config)\")\n", + "print(\" → Implements the 3-case strategy for a specific configuration\")\n", + "print(\" → Returns detailed result with strategy used and success status\")\n", + "\n", + "print(\"\\n2. _check_metadata_exists_in_duckdb(table_name)\")\n", + "print(\" → Case 1: Checks if metadata table already exists in DuckDB\")\n", + "print(\" → Fast check using information_schema.tables\")\n", + "\n", + "print(\"\\n3. _load_metadata_from_cache(config, table_name)\")\n", + "print(\" → Case 2: Attempts to load from local HuggingFace cache\")\n", + "print(\" → Uses try_to_load_from_cache() to find cached files\")\n", + "\n", + "print(\"\\n4. _download_and_load_metadata(config, table_name)\")\n", + "print(\" → Case 3: Downloads from HuggingFace Hub if not cached\")\n", + "print(\" → Uses snapshot_download() for efficient file retrieval\")\n", + "\n", + "print(\"\\n5. _create_duckdb_table_from_files(file_paths, table_name)\")\n", + "print(\" → Creates DuckDB views from parquet files\")\n", + "print(\" → Handles both single files and multiple files efficiently\")\n", + "\n", + "print(\"\\n6. _extract_embedded_metadata_field(data_table, field, metadata_table)\")\n", + "print(\" → Extracts metadata fields from data tables\")\n", + "print(\" → Creates separate queryable metadata views\")\n", + "\n", + "print(\"\\nThese methods work together to provide:\")\n", + "print(\"• Transparent caching that 'just works'\")\n", + "print(\"• Minimal network usage through intelligent fallbacks\")\n", + "print(\"• Fast metadata access via DuckDB views\")\n", + "print(\"• Automatic handling of different file structures\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 5. Extracting Embedded Metadata\n", + "\n", + "Some datasets have metadata embedded within their data files. The HfCacheManager can extract this embedded metadata into separate, queryable tables." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4. Embedded Metadata Extraction\n", + "\n", + "One unique feature of HfCacheManager is the ability to extract embedded metadata fields from data tables into separate, queryable metadata tables." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Demonstrate embedded metadata extraction concept\n", + "print(\"Embedded Metadata Extraction:\")\n", + "print(\"=\" * 35)\n", + "\n", + "print(\"\\nScenario: You have a data table with embedded metadata fields\")\n", + "print(\"Example: genomics data with 'experimental_condition' field\")\n", + "\n", + "# Create sample data to demonstrate the concept\n", + "conn.execute(\"\"\"\n", + " CREATE TABLE sample_genomics_data AS \n", + " SELECT \n", + " 'gene_' || (row_number() OVER()) as gene_id,\n", + " random() * 1000 as expression_value,\n", + " CASE \n", + " WHEN (row_number() OVER()) % 4 = 0 THEN 'control'\n", + " WHEN (row_number() OVER()) % 4 = 1 THEN 'treatment_A'\n", + " WHEN (row_number() OVER()) % 4 = 2 THEN 'treatment_B'\n", + " ELSE 'stress_condition'\n", + " END as experimental_condition,\n", + " CASE \n", + " WHEN (row_number() OVER()) % 3 = 0 THEN 'timepoint_0h'\n", + " WHEN (row_number() OVER()) % 3 = 1 THEN 'timepoint_6h'\n", + " ELSE 'timepoint_24h'\n", + " END as timepoint\n", + " FROM range(100)\n", + "\"\"\")\n", + "\n", + "print(\"✓ Created sample genomics data with embedded metadata fields\")\n", + "\n", + "# Show the data structure\n", + "sample_data = conn.execute(\n", + " \"SELECT * FROM sample_genomics_data LIMIT 5\"\n", + ").fetchall()\n", + "\n", + "print(f\"\\nSample data structure:\")\n", + "print(\"gene_id | expression_value | experimental_condition | timepoint\")\n", + "print(\"-\" * 65)\n", + "for row in sample_data:\n", + " print(f\"{row[0]:8} | {row[1]:15.1f} | {row[2]:20} | {row[3]}\")\n", + "\n", + "print(f\"\\nEmbedded metadata fields identified:\")\n", + "print(\"• experimental_condition: Contains treatment/control information\")\n", + "print(\"• timepoint: Contains temporal sampling information\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Use HfCacheManager to extract embedded metadata\n", + "print(\"Using HfCacheManager for Metadata Extraction:\")\n", + "print(\"=\" * 50)\n", + "\n", + "# Extract experimental_condition metadata\n", + "success1 = cache_manager._extract_embedded_metadata_field(\n", + " 'sample_genomics_data', \n", + " 'experimental_condition', \n", + " 'metadata_experimental_conditions'\n", + ")\n", + "\n", + "# Extract timepoint metadata \n", + "success2 = cache_manager._extract_embedded_metadata_field(\n", + " 'sample_genomics_data',\n", + " 'timepoint', \n", + " 'metadata_timepoints'\n", + ")\n", + "\n", + "print(f\"Experimental condition extraction: {'✓ Success' if success1 else '✗ Failed'}\")\n", + "print(f\"Timepoint extraction: {'✓ Success' if success2 else '✗ Failed'}\")\n", + "\n", + "# Show extracted metadata tables\n", + "if success1:\n", + " print(f\"\\nExtracted experimental conditions:\")\n", + " conditions = conn.execute(\n", + " \"SELECT value, count FROM metadata_experimental_conditions ORDER BY count DESC\"\n", + " ).fetchall()\n", + " \n", + " for condition, count in conditions:\n", + " print(f\" • {condition}: {count} samples\")\n", + "\n", + "if success2:\n", + " print(f\"\\nExtracted timepoints:\")\n", + " timepoints = conn.execute(\n", + " \"SELECT value, count FROM metadata_timepoints ORDER BY count DESC\"\n", + " ).fetchall()\n", + " \n", + " for timepoint, count in timepoints:\n", + " print(f\" • {timepoint}: {count} samples\")\n", + "\n", + "print(f\"\\nBenefits of extraction:\")\n", + "print(\"• Separate queryable metadata tables\")\n", + "print(\"• Fast metadata-based filtering and analysis\") \n", + "print(\"• Clear separation of data and metadata concerns\")\n", + "print(\"• Reusable metadata across different analyses\")" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Current HuggingFace Cache Status:\n", + "===================================\n", + "Total size: 4.6G\n", + "Number of repositories: 9\n", + "\n", + "Repository breakdown:\n", + " • BrentLab/mahendrawada_2025: 2.0M (3 revisions)\n", + " • BrentLab/yeast_genome_resources: 114.5K (7 revisions)\n", + " • BrentLab/barkai_compendium: 3.6G (1 revisions)\n", + " • BrentLab/rossi_2021: 213.7M (7 revisions)\n", + " • BrentLab/harbison_2004: 12.3K (2 revisions)\n", + " ... and 4 more repositories\n", + "\n", + "Target repository (BrentLab/hackett_2020) cache info:\n", + " Size: 407.3M\n", + " Revisions: 5\n", + " Latest revision: 60b3ecf6\n", + " Last modified: 1758155372.1328666\n" + ] + } + ], + "source": [ + "from huggingface_hub import scan_cache_dir\n", + "\n", + "# Get current cache information \n", + "cache_info = scan_cache_dir()\n", + "\n", + "print(\"Current HuggingFace Cache Status:\")\n", + "print(\"=\" * 35)\n", + "print(f\"Total size: {cache_info.size_on_disk_str}\")\n", + "print(f\"Number of repositories: {len(cache_info.repos)}\")\n", + "\n", + "print(\"\\nRepository breakdown:\")\n", + "for repo in list(cache_info.repos)[:5]: # Show first 5 repos\n", + " print(f\" • {repo.repo_id}: {repo.size_on_disk_str} ({len(repo.revisions)} revisions)\")\n", + "\n", + "if len(cache_info.repos) > 5:\n", + " print(f\" ... and {len(cache_info.repos) - 5} more repositories\")\n", + "\n", + "# Show target repository if it exists in cache\n", + "target_repo = None\n", + "for repo in cache_info.repos:\n", + " if repo.repo_id == cache_manager.repo_id:\n", + " target_repo = repo\n", + " break\n", + "\n", + "if target_repo:\n", + " print(f\"\\nTarget repository ({cache_manager.repo_id}) cache info:\")\n", + " print(f\" Size: {target_repo.size_on_disk_str}\")\n", + " print(f\" Revisions: {len(target_repo.revisions)}\")\n", + " if target_repo.revisions:\n", + " latest_rev = max(target_repo.revisions, key=lambda r: r.last_modified)\n", + " print(f\" Latest revision: {latest_rev.commit_hash[:8]}\")\n", + " print(f\" Last modified: {latest_rev.last_modified}\")\n", + "else:\n", + " print(f\"\\nTarget repository ({cache_manager.repo_id}) not found in cache.\")\n", + " print(\"It may need to be downloaded first.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Cache Cleanup by Age" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Cleaning cache by age (30+ days old):\n", + "========================================\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:__main__:No old revisions found to delete\n", + "INFO:__main__:Found 0 old revisions. Will free 0.0\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Cleanup strategy created:\n", + "Expected space freed: 0.0\n", + "Items to delete: 0\n", + "No old files found for cleanup.\n" + ] + } + ], + "source": [ + "# Clean cache entries older than 30 days (dry run)\n", + "print(\"Cleaning cache by age (30+ days old):\")\n", + "print(\"=\" * 40)\n", + "\n", + "age_cleanup = cache_manager.clean_cache_by_age(\n", + " max_age_days=30,\n", + " dry_run=True # Set to False to actually execute\n", + ")\n", + "\n", + "print(f\"\\nCleanup strategy created:\")\n", + "print(f\"Expected space freed: {age_cleanup.expected_freed_size_str}\")\n", + "\n", + "# Count total items to delete across all categories\n", + "total_items = len(age_cleanup.blobs) + len(age_cleanup.refs) + len(age_cleanup.repos) + len(age_cleanup.snapshots)\n", + "print(f\"Items to delete: {total_items}\")\n", + "\n", + "# Show breakdown of what would be deleted\n", + "if total_items > 0:\n", + " print(f\"\\nBreakdown of items to delete:\")\n", + " print(f\" • Blob files: {len(age_cleanup.blobs)}\")\n", + " print(f\" • Reference files: {len(age_cleanup.refs)}\")\n", + " print(f\" • Repository directories: {len(age_cleanup.repos)}\")\n", + " print(f\" • Snapshot directories: {len(age_cleanup.snapshots)}\")\n", + " \n", + " # Show some example items\n", + " if age_cleanup.blobs:\n", + " print(f\"\\nSample blob files to delete:\")\n", + " for item in list(age_cleanup.blobs)[:3]:\n", + " print(f\" • {item}\")\n", + " if len(age_cleanup.blobs) > 3:\n", + " print(f\" ... and {len(age_cleanup.blobs) - 3} more blob files\")\n", + "else:\n", + " print(\"No old files found for cleanup.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Cache Cleanup by Size" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Cleaning cache to target size: 1GB\n", + "========================================\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:__main__:Selected 14 revisions for deletion. Will free 4.0G\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Size-based cleanup strategy:\n", + "Expected space freed: 4.0G\n", + "Items to delete: 30\n", + "\n", + "Comparing cleanup strategies for 1GB:\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:__main__:Selected 14 revisions for deletion. Will free 4.0G\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " • oldest_first : 4.0G (30 items)\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:__main__:Selected 1 revisions for deletion. Will free 3.6G\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " • largest_first : 3.6G (1 items)\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:__main__:Selected 14 revisions for deletion. Will free 4.0G\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " • least_used : 4.0G (30 items)\n" + ] + } + ], + "source": [ + "# Clean cache to target size (dry run)\n", + "target_size = \"1GB\"\n", + "print(f\"Cleaning cache to target size: {target_size}\")\n", + "print(\"=\" * 40)\n", + "\n", + "size_cleanup = cache_manager.clean_cache_by_size(\n", + " target_size=target_size,\n", + " strategy=\"oldest_first\", # Can be: oldest_first, largest_first, least_used\n", + " dry_run=True\n", + ")\n", + "\n", + "print(f\"\\nSize-based cleanup strategy:\")\n", + "print(f\"Expected space freed: {size_cleanup.expected_freed_size_str}\")\n", + "\n", + "# Count total items to delete across all categories\n", + "total_items = len(size_cleanup.blobs) + len(size_cleanup.refs) + len(size_cleanup.repos) + len(size_cleanup.snapshots)\n", + "print(f\"Items to delete: {total_items}\")\n", + "\n", + "# Compare different strategies\n", + "strategies = [\"oldest_first\", \"largest_first\", \"least_used\"]\n", + "print(f\"\\nComparing cleanup strategies for {target_size}:\")\n", + "\n", + "for strategy in strategies:\n", + " try:\n", + " strategy_result = cache_manager.clean_cache_by_size(\n", + " target_size=target_size,\n", + " strategy=strategy,\n", + " dry_run=True\n", + " )\n", + " strategy_total = (len(strategy_result.blobs) + len(strategy_result.refs) + \n", + " len(strategy_result.repos) + len(strategy_result.snapshots))\n", + " print(f\" • {strategy:15}: {strategy_result.expected_freed_size_str:>8} \"\n", + " f\"({strategy_total} items)\")\n", + " except Exception as e:\n", + " print(f\" • {strategy:15}: Error - {e}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Cleaning Unused Revisions" + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Cleaning unused revisions (keep latest 2 per repo):\n", + "==================================================\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:__main__:Found 14 unused revisions. Will free 216.4M\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Revision cleanup strategy:\n", + "Expected space freed: 216.4M\n", + "Items to delete: 75\n", + "\n", + "Breakdown of cleanup:\n", + " • Blob files: 61\n", + " • Reference files: 0\n", + " • Repository directories: 0\n", + " • Snapshot directories: 14\n", + "\n", + "Per-repository revision analysis:\n", + "\n", + " • BrentLab/mahendrawada_2025:\n", + " Total revisions: 3\n", + " Would keep: 2\n", + " Would delete: 1\n", + " Keep: 8bea431b (modified: 1758155849.9076133)\n", + " Keep: d602c165 (modified: 1757439229.7949307)\n", + " Delete: 66e0cf1f (modified: 1756858618.1383443)\n", + "\n", + " • BrentLab/yeast_genome_resources:\n", + " Total revisions: 7\n", + " Would keep: 2\n", + " Would delete: 5\n", + " Keep: 42beb284 (modified: 1758155946.5549896)\n", + " Keep: 15fdb72f (modified: 1755819093.2306638)\n", + " Delete: 7441b9a8 (modified: 1755816785.6988702)\n", + "\n", + " • BrentLab/barkai_compendium:\n", + " Total revisions: 1\n", + " Would keep: 1\n", + " Would delete: 0\n", + " Keep: a987ef37 (modified: 1756926783.3167186)\n" + ] + } + ], + "source": [ + "# Clean unused revisions, keeping only the latest 2 per repository\n", + "print(\"Cleaning unused revisions (keep latest 2 per repo):\")\n", + "print(\"=\" * 50)\n", + "\n", + "revision_cleanup = cache_manager.clean_unused_revisions(\n", + " keep_latest=2,\n", + " dry_run=True\n", + ")\n", + "\n", + "print(f\"\\nRevision cleanup strategy:\")\n", + "print(f\"Expected space freed: {revision_cleanup.expected_freed_size_str}\")\n", + "\n", + "# Count total items to delete across all categories\n", + "total_items = len(revision_cleanup.blobs) + len(revision_cleanup.refs) + len(revision_cleanup.repos) + len(revision_cleanup.snapshots)\n", + "print(f\"Items to delete: {total_items}\")\n", + "\n", + "# Show breakdown\n", + "if total_items > 0:\n", + " print(f\"\\nBreakdown of cleanup:\")\n", + " print(f\" • Blob files: {len(revision_cleanup.blobs)}\")\n", + " print(f\" • Reference files: {len(revision_cleanup.refs)}\") \n", + " print(f\" • Repository directories: {len(revision_cleanup.repos)}\")\n", + " print(f\" • Snapshot directories: {len(revision_cleanup.snapshots)}\")\n", + "\n", + "# Show repository-specific breakdown\n", + "cache_info = scan_cache_dir()\n", + "if cache_info.repos:\n", + " print(\"\\nPer-repository revision analysis:\")\n", + " for repo in list(cache_info.repos)[:3]:\n", + " print(f\"\\n • {repo.repo_id}:\")\n", + " print(f\" Total revisions: {len(repo.revisions)}\")\n", + " print(f\" Would keep: {min(2, len(repo.revisions))}\")\n", + " print(f\" Would delete: {max(0, len(repo.revisions) - 2)}\")\n", + " \n", + " # Show revision details\n", + " sorted_revisions = sorted(repo.revisions, key=lambda r: r.last_modified, reverse=True)\n", + " for i, rev in enumerate(sorted_revisions[:2]):\n", + " print(f\" Keep: {rev.commit_hash[:8]} (modified: {rev.last_modified})\")\n", + " \n", + " for rev in sorted_revisions[2:3]: # Show one that would be deleted\n", + " print(f\" Delete: {rev.commit_hash[:8]} (modified: {rev.last_modified})\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Automated Cache Management" + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:__main__:Starting automated cache cleanup...\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Automated cache cleanup (comprehensive):\n", + "========================================\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:__main__:No old revisions found to delete\n", + "INFO:__main__:Found 0 old revisions. Will free 0.0\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n", + "INFO:__main__:Found 14 unused revisions. Will free 216.4M\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n", + "INFO:__main__:Automated cleanup complete. Total freed: 206.4MB\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Automated cleanup executed 2 strategies:\n", + " 1. Strategy freed: 0.0\n", + " 2. Strategy freed: 216.4M\n", + "\n", + "Total space that would be freed: 206.4MB\n", + "Cache size after cleanup: 4.1GB\n" + ] + } + ], + "source": [ + "# Automated cache cleanup with multiple strategies\n", + "print(\"Automated cache cleanup (comprehensive):\")\n", + "print(\"=\" * 40)\n", + "\n", + "auto_cleanup = cache_manager.auto_clean_cache(\n", + " max_age_days=30, # Remove anything older than 30 days\n", + " max_total_size=\"5GB\", # Target maximum cache size\n", + " keep_latest_per_repo=2, # Keep 2 latest revisions per repo\n", + " dry_run=True # Dry run for safety\n", + ")\n", + "\n", + "print(f\"\\nAutomated cleanup executed {len(auto_cleanup)} strategies:\")\n", + "\n", + "total_freed = 0\n", + "for i, strategy in enumerate(auto_cleanup, 1):\n", + " print(f\" {i}. Strategy freed: {strategy.expected_freed_size_str}\")\n", + " total_freed += strategy.expected_freed_size\n", + "\n", + "print(f\"\\nTotal space that would be freed: {cache_manager._format_bytes(total_freed)}\")\n", + "\n", + "# Calculate final cache size\n", + "current_cache = scan_cache_dir()\n", + "final_size = current_cache.size_on_disk - total_freed\n", + "print(f\"Cache size after cleanup: {cache_manager._format_bytes(max(0, final_size))}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 7. Best Practices and Performance Tips\n", + "\n", + "Here are some best practices for using HfCacheManager effectively:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Performance Best Practices" + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Performance Demonstration: Cache Management Benefits\n", + "=======================================================\n", + "\n", + "Demonstrating cache cleanup performance...\n", + "\n", + "1. Cache scanning performance:\n", + " Time to scan cache: 0.111 seconds\n", + " Repositories found: 9\n", + " Total cache size: 4.6G\n", + "\n", + "2. Cleanup strategy creation performance:\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:__main__:No old revisions found to delete\n", + "INFO:__main__:Found 0 old revisions. Will free 0.0\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Age cleanup strategy: 0.109 seconds\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:__main__:Selected 14 revisions for deletion. Will free 4.0G\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Size cleanup strategy: 0.105 seconds\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:__main__:Found 14 unused revisions. Will free 216.4M\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Revision cleanup strategy: 0.111 seconds\n", + "\n", + "Performance insights:\n", + "• Cache scanning is fast: 0.111s for 9 repos\n", + "• Cleanup strategy creation is efficient\n", + "• Dry runs allow safe preview of cleanup operations\n", + "• Multiple strategies can be compared quickly\n" + ] + } + ], + "source": [ + "import time\n", + "\n", + "print(\"Performance Demonstration: Cache Management Benefits\")\n", + "print(\"=\" * 55)\n", + "\n", + "print(\"\\nDemonstrating cache cleanup performance...\")\n", + "\n", + "# Show performance of cache scanning and cleanup strategy creation\n", + "print(\"\\n1. Cache scanning performance:\")\n", + "start_time = time.time()\n", + "cache_info = scan_cache_dir()\n", + "scan_time = time.time() - start_time\n", + "print(f\" Time to scan cache: {scan_time:.3f} seconds\")\n", + "print(f\" Repositories found: {len(cache_info.repos)}\")\n", + "print(f\" Total cache size: {cache_info.size_on_disk_str}\")\n", + "\n", + "# Show performance of cleanup strategy creation\n", + "print(\"\\n2. Cleanup strategy creation performance:\")\n", + "\n", + "start_time = time.time()\n", + "age_strategy = cache_manager.clean_cache_by_age(max_age_days=30, dry_run=True)\n", + "age_time = time.time() - start_time\n", + "print(f\" Age cleanup strategy: {age_time:.3f} seconds\")\n", + "\n", + "start_time = time.time()\n", + "size_strategy = cache_manager.clean_cache_by_size(target_size=\"1GB\", dry_run=True)\n", + "size_time = time.time() - start_time\n", + "print(f\" Size cleanup strategy: {size_time:.3f} seconds\")\n", + "\n", + "start_time = time.time()\n", + "revision_strategy = cache_manager.clean_unused_revisions(keep_latest=2, dry_run=True)\n", + "revision_time = time.time() - start_time\n", + "print(f\" Revision cleanup strategy: {revision_time:.3f} seconds\")\n", + "\n", + "print(f\"\\nPerformance insights:\")\n", + "print(f\"• Cache scanning is fast: {scan_time:.3f}s for {len(cache_info.repos)} repos\")\n", + "print(f\"• Cleanup strategy creation is efficient\")\n", + "print(f\"• Dry runs allow safe preview of cleanup operations\")\n", + "print(f\"• Multiple strategies can be compared quickly\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Memory and Storage Optimization" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Memory and Storage Optimization Tips:\n", + "========================================\n", + "\n", + "1. DuckDB Views vs Tables:\n", + " • HfCacheManager creates VIEWS by default (not tables)\n", + " • Views reference original parquet files without duplication\n", + " • This saves storage space while enabling fast SQL queries\n", + "\n", + "2. Metadata-First Workflow:\n", + " • Load metadata first to understand data structure\n", + " • Use metadata to filter and select specific data subsets\n", + " • Avoid loading entire datasets when only portions are needed\n", + "\n", + "3. Cache Management Strategy:\n", + " • Run automated cleanup regularly\n", + " • Keep cache size reasonable for your system\n", + " • Prioritize keeping recent and frequently-used datasets\n" + ] + } + ], + "source": [ + "print(\"Memory and Storage Optimization Tips:\")\n", + "print(\"=\" * 40)\n", + "\n", + "print(\"\\n1. DuckDB Views vs Tables:\")\n", + "print(\" • HfCacheManager creates VIEWS by default (not tables)\")\n", + "print(\" • Views reference original parquet files without duplication\")\n", + "print(\" • This saves storage space while enabling fast SQL queries\")\n", + "\n", + "print(\"\\n2. Metadata-First Workflow:\")\n", + "print(\" • Load metadata first to understand data structure\")\n", + "print(\" • Use metadata to filter and select specific data subsets\")\n", + "print(\" • Avoid loading entire datasets when only portions are needed\")\n", + "\n", + "print(\"\\n3. Cache Management Strategy:\")\n", + "print(\" • Run automated cleanup regularly\")\n", + "print(\" • Keep cache size reasonable for your system\")\n", + "print(\" • Prioritize keeping recent and frequently-used datasets\")\n", + "\n", + "# Demonstrate DuckDB view benefits\n", + "tables_info = conn.execute(\n", + " \"SELECT table_name, table_type FROM information_schema.tables WHERE table_name LIKE 'metadata_%'\"\n", + ").fetchall()\n", + "\n", + "if tables_info:\n", + " print(f\"\\nCurrent DuckDB objects ({len(tables_info)} total):\")\n", + " for table_name, table_type in tables_info:\n", + " print(f\" • {table_name}: {table_type}\")\n", + " \n", + " view_count = sum(1 for _, table_type in tables_info if table_type == 'VIEW')\n", + " print(f\"\\n {view_count} views created (space-efficient!)\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 8. Integration with Other Components\n", + "\n", + "The HfCacheManager works seamlessly with other components in the tfbpapi ecosystem." + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "HfCacheManager Integration Workflow:\n", + "========================================\n", + "\n", + "1. Cache Management Setup:\n", + " from tfbpapi.HfCacheManager import HfCacheManager\n", + " cache_mgr = HfCacheManager(repo_id, duckdb_conn)\n", + " # Inherits all DataCard functionality + cache management\n", + "\n", + "2. Proactive Cache Cleanup:\n", + " # Clean before large operations\n", + " cache_mgr.auto_clean_cache(max_total_size='5GB', dry_run=False)\n", + " # Or use specific strategies\n", + " cache_mgr.clean_cache_by_age(max_age_days=30)\n", + "\n", + "3. Data Loading with Cache Awareness:\n", + " # The 3-case strategy works automatically with HfQueryAPI\n", + " from tfbpapi import HfQueryAPI\n", + " query_api = HfQueryAPI(repo_id, duckdb_conn)\n", + " # Metadata loading uses cache manager's strategy\n", + " data_df = query_api.get_pandas('config_name')\n", + "\n", + "4. Embedded Metadata Extraction:\n", + " # Extract metadata fields after data loading\n", + " cache_mgr._extract_embedded_metadata_field(\n", + " 'data_table_name', 'metadata_field', 'metadata_table_name')\n", + "\n", + "5. Regular Cache Maintenance:\n", + " # Schedule regular cleanup\n", + " cache_mgr.clean_unused_revisions(keep_latest=2)\n", + " cache_mgr.clean_cache_by_size('10GB', strategy='oldest_first')\n", + "\n", + "Current Session State:\n", + "Repository: BrentLab/hackett_2020\n", + "DuckDB tables: 0\n", + "HF cache size: 4.6G\n", + "Cache repositories: 9\n" + ] + } + ], + "source": [ + "print(\"HfCacheManager Integration Workflow:\")\n", + "print(\"=\" * 40)\n", + "\n", + "print(\"\\n1. Cache Management Setup:\")\n", + "print(\" from tfbpapi.HfCacheManager import HfCacheManager\")\n", + "print(\" cache_mgr = HfCacheManager(repo_id, duckdb_conn)\")\n", + "print(\" # Inherits all DataCard functionality + cache management\")\n", + "\n", + "print(\"\\n2. Proactive Cache Cleanup:\")\n", + "print(\" # Clean before large operations\")\n", + "print(\" cache_mgr.auto_clean_cache(max_total_size='5GB', dry_run=False)\")\n", + "print(\" # Or use specific strategies\")\n", + "print(\" cache_mgr.clean_cache_by_age(max_age_days=30)\")\n", + "\n", + "print(\"\\n3. Data Loading with Cache Awareness:\")\n", + "print(\" # The 3-case strategy works automatically with HfQueryAPI\")\n", + "print(\" from tfbpapi import HfQueryAPI\")\n", + "print(\" query_api = HfQueryAPI(repo_id, duckdb_conn)\")\n", + "print(\" # Metadata loading uses cache manager's strategy\")\n", + "print(\" data_df = query_api.get_pandas('config_name')\")\n", + "\n", + "print(\"\\n4. Embedded Metadata Extraction:\")\n", + "print(\" # Extract metadata fields after data loading\")\n", + "print(\" cache_mgr._extract_embedded_metadata_field(\")\n", + "print(\" 'data_table_name', 'metadata_field', 'metadata_table_name')\")\n", + "\n", + "print(\"\\n5. Regular Cache Maintenance:\")\n", + "print(\" # Schedule regular cleanup\")\n", + "print(\" cache_mgr.clean_unused_revisions(keep_latest=2)\")\n", + "print(\" cache_mgr.clean_cache_by_size('10GB', strategy='oldest_first')\")\n", + "\n", + "# Show current state\n", + "print(f\"\\nCurrent Session State:\")\n", + "print(f\"Repository: {cache_manager.repo_id}\")\n", + "print(f\"DuckDB tables: {len(conn.execute('SELECT table_name FROM information_schema.tables').fetchall())}\")\n", + "\n", + "cache_info = scan_cache_dir()\n", + "print(f\"HF cache size: {cache_info.size_on_disk_str}\")\n", + "print(f\"Cache repositories: {len(cache_info.repos)}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 9. Troubleshooting and Error Handling\n", + "\n", + "The HfCacheManager includes comprehensive error handling and diagnostic capabilities." + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Cache Management Troubleshooting:\n", + "===================================\n", + "\n", + "1. Import and Setup Issues:\n", + " • Ensure correct import: from tfbpapi.HfCacheManager import HfCacheManager\n", + " • Verify DuckDB connection: conn = duckdb.connect(':memory:')\n", + " • Check repository access permissions\n", + "\n", + "2. Cache Space and Performance Issues:\n", + " Current cache size: 4.6G\n", + " • Use auto_clean_cache() for automated management\n", + " • Monitor cache growth with scan_cache_dir()\n", + " • Set appropriate size limits for your system\n", + "\n", + "3. Cache Cleanup Issues:\n", + " • Use dry_run=True first to preview changes\n", + " • Check disk permissions for cache directory\n", + " • Verify no active processes are using cached files\n", + "\n", + "4. DuckDB Integration Issues:\n", + " • Ensure DuckDB connection is active\n", + " • Check memory limits for in-memory databases\n", + " • Verify table names don't conflict\n", + "\n", + "Cache Health Check:\n", + "✓ DuckDB connection: DuckDB OK\n", + "✓ Cache access: 9 repositories found\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:__main__:No old revisions found to delete\n", + "INFO:__main__:Found 0 old revisions. Will free 0.0\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "✓ Cache cleanup methods: Working\n", + "\n", + "Current Status:\n", + "Repository: BrentLab/hackett_2020\n", + "Logger configured: True\n", + "Cache management ready: ✓\n" + ] + } + ], + "source": [ + "print(\"Cache Management Troubleshooting:\")\n", + "print(\"=\" * 35)\n", + "\n", + "print(\"\\n1. Import and Setup Issues:\")\n", + "print(\" • Ensure correct import: from tfbpapi.HfCacheManager import HfCacheManager\")\n", + "print(\" • Verify DuckDB connection: conn = duckdb.connect(':memory:')\")\n", + "print(\" • Check repository access permissions\")\n", + "\n", + "print(\"\\n2. Cache Space and Performance Issues:\")\n", + "try:\n", + " cache_info = scan_cache_dir()\n", + " print(f\" Current cache size: {cache_info.size_on_disk_str}\")\n", + " print(\" • Use auto_clean_cache() for automated management\")\n", + " print(\" • Monitor cache growth with scan_cache_dir()\")\n", + " print(\" • Set appropriate size limits for your system\")\n", + " \n", + " # Show if cache is getting large\n", + " total_gb = cache_info.size_on_disk / (1024**3)\n", + " if total_gb > 10:\n", + " print(f\" ⚠️ Large cache detected ({total_gb:.1f}GB) - consider cleanup\")\n", + " \n", + "except Exception as e:\n", + " print(f\" Cache scan error: {e}\")\n", + "\n", + "print(\"\\n3. Cache Cleanup Issues:\")\n", + "print(\" • Use dry_run=True first to preview changes\")\n", + "print(\" • Check disk permissions for cache directory\")\n", + "print(\" • Verify no active processes are using cached files\")\n", + "\n", + "print(\"\\n4. DuckDB Integration Issues:\")\n", + "print(\" • Ensure DuckDB connection is active\")\n", + "print(\" • Check memory limits for in-memory databases\")\n", + "print(\" • Verify table names don't conflict\")\n", + "\n", + "# Perform health checks\n", + "print(f\"\\nCache Health Check:\")\n", + "\n", + "# Test DuckDB\n", + "try:\n", + " test_result = conn.execute(\"SELECT 'DuckDB OK' as status\").fetchone()\n", + " print(f\"✓ DuckDB connection: {test_result[0]}\")\n", + "except Exception as e:\n", + " print(f\"✗ DuckDB connection: {e}\")\n", + "\n", + "# Test cache access\n", + "try:\n", + " cache_info = scan_cache_dir()\n", + " print(f\"✓ Cache access: {len(cache_info.repos)} repositories found\")\n", + "except Exception as e:\n", + " print(f\"✗ Cache access: {e}\")\n", + "\n", + "# Test cache manager methods\n", + "try:\n", + " test_cleanup = cache_manager.clean_cache_by_age(max_age_days=999, dry_run=True)\n", + " print(f\"✓ Cache cleanup methods: Working\")\n", + "except Exception as e:\n", + " print(f\"✗ Cache cleanup methods: {e}\")\n", + "\n", + "print(f\"\\nCurrent Status:\")\n", + "print(f\"Repository: {cache_manager.repo_id}\")\n", + "print(f\"Logger configured: {cache_manager.logger is not None}\")\n", + "print(f\"Cache management ready: ✓\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 10. Summary and Best Practices\n", + "\n", + "The HfCacheManager provides a sophisticated and efficient way to manage metadata and cache for HuggingFace genomics datasets." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Key HfCacheManager Features:\n", + "\n", + "1. **3-Case Metadata Strategy**: Intelligent caching that checks DuckDB → HF cache → download\n", + "2. **Automated Cache Cleanup**: Multiple strategies (age, size, revision-based) for cache management\n", + "3. **Cache Monitoring**: Real-time cache analysis and diagnostics\n", + "4. **DuckDB Integration**: Efficient metadata storage and querying without data duplication\n", + "5. **Embedded Metadata Extraction**: Extract metadata fields from data tables into queryable views\n", + "\n", + "### Cache Management Best Practices:\n", + "\n", + "1. **Monitor Cache Size**: Regular `scan_cache_dir()` checks prevent disk space issues\n", + "2. **Proactive Cleanup**: Use `auto_clean_cache()` before large data operations\n", + "3. **Strategic Retention**: Keep recent revisions, clean old ones with `clean_unused_revisions()`\n", + "4. **Size Management**: Set cache size limits appropriate for your system resources\n", + "5. **Integration with DataCard**: Use DataCard for exploration, HfCacheManager for cache management\n", + "\n", + "### Recommended Cache Management Workflow:\n", + "\n", + "```python\n", + "# 1. Initialize cache manager\n", + "from tfbpapi.HfCacheManager import HfCacheManager\n", + "import duckdb\n", + "\n", + "conn = duckdb.connect(':memory:')\n", + "cache_mgr = HfCacheManager(repo_id, conn)\n", + "\n", + "# 2. Check current cache status\n", + "from huggingface_hub import scan_cache_dir\n", + "cache_info = scan_cache_dir()\n", + "print(f\"Current cache: {cache_info.size_on_disk_str}\")\n", + "\n", + "# 3. Automated cleanup before heavy operations\n", + "cache_mgr.auto_clean_cache(\n", + " max_total_size=\"10GB\",\n", + " max_age_days=30,\n", + " keep_latest_per_repo=2,\n", + " dry_run=False\n", + ")\n", + "\n", + "# 4. Load data (uses 3-case strategy automatically)\n", + "from tfbpapi import HfQueryAPI\n", + "query_api = HfQueryAPI(repo_id, conn)\n", + "data = query_api.get_pandas('config_name')\n", + "\n", + "# 5. Extract embedded metadata if needed\n", + "cache_mgr._extract_embedded_metadata_field(\n", + " 'data_table', 'metadata_field', 'metadata_table'\n", + ")\n", + "\n", + "# 6. Regular maintenance\n", + "cache_mgr.clean_cache_by_age(max_age_days=30, dry_run=False)\n", + "```\n", + "\n", + "This approach ensures optimal cache performance while maintaining efficient access to genomics datasets." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "tfbpapi-py3.11", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/tutorials/database_interface.ipynb b/docs/tutorials/database_interface.ipynb deleted file mode 100644 index 79e41f7..0000000 --- a/docs/tutorials/database_interface.ipynb +++ /dev/null @@ -1,2266 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# The Database Interface Classes\n", - "\n", - "For each API endpoint exposed in the Django app, there is a corresponding class that\n", - "provide methods to execute CRUD operations asynchronously.\n", - "\n", - "There are two types of API endpoints -- those that contain only records data, and \n", - "those that store both records and pointers to files.\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Connecting to the Database\n", - "\n", - "The database currently runs on HTCF service partition. This is a single\n", - "node with 8 CPU and 30 GB that is meant for long running low resource jobs.\n", - "The components that need to run are a postgres database, a redis instance and the\n", - "django app. As long as these components are running on the service partition, \n", - "you can connect via an ssh tunnel with:\n", - "\n", - "```bash\n", - "ssh username@login.htcf.wustl.edu -N -L 8001:n240:8000\n", - "```\n", - "\n", - "where the `8001:n240:8000` takes the form of `local_port:cluster_node:app_port`. The\n", - "django app will always be served on port `8000`, and `n240` is the only service\n", - "partition node. You may choose a different local port.\n", - "\n", - "If you do this and cannot connect, let me know and I'll check the status of the jobs \n", - "on the cluster.\n", - "\n", - "### Database username and password\n", - "\n", - "Once you have a tunnel, you can access the database frontend at `127.0.0.1:8001`\n", - "(or a different local port, if you changed that number). If you haven't already\n", - "signed up, you'll need to click the 'sign up' button and follow\n", - "the instructions. The e-mail server is not hooked up at the moment, so when it says\n", - "\"see the e-mail\", send a slack message and let me know. I'll give you a link to\n", - "complete the sign up process. After that, you can just use the \"sign in\" button.\n", - "\n", - "For computational tasks, including generating rank response data, celery workers must\n", - "be launched on the HTCF general partition. There is currently a script that is meant to\n", - "monitor the redis queue and launch/kill these workers automatically, but this\n", - "functionality is new and largely untested. You can monitor the workers/tasks if you\n", - "create another tunnel with:\n", - "\n", - "```bash\n", - "ssh username@login.htcf.wustl.edu -N -L 8002:n240:5555\n", - "```\n", - "You'd access this dashboard at `127.0.0.1:5555`\n", - "\n", - "The login is currently:\n", - "\n", - "```raw\n", - "username: \"flower\"\n", - "password: \"daisy\"\n", - "```\n", - "(yes, really -- the security comes from the fact that you need to login with HTCF)\n", - "\n", - "### Configuring the Database Interface Classes\n", - "\n", - "The database classes expect the following environmental variables to be set. \n", - "\n", - "```raw\n", - "BASE_URL='http://127.0.0.1:8001'\n", - "TOKEN=''\n", - "BINDING_URL='http://127.0.0.1:8001/api/binding'\n", - "BINDINGMANUALQC_URL='http://127.0.0.1:8001/api/bindingmanualqc'\n", - "CALLINGCARDSBACKGROUND_URL='http://127.0.0.1:8001/api/callingcardsbackground'\n", - "DATASOURCE_URL='http://127.0.0.1:8001/api/datasource'\n", - "EXPRESSION_URL='http://127.0.0.1:8001/api/expression'\n", - "EXPRESSIONMANUALQC_URL='http://127.0.0.1:8001/api/expressionmanualqc'\n", - "FILEFORMAT_URL='http://127.0.0.1:8001/api/fileformat'\n", - "GENOMICFEATURE_URL='http://127.0.0.1:8001/api/genomicfeature'\n", - "PROMOTERSET_URL='http://127.0.0.1:8001/api/promoterset'\n", - "PROMOTERSETSIG_URL='http://127.0.0.1:8001/api/promotersetsig'\n", - "REGULATOR_URL='http://127.0.0.1:8001/api/regulator'\n", - "```\n", - "\n", - "This can be achieved in the package during development with a `.env` file at the\n", - "top most level of the package. The `.env` file is loaded in the package `__init__.py`.\n", - "\n", - "If you are importing `yeastdnnexplorer` into a different environment, then you'll \n", - "need to add the package `dotenv` and execute `load_dotenv(dotenv_path=env_path)`. If\n", - "the `.env` file is in the same `PWD` in which you execute that command, there is no\n", - "need to specify a path.\n", - "\n", - "### Token Authentication\n", - "\n", - "Once you have a username and password to the database, you can retrieve your token. \n", - "Make sure that you put this token, at least, in a `.env` file, and make sure that \n", - "`.env` file is in your `.gitignore`.\n", - "\n", - "Alternatively, you could retrieve and store in memory the token at the beginning of \n", - "each session -- this is more secure if you are not using a `.env` file. \n", - "\n", - "The `.env` file is already in the `yeastddnexplorer` `.gitignore`\n", - "\n", - "```bash\n", - "curl -X 'POST' \\\n", - " 'http://127.0.0.1:8001/auth-token/' \\\n", - " -H 'accept: application/json' \\\n", - " -H 'Content-Type: application/json' \\\n", - " -d '{\n", - " \"username\": \"username\",\n", - " \"password\": \"password\"\n", - "}'\n", - "```\n", - "\n", - "Or with python:\n", - "\n", - "```python\n", - "import requests\n", - "\n", - "url = \"http://127.0.0.1:8001/auth-token/\"\n", - "headers = {\n", - " \"accept\": \"application/json\",\n", - " \"Content-Type\": \"application/json\",\n", - "}\n", - "data = {\n", - " \"username\": \"username\",\n", - " \"password\": \"password\",\n", - "}\n", - "\n", - "response = requests.post(url, json=data, headers=headers)\n", - "print(response.text)\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Using the Interface Classes" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "from yeastdnnexplorer.interface import *\n", - "import matplotlib.pyplot as plt" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Records Only Endpoints\n", - "\n", - "The records only endpoints are:\n", - "\n", - "- BindingManualQC\n", - "\n", - "- DataSource\n", - "\n", - "- ExpressionManualQC\n", - "\n", - "- FileFormat\n", - "\n", - "- GenomicFeature\n", - "\n", - "- PromoterSetSig\n", - "\n", - "- Regulator\n", - "\n", - "When the `read()` method is called on the corresponding API classes, a dataframe will\n", - "be returned in the response.\n", - "\n", - "All of the `read()` methods, for both types of API endpoints, return the result of\n", - "a callable. By default, the callable returns a dictionary with two keys: `metadata` and\n", - "`data`. For response only tables, the `metadata` value will be the records from the\n", - "database as a pandas dataframe and the `data` will be `None.\n", - "\n", - "### Example -- RegulatorAPI" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "

\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
iduploadermodifierregulator_locus_tagregulator_symbolupload_datemodified_dateunder_developmentnotesgenomicfeature
01chasemchasemYAL051WOAF12024-07-012024-07-01T12:47:18.619129-05:00Falsenone24
12chasemchasemYBL103CRTG32024-07-012024-07-01T12:47:19.667722-05:00Falsenone140
23chasemchasemYBL066CSEF12024-07-012024-07-01T12:47:20.523161-05:00Falsenone186
34chasemchasemYBL054WTOD62024-07-012024-07-01T12:47:21.309606-05:00Falsenone199
45chasemchasemYBL052CSAS32024-07-012024-07-01T12:47:22.161007-05:00Falsenone201
.................................
18091810chasemchasemYOR262WGPN22024-07-012024-07-01T14:14:36.164403-05:00Falsenone6387
18101811chasemchasemYPR190CRPC822024-07-012024-07-01T14:14:38.921261-05:00Falsenone7070
18111812chasemchasemYPL228WCET12024-07-012024-07-01T14:15:51.518999-05:00Falsenone6603
18121813chasemchasemYKL049CCSE42024-07-012024-07-01T14:15:56.555122-05:00Falsenone4083
18131814chasemchasemYMR168CCEP32024-07-012024-07-01T14:22:14.060524-05:00Falsenone5258
\n", - "

1814 rows × 10 columns

\n", - "
" - ], - "text/plain": [ - " id uploader modifier regulator_locus_tag regulator_symbol upload_date \\\n", - "0 1 chasem chasem YAL051W OAF1 2024-07-01 \n", - "1 2 chasem chasem YBL103C RTG3 2024-07-01 \n", - "2 3 chasem chasem YBL066C SEF1 2024-07-01 \n", - "3 4 chasem chasem YBL054W TOD6 2024-07-01 \n", - "4 5 chasem chasem YBL052C SAS3 2024-07-01 \n", - "... ... ... ... ... ... ... \n", - "1809 1810 chasem chasem YOR262W GPN2 2024-07-01 \n", - "1810 1811 chasem chasem YPR190C RPC82 2024-07-01 \n", - "1811 1812 chasem chasem YPL228W CET1 2024-07-01 \n", - "1812 1813 chasem chasem YKL049C CSE4 2024-07-01 \n", - "1813 1814 chasem chasem YMR168C CEP3 2024-07-01 \n", - "\n", - " modified_date under_development notes \\\n", - "0 2024-07-01T12:47:18.619129-05:00 False none \n", - "1 2024-07-01T12:47:19.667722-05:00 False none \n", - "2 2024-07-01T12:47:20.523161-05:00 False none \n", - "3 2024-07-01T12:47:21.309606-05:00 False none \n", - "4 2024-07-01T12:47:22.161007-05:00 False none \n", - "... ... ... ... \n", - "1809 2024-07-01T14:14:36.164403-05:00 False none \n", - "1810 2024-07-01T14:14:38.921261-05:00 False none \n", - "1811 2024-07-01T14:15:51.518999-05:00 False none \n", - "1812 2024-07-01T14:15:56.555122-05:00 False none \n", - "1813 2024-07-01T14:22:14.060524-05:00 False none \n", - "\n", - " genomicfeature \n", - "0 24 \n", - "1 140 \n", - "2 186 \n", - "3 199 \n", - "4 201 \n", - "... ... \n", - "1809 6387 \n", - "1810 7070 \n", - "1811 6603 \n", - "1812 4083 \n", - "1813 5258 \n", - "\n", - "[1814 rows x 10 columns]" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "regulator = RegulatorAPI()\n", - "\n", - "result = await regulator.read()\n", - "result.get(\"metadata\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Record and File Endpoints\n", - "\n", - "The record and file endpoints are the following:\n", - "\n", - "- CallingCardsBackground\n", - "\n", - "- Expression\n", - "\n", - "- PromoterSet\n", - "\n", - "- PromoterSetSig\n", - "\n", - "- RankResponse *\n", - "\n", - "The default `read()` method is the same as the Records only Endpoint API classes.\n", - "However, there is an additional argument, `retrieve_files` which if set to `True`\n", - "will retrieve the file for which each record provides metadata. The return value of\n", - "`read()` is again a callable, and by default the `data` key will store a dictionary\n", - "where the keys correspond to the `id` column in the `metadata`.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
iduploadermodifiersingle_bindingcomposite_bindingfileformatbackgroundupload_datemodified_datefilepromotersourceregulator_symbolregulator_locus_tagconditionrank_recalldata_usablebackground_name
01chasemchasem1.0NaN1NaN2024-07-012024-07-01T13:51:48.611781-05:00https://yeastregulatorydb-htcf-data.s3.amazona...NaNharbison_chipOAF1YAL051WYPDunreviewedunreviewedNaN
12chasemchasem2.0NaN1NaN2024-07-012024-07-01T13:51:49.643452-05:00https://yeastregulatorydb-htcf-data.s3.amazona...NaNharbison_chipPDR3YBL005WYPDunreviewedunreviewedNaN
23chasemchasem3.0NaN1NaN2024-07-012024-07-01T13:51:50.744384-05:00https://yeastregulatorydb-htcf-data.s3.amazona...NaNharbison_chipHIR1YBL008WYPDunreviewedunreviewedNaN
34chasemchasem4.0NaN1NaN2024-07-012024-07-01T13:51:51.507918-05:00https://yeastregulatorydb-htcf-data.s3.amazona...NaNharbison_chipHAP3YBL021CYPDunreviewedunreviewedNaN
45chasemchasem5.0NaN1NaN2024-07-012024-07-01T13:51:52.277595-05:00https://yeastregulatorydb-htcf-data.s3.amazona...NaNharbison_chipTOD6YBL054WYPDunreviewedunreviewedNaN
.........................................................
22377011adminadminNaN145.051.02024-07-302024-07-30T16:39:36.457965-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4.0mitra_ccCBF1YJR060WNaNunreviewedpassad1
22387012adminadminNaN146.051.02024-07-302024-07-30T16:39:36.848168-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4.0mitra_ccGCN4YEL009CNaNunreviewedpassad1
22397013adminadminNaN147.051.02024-07-302024-07-30T16:39:37.234144-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4.0mitra_ccOAF1YAL051WNaNunreviewedpassad1
22407014adminadminNaN148.051.02024-07-302024-07-30T16:39:38.547155-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4.0mitra_ccYOX1YML027WNaNunreviewedpassad1
22417015adminadminNaN149.051.02024-07-302024-07-30T16:39:39.713590-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4.0mitra_ccLEU3YLR451WNaNunreviewedpassad1
\n", - "

2242 rows × 18 columns

\n", - "
" - ], - "text/plain": [ - " id uploader modifier single_binding composite_binding fileformat \\\n", - "0 1 chasem chasem 1.0 NaN 1 \n", - "1 2 chasem chasem 2.0 NaN 1 \n", - "2 3 chasem chasem 3.0 NaN 1 \n", - "3 4 chasem chasem 4.0 NaN 1 \n", - "4 5 chasem chasem 5.0 NaN 1 \n", - "... ... ... ... ... ... ... \n", - "2237 7011 admin admin NaN 145.0 5 \n", - "2238 7012 admin admin NaN 146.0 5 \n", - "2239 7013 admin admin NaN 147.0 5 \n", - "2240 7014 admin admin NaN 148.0 5 \n", - "2241 7015 admin admin NaN 149.0 5 \n", - "\n", - " background upload_date modified_date \\\n", - "0 NaN 2024-07-01 2024-07-01T13:51:48.611781-05:00 \n", - "1 NaN 2024-07-01 2024-07-01T13:51:49.643452-05:00 \n", - "2 NaN 2024-07-01 2024-07-01T13:51:50.744384-05:00 \n", - "3 NaN 2024-07-01 2024-07-01T13:51:51.507918-05:00 \n", - "4 NaN 2024-07-01 2024-07-01T13:51:52.277595-05:00 \n", - "... ... ... ... \n", - "2237 1.0 2024-07-30 2024-07-30T16:39:36.457965-05:00 \n", - "2238 1.0 2024-07-30 2024-07-30T16:39:36.848168-05:00 \n", - "2239 1.0 2024-07-30 2024-07-30T16:39:37.234144-05:00 \n", - "2240 1.0 2024-07-30 2024-07-30T16:39:38.547155-05:00 \n", - "2241 1.0 2024-07-30 2024-07-30T16:39:39.713590-05:00 \n", - "\n", - " file promoter \\\n", - "0 https://yeastregulatorydb-htcf-data.s3.amazona... NaN \n", - "1 https://yeastregulatorydb-htcf-data.s3.amazona... NaN \n", - "2 https://yeastregulatorydb-htcf-data.s3.amazona... NaN \n", - "3 https://yeastregulatorydb-htcf-data.s3.amazona... NaN \n", - "4 https://yeastregulatorydb-htcf-data.s3.amazona... NaN \n", - "... ... ... \n", - "2237 https://yeastregulatorydb-htcf-data.s3.amazona... 4.0 \n", - "2238 https://yeastregulatorydb-htcf-data.s3.amazona... 4.0 \n", - "2239 https://yeastregulatorydb-htcf-data.s3.amazona... 4.0 \n", - "2240 https://yeastregulatorydb-htcf-data.s3.amazona... 4.0 \n", - "2241 https://yeastregulatorydb-htcf-data.s3.amazona... 4.0 \n", - "\n", - " source regulator_symbol regulator_locus_tag condition \\\n", - "0 harbison_chip OAF1 YAL051W YPD \n", - "1 harbison_chip PDR3 YBL005W YPD \n", - "2 harbison_chip HIR1 YBL008W YPD \n", - "3 harbison_chip HAP3 YBL021C YPD \n", - "4 harbison_chip TOD6 YBL054W YPD \n", - "... ... ... ... ... \n", - "2237 mitra_cc CBF1 YJR060W NaN \n", - "2238 mitra_cc GCN4 YEL009C NaN \n", - "2239 mitra_cc OAF1 YAL051W NaN \n", - "2240 mitra_cc YOX1 YML027W NaN \n", - "2241 mitra_cc LEU3 YLR451W NaN \n", - "\n", - " rank_recall data_usable background_name \n", - "0 unreviewed unreviewed NaN \n", - "1 unreviewed unreviewed NaN \n", - "2 unreviewed unreviewed NaN \n", - "3 unreviewed unreviewed NaN \n", - "4 unreviewed unreviewed NaN \n", - "... ... ... ... \n", - "2237 unreviewed pass ad1 \n", - "2238 unreviewed pass ad1 \n", - "2239 unreviewed pass ad1 \n", - "2240 unreviewed pass ad1 \n", - "2241 unreviewed pass ad1 \n", - "\n", - "[2242 rows x 18 columns]" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# First, retrieve only the records -- you'll want to filter these results down before\n", - "# retrieving the files most likely\n", - "pss_api = PromoterSetSigAPI()\n", - "result = await pss_api.read()\n", - "result.get(\"metadata\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Filtering\n", - "\n", - "All API classes have a `params` attribute which stores the filtering parameters\n", - "which will be applied to the HTTP requests." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "pss_api.push_params({\"regulator_symbol\": \"GZF3\",\n", - " \"workflow\": \"nf_core_callingcards_1_0_0\",\n", - " \"data_usable\": \"pass\"})" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "ParamsDict({'regulator_symbol': 'GZF3', 'workflow': 'nf_core_callingcards_1_0_0', 'data_usable': 'pass'})" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "pss_api.params" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Retrieving files from a Records and Files Object\n", - "\n", - "To retrieve files from a Records and Files endpoint object, do the following:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
iduploadermodifiersingle_bindingcomposite_bindingfileformatbackgroundupload_datemodified_datefilepromotersourceregulator_symbolregulator_locus_tagconditionrank_recalldata_usablebackground_name
06577adminadmin1837NaN512024-07-012024-07-01T16:43:50.145871-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccGZF3YJL110Cunknownpasspassad1
16580adminadmin1841NaN512024-07-012024-07-01T16:43:50.968078-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccGZF3YJL110Cunknownpasspassad1
26642adminadmin1902NaN512024-07-012024-07-01T16:43:54.969507-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccGZF3YJL110Cunknownpasspassad1
36651adminadmin1911NaN512024-07-012024-07-01T16:43:55.326651-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccGZF3YJL110Cunknownpasspassad1
46717adminadmin1960NaN512024-07-012024-07-01T16:44:00.500038-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccGZF3YJL110Cunknownpasspassad1
\n", - "
" - ], - "text/plain": [ - " id uploader modifier single_binding composite_binding fileformat \\\n", - "0 6577 admin admin 1837 NaN 5 \n", - "1 6580 admin admin 1841 NaN 5 \n", - "2 6642 admin admin 1902 NaN 5 \n", - "3 6651 admin admin 1911 NaN 5 \n", - "4 6717 admin admin 1960 NaN 5 \n", - "\n", - " background upload_date modified_date \\\n", - "0 1 2024-07-01 2024-07-01T16:43:50.145871-05:00 \n", - "1 1 2024-07-01 2024-07-01T16:43:50.968078-05:00 \n", - "2 1 2024-07-01 2024-07-01T16:43:54.969507-05:00 \n", - "3 1 2024-07-01 2024-07-01T16:43:55.326651-05:00 \n", - "4 1 2024-07-01 2024-07-01T16:44:00.500038-05:00 \n", - "\n", - " file promoter source \\\n", - "0 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - "1 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - "2 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - "3 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - "4 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - "\n", - " regulator_symbol regulator_locus_tag condition rank_recall data_usable \\\n", - "0 GZF3 YJL110C unknown pass pass \n", - "1 GZF3 YJL110C unknown pass pass \n", - "2 GZF3 YJL110C unknown pass pass \n", - "3 GZF3 YJL110C unknown pass pass \n", - "4 GZF3 YJL110C unknown pass pass \n", - "\n", - " background_name \n", - "0 ad1 \n", - "1 ad1 \n", - "2 ad1 \n", - "3 ad1 \n", - "4 ad1 " - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# note that retrieve_files is set to True\n", - "result = await pss_api.read(retrieve_files = True)\n", - "\n", - "# the metadata slot is the same as before\n", - "result.get(\"metadata\")" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "# but now the data slot is a dictionary where the `id` are keys and the values\n", - "# are the files parsed into pandas dataframes\n", - "result.get(\"data\").get(\"6568\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Filtering on multiple items\n", - "\n", - "Some filters, and eventually all, will accept multiple arguments as a comma\n", - "separated string without spaces. For example:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "regulator_symbol: GZF3,RTG3, workflow: nf_core_callingcards_1_0_0, data_usable: pass\n" - ] - } - ], - "source": [ - "pss_api.push_params({\"regulator_symbol\": \"GZF3,RTG3\"})\n", - "\n", - "print(pss_api.params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Parameters can be removed one by one" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "regulator_symbol: GZF3,RTG3, workflow: nf_core_callingcards_1_0_0, data_usable: pass\n" - ] - } - ], - "source": [ - "print(pss_api.params)" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "regulator_symbol: GZF3,RTG3, workflow: nf_core_callingcards_1_0_0\n" - ] - } - ], - "source": [ - "pss_api.pop_params('data_usable')\n", - "\n", - "print(pss_api.params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "or cleared entirely" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], - "source": [ - "pss_api.pop_params(None)\n", - "\n", - "print(pss_api.params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Another example with Expression\n", - "\n", - "This is used to get an expression_id to use in the RankResponseAPI() below" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
iduploadermodifierregulator_idregulator_locus_tagregulator_symbolsource_nameassayupload_datemodified_date...replicatecontrolmechanismrestrictiontimefilenotesregulatorsourcepromotersetsig_processing
02516chasemchasem3578YJL110CGZF3mcisaac_oeoverexpression2024-07-012024-07-01T13:36:13.814201-05:00...1undefinedgevN15.0https://yeastregulatorydb-htcf-data.s3.amazona...strain_id:SMY156n ; date:201501011357False
12510chasemchasem3578YJL110CGZF3mcisaac_oeoverexpression2024-07-012024-07-01T13:36:08.810276-05:00...1undefinedgevP15.0https://yeastregulatorydb-htcf-data.s3.amazona...strain_id:SMY156 ; date:201501011357False
\n", - "

2 rows × 22 columns

\n", - "
" - ], - "text/plain": [ - " id uploader modifier regulator_id regulator_locus_tag regulator_symbol \\\n", - "0 2516 chasem chasem 3578 YJL110C GZF3 \n", - "1 2510 chasem chasem 3578 YJL110C GZF3 \n", - "\n", - " source_name assay upload_date modified_date \\\n", - "0 mcisaac_oe overexpression 2024-07-01 2024-07-01T13:36:13.814201-05:00 \n", - "1 mcisaac_oe overexpression 2024-07-01 2024-07-01T13:36:08.810276-05:00 \n", - "\n", - " ... replicate control mechanism restriction time \\\n", - "0 ... 1 undefined gev N 15.0 \n", - "1 ... 1 undefined gev P 15.0 \n", - "\n", - " file \\\n", - "0 https://yeastregulatorydb-htcf-data.s3.amazona... \n", - "1 https://yeastregulatorydb-htcf-data.s3.amazona... \n", - "\n", - " notes regulator source \\\n", - "0 strain_id:SMY156n ; date:20150101 135 7 \n", - "1 strain_id:SMY156 ; date:20150101 135 7 \n", - "\n", - " promotersetsig_processing \n", - "0 False \n", - "1 False \n", - "\n", - "[2 rows x 22 columns]" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "expression = ExpressionAPI()\n", - "\n", - "expression.push_params({\"regulator_symbol\": \"GZF3\",\n", - " \"lab\": \"mcisaac\",\n", - " \"time\": \"15\"})\n", - "\n", - "expression_res = await expression.read()\n", - "\n", - "expression_res.get(\"metadata\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Rank Response\n", - "\n", - "The rank response endpoint is slightly different than the others. It is implemented\n", - "asynchronously on the database side, and will run many tasks simultaneously. As such,\n", - "it uses `submit()` and `retrieve()` methods.\n", - "\n", - "Additionally, it is a POST request and all parameters are passed in a json list.\n", - "If you include, for a given dictionary, multiple items to promoetersetsig_ids and/or\n", - "expression_ids, those datasets will be aggregated prior to calculating the rank\n", - "response. The current parameters that may be passed for each are:\n", - "\n", - "- promotersetsig_ids: a list of promotersetsig_ids. If more than 1, then the data\n", - "will be aggregated prior to calcuating rank response\n", - "- expression_ids: a list of expression_ids. If more than 1, then the data\n", - "will be aggregated prior to calcuating rank response\n", - "- expression_effect_colname: name of the column to use for the rank response effect\n", - "- expression_effect_threshold: The threshold to use on abs(effect) to label responsive/\n", - "unresponsive genes\n", - "- expression_pvalue_threshold: the threshold to use below which to label responsive/\n", - "unresponsive genes\n", - "- rank_bin_size: the size of the bins by which rank response is summarized.\n", - "- rank_by_binding_effect: if this is \"true\", then rank by the binding effect first\n", - "rather than pvalue. This is used for harbison_chip and mcisaac_oe\n", - "- summarize_by_rank_bin: if this is set to false, the unsummarized rank response\n", - "(merged binding/response) is returned" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "rr_api = RankResponseAPI()\n", - "\n", - "data = [\n", - " {\n", - " \"promotersetsig_ids\": [\"5555\"],\n", - " \"expression_ids\": [\"2510\"],\n", - " \"rank_by_binding_effect\": \"true\",\n", - " }\n", - "]\n", - "\n", - "group_id = await rr_api.submit(post_dict=data)\n", - "\n", - "result = await rr_api.retrieve(group_id)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
regulator_symbolpromotersetsig_idsexpression_idsn_responsivetotal_expression_genesid
0GZF3555525108106175.0a46a15db-8318-476e-80aa-af5588909eef
\n", - "
" - ], - "text/plain": [ - " regulator_symbol promotersetsig_ids expression_ids n_responsive \\\n", - "0 GZF3 5555 2510 810 \n", - "\n", - " total_expression_genes id \n", - "0 6175.0 a46a15db-8318-476e-80aa-af5588909eef " - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "result.get(\"metadata\")" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
rank_binn_responsive_in_rankrandomn_successesresponse_ratiopvalueci_lowerci_upper
0520.13117420.4000000.1312110.0527450.853367
11030.13117450.5000000.0055100.1870860.812914
21540.13117490.6000000.0000270.3228700.836636
32000.13117490.4500000.0004900.2305780.684722
42520.131174110.4400000.0001490.2440240.650718
53010.131174120.4000000.0002240.2265580.593965
63510.131174130.3714290.0002960.2147320.550769
74000.131174130.3250000.0012780.1857290.491295
84510.131174140.3111110.0013630.1816590.466491
95000.131174140.2800000.0049460.1623110.424905
105510.131174150.2727270.0043780.1613800.409619
116000.131174150.2500000.0115260.1471860.378596
\n", - "
" - ], - "text/plain": [ - " rank_bin n_responsive_in_rank random n_successes response_ratio \\\n", - "0 5 2 0.131174 2 0.400000 \n", - "1 10 3 0.131174 5 0.500000 \n", - "2 15 4 0.131174 9 0.600000 \n", - "3 20 0 0.131174 9 0.450000 \n", - "4 25 2 0.131174 11 0.440000 \n", - "5 30 1 0.131174 12 0.400000 \n", - "6 35 1 0.131174 13 0.371429 \n", - "7 40 0 0.131174 13 0.325000 \n", - "8 45 1 0.131174 14 0.311111 \n", - "9 50 0 0.131174 14 0.280000 \n", - "10 55 1 0.131174 15 0.272727 \n", - "11 60 0 0.131174 15 0.250000 \n", - "\n", - " pvalue ci_lower ci_upper \n", - "0 0.131211 0.052745 0.853367 \n", - "1 0.005510 0.187086 0.812914 \n", - "2 0.000027 0.322870 0.836636 \n", - "3 0.000490 0.230578 0.684722 \n", - "4 0.000149 0.244024 0.650718 \n", - "5 0.000224 0.226558 0.593965 \n", - "6 0.000296 0.214732 0.550769 \n", - "7 0.001278 0.185729 0.491295 \n", - "8 0.001363 0.181659 0.466491 \n", - "9 0.004946 0.162311 0.424905 \n", - "10 0.004378 0.161380 0.409619 \n", - "11 0.011526 0.147186 0.378596 " - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "result.get(\"data\").get(result.get(\"metadata\").id[0])" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "df = result.get(\"data\").get(result.get(\"metadata\").id[0]).iloc[1:50,:]\n", - "\n", - "# Plot\n", - "plt.figure(figsize=(10, 6))\n", - "plt.plot(df['rank_bin'], df['response_ratio'], marker='o')\n", - "plt.title('GZF3 - promotersetsig_id 6577. McIsaac 15 2510')\n", - "plt.xlabel('Rank Bin')\n", - "plt.ylabel('Response Ratio')\n", - "plt.title('Response Ratio vs Rank Bin')\n", - "plt.grid(True)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# CallingCards Aggregated Data\n", - "\n", - "For regulators which have data generated in the Brentlab and processed through the\n", - "nf-core/callingcards:1.0.0 workflow, if that data has been manually (or eventually \n", - "automatically) QC reviewed,\n", - "*and if there are at least 2 samples which are labeled as data_usable*, then there will\n", - "exist a BindingConcatenated record to which both a BindingManualQC record *and* a\n", - "PromoterSetSig record are foreign keyed.\n", - "\n", - "To view the set of samples for which there is aggregated data, you may do the following:" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "pss_api.pop_params(None)\n", - "\n", - "pss_api.push_params({\"source_name\": \"brent_nf_cc\", \"aggregated\": \"true\"})\n", - "\n", - "callingcards_aggregated_meta_res = await pss_api.read()\n", - "\n", - "callingcards_aggregated_meta_df = callingcards_aggregated_meta_res.get(\"metadata\")" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
iduploadermodifiersingle_bindingcomposite_bindingfileformatbackgroundupload_datemodified_datefilepromotersourceregulator_symbolregulator_locus_tagrank_recalldata_usablebackground_name
06867adminadminNaN6512024-07-062024-07-06T11:08:33.125644-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccWTM1YOR230Wpasspassad1
16868adminadminNaN3512024-07-062024-07-06T11:08:33.125640-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccMIG2YGL209Wpasspassad1
26869adminadminNaN1512024-07-062024-07-06T11:08:33.119818-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccCAT8YMR280Cpasspassad1
36870adminadminNaN5512024-07-062024-07-06T11:08:33.125605-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccPDR1YGL013Cpasspassad1
46871adminadminNaN4512024-07-062024-07-06T11:08:33.129564-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccPHO4YFR034Cpasspassad1
......................................................
666933adminadminNaN67512024-07-062024-07-06T11:08:45.567814-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccABF2YMR072Wfailpassad1
676934adminadminNaN68512024-07-062024-07-06T11:08:45.921894-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccUSV1YPL230Wpasspassad1
686935adminadminNaN69512024-07-062024-07-06T11:08:46.166036-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccMGA1YGR249Wpasspassad1
696936adminadminNaN70512024-07-062024-07-06T11:08:46.246482-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccCIN5YOR028Cpasspassad1
706937adminadminNaN71512024-07-062024-07-06T11:08:47.987665-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccROX1YPR065Wpasspassad1
\n", - "

71 rows × 17 columns

\n", - "
" - ], - "text/plain": [ - " id uploader modifier single_binding composite_binding fileformat \\\n", - "0 6867 admin admin NaN 6 5 \n", - "1 6868 admin admin NaN 3 5 \n", - "2 6869 admin admin NaN 1 5 \n", - "3 6870 admin admin NaN 5 5 \n", - "4 6871 admin admin NaN 4 5 \n", - ".. ... ... ... ... ... ... \n", - "66 6933 admin admin NaN 67 5 \n", - "67 6934 admin admin NaN 68 5 \n", - "68 6935 admin admin NaN 69 5 \n", - "69 6936 admin admin NaN 70 5 \n", - "70 6937 admin admin NaN 71 5 \n", - "\n", - " background upload_date modified_date \\\n", - "0 1 2024-07-06 2024-07-06T11:08:33.125644-05:00 \n", - "1 1 2024-07-06 2024-07-06T11:08:33.125640-05:00 \n", - "2 1 2024-07-06 2024-07-06T11:08:33.119818-05:00 \n", - "3 1 2024-07-06 2024-07-06T11:08:33.125605-05:00 \n", - "4 1 2024-07-06 2024-07-06T11:08:33.129564-05:00 \n", - ".. ... ... ... \n", - "66 1 2024-07-06 2024-07-06T11:08:45.567814-05:00 \n", - "67 1 2024-07-06 2024-07-06T11:08:45.921894-05:00 \n", - "68 1 2024-07-06 2024-07-06T11:08:46.166036-05:00 \n", - "69 1 2024-07-06 2024-07-06T11:08:46.246482-05:00 \n", - "70 1 2024-07-06 2024-07-06T11:08:47.987665-05:00 \n", - "\n", - " file promoter source \\\n", - "0 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - "1 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - "2 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - "3 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - "4 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - ".. ... ... ... \n", - "66 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - "67 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - "68 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - "69 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - "70 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - "\n", - " regulator_symbol regulator_locus_tag rank_recall data_usable \\\n", - "0 WTM1 YOR230W pass pass \n", - "1 MIG2 YGL209W pass pass \n", - "2 CAT8 YMR280C pass pass \n", - "3 PDR1 YGL013C pass pass \n", - "4 PHO4 YFR034C pass pass \n", - ".. ... ... ... ... \n", - "66 ABF2 YMR072W fail pass \n", - "67 USV1 YPL230W pass pass \n", - "68 MGA1 YGR249W pass pass \n", - "69 CIN5 YOR028C pass pass \n", - "70 ROX1 YPR065W pass pass \n", - "\n", - " background_name \n", - "0 ad1 \n", - "1 ad1 \n", - "2 ad1 \n", - "3 ad1 \n", - "4 ad1 \n", - ".. ... \n", - "66 ad1 \n", - "67 ad1 \n", - "68 ad1 \n", - "69 ad1 \n", - "70 ad1 \n", - "\n", - "[71 rows x 17 columns]" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "callingcards_aggregated_meta_df" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
iduploadermodifiersingle_bindingcomposite_bindingfileformatbackgroundupload_datemodified_datefilepromotersourceregulator_symbolregulator_locus_tagrank_recalldata_usablebackground_name
06873adminadminNaN7512024-07-062024-07-06T11:08:33.117009-05:00https://yeastregulatorydb-htcf-data.s3.amazona...4brent_nf_ccGZF3YJL110Cpasspassad1
\n", - "
" - ], - "text/plain": [ - " id uploader modifier single_binding composite_binding fileformat \\\n", - "0 6873 admin admin NaN 7 5 \n", - "\n", - " background upload_date modified_date \\\n", - "0 1 2024-07-06 2024-07-06T11:08:33.117009-05:00 \n", - "\n", - " file promoter source \\\n", - "0 https://yeastregulatorydb-htcf-data.s3.amazona... 4 brent_nf_cc \n", - "\n", - " regulator_symbol regulator_locus_tag rank_recall data_usable background_name \n", - "0 GZF3 YJL110C pass pass ad1 " - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "pss_api.push_params({\"regulator_symbol\": \"GZF3\"})\n", - "\n", - "callingcards_res = await pss_api.read()\n", - "\n", - "gzf3_callingcards_res = callingcards_res.get(\"metadata\")\n", - "\n", - "gzf3_callingcards_res\n", - "\n", - "#gzf3_callingcards_res[~gzf3_callingcards_res.composite_binding_id.isna()]" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "data = [\n", - " {\n", - " \"promotersetsig_ids\": [\"6873\"],\n", - " \"expression_ids\": [\"2510\"],\n", - " }\n", - "]\n", - "\n", - "group_id = await rr_api.submit(post_dict=data)\n", - "\n", - "agg_result = await rr_api.retrieve(group_id)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA04AAAIjCAYAAAA0vUuxAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAB/eUlEQVR4nO3deVxU9f7H8ffMsIzIJiKCS4pbhrikhlGZlmuZZd3Ka1lm2820utpqG9mv275velutvJXV9Wq2mHtloqhoibuIO4iCAi5sM+f3hzGJLDPgwAzwej4ePHLO+Z5zPjN8Q95+v+d7TIZhGAIAAAAAVMjs6QIAAAAAwNsRnAAAAADACYITAAAAADhBcAIAAAAAJwhOAAAAAOAEwQkAAAAAnCA4AQAAAIATBCcAAAAAcILgBAAAAABOEJwAAA3S0qVLZTKZtHTpUk+XUifccsstCgwMrPHr7Ny5UyaTSdOnT6/xawFAVRCcAKAapk+fLpPJ5Pjy8fFRy5Ytdcstt2jfvn2eLq9OKvmFueTLbDYrLCxMl112mRITE6t93nfffbde/BJel/vcU089VeZ7GxUVpSuuuEIrVqzwdHkA4BIfTxcAAHXZ008/rejoaOXn52vFihWaPn26li1bppSUFFmtVk+XVyeNGjVKl19+uWw2m7Zu3ap3331Xl1xyiVatWqWuXbtW+XzvvvuuwsPDdcstt5TafvHFF+vEiRPy8/NzU+W1oy73ualTpyowMFB2u1179uzR+++/r4svvlhJSUnq0aOHJKlNmzY6ceKEfH19PVssAJyG4AQAZ+Cyyy5T7969JUm33367wsPD9cILL+jbb7/V9ddf7+Hq6qaePXtq9OjRjtd9+/bVZZddpqlTp+rdd99123XMZrPXB43y1OU+d+211yo8PNzxesSIEYqNjdXXX3/tCE4mk6lOfl8A1H9M1QMAN+rbt68kKTU1tdT2zZs369prr1VYWJisVqt69+6tb7/9tlSboqIiTZkyRR07dpTValXTpk110UUXacGCBY42JfeZ7NixQ0OGDFHjxo3VokULPf300zIMo9T5jh07pvvvv1+tW7eWv7+/zj77bL388stl2plMJk2YMEGzZ89WbGys/P391aVLF82bN69Uu7y8PP3zn/9U27Zt5e/vr4iICA0aNEjJycml2q1cuVJDhw5VSEiIAgIC1K9fP/3222/V+0BV8Wf68ccf69JLL1VERIT8/f0VExOjqVOnlmrTtm1bbdiwQT///LNjmlj//v0lVXyP09dff61evXqpUaNGCg8P1+jRo51OhVu9erVMJpM++eSTMvt++uknmUwmfffdd5Jc/xxdVd7nU1hYqCeffFK9evVSSEiIGjdurL59+2rJkiWlji2ZHvnyyy/rvffeU/v27eXv76/zzjtPq1atcnrtdevWqVmzZurfv7+OHj1a5dojIyMlST4+f/07bnn3OJX0+3379mnEiBEKDAxUs2bN9MADD8hms1X5ugBQHYw4AYAb7dy5U5LUpEkTx7YNGzbowgsvVMuWLfXII4+ocePG+uqrrzRixAj997//1dVXXy3p5H0gzz33nG6//XbFxcUpNzdXq1evVnJysgYNGuQ4n81m09ChQ3X++efrxRdf1Lx585SQkKDi4mI9/fTTkiTDMHTllVdqyZIluu2229SjRw/99NNPevDBB7Vv3z699tprpepetmyZZs2apbvvvltBQUF688039be//U27d+9W06ZNJUl33XWXvvnmG02YMEExMTHKysrSsmXLtGnTJvXs2VOStHjxYl122WXq1auXEhISZDabHQHn119/VVxcnFs+U+nktK8uXbroyiuvlI+Pj+bOnau7775bdrtd48ePlyS9/vrruueeexQYGKjHHntMktS8efMKrzV9+nSNHTtW5513np577jkdOHBAb7zxhn777TetXbtWoaGh5R7Xu3dvtWvXTl999ZXGjBlTat/MmTPVpEkTDRkyxOXP8Uw/n9zcXH3wwQcaNWqU7rjjDuXl5enDDz/UkCFDSk2LK/H5558rLy9P//jHP2QymfTiiy/qmmuu0Y4dOyqcMrdq1SoNGTJEvXv31pw5c9SoUSOntWZnZ0uS7Ha79u3bp//7v/+T1Wp1aaTMZrNpyJAh6tOnj15++WUtXLhQr7zyitq3b69x48Y5PR4AzpgBAKiyjz/+2JBkLFy40Dh48KCxZ88e45tvvjGaNWtm+Pv7G3v27HG0HTBggNG1a1cjPz/fsc1utxsXXHCB0bFjR8e27t27G8OGDav0umPGjDEkGffcc0+pcw0bNszw8/MzDh48aBiGYcyePduQZDzzzDOljr/22msNk8lkbN++3bFNkuHn51dq2++//25IMt566y3HtpCQEGP8+PEV1ma3242OHTsaQ4YMMex2u2P78ePHjejoaGPQoEGVvre0tDRDkjFlyhTj4MGDRkZGhvHrr78a5513niHJ+Prrr0u1P378eJlzDBkyxGjXrl2pbV26dDH69etXpu2SJUsMScaSJUsMwzCMwsJCIyIiwoiNjTVOnDjhaPfdd98Zkownn3yy0vonT55s+Pr6GtnZ2Y5tBQUFRmhoqHHrrbc6tjn7HCtSlT5XXFxsFBQUlDr+8OHDRvPmzUvVUvKZN23atFTdc+bMMSQZc+fOdWwbM2aM0bhxY8MwDGPZsmVGcHCwMWzYsFL9uiIJCQmGpDJfoaGhxrx580q1Lanp448/LnVtScbTTz9dqu25555r9OrVy+n1AcAdmKoHAGdg4MCBatasmVq3bq1rr71WjRs31rfffqtWrVpJOvkv7IsXL9b111+vvLw8HTp0SIcOHVJWVpaGDBmibdu2OaaBhYaGasOGDdq2bZvT606YMMHx55KpdoWFhVq4cKEk6YcffpDFYtG9995b6rj7779fhmHoxx9/LPM+2rdv73jdrVs3BQcHa8eOHY5toaGhWrlypfbv319uTevWrdO2bdt0ww03KCsry/Fejx07pgEDBuiXX36R3W53+t4SEhLUrFkzRUZGqm/fvtq0aZNeeeUVXXvttaXanTrCkZOTo0OHDqlfv37asWOHcnJynF7ndKtXr1ZmZqbuvvvuUvfYDBs2TJ07d9b3339f6fEjR45UUVGRZs2a5dg2f/58HTlyRCNHjnRsc/Y5OuOsz0mSxWJxLHpht9uVnZ2t4uJi9e7du9wpgSNHjiw1YlUy/e/U73+JJUuWaMiQIRowYIBmzZolf39/l2v/73//qwULFmj+/Pn6+OOP1alTJ/3tb3/T8uXLXTr+rrvuKvW6b9++5dYIADWBqXoAcAbeeecdderUSTk5Ofroo4/0yy+/lPpFcvv27TIMQ0888YSeeOKJcs+RmZmpli1b6umnn9ZVV12lTp06KTY2VkOHDtVNN92kbt26lWpvNpvVrl27Uts6deok6a9pW7t27VKLFi0UFBRUqt0555zj2H+qs846q0xdTZo00eHDhx2vX3zxRY0ZM0atW7dWr169dPnll+vmm2921FIS+E6fqnaqnJycMlPuTnfnnXfquuuuU35+vhYvXqw333yz3PtYfvvtNyUkJCgxMVHHjx8vc52QkJBKr3O6ks/k7LPPLrOvc+fOWrZsWaXHd+/eXZ07d9bMmTN12223STo5TS88PFyXXnqpo52zz9EZZ32uxCeffKJXXnlFmzdvVlFRkWN7dHR0mbanf/9Lvkenfv8lKT8/X8OGDVOvXr301Vdflbo3yRUXX3xxqcUhrr32WnXs2FH33HOP1qxZU+mxVqtVzZo1K1Pn6TUCQE0hOAHAGYiLi3OscDZixAhddNFFuuGGG7RlyxbHssuS9MADDzjucTldhw4dJJ38pTI1NVVz5szR/Pnz9cEHH+i1117TtGnTdPvtt9fo+7BYLOVuN05ZSOL6669X37599b///U/z58/XSy+9pBdeeEGzZs3SZZdd5nivL730Upl7aEq48gDVjh07auDAgZKkK664QhaLRY888oguueQSx2edmpqqAQMGqHPnznr11VfVunVr+fn56YcfftBrr73m0shWTRg5cqT+9a9/6dChQwoKCtK3336rUaNGlQoYzj5HZ5z1OUmaMWOGbrnlFo0YMUIPPvigIiIiZLFY9Nxzz5VZZENy7fsvSf7+/rr88ss1Z84czZs3T1dccYXLn015AgMD1adPH82ZM0fHjh1T48aNK2xbUY0AUFuYqgcAblLyi+n+/fv19ttvS5JjFMHX11cDBw4s9+vUUaGwsDCNHTtWX3zxhfbs2aNu3brpqaeeKnUdu91eZnrS1q1bJZ1cRU46+Syc/fv3Ky8vr1S7zZs3O/ZXR1RUlO6++27Nnj1baWlpatq0qf71r39JkmOqX3BwcIXvtTrP5nnssccUFBSkxx9/3LFt7ty5Kigo0Lfffqt//OMfuvzyyzVw4MByFygwmUwuXafkM9myZUuZfVu2bHHpMxs5cqSKi4v13//+Vz/++KNyc3P197//vUy7yj7Hqiivz0nSN998o3bt2mnWrFm66aabNGTIEA0cOFD5+flVvsapTCaT/vOf/2jAgAG67rrryqxIWB3FxcWSVK1V+QCgNhGcAMCN+vfvr7i4OL3++uvKz89XRESE+vfvr3//+99KT08v0/7gwYOOP2dlZZXaFxgYqA4dOqigoKDMcaf+kmwYht5++235+vpqwIABkuR4gOyp7STptddek8lkcmlk41Q2m63MfUMRERFq0aKFo75evXqpffv2evnll8v9JfjU91oVoaGh+sc//qGffvpJ69atk/TX6MOpIyI5OTn6+OOPyxzfuHFjHTlyxOl1evfurYiICE2bNq3UZ/7jjz9q06ZNGjZsmNNznHPOOeratatmzpypmTNnKioqShdffLFjvyufY1Wd3uek8j+flStXKjExsVrXOJWfn59mzZql8847T8OHD1dSUlK1z5Wdna3ly5crMjJSERERZ1wbANQkpuoBgJs9+OCDuu666zR9+nTdddddeuedd3TRRRepa9euuuOOO9SuXTsdOHBAiYmJ2rt3r37//XdJUkxMjPr3769evXopLCxMq1evdixbfSqr1ap58+ZpzJgx6tOnj3788Ud9//33evTRRx33gAwfPlyXXHKJHnvsMe3cuVPdu3fX/PnzNWfOHP3zn/8stRCEK/Ly8tSqVStde+216t69uwIDA7Vw4UKtWrVKr7zyiqST91598MEHuuyyy9SlSxeNHTtWLVu21L59+7RkyRIFBwdr7ty51fpM77vvPr3++ut6/vnn9eWXX2rw4MHy8/PT8OHD9Y9//ENHjx7V+++/r4iIiDIBtVevXpo6daqeeeYZdejQQREREaXuOSrh6+urF154QWPHjlW/fv00atQox3Lkbdu21cSJE12qdeTIkXryySdltVp12223yWz+698oXfkcq+P0PnfFFVdo1qxZuvrqqzVs2DClpaVp2rRpiomJccvITqNGjfTdd9/p0ksv1WWXXaaff/5ZsbGxTo/75ptvFBgYKMMwtH//fn344Yc6fPiwpk2b5vLIIAB4jOcW9AOAuqtkaehVq1aV2Wez2Yz27dsb7du3N4qLiw3DMIzU1FTj5ptvNiIjIw1fX1+jZcuWxhVXXGF88803juOeeeYZIy4uzggNDTUaNWpkdO7c2fjXv/5lFBYWOtqULAmdmppqDB482AgICDCaN29uJCQkGDabrVQdeXl5xsSJE40WLVoYvr6+RseOHY2XXnqp1FLhhnFyOfLylsdu06aNMWbMGMMwTi6r/eCDDxrdu3c3goKCjMaNGxvdu3c33n333TLHrV271rjmmmuMpk2bGv7+/kabNm2M66+/3li0aFGln2nJMtQvvfRSuftvueUWw2KxOJZN//bbb41u3boZVqvVaNu2rfHCCy8YH330kSHJSEtLcxyXkZFhDBs2zAgKCjIkOZYmP3058hIzZ840zj33XMPf398ICwszbrzxRmPv3r2V1n6qbdu2OZbbXrZsWal9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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "agg_df = agg_result.get(\"data\").get(agg_result.get(\"metadata\").id[0]).iloc[1:50,:]\n", - "\n", - "# Plot\n", - "plt.figure(figsize=(10, 6))\n", - "plt.plot(agg_df['rank_bin'], agg_df['response_ratio'], marker='o')\n", - "plt.title('Aggregated GZF3. McIsaac 15 2510')\n", - "plt.xlabel('Rank Bin')\n", - "plt.ylabel('Response Ratio')\n", - "plt.title('Response Ratio vs Rank Bin')\n", - "plt.grid(True)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Caveats\n", - "\n", - "1. I have written the scripts to automatically check the redis queue for work and to \n", - " both launch celery worker nodes, and kill them when they are finished. But, though\n", - " they work if I run them manually, they have not worked when scheduled through a\n", - " cronjob. I'll work with Brian and Eric next week to figure out why.\n", - "\n", - "1. I haven't tested each of the endpoint APIs individually. Help is welcome." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.9" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/tutorials/datacard_tutorial.ipynb b/docs/tutorials/datacard_tutorial.ipynb index 071397b..47df9b6 100644 --- a/docs/tutorials/datacard_tutorial.ipynb +++ b/docs/tutorials/datacard_tutorial.ipynb @@ -27,14 +27,22 @@ }, { "cell_type": "code", - "execution_count": 132, + "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Repository: BrentLab/rossi_2021\n" + "Repository: BrentLab/mahendrawada_2025\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chase/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" ] } ], @@ -43,7 +51,7 @@ "\n", "# Initialize DataCard with the Rossi 2021 dataset\n", "# try this with mahendrawada_2025, which is more complex\n", - "card = DataCard('BrentLab/rossi_2021')\n", + "card = DataCard('BrentLab/mahendrawada_2025')\n", "\n", "print(f\"Repository: {card.repo_id}\")" ] @@ -59,7 +67,7 @@ }, { "cell_type": "code", - "execution_count": 133, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -68,16 +76,16 @@ "text": [ "Repository Information:\n", "========================================\n", - "repo_id : BrentLab/rossi_2021\n", - "pretty_name : Rossi ChIP-exo 2021\n", + "repo_id : BrentLab/mahendrawada_2025\n", + "pretty_name : Mahendrawada 2025 ChEC-seq and Nascent RNA-seq data\n", "license : mit\n", - "tags : ['transcription-factor', 'binding', 'chipexo', 'genomics', 'biology']\n", + "tags : ['biology', 'genomics', 'yeast', 'transcription-factors', 'gene-expression', 'binding', 'chec', 'perturbation', 'rnaseq', 'nascent rnaseq']\n", "language : ['en']\n", - "size_categories : None\n", - "num_configs : 2\n", - "dataset_types : ['metadata', 'genome_map']\n", - "total_files : 1237\n", - "last_modified : 2025-09-18T00:59:17+00:00\n", + "size_categories : ['100K \u001b[39m\u001b[32m2\u001b[39m metadata_info = \u001b[43mcard\u001b[49m\u001b[43m.\u001b[49m\u001b[43mexplore_config\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43mmetadata\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[32m 4\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33mMetadata Configuration Details:\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 5\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33m=\u001b[39m\u001b[33m\"\u001b[39m * \u001b[32m40\u001b[39m)\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/code/tfbp/tfbpapi/tfbpapi/datainfo/datacard.py:291\u001b[39m, in \u001b[36mDataCard.explore_config\u001b[39m\u001b[34m(self, config_name)\u001b[39m\n\u001b[32m 289\u001b[39m config = \u001b[38;5;28mself\u001b[39m.get_config(config_name)\n\u001b[32m 290\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m config:\n\u001b[32m--> \u001b[39m\u001b[32m291\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m DataCardError(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mConfiguration \u001b[39m\u001b[33m'\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mconfig_name\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m'\u001b[39m\u001b[33m not found\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 293\u001b[39m info: \u001b[38;5;28mdict\u001b[39m[\u001b[38;5;28mstr\u001b[39m, Any] = {\n\u001b[32m 294\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mconfig_name\u001b[39m\u001b[33m\"\u001b[39m: config.config_name,\n\u001b[32m 295\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mdescription\u001b[39m\u001b[33m\"\u001b[39m: config.description,\n\u001b[32m (...)\u001b[39m\u001b[32m 305\u001b[39m ],\n\u001b[32m 306\u001b[39m }\n\u001b[32m 308\u001b[39m \u001b[38;5;66;03m# Add partitioning info if present\u001b[39;00m\n", + "\u001b[31mDataCardError\u001b[39m: Configuration 'metadata' not found" ] } ], @@ -230,7 +264,7 @@ }, { "cell_type": "code", - "execution_count": 137, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -289,7 +323,7 @@ }, { "cell_type": "code", - "execution_count": 138, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -333,7 +367,7 @@ }, { "cell_type": "code", - "execution_count": 139, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -376,7 +410,7 @@ }, { "cell_type": "code", - "execution_count": 140, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -413,7 +447,7 @@ }, { "cell_type": "code", - "execution_count": 141, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -465,7 +499,7 @@ }, { "cell_type": "code", - "execution_count": 142, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -524,7 +558,7 @@ }, { "cell_type": "code", - "execution_count": 143, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -556,7 +590,7 @@ }, { "cell_type": "code", - "execution_count": 144, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -615,7 +649,7 @@ }, { "cell_type": "code", - "execution_count": 145, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -663,7 +697,7 @@ }, { "cell_type": "code", - "execution_count": 146, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -737,7 +771,7 @@ ], "metadata": { "kernelspec": { - "display_name": "tfbpapi-py3.11 (3.11.9)", + "display_name": "tfbpapi-py3.11", "language": "python", "name": "python3" }, diff --git a/docs/tutorials/hfqueryapi_tutorial.ipynb b/docs/tutorials/hfqueryapi_tutorial.ipynb new file mode 100644 index 0000000..70c3ebb --- /dev/null +++ b/docs/tutorials/hfqueryapi_tutorial.ipynb @@ -0,0 +1,2214 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# HfQueryAPI Tutorial: Metadata-Driven Data Exploration\n", + "\n", + "This tutorial demonstrates how to use the HfQueryAPI for efficient metadata-driven exploration and querying of Hugging Face datasets, using the Hackett 2020 transcription factor overexpression dataset as an example.\n", + "\n", + "## Overview\n", + "\n", + "The HfQueryAPI provides a streamlined workflow for:\n", + "1. **Exploring metadata** to understand dataset structure\n", + "2. **Setting filters** based on metadata values\n", + "3. **Querying data** with automatic filter application\n", + "4. **Efficient SQL-based filtering** on large datasets\n", + "\n", + "## Dataset: Hackett 2020\n", + "\n", + "The `BrentLab/hackett_2020` dataset contains transcription factor overexpression data with embedded metadata fields including:\n", + "- `regulator_locus_tag` & `regulator_symbol`: Transcription factor identifiers\n", + "- `time`: Time point (0, 15, 30, 90 minutes)\n", + "- `mechanism`: Experimental mechanism (ZEV)\n", + "- `restriction`: Restriction enzyme treatment (P, NP)\n", + "- `date`, `strain`: Experimental metadata\n", + "\n", + "Let's start exploring!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Initialize HfQueryAPI and Getting Metadata\n", + "\n", + "First, we'll initialize the API client for the Hackett 2020 dataset:" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Initialized HfQueryAPI for BrentLab/hackett_2020\n", + "Repository type: dataset\n", + "Available configurations: ['hackett_2020']\n" + ] + } + ], + "source": [ + "from tfbpapi.HfQueryAPI import HfQueryAPI\n", + "import pandas as pd\n", + "\n", + "# Initialize the API client\n", + "api = HfQueryAPI(repo_id=\"BrentLab/hackett_2020\", repo_type=\"dataset\")\n", + "\n", + "print(f\"Initialized HfQueryAPI for {api.repo_id}\")\n", + "print(f\"Repository type: {api.repo_type}\")\n", + "print(f\"Available configurations: {[c.config_name for c in api.configs]}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [], + "source": [ + "metadata = api.get_metadata(\"hackett_2020\")" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Experimental Conditions Summary\n", + "==================================================\n", + "\n", + "⏱️ Time Points:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Time Point (min)", + "rawType": "float64", + "type": "float" + }, + { + "name": "Frequency", + "rawType": "int64", + "type": "integer" + }, + { + "name": "% of Records", + "rawType": "float64", + "type": "float" + } + ], + "ref": "537d2492-acbe-4e3d-a2cc-3c1129ec2ed1", + "rows": [ + [ + "0", + "0.0", + "217", + "12.8" + ], + [ + "1", + "2.5", + "8", + "0.5" + ], + [ + "2", + "5.0", + "212", + "12.5" + ], + [ + "3", + "7.5", + "2", + "0.1" + ], + [ + "4", + "8.0", + "2", + "0.1" + ], + [ + "5", + "10.0", + "187", + "11.0" + ], + [ + "6", + "12.5", + "2", + "0.1" + ], + [ + "7", + "15.0", + "212", + "12.5" + ], + [ + "8", + "18.0", + "1", + "0.1" + ], + [ + "9", + "20.0", + "184", + "10.9" + ], + [ + "10", + "30.0", + "216", + "12.8" + ], + [ + "11", + "45.0", + "213", + "12.6" + ], + [ + "12", + "60.0", + "20", + "1.2" + ], + [ + "13", + "90.0", + "212", + "12.5" + ], + [ + "14", + "100.0", + "2", + "0.1" + ], + [ + "15", + "120.0", + "1", + "0.1" + ], + [ + "16", + "180.0", + "1", + "0.1" + ], + [ + "17", + "290.0", + "1", + "0.1" + ] + ], + "shape": { + "columns": 3, + "rows": 18 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Time Point (min)Frequency% of Records
00.021712.8
12.580.5
25.021212.5
37.520.1
48.020.1
510.018711.0
612.520.1
715.021212.5
818.010.1
920.018410.9
1030.021612.8
1145.021312.6
1260.0201.2
1390.021212.5
14100.020.1
15120.010.1
16180.010.1
17290.010.1
\n", + "
" + ], + "text/plain": [ + " Time Point (min) Frequency % of Records\n", + "0 0.0 217 12.8\n", + "1 2.5 8 0.5\n", + "2 5.0 212 12.5\n", + "3 7.5 2 0.1\n", + "4 8.0 2 0.1\n", + "5 10.0 187 11.0\n", + "6 12.5 2 0.1\n", + "7 15.0 212 12.5\n", + "8 18.0 1 0.1\n", + "9 20.0 184 10.9\n", + "10 30.0 216 12.8\n", + "11 45.0 213 12.6\n", + "12 60.0 20 1.2\n", + "13 90.0 212 12.5\n", + "14 100.0 2 0.1\n", + "15 120.0 1 0.1\n", + "16 180.0 1 0.1\n", + "17 290.0 1 0.1" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Experimental Design Overview:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Condition Type", + "rawType": "object", + "type": "string" + }, + { + "name": "Unique Values", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Most Common", + "rawType": "object", + "type": "string" + } + ], + "ref": "fc6ebeb5-026f-49dc-ab9a-89995ff14cb9", + "rows": [ + [ + "0", + "Mechanisms", + "2", + "ZEV" + ], + [ + "1", + "Restriction Treatments", + "3", + "P" + ], + [ + "2", + "Strains", + "215", + "SMY2207" + ], + [ + "3", + "Experimental Dates", + "23", + "20150101 to 20161117" + ] + ], + "shape": { + "columns": 3, + "rows": 4 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Condition TypeUnique ValuesMost Common
0Mechanisms2ZEV
1Restriction Treatments3P
2Strains215SMY2207
3Experimental Dates2320150101 to 20161117
\n", + "
" + ], + "text/plain": [ + " Condition Type Unique Values Most Common\n", + "0 Mechanisms 2 ZEV\n", + "1 Restriction Treatments 3 P\n", + "2 Strains 215 SMY2207\n", + "3 Experimental Dates 23 20150101 to 20161117" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Create summary tables for experimental conditions\n", + "print(\"Experimental Conditions Summary\")\n", + "print(\"=\" * 50)\n", + "\n", + "# Time points summary\n", + "time_summary = pd.DataFrame({\n", + " 'Time Point (min)': metadata['time'].value_counts().sort_index().index,\n", + " 'Frequency': metadata['time'].value_counts().sort_index().values,\n", + "})\n", + "time_summary['% of Records'] = (time_summary['Frequency'] / time_summary['Frequency'].sum() * 100).round(1)\n", + "\n", + "print(\"\\n⏱️ Time Points:\")\n", + "display(time_summary)\n", + "\n", + "# Experimental conditions table\n", + "conditions_summary = pd.DataFrame({\n", + " 'Condition Type': ['Mechanisms', 'Restriction Treatments', 'Strains', 'Experimental Dates'],\n", + " 'Unique Values': [\n", + " metadata['mechanism'].nunique(),\n", + " metadata['restriction'].nunique(), \n", + " metadata['strain'].nunique(),\n", + " metadata['date'].nunique()\n", + " ],\n", + " 'Most Common': [\n", + " metadata['mechanism'].mode().iloc[0],\n", + " metadata['restriction'].mode().iloc[0],\n", + " metadata['strain'].mode().iloc[0],\n", + " f\"{metadata['date'].min()} to {metadata['date'].max()}\"\n", + " ]\n", + "})\n", + "\n", + "print(\"\\nExperimental Design Overview:\")\n", + "display(conditions_summary)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Transcription Factor Analysis\n", + "==================================================\n", + "\n", + "Dataset Overview:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Metric", + "rawType": "object", + "type": "string" + }, + { + "name": "Value", + "rawType": "object", + "type": "string" + } + ], + "ref": "bd31a290-891e-4027-8987-337b87e04e47", + "rows": [ + [ + "0", + "Total Unique Transcription Factors", + "203" + ] + ], + "shape": { + "columns": 2, + "rows": 1 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
MetricValue
0Total Unique Transcription Factors203
\n", + "
" + ], + "text/plain": [ + " Metric Value\n", + "0 Total Unique Transcription Factors 203" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Create comprehensive transcription factor analysis tables\n", + "print(\"Transcription Factor Analysis\")\n", + "print(\"=\" * 50)\n", + "\n", + "print(f\"\\nDataset Overview:\")\n", + "tf_overview = pd.DataFrame({\n", + " 'Metric': [\n", + " 'Total Unique Transcription Factors',\n", + " ],\n", + " 'Value': [\n", + " f\"{metadata['regulator_locus_tag'].nunique():,}\",\n", + " ]\n", + "})\n", + "display(tf_overview)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setting Simple Filters" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Filter Analysis\n", + "========================================\n", + "\n", + "Applied Filters:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Filter Component", + "rawType": "object", + "type": "string" + }, + { + "name": "Value", + "rawType": "object", + "type": "string" + } + ], + "ref": "6c3fe59a-d2b0-49aa-b067-c472c07db1e0", + "rows": [ + [ + "0", + "Time Point", + "15 minutes" + ], + [ + "1", + "Mechanism", + "ZEV" + ], + [ + "2", + "Restriction", + "P (Restriction)" + ], + [ + "3", + "SQL Filter", + "time = 15 AND mechanism = 'ZEV' AND restriction = 'P'" + ] + ], + "shape": { + "columns": 2, + "rows": 4 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Filter ComponentValue
0Time Point15 minutes
1MechanismZEV
2RestrictionP (Restriction)
3SQL Filtertime = 15 AND mechanism = 'ZEV' AND restrictio...
\n", + "
" + ], + "text/plain": [ + " Filter Component Value\n", + "0 Time Point 15 minutes\n", + "1 Mechanism ZEV\n", + "2 Restriction P (Restriction)\n", + "3 SQL Filter time = 15 AND mechanism = 'ZEV' AND restrictio..." + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Top Transcription Factors in Filtered Dataset:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Locus Tag", + "rawType": "object", + "type": "string" + }, + { + "name": "Symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "Records in Subset", + "rawType": "int64", + "type": "integer" + } + ], + "ref": "183800f8-03b3-4fcb-846a-7585ea8533b5", + "rows": [ + [ + "0", + "YPL016W", + "SWI1", + "18525" + ], + [ + "1", + "YEL009C", + "GCN4", + "12350" + ], + [ + "2", + "YPL133C", + "RDS2", + "12350" + ], + [ + "3", + "Z3EV", + "Z3EV", + "12350" + ], + [ + "4", + "YBL008W", + "HIR1", + "6175" + ], + [ + "5", + "YBL021C", + "HAP3", + "6175" + ], + [ + "6", + "YBL025W", + "RRN10", + "6175" + ], + [ + "7", + "YBR239C", + "ERT1", + "6175" + ] + ], + "shape": { + "columns": 3, + "rows": 8 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Locus TagSymbolRecords in Subset
0YPL016WSWI118525
1YEL009CGCN412350
2YPL133CRDS212350
3Z3EVZ3EV12350
4YBL008WHIR16175
5YBL021CHAP36175
6YBL025WRRN106175
7YBR239CERT16175
\n", + "
" + ], + "text/plain": [ + " Locus Tag Symbol Records in Subset\n", + "0 YPL016W SWI1 18525\n", + "1 YEL009C GCN4 12350\n", + "2 YPL133C RDS2 12350\n", + "3 Z3EV Z3EV 12350\n", + "4 YBL008W HIR1 6175\n", + "5 YBL021C HAP3 6175\n", + "6 YBL025W RRN10 6175\n", + "7 YBR239C ERT1 6175" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Set filters for 15-minute timepoint with ZEV mechanism and P restriction\n", + "api.set_filter(\"hackett_2020\", time=15, mechanism=\"ZEV\", restriction=\"P\")\n", + "\n", + "# Create comprehensive filter analysis\n", + "print(\"Filter Analysis\")\n", + "print(\"=\" * 40)\n", + "\n", + "# Show current filter\n", + "current_filter = api.get_current_filter(\"hackett_2020\")\n", + "filter_info = pd.DataFrame({\n", + " 'Filter Component': ['Time Point', 'Mechanism', 'Restriction', 'SQL Filter'],\n", + " 'Value': ['15 minutes', 'ZEV', 'P (Restriction)', current_filter]\n", + "})\n", + "\n", + "print(\"\\nApplied Filters:\")\n", + "display(filter_info)\n", + "\n", + "# Analyze filter impact\n", + "filtered_metadata = metadata[\n", + " (metadata['time'] == 15) & \n", + " (metadata['mechanism'] == 'ZEV') & \n", + " (metadata['restriction'] == 'P')\n", + "]\n", + "\n", + "# Show top TFs in filtered data\n", + "filtered_tf_summary = filtered_metadata.groupby(['regulator_locus_tag', 'regulator_symbol'])['count'].sum().sort_values(ascending=False).head(8)\n", + "tf_filtered_df = pd.DataFrame({\n", + " 'Locus Tag': [idx[0] for idx in filtered_tf_summary.index],\n", + " 'Symbol': [idx[1] for idx in filtered_tf_summary.index],\n", + " 'Records in Subset': filtered_tf_summary.values,\n", + "})\n", + "\n", + "print(\"\\nTop Transcription Factors in Filtered Dataset:\")\n", + "display(tf_filtered_df)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Query Data with Automatic Filter Application\n", + "\n", + "Now when we query the data, our filters will be automatically applied:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setting Complex SQL Filters\n", + "\n", + "For more sophisticated filtering, we can use the `set_sql_filter()` method with full SQL expressions:" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Complex SQL Filter Analysis\n", + "==================================================\n", + "\n", + "Applied Complex Filter:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Filter Component", + "rawType": "object", + "type": "string" + }, + { + "name": "Value", + "rawType": "object", + "type": "string" + } + ], + "ref": "1063532f-04e7-453f-9fac-2261490f4701", + "rows": [ + [ + "0", + "Time Points", + "15, 30 minutes" + ], + [ + "1", + "Mechanism", + "ZEV (overexpression)" + ], + [ + "2", + "Restriction", + "P (restriction enzyme)" + ], + [ + "3", + "Selected TFs", + "YER040W (GLN3), YER028C (RSF2), YPL016W (SWI1)" + ], + [ + "4", + "Complete SQL Filter", + "time IN (15, 30) AND \n mechanism = 'ZEV' AND \n restriction = 'P' AND \n regulator_locus_tag IN ('YER040W', 'YER028C', 'YPL016W')" + ] + ], + "shape": { + "columns": 2, + "rows": 5 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Filter ComponentValue
0Time Points15, 30 minutes
1MechanismZEV (overexpression)
2RestrictionP (restriction enzyme)
3Selected TFsYER040W (GLN3), YER028C (RSF2), YPL016W (SWI1)
4Complete SQL Filtertime IN (15, 30) AND \\n mechanism = 'ZEV' A...
\n", + "
" + ], + "text/plain": [ + " Filter Component Value\n", + "0 Time Points 15, 30 minutes\n", + "1 Mechanism ZEV (overexpression)\n", + "2 Restriction P (restriction enzyme)\n", + "3 Selected TFs YER040W (GLN3), YER028C (RSF2), YPL016W (SWI1)\n", + "4 Complete SQL Filter time IN (15, 30) AND \\n mechanism = 'ZEV' A..." + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Set a complex filter for multiple time points and specific transcription factors\n", + "api.set_sql_filter(\"hackett_2020\", \"\"\"\n", + " time IN (15, 30) AND \n", + " mechanism = 'ZEV' AND \n", + " restriction = 'P' AND \n", + " regulator_locus_tag IN ('YER040W', 'YER028C', 'YPL016W')\n", + "\"\"\")\n", + "\n", + "print(\"Complex SQL Filter Analysis\")\n", + "print(\"=\" * 50)\n", + "\n", + "# Show filter details\n", + "filter_details = pd.DataFrame({\n", + " 'Filter Component': [\n", + " 'Time Points',\n", + " 'Mechanism', \n", + " 'Restriction',\n", + " 'Selected TFs',\n", + " 'Complete SQL Filter'\n", + " ],\n", + " 'Value': [\n", + " '15, 30 minutes',\n", + " 'ZEV (overexpression)',\n", + " 'P (restriction enzyme)',\n", + " 'YER040W (GLN3), YER028C (RSF2), YPL016W (SWI1)',\n", + " api.get_current_filter('hackett_2020')\n", + " ]\n", + "})\n", + "\n", + "print(\"\\nApplied Complex Filter:\")\n", + "display(filter_details)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [], + "source": [ + "## Query with the complex filter" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Time Course Comparison for Selected TFs:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Locus Tag", + "rawType": "object", + "type": "string" + }, + { + "name": "Symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "Time (min)", + "rawType": "float64", + "type": "float" + }, + { + "name": "Target Count", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Avg Response", + "rawType": "float64", + "type": "float" + }, + { + "name": "Strong Responders", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Max |Response|", + "rawType": "float64", + "type": "float" + }, + { + "name": "% Strong", + "rawType": "float64", + "type": "float" + } + ], + "ref": "fbfefef3-454e-4318-99ae-3d43d79d3800", + "rows": [ + [ + "0", + "YER028C", + "MIG3", + "15.0", + "6175", + "-0.01", + "99", + "5.894", + "1.6" + ], + [ + "1", + "YER028C", + "MIG3", + "30.0", + "6175", + "-0.028", + "246", + "5.516", + "4.0" + ], + [ + "2", + "YER040W", + "GLN3", + "15.0", + "6175", + "0.018", + "81", + "7.923", + "1.3" + ], + [ + "3", + "YER040W", + "GLN3", + "30.0", + "6175", + "0.042", + "631", + "10.459", + "10.2" + ], + [ + "4", + "YPL016W", + "SWI1", + "15.0", + "18525", + "0.001", + "431", + "6.216", + "2.3" + ], + [ + "5", + "YPL016W", + "SWI1", + "30.0", + "18525", + "0.033", + "762", + "6.753", + "4.1" + ] + ], + "shape": { + "columns": 8, + "rows": 6 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Locus TagSymbolTime (min)Target CountAvg ResponseStrong RespondersMax |Response|% Strong
0YER028CMIG315.06175-0.010995.8941.6
1YER028CMIG330.06175-0.0282465.5164.0
2YER040WGLN315.061750.018817.9231.3
3YER040WGLN330.061750.04263110.45910.2
4YPL016WSWI115.0185250.0014316.2162.3
5YPL016WSWI130.0185250.0337626.7534.1
\n", + "
" + ], + "text/plain": [ + " Locus Tag Symbol Time (min) Target Count Avg Response Strong Responders \\\n", + "0 YER028C MIG3 15.0 6175 -0.010 99 \n", + "1 YER028C MIG3 30.0 6175 -0.028 246 \n", + "2 YER040W GLN3 15.0 6175 0.018 81 \n", + "3 YER040W GLN3 30.0 6175 0.042 631 \n", + "4 YPL016W SWI1 15.0 18525 0.001 431 \n", + "5 YPL016W SWI1 30.0 18525 0.033 762 \n", + "\n", + " Max |Response| % Strong \n", + "0 5.894 1.6 \n", + "1 5.516 4.0 \n", + "2 7.923 1.3 \n", + "3 10.459 10.2 \n", + "4 6.216 2.3 \n", + "5 6.753 4.1 " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Overall TF Performance Summary:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Locus Tag", + "rawType": "object", + "type": "string" + }, + { + "name": "Symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "Total Targets", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Total Strong", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Avg Response", + "rawType": "float64", + "type": "float" + }, + { + "name": "% Strong Overall", + "rawType": "float64", + "type": "float" + } + ], + "ref": "ca80d3c1-4fec-4613-9f73-12ba5359ed44", + "rows": [ + [ + "0", + "YER028C", + "MIG3", + "12350", + "345", + "-0.019", + "2.8" + ], + [ + "1", + "YER040W", + "GLN3", + "12350", + "712", + "0.03", + "5.8" + ], + [ + "2", + "YPL016W", + "SWI1", + "37050", + "1193", + "0.017", + "3.2" + ] + ], + "shape": { + "columns": 6, + "rows": 3 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Locus TagSymbolTotal TargetsTotal StrongAvg Response% Strong Overall
0YER028CMIG312350345-0.0192.8
1YER040WGLN3123507120.0305.8
2YPL016WSWI13705011930.0173.2
\n", + "
" + ], + "text/plain": [ + " Locus Tag Symbol Total Targets Total Strong Avg Response \\\n", + "0 YER028C MIG3 12350 345 -0.019 \n", + "1 YER040W GLN3 12350 712 0.030 \n", + "2 YPL016W SWI1 37050 1193 0.017 \n", + "\n", + " % Strong Overall \n", + "0 2.8 \n", + "1 5.8 \n", + "2 3.2 " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "\n", + "time_comparison = api.query(\"\"\"\n", + " SELECT \n", + " regulator_locus_tag,\n", + " regulator_symbol,\n", + " time,\n", + " COUNT(*) as target_count,\n", + " ROUND(AVG(log2_shrunken_timecourses), 3) as avg_response,\n", + " COUNT(CASE WHEN ABS(log2_shrunken_timecourses) > 0.5 THEN 1 END) as strong_responders,\n", + " ROUND(MAX(ABS(log2_shrunken_timecourses)), 3) as max_abs_response\n", + " FROM hackett_2020 \n", + " GROUP BY regulator_locus_tag, regulator_symbol, time\n", + " ORDER BY regulator_locus_tag, time\n", + "\"\"\", \"hackett_2020\")\n", + "\n", + "# Format display\n", + "time_comparison_display = time_comparison.copy()\n", + "time_comparison_display.columns = ['Locus Tag', 'Symbol', 'Time (min)', 'Target Count', 'Avg Response', 'Strong Responders', 'Max |Response|']\n", + "time_comparison_display['% Strong'] = (time_comparison_display['Strong Responders'] / time_comparison_display['Target Count'] * 100).round(1)\n", + "\n", + "print(\"\\nTime Course Comparison for Selected TFs:\")\n", + "display(time_comparison_display)\n", + "\n", + "# Summary analysis\n", + "tf_summary = time_comparison.groupby(['regulator_locus_tag', 'regulator_symbol']).agg({\n", + " 'target_count': 'sum',\n", + " 'strong_responders': 'sum',\n", + " 'avg_response': 'mean'\n", + "}).reset_index()\n", + "\n", + "tf_summary['total_%_strong'] = (tf_summary['strong_responders'] / tf_summary['target_count'] * 100).round(1)\n", + "tf_summary_display = tf_summary.copy()\n", + "tf_summary_display.columns = ['Locus Tag', 'Symbol', 'Total Targets', 'Total Strong', 'Avg Response', '% Strong Overall']\n", + "\n", + "print(\"\\nOverall TF Performance Summary:\")\n", + "display(tf_summary_display)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Filter Management\n", + "\n", + "The filtering system provides full control over active filters:" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "🔧 Filter Management Demonstration\n", + "==================================================\n", + "\n", + "Current filter:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Status", + "rawType": "object", + "type": "string" + }, + { + "name": "SQL WHERE clause", + "rawType": "object", + "type": "string" + } + ], + "ref": "e93f9433-b31e-49db-b98d-5c5b4116e1d6", + "rows": [ + [ + "0", + "Active Filter", + "time IN (15, 30) AND \n mechanism = 'ZEV' AND \n restriction = 'P' AND \n regulator_locus_tag IN ('YER040W', 'YER028C', 'YPL016W')" + ] + ], + "shape": { + "columns": 2, + "rows": 1 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
StatusSQL WHERE clause
0Active Filtertime IN (15, 30) AND \\n mechanism = 'ZEV' A...
\n", + "
" + ], + "text/plain": [ + " Status SQL WHERE clause\n", + "0 Active Filter time IN (15, 30) AND \\n mechanism = 'ZEV' A..." + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "After clearing filters:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Status", + "rawType": "object", + "type": "string" + }, + { + "name": "SQL WHERE clause", + "rawType": "object", + "type": "string" + } + ], + "ref": "7ca71950-6a31-404e-baa4-d096e56c4991", + "rows": [ + [ + "0", + "After Clearing", + "None" + ] + ], + "shape": { + "columns": 2, + "rows": 1 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
StatusSQL WHERE clause
0After ClearingNone
\n", + "
" + ], + "text/plain": [ + " Status SQL WHERE clause\n", + "0 After Clearing None" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "📊 Dataset Size Comparison:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Dataset State", + "rawType": "object", + "type": "string" + }, + { + "name": "Total Records", + "rawType": "object", + "type": "string" + }, + { + "name": "Percentage", + "rawType": "object", + "type": "string" + } + ], + "ref": "d30810e1-4013-4929-a14a-522ee08c48fe", + "rows": [ + [ + "0", + "Unfiltered (Full Dataset)", + "10,454,275", + "100.0%" + ], + [ + "1", + "Filtered (time=15, ZEV, P)", + "1,012,700", + "9.7%" + ], + [ + "2", + "Data Reduction", + "9,441,575", + "90.3%" + ] + ], + "shape": { + "columns": 3, + "rows": 3 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Dataset StateTotal RecordsPercentage
0Unfiltered (Full Dataset)10,454,275100.0%
1Filtered (time=15, ZEV, P)1,012,7009.7%
2Data Reduction9,441,57590.3%
\n", + "
" + ], + "text/plain": [ + " Dataset State Total Records Percentage\n", + "0 Unfiltered (Full Dataset) 10,454,275 100.0%\n", + "1 Filtered (time=15, ZEV, P) 1,012,700 9.7%\n", + "2 Data Reduction 9,441,575 90.3%" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Complete HfQueryAPI Workflow:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "Step", + "rawType": "object", + "type": "string" + }, + { + "name": "Method", + "rawType": "object", + "type": "string" + }, + { + "name": "Purpose", + "rawType": "object", + "type": "string" + } + ], + "ref": "f8e9e25a-c9d1-479d-b664-f70bac590d68", + "rows": [ + [ + "0", + "1. Explore Metadata", + "api.get_metadata()", + "Understand dataset structure" + ], + [ + "1", + "2. Set Simple Filters", + "api.set_filter(config, **kwargs)", + "Filter by metadata values" + ], + [ + "2", + "3. Set Complex SQL Filters", + "api.set_sql_filter(config, sql)", + "Complex multi-condition filtering" + ], + [ + "3", + "4. Query with Auto-Apply", + "api.query(sql, config)", + "Analyze with automatic filtering" + ], + [ + "4", + "5. Clear/Manage Filters", + "api.clear_filter() / get_current_filter()", + "Reset or inspect current state" + ] + ], + "shape": { + "columns": 3, + "rows": 5 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
StepMethodPurpose
01. Explore Metadataapi.get_metadata()Understand dataset structure
12. Set Simple Filtersapi.set_filter(config, **kwargs)Filter by metadata values
23. Set Complex SQL Filtersapi.set_sql_filter(config, sql)Complex multi-condition filtering
34. Query with Auto-Applyapi.query(sql, config)Analyze with automatic filtering
45. Clear/Manage Filtersapi.clear_filter() / get_current_filter()Reset or inspect current state
\n", + "
" + ], + "text/plain": [ + " Step Method \\\n", + "0 1. Explore Metadata api.get_metadata() \n", + "1 2. Set Simple Filters api.set_filter(config, **kwargs) \n", + "2 3. Set Complex SQL Filters api.set_sql_filter(config, sql) \n", + "3 4. Query with Auto-Apply api.query(sql, config) \n", + "4 5. Clear/Manage Filters api.clear_filter() / get_current_filter() \n", + "\n", + " Purpose \n", + "0 Understand dataset structure \n", + "1 Filter by metadata values \n", + "2 Complex multi-condition filtering \n", + "3 Analyze with automatic filtering \n", + "4 Reset or inspect current state " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Demonstrate filter management capabilities\n", + "print(\"🔧 Filter Management Demonstration\")\n", + "print(\"=\" * 50)\n", + "\n", + "# Show current filter\n", + "current_filter = api.get_current_filter('hackett_2020')\n", + "print(f\"\\nCurrent filter:\")\n", + "current_filter_df = pd.DataFrame({\n", + " 'Status': ['Active Filter'],\n", + " 'SQL WHERE clause': [current_filter if current_filter else 'None']\n", + "})\n", + "display(current_filter_df)\n", + "\n", + "# Clear all filters and show impact\n", + "api.clear_filter(\"hackett_2020\")\n", + "print(f\"\\nAfter clearing filters:\")\n", + "cleared_filter_df = pd.DataFrame({\n", + " 'Status': ['After Clearing'],\n", + " 'SQL WHERE clause': [api.get_current_filter('hackett_2020') or 'None']\n", + "})\n", + "display(cleared_filter_df)\n", + "\n", + "# Query unfiltered vs filtered data comparison\n", + "total_records = api.query(\"SELECT COUNT(*) as total FROM hackett_2020\", \"hackett_2020\")\n", + "\n", + "# Set filters again for comparison\n", + "api.set_filter(\"hackett_2020\", time=15, mechanism=\"ZEV\", restriction=\"P\")\n", + "filtered_records = api.query(\"SELECT COUNT(*) as total FROM hackett_2020\", \"hackett_2020\")\n", + "\n", + "# Create comprehensive comparison table\n", + "comparison_results = pd.DataFrame({\n", + " 'Dataset State': [\n", + " 'Unfiltered (Full Dataset)',\n", + " 'Filtered (time=15, ZEV, P)',\n", + " 'Data Reduction'\n", + " ],\n", + " 'Total Records': [\n", + " f\"{total_records.iloc[0]['total']:,}\",\n", + " f\"{filtered_records.iloc[0]['total']:,}\",\n", + " f\"{total_records.iloc[0]['total'] - filtered_records.iloc[0]['total']:,}\"\n", + " ],\n", + " 'Percentage': [\n", + " '100.0%',\n", + " f\"{(filtered_records.iloc[0]['total'] / total_records.iloc[0]['total'] * 100):.1f}%\",\n", + " f\"{((total_records.iloc[0]['total'] - filtered_records.iloc[0]['total']) / total_records.iloc[0]['total'] * 100):.1f}%\"\n", + " ]\n", + "})\n", + "\n", + "print(\"\\n📊 Dataset Size Comparison:\")\n", + "display(comparison_results)\n", + "\n", + "# Show filter workflow summary\n", + "workflow_summary = pd.DataFrame({\n", + " 'Step': [\n", + " '1. Explore Metadata',\n", + " '2. Set Simple Filters', \n", + " '3. Set Complex SQL Filters',\n", + " '4. Query with Auto-Apply',\n", + " '5. Clear/Manage Filters'\n", + " ],\n", + " 'Method': [\n", + " 'api.get_metadata()',\n", + " 'api.set_filter(config, **kwargs)',\n", + " 'api.set_sql_filter(config, sql)',\n", + " 'api.query(sql, config)',\n", + " 'api.clear_filter() / get_current_filter()'\n", + " ],\n", + " 'Purpose': [\n", + " 'Understand dataset structure',\n", + " 'Filter by metadata values',\n", + " 'Complex multi-condition filtering',\n", + " 'Analyze with automatic filtering',\n", + " 'Reset or inspect current state'\n", + " ]\n", + "})\n", + "\n", + "print(\"\\nComplete HfQueryAPI Workflow:\")\n", + "display(workflow_summary)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "tfbpapi-py3.11", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/mkdocs.yml b/mkdocs.yml index 85960a2..e6fd78e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -150,6 +150,8 @@ nav: - Home: index.md - Tutorials: - "DataCard: Exploring Datasets": tutorials/datacard_tutorial.ipynb + - "Cache Management": tutorials/cache_manager_tutorial.ipynb + - "Querying the Datasets": tutorials/hfqueryapi_tutorial.ipynb - API Reference: - Core Components: - HfQueryAPI: HfQueryAPI.md diff --git a/tfbpapi/HfCacheManager.py b/tfbpapi/HfCacheManager.py index 95a0e02..d8b9ad9 100644 --- a/tfbpapi/HfCacheManager.py +++ b/tfbpapi/HfCacheManager.py @@ -1,17 +1,247 @@ import logging from datetime import datetime, timedelta -from typing import Literal +from pathlib import Path +from typing import Any, Literal -from huggingface_hub import scan_cache_dir +import duckdb +from huggingface_hub import scan_cache_dir, try_to_load_from_cache from huggingface_hub.utils import DeleteCacheStrategy +from .datainfo.datacard import DataCard -class HFCacheManager: - """Cache memory management for Hugging Face Hub cache.""" - def __init__(self, logger: logging.Logger | None = None): +class HfCacheManager(DataCard): + """Enhanced cache management for Hugging Face Hub with metadata-focused + retrieval.""" + + def __init__( + self, + repo_id: str, + duckdb_conn: duckdb.DuckDBPyConnection, + token: str | None = None, + logger: logging.Logger | None = None, + ): + super().__init__(repo_id, token) + self.duckdb_conn = duckdb_conn self.logger = logger or logging.getLogger(__name__) + def _get_metadata_for_config(self, config) -> dict[str, Any]: + """Get metadata for a specific configuration using 3-case strategy.""" + config_result = { + "config_name": config.config_name, + "strategy": None, + "table_name": None, + "success": False, + "message": "", + } + + table_name = f"metadata_{config.config_name}" + + try: + # Case 1: Check if metadata already exists in DuckDB + if self._check_metadata_exists_in_duckdb(table_name): + config_result.update( + { + "strategy": "duckdb_exists", + "table_name": table_name, + "success": True, + "message": f"Metadata table {table_name} " + "already exists in DuckDB", + } + ) + return config_result + + # Case 2: Check if HF data is in cache, create DuckDB representation + if self._load_metadata_from_cache(config, table_name): + config_result.update( + { + "strategy": "cache_loaded", + "table_name": table_name, + "success": True, + "message": "Loaded metadata from cache " + f"into table {table_name}", + } + ) + return config_result + + # Case 3: Download from HF (explicit vs embedded) + if self._download_and_load_metadata(config, table_name): + config_result.update( + { + "strategy": "downloaded", + "table_name": table_name, + "success": True, + "message": "Downloaded and loaded metadata " + f"into table {table_name}", + } + ) + return config_result + + config_result["message"] = ( + f"Failed to retrieve metadata for {config.config_name}" + ) + + except Exception as e: + config_result["message"] = f"Error processing {config.config_name}: {e}" + self.logger.error(f"Error in metadata config {config.config_name}: {e}") + + return config_result + + def _check_metadata_exists_in_duckdb(self, table_name: str) -> bool: + """Case 1: Check if metadata table already exists in DuckDB database.""" + try: + # Query information schema to check if table exists + result = self.duckdb_conn.execute( + "SELECT table_name FROM information_schema.tables WHERE table_name = ?", + [table_name], + ).fetchone() + + exists = result is not None + if exists: + self.logger.debug(f"Table {table_name} already exists in DuckDB") + return exists + + except Exception as e: + self.logger.debug(f"Error checking DuckDB table existence: {e}") + return False + + def _load_metadata_from_cache(self, config, table_name: str) -> bool: + """Case 2: HF data in cache, create DuckDB representation.""" + try: + # Check if metadata files are cached locally + cached_files = [] + for data_file in config.data_files: + cached_path = try_to_load_from_cache( + repo_id=self.repo_id, + filename=data_file.path, + repo_type="dataset", + ) + + if isinstance(cached_path, str) and Path(cached_path).exists(): + cached_files.append(cached_path) + + if not cached_files: + self.logger.debug(f"No cached files found for {config.config_name}") + return False + + # Load cached parquet files into DuckDB + self._create_duckdb_table_from_files(cached_files, table_name) + self.logger.info( + f"Loaded {len(cached_files)} cached files into {table_name}" + ) + return True + + except Exception as e: + self.logger.debug(f"Error loading from cache for {config.config_name}: {e}") + return False + + def _download_and_load_metadata(self, config, table_name: str) -> bool: + """Case 3: Download from HF (explicit vs embedded).""" + try: + from huggingface_hub import snapshot_download + + # Download specific files for this metadata config + file_patterns = [data_file.path for data_file in config.data_files] + + downloaded_path = snapshot_download( + repo_id=self.repo_id, + repo_type="dataset", + allow_patterns=file_patterns, + token=self.token, + ) + + # Find downloaded parquet files + downloaded_files = [] + for pattern in file_patterns: + file_path = Path(downloaded_path) / pattern + if file_path.exists() and file_path.suffix == ".parquet": + downloaded_files.append(str(file_path)) + else: + # Handle wildcard patterns + parent_dir = Path(downloaded_path) / Path(pattern).parent + if parent_dir.exists(): + downloaded_files.extend( + [str(f) for f in parent_dir.glob("*.parquet")] + ) + + if not downloaded_files: + self.logger.warning( + f"No parquet files found after download for {config.config_name}" + ) + return False + + # Load downloaded files into DuckDB + self._create_duckdb_table_from_files(downloaded_files, table_name) + self.logger.info( + f"Downloaded and loaded {len(downloaded_files)} files into {table_name}" + ) + return True + + except Exception as e: + self.logger.error( + f"Error downloading metadata for {config.config_name}: {e}" + ) + return False + + def _create_duckdb_table_from_files( + self, file_paths: list[str], table_name: str + ) -> None: + """Create DuckDB table/view from parquet files.""" + if len(file_paths) == 1: + # Single file + create_sql = f""" + CREATE OR REPLACE VIEW {table_name} AS + SELECT * FROM read_parquet('{file_paths[0]}') + """ + else: + # Multiple files + files_str = "', '".join(file_paths) + create_sql = f""" + CREATE OR REPLACE VIEW {table_name} AS + SELECT * FROM read_parquet(['{files_str}']) + """ + + self.duckdb_conn.execute(create_sql) + self.logger.debug( + f"Created DuckDB view {table_name} from {len(file_paths)} files" + ) + + def _extract_embedded_metadata_field( + self, data_table_name: str, field_name: str, metadata_table_name: str + ) -> bool: + """Extract a specific metadata field from a data table.""" + try: + # Create a metadata view with unique values from the specified field + extract_sql = f""" + CREATE OR REPLACE VIEW {metadata_table_name} AS + SELECT DISTINCT {field_name} as value, COUNT(*) as count + FROM {data_table_name} + WHERE {field_name} IS NOT NULL + GROUP BY {field_name} + ORDER BY count DESC + """ + + self.duckdb_conn.execute(extract_sql) + + # Verify the table was created and has data + count_result = self.duckdb_conn.execute( + f"SELECT COUNT(*) FROM {metadata_table_name}" + ).fetchone() + + if count_result and count_result[0] > 0: + self.logger.info( + f"Extracted {count_result[0]} unique values for {field_name} " + f"into {metadata_table_name}" + ) + return True + else: + self.logger.warning(f"No data found for field {field_name}") + return False + + except Exception as e: + self.logger.error(f"Error extracting field {field_name}: {e}") + return False + def clean_cache_by_age( self, max_age_days: int = 30, diff --git a/tfbpapi/HfQueryAPI.py b/tfbpapi/HfQueryAPI.py index 4eee06d..ddca995 100644 --- a/tfbpapi/HfQueryAPI.py +++ b/tfbpapi/HfQueryAPI.py @@ -1,21 +1,15 @@ import logging -import os -import re from pathlib import Path -from typing import Any, Literal +from typing import Literal -import duckdb import pandas as pd -from datasets import Dataset, DatasetDict, load_dataset -from huggingface_hub import hf_hub_download, snapshot_download -from huggingface_hub.constants import HF_HUB_CACHE -from .errors import RepoTooLargeError -from .HfCacheManager import HFCacheManager +from .constants import CACHE_DIR +from .HfCacheManager import HfCacheManager -class HfQueryAPI: - """Hugging Face API client with intelligent downloading and SQL querying.""" +class HfQueryAPI(HfCacheManager): + """Minimal Hugging Face API client focused on metadata retrieval.""" def __init__( self, @@ -23,136 +17,35 @@ def __init__( repo_type: Literal["model", "dataset", "space"] = "dataset", token: str | None = None, cache_dir: str | Path | None = None, - auto_download_threshold_mb: float = 100.0, - auto_parse_datacard: bool = True, - enable_cache_management: bool = True, - cache_auto_cleanup: bool = False, - cache_max_age_days: int = 30, - cache_max_size: str = "10GB", ): """ - Initialize the HF Query API client. + Initialize the minimal HF Query API client. :param repo_id: Repository identifier (e.g., "user/dataset") :param repo_type: Type of repository ("dataset", "model", "space") :param token: HuggingFace token for authentication :param cache_dir: HF cache_dir for downloads - :param auto_download_threshold_mb: Threshold in MB for auto full download - :param auto_parse_datacard: Whether to auto-parse the datacard on init - :param enable_cache_management: Enable integrated cache management features - :param cache_auto_cleanup: Enable automatic cache cleanup - :param cache_max_age_days: Maximum age in days for cache entries - :param cache_max_size: Maximum total cache size (e.g., "10GB") """ - self.logger = logging.getLogger(self.__class__.__name__) + # No DuckDB connection needed for metadata-only functionality + import duckdb - # Initialize data info manager with new architecture - self.data_info = HfDataInfoManager( - repo_id=repo_id, repo_type=repo_type, token=token - ) - - self.cache_dir = Path( - cache_dir if cache_dir else os.getenv("HF_CACHE_DIR", HF_HUB_CACHE) - ) - self.auto_download_threshold_mb = auto_download_threshold_mb - self._loaded_datasets: dict[str, Dataset | DatasetDict] = {} self._duckdb_conn = duckdb.connect(":memory:") - self._table_filters: dict[str, str] = {} - self._partition_cache: dict[str, set[str]] = {} # Track downloaded partitions - - # Initialize cache management - self._cache_manager = None - if enable_cache_management: - self._cache_manager = HFCacheManager(logger=self.logger) - self._cache_auto_cleanup = cache_auto_cleanup - self._cache_max_age_days = cache_max_age_days - self._cache_max_size = cache_max_size - - if auto_parse_datacard: - try: - self.data_info.parse_datacard() - except Exception as e: - self.logger.warning(f"Failed to auto-parse datacard: {e}") - self.data_info.clear() - - @property - def repo_id(self) -> str: - return self.data_info.repo_id - - @property - def repo_type(self) -> str: - return self.data_info.repo_type - - @property - def token(self) -> str | None: - return self.data_info.token - @property - def datasets(self) -> dict[str, Any]: - """Parsed dataset configurations from the datacard.""" - # Convert TableConfig objects to legacy dictionary format for backward compatibility - result = {} - for name, table_config in self.data_info.datasets.items(): - if table_config: - # Convert FeatureInfo objects to legacy format - features = {} - for feat_name, feat_info in table_config.features.items(): - features[feat_name] = { - "dtype": feat_info.dtype, - "description": feat_info.description, - } - - # Convert DataFileInfo objects to legacy format - data_files = [] - for file_info in table_config.data_files: - data_files.append( - {"path": file_info.path, "split": file_info.split} - ) - - result[name] = { - "features": features, - "data_files": data_files, - "config": table_config.config, - "loaded": table_config.downloaded, # Use "loaded" for backward compatibility - "is_partitioned": table_config.is_partitioned, - } - return result - - @datasets.setter - def datasets(self, value: dict[str, Any]) -> None: - """Set dataset configurations (for backward compatibility).""" - # Clear existing data and register new tables - self.data_info.clear() - # Convert legacy format to TableConfig objects if needed - from .datainfo.models import TableConfig - - table_configs = {} - for name, config in value.items(): - if isinstance(config, TableConfig): - table_configs[name] = config - else: - # Convert from legacy dict format - table_configs[name] = TableConfig( - name=name, - features=config.get("features", {}), - data_files=config.get("data_files", []), - config=config, - downloaded=config.get("loaded", False), # "loaded" was the old name - is_partitioned=config.get("is_partitioned", False), - partition_info=None, - ) - self.data_info._registry.register_tables(table_configs) + # Initialize parent with minimal setup + super().__init__( + repo_id=repo_id, + duckdb_conn=self._duckdb_conn, + token=token, + logger=logging.getLogger(self.__class__.__name__), + ) - @property - def available_tables(self) -> list[str]: - """List of available table names for querying.""" - return self.data_info.available_tables + # Store basic configuration + self.repo_type = repo_type + self.cache_dir = Path(cache_dir) if cache_dir is not None else CACHE_DIR - @property - def cache_manager(self) -> HFCacheManager | None: - """Access to the integrated cache manager for advanced cache operations.""" - return self._cache_manager + # Filter storage system + self._table_filters: dict[str, str] = {} # config_name -> SQL WHERE clause @property def cache_dir(self) -> Path: @@ -166,2025 +59,286 @@ def cache_dir(self, value: str | Path) -> None: raise FileNotFoundError(f"Cache directory {path} does not exist") self._cache_dir = path - @property - def size(self) -> dict[str, Any] | None: - """Size information from the HF Dataset Server API.""" - size_info = self.data_info.get_dataset_size() - if size_info: - return { - "total": size_info.total_bytes, - "total_mb": size_info.total_mb, - "configs": size_info.config_sizes, - } - return None - - @size.setter - def size(self, value: dict[str, Any]) -> None: - """Set size information (for backward compatibility).""" - # Convert legacy format to DatasetSize object - from .datainfo.models import DatasetSize - - dataset_size = DatasetSize.from_hf_size_response(value) - self.data_info._dataset_size = dataset_size - - @property - def snapshot_path(self) -> Path | None: - """Path to the last downloaded snapshot (if any).""" - return getattr(self, "_snapshot_path", None) - - @snapshot_path.setter - def snapshot_path(self, value: str | Path | None) -> None: - self._snapshot_path = None if value is None else Path(value) - - def _build_auth_headers(self) -> dict[str, str]: - """Build authentication headers if token is available.""" - return ( - {"Authorization": f"Bearer {self.data_info.token}"} - if self.data_info.token - else {} - ) - - def _normalize_patterns(self, kwargs: dict[str, Any]) -> None: - """Convert string patterns to lists.""" - for pattern_key in ["allow_patterns", "ignore_patterns"]: - if pattern_key in kwargs and kwargs[pattern_key] is not None: - patterns = kwargs[pattern_key] - if isinstance(patterns, str): - kwargs[pattern_key] = [patterns] - - def download_partitions( - self, table_name: str, partition_values: set[str] | None = None - ) -> Path: - """ - Download specific partitions using the path_template from partitioning metadata. - - :param table_name: Name of the dataset table - :param partition_values: Specific partition values to download (None for all) - :return: Path to downloaded data - + def get_metadata(self, config_name: str | None = None) -> pd.DataFrame: """ - table_config = self.data_info.get_table_or_raise(table_name) - - if not table_config.is_partitioned: - raise ValueError(f"Table {table_name} is not configured as partitioned") - - partition_info = self.data_info.get_partition_info(table_name) - if not partition_info: - raise ValueError(f"Table {table_name} missing partition information") - - path_template = partition_info.get("path_template") - if not path_template: - raise ValueError( - f"Table {table_name} missing required path_template in partitioning config" - ) - - partition_columns = partition_info.get("partition_by", []) - - if partition_values and partition_columns: - # Download specific partitions using path template - patterns = [] - for partition_value in partition_values: - # For single-column partitioning, substitute the first column - if len(partition_columns) == 1: - column = partition_columns[0] - pattern = path_template.replace(f"{{{column}}}", partition_value) - patterns.append(pattern) - else: - # For multi-column partitioning, we'd need more sophisticated logic - # For now, create a wildcard pattern for the specific value - # This is a simplified approach - real implementation would need - # to handle multi-dimensional partition filtering - pattern = path_template - for column in partition_columns: - if f"{{{column}}}" in pattern: - pattern = pattern.replace(f"{{{column}}}", partition_value) - break - patterns.append(pattern) - - self.logger.info( - f"Downloading partitions for {table_name}: {partition_values}" - ) - self.logger.debug(f"Using patterns: {patterns}") - return self.download(allow_patterns=patterns) - else: - # Download all partitions - use the original data files paths - patterns = table_config.get_file_paths() + Retrieve metadata as a DataFrame with actual metadata values. - self.logger.info(f"Downloading all partitions for {table_name}") - return self.download(allow_patterns=patterns) + For explicit metadata (dataset_type == METADATA): Returns all rows from metadata + table. For embedded metadata (has metadata_fields): Returns distinct + combinations of metadata fields. - def download( - self, - files: list[str] | str | None = None, - force_full_download: bool = False, - auto_download_threshold_mb: float = 100.0, - dry_run: bool = False, - **kwargs, - ) -> Path: - """Download dataset with intelligent partitioning support.""" - dataset_size_mb = self.data_info.get_dataset_size_mb() - - if dataset_size_mb <= auto_download_threshold_mb or force_full_download: - self.logger.info( - f"Dataset size ({dataset_size_mb:.2f} MB) is below threshold. " - "Downloading entire repo." - ) - files = None - kwargs.pop("allow_patterns", None) - kwargs.pop("ignore_patterns", None) - elif ( - not files - and not kwargs.get("allow_patterns") - and not kwargs.get("ignore_patterns") - ): - excess_size_mb = dataset_size_mb - auto_download_threshold_mb - raise RepoTooLargeError( - f"Dataset size ({dataset_size_mb:.2f} MB) exceeds threshold by " - f"{excess_size_mb:.2f} MB. Specify files, patterns, or set " - "force_full_download=True." - ) - - # Handle specific file downloads - if files is not None: - if isinstance(files, str) or (isinstance(files, list) and len(files) == 1): - filename = files if isinstance(files, str) else files[0] - return self._download_single_file(filename, dry_run=dry_run, **kwargs) - elif isinstance(files, list) and len(files) > 1: - if kwargs.get("allow_patterns") is not None: - self.logger.warning( - "Both 'files' and 'allow_patterns' provided. Using 'files'." - ) - kwargs["allow_patterns"] = files - - return self._download_snapshot(dry_run=dry_run, **kwargs) - - def _download_single_file( - self, filename: str, dry_run: bool = False, **kwargs - ) -> Path: - """Download a single file using hf_hub_download.""" - self.logger.info(f"Downloading single file: {filename}") - - if dry_run: - self.logger.info(f"[DRY RUN] Would download {filename} from {self.repo_id}") - return Path("dry_run_path") - - hf_kwargs = { - "repo_id": self.repo_id, - "repo_type": self.repo_type, - "filename": filename, - "token": self.token, - **kwargs, - } - - if "local_dir" not in hf_kwargs and self.cache_dir is not None: - hf_kwargs["cache_dir"] = str(self.cache_dir) - - for key in ["local_dir", "cache_dir"]: - if key in hf_kwargs and hf_kwargs[key] is not None: - hf_kwargs[key] = str(hf_kwargs[key]) - file_path = hf_hub_download(**hf_kwargs) - self.snapshot_path = Path(file_path).parent - return Path(file_path) - - def _download_snapshot(self, dry_run: bool = False, **kwargs) -> Path: - """Download repository snapshot using snapshot_download.""" - if dry_run: - self.logger.info(f"[DRY RUN] Would download from {self.repo_id}:") - self.logger.info(f" - allow_patterns: {kwargs.get('allow_patterns')}") - self.logger.info(f" - ignore_patterns: {kwargs.get('ignore_patterns')}") - return Path("dry_run_path") - - self.logger.info( - f"Downloading repo snapshot - " - f"allow: {kwargs.get('allow_patterns')}, " - f"ignore: {kwargs.get('ignore_patterns')}" - ) + :param config_name: Optional specific config name, otherwise returns all + metadata + :return: DataFrame with metadata values + :raises ValueError: If config_name is specified but not found - snapshot_kwargs = { - "repo_id": self.repo_id, - "repo_type": self.repo_type, - "token": self.token, - **kwargs, - } - - if ( - "local_dir" not in snapshot_kwargs - and "cache_dir" not in snapshot_kwargs - and self.cache_dir is not None - ): - snapshot_kwargs["cache_dir"] = str(self.cache_dir) - - self._normalize_patterns(snapshot_kwargs) - snapshot_path = snapshot_download(**snapshot_kwargs) - self.snapshot_path = Path(snapshot_path) - return self.snapshot_path - - def _update_available_tables(self) -> None: - """Update the logger with information about available tables.""" - if self.data_info.available_tables: - self.logger.info(f"Available tables: {', '.join(self.available_tables)}") - - def _extract_table_references(self, sql: str) -> set[str]: """ - Extract all table references from a SQL query. + # Get explicit metadata configurations + explicit_metadata_configs = self.dataset_card.get_metadata_configs() - Handles FROM clauses, JOINs, subqueries, and direct parquet file references. + # Get data configurations that have embedded metadata + # (metadata_fields specified) + embedded_metadata_configs = [ + config + for config in self.dataset_card.get_data_configs() + if config.metadata_fields + ] - :param sql: SQL query string - :return: Set of table names/file references found in the query + # Combine both types + all_metadata_sources = explicit_metadata_configs + embedded_metadata_configs - """ - table_refs = set() - - # Remove comments and normalize whitespace - sql_clean = re.sub(r"--.*?\n", " ", sql, flags=re.DOTALL) - sql_clean = re.sub(r"/\*.*?\*/", " ", sql_clean, flags=re.DOTALL) - sql_clean = re.sub(r"\s+", " ", sql_clean.strip()) - - # Pattern to match table references in FROM and JOIN clauses - # This handles: FROM table, FROM read_parquet('file.parquet'), JOIN table ON... - from_pattern = r""" - (?:FROM|JOIN)\s+ # FROM or JOIN keyword - (?: - read_parquet\s*\(\s*['"]([^'"]+)['"][\s)]* # read_parquet('file.parquet') - | - ([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*) # table.name or tablename - ) - (?:\s+(?:AS\s+)?[a-zA-Z_][a-zA-Z0-9_]*)? # Optional alias - (?:\s+ON\s+.*?(?=\s+(?:FROM|JOIN|WHERE|GROUP|ORDER|LIMIT|$)))? # Optional ON clause for JOINs - """ + if not all_metadata_sources: + # Return empty DataFrame + return pd.DataFrame() - for match in re.finditer(from_pattern, sql_clean, re.IGNORECASE | re.VERBOSE): - parquet_file = match.group(1) - table_name = match.group(2) - - if parquet_file: - # Extract just the filename without extension for parquet files - file_ref = Path(parquet_file).stem - table_refs.add(file_ref) - elif table_name: - # Clean table name (remove schema prefix if present) - clean_name = table_name.split(".")[-1] - table_refs.add(clean_name) - - # Also check for simple table name patterns in case the regex missed something - simple_pattern = r"\b(?:FROM|JOIN)\s+([a-zA-Z_][a-zA-Z0-9_]*)\b" - for match in re.finditer(simple_pattern, sql_clean, re.IGNORECASE): - table_name = match.group(1).lower() - # Filter out SQL keywords and function names - if table_name not in { - "select", - "where", - "group", - "order", - "having", - "limit", - "offset", - "union", - "intersect", - "except", - "read_parquet", - "read_csv", - "read_json", - }: - table_refs.add(table_name) - - return table_refs - - def _resolve_table_to_files(self, table_ref: str) -> list[str]: - """ - Resolve a table reference to specific files that need to be downloaded. + # Filter by config_name if specified + if config_name: + matching_configs = [ + c for c in all_metadata_sources if c.config_name == config_name + ] + if not matching_configs: + available_names = [c.config_name for c in all_metadata_sources] + raise ValueError( + f"Config '{config_name}' not found. " + f"Available metadata configs: {available_names}" + ) + configs_to_process = matching_configs + else: + configs_to_process = all_metadata_sources - :param table_ref: Table name or file reference from SQL - :return: List of file paths/patterns needed for this table + # Process each configuration and collect DataFrames + dataframes = [] + for config in configs_to_process: + # Ensure the data/metadata is loaded + config_result = self._get_metadata_for_config(config) - """ - return self.data_info.resolve_table_to_files(table_ref) - - def _ensure_tables_available(self, sql: str) -> set[str]: - """ - Ensure all tables referenced in SQL are available, downloading if necessary. + if not config_result.get("success", False): + self.logger.warning( + f"Failed to load data for config {config.config_name}" + ) + continue - :param sql: SQL query string - :return: Set of table names that were processed + table_name = config_result.get("table_name") + if not table_name: + self.logger.warning(f"No table name for config {config.config_name}") + continue - """ - table_refs = self._extract_table_references(sql) - processed_tables = set() - - for table_ref in table_refs: - if self.data_info.has_table(table_ref): - # Table is already known from dataset card - if not self.data_info.is_table_downloaded(table_ref): - self._ensure_dataset_loaded(table_ref, sql) - processed_tables.add(table_ref) - else: - # Try to discover as standalone file - if self._try_discover_standalone_table(table_ref): - processed_tables.add(table_ref) + try: + if config in explicit_metadata_configs: + # Explicit metadata: return all rows from metadata table + sql = f"SELECT * FROM {table_name}" else: - # File doesn't exist locally, try to download it - files_needed = self._resolve_table_to_files(table_ref) - if self._try_download_specific_files(files_needed, table_ref): - processed_tables.add(table_ref) - else: - self.logger.warning( - f"Could not locate or download table: {table_ref}" + # Embedded metadata: return distinct combinations of metadata fields + if config.metadata_fields is None: + raise ValueError( + f"Config {config.config_name} has no metadata fields" ) - - return processed_tables - - def _try_download_specific_files(self, files: list[str], table_name: str) -> bool: - """ - Attempt to download specific files for a table. - - :param files: List of file paths/patterns to download - :param table_name: Name of the table these files represent - :return: True if download was successful - - """ - try: - # Try downloading specific files - for file_path in files: - try: - # First check if file exists in repo - downloaded_file_path = self._download_single_file( - file_path, dry_run=False + fields = ", ".join(config.metadata_fields) + where_clauses = " AND ".join( + [f"{field} IS NOT NULL" for field in config.metadata_fields] ) - if downloaded_file_path and downloaded_file_path.exists(): - # Register the file as a table if it's a parquet file - if file_path.endswith(".parquet"): - self._register_parquet_as_table( - downloaded_file_path, table_name - ) - return True - except Exception as e: - self.logger.debug(f"Failed to download {file_path}: {e}") - continue - - # If individual file downloads failed, try pattern-based download - if files: - try: - self.download(allow_patterns=files, force_full_download=False) - # After download, try to discover the table again - return self._try_discover_standalone_table(table_name) - except RepoTooLargeError: - self.logger.error( - f"Repository too large to download files for table: {table_name}" - ) - return False - except Exception as e: - self.logger.error( - f"Failed to download files for table {table_name}: {e}" - ) - return False - - return False - except Exception as e: - self.logger.error(f"Error downloading files for table {table_name}: {e}") - return False - - def _register_parquet_as_table(self, parquet_path: Path, table_name: str) -> None: - """Register a parquet file directly as a DuckDB table.""" - create_view_sql = f""" - CREATE OR REPLACE VIEW {table_name} AS - SELECT * FROM read_parquet('{parquet_path}') - """ - self._duckdb_conn.execute(create_view_sql) + sql = f""" + SELECT DISTINCT {fields}, COUNT(*) as count + FROM {table_name} + WHERE {where_clauses} + GROUP BY {fields} + ORDER BY count DESC + """ - # Add to datasets registry via DataInfo - self.data_info.add_standalone_table(table_name, parquet_path, downloaded=True) + df = self.duckdb_conn.execute(sql).fetchdf() - def _try_discover_standalone_table(self, table_name: str) -> bool: - """ - Try to discover a standalone parquet file as a table. - - :param table_name: The name of the table to discover - :return: True if the table was discovered and registered - - """ - if not self.snapshot_path: - return False - - # Look for parquet file with matching name - parquet_file = self.snapshot_path / f"{table_name}.parquet" - if parquet_file.exists(): - # Register the standalone parquet file with DataInfo - self.data_info.add_standalone_table( - table_name, parquet_file, downloaded=True - ) - - # Register directly with DuckDB - create_view_sql = f""" - CREATE OR REPLACE VIEW {table_name} AS - SELECT * FROM read_parquet('{parquet_file}') - """ - self._duckdb_conn.execute(create_view_sql) - self.logger.debug( - f"Registered standalone parquet file as table: {table_name}" - ) - return True - - return False - - def _ensure_dataset_loaded( - self, table_name: str, sql: str | None = None - ) -> Dataset | DatasetDict | None: - """ - Ensure a dataset is loaded, with intelligent partition downloading. + # Add config source column if multiple configs + if len(configs_to_process) > 1: + df["config_name"] = config.config_name - :param table_name: Name of the dataset configuration - :param sql: Optional SQL query to determine required partitions - :return: The loaded dataset (None for partitioned datasets that are loaded - directly into DuckDB) + dataframes.append(df) - """ - if not self.data_info.has_table(table_name): - # Try to discover the table as a standalone parquet file - if self._try_discover_standalone_table(table_name): - self.logger.info(f"Discovered standalone table: {table_name}") - else: - raise ValueError( - f"Table '{table_name}' not found. " - f"Available tables: {self.available_tables}" + except Exception as e: + self.logger.error( + f"Error querying metadata for {config.config_name}: {e}" ) + continue - dataset_info = self.data_info.get_table_info(table_name) - - # Check if we need to download partitions - if dataset_info and dataset_info.is_partitioned and sql: - required_partitions = self._get_required_partitions(sql, table_name) - - if required_partitions: - # Check if we have these partitions cached - cached_partitions = self._partition_cache.get(table_name, set()) - missing_partitions = required_partitions - cached_partitions - - if missing_partitions: - self.logger.info( - f"Downloading missing partitions: {missing_partitions}" - ) - self._download_partitions(table_name, missing_partitions) - self._partition_cache.setdefault(table_name, set()).update( - missing_partitions - ) + # Combine all DataFrames + if not dataframes: + return pd.DataFrame() + elif len(dataframes) == 1: + return dataframes[0] + else: + return pd.concat(dataframes, ignore_index=True) - # Check if dataset is already loaded - if table_name in self._loaded_datasets: - return self._loaded_datasets[table_name] + def set_filter(self, config_name: str, **kwargs) -> None: + """ + Set simple filters using keyword arguments. - # Check if standalone table is already registered - if dataset_info and dataset_info.downloaded: - return None # Standalone tables are registered directly with DuckDB + Converts keyword arguments to SQL WHERE clause and stores + for automatic application. - # Download if needed - if not self.snapshot_path: - if dataset_info and dataset_info.is_partitioned and sql: - required_partitions = self._get_required_partitions(sql, table_name) - self._download_partitions(table_name, required_partitions) - else: - self.logger.info(f"Downloading dataset for table '{table_name}'...") - self.download( - auto_download_threshold_mb=self.auto_download_threshold_mb - ) + :param config_name: Configuration name to apply filters to + :param kwargs: Filter conditions as keyword arguments + (e.g., time=15, mechanism="ZEV") - # Load the dataset - try: - self.logger.info(f"Loading dataset configuration '{table_name}'...") - - if dataset_info and dataset_info.is_partitioned: - # For partitioned datasets, load directly into DuckDB - self._load_partitioned_dataset(table_name) - # Mark as downloaded but don't store in _loaded_datasets since it's in DuckDB - self.data_info.mark_table_downloaded(table_name, True) - return None - else: - # Standard dataset loading - dataset = load_dataset( - str(self.snapshot_path), name=table_name, keep_in_memory=False - ) - self._loaded_datasets[table_name] = dataset - self._register_dataset_with_duckdb(table_name, dataset) - return dataset - except Exception as e: - self.logger.error(f"Failed to load dataset '{table_name}': {e}") - raise + Example: + api.set_filter("hackett_2020", time=15, mechanism="ZEV", restriction="P") + # Equivalent to: WHERE time = 15 AND mechanism = 'ZEV' AND restriction = 'P' - def _load_partitioned_dataset(self, table_name: str) -> None: """ - Load a partitioned dataset directly into DuckDB without using Hugging Face - datasets. - - This is more efficient for partitioned datasets like genome_map. + if not kwargs: + # If no kwargs provided, clear the filter + self.clear_filter(config_name) + return - """ - # Find parquet files in the snapshot - if not self.snapshot_path: - raise ValueError("No snapshot path available") - - # Look for parquet files matching the dataset pattern - parquet_files: list[Path] = [] - for file_info in self.data_info.get_table_data_files(table_name): - pattern = file_info["path"] - if "*" in pattern: - # Convert pattern to actual file search - search_pattern = pattern.replace("*", "**") - parquet_files.extend(self.snapshot_path.glob(search_pattern)) - - if not parquet_files: - # Fallback: find all parquet files - parquet_files = list(self.snapshot_path.rglob("*.parquet")) - - if parquet_files: - # Register parquet files directly with DuckDB - file_paths = [str(f) for f in parquet_files] - self.logger.info( - f"Registering {len(file_paths)} parquet files for {table_name}" - ) - - # Create a view that reads from all parquet files - files_str = "', '".join(file_paths) - create_view_sql = f""" - CREATE OR REPLACE VIEW {table_name} AS - SELECT * FROM read_parquet(['{files_str}']) - """ - self._duckdb_conn.execute(create_view_sql) - self.logger.debug( - f"Created view '{table_name}' from {len(file_paths)} parquet files" - ) - else: - raise ValueError( - f"No parquet files found for partitioned dataset {table_name}" - ) - - def _register_dataset_with_duckdb( - self, table_name: str, dataset: Dataset | DatasetDict - ) -> None: - """Register a standard dataset with DuckDB for SQL querying.""" - try: - if isinstance(dataset, DatasetDict): - for split_name, split_dataset in dataset.items(): - view_name = ( - f"{table_name}_{split_name}" - if split_name != "train" - else table_name - ) - df = split_dataset.to_pandas() - self._duckdb_conn.register(view_name, df) - self.logger.debug( - f"Registered view '{view_name}' with {len(df)} rows" - ) + # Convert kwargs to SQL WHERE clause + conditions = [] + for key, value in kwargs.items(): + if isinstance(value, str): + # String values need quotes + conditions.append(f"{key} = '{value}'") + elif value is None: + # Handle NULL values + conditions.append(f"{key} IS NULL") else: - df = dataset.to_pandas() - self._duckdb_conn.register(table_name, df) - self.logger.debug( - f"Registered table '{table_name}' with {len(df)} rows" - ) - except Exception as e: - self.logger.error( - f"Failed to register dataset '{table_name}' with DuckDB: {e}" - ) - raise + # Numeric/boolean values + conditions.append(f"{key} = {value}") - def query(self, sql: str, table_name: str | None = None) -> pd.DataFrame: - """ - Execute a SQL query with intelligent table discovery and loading. - - :param sql: SQL query string - :param table_name: Optional table name to ensure is loaded (legacy parameter) - :return: Query results as a pandas DataFrame + where_clause = " AND ".join(conditions) + self._table_filters[config_name] = where_clause + self.logger.info(f"Set filter for {config_name}: {where_clause}") + def set_sql_filter(self, config_name: str, sql_where: str) -> None: """ - # Use intelligent table discovery to ensure all referenced tables are available - processed_tables = self._ensure_tables_available(sql) - - # Legacy support: if table_name is provided, ensure it's also available - if table_name and table_name not in processed_tables: - try: - self._ensure_dataset_loaded(table_name, sql) - except ValueError: - # Try to discover as standalone table - if not self._try_discover_standalone_table(table_name): - self.logger.warning(f"Could not load specified table: {table_name}") - - # Apply table filters - modified_sql = self._apply_table_filters(sql) - - try: - result = self._duckdb_conn.execute(modified_sql).fetchdf() - self.logger.debug(f"Query returned {len(result)} rows") - return result - except Exception as e: - self.logger.error(f"Query failed: {e}") - if modified_sql != sql: - self.logger.debug(f"Original query: {sql}") - self.logger.debug(f"Modified query: {modified_sql}") - - # If query failed, provide helpful information about available tables - if self.data_info.available_tables: - self.logger.info( - f"Available tables: {', '.join(self.data_info.available_tables)}" - ) - - raise - - def _apply_table_filters(self, sql: str) -> str: - """Apply table filters to a SQL query.""" - if not self._table_filters: - return sql - - modified_sql = sql - for table_name, filter_condition in self._table_filters.items(): - pattern = rf"\b{re.escape(table_name)}\b" - replacement = f"(SELECT * FROM {table_name} WHERE {filter_condition})" - - if re.search(pattern, modified_sql, re.IGNORECASE): - if not re.search( - rf"\(SELECT.*FROM\s+{re.escape(table_name)}\s+WHERE", - modified_sql, - re.IGNORECASE, - ): - modified_sql = re.sub( - pattern, replacement, modified_sql, flags=re.IGNORECASE - ) - - return modified_sql - - def get_table_filter(self, table_name: str) -> str | None: - """Get the current filter for a table.""" - return self._table_filters.get(table_name) - - def set_table_filter(self, table_name: str, filter_condition: str) -> None: - """Set a filter condition for a table.""" - self._table_filters[table_name] = filter_condition - self.logger.debug(f"Set filter for table '{table_name}': {filter_condition}") - - def remove_table_filter(self, table_name: str) -> None: - """Remove any filter condition for a table.""" - removed_filter = self._table_filters.pop(table_name, None) - if removed_filter: - self.logger.debug( - f"Removed filter for table '{table_name}': {removed_filter}" - ) + Set complex filters using SQL WHERE clause. - # Standard methods - def describe_table(self, table_name: str) -> pd.DataFrame: - """Get information about a table's structure.""" - self._ensure_dataset_loaded(table_name) - return self.query(f"DESCRIBE {table_name}") + Stores raw SQL WHERE clause for automatic application to queries. - def sample(self, table_name: str, n: int = 5) -> pd.DataFrame: - """Get a sample of rows from a table.""" - return self.query(f"SELECT * FROM {table_name} LIMIT {n}", table_name) + :param config_name: Configuration name to apply filters to + :param sql_where: SQL WHERE clause (without the 'WHERE' keyword) - def count(self, table_name: str) -> int: - """Get the number of rows in a table.""" - result = self.query(f"SELECT COUNT(*) as count FROM {table_name}", table_name) - return result.iloc[0]["count"] + Example: + api.set_sql_filter("hackett_2020", "time IN (15, 30) AND mechanism = 'ZEV'") - def get_columns(self, table_name: str) -> list[str]: - """Get column names for a table.""" - if not self.data_info.has_table(table_name): - raise ValueError(f"Table '{table_name}' not found") - return list(self.data_info.get_table_features(table_name).keys()) - - # ============== Cache Management Methods ============== - - def get_cache_info(self) -> dict[str, Any]: """ - Get comprehensive cache information including current repo details. + if not sql_where.strip(): + self.clear_filter(config_name) + return - :return: Dictionary with cache stats, repo info, and recommendations + self._table_filters[config_name] = sql_where.strip() + self.logger.info(f"Set SQL filter for {config_name}: {sql_where}") + def clear_filter(self, config_name: str) -> None: """ - if not self._cache_manager: - self.logger.debug("Cache management is disabled") - return {"cache_management": "disabled"} - - self.logger.info("Retrieving comprehensive cache information") + Remove all filters for the specified configuration. - from huggingface_hub import scan_cache_dir - - try: - cache_info = scan_cache_dir() - self.logger.debug( - f"Scanned cache directory: {cache_info.size_on_disk_str} in {len(cache_info.repos)} repos" - ) - except Exception as e: - self.logger.error(f"Failed to scan cache directory: {e}") - return {"error": f"Failed to scan cache: {e}"} - - # Find current repo in cache - current_repo_info = None - for repo in cache_info.repos: - if ( - repo.repo_id == self.repo_id - and repo.repo_type.lower() == self.repo_type - ): - current_repo_info = repo - break - - result = { - "cache_management": "enabled", - "cache_directory": str(self.cache_dir), - "total_cache_size": cache_info.size_on_disk_str, - "total_cache_size_bytes": cache_info.size_on_disk, - "total_repos_cached": len(cache_info.repos), - "current_repo": { - "repo_id": self.repo_id, - "repo_type": self.repo_type, - "cached": current_repo_info is not None, - "cache_info": None, - }, - "cache_policies": { - "auto_cleanup": getattr(self, "_cache_auto_cleanup", False), - "max_age_days": getattr(self, "_cache_max_age_days", 30), - "max_size": getattr(self, "_cache_max_size", "10GB"), - }, - "recommendations": [], - } - - if current_repo_info: - result["current_repo"]["cache_info"] = { - "size_on_disk": current_repo_info.size_on_disk_str, - "size_bytes": current_repo_info.size_on_disk, - "nb_files": current_repo_info.nb_files, - "last_accessed": current_repo_info.last_accessed, - "last_modified": current_repo_info.last_modified, - "revisions_count": len(current_repo_info.revisions), - "revisions": [ - { - "commit_hash": rev.commit_hash[:8], - "size_on_disk": rev.size_on_disk_str, - "last_modified": rev.last_modified, - "files_count": len(rev.files), - } - for rev in sorted( - current_repo_info.revisions, - key=lambda r: r.last_modified, - reverse=True, - ) - ], - } + :param config_name: Configuration name to clear filters for - # Add recommendations with logging - max_size_bytes = self._cache_manager._parse_size_string( - getattr(self, "_cache_max_size", "10GB") - ) - if cache_info.size_on_disk > max_size_bytes: - recommendation = ( - f"Cache size ({cache_info.size_on_disk_str}) exceeds configured limit " - f"({getattr(self, '_cache_max_size', '10GB')}). Consider running cache cleanup." - ) - result["recommendations"].append(recommendation) - self.logger.warning(f"Cache size warning: {recommendation}") - - if len(cache_info.repos) > 50: # Arbitrary threshold - recommendation = ( - f"Large number of cached repos ({len(cache_info.repos)}). " - "Consider cleaning unused repositories." - ) - result["recommendations"].append(recommendation) - self.logger.info(f"Cache optimization suggestion: {recommendation}") - - if current_repo_info: - self.logger.info( - f"Current repo {self.repo_id} found in cache: " - f"{current_repo_info.size_on_disk_str}, {len(current_repo_info.revisions)} revisions" - ) - else: - self.logger.debug(f"Current repo {self.repo_id} not found in cache") - - self.logger.info( - f"Cache info summary: {cache_info.size_on_disk_str} total, " - f"{len(cache_info.repos)} repos, {len(result['recommendations'])} recommendations" - ) - - return result - - def get_repo_cache_info(self, repo_id: str | None = None) -> dict[str, Any]: """ - Get detailed cache information for a specific repository. - - :param repo_id: Repository ID (defaults to current repo) - :return: Dictionary with detailed repo cache information + if config_name in self._table_filters: + del self._table_filters[config_name] + self.logger.info(f"Cleared filter for {config_name}") + def get_current_filter(self, config_name: str) -> str | None: """ - if not self._cache_manager: - self.logger.debug("Cache management is disabled") - return {"cache_management": "disabled"} + Get the current filter for the specified configuration. - target_repo_id = repo_id or self.repo_id - self.logger.info( - f"Retrieving detailed cache information for repo: {target_repo_id}" - ) - - from huggingface_hub import scan_cache_dir - - try: - cache_info = scan_cache_dir() - except Exception as e: - self.logger.error(f"Failed to scan cache directory: {e}") - return {"error": f"Failed to scan cache: {e}"} - - # Find the specified repo - target_repo = None - for repo in cache_info.repos: - if repo.repo_id == target_repo_id: - target_repo = repo - break - - if not target_repo: - self.logger.info(f"Repository {target_repo_id} not found in cache") - return { - "repo_id": target_repo_id, - "cached": False, - "message": "Repository not found in cache", - } - - return { - "repo_id": target_repo_id, - "repo_type": target_repo.repo_type, - "cached": True, - "size_on_disk": target_repo.size_on_disk_str, - "size_bytes": target_repo.size_on_disk, - "files_count": target_repo.nb_files, - "last_accessed": target_repo.last_accessed, - "last_modified": target_repo.last_modified, - "revisions_count": len(target_repo.revisions), - "revisions": [ - { - "commit_hash": rev.commit_hash, - "short_hash": rev.commit_hash[:8], - "size_on_disk": rev.size_on_disk_str, - "size_bytes": rev.size_on_disk, - "last_modified": rev.last_modified, - "files_count": len(rev.files), - "files": [ - { - "name": file.file_name, - "size": file.size_on_disk, - "blob_last_accessed": file.blob_last_accessed, - "blob_last_modified": file.blob_last_modified, - } - for file in sorted(rev.files, key=lambda f: f.file_name) - ], - } - for rev in sorted( - target_repo.revisions, key=lambda r: r.last_modified, reverse=True - ) - ], - } - - self.logger.info( - f"Found repo {target_repo_id} in cache: {target_repo.size_on_disk_str}, " - f"{target_repo.nb_files} files, {len(target_repo.revisions)} revisions" - ) + :param config_name: Configuration name to get filter for + :return: Current SQL WHERE clause or None if no filter set - return result - - def check_cached_files(self, table_name: str | None = None) -> dict[str, Any]: """ - Check which files for current dataset/table are cached locally. - - :param table_name: Specific table to check (defaults to all tables) - :return: Dictionary with file cache status + return self._table_filters.get(config_name) + def query(self, sql: str, config_name: str) -> pd.DataFrame: """ - if not self._cache_manager: - self.logger.debug("Cache management is disabled") - return {"cache_management": "disabled"} + Execute SQL query with automatic filter application. - if table_name: - self.logger.info(f"Checking cache status for table: {table_name}") - else: - self.logger.info(f"Checking cache status for all tables in {self.repo_id}") - - from huggingface_hub import _CACHED_NO_EXIST, try_to_load_from_cache - - result = { - "repo_id": self.repo_id, - "cache_directory": str(self.cache_dir), - "tables": {}, - } - - # Check specific table or all tables - tables_to_check = [table_name] if table_name else self.available_tables - self.logger.debug(f"Checking {len(tables_to_check)} tables: {tables_to_check}") - - for table in tables_to_check: - if not self.data_info.has_table(table): - self.logger.warning(f"Table {table} not found in dataset configuration") - result["tables"][table] = { - "exists": False, - "message": "Table not found in dataset configuration", - } - continue - - table_config = self.data_info.get_table_info(table) - if not table_config: - continue - - file_status = {} - for file_info in table_config.data_files: - file_path = file_info.path + Loads the specified configuration, applies any stored filters, + and executes the query. - # Check if file is cached - cached_path = try_to_load_from_cache( - repo_id=self.repo_id, - filename=file_path, - repo_type=self.repo_type, - ) + :param sql: SQL query to execute + :param config_name: Configuration name to query (table will be loaded if needed) + :return: DataFrame with query results + :raises ValueError: If config_name not found or query fails - if isinstance(cached_path, str): - # File is cached - file_status[file_path] = { - "cached": True, - "local_path": cached_path, - "split": file_info.split, - } - elif cached_path is _CACHED_NO_EXIST: - # Non-existence is cached (file doesn't exist on Hub) - file_status[file_path] = { - "cached": False, - "exists_on_hub": False, - "split": file_info.split, - } - else: - # File is not cached - file_status[file_path] = { - "cached": False, - "exists_on_hub": True, # Assumed - "split": file_info.split, - } - - cached_count = sum( - 1 for f in file_status.values() if f.get("cached", False) - ) - total_files = len(table_config.data_files) - - result["tables"][table] = { - "exists": True, - "files": file_status, - "total_files": total_files, - "cached_files": cached_count, - "is_partitioned": table_config.is_partitioned, - } - - self.logger.info( - f"Table {table}: {cached_count}/{total_files} files cached " - f"({cached_count/total_files*100:.1f}%)" - ) - - total_tables = len( - [t for t in result["tables"].values() if t.get("exists", False)] - ) - self.logger.info(f"Cache check complete: processed {total_tables} tables") - - return result - - def cleanup_cache( - self, - strategy: str = "auto", - max_age_days: int | None = None, - target_size: str | None = None, - keep_current_repo: bool = True, - dry_run: bool = True, - ) -> dict[str, Any]: - """ - Clean up the HuggingFace Hub cache using various strategies. - - :param strategy: Cleanup strategy - "auto", "age", "size", "unused" - :param max_age_days: Maximum age for cache entries (for "age" strategy) - :param target_size: Target cache size (for "size" strategy, e.g., "5GB") - :param keep_current_repo: Whether to preserve current repo from cleanup - :param dry_run: If True, show what would be deleted without executing - :return: Dictionary with cleanup results and summary + Example: + api.set_filter("hackett_2020", time=15, mechanism="ZEV") + df = api.query("SELECT regulator_locus_tag, target_locus_tag + FROM hackett_2020", "hackett_2020") + # Automatically applies: WHERE time = 15 AND mechanism = 'ZEV' """ - if not self._cache_manager: - self.logger.warning("Cache management is disabled, cannot perform cleanup") - return { - "cache_management": "disabled", - "message": "Cache management is not enabled", - } - - # Use instance defaults if not specified - max_age_days = max_age_days or getattr(self, "_cache_max_age_days", 30) - target_size = target_size or getattr(self, "_cache_max_size", "10GB") - - self.logger.info( - f"Starting cache cleanup: strategy={strategy}, max_age={max_age_days}d, " - f"target_size={target_size}, dry_run={dry_run}, keep_current_repo={keep_current_repo}" - ) - - result = { - "strategy": strategy, - "dry_run": dry_run, - "keep_current_repo": keep_current_repo, - "strategies_executed": [], - "total_freed_bytes": 0, - "total_freed_str": "0B", - } - - try: - if strategy == "auto": - # Multi-strategy automated cleanup - self.logger.info("Executing automated multi-strategy cleanup") - strategies = self._cache_manager.auto_clean_cache( - max_age_days=max_age_days, - max_total_size=target_size, - keep_latest_per_repo=2, - dry_run=dry_run, - ) - total_freed = sum(s.expected_freed_size for s in strategies) - result["strategies_executed"] = [ - { - "type": "auto_cleanup", - "freed_bytes": total_freed, - "freed_str": self._cache_manager._format_bytes(total_freed), - "details": f"Executed {len(strategies)} cleanup strategies", - } - ] - result["total_freed_bytes"] = total_freed - self.logger.info( - f"Auto cleanup {'would free' if dry_run else 'freed'} " - f"{self._cache_manager._format_bytes(total_freed)} using {len(strategies)} strategies" - ) - - elif strategy == "age": - # Age-based cleanup - self.logger.info( - f"Executing age-based cleanup (older than {max_age_days} days)" - ) - delete_strategy = self._cache_manager.clean_cache_by_age( - max_age_days=max_age_days, dry_run=dry_run - ) - result["strategies_executed"] = [ - { - "type": "age_based", - "freed_bytes": delete_strategy.expected_freed_size, - "freed_str": delete_strategy.expected_freed_size_str, - "max_age_days": max_age_days, - } - ] - result["total_freed_bytes"] = delete_strategy.expected_freed_size - self.logger.info( - f"Age-based cleanup {'would free' if dry_run else 'freed'} " - f"{delete_strategy.expected_freed_size_str}" - ) - - elif strategy == "size": - # Size-based cleanup - self.logger.info( - f"Executing size-based cleanup (target: {target_size})" - ) - delete_strategy = self._cache_manager.clean_cache_by_size( - target_size=target_size, strategy="oldest_first", dry_run=dry_run - ) - result["strategies_executed"] = [ - { - "type": "size_based", - "freed_bytes": delete_strategy.expected_freed_size, - "freed_str": delete_strategy.expected_freed_size_str, - "target_size": target_size, - } - ] - result["total_freed_bytes"] = delete_strategy.expected_freed_size - self.logger.info( - f"Size-based cleanup {'would free' if dry_run else 'freed'} " - f"{delete_strategy.expected_freed_size_str} to reach target {target_size}" - ) - - elif strategy == "unused": - # Clean unused revisions - self.logger.info( - "Executing unused revisions cleanup (keeping 2 latest per repo)" - ) - delete_strategy = self._cache_manager.clean_unused_revisions( - keep_latest=2, dry_run=dry_run - ) - result["strategies_executed"] = [ - { - "type": "unused_revisions", - "freed_bytes": delete_strategy.expected_freed_size, - "freed_str": delete_strategy.expected_freed_size_str, - "keep_latest": 2, - } - ] - result["total_freed_bytes"] = delete_strategy.expected_freed_size - self.logger.info( - f"Unused revisions cleanup {'would free' if dry_run else 'freed'} " - f"{delete_strategy.expected_freed_size_str}" - ) - - else: - self.logger.error(f"Unknown cleanup strategy: {strategy}") - return { - "error": f"Unknown cleanup strategy: {strategy}", - "available_strategies": ["auto", "age", "size", "unused"], - } - - result["total_freed_str"] = self._cache_manager._format_bytes( - result["total_freed_bytes"] - ) - - # Add current repo protection info - if keep_current_repo: - result["current_repo_protected"] = { - "repo_id": self.repo_id, - "message": "Current repository was protected from cleanup", - } - self.logger.debug( - f"Current repository {self.repo_id} protected from cleanup" - ) - - # Final summary logging - self.logger.info( - f"Cache cleanup completed: {strategy} strategy, " - f"{'would free' if dry_run else 'freed'} {result['total_freed_str']}" + # Validate config exists + if config_name not in [c.config_name for c in self.dataset_card.configs]: + available_configs = [c.config_name for c in self.dataset_card.configs] + raise ValueError( + f"Config '{config_name}' not found. " + f"Available configs: {available_configs}" ) - return result + # Load the configuration data + config = self.dataset_card.get_config_by_name(config_name) + if not config: + raise ValueError(f"Could not retrieve config '{config_name}'") - except Exception as e: - self.logger.error(f"Cache cleanup failed: {e}") - return { - "error": f"Cache cleanup failed: {str(e)}", - "strategy": strategy, - "dry_run": dry_run, - } - - def auto_cleanup_cache_if_needed(self) -> dict[str, Any]: - """ - Automatically clean cache if configured policies are exceeded. - - This method is called automatically during operations if auto_cleanup is - enabled. - - :return: Dictionary with cleanup results or None if no cleanup was needed - - """ - if not self._cache_manager or not getattr(self, "_cache_auto_cleanup", False): - self.logger.debug("Auto-cleanup is disabled") - return {"auto_cleanup": "disabled"} - - self.logger.debug("Checking if auto-cleanup is needed") - - from huggingface_hub import scan_cache_dir - - try: - cache_info = scan_cache_dir() - except Exception as e: - self.logger.error(f"Failed to scan cache for auto-cleanup: {e}") - return {"auto_cleanup": "error", "error": str(e)} - max_size_bytes = self._cache_manager._parse_size_string( - getattr(self, "_cache_max_size", "10GB") - ) - - cleanup_needed = cache_info.size_on_disk > max_size_bytes - - if not cleanup_needed: - self.logger.debug( - f"Auto-cleanup not needed: {cache_info.size_on_disk_str} " - f"< {getattr(self, '_cache_max_size', '10GB')}" + config_result = self._get_metadata_for_config(config) + if not config_result.get("success", False): + raise ValueError( + f"Failed to load data for config '{config_name}': " + f"{config_result.get('message', 'Unknown error')}" ) - return { - "auto_cleanup": "enabled", - "cleanup_needed": False, - "current_size": cache_info.size_on_disk_str, - "max_size": getattr(self, "_cache_max_size", "10GB"), - } - - self.logger.info( - f"Auto-cleanup triggered: cache size ({cache_info.size_on_disk_str}) " - f"exceeds limit ({getattr(self, '_cache_max_size', '10GB')})" - ) - cleanup_result = self.cleanup_cache( - strategy="auto", - dry_run=False, # Execute cleanup - keep_current_repo=True, - ) - - cleanup_result.update( - { - "auto_cleanup": "enabled", - "triggered": True, - "reason": "cache_size_exceeded", - "previous_size": cache_info.size_on_disk_str, - } - ) + table_name = config_result.get("table_name") + if not table_name: + raise ValueError(f"No table available for config '{config_name}'") - return cleanup_result - - def suggest_cache_cleanup(self) -> dict[str, Any]: - """ - Analyze cache and provide cleanup recommendations without executing. + # Replace config name with actual table name in SQL for user convenience + sql_with_table = sql.replace(config_name, table_name) - :return: Dictionary with analysis and recommendations + # Apply stored filters + final_sql = self._apply_filter_to_sql(sql_with_table, config_name) - """ - if not self._cache_manager: - return {"cache_management": "disabled"} - - from datetime import datetime, timedelta - - from huggingface_hub import scan_cache_dir - - cache_info = scan_cache_dir() - suggestions = { - "cache_analysis": { - "total_size": cache_info.size_on_disk_str, - "total_repos": len(cache_info.repos), - "recommendations": [], - }, - "cleanup_strategies": {}, - } - - # Analyze size - max_size_bytes = self._cache_manager._parse_size_string( - getattr(self, "_cache_max_size", "10GB") - ) - if cache_info.size_on_disk > max_size_bytes: - suggestions["cache_analysis"]["recommendations"].append( - { - "type": "size_exceeded", - "message": f"Cache size ({cache_info.size_on_disk_str}) exceeds " - f"configured limit ({getattr(self, '_cache_max_size', '10GB')})", - "suggested_action": "Run cleanup_cache(strategy='size')", - } - ) - - # Analyze age - cutoff_date = datetime.now() - timedelta( - days=getattr(self, "_cache_max_age_days", 30) - ) - old_repos = [] - for repo in cache_info.repos: - for revision in repo.revisions: - if datetime.fromtimestamp(revision.last_modified) < cutoff_date: - old_repos.append((repo.repo_id, revision.commit_hash[:8])) - - if old_repos: - suggestions["cache_analysis"]["recommendations"].append( - { - "type": "old_revisions", - "message": f"Found {len(old_repos)} old revisions " - f"(older than {getattr(self, '_cache_max_age_days', 30)} days)", - "suggested_action": "Run cleanup_cache(strategy='age')", - } - ) - - # Analyze unused revisions - repos_with_multiple_revisions = [ - repo for repo in cache_info.repos if len(repo.revisions) > 2 - ] - if repos_with_multiple_revisions: - suggestions["cache_analysis"]["recommendations"].append( - { - "type": "multiple_revisions", - "message": f"Found {len(repos_with_multiple_revisions)} repos " - "with multiple cached revisions", - "suggested_action": "Run cleanup_cache(strategy='unused')", - } - ) - - # Dry run different strategies to show potential savings try: - age_cleanup = self._cache_manager.clean_cache_by_age( - max_age_days=getattr(self, "_cache_max_age_days", 30), dry_run=True - ) - suggestions["cleanup_strategies"]["age_based"] = { - "description": f"Remove revisions older than {getattr(self, '_cache_max_age_days', 30)} days", - "potential_savings": age_cleanup.expected_freed_size_str, - "potential_savings_bytes": age_cleanup.expected_freed_size, - } - - size_cleanup = self._cache_manager.clean_cache_by_size( - target_size=getattr(self, "_cache_max_size", "10GB"), - strategy="oldest_first", - dry_run=True, - ) - suggestions["cleanup_strategies"]["size_based"] = { - "description": f"Reduce cache to {getattr(self, '_cache_max_size', '10GB')}", - "potential_savings": size_cleanup.expected_freed_size_str, - "potential_savings_bytes": size_cleanup.expected_freed_size, - } - - unused_cleanup = self._cache_manager.clean_unused_revisions( - keep_latest=2, dry_run=True - ) - suggestions["cleanup_strategies"]["unused_revisions"] = { - "description": "Remove unused revisions (keep 2 latest per repo)", - "potential_savings": unused_cleanup.expected_freed_size_str, - "potential_savings_bytes": unused_cleanup.expected_freed_size, - } - + return self.duckdb_conn.execute(final_sql).fetchdf() except Exception as e: - suggestions["error"] = f"Failed to analyze cleanup strategies: {e}" - - return suggestions + self.logger.error(f"Query execution failed: {e}") + self.logger.error(f"Final SQL: {final_sql}") + raise ValueError(f"Query execution failed: {e}") from e - def warm_cache( - self, - repo_ids: list[str] | None = None, - tables: list[str] | None = None, - include_current_repo: bool = True, - dry_run: bool = False, - ) -> dict[str, Any]: + def _apply_filter_to_sql(self, sql: str, config_name: str) -> str: """ - Pre-download (warm) cache with specified repositories or tables. + Apply stored filters to SQL query. - :param repo_ids: List of repository IDs to pre-download - :param tables: List of table names from current repo to pre-download - :param include_current_repo: Whether to include current repo if repo_ids - specified - :param dry_run: If True, show what would be downloaded without executing - :return: Dictionary with warming results + Modifies the SQL query to include stored WHERE clause filters. - """ - if not self._cache_manager: - return {"cache_management": "disabled"} - - result = { - "cache_warming": "enabled", - "dry_run": dry_run, - "operations": [], - "total_downloaded": 0, - "errors": [], - } - - # Handle table-specific warming for current repo - if tables: - repo_result = { - "repo_id": self.repo_id, - "type": "table_specific", - "tables": {}, - "success": True, - } - - for table_name in tables: - try: - if not self.data_info.has_table(table_name): - repo_result["tables"][table_name] = { - "status": "error", - "message": "Table not found in dataset configuration", - } - continue - - if not dry_run: - # Download files for this table - self._ensure_dataset_loaded(table_name) - repo_result["tables"][table_name] = { - "status": "downloaded", - "message": "Table files cached successfully", - } - result["total_downloaded"] += 1 - else: - repo_result["tables"][table_name] = { - "status": "would_download", - "message": "Would download table files", - } - - except Exception as e: - error_msg = f"Failed to warm cache for table {table_name}: {e}" - repo_result["tables"][table_name] = { - "status": "error", - "message": str(e), - } - result["errors"].append(error_msg) - repo_result["success"] = False - - result["operations"].append(repo_result) - - # Handle repository-specific warming - if repo_ids or (not tables and include_current_repo): - target_repos = repo_ids or [] - if include_current_repo and self.repo_id not in target_repos: - target_repos.append(self.repo_id) - - for repo_id in target_repos: - repo_result = { - "repo_id": repo_id, - "type": "full_repo", - "success": True, - "message": "", - } - - try: - if not dry_run: - if repo_id == self.repo_id: - # Use current API instance for current repo - downloaded_path = self.download() - repo_result["message"] = ( - f"Repository cached at {downloaded_path}" - ) - else: - # Create temporary API instance for other repos - temp_api = HfQueryAPI( - repo_id=repo_id, - repo_type=self.repo_type, - token=self.token, - cache_dir=self.cache_dir, - enable_cache_management=False, # Avoid recursive cache management - ) - downloaded_path = temp_api.download() - repo_result["message"] = ( - f"Repository cached at {downloaded_path}" - ) - - result["total_downloaded"] += 1 - else: - repo_result["message"] = f"Would download repository {repo_id}" - - except Exception as e: - error_msg = f"Failed to warm cache for repo {repo_id}: {e}" - repo_result["success"] = False - repo_result["message"] = str(e) - result["errors"].append(error_msg) - - result["operations"].append(repo_result) - - if not repo_ids and not tables and not include_current_repo: - result["message"] = "No repositories or tables specified for cache warming" - - return result - - def verify_cache_integrity(self) -> dict[str, Any]: - """ - Verify integrity of cached files and detect corruption. - - :return: Dictionary with verification results + :param sql: Original SQL query + :param config_name: Configuration name to get filters for + :return: Modified SQL query with filters applied """ - if not self._cache_manager: - return {"cache_management": "disabled"} - - import os - - from huggingface_hub import scan_cache_dir - - cache_info = scan_cache_dir() - result = { - "cache_verification": "enabled", - "total_repos_scanned": len(cache_info.repos), - "issues_found": [], - "healthy_repos": [], - "summary": { - "healthy": 0, - "corrupted": 0, - "missing_files": 0, - "symlink_issues": 0, - }, - } - - for repo in cache_info.repos: - repo_issues = [] - - # Check if snapshots directory exists - if not repo.repo_path.exists(): - repo_issues.append( - { - "type": "missing_repo_directory", - "message": f"Repository directory does not exist: {repo.repo_path}", - } - ) - result["summary"]["corrupted"] += 1 - continue + if config_name not in self._table_filters: + return sql - # Check each revision - for revision in repo.revisions: - if not revision.snapshot_path.exists(): - repo_issues.append( - { - "type": "missing_snapshot", - "revision": revision.commit_hash[:8], - "message": f"Snapshot directory missing: {revision.snapshot_path}", - } - ) - continue - - # Check each file in the revision - for file_info in revision.files: - if not file_info.file_path.exists(): - repo_issues.append( - { - "type": "missing_file", - "revision": revision.commit_hash[:8], - "file": file_info.file_name, - "message": f"File missing: {file_info.file_path}", - } - ) - continue - - # Check if it's a symlink and target exists - if file_info.file_path.is_symlink(): - if not file_info.blob_path.exists(): - repo_issues.append( - { - "type": "broken_symlink", - "revision": revision.commit_hash[:8], - "file": file_info.file_name, - "message": f"Symlink target missing: {file_info.blob_path}", - } - ) - continue - - # Basic file size check - try: - actual_size = os.path.getsize(file_info.file_path) - if actual_size != file_info.size_on_disk: - repo_issues.append( - { - "type": "size_mismatch", - "revision": revision.commit_hash[:8], - "file": file_info.file_name, - "expected_size": file_info.size_on_disk, - "actual_size": actual_size, - "message": f"File size mismatch in {file_info.file_name}", - } - ) - except OSError as e: - repo_issues.append( - { - "type": "access_error", - "revision": revision.commit_hash[:8], - "file": file_info.file_name, - "message": f"Cannot access file: {e}", - } - ) + filter_clause = self._table_filters[config_name] + sql_upper = sql.upper() - # Categorize the repo - if repo_issues: - result["issues_found"].append( - { - "repo_id": repo.repo_id, - "repo_type": repo.repo_type, - "issues": repo_issues, - "issues_count": len(repo_issues), - } - ) - - # Update summary - if any( - issue["type"] in ["missing_repo_directory", "missing_snapshot"] - for issue in repo_issues - ): - result["summary"]["corrupted"] += 1 - elif any(issue["type"] == "missing_file" for issue in repo_issues): - result["summary"]["missing_files"] += 1 - elif any(issue["type"] == "broken_symlink" for issue in repo_issues): - result["summary"]["symlink_issues"] += 1 - else: - result["summary"]["corrupted"] += 1 - else: - result["healthy_repos"].append( - { - "repo_id": repo.repo_id, - "repo_type": repo.repo_type, - "size": repo.size_on_disk_str, - } - ) - result["summary"]["healthy"] += 1 - - # Overall health assessment - total_repos = len(cache_info.repos) - if total_repos > 0: - health_percentage = (result["summary"]["healthy"] / total_repos) * 100 - result["overall_health"] = { - "percentage": round(health_percentage, 1), - "status": ( - "healthy" - if health_percentage > 95 - else "warning" if health_percentage > 80 else "critical" - ), - "recommendation": self._get_health_recommendation(health_percentage), - } - - return result - - def _get_health_recommendation(self, health_percentage: float) -> str: - """Get recommendation based on cache health percentage.""" - if health_percentage > 95: - return "Cache is in excellent condition" - elif health_percentage > 80: - return "Cache has minor issues. Consider running cleanup to remove problematic entries" + if "WHERE" in sql_upper: + # SQL already has WHERE clause, append with AND + return f"{sql} AND ({filter_clause})" else: - return "Cache has significant issues. Run cleanup_cache() or consider clearing the cache entirely" - - def migrate_cache(self, new_cache_dir: str | Path) -> dict[str, Any]: - """ - Migrate cache to a new directory location. - - :param new_cache_dir: Target directory for cache migration - :return: Dictionary with migration results - - """ - if not self._cache_manager: - return {"cache_management": "disabled"} - - import shutil - from pathlib import Path - - new_cache_path = Path(new_cache_dir) - current_cache_path = self.cache_dir - - result = { - "cache_migration": "enabled", - "source": str(current_cache_path), - "destination": str(new_cache_path), - "success": False, - "files_migrated": 0, - "errors": [], - } - - try: - # Validate target directory - if new_cache_path.exists() and list(new_cache_path.iterdir()): - return { - **result, - "error": f"Target directory {new_cache_path} is not empty", - "suggestion": "Choose an empty directory or clear the target directory", - } - - # Create target directory if needed - new_cache_path.mkdir(parents=True, exist_ok=True) - - # Get current cache info - from huggingface_hub import scan_cache_dir - - cache_info = scan_cache_dir() - - if not cache_info.repos: - result.update( - { - "success": True, - "message": "No cached repositories to migrate", - } - ) - # Update cache directory - self.cache_dir = new_cache_path - return result - - # Migrate each repository - migrated_repos = [] - for repo in cache_info.repos: - try: - # Create repo structure in new location - repo_name = repo.repo_path.name - new_repo_path = new_cache_path / repo_name - - # Copy entire repository directory - shutil.copytree(repo.repo_path, new_repo_path, symlinks=True) - migrated_repos.append(repo.repo_id) - result["files_migrated"] += repo.nb_files - - except Exception as e: - error_msg = f"Failed to migrate repo {repo.repo_id}: {e}" - result["errors"].append(error_msg) - - if migrated_repos and not result["errors"]: - # Migration successful, update cache directory - self.cache_dir = new_cache_path - result.update( - { - "success": True, - "migrated_repos": migrated_repos, - "repos_count": len(migrated_repos), - "message": f"Successfully migrated {len(migrated_repos)} repositories", - } - ) - elif migrated_repos: - result.update( - { - "success": True, # Partial success - "migrated_repos": migrated_repos, - "repos_count": len(migrated_repos), - "message": f"Partially migrated {len(migrated_repos)} repositories with {len(result['errors'])} errors", - } - ) + # Add WHERE clause + # Find the position to insert WHERE (before ORDER BY, GROUP BY, LIMIT, etc.) + insert_keywords = ["ORDER BY", "GROUP BY", "HAVING", "LIMIT", "OFFSET"] + insert_position = len(sql) + + for keyword in insert_keywords: + pos = sql_upper.find(keyword) + if pos != -1 and pos < insert_position: + insert_position = pos + + if insert_position == len(sql): + # No special clauses, append WHERE at the end + return f"{sql} WHERE {filter_clause}" else: - result.update( - { - "success": False, - "message": "Migration failed completely", - } - ) - - except Exception as e: - result.update( - { - "success": False, - "error": f"Migration failed: {str(e)}", - } - ) - - return result - - # ============== Cache Configuration Management ============== - - def configure_cache_policies( - self, - auto_cleanup: bool | None = None, - max_age_days: int | None = None, - max_size: str | None = None, - save_to_env: bool = False, - ) -> dict[str, Any]: - """ - Configure cache management policies for this instance. - - :param auto_cleanup: Enable/disable automatic cache cleanup - :param max_age_days: Maximum age in days for cache entries - :param max_size: Maximum total cache size (e.g., "10GB") - :param save_to_env: Save configuration to environment variables - :return: Dictionary with updated configuration - - """ - if not self._cache_manager: - return {"cache_management": "disabled"} - - # Update instance configuration - if auto_cleanup is not None: - self._cache_auto_cleanup = auto_cleanup - if max_age_days is not None: - self._cache_max_age_days = max_age_days - if max_size is not None: - self._cache_max_size = max_size - - config = { - "cache_management": "enabled", - "configuration_updated": True, - "policies": { - "auto_cleanup": getattr(self, "_cache_auto_cleanup", False), - "max_age_days": getattr(self, "_cache_max_age_days", 30), - "max_size": getattr(self, "_cache_max_size", "10GB"), - }, - "env_variables_updated": False, - } - - # Save to environment variables if requested - if save_to_env: - import os - - env_vars = {} - - if auto_cleanup is not None: - env_var = "TFBPAPI_CACHE_AUTO_CLEANUP" - os.environ[env_var] = str(auto_cleanup).lower() - env_vars[env_var] = str(auto_cleanup).lower() - - if max_age_days is not None: - env_var = "TFBPAPI_CACHE_MAX_AGE_DAYS" - os.environ[env_var] = str(max_age_days) - env_vars[env_var] = str(max_age_days) - - if max_size is not None: - env_var = "TFBPAPI_CACHE_MAX_SIZE" - os.environ[env_var] = max_size - env_vars[env_var] = max_size - - config.update( - { - "env_variables_updated": True, - "env_variables": env_vars, - "note": "Environment variables set for current session. " - "Add them to your shell profile for persistence.", - } - ) - - return config - - def get_cache_configuration(self) -> dict[str, Any]: - """ - Get current cache configuration including environment variables. - - :return: Dictionary with comprehensive cache configuration - - """ - import os - - config = { - "cache_management": "enabled" if self._cache_manager else "disabled", - "cache_directory": str(self.cache_dir), - "instance_config": {}, - "environment_config": {}, - "effective_config": {}, - } - - if not self._cache_manager: - return config - - # Instance configuration - config["instance_config"] = { - "auto_cleanup": getattr(self, "_cache_auto_cleanup", False), - "max_age_days": getattr(self, "_cache_max_age_days", 30), - "max_size": getattr(self, "_cache_max_size", "10GB"), - } - - # Environment configuration - env_config = {} - env_vars = { - "TFBPAPI_CACHE_AUTO_CLEANUP": "auto_cleanup", - "TFBPAPI_CACHE_MAX_AGE_DAYS": "max_age_days", - "TFBPAPI_CACHE_MAX_SIZE": "max_size", - "HF_CACHE_DIR": "cache_directory", - "HF_HUB_CACHE": "cache_directory_fallback", - } - - for env_var, config_key in env_vars.items(): - value = os.getenv(env_var) - if value: - env_config[config_key] = value - - config["environment_config"] = env_config - - # Effective configuration (environment overrides instance) - effective = config["instance_config"].copy() - - # Apply environment overrides - if "auto_cleanup" in env_config: - effective["auto_cleanup"] = env_config["auto_cleanup"].lower() in ( - "true", - "1", - "yes", - ) - if "max_age_days" in env_config: - try: - effective["max_age_days"] = int(env_config["max_age_days"]) - except ValueError: - pass - if "max_size" in env_config: - effective["max_size"] = env_config["max_size"] - - config["effective_config"] = effective - - return config - - def reset_cache_configuration( - self, remove_env_vars: bool = False - ) -> dict[str, Any]: - """ - Reset cache configuration to defaults. - - :param remove_env_vars: Also remove related environment variables - :return: Dictionary with reset results - - """ - if not self._cache_manager: - return {"cache_management": "disabled"} - - # Reset instance configuration to defaults - self._cache_auto_cleanup = False - self._cache_max_age_days = 30 - self._cache_max_size = "10GB" - - result = { - "cache_management": "enabled", - "configuration_reset": True, - "new_config": { - "auto_cleanup": False, - "max_age_days": 30, - "max_size": "10GB", - }, - "env_variables_removed": [], - } - - # Remove environment variables if requested - if remove_env_vars: - import os - - env_vars_to_remove = [ - "TFBPAPI_CACHE_AUTO_CLEANUP", - "TFBPAPI_CACHE_MAX_AGE_DAYS", - "TFBPAPI_CACHE_MAX_SIZE", - ] - - for env_var in env_vars_to_remove: - if env_var in os.environ: - del os.environ[env_var] - result["env_variables_removed"].append(env_var) - - if result["env_variables_removed"]: - result["note"] = ( - "Environment variables removed from current session. " - "Remove them from your shell profile for permanent effect." + # Insert WHERE before the special clause + return ( + f"{sql[:insert_position].rstrip()} " + f"WHERE {filter_clause} {sql[insert_position:]}" ) - - return result - - def apply_cache_policy_from_env(self) -> dict[str, Any]: - """ - Apply cache policies from environment variables. - - :return: Dictionary with applied configuration - - """ - if not self._cache_manager: - return {"cache_management": "disabled"} - - import os - - applied_config = [] - - # Auto cleanup - auto_cleanup_env = os.getenv("TFBPAPI_CACHE_AUTO_CLEANUP") - if auto_cleanup_env: - self._cache_auto_cleanup = auto_cleanup_env.lower() in ("true", "1", "yes") - applied_config.append(f"auto_cleanup: {self._cache_auto_cleanup}") - - # Max age days - max_age_env = os.getenv("TFBPAPI_CACHE_MAX_AGE_DAYS") - if max_age_env: - try: - self._cache_max_age_days = int(max_age_env) - applied_config.append(f"max_age_days: {self._cache_max_age_days}") - except ValueError: - applied_config.append( - f"max_age_days: invalid value '{max_age_env}' (keeping default)" - ) - - # Max size - max_size_env = os.getenv("TFBPAPI_CACHE_MAX_SIZE") - if max_size_env: - self._cache_max_size = max_size_env - applied_config.append(f"max_size: {self._cache_max_size}") - - return { - "cache_management": "enabled", - "environment_config_applied": True, - "applied_settings": applied_config, - "current_config": { - "auto_cleanup": getattr(self, "_cache_auto_cleanup", False), - "max_age_days": getattr(self, "_cache_max_age_days", 30), - "max_size": getattr(self, "_cache_max_size", "10GB"), - }, - } - - def export_cache_configuration(self, format: str = "env") -> dict[str, Any]: - """ - Export current cache configuration in various formats. - - :param format: Export format - "env", "json", "yaml" - :return: Dictionary with exported configuration - - """ - if not self._cache_manager: - return {"cache_management": "disabled"} - - current_config = { - "auto_cleanup": getattr(self, "_cache_auto_cleanup", False), - "max_age_days": getattr(self, "_cache_max_age_days", 30), - "max_size": getattr(self, "_cache_max_size", "10GB"), - "cache_directory": str(self.cache_dir), - } - - result = { - "cache_management": "enabled", - "format": format, - "configuration": current_config, - } - - if format == "env": - env_lines = [ - f"export TFBPAPI_CACHE_AUTO_CLEANUP={str(current_config['auto_cleanup']).lower()}", - f"export TFBPAPI_CACHE_MAX_AGE_DAYS={current_config['max_age_days']}", - f"export TFBPAPI_CACHE_MAX_SIZE={current_config['max_size']}", - f"export HF_CACHE_DIR={current_config['cache_directory']}", - ] - result["exported_config"] = "\n".join(env_lines) - result["usage"] = "Add these lines to your ~/.bashrc or ~/.zshrc file" - - elif format == "json": - import json - - result["exported_config"] = json.dumps(current_config, indent=2) - - elif format == "yaml": - yaml_lines = [ - "cache_configuration:", - f" auto_cleanup: {str(current_config['auto_cleanup']).lower()}", - f" max_age_days: {current_config['max_age_days']}", - f" max_size: \"{current_config['max_size']}\"", - f" cache_directory: \"{current_config['cache_directory']}\"", - ] - result["exported_config"] = "\n".join(yaml_lines) - - else: - result["error"] = f"Unsupported format: {format}" - result["supported_formats"] = ["env", "json", "yaml"] - - return result - - def close(self) -> None: - """Close the DuckDB connection.""" - if self._duckdb_conn: - self._duckdb_conn.close() - - def __enter__(self): - """Context manager entry.""" - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - """Context manager exit.""" - self.close() diff --git a/tfbpapi/constants.py b/tfbpapi/constants.py new file mode 100644 index 0000000..749678f --- /dev/null +++ b/tfbpapi/constants.py @@ -0,0 +1,11 @@ +import os +from pathlib import Path + +from huggingface_hub.constants import HF_HUB_CACHE + +CACHE_DIR = Path(os.getenv("HF_CACHE_DIR", HF_HUB_CACHE)) + + +def get_hf_token() -> str | None: + """Get HuggingFace token from environment variable.""" + return os.getenv("HF_TOKEN") diff --git a/tfbpapi/datainfo/fetchers.py b/tfbpapi/datainfo/fetchers.py index e33ac05..30cc72a 100644 --- a/tfbpapi/datainfo/fetchers.py +++ b/tfbpapi/datainfo/fetchers.py @@ -1,14 +1,14 @@ """Data fetchers for HuggingFace Hub integration.""" import logging -import os import re -from typing import Any, Dict, List, Optional, Set +from typing import Any import requests from huggingface_hub import DatasetCard, repo_info from requests import HTTPError +from ..constants import get_hf_token from ..errors import HfDataFetchError @@ -23,7 +23,7 @@ def __init__(self, token: str | None = None): """ self.logger = logging.getLogger(self.__class__.__name__) - self.token = token or os.getenv("HF_TOKEN") + self.token = token or get_hf_token() def fetch(self, repo_id: str, repo_type: str = "dataset") -> dict[str, Any]: """ @@ -62,7 +62,7 @@ def __init__(self, token: str | None = None): """ self.logger = logging.getLogger(self.__class__.__name__) - self.token = token or os.getenv("HF_TOKEN") + self.token = token or get_hf_token() self.base_url = "https://datasets-server.huggingface.co" def _build_headers(self) -> dict[str, str]: @@ -129,7 +129,7 @@ def __init__(self, token: str | None = None): """ self.logger = logging.getLogger(self.__class__.__name__) - self.token = token or os.getenv("HF_TOKEN") + self.token = token or get_hf_token() self._cached_structure: dict[str, dict[str, Any]] = {} def fetch(self, repo_id: str, force_refresh: bool = False) -> dict[str, Any]: diff --git a/tfbpapi/errors.py b/tfbpapi/errors.py index 654d6fc..6952b79 100644 --- a/tfbpapi/errors.py +++ b/tfbpapi/errors.py @@ -1,6 +1,6 @@ """Custom exception classes for dataset management.""" -from typing import Any, Dict, Optional +from typing import Any class DatasetError(Exception): @@ -22,7 +22,8 @@ class RepoTooLargeError(DatasetError): """Raised when repository exceeds auto-download threshold.""" def __init__(self, repo_id: str, size_mb: float, threshold_mb: float): - message = f"Repository '{repo_id}' is too large for auto-download: {size_mb:.2f}MB exceeds {threshold_mb}MB threshold" + message = f"Repository '{repo_id}' is too large for " + f"auto-download: {size_mb:.2f}MB exceeds {threshold_mb}MB threshold" super().__init__( message, details={ @@ -46,7 +47,7 @@ def __init__( config_name: str | None = None, original_error: Exception | None = None, ): - details = {} + details: dict[str, Any] = {} if repo_id: details["repo_id"] = repo_id if config_name: @@ -70,7 +71,7 @@ def __init__( status_code: int | None = None, endpoint: str | None = None, ): - details = {} + details: dict[str, Any] = {} if repo_id: details["repo_id"] = repo_id if status_code: @@ -184,7 +185,7 @@ def __init__( repo_id: str | None = None, validation_errors: list | None = None, ): - details = {} + details: dict[str, Any] = {} if repo_id: details["repo_id"] = repo_id if validation_errors: @@ -204,7 +205,7 @@ def __init__( config_name: str | None = None, field_name: str | None = None, ): - details = {} + details: dict[str, Any] = {} if config_name: details["config_name"] = config_name if field_name: diff --git a/tfbpapi/tests/test_HfCacheManager.py b/tfbpapi/tests/test_HfCacheManager.py index 0a4e472..e044150 100644 --- a/tfbpapi/tests/test_HfCacheManager.py +++ b/tfbpapi/tests/test_HfCacheManager.py @@ -1,55 +1,519 @@ -from tfbpapi.HfCacheManager import HFCacheManager +"""Comprehensive tests for HfCacheManager class.""" +import logging +from datetime import datetime, timedelta +from unittest.mock import Mock, patch -def test_parse_size_string(): - """Test size string parsing.""" - cache_manager = HFCacheManager() - assert cache_manager._parse_size_string("10KB") == 10 * 1024 - assert cache_manager._parse_size_string("5MB") == 5 * 1024**2 - assert cache_manager._parse_size_string("2GB") == 2 * 1024**3 - assert cache_manager._parse_size_string("1TB") == 1 * 1024**4 - assert cache_manager._parse_size_string("500") == 500 +import duckdb +import pytest +from tfbpapi.datainfo.models import DatasetType +from tfbpapi.HfCacheManager import HfCacheManager -def test_clean_cache_by_age(mock_scan_cache_dir): - """Test age-based cache cleaning.""" - cache_manager = HFCacheManager() +class TestHfCacheManagerInit: + """Test HfCacheManager initialization.""" - # This will use the mocked scan_cache_dir - strategy = cache_manager.clean_cache_by_age(max_age_days=1, dry_run=True) + def test_init_basic(self): + """Test basic initialization.""" + conn = duckdb.connect(":memory:") + repo_id = "test/repo" - # Test your logic - assert strategy is not None or len(mock_scan_cache_dir.repos) == 0 - if strategy: - assert len(strategy.repos) >= 0 - assert strategy.expected_freed_size >= 0 + with patch( + "tfbpapi.HfCacheManager.DataCard.__init__", return_value=None + ) as mock_datacard_init: + cache_manager = HfCacheManager(repo_id, conn) + # Manually set the properties that would normally + # be set by DataCard.__init__ + cache_manager.repo_id = repo_id + cache_manager.token = None + assert cache_manager.repo_id == repo_id + assert cache_manager.duckdb_conn == conn + assert cache_manager.token is None + assert cache_manager.logger is not None + # DataCard should be initialized as parent + mock_datacard_init.assert_called_once_with(repo_id, None) -def test_clean_cache_by_size(mock_scan_cache_dir): - """Test size-based cache cleaning.""" + def test_init_with_token_and_logger(self): + """Test initialization with token and custom logger.""" + conn = duckdb.connect(":memory:") + repo_id = "test/repo" + token = "test_token" + logger = logging.getLogger("test_logger") - cache_manager = HFCacheManager() + with patch( + "tfbpapi.HfCacheManager.DataCard.__init__", return_value=None + ) as mock_datacard_init: + cache_manager = HfCacheManager(repo_id, conn, token=token, logger=logger) + # Manually set the properties that would + # normally be set by DataCard.__init__ + cache_manager.repo_id = repo_id + cache_manager.token = token - # Test with a very small target to force cleanup - strategy = cache_manager.clean_cache_by_size(target_size="1GB", dry_run=True) + assert cache_manager.repo_id == repo_id + assert cache_manager.duckdb_conn == conn + assert cache_manager.token == token + assert cache_manager.logger == logger + # DataCard should be initialized as parent with token + mock_datacard_init.assert_called_once_with(repo_id, token) - if mock_scan_cache_dir.size_on_disk > 1024: # If cache has data - assert strategy is not None - assert len(strategy.repos) > 0 +class TestHfCacheManagerDatacard: + """Test DataCard integration since HfCacheManager now inherits from DataCard.""" -def test_auto_clean_cache(mock_scan_cache_dir): - """Test automated cache cleaning.""" + def test_datacard_inheritance(self): + """Test that HfCacheManager properly inherits from DataCard.""" + conn = duckdb.connect(":memory:") + repo_id = "test/repo" + token = "test_token" - cache_manager = HFCacheManager() + with patch( + "tfbpapi.HfCacheManager.DataCard.__init__", return_value=None + ) as mock_datacard_init: + cache_manager = HfCacheManager(repo_id, conn, token=token) - strategies = cache_manager.auto_clean_cache( - max_age_days=1, - max_total_size="1KB", - keep_latest_per_repo=1, - dry_run=True, + # DataCard should be initialized during construction + mock_datacard_init.assert_called_once_with(repo_id, token) + + # Should have DataCard methods available (they exist on the class) + assert hasattr(cache_manager, "get_config") + assert hasattr(cache_manager, "get_configs_by_type") + + +class TestHfCacheManagerDuckDBOperations: + """Test DuckDB operations that are still part of HfCacheManager.""" + + @patch("tfbpapi.HfCacheManager.DataCard.__init__", return_value=None) + def test_create_duckdb_table_from_files_single_file( + self, mock_datacard_init, tmpdir + ): + """Test creating DuckDB table from single parquet file.""" + # Create a mock parquet file + parquet_file = tmpdir.join("test.parquet") + parquet_file.write("dummy_content") + + # Use a separate cache manager with mock connection for this test + mock_conn = Mock() + test_cache_manager = HfCacheManager("test/repo", mock_conn) + + test_cache_manager._create_duckdb_table_from_files( + [str(parquet_file)], "test_table" + ) + + mock_conn.execute.assert_called_once() + sql_call = mock_conn.execute.call_args[0][0] + assert "CREATE OR REPLACE VIEW test_table" in sql_call + assert str(parquet_file) in sql_call + + @patch("tfbpapi.HfCacheManager.DataCard.__init__", return_value=None) + def test_create_duckdb_table_from_files_multiple_files( + self, mock_datacard_init, tmpdir + ): + """Test creating DuckDB table from multiple parquet files.""" + # Create mock parquet files + file1 = tmpdir.join("test1.parquet") + file1.write("dummy_content1") + file2 = tmpdir.join("test2.parquet") + file2.write("dummy_content2") + + files = [str(file1), str(file2)] + + # Use a separate cache manager with mock connection for this test + mock_conn = Mock() + test_cache_manager = HfCacheManager("test/repo", mock_conn) + + test_cache_manager._create_duckdb_table_from_files(files, "test_table") + + mock_conn.execute.assert_called_once() + sql_call = mock_conn.execute.call_args[0][0] + assert "CREATE OR REPLACE VIEW test_table" in sql_call + assert str(file1) in sql_call + assert str(file2) in sql_call + + +class TestHfCacheManagerCacheManagement: + """Test cache management functionality.""" + + def setup_method(self): + """Set up test fixtures.""" + with patch("tfbpapi.HfCacheManager.DataCard.__init__", return_value=None): + self.conn = duckdb.connect(":memory:") + self.repo_id = "test/repo" + self.cache_manager = HfCacheManager(self.repo_id, self.conn) + + def test_parse_size_string(self): + """Test size string parsing.""" + assert self.cache_manager._parse_size_string("10KB") == 10 * 1024 + assert self.cache_manager._parse_size_string("5MB") == 5 * 1024**2 + assert self.cache_manager._parse_size_string("2GB") == 2 * 1024**3 + assert self.cache_manager._parse_size_string("1TB") == 1 * 1024**4 + assert self.cache_manager._parse_size_string("500") == 500 + assert self.cache_manager._parse_size_string("10.5GB") == int(10.5 * 1024**3) + + def test_format_bytes(self): + """Test byte formatting.""" + assert self.cache_manager._format_bytes(0) == "0B" + assert self.cache_manager._format_bytes(1023) == "1023.0B" + assert self.cache_manager._format_bytes(1024) == "1.0KB" + assert self.cache_manager._format_bytes(1024**2) == "1.0MB" + assert self.cache_manager._format_bytes(1024**3) == "1.0GB" + assert self.cache_manager._format_bytes(1024**4) == "1.0TB" + + @patch("tfbpapi.HfCacheManager.scan_cache_dir") + def test_clean_cache_by_age(self, mock_scan_cache_dir): + """Test age-based cache cleaning.""" + # Setup mock cache info + mock_cache_info = Mock() + mock_revision = Mock() + mock_revision.commit_hash = "abc123" + mock_revision.last_modified = (datetime.now() - timedelta(days=35)).timestamp() + + mock_repo = Mock() + mock_repo.revisions = [mock_revision] + + mock_cache_info.repos = [mock_repo] + mock_delete_strategy = Mock() + mock_delete_strategy.expected_freed_size_str = "100MB" + mock_cache_info.delete_revisions.return_value = mock_delete_strategy + + mock_scan_cache_dir.return_value = mock_cache_info + + result = self.cache_manager.clean_cache_by_age(max_age_days=30, dry_run=True) + + assert result == mock_delete_strategy + mock_cache_info.delete_revisions.assert_called_once_with("abc123") + + @patch("tfbpapi.HfCacheManager.scan_cache_dir") + def test_clean_cache_by_age_no_old_revisions(self, mock_scan_cache_dir): + """Test age-based cleaning when no old revisions exist.""" + mock_cache_info = Mock() + mock_revision = Mock() + mock_revision.commit_hash = "abc123" + mock_revision.last_modified = datetime.now().timestamp() # Recent + + mock_repo = Mock() + mock_repo.revisions = [mock_revision] + + mock_cache_info.repos = [mock_repo] + mock_delete_strategy = Mock() + mock_delete_strategy.expected_freed_size_str = "0B" + mock_cache_info.delete_revisions.return_value = mock_delete_strategy + + mock_scan_cache_dir.return_value = mock_cache_info + + result = self.cache_manager.clean_cache_by_age(max_age_days=30, dry_run=True) + + # Should still return a strategy, but with empty revisions + assert result == mock_delete_strategy + mock_cache_info.delete_revisions.assert_called_once_with() + + @patch("tfbpapi.HfCacheManager.scan_cache_dir") + def test_clean_cache_by_size(self, mock_scan_cache_dir): + """Test size-based cache cleaning.""" + # Setup mock cache info + mock_cache_info = Mock() + mock_cache_info.size_on_disk = 5 * 1024**3 # 5GB + mock_cache_info.size_on_disk_str = "5.0GB" + + mock_revision = Mock() + mock_revision.commit_hash = "abc123" + mock_revision.last_modified = datetime.now().timestamp() + mock_revision.size_on_disk = 2 * 1024**3 # 2GB + + mock_repo = Mock() + mock_repo.revisions = [mock_revision] + + mock_cache_info.repos = [mock_repo] + mock_delete_strategy = Mock() + mock_delete_strategy.expected_freed_size_str = "2GB" + mock_cache_info.delete_revisions.return_value = mock_delete_strategy + + mock_scan_cache_dir.return_value = mock_cache_info + + result = self.cache_manager.clean_cache_by_size( + target_size="3GB", strategy="oldest_first", dry_run=True + ) + + assert result == mock_delete_strategy + mock_cache_info.delete_revisions.assert_called_once() + + @patch("tfbpapi.HfCacheManager.scan_cache_dir") + def test_clean_cache_by_size_already_under_target(self, mock_scan_cache_dir): + """Test size-based cleaning when already under target.""" + mock_cache_info = Mock() + mock_cache_info.size_on_disk = 1 * 1024**3 # 1GB + mock_cache_info.size_on_disk_str = "1.0GB" + mock_cache_info.repos = [] + + mock_delete_strategy = Mock() + mock_delete_strategy.expected_freed_size_str = "0B" + mock_cache_info.delete_revisions.return_value = mock_delete_strategy + + mock_scan_cache_dir.return_value = mock_cache_info + + result = self.cache_manager.clean_cache_by_size( + target_size="2GB", strategy="oldest_first", dry_run=True + ) + + assert result == mock_delete_strategy + + @patch("tfbpapi.HfCacheManager.scan_cache_dir") + def test_clean_unused_revisions(self, mock_scan_cache_dir): + """Test cleaning unused revisions.""" + # Setup mock with multiple revisions + mock_cache_info = Mock() + + mock_revision1 = Mock() + mock_revision1.commit_hash = "abc123" + mock_revision1.last_modified = (datetime.now() - timedelta(days=1)).timestamp() + + mock_revision2 = Mock() + mock_revision2.commit_hash = "def456" + mock_revision2.last_modified = (datetime.now() - timedelta(days=10)).timestamp() + + mock_revision3 = Mock() + mock_revision3.commit_hash = "ghi789" + mock_revision3.last_modified = (datetime.now() - timedelta(days=20)).timestamp() + + mock_repo = Mock() + mock_repo.revisions = [mock_revision1, mock_revision2, mock_revision3] + + mock_cache_info.repos = [mock_repo] + mock_delete_strategy = Mock() + mock_delete_strategy.expected_freed_size_str = "1GB" + mock_cache_info.delete_revisions.return_value = mock_delete_strategy + + mock_scan_cache_dir.return_value = mock_cache_info + + result = self.cache_manager.clean_unused_revisions(keep_latest=2, dry_run=True) + + assert result == mock_delete_strategy + # Should delete oldest revision (ghi789) + mock_cache_info.delete_revisions.assert_called_once_with("ghi789") + + @patch("tfbpapi.HfCacheManager.scan_cache_dir") + def test_auto_clean_cache(self, mock_scan_cache_dir): + """Test automated cache cleaning.""" + mock_cache_info = Mock() + mock_cache_info.size_on_disk = 10 * 1024**3 # 10GB + mock_cache_info.repos = [] + + mock_delete_strategy = Mock() + mock_delete_strategy.expected_freed_size = 1 * 1024**3 # 1GB + mock_delete_strategy.expected_freed_size_str = "1GB" + + mock_scan_cache_dir.return_value = mock_cache_info + + with patch.object( + self.cache_manager, "clean_cache_by_age", return_value=mock_delete_strategy + ): + with patch.object( + self.cache_manager, + "clean_unused_revisions", + return_value=mock_delete_strategy, + ): + with patch.object( + self.cache_manager, + "clean_cache_by_size", + return_value=mock_delete_strategy, + ): + result = self.cache_manager.auto_clean_cache( + max_age_days=30, + max_total_size="5GB", + keep_latest_per_repo=2, + dry_run=True, + ) + + assert ( + len(result) == 3 + ) # All three cleanup strategies should be executed + assert all(strategy == mock_delete_strategy for strategy in result) + + +class TestHfCacheManagerErrorHandling: + """Test error handling and edge cases.""" + + def setup_method(self): + """Set up test fixtures.""" + with patch("tfbpapi.HfCacheManager.DataCard.__init__", return_value=None): + self.conn = duckdb.connect(":memory:") + self.repo_id = "test/repo" + self.cache_manager = HfCacheManager(self.repo_id, self.conn) + + def test_parse_size_string_invalid_input(self): + """Test error handling for invalid size strings.""" + with pytest.raises(ValueError): + self.cache_manager._parse_size_string("invalid") + + @patch("tfbpapi.HfCacheManager.scan_cache_dir") + def test_clean_cache_invalid_strategy(self, mock_scan_cache_dir): + """Test error handling for invalid cleanup strategy.""" + mock_cache_info = Mock() + mock_cache_info.size_on_disk = 5 * 1024**3 + mock_cache_info.repos = [] + mock_scan_cache_dir.return_value = mock_cache_info + + with pytest.raises(ValueError, match="Unknown strategy"): + self.cache_manager.clean_cache_by_size( + target_size="1GB", + strategy="invalid_strategy", # type: ignore[arg-type] + dry_run=True, + ) + + +class TestHfCacheManagerIntegration: + """Integration tests with real DuckDB operations.""" + + def setup_method(self): + """Set up test fixtures.""" + with patch("tfbpapi.HfCacheManager.DataCard.__init__", return_value=None): + self.conn = duckdb.connect(":memory:") + self.repo_id = "test/repo" + self.cache_manager = HfCacheManager(self.repo_id, self.conn) + + def test_metadata_workflow_integration(self, tmpdir): + """Test complete metadata workflow with real files.""" + # Create temporary parquet file content + metadata_file = tmpdir.join("metadata.parquet") + metadata_file.write("dummy_parquet_content") + + # Test the core table creation functionality + mock_conn = Mock() + test_cache_manager = HfCacheManager("test/repo", mock_conn) + + # Test _create_duckdb_table_from_files directly + test_cache_manager._create_duckdb_table_from_files( + [str(metadata_file)], "metadata_test_metadata" + ) + + # Verify the SQL was generated correctly + mock_conn.execute.assert_called_once() + sql_call = mock_conn.execute.call_args[0][0] + assert "CREATE OR REPLACE VIEW metadata_test_metadata" in sql_call + assert str(metadata_file) in sql_call + + def test_embedded_metadata_workflow_integration(self): + """Test complete embedded metadata workflow with real DuckDB operations.""" + # Create real test data in DuckDB + self.conn.execute( + """ + CREATE TABLE test_data AS + SELECT + 'gene_' || (row_number() OVER()) as gene_id, + CASE + WHEN (row_number() OVER()) % 3 = 0 THEN 'treatment_A' + WHEN (row_number() OVER()) % 3 = 1 THEN 'treatment_B' + ELSE 'control' + END as experimental_condition, + random() * 1000 as expression_value + FROM range(30) + """ + ) + + # Extract embedded metadata + result = self.cache_manager._extract_embedded_metadata_field( + "test_data", "experimental_condition", "metadata_test_condition" + ) + + assert result is True + + # Verify the metadata table was created correctly + metadata_results = self.conn.execute( + "SELECT value, count FROM metadata_test_condition ORDER BY count DESC" + ).fetchall() + + assert len(metadata_results) == 3 # Three unique conditions + + # Check that the counts make sense (should be 10 each for 30 total rows) + total_count = sum(row[1] for row in metadata_results) + assert total_count == 30 + + # Check that conditions are as expected + conditions = {row[0] for row in metadata_results} + assert conditions == {"treatment_A", "treatment_B", "control"} + + def test_table_existence_checking_integration(self): + """Test table existence checking with real DuckDB operations.""" + # Test non-existent table + assert ( + self.cache_manager._check_metadata_exists_in_duckdb("nonexistent_table") + is False + ) + + # Create a real table + self.conn.execute("CREATE TABLE test_table (id INTEGER, name TEXT)") + + # Test existing table + assert self.cache_manager._check_metadata_exists_in_duckdb("test_table") is True + + # Test with view + self.conn.execute("CREATE VIEW test_view AS SELECT * FROM test_table") + assert self.cache_manager._check_metadata_exists_in_duckdb("test_view") is True + + +# Fixtures for common test data +@pytest.fixture +def sample_metadata_config(): + """Sample metadata configuration for testing.""" + return Mock( + config_name="test_metadata", + description="Test metadata configuration", + data_files=[Mock(path="metadata.parquet")], + applies_to=["data_config"], + ) + + +@pytest.fixture +def sample_data_config(): + """Sample data configuration for testing.""" + return Mock( + config_name="test_data", + metadata_fields=["condition", "replicate"], + dataset_type=DatasetType.ANNOTATED_FEATURES, ) - # Should return a list of strategies - assert isinstance(strategies, list) + +@pytest.fixture +def mock_cache_revision(): + """Mock cache revision for testing.""" + revision = Mock() + revision.commit_hash = "abc123def456" + revision.last_modified = datetime.now().timestamp() + revision.size_on_disk = 1024 * 1024 * 100 # 100MB + return revision + + +@pytest.fixture +def mock_cache_repo(mock_cache_revision): + """Mock cache repository for testing.""" + repo = Mock() + repo.repo_id = "test/repository" + repo.revisions = [mock_cache_revision] + repo.size_on_disk = 1024 * 1024 * 100 # 100MB + repo.size_on_disk_str = "100.0MB" + return repo + + +@pytest.fixture +def mock_cache_info(mock_cache_repo): + """Mock cache info for testing.""" + cache_info = Mock() + cache_info.cache_dir = "/tmp/cache" + cache_info.repos = [mock_cache_repo] + cache_info.size_on_disk = 1024 * 1024 * 100 # 100MB + cache_info.size_on_disk_str = "100.0MB" + + # Mock delete_revisions method + def mock_delete_revisions(*revision_hashes): + strategy = Mock() + strategy.expected_freed_size = ( + len(revision_hashes) * 1024 * 1024 * 50 + ) # 50MB per revision + strategy.expected_freed_size_str = f"{len(revision_hashes) * 50}.0MB" + strategy.delete_content = list(revision_hashes) + strategy.execute = Mock() + return strategy + + cache_info.delete_revisions = mock_delete_revisions + return cache_info From 057742e4a2f86bb794a279d7ec4fa82a3d6bfb70 Mon Sep 17 00:00:00 2001 From: chasem Date: Wed, 24 Sep 2025 22:55:35 -0500 Subject: [PATCH 33/49] adding rank response tutorial and role to features --- docs/huggingface_datacard.md | 19 + docs/tutorials/passing_callingcards.csv | 82 ++ docs/tutorials/rank_response_tutorial.ipynb | 968 ++++++++++++++++++++ mkdocs.yml | 1 + pyproject.toml | 2 + tfbpapi/HfCacheManager.py | 81 +- tfbpapi/HfQueryAPI.py | 601 +++++++++--- tfbpapi/HfRankResponse.py | 2 +- tfbpapi/RankResponseAnalysis.py | 277 ++++++ tfbpapi/constants.py | 44 + tfbpapi/datainfo/datacard.py | 24 +- tfbpapi/datainfo/models.py | 4 + tfbpapi/errors.py | 35 + tfbpapi/tests/datainfo/test_datacard.py | 6 +- tfbpapi/tests/test_HfQueryAPI.py | 859 +++++++++++++++++ 15 files changed, 2856 insertions(+), 149 deletions(-) create mode 100644 docs/tutorials/passing_callingcards.csv create mode 100644 docs/tutorials/rank_response_tutorial.ipynb create mode 100644 tfbpapi/RankResponseAnalysis.py create mode 100644 tfbpapi/tests/test_HfQueryAPI.py diff --git a/docs/huggingface_datacard.md b/docs/huggingface_datacard.md index 916d6d4..c41b062 100644 --- a/docs/huggingface_datacard.md +++ b/docs/huggingface_datacard.md @@ -78,6 +78,7 @@ dataset_info: - name: field_name # Column name in the data dtype: string # Data type (string, int64, float64, etc.) description: "Detailed description of what this field contains" + role: "target_identifier" # Optional: semantic role of the feature ``` ### Common Data Types @@ -103,6 +104,17 @@ dataset_info: - `pos`: Single position - `strand`: Strand information (+ or -) +### Feature Roles + +The optional `role` field provides semantic meaning to features, especially useful for `annotated_features` datasets: + +**Standard Roles:** +- `target_identifier`: Identifies target genes/features (e.g., target_locus_tag, target_symbol) +- `regulator_identifier`: Identifies regulatory factors (e.g., regulator_locus_tag, regulator_symbol) +- `quantitative_measure`: Quantitative measurements (e.g., binding_score, expression_level, p_value) +- `experimental_condition`: Experimental conditions or metadata +- `genomic_coordinate`: Positional information (chr, start, end, pos) + ## Partitioned Datasets For large datasets (eg most genome_map datasets), use partitioning: @@ -270,12 +282,19 @@ configs: - name: regulator_symbol dtype: string description: Transcription factor name + role: regulator_identifier + - name: target_locus_tag + dtype: string + description: Target gene systematic identifier + role: target_identifier - name: target_symbol dtype: string description: Target gene name + role: target_identifier - name: binding_score dtype: float64 description: Quantitative binding measurement + role: quantitative_measure - config_name: experiment_metadata description: Experimental conditions and sample information diff --git a/docs/tutorials/passing_callingcards.csv b/docs/tutorials/passing_callingcards.csv new file mode 100644 index 0000000..acaea06 --- /dev/null +++ b/docs/tutorials/passing_callingcards.csv @@ -0,0 +1,82 @@ +id,uploader,modifier,source_name,regulator_symbol,regulator_locus_tag,condition,data_usable,preferred_replicate,background_name,promoterset,batch,lab,assay,source_orig_id,upload_date,modified_date,file,single_binding,composite_binding,promoter,background,fileformat +8573,admin,admin,brent_nf_cc,SOK2,YMR016C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.335275-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8573.csv.gz,NA,1328,4,1,5 +8574,admin,admin,brent_nf_cc,RDR1,YOR380W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.373096-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8574.csv.gz,NA,1329,4,1,5 +8575,admin,admin,brent_nf_cc,PGD1,YGL025C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.674607-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8575.csv.gz,NA,1330,4,1,5 +8576,admin,admin,brent_nf_cc,TOG1,YER184C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.701098-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8576.csv.gz,NA,1331,4,1,5 +8577,admin,admin,brent_nf_cc,MET4,YNL103W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.787380-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8577.csv.gz,NA,1332,4,1,5 +8578,admin,admin,brent_nf_cc,CST6,YIL036W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.794787-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8578.csv.gz,NA,1333,4,1,5 +8579,admin,admin,brent_nf_cc,YOX1,YML027W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.813536-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8579.csv.gz,NA,1334,4,1,5 +8580,admin,admin,brent_nf_cc,RPD3,YNL330C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.866654-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8580.csv.gz,NA,1336,4,1,5 +8581,admin,admin,brent_nf_cc,MSN1,YOL116W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.844507-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8581.csv.gz,NA,1335,4,1,5 +8582,admin,admin,brent_nf_cc,HAP3,YBL021C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.918717-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8582.csv.gz,NA,1338,4,1,5 +8583,admin,admin,brent_nf_cc,SKN7,YHR206W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.924313-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8583.csv.gz,NA,1337,4,1,5 +8584,admin,admin,brent_nf_cc,SMP1,YBR182C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.984147-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8584.csv.gz,NA,1339,4,1,5 +8585,admin,admin,brent_nf_cc,MBP1,YDL056W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.068717-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8585.csv.gz,NA,1340,4,1,5 +8586,admin,admin,brent_nf_cc,RTG3,YBL103C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.078145-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8586.csv.gz,NA,1341,4,1,5 +8587,admin,admin,brent_nf_cc,DAT1,YML113W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.146444-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8587.csv.gz,NA,1342,4,1,5 +8588,admin,admin,brent_nf_cc,GZF3,YJL110C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.294324-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8588.csv.gz,NA,1344,4,1,5 +8589,admin,admin,brent_nf_cc,RDS2,YPL133C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.280370-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8589.csv.gz,NA,1343,4,1,5 +8590,admin,admin,brent_nf_cc,PDR3,YBL005W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.343977-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8590.csv.gz,NA,1346,4,1,5 +8591,admin,admin,brent_nf_cc,RFX1,YLR176C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.304403-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8591.csv.gz,NA,1345,4,1,5 +8592,admin,admin,brent_nf_cc,MET31,YPL038W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.521624-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8592.csv.gz,NA,1347,4,1,5 +8593,admin,admin,brent_nf_cc,INO4,YOL108C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:44.341119-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8593.csv.gz,NA,1349,4,1,5 +8594,admin,admin,brent_nf_cc,RGT1,YKL038W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:44.301633-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8594.csv.gz,NA,1348,4,1,5 +8595,admin,admin,brent_nf_cc,STP2,YHR006W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:44.653232-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8595.csv.gz,NA,1350,4,1,5 +8596,admin,admin,brent_nf_cc,AZF1,YOR113W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.090708-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8596.csv.gz,NA,1352,4,1,5 +8597,admin,admin,brent_nf_cc,ZAP1,YJL056C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.085289-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8597.csv.gz,NA,1351,4,1,5 +8598,admin,admin,brent_nf_cc,MOT3,YMR070W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.171772-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8598.csv.gz,NA,1353,4,1,5 +8599,admin,admin,brent_nf_cc,VHR1,YIL056W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.215135-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8599.csv.gz,NA,1354,4,1,5 +8600,admin,admin,brent_nf_cc,URC2,YDR520C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.286065-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8600.csv.gz,NA,1355,4,1,5 +8601,admin,admin,brent_nf_cc,INO2,YDR123C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.339489-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8601.csv.gz,NA,1356,4,1,5 +8602,admin,admin,brent_nf_cc,FKH2,YNL068C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.395206-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8602.csv.gz,NA,1357,4,1,5 +8603,admin,admin,brent_nf_cc,CHA4,YLR098C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.445223-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8603.csv.gz,NA,1360,4,1,5 +8604,admin,admin,brent_nf_cc,TYE7,YOR344C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.446764-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8604.csv.gz,NA,1359,4,1,5 +8605,admin,admin,brent_nf_cc,OAF1,YAL051W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.447196-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8605.csv.gz,NA,1358,4,1,5 +8606,admin,admin,brent_nf_cc,MET28,YIR017C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.551152-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8606.csv.gz,NA,1361,4,1,5 +8607,admin,admin,brent_nf_cc,AFT1,YGL071W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.825510-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8607.csv.gz,NA,1362,4,1,5 +8608,admin,admin,brent_nf_cc,GAT2,YMR136W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.897217-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8608.csv.gz,NA,1363,4,1,5 +8609,admin,admin,brent_nf_cc,PHO2,YDL106C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:46.029628-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8609.csv.gz,NA,1366,4,1,5 +8610,admin,admin,brent_nf_cc,DAL80,YKR034W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.927940-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8610.csv.gz,NA,1364,4,1,5 +8611,admin,admin,brent_nf_cc,UME1,YPL139C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.951004-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8611.csv.gz,NA,1365,4,1,5 +8612,admin,admin,brent_nf_cc,RLM1,YPL089C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:46.219541-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8612.csv.gz,NA,1367,4,1,5 +8613,admin,admin,brent_nf_cc,HAP5,YOR358W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:46.366453-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8613.csv.gz,NA,1368,4,1,5 +8614,admin,admin,brent_nf_cc,TUP1,YCR084C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:46.441695-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8614.csv.gz,NA,1369,4,1,5 +8615,admin,admin,brent_nf_cc,IXR1,YKL032C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:46.605683-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8615.csv.gz,NA,1370,4,1,5 +8616,admin,admin,brent_nf_cc,CYC8,YBR112C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.115289-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8616.csv.gz,NA,1371,4,1,5 +8617,admin,admin,brent_nf_cc,UPC2,YDR213W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.185506-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8617.csv.gz,NA,1372,4,1,5 +8618,admin,admin,brent_nf_cc,ARO80,YDR421W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.300436-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8618.csv.gz,NA,1373,4,1,5 +8619,admin,admin,brent_nf_cc,YHP1,YDR451C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.432083-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8619.csv.gz,NA,1374,4,1,5 +8620,admin,admin,brent_nf_cc,GAL11,YOL051W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.457453-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8620.csv.gz,NA,1375,4,1,5 +8621,admin,admin,brent_nf_cc,MED2,YDL005C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.457928-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8621.csv.gz,NA,1376,4,1,5 +8622,admin,admin,brent_nf_cc,GIS1,YDR096W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.495815-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8622.csv.gz,NA,1377,4,1,5 +8623,admin,admin,brent_nf_cc,STP1,YDR463W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.533662-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8623.csv.gz,NA,1379,4,1,5 +8624,admin,admin,brent_nf_cc,RTG1,YOL067C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.529096-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8624.csv.gz,NA,1378,4,1,5 +8625,admin,admin,brent_nf_cc,HAP2,YGL237C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.546475-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8625.csv.gz,NA,1380,4,1,5 +8626,admin,admin,brent_nf_cc,MSN2,YMR037C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.876943-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8626.csv.gz,NA,1381,4,1,5 +8627,admin,admin,brent_nf_cc,LEU3,YLR451W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:48.130584-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8627.csv.gz,NA,1382,4,1,5 +8628,admin,admin,brent_nf_cc,GAL4,YPL248C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:48.383735-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8628.csv.gz,NA,1383,4,1,5 +8629,admin,admin,brent_nf_cc,GCN4,YEL009C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:48.661669-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8629.csv.gz,NA,1384,4,1,5 +8630,admin,admin,brent_nf_cc,CRZ1,YNL027W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:48.724198-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8630.csv.gz,NA,1385,4,1,5 +8631,admin,admin,brent_nf_cc,STE12,YHR084W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:48.827903-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8631.csv.gz,NA,1386,4,1,5 +8632,admin,admin,brent_nf_cc,STP3,YLR375W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.012518-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8632.csv.gz,NA,1387,4,1,5 +8633,admin,admin,brent_nf_cc,PHD1,YKL043W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.172299-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8633.csv.gz,NA,1388,4,1,5 +8634,admin,admin,brent_nf_cc,RPN4,YDL020C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.219434-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8634.csv.gz,NA,1389,4,1,5 +8635,admin,admin,brent_nf_cc,ASH1,YKL185W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.252413-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8635.csv.gz,NA,1390,4,1,5 +8636,admin,admin,brent_nf_cc,TEA1,YOR337W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.600060-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8636.csv.gz,NA,1391,4,1,5 +8637,admin,admin,brent_nf_cc,SUM1,YDR310C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.893152-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8637.csv.gz,NA,1392,4,1,5 +8638,admin,admin,brent_nf_cc,NRG1,YDR043C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.991998-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8638.csv.gz,NA,1394,4,1,5 +8639,admin,admin,brent_nf_cc,TEC1,YBR083W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.962020-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8639.csv.gz,NA,1393,4,1,5 +8640,admin,admin,brent_nf_cc,CSE2,YNR010W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:50.509234-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8640.csv.gz,NA,1395,4,1,5 +8641,admin,admin,brent_nf_cc,ROX1,YPR065W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:50.797488-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8641.csv.gz,NA,1397,4,1,5 +8642,admin,admin,brent_nf_cc,FKH1,YIL131C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:50.782743-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8642.csv.gz,NA,1396,4,1,5 +8643,admin,admin,brent_nf_cc,GAL80,YML051W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:50.868439-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8643.csv.gz,NA,1398,4,1,5 +8644,admin,admin,brent_nf_cc,SKO1,YNL167C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:51.214395-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8644.csv.gz,NA,1399,4,1,5 +8645,admin,admin,brent_nf_cc,PDR1,YGL013C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:51.527347-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8645.csv.gz,NA,1400,4,1,5 +8646,admin,admin,brent_nf_cc,CIN5,YOR028C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:51.544390-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8646.csv.gz,NA,1401,4,1,5 +8647,admin,admin,brent_nf_cc,PPR1,YLR014C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:51.974579-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8647.csv.gz,NA,1402,4,1,5 +8648,admin,admin,brent_nf_cc,HAL9,YOL089C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:52.067382-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8648.csv.gz,NA,1403,4,1,5 +8649,admin,admin,brent_nf_cc,CBF1,YJR060W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:52.302853-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8649.csv.gz,NA,1404,4,1,5 +8650,admin,admin,brent_nf_cc,USV1,YPL230W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:52.353098-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8650.csv.gz,NA,1405,4,1,5 +8651,admin,admin,brent_nf_cc,RPH1,YER169W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:53.152828-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8651.csv.gz,NA,1406,4,1,5 +8652,admin,admin,brent_nf_cc,ERT1,YBR239C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:53.548696-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8652.csv.gz,NA,1407,4,1,5 +8653,admin,admin,brent_nf_cc,MET32,YDR253C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:54.027532-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8653.csv.gz,NA,1408,4,1,5 diff --git a/docs/tutorials/rank_response_tutorial.ipynb b/docs/tutorials/rank_response_tutorial.ipynb new file mode 100644 index 0000000..2e85f20 --- /dev/null +++ b/docs/tutorials/rank_response_tutorial.ipynb @@ -0,0 +1,968 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Rank-Response Analysis Tutorial\n", + "\n", + "This tutorial demonstrates how to conduct comprehensive rank-response analysis using tfbpapi to compare binding data with perturbation responses across multiple datasets.\n", + "\n", + "## Overview\n", + "\n", + "Rank-response analysis evaluates whether targets with stronger binding evidence are more likely to show transcriptional responses upon regulator perturbation. This approach helps validate functional relevance of binding data.\n", + "\n", + "### Datasets Used\n", + "- **Binding data**: Calling Cards, ChEC-seq, ChIP-chip\n", + "- **Perturbation data**: McIsaac (ZEV system), Kemmeren (overexpression), Mahendrawada (RNA-seq)\n", + "\n", + "### Analysis Strategy\n", + "1. Load and filter datasets to common regulators/targets\n", + "2. Rank targets by binding strength for each regulator\n", + "3. Label targets as responsive/non-responsive based on perturbation data\n", + "4. Perform binned analysis to assess rank-response relationship\n", + "5. Compare results across different data combinations" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "from tfbpapi.HfQueryAPI import HfQueryAPI\n", + "from tfbpapi.RankResponseAnalysis import RankResponseAnalyzer\n", + "from tfbpapi.datainfo.datacard import DataCard\n", + "from tfbpapi.errors import DataCardError, HfDataFetchError\n", + "import matplotlib.pyplot as plt\n", + "import seaborn as sns\n", + "\n", + "# Configure plotting\n", + "plt.style.use('default')\n", + "sns.set_palette(\"husl\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Dataset Exploration with DataCard\n", + "\n", + "Before loading data, let's explore dataset structure and metadata using the DataCard interface." + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Exploring dataset structures...\n", + "\n", + "==================================================\n", + "Exploring BrentLab/callingcards\n", + "Dataset types in configs: {'genome_map', 'annotated_features', 'metadata'}\n", + "Configurations: 4\n", + "\n", + "Config: annotated_features\n", + " Type: annotated_features\n", + " Features: 13\n", + " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", + " Target fields: ['target_locus_tag', 'target_symbol']\n", + "\n", + "Config: genome_map\n", + " Type: genome_map\n", + " Features: 7\n", + "\n", + "Config: annotated_features_meta\n", + " Type: metadata\n", + " Features: 9\n", + " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", + "\n", + "Config: genome_map_meta\n", + " Type: metadata\n", + " Features: 7\n", + " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", + "\n", + "==================================================\n", + "Exploring BrentLab/mahendrawada_2025\n", + "Dataset types in configs: {'genome_map', 'annotated_features', 'metadata', 'genomic_features'}\n", + "Configurations: 6\n", + "\n", + "Config: genomic_features\n", + " Type: genomic_features\n", + " Features: 24\n", + "\n", + "Config: mahendrawada_2025_metadata\n", + " Type: metadata\n", + " Features: 7\n", + " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", + "\n", + "Config: chec_seq_genome_map\n", + " Type: genome_map\n", + " Features: 3\n", + "\n", + "Config: mahendrawada_chec_seq\n", + " Type: annotated_features\n", + " Features: 6\n", + " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", + " Target fields: ['target_locus_tag', 'target_symbol']\n", + " Metadata fields: ['regulator_locus_tag', 'regulator_symbol']\n", + "\n", + "Config: reprocessed_chec_seq\n", + " Type: annotated_features\n", + " Features: 6\n", + " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", + " Target fields: ['target_locus_tag', 'target_symbol']\n", + "\n", + "Config: rna_seq\n", + " Type: annotated_features\n", + " Features: 5\n", + " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", + " Target fields: ['target_locus_tag', 'target_symbol']\n", + " Metadata fields: ['regulator_locus_tag', 'regulator_symbol']\n", + "\n", + "==================================================\n", + "Exploring BrentLab/harbison_2004\n", + "Dataset types in configs: {'annotated_features'}\n", + "Configurations: 1\n", + "\n", + "Config: harbison_2004\n", + " Type: annotated_features\n", + " Features: 7\n", + " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", + " Target fields: ['target_locus_tag', 'target_symbol']\n", + " Metadata fields: ['regulator_locus_tag', 'regulator_symbol', 'condition']\n", + "\n", + "==================================================\n", + "Exploring BrentLab/hackett_2020\n", + "Dataset types in configs: {'annotated_features'}\n", + "Configurations: 1\n", + "\n", + "Config: hackett_2020\n", + " Type: annotated_features\n", + " Features: 17\n", + " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", + " Target fields: ['target_locus_tag', 'target_symbol']\n", + " Metadata fields: ['regulator_locus_tag', 'regulator_symbol', 'time', 'mechanism', 'restriction', 'date', 'strain']\n", + "\n", + "==================================================\n", + "Exploring BrentLab/kemmeren_2014\n", + "Dataset types in configs: {'annotated_features'}\n", + "Configurations: 1\n", + "\n", + "Config: kemmeren_2014\n", + " Type: annotated_features\n", + " Features: 11\n", + " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", + " Target fields: ['target_locus_tag', 'target_symbol']\n", + " Metadata fields: ['regulator_locus_tag', 'regulator_symbol']\n", + "\n", + "==================================================\n", + "Successfully explored 5 datasets\n" + ] + } + ], + "source": [ + "# Explore dataset structure using DataCard\n", + "print(\"Exploring dataset structures...\")\n", + "\n", + "datasets_to_explore = [\n", + " \"BrentLab/callingcards\",\n", + " \"BrentLab/mahendrawada_2025\", \n", + " \"BrentLab/harbison_2004\",\n", + " \"BrentLab/hackett_2020\",\n", + " \"BrentLab/kemmeren_2014\"\n", + "]\n", + "\n", + "dataset_info = {}\n", + "\n", + "for repo_id in datasets_to_explore:\n", + " try:\n", + " print(f\"\\n{'='*50}\")\n", + " print(f\"Exploring {repo_id}\")\n", + "\n", + " # Create DataCard instance\n", + " datacard = DataCard(repo_id)\n", + " card = datacard.dataset_card\n", + "\n", + " # DatasetCard doesn't have dataset_type directly - it's on individual configs\n", + " dataset_types = [config.dataset_type.value for config in card.configs]\n", + " print(f\"Dataset types in configs: {set(dataset_types)}\")\n", + " print(f\"Configurations: {len(card.configs)}\")\n", + "\n", + " # Store dataset info for later use\n", + " dataset_info[repo_id] = {\n", + " 'datacard': datacard,\n", + " 'card': card,\n", + " 'configs': {config.config_name: config for config in card.configs}\n", + " }\n", + "\n", + " # Display configuration details\n", + " for config in card.configs:\n", + " print(f\"\\nConfig: {config.config_name}\")\n", + " print(f\" Type: {config.dataset_type.value}\")\n", + " print(f\" Features: {len(config.dataset_info.features) if config.dataset_info.features else 0}\")\n", + "\n", + " if config.dataset_info.features:\n", + " # Show regulator and target fields if available\n", + " feature_names = [f.name for f in config.dataset_info.features]\n", + " regulator_fields = [f for f in feature_names if 'regulator' in f.lower()]\n", + " target_fields = [f for f in feature_names if 'target' in f.lower()]\n", + "\n", + " if regulator_fields:\n", + " print(f\" Regulator fields: {regulator_fields}\")\n", + " if target_fields:\n", + " print(f\" Target fields: {target_fields}\")\n", + "\n", + " if config.metadata_fields:\n", + " print(f\" Metadata fields: {config.metadata_fields}\")\n", + "\n", + " except (DataCardError, HfDataFetchError) as e:\n", + " print(f\"Error exploring {repo_id}: {e}\")\n", + " continue\n", + " except Exception as e:\n", + " print(f\"Unexpected error exploring {repo_id}: {e}\")\n", + " continue\n", + "\n", + "print(f\"\\n{'='*50}\")\n", + "print(f\"Successfully explored {len(dataset_info)} datasets\")" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": {}, + "outputs": [], + "source": [ + "## Initialize dataset connections and load data\n" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Initializing HfQueryAPI connections...\n", + "All API connections initialized\n" + ] + } + ], + "source": [ + "\n", + "print(\"Initializing HfQueryAPI connections...\")\n", + "\n", + "mahendrawada_2025 = HfQueryAPI(repo_id=\"BrentLab/mahendrawada_2025\")\n", + "hackett_2020 = HfQueryAPI(repo_id=\"BrentLab/hackett_2020\")\n", + "callingcards = HfQueryAPI(repo_id=\"BrentLab/callingcards\")\n", + "harbison_2004 = HfQueryAPI(repo_id=\"BrentLab/harbison_2004\") \n", + "kemmeren_2014 = HfQueryAPI(repo_id=\"BrentLab/kemmeren_2014\")\n", + "\n", + "print(\"All API connections initialized\")" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Getting metadata from datasets...\n", + "All metadata loaded\n", + "Found 81 common regulators across all datasets\n" + ] + } + ], + "source": [ + "# Get metadata from each dataset to find common regulators\n", + "print(\"Getting metadata from datasets...\")\n", + "\n", + "harbison_meta = harbison_2004.get_metadata(\"harbison_2004\")\n", + "kemmeren_meta = kemmeren_2014.get_metadata(\"kemmeren_2014\") \n", + "callingcards_meta = callingcards.get_metadata(\"annotated_features\")\n", + "mahendrawada_checseq_meta = mahendrawada_2025.get_metadata(\"mahendrawada_chec_seq\")\n", + "mahendrawada_rnaseq_meta = mahendrawada_2025.get_metadata(\"rna_seq\")\n", + "hackett_2020_meta = hackett_2020.get_metadata(\"hackett_2020\")\n", + "\n", + "print(\"All metadata loaded\")\n", + "\n", + "# Get the intersection of common regulators\n", + "common_regulators = (set(mahendrawada_rnaseq_meta.regulator_locus_tag.unique())\n", + " & set(mahendrawada_checseq_meta.regulator_locus_tag.unique())\n", + " & set(hackett_2020_meta.regulator_locus_tag.unique())\n", + " & set(callingcards_meta.regulator_locus_tag.unique())\n", + " & set(harbison_meta.regulator_locus_tag.unique())\n", + " & set(kemmeren_meta.regulator_locus_tag.unique()))\n", + "\n", + "print(f\"Found {len(common_regulators)} common regulators across all datasets\")\n", + "\n", + "# Create proper SQL IN clause\n", + "if common_regulators:\n", + " regulator_clause = \"(\" + \", \".join(f\"'{reg}'\" for reg in common_regulators) + \")\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Filter the data" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Loading calling cards quality filter...\n", + "Found 81 passing calling cards experiments\n", + "Applying dataset-specific filters...\n", + "All filters applied\n" + ] + } + ], + "source": [ + "# Load and filter calling cards data with passing experiments\n", + "print(\"Loading calling cards quality filter...\")\n", + "\n", + "passing_cc = pd.read_csv(\"./passing_callingcards.csv\")\n", + "db_ids_as_str = [str(id) for id in passing_cc.id.unique()]\n", + "\n", + "# filter the callingcards_data on the db_id column using passing_cc.id\n", + "db_ids = callingcards_meta[callingcards_meta.db_id.isin(db_ids_as_str)].id\n", + "cc_ids_clause = \"(\" + \", \".join(f\"'{db_id}'\" for db_id in db_ids) + \")\"\n", + "\n", + "print(f\"Found {len(db_ids)} passing calling cards experiments\")\n", + "\n", + "# Apply dataset-specific filters\n", + "print(\"Applying dataset-specific filters...\")\n", + "\n", + "hackett_2020.set_sql_filter(\n", + " \"hackett_2020\",\n", + " f\"\"\"\n", + " time = 15 \n", + " AND mechanism = 'ZEV' \n", + " AND restriction = 'P' \n", + " AND regulator_locus_tag IN {regulator_clause}\n", + " \"\"\")\n", + "\n", + "mahendrawada_2025.set_sql_filter(\n", + " \"mahendrawada_chec_seq\",\n", + " f\"\"\"\n", + " regulator_locus_tag IN {regulator_clause}\n", + " \"\"\")\n", + "\n", + "mahendrawada_2025.set_sql_filter(\n", + " \"rna_seq\",\n", + " f\"\"\"\n", + " regulator_locus_tag IN {regulator_clause}\n", + " \"\"\")\n", + "\n", + "callingcards.set_sql_filter(\n", + " \"annotated_features\",\n", + " f\"\"\"\n", + " regulator_locus_tag IN {regulator_clause} \n", + " AND id IN {cc_ids_clause}\n", + " \"\"\")\n", + "\n", + "harbison_2004.set_sql_filter(\n", + " \"harbison_2004\",\n", + " f\"\"\"\n", + " regulator_locus_tag IN {regulator_clause} \n", + " AND condition = 'YPD'\n", + " \"\"\")\n", + "\n", + "print(\"All filters applied\")" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Loading initial datasets...\n", + "Initial datasets loaded\n", + "mcisaac: 61 regulators\n", + "mahendrawada_chec: 81 regulators\n", + "callingcards: 46 regulators\n", + "harbison: 81 regulators\n", + "Final intersection: 33 regulators\n" + ] + } + ], + "source": [ + "# Load initial datasets to determine final common regulators and targets\n", + "print(\"Loading initial datasets...\")\n", + "\n", + "mcisaac = hackett_2020.query(\"SELECT * FROM hackett_2020\", \"hackett_2020\")\n", + "mahendrawada_chec = mahendrawada_2025.query(\"SELECT * FROM mahendrawada_chec_seq\", \"mahendrawada_chec_seq\") \n", + "mahendrawada_perturb = mahendrawada_2025.query(\"SELECT * FROM rna_seq\", \"rna_seq\")\n", + "callingcards_data = callingcards.query(\"SELECT * FROM annotated_features\", \"annotated_features\")\n", + "harbison_data = harbison_2004.query(\"SELECT * FROM harbison_2004\", \"harbison_2004\")\n", + "mahendrawada_features = mahendrawada_2025.query(\"SELECT * FROM genomic_features\", \"genomic_features\")\n", + "\n", + "print(\"Initial datasets loaded\")\n", + "\n", + "# After running your current queries, check what you actually got\n", + "actual_regulators = {\n", + " 'mcisaac': set(mcisaac.regulator_locus_tag.unique()),\n", + " 'mahendrawada_chec': set(mahendrawada_chec.regulator_locus_tag.unique()),\n", + " 'callingcards': set(callingcards_data.regulator_locus_tag.unique()),\n", + " 'harbison': set(harbison_data.regulator_locus_tag.unique())\n", + "}\n", + "\n", + "for name, regulators in actual_regulators.items():\n", + " print(f\"{name}: {len(regulators)} regulators\")\n", + "\n", + "final_common = set.intersection(*actual_regulators.values())\n", + "print(f\"Final intersection: {len(final_common)} regulators\")\n", + "\n", + "final_common_clause = \"(\" + \", \".join(f\"'{reg}'\" for reg in final_common) + \")\"" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Finding common targets...\n", + "Found 5525 common targets\n", + "Loading final datasets with complete filtering...\n", + "Final datasets loaded with complete filtering\n", + "McIsaac: 187,884 rows, 33 regulators\n", + "Kemmeren: 178,332 rows, 33 regulators\n", + "Mahendrawada RNA-seq: 4,411 rows, 33 regulators\n", + "Calling Cards: 182,325 rows, 33 regulators\n", + "Mahendrawada ChEC: 22,478 rows, 33 regulators\n", + "Harbison: 182,952 rows, 33 regulators\n" + ] + } + ], + "source": [ + "# Find common targets across all datasets\n", + "print(\"Finding common targets...\")\n", + "\n", + "common_targets = (set(mcisaac.target_locus_tag.unique())\n", + " & set(mahendrawada_features.locus_tag.unique())\n", + " & set(callingcards_data.target_locus_tag.unique())\n", + " & set(harbison_data.target_locus_tag.unique()))\n", + "\n", + "print(f\"Found {len(common_targets)} common targets\")\n", + "\n", + "if common_targets:\n", + " target_clause = \"(\" + \", \".join(f\"'{tgt}'\" for tgt in common_targets) + \")\"\n", + "\n", + "# Load final datasets with both regulator and target filtering \n", + "print(\"Loading final datasets with complete filtering...\")\n", + "\n", + "mcisaac = hackett_2020.query(f\"\"\"\n", + " SELECT *\n", + " FROM hackett_2020\n", + " WHERE regulator_locus_tag in {final_common_clause} \n", + " AND target_locus_tag IN {target_clause}\n", + " \"\"\", \"hackett_2020\")\n", + "\n", + "mahendrawada_perturb = mahendrawada_2025.query(f\"\"\"\n", + " SELECT *\n", + " FROM rna_seq\n", + " WHERE regulator_locus_tag IN {final_common_clause} \n", + " AND target_locus_tag IN {target_clause}\n", + " \"\"\", \"rna_seq\")\n", + "\n", + "kemmeren = kemmeren_2014.query(f\"\"\"\n", + " SELECT * FROM kemmeren_2014 WHERE regulator_locus_tag IN {final_common_clause} \n", + " AND target_locus_tag IN {target_clause} \n", + " AND variable_in_wt = 0\n", + " \"\"\",\n", + " \"kemmeren_2014\")\n", + "\n", + "mahendrawada_chec = mahendrawada_2025.query(f\"SELECT * FROM mahendrawada_chec_seq WHERE regulator_locus_tag IN {final_common_clause} AND target_locus_tag IN {target_clause}\", \"mahendrawada_chec_seq\")\n", + "\n", + "callingcards_data = callingcards.query(f\"SELECT * FROM annotated_features WHERE regulator_locus_tag IN {final_common_clause} AND target_locus_tag IN {target_clause}\",\n", + " \"annotated_features\")\n", + "\n", + "harbison = harbison_2004.query(f\"SELECT * FROM harbison_2004 WHERE regulator_locus_tag IN {final_common_clause} AND target_locus_tag IN {target_clause}\",\n", + " \"harbison_2004\")\n", + "\n", + "print(\"Final datasets loaded with complete filtering\")\n", + "\n", + "# Print final dataset sizes\n", + "datasets_info = [\n", + " ('McIsaac', mcisaac), ('Kemmeren', kemmeren), ('Mahendrawada RNA-seq', mahendrawada_perturb),\n", + " ('Calling Cards', callingcards_data), ('Mahendrawada ChEC', mahendrawada_chec), ('Harbison', harbison)\n", + "]\n", + "\n", + "for name, data in datasets_info:\n", + " if len(data) > 0:\n", + " regulators = data['regulator_locus_tag'].nunique() if 'regulator_locus_tag' in data.columns else 0\n", + " print(f\"{name}: {len(data):,} rows, {regulators} regulators\")\n", + " else:\n", + " print(f\"{name}: No data loaded\")" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": {}, + "outputs": [], + "source": [ + "## 2. Rank Binding Data by Strength" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Binding datasets ranked by strength\n" + ] + } + ], + "source": [ + "\n", + "# Rank binding datasets by strength (stronger binding = lower rank)\n", + "callingcards_ranked = (callingcards_data\n", + " .sort_values(['regulator_locus_tag', 'poisson_pval', 'callingcards_enrichment'],\n", + " ascending=[True, True, False], # Lower p-value, higher enrichment = stronger\n", + " kind='stable')\n", + " .reset_index(drop=True)\n", + " )\n", + "\n", + "mahendrawada_chec_ranked = (mahendrawada_chec\n", + " .sort_values(['regulator_locus_tag', 'peak_score'],\n", + " ascending=[True, False], # Higher peak score = stronger\n", + " kind='stable')\n", + " .reset_index(drop=True)\n", + " )\n", + "\n", + "harbison_ranked = (harbison\n", + " .sort_values(['regulator_locus_tag', 'pvalue', 'effect'],\n", + " ascending=[True, True, False], # Lower p-value, higher effect = stronger\n", + " kind='stable')\n", + " .reset_index(drop=True)\n", + " )\n", + "\n", + "print(\"Binding datasets ranked by strength\")" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Labeling functions defined\n" + ] + } + ], + "source": [ + "\n", + "def label_by_mcisaac(df):\n", + " \"\"\"Label targets as responsive based on McIsaac perturbation data.\"\"\"\n", + " out = df.merge(\n", + " mcisaac[['regulator_locus_tag', 'target_locus_tag', 'log2_shrunken_timecourses']],\n", + " on=['regulator_locus_tag', 'target_locus_tag'],\n", + " how='left',\n", + " )\n", + "\n", + " out['is_responsive'] = (\n", + " out.get('log2_shrunken_timecourses', pd.Series(0)).fillna(0).abs() > 0)\n", + "\n", + " return out\n", + "\n", + "def label_by_kemmeren(df):\n", + " \"\"\"Label targets as responsive based on Kemmeren perturbation data.\"\"\" \n", + " out = df.merge(\n", + " kemmeren[['regulator_locus_tag', 'target_locus_tag', 'Madj', 'pval']],\n", + " on=['regulator_locus_tag', 'target_locus_tag'],\n", + " how='left',\n", + " )\n", + "\n", + " out['is_responsive'] = (\n", + " out.get('pval', pd.Series(1)).fillna(1) <= 0.05)\n", + "\n", + " return out\n", + "\n", + "def label_by_mahendrawada_rnaseq(df):\n", + " \"\"\"Label targets as responsive based on Mahendrawada RNA-seq data.\"\"\"\n", + " out = df.merge(\n", + " mahendrawada_perturb[['regulator_locus_tag', 'target_locus_tag', 'log2fc']],\n", + " on=['regulator_locus_tag', 'target_locus_tag'],\n", + " how='left',\n", + " )\n", + "\n", + " out['is_responsive'] = ~out.log2fc.isna()\n", + "\n", + " return out\n", + "\n", + "print(\"Labeling functions defined\")" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Analysis functions defined\n" + ] + } + ], + "source": [ + "\n", + "def get_rr_results(df):\n", + " \"\"\"\n", + " Perform rank-response analysis for each regulator in the dataset.\n", + "\n", + " :param df: DataFrame with ranked binding data and responsive labels\n", + "\n", + " :returns: DataFrame with bin-level results for all regulators\n", + " \"\"\"\n", + " res_dict = {}\n", + "\n", + " # For each regulator separately\n", + " for regulator in df['regulator_locus_tag'].unique():\n", + " regulator_data = df[\n", + " df['regulator_locus_tag'] == regulator\n", + " ]\n", + "\n", + " try:\n", + " analyzer = RankResponseAnalyzer(\n", + " data=regulator_data[['target_locus_tag', 'is_responsive']],\n", + " target_col='target_locus_tag',\n", + " responsive_col='is_responsive',\n", + " bin_size=5 # Small bin size for detailed analysis\n", + " )\n", + "\n", + " results = analyzer.get_bin_summary()\n", + " res_dict[regulator] = results\n", + "\n", + " except Exception as e:\n", + " print(f\"Warning: Failed to analyze {regulator}: {e}\")\n", + " continue\n", + "\n", + " if not res_dict:\n", + " return pd.DataFrame()\n", + "\n", + " return (pd.concat(res_dict, names=['regulator_locus_tag', 'original_index'])\n", + " .reset_index(level=0)\n", + " .reset_index(drop=True))\n", + "\n", + "def combine_rr_results(ranked_df, binding_source):\n", + " \"\"\"\n", + " Generate rank-response results for all perturbation sources and combine filtered results.\n", + "\n", + " :param ranked_df: DataFrame with ranked binding data\n", + " :param binding_source: String identifier for the binding data source\n", + "\n", + " :returns: Combined DataFrame with bin==5 results from all perturbation sources\n", + " \"\"\"\n", + " print(f\"Analyzing {binding_source} binding data...\")\n", + "\n", + " # Generate results for each perturbation source\n", + " cc_mcisaac_rr = get_rr_results(label_by_mcisaac(ranked_df))\n", + " cc_kem_rr = get_rr_results(label_by_kemmeren(ranked_df))\n", + " cc_mahendrawada_rr = get_rr_results(label_by_mahendrawada_rnaseq(ranked_df))\n", + "\n", + " # Add source columns to each result\n", + " cc_mcisaac_rr['binding_source'] = binding_source\n", + " cc_mcisaac_rr['perturbation_source'] = 'mcisaac'\n", + "\n", + " cc_kem_rr['binding_source'] = binding_source\n", + " cc_kem_rr['perturbation_source'] = 'kemmeren'\n", + "\n", + " cc_mahendrawada_rr['binding_source'] = binding_source\n", + " cc_mahendrawada_rr['perturbation_source'] = 'mahendrawada_rnaseq'\n", + "\n", + " # Filter to bin==5 (strongest binding bin) and combine\n", + " bin5_results = [cc_mcisaac_rr[cc_mcisaac_rr.bin == 5],\n", + " cc_kem_rr[cc_kem_rr.bin == 5],\n", + " cc_mahendrawada_rr[cc_mahendrawada_rr.bin == 5]]\n", + "\n", + " combined = pd.concat(bin5_results, ignore_index=True)\n", + "\n", + " # Find regulators that have observations from all 3 perturbation sources\n", + " regulator_counts = combined.groupby('regulator_locus_tag')['perturbation_source'].nunique()\n", + " complete_regulators = regulator_counts[regulator_counts == 3].index\n", + "\n", + " # Filter to only keep rows for regulators with all 3 perturbation sources\n", + " final_result = combined[combined.regulator_locus_tag.isin(complete_regulators)]\n", + "\n", + " print(f\"Analysis complete: {len(complete_regulators)} regulators with complete data\")\n", + "\n", + " return final_result\n", + "\n", + "print(\"Analysis functions defined\")" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Binding data ranked by strength\n" + ] + } + ], + "source": [ + "def rank_by_binding_strength(df, binding_cols, ascending_order):\n", + " \"\"\"\n", + " Rank targets by binding strength within each regulator group.\n", + "\n", + " :param df: DataFrame with binding data\n", + " :param binding_cols: List of columns to sort by\n", + " :param ascending_order: List of boolean values for sort order\n", + " \"\"\"\n", + " return (df\n", + " .sort_values(['regulator_locus_tag'] + binding_cols, \n", + " ascending=[True] + ascending_order, \n", + " kind='stable')\n", + " .reset_index(drop=True))\n", + "\n", + "# Rank binding datasets\n", + "callingcards_ranked = rank_by_binding_strength(\n", + " callingcards_data,\n", + " ['poisson_pval', 'callingcards_enrichment'],\n", + " [True, False] # Lower p-value and higher enrichment = stronger binding\n", + ")\n", + "\n", + "mahendrawada_chec_ranked = rank_by_binding_strength(\n", + " mahendrawada_chec,\n", + " ['peak_score'],\n", + " [False] # Higher peak score = stronger binding\n", + ")\n", + "\n", + "harbison_ranked = rank_by_binding_strength(\n", + " harbison_data,\n", + " ['pvalue', 'effect'],\n", + " [True, False] # Lower p-value and higher effect = stronger binding\n", + ")\n", + "\n", + "print(\"Binding data ranked by strength\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Rank by binding and label each target as responsive or non-responsive" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Executing comprehensive rank-response analysis...\n", + "This will analyze each binding dataset against all perturbation sources...\n", + "Analyzing callingcards binding data...\n", + "Analysis complete: 33 regulators with complete data\n", + "Analyzing mahendrawada_chec binding data...\n", + "Analysis complete: 33 regulators with complete data\n", + "Analyzing harbison binding data...\n", + "Analysis complete: 81 regulators with complete data\n", + "\n", + "==================================================\n", + "Individual dataset results:\n", + "Calling Cards: 99 result rows\n", + "ChEC-seq: 99 result rows\n", + "Harbison: 243 result rows\n" + ] + } + ], + "source": [ + "# Run analysis for all available binding datasets\n", + "print(\"Executing comprehensive rank-response analysis...\")\n", + "print(\"This will analyze each binding dataset against all perturbation sources...\")\n", + "\n", + "# Execute analysis for each binding dataset\n", + "cc_rr_res = combine_rr_results(callingcards_ranked, \"callingcards\")\n", + "chec_rr_res = combine_rr_results(mahendrawada_chec_ranked, \"mahendrawada_chec\") \n", + "harb_rr_res = combine_rr_results(harbison_ranked, \"harbison\")\n", + "\n", + "print(f\"\\n{'='*50}\")\n", + "print(\"Individual dataset results:\")\n", + "print(f\"Calling Cards: {len(cc_rr_res)} result rows\")\n", + "print(f\"ChEC-seq: {len(chec_rr_res)} result rows\") \n", + "print(f\"Harbison: {len(harb_rr_res)} result rows\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Visualize rank-response relationships" + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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7O3tBSMjI6Fhw4ZCbGysYGVlJfTr1++LxyOn/flUVsxPnz6Vm67s99LkyZMFR0dHQUdHR+4zosqxPH/+vODr6yuYmpoKxsbGQoMGDYSTJ08qLHv58mXB29tbMDQ0FBwdHYXJkycLK1euVPhsElH+Yk7PnJ45PXP63GRdIz79Z2hoKHh4eAhLlizJ9nP4ad6ZU26SXT7+4cMHYfDgwYKVlZVgYmIi+Pn5Cffv31dYpyq5fHZ554ULFwQvLy/BwMBAKF68uDB9+nRhwYIFAgDh8ePHuR6P//77T2jTpo1gYWEhmJubCx06dBAePnxYIPudnS995j7f7y9d03KKWxA+5rKDBg0SbGxsBIlEIpcnP336VGjXrp1gbGwsFC1aVOjbt69w9epVhc9FTp9VQfi/z+u///4r1K5dWzA0NBScnZ2FRYsWyZWTyWTCtGnTBGdnZ7Fe9vfffwv+/v6Cs7OzXNmTJ0+KOf6nxzO7PD89PV2YOHGi4OrqKujp6QlOTk7C6NGjhZSUFLlyqpxr2WFOT0TaSCIIHFknrwICArB582a19MtJRMp59uwZihUrhvHjx+f6+CsRqebVq1coWrQopkyZgjFjxhR2OEREBYY5PVHBY06fs+DgYCxbtgzJyck5DpBMRET0vfqmx8wgom9PVFQUMjMz0a1bt8IOhUhrffjwQWFaVr+8Pj4+BRsMERERfXeY03/0eU72/PlzrFmzBvXq1WNDBhERUTa++TEziOjbcOjQIVy/fh1Tp05F69at4eLiUtghEWmtjRs3IioqCs2bN4epqSmOHz+O9evX48cff0TdunULOzwiIiL6RjGnl1e7dm34+PigfPnyePLkCVauXIk3b97waRUiIqIcsDGDiLTCpEmTcPLkSdStWxcLFy4s7HCItFqVKlWgq6uLWbNm4c2bN+Kg4FOmTCns0IiIiOgbxpxeXvPmzbF582ZERERAIpGgWrVqWLlyJerXr1/YoREREWkkjplBREREREREREREREQajWNmEBERERERERERERGRRmNjBhERERERERERERERabTvbswMmUyGhw8fokiRIpBIJIUdDhERERUgQRDw9u1bODg4QEeH93QQaSvm9ERERN8v5vRE36/vrjHj4cOHcHJyKuwwiIiIqBDdv38fxYsXL+wwiCiPmNMTERERc3qi789315hRpEgRAB8veGZmZoUcDRERERWkN2/ewMnJScwHiEg7MacnIiL6fjGnJ/p+fXeNGVmPoZuZmbHiQ0RE9J1itzRE2o05PRERETGnJ/r+sGM5IiIiIiIiIiIiIiLSaGzMICIiIiIiIiIiIiIijcbGDCIiIiIiIiIiIiIi0mjf3ZgZRERERJogMzMT6enphR0GaQF9fX3o6PAeJCIiIiJNw5ye6OupUt9hYwYRERFRARIEAY8fP8arV68KOxTSEjo6OnB1dYW+vn5hh0JEREREYE5PpE6q1HfYmEFERERUgLIqPba2tjA2NoZEIinskEiDyWQyPHz4EI8ePUKJEiV4vhARERFpAOb0ROqhan2HjRlEREREBSQzM1Os9FhZWRV2OKQlbGxs8PDhQ2RkZEBPT6+wwyEiIiL6rjGnJ1IvVeo77HyXiIiIqIBk9adrbGxcyJGQNsl63DozM7OQIyEiIiIi5vRE6qVKfYeNGUREREQFjI+hkyp4vhARERFpHuZoROqhymeJjRlERERERERERERERKTR2JhBRERE9I1KSEiARCLBxYsXv2o9EyZMgIeHh/g6ICAArVu3/qp1ahIfHx8EBwerfb2fHzciIiIiou9ZftYjvqU6irrqcdlxcXFBWFiY2tdbUAq1MePo0aPw8/ODg4MDJBIJtm/f/sVloqOjUa1aNRgYGKB06dKIiorK9ziJiIiINE1AQAAkEon4z8rKCk2bNsXly5fFMk5OTnj06BEqVaqk1m3Pnz+/QHKwrH3s16+fwrwBAwZAIpEgICBA6fVFR0dDIpHg1atX6guSmNMTERERFbJP6wb6+vooXbo0Jk2ahIyMjK9er6Y1EOT0Q39B1VGyjvPp06flpqempsLKygoSiQTR0dFKr08Tj7EmK9TGjHfv3sHd3R2LFy9Wqnx8fDxatGiBBg0a4OLFiwgODkbv3r2xb9++fI6UiIiISPM0bdoUjx49wqNHj3Dw4EHo6uqiZcuW4nypVAp7e3vo6uqqdbvm5uawsLBQ6zpz4uTkhA0bNuDDhw/itJSUFPzxxx8oUaJEgcRAuWNOT0RERFT4suoGt2/fxvDhwzFhwgTMnj07T+vKzMyETCZTW2zqXl92CrqOEhkZKTdt27ZtMDU1LZDtf88KtTGjWbNmmDJlCtq0aaNU+aVLl8LV1RVz585F+fLlMXDgQLRv3x6//fZbPkdKREREpHkMDAxgb28Pe3t7eHh4YNSoUbh//z6ePn0KQPGupawnEw4ePIjq1avD2NgYderUwc2bN+XWO2PGDNjZ2aFIkSLo1asXUlJS5OZ/fveQj48PBg8ejJEjR8LS0hL29vaYMGGC3DI3btxAvXr1YGhoiAoVKuDAgQNK3cVfrVo1ODk5YevWreK0rVu3okSJEqhatapcWZlMhunTp8PV1RVGRkZwd3fH5s2bxWPRoEEDAEDRokUVnuqQyWS5xp+YmIhWrVrB1NQUZmZm6NixI548eaLScftWMacnIiIiKnxZdQNnZ2f0798fjRs3xo4dOwB8fGogJCQEjo6OMDExQa1ateSeHoiKioKFhQV27NiBChUqwMDAAD179sTq1avx119/iU8jREdHZ/u088WLFyGRSJCQkJDj+hITE8XyEydOhI2NDczMzNCvXz+kpaWJ8/bu3Yt69erBwsICVlZWaNmyJe7cuSPOd3V1BQBUrVoVEokEPj4+ABTrKKmpqRg8eDBsbW1haGiIevXq4ezZs+J8ZetG2fH391e44WrVqlXw9/dXKHv//n107NgRFhYWsLS0RKtWrcTjNGHChGyPcZa7d++iQYMGMDY2hru7O06dOiW37i1btqBixYowMDCAi4sL5s6dKzc/KSkJfn5+MDIygqurK9atW/fFfdN06r1NL5+dOnUKjRs3lpvm6+ubax/HqampSE1NFV+/efMmv8LLUUpKCu7fv5+v23BycoKhoWG+boOIiIg0V3JyMtauXYvSpUvDysoq17JjxozB3LlzYWNjg379+qFnz544ceIEAODPP//EhAkTsHjxYtSrVw9r1qzBggULULJkyVzXuXr1agwbNgwxMTE4deoUAgICULduXTRp0gSZmZlo3bo1SpQogZiYGLx9+xbDhw9Xet969uyJyMhIdO3aFcDHikKPHj0UHt+ePn061q5di6VLl8LNzQ1Hjx7FL7/8AhsbG9SrVw9btmxBu3btcPPmTZiZmcHIyEip+GUymdiQceTIEWRkZGDAgAH4+eefxRjyety+R8zps8d8noiIiNTJyMgIz58/BwAMHDgQ169fx4YNG+Dg4IBt27ahadOmuHLlCtzc3AAA79+/x8yZM7FixQpYWVmhWLFi+PDhA968eSM+hWBpaYmTJ08qtf3P12drawsAOHjwIAwNDREdHY2EhAT06NEDVlZWmDp1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I7LMmDED7dq1Q7du3VCtWjXExcVh3759KFq0qNKxfolEIoG1tbX4tMjnjI2NcfToUZQoUQJt27ZF+fLlxW6psuo12R1jZVSrVg1//vknNmzYgEqVKmH8+PGYNGkSAgICxDKRkZFwcHCAt7c32rZti8DAQNja2n71fhcmifBpB2ffgTdv3sDc3ByvX7/OtTJcWG7fvo2goCCEh4ezf10iIiI1K+w8ICUlBfHx8XB1dVXpR/xv0YkTJ1CvXj3ExcWhVKlShR2ORuN5o6iwP8tfwpyeiIgo/xR2HsDcjEi9VPlMadUA4ERERESknbZt2wZTU1O4ubkhLi4OQ4YMQd26ddmQQUREREREREphYwYRERER5bu3b9/if//7HxITE2FtbY3GjRur1LcuERERERERfd/YmEFERERE+a579+5yg0ATERERERERqaJQBwAnIiIiIiIiIiIiIiL6EjZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFG0y3sAIiIiIgISEpKwuvXrwtse+bm5rC1tS2w7WWJiopCcHAwXr16BQCYMGECtm/fjosXLwIAAgIC8OrVK2zfvr3AY8tvLi4uCA4ORnBwcGGHQkRERET54HvJ6bN8y7k7aSY2ZhAREREVsqSkJPTs0QOpaWkFtk0DfX2sioxUqfLz+PFjTJ06Fbt27cKDBw9ga2sLDw8PBAcHo1GjRmqJa/78+RAEQS3r+pKC2B8iIiIi+j5oS06vTgWZuxMBbMwgIiIiKnSvX79Galoa+pWtBgfjIvm+vYfv32LpzfN4/fq10hWfhIQE1K1bFxYWFpg9ezYqV66M9PR07Nu3DwMGDMCNGzfUEpu5ubla1vMl+bU/mZmZkEgk0NFhb65ERERE3xNtyOnVraByd6IsrGURERERaQgH4yJwMbXI9395qVwFBQVBIpHgzJkzaNeuHcqUKYOKFSti2LBhOH36tFhu3rx5qFy5MkxMTODk5ISgoCAkJycrvZ2AgAC0bt1afO3j44PBgwdj5MiRsLS0hL29PSZMmCC3zI0bN1CvXj0YGhqiQoUKOHDgACQSSa6Pu6trf6KiomBhYYEdO3agQoUKMDAwQGJiIpKSkuDn5wcjIyO4urpi3bp1ctsXBAET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zHD06FGEhYXhzZs3cHZ2xty5c9GsWTMAwMqVKxEYGIhq1arByckJ06ZNQ0hIiLi8Mrl3mTJl8M8//+DXX39FzZo1YWRkhFq1aqFz5875fixIM0gEVUZA/Aa8efMG5ubmeP36NczMzAo7HAW3b99GUFAQwsPD4ebmVtjhEBERfVMKOw9ISUlBfHw8XF1d5X5oT0pKQs8ePZD6FeNKqMpAXx+rIiNha2tbYNssKCdOnEC9evUQFxeHUqVKFXY4Xy2n8+Z7Vtif5S9hTk9ERJR/CjsPYE5PpF6q1Hf4ZAYRERFRIbO1tcWqyEi8fv26wLZpbm7+zVR6tm3bBlNTU7i5uSEuLg5DhgxB3bp1v4mGDCIiIiLSDszpifIfGzOIiIiINICtrS0rInn09u1b/O9//0NiYiKsra3RuHFjzJ07t7DDIiIiIqLvDHN6ovzFxgwiIiIi0mrdu3fP13E5iIiIiIiIqPDpFHYAREREREREREREREREuWFjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNN3CDoCIiIiIgKSkJLx+/brAtmdubg5bW1u1r1cikWDbtm1o3bq12tdNRERERKTJND2n9/HxgYeHB8LCwvIvKKJ8xMYMIiIiokKWlJSEnj0CkJqWXmDbNNDXw6rIKJUbNB4/foypU6di165dePDgAWxtbeHh4YHg4GA0atToi8snJCTA1dU123mnTp3CDz/8AABIS0tDWFgY1q1bh9u3b8PY2Bhly5ZF79698csvv0BPT0+luImIiIiI8pM25fRE2oqNGURERESF7PXr10hNS0cDDwFFTfN/ey+TgcMX0/H69WuVKj4JCQmoW7cuLCwsMHv2bFSuXBnp6enYt28fBgwYgBs3bii9rgMHDqBixYpy06ysrAB8bMjw9fXFpUuXMHnyZNStWxdmZmY4ffo05syZg6pVq8LDw0PpbRERERER5Tdtyem/Venp6bzh6TvAMTOIiIiINERRU8DaPP//5bVyFRQUBIlEgjNnzqBdu3YoU6YMKlasiGHDhuH06dNiuWfPnqFNmzYwNjaGm5sbduzYobAuKysr2Nvby/3LqnyEhYXh6NGjOHjwIAYMGAAPDw+ULFkSXbp0QUxMDNzc3HKM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b/VcZX6Xd9p67FiPIyLKX4q3PRWgYcOGYfny5Vi9ejViY2PRv39/vHv3Dj169AAAdO/eXW4wQT8/PyxZsgQbNmxAfHw89u/fj3HjxsHPz0+jBnwizSWVShEYGIiYmBiEhobi+vXreP/+Pa5fv47Q0FDExMQgMDCQ5xMVOn19fbRt2xYvX75Ely5dsGvXLjx79gy7du1Cly5d8PLlS7Rt25aDxn1DeH36Pujp6QH4+GMukbLS0tIAQGM//8zpiYiyx/wu77T12LEe932RyWSFHQLRN0GVm7cKdcyMn3/+GU+fPsX48ePx+PFjeHh4YO/eveIAgomJiXKt72PHjoVEIsHYsWPx4MED2NjYwM/PD1OnTi2sXSAt5OXlhXHjxiEiIgJDhgwRp9vb22PcuHHw8vIqxOiI/k9gYCAAYOvWrQgLCxOnS6VSdOjQQZxP3w5en759UqkUFhYWSEpKAgAYGxtDIpEUclSkyWQyGZ4+fQpjY2OFgUQ1BXN6IqKcMb/LO209dqzHffv09fWho6ODhw8fwsbGBvr6+szpifJIEAQ8ffoUEolEvPkvN4VeIxo4cGCOj6BHR0fLvdbV1UVoaChCQ0MLIDL6lnl5eaFOnTq4evUqnj9/DisrK1SqVEnj7uogCgwMREBAAHbu3ImHDx/CwcEBfn5+vJPnG8br07cvaxyBrAYNoi/R0dFBiRIlNLqSzJyeiChnzO/yTluPHetx3zYdHR24urri0aNHHMydSA0kEgmKFy+u1LW90BsziAqLVCqFu7t7YYdB9EX6+vpo165dYYdBBYjXp2+bRCJBsWLFYGtri/T09MIOh7RA1t1/RESkvZjf5Z22HjvW475t+vr6KFGiBDIyMpCZmVnY4RBpNT09PaUbqdmYQURERFQIpFKpxt9VSERERERE2cvqFkeZrnGISD14ixcREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUb7qsaMlJQUdcVBRERERESFgDk9ERERERFpA5UbM2QyGSZPngxHR0eYmpri7t27AIBx48Zh5cqVag+QiIiIiIjUizk9ERERERFpG5UbM6ZMmYKoqCjMmjUL+vr64vRKlSphxYoVag2OiIiIiIjUjzk9ERERERFpG5UbM37//XdERESga9eukEql4nR3d3fcuHFDrcEREREREZH6MacnIiIiIiJto3JjxoMHD1C6dGmF6TKZDOnp6WoJioiIiIiI8g9zeiIiIiIi0jYqN2ZUqFABx44dU5i+efNmVK1aVS1BERERERFR/mFOT0RERERE2kZX1QXGjx8Pf39/PHjwADKZDFu3bsXNmzfx+++/4++//86PGImIiIiISI2Y0xMRERERkbZR+cmMVq1aYefOnThw4ABMTEwwfvx4xMbGYufOnWjSpEl+xEhERERERGrEnJ6IiIiIiLSNyk9mAICXlxf279+v7liIiIiIiKiAMKcnIiIiIiJtovKTGSVLlsTz588Vpr969QolS5ZUS1BERERERJR/mNMTEREREZG2UfnJjISEBGRmZipMT01NxYMHD9QSFFFBSEtLw86dO/Hw4UM4ODjAz88P+vr6hR0WkQKeq18nMzMTV69exfPnz2FlZYVKlSpBKpUWdljfJJ6rRNqDOT19C/gdT/R90NYck3ETEamf0o0ZO3bsEP/et28fzM3NxdeZmZk4ePAgXFxcVA5g8eLFmD17Nh4/fgx3d3csXLgQNWvWzLH8q1evMGbMGGzduhUvXryAs7MzwsLC0Lx5c5W3Td+viIgIbN26Va4Sv3z5crRt2xaBgYGFGBmRPJ6rX+fYsWOIiIjA48ePxWn29vYIDAyEl5dXIUb27eG5SqQdmNPTt4Lf8UTfB23NMRk3EVH+ULoxo3Xr1gAAiUQCf39/uXl6enpwcXHB3LlzVdr4xo0bMWzYMCxduhS1atVCWFgYfH19cfPmTdja2iqUT0tLQ5MmTWBra4vNmzfD0dER9+7dg4WFhUrbpe9bREQENm3ahKJFiyIgIAA//PADTp8+jaioKGzatAkA+CVNGoHn6tc5duwYJk+ejFq1amH06NFwdXVFfHw81q9fj8mTJ2PcuHH8sUNNeK4SaQ/m9PQt4Hc80fdBW3NMxk1ElH+UHjNDJpNBJpOhRIkSSEpKEl/LZDKkpqbi5s2baNmypUobnzdvHvr06YMePXqgQoUKWLp0KYyNjbFq1apsy69atQovXrzA9u3bUbduXbi4uMDb2xvu7u4qbZe+X2lpadi6dSuKFi2KP/74A82bN4elpSWaN2+OP/74A0WLFsXWrVuRlpZW2KHSd47n6tfJzMxEREQEatWqhYkTJ6JChQowMjJChQoVMHHiRNSqVQsRERHZdrFCquG5SqRdmNOTtuN3PNH3QVtzTMZNRJS/VB4APD4+HtbW1l+94bS0NJw7dw6NGzf+v2B0dNC4cWOcOnUq22V27NiB2rVrY8CAAbCzs0OlSpUwbdq0XBPV1NRUvHnzRu4ffb927tyJzMxMBAQEQFdX/sEkXV1d+Pv7IzMzEzt37iykCIk+4rn6da5evYrHjx+jc+fO0NGR/6rT0dFB586d8fjxY1y9erWQIvx28Fwl0k7M6Ulb8Tue6PugrTkm4yYiyl8qDwAOAO/evcORI0eQmJio0Co7ePBgpdbx7NkzZGZmws7OTm66nZ0dbty4ke0yd+/exaFDh9C1a1fs3r0bcXFxCAoKQnp6OkJDQ7NdZvr06Zg4caJSMdG37+HDhwCAH374Idv5tWrVkitHVFh4rn6d58+fAwBcXV2znZ/VH3xWOco7nqtE2os5PWkjfscTfR+0Ncdk3ERE+UvlxowLFy6gefPmeP/+Pd69ewdLS0s8e/YMxsbGsLW1VbrikxcymQy2traIiIiAVCqFp6cnHjx4gNmzZ+dY8Rk9ejSGDRsmvn7z5g2cnJzyLUbSbA4ODgCA06dPZzvAZExMjFw5osLCc/XrWFlZAfh453GFChUU5ickJMiVo7zjuUqknZjTk7bidzzR90Fbc0zGTUSUv1TuZmro0KHw8/PDy5cvYWRkhNOnT+PevXvw9PTEnDlzlF6PtbU1pFIpnjx5Ijf9yZMnsLe3z3aZYsWKoUyZMpBKpeK08uXL4/Hjxzn222dgYAAzMzO5f/T98vPzg1QqRVRUFDIyMuTmZWRkYPXq1ZBKpfDz8yukCIk+4rn6dSpVqgR7e3usX78eMplMbp5MJsP69ethb2+PSpUqFVKE3w6eq0TaiTk9aSt+xxN9H7Q1x2TcRET5S+XGjIsXL2L48OHQ0dGBVCpFamoqnJycMGvWLPz6669Kr0dfXx+enp44ePCgOE0mk+HgwYOoXbt2tsvUrVsXcXFxcknrrVu3UKxYMejr66u6K/Qd0tfXR9u2bfHy5Ut06dIFu3btwrNnz7Br1y506dIFL1++RNu2bXk+UaHjufp1pFIpAgMDERMTg9DQUFy/fh3v37/H9evXERoaipiYGAQGBsr9kEZ5w3OVSDsxpydtxe94ou+DtuaYjJuIKH+p3M2Unp6eONCara0tEhMTUb58eZibm+P+/fsqrWvYsGHw9/dH9erVUbNmTYSFheHdu3fo0aMHAKB79+5wdHTE9OnTAQD9+/fHokWLMGTIEAwaNAi3b9/GtGnT8vUxePr2BAYGAgC2bt2KsLAwcbpUKkWHDh3E+USFjefq1/Hy8sK4ceMQERGBIUOGiNPt7e0xbtw4eHl5FWJ03xaeq0Tahzk9aTN+xxN9H7Q1x2TcRET5R+XGjKpVq+Ls2bNwc3ODt7c3xo8fj2fPnmHNmjUqP8r7888/4+nTpxg/fjweP34MDw8P7N27VxxAMDExUaxkAYCTkxP27duHoUOHokqVKnB0dMSQIUPwv//9T9XdoO9cYGAgAgICsHPnTjx8+BAODg7w8/PjXQakcXiufh0vLy/UqVMHV69exfPnz2FlZYVKlSrxbs18wHOVSLswpydtx+94ou+DtuaYjJuIKH+o3Jgxbdo0vH37FgAwdepUdO/eHf3794ebmxtWrlypcgADBw7EwIEDs50XHR2tMK127do4ffq0ytsh+py+vj7atWtX2GEQfRHP1a8jlUrh7u5e2GF8F3iuEmkP5vT0LeB3PNH3QVtzTMZNRKR+KjdmVK9eXfzb1tYWe/fuVWtARERERESUv5jTExERERGRtlF5APCcnD9/Hi1btlTX6oiIiIiIqIAxpyciIiIiIk2lUmPGvn37EBISgl9//RV3794FANy4cQOtW7dGjRo1IJPJ8iVIIiIiIiJSD+b0RERERESkjZTuZmrlypXo06cPLC0t8fLlS6xYsQLz5s3DoEGD8PPPP+Pq1asoX758fsZKRERERERfgTk9ERERERFpK6WfzJg/fz5mzpyJZ8+e4c8//8SzZ88QHh6OK1euYOnSpaz0EBERERFpOOb0RERERESkrZRuzLhz5w46dOgAAGjbti10dXUxe/ZsFC9ePN+CIyIiIiIi9WFOT0RERERE2krpxowPHz7A2NgYACCRSGBgYIBixYrlW2BERERERKRezOmJiIiIiEhbKT1mBgCsWLECpqamAICMjAxERUXB2tparszgwYPVFx0REREREakVc3oiIiIiItJGSjdmlChRAsuXLxdf29vbY82aNXJlJBIJKz5ERERERBqKOT0REREREWkrpRszEhIS8jEMIiIiIiLKb8zpiYiIiIhIWyk9ZgYREREREREREREREVFhYGMGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUbLU2PGnTt3MHbsWHTu3BlJSUkAgD179uDatWtqDY6IiIiIiPIHc3oiIiIiItImKjdmHDlyBJUrV0ZMTAy2bt2K5ORkAMClS5cQGhqq9gCJiIiIiEi9mNMTEREREZG2UbkxY9SoUZgyZQr2798PfX19cXrDhg1x+vRptQZHRERERETqx5yeiIiIiIi0jcqNGVeuXEGbNm0Uptva2uLZs2dqCYqIiIiIiPIPc3oiIiIiItI2KjdmWFhY4NGjRwrTL1y4AEdHR7UERURERERE+Yc5PRERERERaRuVGzM6deqE//3vf3j8+DEkEglkMhlOnDiBkJAQdO/ePT9iJCIiIiIiNWJOT0RERERE2kblxoxp06ahXLlycHJyQnJyMipUqID69eujTp06GDt2bH7ESEREREREasScnoiIiIiItI2uqgvo6+tj+fLlGD9+PK5cuYLk5GRUrVoVbm5u+REfERERERGpGXN6IiIiIiLSNio3ZmRxcnKCk5MTMjMzceXKFbx8+RJFixZVZ2xERERERJSPmNMTEREREZG2ULmbqeDgYKxcuRIAkJmZCW9vb1SrVg1OTk6Ijo5Wd3xERERERKRmzOmJiIiIiEjbqNyYsXnzZri7uwMAdu7cibt37+LGjRsYOnQoxowZo/YAiYiIiIhIvZjTExERERGRtlG5MePZs2ewt7cHAOzevRsdO3ZEmTJl0LNnT1y5ckXtARIRERERkXoxpyciIiIiIm2jcmOGnZ0drl+/jszMTOzduxdNmjQBALx//x5SqVTtARIRERERkXoxpyciIiIiIm2j8gDgPXr0QMeOHVGsWDFIJBI0btwYABATE4Ny5cqpPUAiIiIiIlIv5vRERERERKRtVG7MmDBhAipVqoT79++jQ4cOMDAwAABIpVKMGjVK7QESEREREZF6MacnIiIiIiJto3JjBgC0b99eYZq/v/9XB0NERERERAWDOT0REREREWmTPDVmHDx4EAcPHkRSUhJkMpncvFWrVqklMCIiIiIiyj/M6YmIiIiISJuo3JgxceJETJo0CdWrVxf72CUiIiIiIu3BnJ6IiIiIiLSNyo0ZS5cuRVRUFLp165Yf8RARERERUT5jTk9ERERERNpGR9UF0tLSUKdOnfyIhYiIiIiICgBzeiIiIiIi0jYqN2b07t0bf/zxR37EQkREREREBYA5PRERERERaRuVu5lKSUlBREQEDhw4gCpVqkBPT09u/rx589QWHBERERERqR9zeiIiIiIi0jYqN2ZcvnwZHh4eAICrV6/KzePAgUREREREmo85PRERERERaRuVGzMOHz6cH3EQEREREVEBYU5PRERERETaRuUxMz7133//4b///lNXLEREREREVMCY0xMRERERkTZQuTFDJpNh0qRJMDc3h7OzM5ydnWFhYYHJkydDJpPlR4xERERERKRGzOmJiIiIiEjbqNzN1JgxY7By5UrMmDEDdevWBQAcP34cEyZMQEpKCqZOnar2IImIiIiISH2Y0xMRERERkbZRuTFj9erVWLFiBX766SdxWpUqVeDo6IigoCBWfIiIiIiINBxzeiIiIiIi0jYqdzP14sULlCtXTmF6uXLl8OLFC7UERURERERE+Yc5PRERERERaRuVGzPc3d2xaNEihemLFi2Cu7u7WoIiIiIiIqL8w5yeiIiIiIi0jcrdTM2aNQstWrTAgQMHULt2bQDAqVOncP/+fezevVvtARIRERERkXoxpyciIiIiIm2j8pMZ3t7euHXrFtq0aYNXr17h1atXaNu2LW7evAkvL6/8iJGIiIiIiNSIOT0REREREWkblZ/MAAAHBwcOCkhEREREpMWY0xMRERERkTbJU2PGy5cvsXLlSsTGxgIAKlSogB49esDS0lKtwRERERERUf5gTk9ERERERNpE5W6mjh49ChcXFyxYsAAvX77Ey5cvsWDBAri6uuLo0aP5ESMREREREakRc3oiIiIiItI2Kj+ZMWDAAPz8889YsmQJpFIpACAzMxNBQUEYMGAArly5ovYgiYiIiIhIfZjTExERERGRtlH5yYy4uDgMHz5crPQAgFQqxbBhwxAXF6fW4IiIiIiISP2Y0xMRERERkbZRuTGjWrVqYr+6n4qNjYW7u7tagiIiIiIiovzDnJ6IiIiIiLSNyt1MDR48GEOGDEFcXBx++OEHAMDp06exePFizJgxA5cvXxbLVqlSRX2REhERERGRWjCnJyIiIiIibaNyY0bnzp0BACNHjsx2nkQigSAIkEgkyMzM/PoIiYiIiIhIrZjTExERERGRtlG5MSM+Pj4/4iAiIiIiogLCnJ6IiIiIiLSNyo0Zzs7O+REHEREREREVEOb0RERERESkbVQeAHz16tXYtWuX+HrkyJGwsLBAnTp1cO/ePbUGR0RERERE6secnoiIiIiItI3KjRnTpk2DkZERAODUqVNYtGgRZs2aBWtrawwdOlTtARIRERERkXoxpyciIiIiIm2jcjdT9+/fR+nSpQEA27dvR/v27REYGIi6devCx8dH3fEREREREZGaMacnIiIiIiJto/KTGaampnj+/DkA4J9//kGTJk0AAIaGhvjw4UOegli8eDFcXFxgaGiIWrVq4cyZM0ott2HDBkgkErRu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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Summary Statistics by Method Combination:\n", + "============================================================\n", + " Count Mean Median Std Min Max\n", + "binding_source perturbation_source \n", + "Calling Cards kemmeren 33 0.309 0.4 0.265 0.0 0.8\n", + " mahendrawada_rnaseq 33 0.103 0.0 0.167 0.0 0.6\n", + " mcisaac 33 0.297 0.2 0.292 0.0 1.0\n", + "ChEC-seq kemmeren 33 0.261 0.2 0.247 0.0 0.8\n", + " mahendrawada_rnaseq 33 0.091 0.0 0.181 0.0 0.8\n", + " mcisaac 33 0.255 0.2 0.284 0.0 0.8\n", + "Harbison kemmeren 81 0.074 0.0 0.177 0.0 0.8\n", + " mahendrawada_rnaseq 81 0.020 0.0 0.098 0.0 0.6\n", + " mcisaac 81 0.064 0.0 0.158 0.0 0.8\n", + "\n", + "Overall Performance by Binding Method:\n", + "========================================\n", + " Count Mean Median Std\n", + "binding_source \n", + "Calling Cards 99 0.236 0.2 0.262\n", + "ChEC-seq 99 0.202 0.2 0.252\n", + "Harbison 243 0.053 0.0 0.149\n" + ] + } + ], + "source": [ + "\n", + "# Combine all rank-response results for comparison\n", + "combined_results = []\n", + "\n", + "if len(cc_rr_res) > 0:\n", + " cc_rr_res['binding_source'] = 'Calling Cards'\n", + " combined_results.append(cc_rr_res)\n", + "\n", + "if len(chec_rr_res) > 0:\n", + " chec_rr_res['binding_source'] = 'ChEC-seq' \n", + " combined_results.append(chec_rr_res)\n", + "\n", + "if len(harb_rr_res) > 0:\n", + " harb_rr_res['binding_source'] = 'Harbison'\n", + " combined_results.append(harb_rr_res)\n", + "\n", + "if combined_results:\n", + " all_rr_data = pd.concat(combined_results, ignore_index=True)\n", + "\n", + " # Create comparative boxplot visualizations\n", + " fig, axes = plt.subplots(1, 2, figsize=(16, 6))\n", + "\n", + " # 1. Combined boxplot - binding methods grouped by perturbation source\n", + " sns.boxplot(data=all_rr_data, x='perturbation_source', y='response_rate', \n", + " hue='binding_source', ax=axes[0])\n", + " axes[0].set_title('Response Rate Performance by Perturbation and Binding Method')\n", + " axes[0].set_xlabel('Perturbation Source')\n", + " axes[0].set_ylabel('Response Rate')\n", + " axes[0].legend(title='Binding Method', bbox_to_anchor=(1.05, 1), loc='upper left')\n", + " axes[0].tick_params(axis='x', rotation=45)\n", + "\n", + " # 2. Faceted view - binding methods grouped by binding source \n", + " sns.boxplot(data=all_rr_data, x='binding_source', y='response_rate',\n", + " hue='perturbation_source', ax=axes[1])\n", + " axes[1].set_title('Response Rate Performance by Binding and Perturbation Method')\n", + " axes[1].set_xlabel('Binding Source')\n", + " axes[1].set_ylabel('Response Rate')\n", + " axes[1].legend(title='Perturbation Method', bbox_to_anchor=(1.05, 1), loc='upper left')\n", + " axes[1].tick_params(axis='x', rotation=45)\n", + "\n", + " plt.tight_layout()\n", + " plt.show()\n", + "\n", + " # Summary statistics table\n", + " print(\"Summary Statistics by Method Combination:\")\n", + " print(\"=\"*60)\n", + " summary_stats = all_rr_data.groupby(['binding_source', 'perturbation_source']).agg({\n", + " 'response_rate': ['count', 'mean', 'median', 'std', 'min', 'max']\n", + " }).round(3)\n", + " summary_stats.columns = ['Count', 'Mean', 'Median', 'Std', 'Min', 'Max']\n", + " print(summary_stats)\n", + "\n", + " # Overall performance by binding method\n", + " print(f\"\\nOverall Performance by Binding Method:\")\n", + " print(\"=\"*40)\n", + " binding_performance = all_rr_data.groupby('binding_source').agg({\n", + " 'response_rate': ['count', 'mean', 'median', 'std']\n", + " }).round(3)\n", + " binding_performance.columns = ['Count', 'Mean', 'Median', 'Std']\n", + " print(binding_performance)\n", + "\n", + "else:\n", + " print(\"No results available for comparison visualization\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "tfbpapi-py3.11", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/mkdocs.yml b/mkdocs.yml index e6fd78e..a751e9f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -152,6 +152,7 @@ nav: - "DataCard: Exploring Datasets": tutorials/datacard_tutorial.ipynb - "Cache Management": tutorials/cache_manager_tutorial.ipynb - "Querying the Datasets": tutorials/hfqueryapi_tutorial.ipynb + - "Rank Response Analysis": tutorials/rank_response_tutorial.ipynb - API Reference: - Core Components: - HfQueryAPI: HfQueryAPI.md diff --git a/pyproject.toml b/pyproject.toml index 72a4b01..ee98517 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,6 +35,8 @@ mkdocs-autorefs = "^1.4.3" mkdocs-section-index = "^0.3.10" mkdocs-jupyter = "^0.25.1" mkdocstrings = {extras = ["python"], version = "^0.30.0"} +matplotlib = "^3.10.6" +seaborn = "^0.13.2" [tool.pytest.ini_options] diff --git a/tfbpapi/HfCacheManager.py b/tfbpapi/HfCacheManager.py index d8b9ad9..4d2e38e 100644 --- a/tfbpapi/HfCacheManager.py +++ b/tfbpapi/HfCacheManager.py @@ -25,8 +25,16 @@ def __init__( self.duckdb_conn = duckdb_conn self.logger = logger or logging.getLogger(__name__) - def _get_metadata_for_config(self, config) -> dict[str, Any]: - """Get metadata for a specific configuration using 3-case strategy.""" + def _get_metadata_for_config( + self, config, force_refresh: bool = False + ) -> dict[str, Any]: + """ + Get metadata for a specific configuration using 3-case strategy. + + :param config: Configuration object to process + :param force_refresh: If True, skip cache checks and download fresh from remote + + """ config_result = { "config_name": config.config_name, "strategy": None, @@ -38,31 +46,33 @@ def _get_metadata_for_config(self, config) -> dict[str, Any]: table_name = f"metadata_{config.config_name}" try: - # Case 1: Check if metadata already exists in DuckDB - if self._check_metadata_exists_in_duckdb(table_name): - config_result.update( - { - "strategy": "duckdb_exists", - "table_name": table_name, - "success": True, - "message": f"Metadata table {table_name} " - "already exists in DuckDB", - } - ) - return config_result - - # Case 2: Check if HF data is in cache, create DuckDB representation - if self._load_metadata_from_cache(config, table_name): - config_result.update( - { - "strategy": "cache_loaded", - "table_name": table_name, - "success": True, - "message": "Loaded metadata from cache " - f"into table {table_name}", - } - ) - return config_result + # Skip cache checks if force_refresh is True + if not force_refresh: + # Case 1: Check if metadata already exists in DuckDB + if self._check_metadata_exists_in_duckdb(table_name): + config_result.update( + { + "strategy": "duckdb_exists", + "table_name": table_name, + "success": True, + "message": f"Metadata table {table_name} " + "already exists in DuckDB", + } + ) + return config_result + + # Case 2: Check if HF data is in cache, create DuckDB representation + if self._load_metadata_from_cache(config, table_name): + config_result.update( + { + "strategy": "cache_loaded", + "table_name": table_name, + "success": True, + "message": "Loaded metadata from cache " + f"into table {table_name}", + } + ) + return config_result # Case 3: Download from HF (explicit vs embedded) if self._download_and_load_metadata(config, table_name): @@ -157,12 +167,21 @@ def _download_and_load_metadata(self, config, table_name: str) -> bool: if file_path.exists() and file_path.suffix == ".parquet": downloaded_files.append(str(file_path)) else: - # Handle wildcard patterns - parent_dir = Path(downloaded_path) / Path(pattern).parent - if parent_dir.exists(): + # Handle wildcard patterns, including nested wildcards + if "*" in pattern: + # Use glob on the full pattern relative to downloaded_path + base_path = Path(downloaded_path) + matching_files = list(base_path.glob(pattern)) downloaded_files.extend( - [str(f) for f in parent_dir.glob("*.parquet")] + [str(f) for f in matching_files if f.suffix == ".parquet"] ) + else: + # Handle non-wildcard patterns that might be directories + parent_dir = Path(downloaded_path) / Path(pattern).parent + if parent_dir.exists(): + downloaded_files.extend( + [str(f) for f in parent_dir.glob("*.parquet")] + ) if not downloaded_files: self.logger.warning( diff --git a/tfbpapi/HfQueryAPI.py b/tfbpapi/HfQueryAPI.py index ddca995..02ae684 100644 --- a/tfbpapi/HfQueryAPI.py +++ b/tfbpapi/HfQueryAPI.py @@ -1,10 +1,13 @@ import logging +import re from pathlib import Path from typing import Literal +import duckdb import pandas as pd -from .constants import CACHE_DIR +from .constants import CACHE_DIR, SQL_FILTER_KEYWORDS +from .errors import InvalidFilterFieldError from .HfCacheManager import HfCacheManager @@ -17,6 +20,7 @@ def __init__( repo_type: Literal["model", "dataset", "space"] = "dataset", token: str | None = None, cache_dir: str | Path | None = None, + duckdb_conn: duckdb.DuckDBPyConnection = duckdb.connect(":memory:"), ): """ Initialize the minimal HF Query API client. @@ -27,10 +31,7 @@ def __init__( :param cache_dir: HF cache_dir for downloads """ - # No DuckDB connection needed for metadata-only functionality - import duckdb - - self._duckdb_conn = duckdb.connect(":memory:") + self._duckdb_conn = duckdb_conn # Initialize parent with minimal setup super().__init__( @@ -45,7 +46,9 @@ def __init__( self.cache_dir = Path(cache_dir) if cache_dir is not None else CACHE_DIR # Filter storage system - self._table_filters: dict[str, str] = {} # config_name -> SQL WHERE clause + # dict structure: + # {config_name: "SQL WHERE clause", ...} + self._table_filters: dict[str, str] = {} @property def cache_dir(self) -> Path: @@ -59,124 +62,494 @@ def cache_dir(self, value: str | Path) -> None: raise FileNotFoundError(f"Cache directory {path} does not exist") self._cache_dir = path - def get_metadata(self, config_name: str | None = None) -> pd.DataFrame: + def _get_explicit_metadata(self, config, table_name: str) -> pd.DataFrame: + """Helper function to handle explicit metadata configurations.""" + sql = f"SELECT * FROM {table_name}" + return self.duckdb_conn.execute(sql).fetchdf() + + def _get_embedded_metadata(self, config, table_name: str) -> pd.DataFrame: + """Helper function to handle embedded metadata configurations.""" + if config.metadata_fields is None: + raise ValueError(f"Config {config.config_name} has no metadata fields") + fields = ", ".join(config.metadata_fields) + where_clauses = " AND ".join( + [f"{field} IS NOT NULL" for field in config.metadata_fields] + ) + sql = f""" + SELECT DISTINCT {fields}, COUNT(*) as count + FROM {table_name} + WHERE {where_clauses} + GROUP BY {fields} + ORDER BY count DESC """ - Retrieve metadata as a DataFrame with actual metadata values. + return self.duckdb_conn.execute(sql).fetchdf() - For explicit metadata (dataset_type == METADATA): Returns all rows from metadata - table. For embedded metadata (has metadata_fields): Returns distinct - combinations of metadata fields. + def _validate_metadata_fields( + self, config_name: str, field_names: list[str] + ) -> None: + """ + Validate that field names exist in the config's metadata columns. - :param config_name: Optional specific config name, otherwise returns all - metadata - :return: DataFrame with metadata values - :raises ValueError: If config_name is specified but not found + :param config_name: Configuration name to validate against + :param field_names: List of field names to validate + :raises InvalidFilterFieldError: If any fields don't exist in metadata """ - # Get explicit metadata configurations - explicit_metadata_configs = self.dataset_card.get_metadata_configs() - - # Get data configurations that have embedded metadata - # (metadata_fields specified) - embedded_metadata_configs = [ - config - for config in self.dataset_card.get_data_configs() - if config.metadata_fields - ] - - # Combine both types - all_metadata_sources = explicit_metadata_configs + embedded_metadata_configs - - if not all_metadata_sources: - # Return empty DataFrame - return pd.DataFrame() - - # Filter by config_name if specified - if config_name: - matching_configs = [ - c for c in all_metadata_sources if c.config_name == config_name - ] - if not matching_configs: - available_names = [c.config_name for c in all_metadata_sources] - raise ValueError( - f"Config '{config_name}' not found. " - f"Available metadata configs: {available_names}" + if not field_names: + return + + try: + metadata_df = self.get_metadata(config_name) + if metadata_df.empty: + raise InvalidFilterFieldError( + config_name=config_name, + invalid_fields=field_names, + available_fields=[], ) - configs_to_process = matching_configs - else: - configs_to_process = all_metadata_sources - # Process each configuration and collect DataFrames - dataframes = [] - for config in configs_to_process: - # Ensure the data/metadata is loaded - config_result = self._get_metadata_for_config(config) + available_fields = list(metadata_df.columns) + invalid_fields = [ + field for field in field_names if field not in available_fields + ] - if not config_result.get("success", False): - self.logger.warning( - f"Failed to load data for config {config.config_name}" + if invalid_fields: + raise InvalidFilterFieldError( + config_name=config_name, + invalid_fields=invalid_fields, + available_fields=available_fields, ) + except Exception as e: + if isinstance(e, InvalidFilterFieldError): + raise + # If metadata retrieval fails for other reasons, log warning but allow + self.logger.warning( + f"Could not validate filter fields for {config_name}: {e}" + ) + + def _extract_fields_from_sql(self, sql_where: str) -> list[str]: + """ + Extract potential field names from SQL WHERE clause. + + Uses a more robust approach to identify column references while avoiding string + literals used as values. + + :param sql_where: SQL WHERE clause (without 'WHERE' keyword) + :return: List of potential field names found in the SQL + + """ + if not sql_where.strip(): + return [] + + field_names = set() + + # Tokenize the SQL to better understand context + # This regex splits on key tokens while preserving them + tokens = re.findall( + r""" + \bIN\s*\([^)]+\)| # IN clauses with content + \bBETWEEN\s+\S+\s+AND\s+\S+| # BETWEEN clauses + (?:'[^']*')|(?:"[^"]*")| # Quoted strings + \b(?:AND|OR|NOT|IS|NULL|LIKE|BETWEEN|IN)\b| # SQL keywords + [=!<>]+| # Comparison operators + [(),]| # Delimiters + \b[a-zA-Z_][a-zA-Z0-9_]*\b| # Identifiers + \S+ # Other tokens + """, + sql_where, + re.VERBOSE | re.IGNORECASE, + ) + + # Track the context to determine if an identifier is a field name or value + i = 0 + while i < len(tokens): + token = tokens[i].strip() + if not token: + i += 1 + continue + + # Skip IN clauses entirely - they contain values, not field names + if re.match(r"\bIN\s*\(", token, re.IGNORECASE): + i += 1 continue - table_name = config_result.get("table_name") - if not table_name: - self.logger.warning(f"No table name for config {config.config_name}") + # Skip BETWEEN clauses entirely - they contain values, not field names + if re.match(r"\bBETWEEN\b", token, re.IGNORECASE): + i += 1 continue - try: - if config in explicit_metadata_configs: - # Explicit metadata: return all rows from metadata table - sql = f"SELECT * FROM {table_name}" - else: - # Embedded metadata: return distinct combinations of metadata fields - if config.metadata_fields is None: - raise ValueError( - f"Config {config.config_name} has no metadata fields" + # Handle quoted strings - could be identifiers or values depending on context + if token.startswith(("'", '"')): + # Extract the content inside quotes + quoted_content = token[1:-1] + + # Find next significant token to determine context + next_significant_token = None + for j in range(i + 1, len(tokens)): + next_token = tokens[j].strip() + if next_token and next_token not in [" ", "\n", "\t"]: + next_significant_token = next_token + break + + # Check if this quoted string is a field name based on context + is_quoted_field = False + + # Check what comes after this quoted string + if next_significant_token: + # If followed by comparison operators or SQL keywords, it's a field name + if ( + next_significant_token + in ["=", "!=", "<>", "<", ">", "<=", ">="] + or next_significant_token.upper() in ["IS", "LIKE", "NOT"] + or re.match( + r"\bBETWEEN\b", next_significant_token, re.IGNORECASE ) - fields = ", ".join(config.metadata_fields) - where_clauses = " AND ".join( - [f"{field} IS NOT NULL" for field in config.metadata_fields] - ) - sql = f""" - SELECT DISTINCT {fields}, COUNT(*) as count - FROM {table_name} - WHERE {where_clauses} - GROUP BY {fields} - ORDER BY count DESC - """ - - df = self.duckdb_conn.execute(sql).fetchdf() - - # Add config source column if multiple configs - if len(configs_to_process) > 1: - df["config_name"] = config.config_name - - dataframes.append(df) - - except Exception as e: - self.logger.error( - f"Error querying metadata for {config.config_name}: {e}" - ) + or re.match(r"\bIN\s*\(", next_significant_token, re.IGNORECASE) + ): + is_quoted_field = True + + # Also check what comes before this quoted string + if not is_quoted_field and i > 0: + # Find the previous significant token + prev_significant_token = None + for j in range(i - 1, -1, -1): + prev_token = tokens[j].strip() + if prev_token and prev_token not in [" ", "\n", "\t"]: + prev_significant_token = prev_token + break + + # If preceded by a comparison operator, could be a field name + # But we need to be very careful not to treat string literals as field names + if prev_significant_token and prev_significant_token in [ + "=", + "!=", + "<>", + "<", + ">", + "<=", + ">=", + ]: + # Only treat as field name if it looks like a database identifier + # AND doesn't look like a typical string value + if self._looks_like_identifier( + quoted_content + ) and self._looks_like_database_identifier(quoted_content): + is_quoted_field = True + + if is_quoted_field: + field_names.add(quoted_content) + + i += 1 + continue + + # Skip SQL keywords and operators + if token.upper() in SQL_FILTER_KEYWORDS or token in [ + "=", + "!=", + "<>", + "<", + ">", + "<=", + ">=", + "(", + ")", + ",", + ]: + i += 1 + continue + + # Skip numeric literals + if re.match(r"^-?\d+(\.\d+)?$", token): + i += 1 continue - # Combine all DataFrames - if not dataframes: - return pd.DataFrame() - elif len(dataframes) == 1: - return dataframes[0] + # Check if this looks like an identifier (field name) + if re.match(r"^[a-zA-Z_][a-zA-Z0-9_]*$", token): + # Check the context - if the next non-whitespace token is a comparison operator, + # then this is likely a field name + next_significant_token = None + for j in range(i + 1, len(tokens)): + next_token = tokens[j].strip() + if next_token and next_token not in [" ", "\n", "\t"]: + next_significant_token = next_token + break + + # Check if followed by a comparison operator or SQL keyword that indicates a field + is_field = False + + if next_significant_token: + # Direct comparison operators + if next_significant_token in [ + "=", + "!=", + "<>", + "<", + ">", + "<=", + ">=", + ]: + is_field = True + # SQL keywords that follow field names + elif next_significant_token.upper() in ["IS", "LIKE", "NOT"]: + is_field = True + # BETWEEN clause (could be just 'BETWEEN' or 'BETWEEN ... AND ...') + elif next_significant_token.upper() == "BETWEEN" or re.match( + r"\bBETWEEN\b", next_significant_token, re.IGNORECASE + ): + is_field = True + # IN clause (could be just 'IN' or 'IN (...)') + elif next_significant_token.upper() == "IN" or re.match( + r"\bIN\s*\(", next_significant_token, re.IGNORECASE + ): + is_field = True + + # If not a field yet, check other contexts + if not is_field and i > 0: + # Find the previous significant token + prev_significant_token = None + for j in range(i - 1, -1, -1): + prev_token = tokens[j].strip() + if prev_token and prev_token not in [" ", "\n", "\t"]: + prev_significant_token = prev_token + break + + # Case 1: After AND/OR and before an operator (original logic) + if ( + prev_significant_token + and prev_significant_token.upper() in ["AND", "OR"] + and next_significant_token + ): + # Same checks as above + if next_significant_token in [ + "=", + "!=", + "<>", + "<", + ">", + "<=", + ">=", + ]: + is_field = True + elif next_significant_token.upper() in ["IS", "LIKE", "NOT"]: + is_field = True + elif next_significant_token.upper() == "BETWEEN" or re.match( + r"\bBETWEEN\b", next_significant_token, re.IGNORECASE + ): + is_field = True + elif next_significant_token.upper() == "IN" or re.match( + r"\bIN\s*\(", next_significant_token, re.IGNORECASE + ): + is_field = True + + # Case 2: After a comparison operator (second operand) + elif prev_significant_token and prev_significant_token in [ + "=", + "!=", + "<>", + "<", + ">", + "<=", + ">=", + ]: + # But exclude function names (identifiers followed by '(') + if next_significant_token != "(": + is_field = True + + # Case 3: After opening parenthesis (function parameter) + elif prev_significant_token == "(": + is_field = True + + if is_field: + field_names.add(token) + + i += 1 + + return list(field_names) + + def _looks_like_identifier(self, content: str) -> bool: + """ + Determine if quoted content looks like an identifier rather than a string + literal. + + :param content: The content inside quotes + :return: True if it looks like an identifier, False if it looks like a string + literal + + """ + if not content: + return False + + # Basic identifier pattern: starts with letter/underscore, contains only alphanumeric/underscore + if re.match(r"^[a-zA-Z_][a-zA-Z0-9_]*$", content): + return True + + # Extended identifier pattern: could contain spaces if it's a column name like "quoted field" + # but not if it contains many special characters or looks like natural language + if " " in content: + # If it contains spaces, it should still look identifier-like + # Allow simple cases like "quoted field" but not "this is a long string value" + words = content.split() + if len(words) <= 3 and all( + re.match(r"^[a-zA-Z_][a-zA-Z0-9_]*$", word) for word in words + ): + return True + return False + + return False + + def _looks_like_database_identifier(self, content: str) -> bool: + """ + Determine if content looks like a database identifier (field/table name). + + This is more strict than _looks_like_identifier and helps distinguish between + quoted identifiers like "field_name" and string values like "value1". + + :param content: The content to check + :return: True if it looks like a database identifier + + """ + if not content: + return False + + # Database identifiers typically: + # 1. Don't start with numbers (field names rarely start with numbers) + # 2. Often contain underscores or descriptive words + # 3. Don't look like simple values + + # Reject if starts with a number (like "value1", "123abc") + if content[0].isdigit(): + return False + + # Handle short simple values that could be literals or field names + if len(content) <= 6 and re.match(r"^[a-z]+\d*$", content.lower()): + # Allow common field name prefixes + field_prefixes = ["field", "col", "column", "attr", "prop"] + if any(content.lower().startswith(prefix) for prefix in field_prefixes): + return True # It's a valid field name like "field1", "col2" + else: + return False # It's likely a simple value like "value", "test" + + # Accept if it contains underscore (common in field names) + if "_" in content: + return True + + # Accept if it has multiple words (like "quoted field") + if " " in content: + return True + + # Accept if it's a longer descriptive name + if len(content) > 8: + return True + + # Reject otherwise (likely a simple value) + return False + + def get_metadata( + self, config_name: str, refresh_cache: bool = False + ) -> pd.DataFrame: + """ + Retrieve metadata as a DataFrame with actual metadata values for a specific + config. + + Supports three types of metadata retrieval: + 1. Direct metadata configs: config_name is itself a metadata config + 2. Embedded metadata: config_name has metadata_fields defined + 3. Applied metadata: config_name appears in another metadata config's applies_to list + + For explicit metadata configs (types 1 & 3), returns all rows from metadata table. + For embedded metadata (type 2), returns distinct combinations of metadata fields. + + :param config_name: Specific config name to retrieve metadata for + :param refresh_cache: If True, force refresh from remote instead of using cache + :return: DataFrame with metadata values for the specified config + :raises ValueError: If config_name has no associated metadata + :raises RuntimeError: If data loading fails for the config + + """ + # Get metadata relationships for this config + relationships = self.get_metadata_relationships(refresh_cache=refresh_cache) + + relevant_relationships = None + + # First priority: data_config matches (config_name is a data config with metadata) + data_config_matches = [r for r in relationships if r.data_config == config_name] + + if data_config_matches: + relevant_relationships = data_config_matches else: - return pd.concat(dataframes, ignore_index=True) + # Second priority: metadata_config matches (config_name is itself a metadata config) + metadata_config_matches = [ + r for r in relationships if r.metadata_config == config_name + ] + relevant_relationships = metadata_config_matches + + if not relevant_relationships: + # Check what configs are available for helpful error message + all_data_configs = {r.data_config for r in relationships} + all_metadata_configs = {r.metadata_config for r in relationships} + all_available = sorted(all_data_configs | all_metadata_configs) + + if not all_available: + return pd.DataFrame() + + raise ValueError( + f"Config '{config_name}' not found. " + f"Available configs with metadata: {all_available}" + ) + + # Get the config object to process + # For explicit relationships, use the metadata config + # For embedded relationships, use the data config + relationship = relevant_relationships[0] # Use first relationship found + + if relationship.relationship_type == "explicit": + # Find the metadata config + if relationship.metadata_config == config_name: + # config_name is itself a metadata config + config = self.get_config(config_name) + else: + # config_name is a data config with metadata applied to it + config = self.get_config(relationship.metadata_config) + else: # embedded + # config_name is a data config with embedded metadata + config = self.get_config(config_name) + + if not config: + raise ValueError(f"Could not find config object for '{config_name}'") + + # Process the single configuration + config_result = self._get_metadata_for_config( + config, force_refresh=refresh_cache + ) + + if not config_result.get("success", False): + raise RuntimeError(f"Failed to load data for config {config.config_name}") + + table_name = config_result.get("table_name") + if not table_name: + raise RuntimeError(f"No table name for config {config.config_name}") + + try: + if relationship.relationship_type == "explicit": + return self._get_explicit_metadata(config, table_name) + else: # embedded + return self._get_embedded_metadata(config, table_name) + except Exception as e: + self.logger.error(f"Error querying metadata for {config.config_name}: {e}") + raise def set_filter(self, config_name: str, **kwargs) -> None: """ Set simple filters using keyword arguments. Converts keyword arguments to SQL WHERE clause and stores - for automatic application. + for automatic application. Validates that all filter fields + exist in the config's metadata columns. :param config_name: Configuration name to apply filters to :param kwargs: Filter conditions as keyword arguments (e.g., time=15, mechanism="ZEV") + :raises InvalidFilterFieldError: If any filter field doesn't exist + in the metadata columns Example: api.set_filter("hackett_2020", time=15, mechanism="ZEV", restriction="P") @@ -188,6 +561,9 @@ def set_filter(self, config_name: str, **kwargs) -> None: self.clear_filter(config_name) return + # Validate that all filter fields exist in metadata columns + self._validate_metadata_fields(config_name, list(kwargs.keys())) + # Convert kwargs to SQL WHERE clause conditions = [] for key, value in kwargs.items(): @@ -205,23 +581,37 @@ def set_filter(self, config_name: str, **kwargs) -> None: self._table_filters[config_name] = where_clause self.logger.info(f"Set filter for {config_name}: {where_clause}") - def set_sql_filter(self, config_name: str, sql_where: str) -> None: + def set_sql_filter( + self, config_name: str, sql_where: str, validate_fields: bool = True + ) -> None: """ Set complex filters using SQL WHERE clause. Stores raw SQL WHERE clause for automatic application to queries. + Validates that field references in the SQL exist in metadata columns + unless validation is disabled. :param config_name: Configuration name to apply filters to :param sql_where: SQL WHERE clause (without the 'WHERE' keyword) + :param validate_fields: Whether to validate field names (default: True) + :raises InvalidFilterFieldError: If any field references don't exist + in the metadata columns (when validate_fields=True) Example: api.set_sql_filter("hackett_2020", "time IN (15, 30) AND mechanism = 'ZEV'") + # To skip validation for complex SQL: + api.set_sql_filter("hackett_2020", "complex_expression(...)", validate_fields=False) """ if not sql_where.strip(): self.clear_filter(config_name) return + # Validate fields if requested + if validate_fields: + extracted_fields = self._extract_fields_from_sql(sql_where) + self._validate_metadata_fields(config_name, extracted_fields) + self._table_filters[config_name] = sql_where.strip() self.logger.info(f"Set SQL filter for {config_name}: {sql_where}") @@ -246,7 +636,9 @@ def get_current_filter(self, config_name: str) -> str | None: """ return self._table_filters.get(config_name) - def query(self, sql: str, config_name: str) -> pd.DataFrame: + def query( + self, sql: str, config_name: str, refresh_cache: bool = False + ) -> pd.DataFrame: """ Execute SQL query with automatic filter application. @@ -255,6 +647,7 @@ def query(self, sql: str, config_name: str) -> pd.DataFrame: :param sql: SQL query to execute :param config_name: Configuration name to query (table will be loaded if needed) + :param refresh_cache: If True, force refresh from remote instead of using cache :return: DataFrame with query results :raises ValueError: If config_name not found or query fails @@ -266,19 +659,21 @@ def query(self, sql: str, config_name: str) -> pd.DataFrame: """ # Validate config exists - if config_name not in [c.config_name for c in self.dataset_card.configs]: - available_configs = [c.config_name for c in self.dataset_card.configs] + if config_name not in [c.config_name for c in self.configs]: + available_configs = [c.config_name for c in self.configs] raise ValueError( f"Config '{config_name}' not found. " f"Available configs: {available_configs}" ) # Load the configuration data - config = self.dataset_card.get_config_by_name(config_name) + config = self.get_config(config_name) if not config: raise ValueError(f"Could not retrieve config '{config_name}'") - config_result = self._get_metadata_for_config(config) + config_result = self._get_metadata_for_config( + config, force_refresh=refresh_cache + ) if not config_result.get("success", False): raise ValueError( f"Failed to load data for config '{config_name}': " diff --git a/tfbpapi/HfRankResponse.py b/tfbpapi/HfRankResponse.py index b52f6f9..67a9c82 100644 --- a/tfbpapi/HfRankResponse.py +++ b/tfbpapi/HfRankResponse.py @@ -24,7 +24,7 @@ def __init__(self, db: IncrementalAnalysisDB): """ self.db = db - self.logger = logging.getLogger(__name__) + self.logger = logging.getLogger(self.__class__.__name__) def compute( self, diff --git a/tfbpapi/RankResponseAnalysis.py b/tfbpapi/RankResponseAnalysis.py new file mode 100644 index 0000000..036e08a --- /dev/null +++ b/tfbpapi/RankResponseAnalysis.py @@ -0,0 +1,277 @@ +""" +Simplified rank-response analysis for pre-arranged binding and perturbation data. + +This module provides a streamlined approach to rank-response analysis where: +1. Binding data is already ranked by strength (rank 1 = strongest binding) +2. Perturbation data provides a simple responsive TRUE/FALSE column +3. Analysis focuses on binning and statistical calculations + +""" + +import logging +from typing import Any, Dict, Optional, Tuple + +import numpy as np +import pandas as pd + + +class RankResponseAnalyzer: + """ + Simplified rank-response analyzer for pre-arranged data. + + Takes a DataFrame with target identifiers (pre-sorted by binding strength) and + responsive boolean values, then performs binning analysis. + + """ + + def __init__( + self, + data: pd.DataFrame, + target_col: str, + responsive_col: str, + bin_size: int = 100, + ): + """ + Initialize the rank-response analyzer. + + :param data: DataFrame with target identifiers and responsive booleans + :param target_col: Name of column containing target identifiers + :param responsive_col: Name of column containing TRUE/FALSE responsive values + :param bin_size: Number of targets per bin for analysis + :raises ValueError: If data validation fails + + """ + self.logger = logging.getLogger(self.__class__.__name__) + + # Store parameters + self.target_col = target_col + self.responsive_col = responsive_col + self.bin_size = bin_size + + # Validate and store data + self.data = self._validate_data(data) + self.n_targets = len(self.data) + self.n_bins = (self.n_targets + bin_size - 1) // bin_size # Ceiling division + + # Calculate overall statistics + self.total_responsive = self.data[responsive_col].sum() + self.overall_response_rate = self.total_responsive / self.n_targets + + self.logger.info( + f"Initialized RankResponseAnalyzer: {self.n_targets} targets, " + f"{self.total_responsive} responsive ({self.overall_response_rate:.1%}), " + f"{self.n_bins} bins of size {bin_size}" + ) + + def _validate_data(self, data: pd.DataFrame) -> pd.DataFrame: + """Validate input data and return cleaned version.""" + if not isinstance(data, pd.DataFrame): + raise ValueError("Data must be a pandas DataFrame") + + if len(data) == 0: + raise ValueError("Data cannot be empty") + + # Check required columns exist + if self.target_col not in data.columns: + raise ValueError(f"Target column '{self.target_col}' not found in data") + if self.responsive_col not in data.columns: + raise ValueError( + f"Responsive column '{self.responsive_col}' not found in data" + ) + + # Extract just the required columns + clean_data = data[[self.target_col, self.responsive_col]].copy() + + # Check for missing values + if clean_data[self.target_col].isna().any(): + raise ValueError( + f"Target column '{self.target_col}' contains missing values" + ) + if clean_data[self.responsive_col].isna().any(): + raise ValueError( + f"Responsive column '{self.responsive_col}' contains missing values" + ) + + # Validate responsive column is boolean-like + unique_values = set(clean_data[self.responsive_col].unique()) + valid_boolean_sets = [ + {True, False}, + {1, 0}, + {1.0, 0.0}, + {"TRUE", "FALSE"}, + {"True", "False"}, + {"true", "false"}, + {"T", "F"}, + ] + + # Allow subsets (e.g., only True values, only False values) + is_valid_boolean = any( + unique_values.issubset(valid_set) for valid_set in valid_boolean_sets + ) + + if not is_valid_boolean: + raise ValueError( + f"Responsive column '{self.responsive_col}' must contain boolean-like values. " + f"Found: {unique_values}" + ) + + # Convert to standard boolean + clean_data[self.responsive_col] = clean_data[self.responsive_col].astype(bool) + + # Reset index to ensure proper ranking (rank 1 = index 0) + clean_data = clean_data.reset_index(drop=True) + + self.logger.debug( + f"Validated data: {len(clean_data)} rows, {unique_values} -> boolean" + ) + + return clean_data + + def create_bins(self) -> pd.DataFrame: + """ + Create bins from the ranked data. + + :return: DataFrame with bin assignments for each target + + """ + bins_data = self.data.copy() + bins_data["rank"] = range(1, len(bins_data) + 1) + bins_data["bin"] = ((bins_data["rank"] - 1) // self.bin_size) + 1 + + return bins_data + + def calculate_bin_stats(self) -> pd.DataFrame: + """ + Calculate statistics for each bin. + + :return: DataFrame with bin-level statistics + + """ + bins_data = self.create_bins() + + bin_stats = [] + for bin_num in range(1, self.n_bins + 1): + bin_data = bins_data[bins_data["bin"] == bin_num] + + n_targets_in_bin = len(bin_data) + n_responsive_in_bin = bin_data[self.responsive_col].sum() + response_rate = ( + n_responsive_in_bin / n_targets_in_bin if n_targets_in_bin > 0 else 0 + ) + + # Calculate rank range for this bin + min_rank = bin_data["rank"].min() + max_rank = bin_data["rank"].max() + + bin_stats.append( + { + "bin": bin_num, + "min_rank": min_rank, + "max_rank": max_rank, + "n_targets": n_targets_in_bin, + "n_responsive": n_responsive_in_bin, + "response_rate": response_rate, + "enrichment_vs_overall": ( + response_rate / self.overall_response_rate + if self.overall_response_rate > 0 + else np.nan + ), + } + ) + + return pd.DataFrame(bin_stats) + + def get_bin_summary(self) -> pd.DataFrame: + """Get comprehensive bin-level summary statistics.""" + return self.calculate_bin_stats() + + def calculate_enrichment(self, reference_rate: float | None = None) -> pd.DataFrame: + """ + Calculate enrichment scores for each bin. + + :param reference_rate: Reference response rate for enrichment calculation. If + None, uses overall response rate. + :return: DataFrame with enrichment calculations + + """ + if reference_rate is None: + reference_rate = self.overall_response_rate + + if reference_rate <= 0: + raise ValueError( + "Reference rate must be greater than 0 for enrichment calculation" + ) + + bin_stats = self.calculate_bin_stats() + bin_stats["enrichment_vs_reference"] = ( + bin_stats["response_rate"] / reference_rate + ) + bin_stats["reference_rate"] = reference_rate + + return bin_stats + + def get_rank_response_curve(self, window_size: int | None = None) -> pd.DataFrame: + """ + Get data for plotting rank vs response rate curve with sliding window. + + :param window_size: Size of sliding window for smoothing. If None, uses + bin_size. + :return: DataFrame with rank positions and smoothed response rates + + """ + if window_size is None: + window_size = self.bin_size + + bins_data = self.create_bins() + curve_data = [] + + for i in range(len(bins_data)): + # Define window around current position + start_idx = max(0, i - window_size // 2) + end_idx = min(len(bins_data), i + window_size // 2 + 1) + + window_data = bins_data.iloc[start_idx:end_idx] + window_response_rate = window_data[self.responsive_col].mean() + + curve_data.append( + { + "rank": bins_data.iloc[i]["rank"], + "target": bins_data.iloc[i][self.target_col], + "responsive": bins_data.iloc[i][self.responsive_col], + "smoothed_response_rate": window_response_rate, + "window_size": len(window_data), + } + ) + + return pd.DataFrame(curve_data) + + def get_summary_stats(self) -> dict[str, Any]: + """Get overall summary statistics.""" + return { + "n_targets": self.n_targets, + "n_responsive": self.total_responsive, + "overall_response_rate": self.overall_response_rate, + "n_bins": self.n_bins, + "bin_size": self.bin_size, + "targets_per_bin_avg": self.n_targets / self.n_bins, + } + + def to_dataframe(self) -> pd.DataFrame: + """Export full results as a comprehensive DataFrame.""" + bins_data = self.create_bins() + bin_stats = self.calculate_bin_stats() + + # Merge bin statistics back to individual target data + result = bins_data.merge(bin_stats, on="bin", suffixes=("", "_bin")) + + return result + + def __repr__(self) -> str: + """String representation of the analyzer.""" + return ( + f"RankResponseAnalyzer(" + f"targets={self.n_targets}, " + f"responsive={self.total_responsive}, " + f"rate={self.overall_response_rate:.1%}, " + f"bins={self.n_bins})" + ) diff --git a/tfbpapi/constants.py b/tfbpapi/constants.py index 749678f..62a4227 100644 --- a/tfbpapi/constants.py +++ b/tfbpapi/constants.py @@ -9,3 +9,47 @@ def get_hf_token() -> str | None: """Get HuggingFace token from environment variable.""" return os.getenv("HF_TOKEN") + + +SQL_FILTER_KEYWORDS = sql_keywords = { + "AND", + "OR", + "NOT", + "IN", + "IS", + "NULL", + "TRUE", + "FALSE", + "LIKE", + "BETWEEN", + "EXISTS", + "ALL", + "ANY", + "SOME", + "CASE", + "WHEN", + "THEN", + "ELSE", + "END", + "CAST", + "AS", + "SELECT", + "FROM", + "WHERE", + "GROUP", + "ORDER", + "BY", + "HAVING", + "LIMIT", + "OFFSET", + "DISTINCT", + "COUNT", + "SUM", + "AVG", + "MIN", + "MAX", + "UPPER", + "LOWER", + "SUBSTR", + "LENGTH", +} diff --git a/tfbpapi/datainfo/datacard.py b/tfbpapi/datainfo/datacard.py index c58cd2d..5bcbd42 100644 --- a/tfbpapi/datainfo/datacard.py +++ b/tfbpapi/datainfo/datacard.py @@ -5,11 +5,7 @@ from pydantic import ValidationError -from ..errors import ( - DataCardError, - DataCardValidationError, - HfDataFetchError, -) +from ..errors import DataCardError, DataCardValidationError, HfDataFetchError from .fetchers import HfDataCardFetcher, HfRepoStructureFetcher, HfSizeInfoFetcher from .models import ( DatasetCard, @@ -224,8 +220,19 @@ def _extract_partition_values( self.logger.warning(f"Failed to extract partition values for {field_name}") return set() - def get_metadata_relationships(self) -> list[MetadataRelationship]: - """Get relationships between data configs and their metadata.""" + def get_metadata_relationships( + self, refresh_cache: bool = False + ) -> list[MetadataRelationship]: + """ + Get relationships between data configs and their metadata. + + :param refresh_cache: If True, force refresh dataset card from remote + + """ + # Clear cached dataset card if refresh requested + if refresh_cache: + self._dataset_card = None + relationships = [] data_configs = self.dataset_card.get_data_configs() metadata_configs = self.dataset_card.get_metadata_configs() @@ -244,9 +251,8 @@ def get_metadata_relationships(self) -> list[MetadataRelationship]: relationship_type="explicit", ) ) - continue - # Check for embedded metadata + # Check for embedded metadata (always runs regardless of explicit relationships) if data_config.metadata_fields: relationships.append( MetadataRelationship( diff --git a/tfbpapi/datainfo/models.py b/tfbpapi/datainfo/models.py index 980c9fa..b152248 100644 --- a/tfbpapi/datainfo/models.py +++ b/tfbpapi/datainfo/models.py @@ -30,6 +30,10 @@ class FeatureInfo(BaseModel): description="Data type (string, int64, float64, etc.) or categorical class labels", ) description: str = Field(..., description="Detailed description of the field") + role: str | None = Field( + default=None, + description="Semantic role of the feature (e.g., 'target_identifier', 'regulator_identifier', 'quantitative_measure')", + ) @field_validator("dtype", mode="before") @classmethod diff --git a/tfbpapi/errors.py b/tfbpapi/errors.py index 6952b79..4fc751e 100644 --- a/tfbpapi/errors.py +++ b/tfbpapi/errors.py @@ -214,3 +214,38 @@ def __init__( super().__init__(message, details) self.config_name = config_name self.field_name = field_name + + +class InvalidFilterFieldError(DatasetError): + """Raised when filter fields don't exist in metadata columns.""" + + def __init__( + self, + config_name: str, + invalid_fields: list[str], + available_fields: list[str] | None = None, + ): + invalid_str = ", ".join(f"'{field}'" for field in invalid_fields) + available_str = ( + f"Available fields: {sorted(available_fields)}" + if available_fields + else "No fields available" + ) + message = ( + f"Invalid filter field(s) {invalid_str} for config '{config_name}'. " + f"{available_str}" + ) + + super().__init__( + message, + details={ + "config_name": config_name, + "invalid_fields": invalid_fields, + "available_fields": ( + sorted(available_fields) if available_fields else [] + ), + }, + ) + self.config_name = config_name + self.invalid_fields = invalid_fields + self.available_fields = sorted(available_fields) if available_fields else [] diff --git a/tfbpapi/tests/datainfo/test_datacard.py b/tfbpapi/tests/datainfo/test_datacard.py index 59345b9..ce22578 100644 --- a/tfbpapi/tests/datainfo/test_datacard.py +++ b/tfbpapi/tests/datainfo/test_datacard.py @@ -7,11 +7,7 @@ from tfbpapi.datainfo import DataCard from tfbpapi.datainfo.models import DatasetType -from tfbpapi.errors import ( - DataCardError, - DataCardValidationError, - HfDataFetchError, -) +from tfbpapi.errors import DataCardError, DataCardValidationError, HfDataFetchError class TestDataCard: diff --git a/tfbpapi/tests/test_HfQueryAPI.py b/tfbpapi/tests/test_HfQueryAPI.py new file mode 100644 index 0000000..02ad783 --- /dev/null +++ b/tfbpapi/tests/test_HfQueryAPI.py @@ -0,0 +1,859 @@ +"""Comprehensive tests for HfQueryAPI class.""" + +import logging +from unittest.mock import MagicMock, Mock, patch + +import duckdb +import pandas as pd +import pytest + +from tfbpapi.datainfo.models import MetadataRelationship +from tfbpapi.errors import InvalidFilterFieldError +from tfbpapi.HfQueryAPI import HfQueryAPI + + +class TestHfQueryAPIInit: + """Test HfQueryAPI initialization.""" + + def test_init_basic(self): + """Test basic initialization.""" + conn = duckdb.connect(":memory:") + repo_id = "test/repo" + + with patch("tfbpapi.HfQueryAPI.HfCacheManager.__init__", return_value=None): + api = HfQueryAPI(repo_id, duckdb_conn=conn) + # Manually set properties that would be set by parent + api.repo_id = repo_id + api.duckdb_conn = conn + api._duckdb_conn = conn + api.logger = logging.getLogger("HfQueryAPI") + + assert api.repo_id == repo_id + assert api.duckdb_conn == conn + assert api._duckdb_conn == conn + + def test_init_with_all_params(self): + """Test initialization with all parameters.""" + import tempfile + + conn = duckdb.connect(":memory:") + repo_id = "test/repo" + token = "test_token" + + with tempfile.TemporaryDirectory() as cache_dir: + with patch("tfbpapi.HfQueryAPI.HfCacheManager.__init__", return_value=None): + api = HfQueryAPI( + repo_id=repo_id, + repo_type="model", + token=token, + cache_dir=cache_dir, + duckdb_conn=conn, + ) + # Manually set properties that would be set by parent + api.repo_id = repo_id + api.duckdb_conn = conn + api._duckdb_conn = conn + + assert api.repo_id == repo_id + assert api.duckdb_conn == conn + assert api._duckdb_conn == conn + assert api.repo_type == "model" + + +class TestHfQueryAPIHelpers: + """Test HfQueryAPI helper methods.""" + + @pytest.fixture + def mock_api(self): + """Create a mock HfQueryAPI instance.""" + conn = duckdb.connect(":memory:") + with patch("tfbpapi.HfQueryAPI.HfCacheManager.__init__", return_value=None): + api = HfQueryAPI("test/repo", duckdb_conn=conn) + api.duckdb_conn = conn + api._duckdb_conn = conn + api.logger = logging.getLogger("test") + return api + + def test_get_explicit_metadata(self, mock_api): + """Test _get_explicit_metadata helper.""" + # Mock config and return value + mock_config = Mock() + mock_config.config_name = "test_config" + + # Create mock DataFrame + expected_df = pd.DataFrame({"field1": [1, 2], "field2": ["a", "b"]}) + + # Replace the duckdb_conn with a mock + mock_conn = Mock() + mock_result = Mock() + mock_result.fetchdf.return_value = expected_df + mock_conn.execute.return_value = mock_result + mock_api.duckdb_conn = mock_conn + + result = mock_api._get_explicit_metadata(mock_config, "test_table") + + # Verify SQL was executed correctly + mock_conn.execute.assert_called_once_with("SELECT * FROM test_table") + pd.testing.assert_frame_equal(result, expected_df) + + def test_get_embedded_metadata(self, mock_api): + """Test _get_embedded_metadata helper.""" + # Mock config with metadata fields + mock_config = Mock() + mock_config.config_name = "test_config" + mock_config.metadata_fields = ["time", "mechanism"] + + expected_df = pd.DataFrame( + {"time": [15, 30], "mechanism": ["ZEV", "ZREV"], "count": [100, 50]} + ) + + # Replace the duckdb_conn with a mock + mock_conn = Mock() + mock_result = Mock() + mock_result.fetchdf.return_value = expected_df + mock_conn.execute.return_value = mock_result + mock_api.duckdb_conn = mock_conn + + result = mock_api._get_embedded_metadata(mock_config, "test_table") + + # Verify correct SQL was generated + expected_sql = """ + SELECT DISTINCT time, mechanism, COUNT(*) as count + FROM test_table + WHERE time IS NOT NULL AND mechanism IS NOT NULL + GROUP BY time, mechanism + ORDER BY count DESC + """ + mock_conn.execute.assert_called_once() + actual_sql = mock_conn.execute.call_args[0][0] + # Normalize whitespace for comparison + assert " ".join(actual_sql.split()) == " ".join(expected_sql.split()) + pd.testing.assert_frame_equal(result, expected_df) + + def test_get_embedded_metadata_no_fields(self, mock_api): + """Test _get_embedded_metadata with no metadata fields.""" + mock_config = Mock() + mock_config.config_name = "test_config" + mock_config.metadata_fields = None + + with pytest.raises(ValueError, match="has no metadata fields"): + mock_api._get_embedded_metadata(mock_config, "test_table") + + def test_extract_fields_from_sql_simple(self, mock_api): + """Test _extract_fields_from_sql with simple SQL.""" + sql = "time = 15 AND mechanism = 'ZEV'" + fields = mock_api._extract_fields_from_sql(sql) + + assert "time" in fields + assert "mechanism" in fields + # Note: The current regex may pick up quoted strings as identifiers + # This is a limitation we accept for simplicity + assert "15" not in fields # Should not include numeric literals + + def test_extract_fields_from_sql_complex(self, mock_api): + """Test _extract_fields_from_sql with complex SQL.""" + sql = "field1 IN (1, 2, 3) AND field2 IS NOT NULL AND field3 LIKE '%test%'" + fields = mock_api._extract_fields_from_sql(sql) + + assert "field1" in fields + assert "field2" in fields + assert "field3" in fields + assert "NULL" not in fields # SQL keyword should be excluded + assert "LIKE" not in fields # SQL keyword should be excluded + + def test_extract_fields_from_sql_quoted(self, mock_api): + """Test _extract_fields_from_sql with quoted identifiers.""" + sql = "\"quoted_field\" = 1 AND 'another_field' > 5" + fields = mock_api._extract_fields_from_sql(sql) + + assert "quoted_field" in fields + assert "another_field" in fields + + def test_validate_metadata_fields_success(self, mock_api): + """Test _validate_metadata_fields with valid fields.""" + # Mock get_metadata to return DataFrame with expected columns + metadata_df = pd.DataFrame( + {"time": [15, 30], "mechanism": ["ZEV", "ZREV"], "restriction": ["P", "A"]} + ) + + with patch.object(mock_api, "get_metadata", return_value=metadata_df): + # Should not raise any exception + mock_api._validate_metadata_fields("test_config", ["time", "mechanism"]) + + def test_validate_metadata_fields_invalid(self, mock_api): + """Test _validate_metadata_fields with invalid fields.""" + metadata_df = pd.DataFrame({"time": [15, 30], "mechanism": ["ZEV", "ZREV"]}) + + with patch.object(mock_api, "get_metadata", return_value=metadata_df): + with pytest.raises(InvalidFilterFieldError) as exc_info: + mock_api._validate_metadata_fields( + "test_config", ["invalid_field", "time"] + ) + + error = exc_info.value + assert "invalid_field" in error.invalid_fields + assert "time" not in error.invalid_fields + assert "time" in error.available_fields + assert error.config_name == "test_config" + + def test_validate_metadata_fields_empty_metadata(self, mock_api): + """Test _validate_metadata_fields with empty metadata.""" + empty_df = pd.DataFrame() + + with patch.object(mock_api, "get_metadata", return_value=empty_df): + with pytest.raises(InvalidFilterFieldError) as exc_info: + mock_api._validate_metadata_fields("test_config", ["any_field"]) + + error = exc_info.value + assert error.invalid_fields == ["any_field"] + assert error.available_fields == [] + + def test_validate_metadata_fields_empty_list(self, mock_api): + """Test _validate_metadata_fields with empty field list.""" + # Should not call get_metadata or raise any exception + with patch.object(mock_api, "get_metadata") as mock_get_metadata: + mock_api._validate_metadata_fields("test_config", []) + mock_get_metadata.assert_not_called() + + +class TestHfQueryAPIMainMethods: + """Test HfQueryAPI main methods.""" + + @pytest.fixture + def mock_api(self): + """Create a mock HfQueryAPI instance.""" + conn = duckdb.connect(":memory:") + with patch("tfbpapi.HfQueryAPI.HfCacheManager.__init__", return_value=None): + api = HfQueryAPI("test/repo", duckdb_conn=conn) + # Set up all necessary attributes + api.repo_id = "test/repo" + api.duckdb_conn = conn + api._duckdb_conn = conn + api.logger = logging.getLogger("test") + api._table_filters = {} + + # Set up the internal dataset card attribute + api._dataset_card = Mock() + return api + + def test_get_metadata_explicit_config(self, mock_api): + """Test get_metadata with explicit metadata config.""" + # Setup mock configurations + explicit_config = Mock() + explicit_config.config_name = "metadata_config" + explicit_config.applies_to = None # This config doesn't apply to others + + # Mock the metadata relationship + relationship = MetadataRelationship( + data_config="some_data", + metadata_config="metadata_config", + relationship_type="explicit", + ) + + # Mock the config loading and table setup + mock_config_result = {"success": True, "table_name": "test_metadata_table"} + + expected_df = pd.DataFrame( + {"sample_id": ["sample1", "sample2"], "condition": ["ctrl", "treatment"]} + ) + + with ( + patch.object( + mock_api, "get_metadata_relationships", return_value=[relationship] + ), + patch.object(mock_api, "get_config", return_value=explicit_config), + patch.object( + mock_api, "_get_metadata_for_config", return_value=mock_config_result + ), + patch.object(mock_api, "_get_explicit_metadata", return_value=expected_df), + ): + + result = mock_api.get_metadata("metadata_config") + + mock_api._get_explicit_metadata.assert_called_once_with( + explicit_config, "test_metadata_table" + ) + pd.testing.assert_frame_equal(result, expected_df) + + def test_get_metadata_embedded_config(self, mock_api): + """Test get_metadata with embedded metadata config.""" + # Setup mock configurations + embedded_config = Mock() + embedded_config.config_name = "data_config" + embedded_config.metadata_fields = ["time", "mechanism"] + + # Mock the metadata relationship + relationship = MetadataRelationship( + data_config="data_config", + metadata_config="data_config_embedded", + relationship_type="embedded", + ) + mock_config_result = {"success": True, "table_name": "test_data_table"} + + expected_df = pd.DataFrame( + {"time": [15, 30], "mechanism": ["ZEV", "ZREV"], "count": [100, 50]} + ) + + with ( + patch.object( + mock_api, "get_metadata_relationships", return_value=[relationship] + ), + patch.object(mock_api, "get_config", return_value=embedded_config), + patch.object( + mock_api, "_get_metadata_for_config", return_value=mock_config_result + ), + patch.object(mock_api, "_get_embedded_metadata", return_value=expected_df), + ): + + result = mock_api.get_metadata("data_config") + + mock_api._get_embedded_metadata.assert_called_once_with( + embedded_config, "test_data_table" + ) + pd.testing.assert_frame_equal(result, expected_df) + + def test_get_metadata_applied_config(self, mock_api): + """Test get_metadata with config that has metadata applied to it.""" + # Setup a metadata config that applies to another config + metadata_config = Mock() + metadata_config.config_name = "experiment_metadata" + metadata_config.applies_to = ["data_config", "other_data_config"] + + # Mock the metadata relationship + relationship = MetadataRelationship( + data_config="data_config", + metadata_config="experiment_metadata", + relationship_type="explicit", + ) + mock_config_result = {"success": True, "table_name": "test_metadata_table"} + + expected_df = pd.DataFrame( + {"experiment_id": ["exp1", "exp2"], "condition": ["ctrl", "treatment"]} + ) + + with ( + patch.object( + mock_api, "get_metadata_relationships", return_value=[relationship] + ), + patch.object(mock_api, "get_config", return_value=metadata_config), + patch.object( + mock_api, "_get_metadata_for_config", return_value=mock_config_result + ), + patch.object(mock_api, "_get_explicit_metadata", return_value=expected_df), + ): + + # Request metadata for a config that appears in applies_to + result = mock_api.get_metadata("data_config") + + # Should return the metadata from the config that applies to it + mock_api._get_explicit_metadata.assert_called_once_with( + metadata_config, "test_metadata_table" + ) + pd.testing.assert_frame_equal(result, expected_df) + + def test_get_metadata_config_not_found(self, mock_api): + """Test get_metadata with non-existent config when other configs exist.""" + # Setup a relationship for a different config + relationship = MetadataRelationship( + data_config="other_data", + metadata_config="other_config", + relationship_type="explicit", + ) + with patch.object( + mock_api, "get_metadata_relationships", return_value=[relationship] + ): + with pytest.raises(ValueError, match="Config 'nonexistent' not found"): + mock_api.get_metadata("nonexistent") + + def test_get_metadata_no_metadata_sources(self, mock_api): + """Test get_metadata when no metadata sources are available.""" + with patch.object(mock_api, "get_metadata_relationships", return_value=[]): + result = mock_api.get_metadata("any_config") + assert result.empty + + def test_get_metadata_load_failure(self, mock_api): + """Test get_metadata when config loading fails.""" + config = Mock() + config.config_name = "test_config" + config.applies_to = None + + # Mock the metadata relationship + relationship = MetadataRelationship( + data_config="some_data", + metadata_config="test_config", + relationship_type="explicit", + ) + mock_config_result = {"success": False} + + with ( + patch.object( + mock_api, "get_metadata_relationships", return_value=[relationship] + ), + patch.object(mock_api, "get_config", return_value=config), + patch.object( + mock_api, "_get_metadata_for_config", return_value=mock_config_result + ), + ): + with pytest.raises(RuntimeError, match="Failed to load data for config"): + mock_api.get_metadata("test_config") + + def test_set_filter_valid_fields(self, mock_api): + """Test set_filter with valid field names.""" + with patch.object(mock_api, "_validate_metadata_fields") as mock_validate: + mock_api.set_filter("test_config", time=15, mechanism="ZEV") + + mock_validate.assert_called_once_with("test_config", ["time", "mechanism"]) + assert ( + mock_api._table_filters["test_config"] + == "time = 15 AND mechanism = 'ZEV'" + ) + + def test_set_filter_clear_on_empty(self, mock_api): + """Test set_filter clears filter when no kwargs provided.""" + # Set an initial filter + mock_api._table_filters["test_config"] = "existing_filter" + + with patch.object(mock_api, "clear_filter") as mock_clear: + mock_api.set_filter("test_config") + mock_clear.assert_called_once_with("test_config") + + def test_set_filter_various_types(self, mock_api): + """Test set_filter with different value types.""" + with patch.object(mock_api, "_validate_metadata_fields"): + mock_api.set_filter( + "test_config", + string_field="text", + numeric_field=42, + null_field=None, + bool_field=True, + ) + + expected = ( + "string_field = 'text' AND numeric_field = 42 AND " + "null_field IS NULL AND bool_field = True" + ) + assert mock_api._table_filters["test_config"] == expected + + def test_set_sql_filter_with_validation(self, mock_api): + """Test set_sql_filter with field validation enabled.""" + sql_where = "time IN (15, 30) AND mechanism = 'ZEV'" + + with ( + patch.object( + mock_api, "_extract_fields_from_sql", return_value=["time", "mechanism"] + ) as mock_extract, + patch.object(mock_api, "_validate_metadata_fields") as mock_validate, + ): + + mock_api.set_sql_filter("test_config", sql_where) + + mock_extract.assert_called_once_with(sql_where) + mock_validate.assert_called_once_with("test_config", ["time", "mechanism"]) + assert mock_api._table_filters["test_config"] == sql_where + + def test_set_sql_filter_without_validation(self, mock_api): + """Test set_sql_filter with field validation disabled.""" + sql_where = "complex_function(field1, field2) > 0" + + with ( + patch.object(mock_api, "_extract_fields_from_sql") as mock_extract, + patch.object(mock_api, "_validate_metadata_fields") as mock_validate, + ): + + mock_api.set_sql_filter("test_config", sql_where, validate_fields=False) + + mock_extract.assert_not_called() + mock_validate.assert_not_called() + assert mock_api._table_filters["test_config"] == sql_where + + def test_set_sql_filter_clear_on_empty(self, mock_api): + """Test set_sql_filter clears filter when empty SQL provided.""" + mock_api._table_filters["test_config"] = "existing_filter" + + with patch.object(mock_api, "clear_filter") as mock_clear: + mock_api.set_sql_filter("test_config", "") + mock_clear.assert_called_once_with("test_config") + + def test_clear_filter(self, mock_api): + """Test clear_filter removes stored filter.""" + mock_api._table_filters["test_config"] = "some_filter" + + mock_api.clear_filter("test_config") + + assert "test_config" not in mock_api._table_filters + + def test_clear_filter_nonexistent(self, mock_api): + """Test clear_filter with non-existent config.""" + # Should not raise an error + mock_api.clear_filter("nonexistent_config") + + def test_get_current_filter_exists(self, mock_api): + """Test get_current_filter returns existing filter.""" + expected_filter = "time = 15" + mock_api._table_filters["test_config"] = expected_filter + + result = mock_api.get_current_filter("test_config") + assert result == expected_filter + + def test_get_current_filter_not_exists(self, mock_api): + """Test get_current_filter returns None for non-existent filter.""" + result = mock_api.get_current_filter("nonexistent_config") + assert result is None + + +class TestHfQueryAPIErrorHandling: + """Test HfQueryAPI error handling and edge cases.""" + + @pytest.fixture + def mock_api(self): + """Create a mock HfQueryAPI instance.""" + conn = duckdb.connect(":memory:") + with patch("tfbpapi.HfQueryAPI.HfCacheManager.__init__", return_value=None): + api = HfQueryAPI("test/repo", duckdb_conn=conn) + # Set up all necessary attributes + api.repo_id = "test/repo" + api.duckdb_conn = conn + api._duckdb_conn = conn + api.logger = logging.getLogger("test") + api._table_filters = {} + + # Set up the internal dataset card attribute + api._dataset_card = Mock() + return api + + def test_set_filter_validation_error_propagates(self, mock_api): + """Test that InvalidFilterFieldError from validation propagates.""" + error = InvalidFilterFieldError( + config_name="test_config", + invalid_fields=["invalid_field"], + available_fields=["valid_field"], + ) + + with patch.object(mock_api, "_validate_metadata_fields", side_effect=error): + with pytest.raises(InvalidFilterFieldError) as exc_info: + mock_api.set_filter("test_config", invalid_field="value") + + assert exc_info.value.config_name == "test_config" + assert "invalid_field" in exc_info.value.invalid_fields + + def test_set_sql_filter_validation_error_propagates(self, mock_api): + """Test that InvalidFilterFieldError from SQL validation propagates.""" + error = InvalidFilterFieldError( + config_name="test_config", + invalid_fields=["nonexistent"], + available_fields=["time", "mechanism"], + ) + + with ( + patch.object( + mock_api, "_extract_fields_from_sql", return_value=["nonexistent"] + ), + patch.object(mock_api, "_validate_metadata_fields", side_effect=error), + ): + + with pytest.raises(InvalidFilterFieldError) as exc_info: + mock_api.set_sql_filter("test_config", "nonexistent = 1") + + assert exc_info.value.config_name == "test_config" + + def test_get_metadata_query_error_propagates(self, mock_api): + """Test that query errors in get_metadata propagate.""" + config = Mock() + config.config_name = "test_config" + config.applies_to = None + + # Mock the metadata relationship + relationship = MetadataRelationship( + data_config="some_data", + metadata_config="test_config", + relationship_type="explicit", + ) + + mock_config_result = {"success": True, "table_name": "test_table"} + + with ( + patch.object( + mock_api, "get_metadata_relationships", return_value=[relationship] + ), + patch.object(mock_api, "get_config", return_value=config), + patch.object( + mock_api, "_get_metadata_for_config", return_value=mock_config_result + ), + patch.object( + mock_api, + "_get_explicit_metadata", + side_effect=Exception("Query failed"), + ), + ): + + with pytest.raises(Exception, match="Query failed"): + mock_api.get_metadata("test_config") + + def test_validate_metadata_fields_get_metadata_error_logged(self, mock_api): + """Test that non-InvalidFilterFieldError exceptions in validation are logged.""" + with ( + patch.object( + mock_api, "get_metadata", side_effect=Exception("Network error") + ), + patch.object(mock_api.logger, "warning") as mock_warning, + ): + + # Should not raise, but should log warning + mock_api._validate_metadata_fields("test_config", ["field1"]) + + mock_warning.assert_called_once() + assert "Could not validate filter fields" in mock_warning.call_args[0][0] + + def test_extract_fields_edge_cases(self, mock_api): + """Test _extract_fields_from_sql with various edge cases.""" + # Empty string + assert mock_api._extract_fields_from_sql("") == [] + + # Only SQL keywords + fields = mock_api._extract_fields_from_sql("AND OR NOT NULL") + assert len(fields) == 0 + + # Mixed quotes and operators + fields = mock_api._extract_fields_from_sql( + '"field1" >= "field2" AND field3 <= field4' + ) + assert "field1" in fields + assert "field2" in fields + assert "field3" in fields + assert "field4" in fields + + # Function calls should be excluded + fields = mock_api._extract_fields_from_sql( + "UPPER(field1) = 'VALUE' AND field2 > MAX(field3)" + ) + assert "field1" in fields + assert "field2" in fields + assert "field3" in fields + assert "UPPER" not in fields + assert "MAX" not in fields + + # Numeric literals should be excluded + fields = mock_api._extract_fields_from_sql("field1 = 123.45 AND field2 = -67") + assert "field1" in fields + assert "field2" in fields + assert "123" not in fields + assert "45" not in fields + assert "67" not in fields + + def test_extract_fields_complex_sql(self, mock_api): + """Test _extract_fields_from_sql with complex SQL patterns.""" + complex_sql = """ + (field1 IN (1, 2, 3) OR field2 IS NOT NULL) + AND field3 LIKE '%pattern%' + AND "quoted field" BETWEEN 'start' AND 'end' + AND field4 > COALESCE(field5, 0) + """ + + fields = mock_api._extract_fields_from_sql(complex_sql) + + expected_fields = [ + "field1", + "field2", + "field3", + "quoted field", + "field4", + "field5", + ] + for field in expected_fields: + assert field in fields, f"Field '{field}' should be extracted from SQL" + + # These should not be extracted (SQL keywords and function names) + unwanted = ["COALESCE", "LIKE", "BETWEEN", "NULL"] + for unwanted_item in unwanted: + assert ( + unwanted_item not in fields + ), f"'{unwanted_item}' should not be extracted" + + # String literals should not be extracted + string_literals = ["start", "end", "pattern"] + for literal in string_literals: + assert ( + literal not in fields + ), f"String literal '{literal}' should not be extracted" + + def test_extract_fields_in_clause_with_quoted_values(self, mock_api): + """Test _extract_fields_from_sql with IN clause containing quoted values.""" + # This is the exact pattern from the user's error case + gene_ids = ["YNL199C", "YDL106C", "YLR098C", "YNR009W", "YLR176C"] + regulator_clause = "(" + ", ".join(f"'{gene_id}'" for gene_id in gene_ids) + ")" + + sql = f""" + time = 15 + AND mechanism = 'ZEV' + AND restriction = 'P' + AND regulator_locus_tag IN {regulator_clause} + """ + + fields = mock_api._extract_fields_from_sql(sql) + + # Should extract field names + expected_fields = ["time", "mechanism", "restriction", "regulator_locus_tag"] + for field in expected_fields: + assert field in fields, f"Field '{field}' should be extracted from SQL" + + # Should NOT extract string literals or gene IDs + unwanted_values = ["ZEV", "P"] + gene_ids + for value in unwanted_values: + assert ( + value not in fields + ), f"String literal '{value}' should not be extracted as field" + + # Should NOT extract numeric literals + assert "15" not in fields + + def test_extract_fields_various_comparison_operators(self, mock_api): + """Test _extract_fields_from_sql with various comparison operators and string + values.""" + sql = """ + field1 = 'value1' AND field2 != 'value2' + AND field3 <> 'value3' AND field4 > 'value4' + AND field5 <= 'value5' AND field6 >= 'value6' + AND field7 LIKE 'pattern%' AND field8 NOT LIKE '%other%' + """ + + fields = mock_api._extract_fields_from_sql(sql) + + # Should extract field names + expected_fields = [ + "field1", + "field2", + "field3", + "field4", + "field5", + "field6", + "field7", + "field8", + ] + for field in expected_fields: + assert field in fields, f"Field '{field}' should be extracted from SQL" + + # Should NOT extract string values + unwanted_values = [ + "value1", + "value2", + "value3", + "value4", + "value5", + "value6", + "pattern", + "other", + ] + for value in unwanted_values: + assert ( + value not in fields + ), f"String literal '{value}' should not be extracted as field" + + def test_extract_fields_between_clause(self, mock_api): + """Test _extract_fields_from_sql with BETWEEN clause containing string + values.""" + sql = ( + "field1 BETWEEN 'start_value' AND 'end_value' AND field2 BETWEEN 10 AND 20" + ) + + fields = mock_api._extract_fields_from_sql(sql) + + # Should extract field names + assert "field1" in fields + assert "field2" in fields + + # Should NOT extract BETWEEN values + assert "start_value" not in fields + assert "end_value" not in fields + assert "10" not in fields + assert "20" not in fields + + def test_get_metadata_table_name_missing(self, mock_api): + """Test get_metadata when table_name is missing from config result.""" + config = Mock() + config.config_name = "test_config" + config.applies_to = None + + # Mock the metadata relationship + relationship = MetadataRelationship( + data_config="some_data", + metadata_config="test_config", + relationship_type="explicit", + ) + # Success but no table name + mock_config_result = {"success": True, "table_name": None} + + with ( + patch.object( + mock_api, "get_metadata_relationships", return_value=[relationship] + ), + patch.object(mock_api, "get_config", return_value=config), + patch.object( + mock_api, "_get_metadata_for_config", return_value=mock_config_result + ), + ): + with pytest.raises(RuntimeError, match="No table name for config"): + mock_api.get_metadata("test_config") + + def test_filter_methods_whitespace_handling(self, mock_api): + """Test that filter methods handle whitespace correctly.""" + # set_sql_filter should strip whitespace + with ( + patch.object(mock_api, "_extract_fields_from_sql", return_value=[]), + patch.object(mock_api, "_validate_metadata_fields"), + ): + + mock_api.set_sql_filter("test_config", " field = 1 ") + assert mock_api._table_filters["test_config"] == "field = 1" + + # Empty/whitespace-only SQL should clear filter + with patch.object(mock_api, "clear_filter") as mock_clear: + mock_api.set_sql_filter("test_config", " ") + mock_clear.assert_called_once_with("test_config") + + +class TestInvalidFilterFieldError: + """Test the InvalidFilterFieldError exception.""" + + def test_error_message_formatting(self): + """Test that error message is formatted correctly.""" + error = InvalidFilterFieldError( + config_name="test_config", + invalid_fields=["field1", "field2"], + available_fields=["valid1", "valid2", "valid3"], + ) + + message = str(error) + assert "test_config" in message + assert "'field1'" in message + assert "'field2'" in message + assert "Available fields:" in message + assert "valid1" in message + + def test_error_with_no_available_fields(self): + """Test error message when no fields are available.""" + error = InvalidFilterFieldError( + config_name="empty_config", + invalid_fields=["any_field"], + available_fields=[], + ) + + message = str(error) + assert "No fields available" in message + + def test_error_attributes(self): + """Test that error attributes are set correctly.""" + invalid_fields = ["bad1", "bad2"] + available_fields = ["good1", "good2"] + + error = InvalidFilterFieldError( + config_name="test_config", + invalid_fields=invalid_fields, + available_fields=available_fields, + ) + + assert error.config_name == "test_config" + assert error.invalid_fields == invalid_fields + assert error.available_fields == sorted(available_fields) + assert error.details["config_name"] == "test_config" + assert error.details["invalid_fields"] == invalid_fields + assert error.details["available_fields"] == sorted(available_fields) From e10ec2cda13c9c8a3b657136e2c55a542dd65c41 Mon Sep 17 00:00:00 2001 From: chasem Date: Wed, 15 Oct 2025 13:24:51 -0500 Subject: [PATCH 34/49] kaiwens correlation tutorial --- docs/tutorials/correlation_tutorial.ipynb | 2415 ++++++++++++++++++--- 1 file changed, 2114 insertions(+), 301 deletions(-) diff --git a/docs/tutorials/correlation_tutorial.ipynb b/docs/tutorials/correlation_tutorial.ipynb index 8ca7fe5..5c3d5d4 100644 --- a/docs/tutorials/correlation_tutorial.ipynb +++ b/docs/tutorials/correlation_tutorial.ipynb @@ -29,10 +29,19 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 1, "id": "30abaca0", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chase/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" + ] + } + ], "source": [ "import pandas as pd\n", "import numpy as np\n", @@ -60,7 +69,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 2, "id": "c9b9afdf", "metadata": {}, "outputs": [ @@ -172,7 +181,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 3, "id": "a6b0424f", "metadata": {}, "outputs": [ @@ -205,7 +214,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 4, "id": "baafb468", "metadata": {}, "outputs": [ @@ -249,7 +258,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 5, "id": "c888c477", "metadata": {}, "outputs": [ @@ -280,7 +289,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 6, "id": "050e3f4c", "metadata": {}, "outputs": [ @@ -288,41 +297,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Loading both perturbation datasets...\n" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "e7178fddad6c4f6780c08e8f7f6e8ddc", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "FloatProgress(value=0.0, layout=Layout(width='auto'), style=ProgressStyle(bar_color='black'))" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "2b31f3e685a449828ae056a9796a9244", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "FloatProgress(value=0.0, layout=Layout(width='auto'), style=ProgressStyle(bar_color='black'))" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ + "Loading both perturbation datasets...\n", "Both datasets loaded\n", "hackett: 145 regulators\n", "kemmeren: 1487 regulators\n", @@ -364,7 +339,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 7, "id": "06293141", "metadata": {}, "outputs": [ @@ -374,27 +349,7 @@ "text": [ "Finding common targets...\n", "Found 6063 common targets\n", - "Loading final datasets with complete filtering...\n" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "b7276d52902547829f90a98ba85c6a5c", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "FloatProgress(value=0.0, layout=Layout(width='auto'), style=ProgressStyle(bar_color='black'))" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ + "Loading final datasets with complete filtering...\n", "Final datasets loaded with complete filtering\n", "McIsaac: 891,408 rows, 145 regulators\n", "Kemmeren: 880,730 rows, 145 regulators\n" @@ -447,12 +402,783 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 8, "id": "549e73ae", "metadata": {}, "outputs": [ { "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "regulator_locus_tag", + "rawType": "object", + "type": "string" + }, + { + "name": "regulator_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "reporterId", + "rawType": "object", + "type": "string" + }, + { + "name": "target_locus_tag", + "rawType": "object", + "type": "string" + }, + { + "name": "target_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "M", + "rawType": "float64", + "type": "float" + }, + { + "name": "Madj", + "rawType": "float64", + "type": "float" + }, + { + "name": "A", + "rawType": "float64", + "type": "float" + }, + { + "name": "pval", + "rawType": "float64", + "type": "float" + }, + { + "name": "variable_in_wt", + "rawType": "bool", + "type": "boolean" + }, + { + "name": "multiple_probes", + "rawType": "bool", + "type": "boolean" + } + ], + "ref": "31ae3af5-8283-4e49-9dc6-2dbd59140d0f", + "rows": [ + [ + "0", + "YBL008W", + "HIR1", + "SCAB000002", + "Q0017", + "Q0017", + "-0.039025753", + "-0.0397208098656576", + "5.666642", + "0.591335", + "False", + "False" + ], + [ + "1", + "YBL008W", + "HIR1", + "SCAB000004", + "Q0045", + "COX1", + "-0.10270845", + "-0.104404483712687", + "7.1872175", + "0.278744", + "False", + "False" + ], + [ + "2", + "YBL008W", + "HIR1", + "SCAB000005", + "Q0050", + "AI1", + "-0.09215408", + "-0.101312394230742", + "7.9818127", + "0.789989", + "True", + "False" + ], + [ + "3", + "YBL008W", + "HIR1", + "SCAB000006", + "Q0055", + "AI2", + "-0.11363403", + "-0.123208021645201", + "9.3025734", + "0.695235", + "True", + "False" + ], + [ + "4", + "YBL008W", + "HIR1", + "SCAB000007", + "Q0060", + "AI3", + "-0.0099832871", + "-0.0186756321118991", + "8.3151407", + "0.975177", + "False", + "False" + ], + [ + "5", + "YBL008W", + "HIR1", + "SCAB000008", + "Q0065", + "AI4", + "-0.16157419", + "-0.173001391865694", + "11.104408", + "0.508279", + "True", + "False" + ], + [ + "6", + "YBL008W", + "HIR1", + "SCAB000009", + "Q0070", + "AI5_ALPHA", + "-0.29374796", + "-0.306969229210268", + "10.721268", + "0.127481", + "True", + "False" + ], + [ + "7", + "YBL008W", + "HIR1", + "SCAB000010", + "Q0075", + "AI5_BETA", + "-0.048776282", + "-0.0512431882361536", + "7.3602157", + "0.788852", + "True", + "False" + ], + [ + "8", + "YBL008W", + "HIR1", + "SCAB000011", + "Q0080", + "ATP8", + "-0.18210337", + "-0.187242319447809", + "6.7582501", + "0.227365", + "True", + "False" + ], + [ + "9", + "YBL008W", + "HIR1", + "SCAB000012", + "Q0085", + "ATP6", + "-0.0065067789", + "-0.00701312899803988", + "6.4296849", + "0.9677", + "False", + "False" + ], + [ + "10", + "YBL008W", + "HIR1", + "SCAB000014", + "Q0105", + "COB", + "-0.023712626", + "-0.0241945156649505", + "5.6905146", + "0.819055", + "False", + "False" + ], + [ + "11", + "YBL008W", + "HIR1", + "SCAB000015", + "Q0110", + "BI2", + "-0.10026133", + "-0.102682551857025", + "6.3605368", + "0.538407", + "False", + "False" + ], + [ + "12", + "YBL008W", + "HIR1", + "SCAB000016", + "Q0115", + "BI3", + "-0.053808146", + "-0.0569359410492525", + "6.6718358", + "0.725973", + "False", + "False" + ], + [ + "13", + "YBL008W", + "HIR1", + "SCAB000017", + "Q0120", + "BI4", + "-0.020458971", + "-0.0236853183042711", + "6.9095507", + "0.927896", + "False", + "False" + ], + [ + "14", + "YBL008W", + "HIR1", + "SCAB000018", + "Q0130", + "OLI1", + "-0.043537368", + "-0.0566553964261391", + "12.556363", + "0.890456", + "True", + "False" + ], + [ + "15", + "YBL008W", + "HIR1", + "SCAB000019", + "Q0140", + "VAR1", + "-0.16343272", + "-0.163938440697184", + "6.1672218", + "0.188679", + "True", + "False" + ], + [ + "16", + "YBL008W", + "HIR1", + "SCAB000023", + "Q0160", + "SCEI", + "-0.11115273", + "-0.112545724325415", + "6.40361", + "0.233242", + "False", + "False" + ], + [ + "17", + "YBL008W", + "HIR1", + "SCAB000024", + "Q0182", + "Q0182", + "-0.074097777", + "-0.07502283029592", + "5.8703247", + "0.373117", + "False", + "False" + ], + [ + "18", + "YBL008W", + "HIR1", + "SCAB000025", + "Q0250", + "COX2", + "-0.093127871", + "-0.0967308066694721", + "7.4043715", + "0.275569", + "False", + "False" + ], + [ + "19", + "YBL008W", + "HIR1", + "SCAB000026", + "Q0255", + "Q0255", + "-0.031337077", + "-0.0317708668814822", + "5.6274759", + "0.677195", + "False", + "False" + ], + [ + "20", + "YBL008W", + "HIR1", + "SCAB000027", + "Q0275", + "COX3", + "-0.018126893", + "-0.0174743777233046", + "6.0614997", + "0.861477", + "False", + "False" + ], + [ + "21", + "YBL008W", + "HIR1", + "SCAB000028", + "Q0297", + "Q0297", + "-0.0015863937", + "-0.00165756742970601", + "6.2053847", + "0.99118", + "False", + "False" + ], + [ + "22", + "YBL008W", + "HIR1", + "SCAB000029", + "YAL001C", + "TFC3", + "-0.060650037", + "-0.0567581406799775", + "9.7950907", + "0.415566", + "False", + "False" + ], + [ + "23", + "YBL008W", + "HIR1", + "SCAB000030", + "YAL002W", + "VPS8", + "-0.0074215245", + "-0.00566180001033513", + "8.3969858", + "0.930872", + "False", + "False" + ], + [ + "24", + "YBL008W", + "HIR1", + "SCAB000031", + "YAL003W", + "EFB1", + "-0.029623397", + "-0.03985574724458", + "14.921067", + "0.84173", + "False", + "False" + ], + [ + "25", + "YBL008W", + "HIR1", + "SCAB000032", + "YAL004W", + "YAL004W", + "0.059369123", + "0.0561266464793721", + "6.6928234", + "0.54419", + "False", + "False" + ], + [ + "26", + "YBL008W", + "HIR1", + "SCAB000033", + "YAL005C", + "SSA1", + "0.042630331", + "0.0487909166720867", + "15.675554", + "0.862133", + "False", + "False" + ], + [ + "27", + "YBL008W", + "HIR1", + "SCAB000034", + "YAL007C", + "ERP2", + "-0.060302507", + "-0.0583166518417961", + "11.623729", + "0.346134", + "False", + "False" + ], + [ + "28", + "YBL008W", + "HIR1", + "SCAB000035", + "YAL008W", + "FUN14", + "-0.089632662", + "-0.0758621939217628", + "9.7471534", + "0.251776", + "False", + "False" + ], + [ + "29", + "YBL008W", + "HIR1", + "SCAB000036", + "YAL009W", + "SPO7", + "0.061596497", + "0.059807970651245", + "9.5949268", + "0.337943", + "False", + "False" + ], + [ + "30", + "YBL008W", + "HIR1", + "SCAB000037", + "YAL010C", + "MDM10", + "0.033553389", + "0.0347720388006548", + "7.8775572", + "0.707184", + "False", + "False" + ], + [ + "31", + "YBL008W", + "HIR1", + "SCAB000038", + "YAL011W", + "SWC3", + "-0.053778196", + "-0.0547258310698601", + "6.1043641", + "0.478414", + "False", + "False" + ], + [ + "32", + "YBL008W", + "HIR1", + "SCAB000039", + "YAL012W", + "CYS3", + "-0.067001533", + "-0.0704603092262692", + "12.296266", + "0.589873", + "False", + "False" + ], + [ + "33", + "YBL008W", + "HIR1", + "SCAB000040", + "YAL013W", + "DEP1", + "0.045037505", + "0.0422594728589327", + "8.8906158", + "0.705922", + "False", + "False" + ], + [ + "34", + "YBL008W", + "HIR1", + "SCAB000041", + "YAL014C", + "SYN8", + "-0.0028853882", + "-0.003421632139455", + "10.140393", + "0.984162", + "False", + "False" + ], + [ + "35", + "YBL008W", + "HIR1", + "SCAB000042", + "YAL015C", + "NTG1", + "0.17305066", + "0.178185020644813", + "9.5554939", + "0.010956", + "False", + "False" + ], + [ + "36", + "YBL008W", + "HIR1", + "SCAB000043", + "YAL016W", + "TPD3", + "-0.084858514", + "-0.0803126247904039", + "11.337088", + "0.270546", + "False", + "False" + ], + [ + "37", + "YBL008W", + "HIR1", + "SCAB000044", + "YAL017W", + "PSK1", + "-0.081218414", + "-0.0672602695566325", + "9.9839762", + "0.450345", + "False", + "False" + ], + [ + "38", + "YBL008W", + "HIR1", + "SCAB000045", + "YAL018C", + "LDS1", + "-0.0015791723", + "-0.00059224976381921", + "5.9934205", + "0.990055", + "False", + "False" + ], + [ + "39", + "YBL008W", + "HIR1", + "SCAB000046", + "YAL019W", + "FUN30", + "0.1520406", + "0.148431864200967", + "10.781851", + "0.0493148", + "False", + "False" + ], + [ + "40", + "YBL008W", + "HIR1", + "SCAB000047", + "YAL020C", + "ATS1", + "-0.020495554", + "-0.0238614082255441", + "9.3828017", + "0.836546", + "False", + "False" + ], + [ + "41", + "YBL008W", + "HIR1", + "SCAB000048", + "YAL021C", + "CCR4", + "-0.13337841", + "-0.13693929825126", + "9.4790413", + "0.171857", + "False", + "False" + ], + [ + "42", + "YBL008W", + "HIR1", + "SCAB000049", + "YAL022C", + "FUN26", + "-0.097163876", + "-0.0973296431760643", + "9.893272", + "0.226213", + "False", + "False" + ], + [ + "43", + "YBL008W", + "HIR1", + "SCAB000050", + "YAL023C", + "PMT2", + "-0.027986767", + "-0.0333328913664413", + "9.0967819", + "0.821875", + "False", + "False" + ], + [ + "44", + "YBL008W", + "HIR1", + "SCAB000051", + "YAL024C", + "LTE1", + "-0.13226007", + "-0.135436445273697", + "8.4398933", + "0.045112", + "False", + "False" + ], + [ + "45", + "YBL008W", + "HIR1", + "SCAB000052", + "YAL025C", + "MAK16", + "0.17071987", + "0.159309256880021", + "11.301871", + "0.0211462", + "False", + "False" + ], + [ + "46", + "YBL008W", + "HIR1", + "SCAB000053", + "YAL026C", + "DRS2", + "-0.1904308", + "-0.190251792611949", + "10.91405", + "0.00111783", + "False", + "False" + ], + [ + "47", + "YBL008W", + "HIR1", + "SCAB000054", + "YAL027W", + "SAW1", + "-0.18881836", + "-0.192964020762811", + "8.2938543", + "7.6856e-06", + "False", + "False" + ], + [ + "48", + "YBL008W", + "HIR1", + "SCAB000055", + "YAL028W", + "FRT2", + "-0.053743754", + "-0.0418562759081939", + "7.9800474", + "0.599747", + "False", + "False" + ], + [ + "49", + "YBL008W", + "HIR1", + "SCAB000056", + "YAL029C", + "MYO4", + "-0.025923023", + "-0.0246822755213326", + "10.036795", + "0.842811", + "False", + "False" + ] + ], + "shape": { + "columns": 11, + "rows": 880730 + } + }, "text/html": [ "
\n", "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
datasetconfig_nameview_name
0BrentLab/harbison_2004harbison_2004BrentLab_harbison_2004_harbison_2004_metadata
1BrentLab/hackett_2020hackett_2020BrentLab_hackett_2020_hackett_2020_metadata
\n", + "
" + ], + "text/plain": [ + " dataset config_name \\\n", + "0 BrentLab/harbison_2004 harbison_2004 \n", + "1 BrentLab/hackett_2020 hackett_2020 \n", + "\n", + " view_name \n", + "0 BrentLab_harbison_2004_harbison_2004_metadata \n", + "1 BrentLab_hackett_2020_hackett_2020_metadata " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Get summary of registered datasets\n", + "summary = mgr.get_summary()\n", + "\n", + "print(\"Registered Datasets Summary\")\n", + "print(\"=\" * 60)\n", + "display(summary)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "print(\"Three-Level Experimental Condition Hierarchy\")\nprint(\"=\" * 80)\n\n# Show hackett_2020 as example\nprint(\"\\nExample: hackett_2020 dataset\")\nprint(\"-\" * 80)\n\n# 1. Repo-level (applies to ALL samples)\nprint(\"\\n[1] REPO-LEVEL (DatasetCard.experimental_conditions)\")\nprint(\" Applies to: ALL configs and ALL samples in the repository\")\nif hackett_schema.get('top_level_conditions'):\n top_cond = hackett_schema['top_level_conditions']\n \n # Check structured environmental_conditions\n if top_cond.environmental_conditions:\n env = top_cond.environmental_conditions\n print(f\" - temperature_celsius: {env.temperature_celsius}\")\n print(f\" - cultivation_method: {env.cultivation_method}\")\n print(f\" - media.name: {env.media.name}\")\n print(f\" - media.carbon_source: {env.media.carbon_source[0].compound} @ {env.media.carbon_source[0].concentration_percent}%\")\n \n # Check extra fields (alternate structure used by some datacards)\n if hasattr(top_cond, 'model_extra') and top_cond.model_extra:\n for key, value in top_cond.model_extra.items():\n print(f\" - {key}: {value}\")\n\n# 2. Config-level (applies to samples in this config)\nprint(\"\\n[2] CONFIG-LEVEL (DatasetConfig.experimental_conditions)\")\nprint(\" Applies to: All samples in the 'hackett_2020' config\")\nif hackett_schema.get('config_level_conditions'):\n print(\" - (Specific conditions defined)\")\nelse:\n print(\" - (None - all repo-level conditions inherited)\")\n\n# 3. Field-level (varies by sample)\nprint(\"\\n[3] FIELD-LEVEL (FeatureInfo.definitions)\")\nprint(\" Applies to: Individual samples based on field value\")\nprint(\" - restriction field: 3 levels (P, N, M)\")\nprint(\" - P: Phosphate limitation\")\nprint(\" - N: Nitrogen limitation\")\nprint(\" - M: Magnesium limitation\")\nprint(\" - time field: Various time points (30.0, 60.0, etc.)\")\n\nprint(\"\\n\" + \"=\" * 80)\nprint(\"KEY CONCEPT: Hierarchy Merging\")\nprint(\"-\" * 80)\nprint(\"When MetadataManager creates metadata tables, it merges all three levels:\")\nprint(\" - Repo-level conditions -> columns for ALL samples (temperature: 30C, etc.)\")\nprint(\" - Config-level conditions -> columns for config samples (override repo-level if present)\")\nprint(\" - Field-level conditions -> vary by sample (restriction: P/N/M)\")\nprint(\"\\nPrecedence: Field-level > Config-level > Repo-level\")\nprint(\"\\nResult: Each sample gets columns from all applicable levels!\")" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part 2.5: Understanding the Three-Level Condition Hierarchy\n", + "\n", + "Experimental conditions can be specified at three different levels in the hierarchy. Let's examine how this works with a concrete example from hackett_2020." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part 3: Querying Across Datasets (Placeholder)\n", + "\n", + "**Note**: The current implementation has a placeholder for actual parquet file loading from HuggingFace. The examples below show the intended usage once parquet integration is complete.\n", + "\n", + "### Example Query 1: Find all regulators across datasets" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "# Example query (will work once parquet loading is implemented)\n# \n# regulators_query = \"\"\"\n# SELECT DISTINCT \n# dataset,\n# config_name,\n# regulator_symbol,\n# regulator_locus_tag\n# FROM unified_metadata\n# WHERE regulator_symbol != 'unspecified'\n# ORDER BY dataset, regulator_symbol\n# \"\"\"\n# \n# regulators_df = mgr.query(regulators_query)\n# print(f\"\\nFound {len(regulators_df)} unique regulators across datasets\")\n# display(regulators_df.head(10))\n\nprint(\"WARNING: Parquet file loading not yet implemented\")\nprint(\" This query will work once HuggingFace parquet integration is added\")" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example Query 2: Filter by specific regulator" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "# Example: Find GLN3 samples across both datasets\n#\n# gln3_query = \"\"\"\n# SELECT \n# dataset,\n# sample_id,\n# regulator_symbol,\n# condition,\n# time,\n# restriction,\n# growth_media,\n# components\n# FROM unified_metadata\n# WHERE regulator_symbol = 'GLN3'\n# \"\"\"\n# \n# gln3_samples = mgr.query(gln3_query)\n# print(f\"\\nGLN3 samples: {len(gln3_samples)}\")\n# display(gln3_samples)\n\nprint(\"WARNING: Parquet file loading not yet implemented\")" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example Query 3: Search by media components" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "# Example: Find samples with D-glucose at 2%\n#\n# glucose_query = \"\"\"\n# SELECT \n# dataset,\n# COUNT(*) as sample_count,\n# growth_media\n# FROM unified_metadata\n# WHERE components LIKE '%carbon_source:D-glucose@2%'\n# GROUP BY dataset, growth_media\n# \"\"\"\n# \n# glucose_samples = mgr.query(glucose_query)\n# print(\"\\nSamples with D-glucose at 2%:\")\n# display(glucose_samples)\n\nprint(\"WARNING: Parquet file loading not yet implemented\")\nprint(\" This demonstrates searching by specific media components with concentrations\")" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part 4: Understanding Metadata Flattening\n", + "\n", + "The MetadataManager flattens condition definitions into searchable fields using known separators." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Separator Conventions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "from tfbpapi.datainfo.metadata_manager import COMPONENT_SEPARATORS\n\nprint(\"\\nComponent Separator Conventions\")\nprint(\"=\" * 60)\nfor key, sep in COMPONENT_SEPARATORS.items():\n print(f\" {key:20s} '{sep}'\")\n \nprint(\"\\nUsage Examples:\")\nprint(\" growth_media: 'YPD' (simple name)\")\nprint(\" components: 'carbon_source:D-glucose@2%|nitrogen_source:yeast_extract@1%;peptone@2%'\")\nprint(\"\\n Breakdown:\")\nprint(\" - ':' separates type from value (carbon_source:D-glucose)\")\nprint(\" - '@' separates value from concentration (D-glucose@2%)\")\nprint(\" - ';' separates multiple compounds of same type (yeast_extract@1%;peptone@2%)\")\nprint(\" - '|' separates different component types (carbon | nitrogen)\")" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example: Flattening a Condition Definition\n", + "\n", + "Let's manually flatten a condition definition to see how it works." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original YPD Condition Definition\n", + "============================================================\n", + "Description: Rich media baseline condition\n", + "\n", + "Flattened Representation\n", + "============================================================\n", + "growth_media: 'unspecified'\n", + "components: ''\n", + "\n", + "This format enables SQL searches like:\n", + " WHERE growth_media = 'YPD'\n", + " WHERE components LIKE '%carbon_source:D-glucose@2%'\n", + " WHERE components LIKE '%nitrogen_source:yeast_extract%'\n" + ] + } + ], + "source": [ + "# Get YPD condition definition from harbison_2004\n", + "ypd_definition = harbison_schema['condition_definitions']['condition']['YPD']\n", + "\n", + "print(\"Original YPD Condition Definition\")\n", + "print(\"=\" * 60)\n", + "print(f\"Description: {ypd_definition.get('description', 'N/A')}\")\n", + "\n", + "if 'environmental_conditions' in ypd_definition:\n", + " env = ypd_definition['environmental_conditions']\n", + " print(f\"\\nTemperature: {env.get('temperature_celsius', 'N/A')}°C\")\n", + " \n", + " if 'media' in env:\n", + " media = env['media']\n", + " print(f\"Media Name: {media.get('name', 'N/A')}\")\n", + " print(f\"\\nCarbon Source:\")\n", + " for cs in media.get('carbon_source', []):\n", + " print(f\" - {cs.get('compound', cs)}: {cs.get('concentration_percent', 'N/A')}%\")\n", + " print(f\"\\nNitrogen Source:\")\n", + " for ns in media.get('nitrogen_source', []):\n", + " print(f\" - {ns.get('compound', ns)}: {ns.get('concentration_percent', 'N/A')}%\")\n", + "\n", + "# Flatten using MetadataManager method\n", + "flattened = mgr._flatten_condition_definition(ypd_definition)\n", + "\n", + "print(\"\\nFlattened Representation\")\n", + "print(\"=\" * 60)\n", + "print(f\"growth_media: '{flattened['growth_media']}'\")\n", + "print(f\"components: '{flattened['components']}'\")\n", + "\n", + "print(\"\\nThis format enables SQL searches like:\")\n", + "print(\" WHERE growth_media = 'YPD'\")\n", + "print(\" WHERE components LIKE '%carbon_source:D-glucose@2%'\")\n", + "print(\" WHERE components LIKE '%nitrogen_source:yeast_extract%'\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part 5: Schema Heterogeneity Handling\n", + "\n", + "Different datasets have different metadata fields. MetadataManager handles this by:\n", + "1. **Role-based alignment**: Fields grouped by semantic role (regulator_identifier, experimental_condition)\n", + "2. **Column defaulting**: Missing columns default to \"unspecified\" in unified view\n", + "3. **UNION ALL**: All views combined with column alignment" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Schema Heterogeneity Example\n", + "============================================================\n", + "\n", + "Harbison 2004 has:\n", + " - condition field (14 levels: YPD, RAPA, HEAT, etc.)\n", + " - NO time, mechanism, restriction fields\n", + "\n", + "Hackett 2020 has:\n", + " - time field (time points in minutes)\n", + " - mechanism field (GEV, ZEV induction systems)\n", + " - restriction field (P, N, M nutrient limitations)\n", + " - NO condition field\n", + "\n", + "In unified view:\n", + " - Harbison rows: condition='YPD', time='unspecified', restriction='unspecified'\n", + " - Hackett rows: condition='unspecified', time=30.0, restriction='P'\n", + "\n", + " Both can be queried together:\n", + " SELECT * FROM unified_metadata WHERE regulator_symbol = 'GLN3'\n", + " Returns samples from BOTH datasets!\n" + ] + } + ], + "source": [ + "print(\"Schema Heterogeneity Example\")\n", + "print(\"=\" * 60)\n", + "\n", + "print(\"\\nHarbison 2004 has:\")\n", + "print(f\" - condition field (14 levels: YPD, RAPA, HEAT, etc.)\")\n", + "print(f\" - NO time, mechanism, restriction fields\")\n", + "\n", + "print(\"\\nHackett 2020 has:\")\n", + "print(f\" - time field (time points in minutes)\")\n", + "print(f\" - mechanism field (GEV, ZEV induction systems)\")\n", + "print(f\" - restriction field (P, N, M nutrient limitations)\")\n", + "print(f\" - NO condition field\")\n", + "\n", + "print(\"\\nIn unified view:\")\n", + "print(\" - Harbison rows: condition='YPD', time='unspecified', restriction='unspecified'\")\n", + "print(\" - Hackett rows: condition='unspecified', time=30.0, restriction='P'\")\n", + "print(\"\\n Both can be queried together:\")\n", + "print(\" SELECT * FROM unified_metadata WHERE regulator_symbol = 'GLN3'\")\n", + "print(\" Returns samples from BOTH datasets!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part 6: Cleanup and Next Steps" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Unregister Datasets\n", + "\n", + "You can remove datasets from the manager when done." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "## Summary\n\nThis tutorial demonstrated:\n\n- **Metadata schema extraction** - Using `DataCard.extract_metadata_schema()` to understand field roles\n\n- **Three-level condition hierarchy** - Repo-level, config-level, and field-level experimental conditions\n\n- **Cross-dataset registration** - Registering multiple datasets in `MetadataManager`\n\n- **Condition flattening** - How definitions are flattened into searchable fields with known separators\n\n- **Schema heterogeneity** - How different metadata schemas are aligned by role with \"unspecified\" defaults\n\n- **Future: SQL queries** - Once parquet loading is implemented, full cross-dataset SQL queries will work\n\n### Key Takeaways\n\n1. **Role-based alignment**: Fields are grouped by semantic role (regulator_identifier, experimental_condition), not by name\n2. **Three-level hierarchy**: Conditions can be specified at repo-level (all samples), config-level (config samples), or field-level (individual samples)\n3. **Hierarchy merging**: MetadataManager merges all three levels with precedence: field > config > repo\n4. **Structured separators**: Media components use `:`, `@`, `;`, `|` for predictable parsing\n5. **Low memory**: DuckDB temp views mean no data loading until you query\n6. **Session-only default**: No caching by default (set `cache=True` if needed)\n7. **Dataset + config uniqueness**: Samples identified by (dataset, config_name, sample_id) tuple\n\n### Understanding the Condition Hierarchy\n\nThe three-level hierarchy allows flexible experimental condition specification:\n\n- **Repo-level**: Common conditions shared by all experiments (e.g., standard temperature, base strain)\n- **Config-level**: Conditions specific to a dataset configuration (e.g., growth phase for a time course)\n- **Field-level**: Sample-specific conditions that vary within an experiment (e.g., different media, treatments)\n\nThis design enables:\n- Efficient specification without repetition\n- Clear inheritance and overriding semantics\n- Consistent querying across heterogeneous datasets" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Next Steps\n", + "\n", + "Once parquet loading is implemented, you'll be able to:\n", + "\n", + "1. **Filter by regulators**: Find all samples for specific TFs across datasets\n", + "2. **Search by conditions**: Query by media, temperature, treatments, etc.\n", + "3. **Cross-dataset analysis**: Compare experimental designs across studies\n", + "4. **Metadata-driven data loading**: Use `get_active_configs()` to load actual expression/binding data\n", + "\n", + "### Integration with HfQueryAPI (Future)\n", + "\n", + "The intended workflow:\n", + "```python\n", + "# 1. Filter metadata across datasets\n", + "mgr = MetadataManager()\n", + "mgr.register(\"BrentLab/harbison_2004\")\n", + "mgr.register(\"BrentLab/hackett_2020\")\n", + "mgr.filter_by_regulator([\"GLN3\", \"GCN4\"])\n", + "mgr.filter_by_conditions(growth_media=\"YPD\")\n", + "\n", + "# 2. Get active configs\n", + "active_configs = mgr.get_active_configs()\n", + "# Returns: [('BrentLab/harbison_2004', 'harbison_2004'), ...]\n", + "\n", + "# 3. Load actual data (requires HfQueryAPI refactor)\n", + "# TBD - design after MetadataManager is complete\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "## Summary\n\nThis tutorial demonstrated:\n\n- **Metadata schema extraction** - Using `DataCard.extract_metadata_schema()` to understand field roles\n\n- **Cross-dataset registration** - Registering multiple datasets in `MetadataManager`\n\n- **Condition flattening** - How definitions are flattened into searchable fields with known separators\n\n- **Schema heterogeneity** - How different metadata schemas are aligned by role with \"unspecified\" defaults\n\n- **Future: SQL queries** - Once parquet loading is implemented, full cross-dataset SQL queries will work\n\n### Key Takeaways\n\n1. **Role-based alignment**: Fields are grouped by semantic role (regulator_identifier, experimental_condition), not by name\n2. **Structured separators**: Media components use `:`, `@`, `;`, `|` for predictable parsing\n3. **Low memory**: DuckDB temp views mean no data loading until you query\n4. **Session-only default**: No caching by default (set `cache=True` if needed)\n5. **Dataset + config uniqueness**: Samples identified by (dataset, config_name, sample_id) tuple" + } + ], + "metadata": { + "kernelspec": { + "display_name": "tfbpapi-py3.11", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} \ No newline at end of file diff --git a/tfbpapi/datainfo/__init__.py b/tfbpapi/datainfo/__init__.py index 88452eb..11f3387 100644 --- a/tfbpapi/datainfo/__init__.py +++ b/tfbpapi/datainfo/__init__.py @@ -1,5 +1,6 @@ from .datacard import DataCard from .fetchers import HfDataCardFetcher, HfRepoStructureFetcher, HfSizeInfoFetcher +from .metadata_manager import MetadataManager from .models import ( DatasetCard, DatasetConfig, @@ -14,6 +15,7 @@ "HfDataCardFetcher", "HfRepoStructureFetcher", "HfSizeInfoFetcher", + "MetadataManager", "DatasetCard", "DatasetConfig", "DatasetType", diff --git a/tfbpapi/datainfo/datacard.py b/tfbpapi/datainfo/datacard.py index 5bcbd42..279b548 100644 --- a/tfbpapi/datainfo/datacard.py +++ b/tfbpapi/datainfo/datacard.py @@ -12,6 +12,7 @@ DatasetConfig, DatasetType, ExtractedMetadata, + FieldRole, MetadataRelationship, ) @@ -328,6 +329,55 @@ def explore_config(self, config_name: str) -> dict[str, Any]: return info + def extract_metadata_schema(self, config_name: str) -> dict[str, Any]: + """ + Extract metadata schema for a configuration. + + Returns structured schema identifying metadata fields by their role, + suitable for metadata extraction by MetadataManager. Includes + experimental conditions at all three hierarchy levels: + - Top-level (repo-wide, applies to all configs/samples) + - Config-level (applies to this config's samples) + - Field-level (varies per sample, from field definitions) + + :param config_name: Configuration name to extract schema for + :return: Dict with field lists grouped by role and condition definitions + :raises DataCardError: If configuration not found + + """ + config = self.get_config(config_name) + if not config: + raise DataCardError(f"Configuration '{config_name}' not found") + + schema: dict[str, Any] = { + "regulator_fields": [], # Fields with role=regulator_identifier + "target_fields": [], # Fields with role=target_identifier + "condition_fields": [], # Fields with role=experimental_condition + "condition_definitions": {}, # Field-level condition details + "top_level_conditions": None, # Repo-level conditions + "config_level_conditions": None, # Config-level conditions + } + + for feature in config.dataset_info.features: + if feature.role == FieldRole.REGULATOR_IDENTIFIER: + schema["regulator_fields"].append(feature.name) + elif feature.role == FieldRole.TARGET_IDENTIFIER: + schema["target_fields"].append(feature.name) + elif feature.role == FieldRole.EXPERIMENTAL_CONDITION: + schema["condition_fields"].append(feature.name) + if feature.definitions: + schema["condition_definitions"][feature.name] = feature.definitions + + # Add top-level conditions (applies to all configs/samples) + if self.dataset_card.experimental_conditions: + schema["top_level_conditions"] = self.dataset_card.experimental_conditions + + # Add config-level conditions (applies to this config's samples) + if config.experimental_conditions: + schema["config_level_conditions"] = config.experimental_conditions + + return schema + def summary(self) -> str: """Get a human-readable summary of the dataset.""" card = self.dataset_card diff --git a/tfbpapi/datainfo/metadata_manager.py b/tfbpapi/datainfo/metadata_manager.py new file mode 100644 index 0000000..fabdd9a --- /dev/null +++ b/tfbpapi/datainfo/metadata_manager.py @@ -0,0 +1,623 @@ +"""MetadataManager for cross-dataset metadata filtering and querying.""" + +import logging +import re +from pathlib import Path +from typing import Any, Optional + +import duckdb +import pandas as pd + +from ..errors import DataCardError +from .datacard import DataCard +from .models import FieldRole + +# Separator conventions for concatenated fields +COMPONENT_SEPARATORS = { + "type_value": ":", # Separates component type from value + "value_conc": "@", # Separates value from concentration + "components": ";", # Separates multiple components of same type + "types": "|", # Separates different component types (future use) +} + + +class MetadataManager: + """ + Cross-dataset metadata query manager using DuckDB temp views. + + Stores metadata field values as-is from parquet files, plus optionally expands field + definitions into searchable columns for experimental conditions. + + """ + + def __init__(self, cache_dir: Path | None = None, cache: bool = False): + """ + Initialize MetadataManager with optional caching. + + :param cache_dir: Directory for cached metadata (if cache=True) + :param cache: If True, persist metadata extractions. Default: False + + """ + self.logger = logging.getLogger(self.__class__.__name__) + self._conn = duckdb.connect(":memory:") + self._registered_datasets: dict[str, DataCard] = {} + self._view_names: dict[tuple[str, str], str] = {} # (repo_id, config) -> view + self._cache_dir = cache_dir + self._cache_enabled = cache + + def register(self, repo_id: str, config_names: list[str] | None = None) -> None: + """ + Register a dataset for cross-dataset queries. + + :param repo_id: HuggingFace repository identifier + :param config_names: Optional list of config names to register. If None, + registers all configs. + + """ + self.logger.info(f"Registering dataset: {repo_id}") + + # Load datacard + if repo_id not in self._registered_datasets: + datacard = DataCard(repo_id=repo_id) + self._registered_datasets[repo_id] = datacard + else: + datacard = self._registered_datasets[repo_id] + + # Determine which configs to register + if config_names is None: + configs_to_register = [c.config_name for c in datacard.configs] + else: + configs_to_register = config_names + + # Register each config + for config_name in configs_to_register: + self._register_config(datacard, config_name) + + # Recreate unified view + self._create_unified_view() + + def _register_config(self, datacard: DataCard, config_name: str) -> None: + """ + Register a single config for querying. + + :param datacard: DataCard instance + :param config_name: Configuration name to register + + """ + self.logger.debug(f"Registering config: {datacard.repo_id}/{config_name}") + + # Extract metadata schema + try: + schema = datacard.extract_metadata_schema(config_name) + except DataCardError as e: + self.logger.error(f"Failed to extract schema for {config_name}: {e}") + raise + + # Extract metadata from parquet file + try: + metadata_df = self._extract_metadata_from_config( + datacard, config_name, schema + ) + except Exception as e: + self.logger.error(f"Failed to extract metadata for {config_name}: {e}") + raise + + # Create temp view + view_name = self._sanitize_view_name(datacard.repo_id, config_name) + self._create_temp_view(datacard.repo_id, config_name, metadata_df, view_name) + self._view_names[(datacard.repo_id, config_name)] = view_name + + def unregister(self, repo_id: str) -> None: + """ + Remove dataset from cross-dataset queries. + + :param repo_id: HuggingFace repository identifier to unregister + + """ + if repo_id not in self._registered_datasets: + self.logger.warning(f"Dataset {repo_id} not registered") + return + + # Remove all views for this repo + views_to_remove = [ + (rid, cfg) for rid, cfg in self._view_names.keys() if rid == repo_id + ] + + for rid, cfg in views_to_remove: + view_name = self._view_names.pop((rid, cfg)) + try: + self._conn.execute(f"DROP VIEW IF EXISTS {view_name}") + except Exception as e: + self.logger.warning(f"Failed to drop view {view_name}: {e}") + + # Remove datacard + del self._registered_datasets[repo_id] + + # Recreate unified view + self._create_unified_view() + + def _extract_metadata_from_config( + self, datacard: DataCard, config_name: str, schema: dict[str, Any] + ) -> pd.DataFrame: + """ + Extract metadata from parquet file for a config. + + Processes experimental conditions at all three hierarchy levels: + - Top-level (repo-wide): Applies to ALL samples + - Config-level: Applies to all samples in this config + - Field-level: Varies per sample (from field definitions) + + Hierarchy merging: field-level > config-level > top-level + + :param datacard: DataCard instance + :param config_name: Configuration name + :param schema: Metadata schema from extract_metadata_schema() + + :return: DataFrame with metadata field values + expanded columns + + """ + # Get config to access data files + config = datacard.get_config(config_name) + if not config: + raise DataCardError(f"Configuration '{config_name}' not found") + + # Determine which fields to extract + metadata_fields = ( + schema["regulator_fields"] + + schema["target_fields"] + + schema["condition_fields"] + ) + + # Add sample_id if not already in list + if "sample_id" not in metadata_fields: + metadata_fields.append("sample_id") + + # TODO: For MVP, we need actual parquet file path from HuggingFace + # This requires integration with HF datasets or direct file access + # For now, create a minimal placeholder that will work with tests + + # Create placeholder DataFrame with required structure + metadata_df = pd.DataFrame() + + # Add computed columns + metadata_df["dataset"] = datacard.repo_id + metadata_df["config_name"] = config_name + + # Add metadata fields as empty for now + for field in metadata_fields: + metadata_df[field] = "unspecified" + + # Process experimental conditions in hierarchy order (low to high priority) + # Start with top-level (repo-wide) conditions + if schema.get("top_level_conditions"): + top_conditions = self._flatten_experimental_conditions( + schema["top_level_conditions"] + ) + for col_name, col_value in top_conditions.items(): + metadata_df[col_name] = col_value + + # Apply config-level conditions (overrides top-level) + if schema.get("config_level_conditions"): + config_conditions = self._flatten_experimental_conditions( + schema["config_level_conditions"] + ) + for col_name, col_value in config_conditions.items(): + metadata_df[col_name] = col_value + + # Expand field-level condition definitions (overrides config/top-level) + if schema["condition_definitions"]: + for field_name, definitions in schema["condition_definitions"].items(): + # Add expanded columns for each condition + metadata_df["growth_media"] = "unspecified" + metadata_df["components"] = "unspecified" + + return metadata_df + + def _flatten_experimental_conditions(self, exp_conditions: Any) -> dict[str, Any]: + """ + Flatten ExperimentalConditions object into column name/value pairs. + + Processes both environmental_conditions and strain_background from repo-level or + config-level ExperimentalConditions. Also handles extra fields stored in + model_extra (Pydantic's extra='allow'). + + :param exp_conditions: ExperimentalConditions instance + :return: Dict mapping column names to values + + """ + flattened: dict[str, Any] = {} + + # Handle strain_background + if ( + hasattr(exp_conditions, "strain_background") + and exp_conditions.strain_background + ): + strain = exp_conditions.strain_background + if isinstance(strain, str): + flattened["strain_background"] = strain + elif isinstance(strain, dict): + flattened["strain_background"] = strain.get("name", "unspecified") + + # Handle extra fields (for models with extra='allow') + # Some datacards store conditions directly as extra fields + if hasattr(exp_conditions, "model_extra") and exp_conditions.model_extra: + for key, value in exp_conditions.model_extra.items(): + # Store extra fields with their original names + flattened[key] = value + + # Handle environmental_conditions + if ( + hasattr(exp_conditions, "environmental_conditions") + and exp_conditions.environmental_conditions + ): + env = exp_conditions.environmental_conditions + + # Temperature + if ( + hasattr(env, "temperature_celsius") + and env.temperature_celsius is not None + ): + flattened["temperature_celsius"] = env.temperature_celsius + + # Cultivation method + if hasattr(env, "cultivation_method") and env.cultivation_method: + flattened["cultivation_method"] = env.cultivation_method + + # Media information + if hasattr(env, "media") and env.media: + media = env.media + if hasattr(media, "name") and media.name: + flattened["growth_media"] = media.name + + # Build components string from media composition + components = [] + + # Carbon source + if hasattr(media, "carbon_source") and media.carbon_source: + for compound in media.carbon_source: + if hasattr(compound, "compound"): + components.append( + self._format_compound( + "carbon_source", compound.model_dump() + ) + ) + + # Nitrogen source + if hasattr(media, "nitrogen_source") and media.nitrogen_source: + for compound in media.nitrogen_source: + if hasattr(compound, "compound"): + components.append( + self._format_compound( + "nitrogen_source", compound.model_dump() + ) + ) + + # Phosphate source + if hasattr(media, "phosphate_source") and media.phosphate_source: + for compound in media.phosphate_source: + if hasattr(compound, "compound"): + components.append( + self._format_compound( + "phosphate_source", compound.model_dump() + ) + ) + + # Additives + if hasattr(media, "additives") and media.additives: + for additive in media.additives: + if hasattr(additive, "name"): + components.append(f"additive:{additive.name}") + + if components: + flattened["components"] = "|".join(components) + + # Growth phase + if hasattr(env, "growth_phase") and env.growth_phase: + gp = env.growth_phase + if hasattr(gp, "stage") and gp.stage: + flattened["growth_stage"] = gp.stage + if hasattr(gp, "od600") and gp.od600 is not None: + flattened["od600"] = gp.od600 + + # Chemical treatments + if hasattr(env, "chemical_treatments") and env.chemical_treatments: + treatments = [] + for treatment in env.chemical_treatments: + if hasattr(treatment, "compound") and treatment.compound: + treatments.append(treatment.compound) + if treatments: + flattened["chemical_treatments"] = ";".join(treatments) + + # Drug treatments + if hasattr(env, "drug_treatments") and env.drug_treatments: + drugs = [] + for drug in env.drug_treatments: + if hasattr(drug, "compound") and drug.compound: + drugs.append(drug.compound) + if drugs: + flattened["drug_treatments"] = ";".join(drugs) + + # Heat treatment + if hasattr(env, "heat_treatment") and env.heat_treatment: + ht = env.heat_treatment + if ( + hasattr(ht, "temperature_celsius") + and ht.temperature_celsius is not None + ): + flattened["heat_treatment_temp"] = ht.temperature_celsius + + # Induction + if hasattr(env, "induction") and env.induction: + ind = env.induction + if hasattr(ind, "system") and ind.system: + flattened["induction_system"] = ind.system + + return flattened + + def _flatten_condition_definition( + self, definition: dict[str, Any] + ) -> dict[str, str]: + """ + Flatten a single condition definition into searchable fields. + + :param definition: Condition definition dict (e.g., YPD definition) + :return: Dict with flattened fields (growth_media, components) + + """ + flattened: dict[str, str] = { + "growth_media": "unspecified", + "components": "", + } + + # Extract environmental conditions if present + if "environmental_conditions" in definition: + env_conds = definition["environmental_conditions"] + + # Extract media information + if "media" in env_conds: + media = env_conds["media"] + if isinstance(media, dict): + # Extract media name + if "name" in media: + flattened["growth_media"] = media["name"] + + # Build components string + components = [] + + # Extract carbon source + if "carbon_source" in media: + carbon = media["carbon_source"] + if isinstance(carbon, list): + for compound in carbon: + components.append( + self._format_compound("carbon_source", compound) + ) + elif isinstance(carbon, dict): + components.append( + self._format_compound("carbon_source", carbon) + ) + + # Extract nitrogen source + if "nitrogen_source" in media: + nitrogen = media["nitrogen_source"] + if isinstance(nitrogen, list): + for compound in nitrogen: + components.append( + self._format_compound("nitrogen_source", compound) + ) + elif isinstance(nitrogen, dict): + components.append( + self._format_compound("nitrogen_source", compound) + ) + + # Extract phosphate source + if "phosphate_source" in media: + phosphate = media["phosphate_source"] + if isinstance(phosphate, list): + for compound in phosphate: + components.append( + self._format_compound("phosphate_source", compound) + ) + elif isinstance(phosphate, dict): + components.append( + self._format_compound("phosphate_source", compound) + ) + + # Join components + if components: + flattened["components"] = "|".join(components) + + return flattened + + def _format_compound(self, component_type: str, compound: dict[str, Any]) -> str: + """ + Format a compound dict into searchable string. + + :param component_type: Type of component (carbon_source, nitrogen_source, etc.) + :paramcompound: Compound info dict with name and concentration + :return: Formatted string (e.g., "carbon_source:D-glucose@2%") + + """ + if isinstance(compound, str): + return f"{component_type}:{compound}" + + name = compound.get("name", "unknown") + result = f"{component_type}:{name}" + + # Add concentration if present + if "concentration_percent" in compound: + result += f"@{compound['concentration_percent']}%" + elif "concentration_g_per_l" in compound: + result += f"@{compound['concentration_g_per_l']}g/L" + elif "concentration_molar" in compound: + result += f"@{compound['concentration_molar']}M" + + return result + + def _create_temp_view( + self, repo_id: str, config_name: str, metadata_df: pd.DataFrame, view_name: str + ) -> None: + """ + Create DuckDB temp view from metadata DataFrame. + + :param repo_id: Repository identifier + :param config_name: Configuration name + :param metadata_df: Metadata DataFrame + :param view_name: Sanitized view name + + """ + try: + # Register DataFrame as temp view in DuckDB + self._conn.register(view_name, metadata_df) + self.logger.debug(f"Created temp view: {view_name}") + except Exception as e: + self.logger.error(f"Failed to create view {view_name}: {e}") + raise + + def _sanitize_view_name(self, repo_id: str, config_name: str) -> str: + """ + Create a sanitized view name from repo_id and config_name. + + :param repo_id: Repository identifier + :param config_name: Configuration name + :return: Sanitized view name safe for SQL + + """ + # Replace non-alphanumeric with underscores + safe_repo = re.sub(r"[^a-zA-Z0-9]+", "_", repo_id) + safe_config = re.sub(r"[^a-zA-Z0-9]+", "_", config_name) + return f"{safe_repo}_{safe_config}_metadata" + + def _create_unified_view(self) -> None: + """Create unified_metadata view combining all registered datasets.""" + if not self._view_names: + self.logger.debug("No views registered, skipping unified view creation") + return + + # Drop existing unified view if present + try: + self._conn.execute("DROP VIEW IF EXISTS unified_metadata") + except Exception: + pass + + # Get all column names across all views + all_columns = set() + for view_name in self._view_names.values(): + try: + cols = ( + self._conn.execute(f"DESCRIBE {view_name}") + .fetchdf()["column_name"] + .tolist() + ) + all_columns.update(cols) + except Exception as e: + self.logger.warning(f"Failed to get columns from {view_name}: {e}") + + if not all_columns: + self.logger.warning("No columns found in registered views") + return + + # Build UNION ALL query with column alignment + union_queries = [] + for view_name in self._view_names.values(): + # Get columns in this view + view_cols = ( + self._conn.execute(f"DESCRIBE {view_name}") + .fetchdf()["column_name"] + .tolist() + ) + + # Build SELECT with coalesce for missing columns + select_parts = [] + for col in sorted(all_columns): + if col in view_cols: + select_parts.append(f'"{col}"') + else: + select_parts.append(f"'unspecified' AS \"{col}\"") + + union_queries.append(f"SELECT {', '.join(select_parts)} FROM {view_name}") + + # Create unified view + unified_query = " UNION ALL ".join(union_queries) + try: + self._conn.execute(f"CREATE VIEW unified_metadata AS {unified_query}") + self.logger.debug( + f"Created unified view from {len(self._view_names)} views with {len(all_columns)} columns" + ) + except Exception as e: + self.logger.error(f"Failed to create unified view: {e}") + raise + + def query(self, sql: str) -> pd.DataFrame: + """ + Execute SQL query across all registered datasets. + + :param sql: SQL query string to execute + :return: Query results as pandas DataFrame + + """ + try: + result = self._conn.execute(sql).fetchdf() + return result + except Exception as e: + self.logger.error(f"Query failed: {e}") + raise + + def filter_by_regulator(self, regulators: list[str]) -> "MetadataManager": + """ + Filter metadata to specific regulators. + + :param regulators: List of regulator symbols to filter by + :return: Self for method chaining + + """ + # TODO: Implement filtering logic + # This will be implemented in Phase 3 + raise NotImplementedError("filter_by_regulator not yet implemented") + + def filter_by_conditions(self, **kwargs: Any) -> "MetadataManager": + """ + Filter by experimental conditions. + + :param kwargs: Condition filters (e.g., media="YPD", temperature=30) + :return: Self for method chaining + + """ + # TODO: Implement filtering logic + # This will be implemented in Phase 3 + raise NotImplementedError("filter_by_conditions not yet implemented") + + def get_active_configs(self) -> list[tuple[str, str]]: + """ + Get (repo_id, config_name) pairs that match active filters. + + :return: List of (repo_id, config_name) tuples + + """ + # TODO: Implement with filter support + # For now, return all registered configs + return list(self._view_names.keys()) + + def get_summary(self) -> pd.DataFrame: + """ + Get summary stats for registered datasets. + + :return: DataFrame with summary statistics + + """ + if not self._view_names: + return pd.DataFrame() + + # TODO: Implement summary statistics + # This will be implemented in Phase 3 + summary_data = [] + for (repo_id, config_name), view_name in self._view_names.items(): + summary_data.append( + { + "dataset": repo_id, + "config_name": config_name, + "view_name": view_name, + } + ) + + return pd.DataFrame(summary_data) diff --git a/tfbpapi/datainfo/models.py b/tfbpapi/datainfo/models.py index b152248..ce5441f 100644 --- a/tfbpapi/datainfo/models.py +++ b/tfbpapi/datainfo/models.py @@ -1,9 +1,10 @@ """Pydantic models for dataset card validation.""" +import warnings from enum import Enum -from typing import Any, Dict, List, Optional, Set, Union +from typing import Any -from pydantic import BaseModel, ConfigDict, Field, field_validator +from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator class DatasetType(str, Enum): @@ -13,6 +14,317 @@ class DatasetType(str, Enum): ANNOTATED_FEATURES = "annotated_features" GENOME_MAP = "genome_map" METADATA = "metadata" + QC_DATA = "qc_data" + + +class FieldRole(str, Enum): + """Valid role values for feature fields.""" + + REGULATOR_IDENTIFIER = "regulator_identifier" + TARGET_IDENTIFIER = "target_identifier" + QUANTITATIVE_MEASURE = "quantitative_measure" + EXPERIMENTAL_CONDITION = "experimental_condition" + GENOMIC_COORDINATE = "genomic_coordinate" + + +class GrowthStage(str, Enum): + """Common growth stages.""" + + MID_LOG_PHASE = "mid_log_phase" + EARLY_LOG_PHASE = "early_log_phase" + LATE_LOG_PHASE = "late_log_phase" + STATIONARY_PHASE = "stationary_phase" + EARLY_STATIONARY_PHASE = "early_stationary_phase" + OVERNIGHT_STATIONARY_PHASE = "overnight_stationary_phase" + MID_LOG = "mid_log" + EARLY_LOG = "early_log" + LATE_LOG = "late_log" + EXPONENTIAL_PHASE = "exponential_phase" + + +class CompoundInfo(BaseModel): + """Information about a chemical compound in media.""" + + compound: str = Field(..., description="Chemical compound name") + concentration_percent: float | None = Field( + default=None, description="Concentration as percentage (w/v)" + ) + concentration_g_per_l: float | None = Field( + default=None, description="Concentration in grams per liter" + ) + concentration_molar: float | None = Field( + default=None, description="Concentration in molar (M)" + ) + specifications: list[str] | None = Field( + default=None, + description="Additional specifications (e.g., 'without_amino_acids')", + ) + + +class MediaAdditiveInfo(BaseModel): + """Information about media additives (e.g., butanol for filamentation).""" + + compound: str = Field(..., description="Additive compound name") + concentration_percent: float | None = Field( + default=None, description="Concentration as percentage (w/v)" + ) + description: str | None = Field( + default=None, description="Additional context about the additive" + ) + + +class MediaInfo(BaseModel): + """Growth media specification.""" + + name: str = Field( + ..., + description="Canonical or descriptive media name (minimal, synthetic_complete, YPD, etc.)", + ) + carbon_source: list[CompoundInfo] = Field( + ..., description="Carbon source compounds and concentrations" + ) + nitrogen_source: list[CompoundInfo] = Field( + ..., description="Nitrogen source compounds and concentrations" + ) + phosphate_source: list[CompoundInfo] | None = Field( + default=None, description="Phosphate source compounds and concentrations" + ) + additives: list[MediaAdditiveInfo] | None = Field( + default=None, + description="Additional media components (e.g., butanol for filamentation)", + ) + + @field_validator("carbon_source", "nitrogen_source", mode="before") + @classmethod + def validate_compound_list(cls, v): + """Validate compound lists and handle 'unspecified' strings.""" + if v is None: + return [] + if isinstance(v, str): + if v == "unspecified": + warnings.warn( + "Compound source specified as string 'unspecified'. " + "Should be null/omitted or a structured list.", + UserWarning, + ) + return [] + # Try to parse as single compound + return [{"compound": v}] + return v + + +class GrowthPhaseInfo(BaseModel): + """Growth phase information at harvest.""" + + od600: float | None = Field( + default=None, description="Optical density at 600nm at harvest" + ) + od600_tolerance: float | None = Field( + default=None, description="Measurement tolerance for OD600" + ) + stage: str | None = Field( + default=None, description="Growth stage (preferred field name)" + ) + phase: str | None = Field( + default=None, + description="Growth stage (alias for 'stage' for backward compatibility)", + ) + description: str | None = Field( + default=None, description="Additional context about growth phase" + ) + + @field_validator("stage", "phase", mode="before") + @classmethod + def validate_stage(cls, v): + """Validate stage and warn if not a common value.""" + if v is None: + return None + # Get the list of known stages from the enum + known_stages = {stage.value for stage in GrowthStage} + if v not in known_stages: + warnings.warn( + f"Growth stage '{v}' not in recognized stages: {known_stages}", + UserWarning, + ) + return v + + @model_validator(mode="after") + def check_stage_phase_consistency(self): + """Ensure stage and phase are consistent if both provided.""" + if self.stage and self.phase and self.stage != self.phase: + raise ValueError( + f"Inconsistent growth phase: stage='{self.stage}' vs phase='{self.phase}'" + ) + return self + + +class ChemicalTreatmentInfo(BaseModel): + """Chemical treatment applied to cultures.""" + + compound: str = Field(..., description="Chemical compound name") + concentration_percent: float | None = Field( + default=None, description="Concentration as percentage" + ) + concentration_molar: float | None = Field( + default=None, description="Concentration in molar (M)" + ) + duration_minutes: int | None = Field( + default=None, description="Duration in minutes" + ) + duration_hours: float | None = Field(default=None, description="Duration in hours") + target_pH: float | None = Field( + default=None, description="Target pH for pH adjustments" + ) + description: str | None = Field( + default=None, description="Additional context about the treatment" + ) + + +class DrugTreatmentInfo(ChemicalTreatmentInfo): + """Drug treatment - same structure as chemical treatment.""" + + pass + + +class HeatTreatmentInfo(BaseModel): + """Heat treatment information.""" + + duration_minutes: int = Field( + ..., description="Duration of heat treatment in minutes" + ) + description: str | None = Field( + default=None, description="Additional description of treatment" + ) + + +class TemperatureShiftInfo(BaseModel): + """Temperature shift for heat shock experiments.""" + + initial_temperature_celsius: float = Field( + ..., description="Initial cultivation temperature in Celsius" + ) + temperature_shift_celsius: float = Field( + ..., description="Temperature after shift in Celsius" + ) + temperature_shift_duration_minutes: int | None = Field( + default=None, description="Duration of temperature shift in minutes" + ) + description: str | None = Field( + default=None, description="Additional context about the temperature shift" + ) + + +class InductionInfo(BaseModel): + """Induction information for expression systems.""" + + inducer: CompoundInfo = Field(..., description="Inducer compound and concentration") + duration_hours: float | None = Field( + default=None, description="Duration of induction in hours" + ) + duration_minutes: int | None = Field( + default=None, description="Duration of induction in minutes" + ) + description: str | None = Field( + default=None, description="Additional context about the induction" + ) + + +class EnvironmentalConditions(BaseModel): + """Environmental conditions for sample cultivation.""" + + temperature_celsius: float | None = Field( + default=None, description="Cultivation temperature in Celsius" + ) + cultivation_method: str | None = Field( + default=None, + description="Cultivation method (e.g., 'batch_culture', 'chemostat')", + ) + growth_phase_at_harvest: GrowthPhaseInfo | None = Field( + default=None, description="Growth phase at time of harvest" + ) + media: MediaInfo | None = Field( + default=None, description="Growth media specification" + ) + chemical_treatment: ChemicalTreatmentInfo | None = Field( + default=None, description="Chemical treatment applied" + ) + drug_treatment: DrugTreatmentInfo | None = Field( + default=None, + description="Drug treatment applied (same structure as chemical_treatment)", + ) + heat_treatment: HeatTreatmentInfo | None = Field( + default=None, description="Heat treatment applied" + ) + temperature_shift: TemperatureShiftInfo | None = Field( + default=None, description="Temperature shift for heat shock experiments" + ) + induction: InductionInfo | None = Field( + default=None, description="Induction system for expression experiments" + ) + incubation_duration_hours: float | None = Field( + default=None, description="Total incubation duration in hours" + ) + incubation_duration_minutes: int | None = Field( + default=None, description="Total incubation duration in minutes" + ) + description: str | None = Field( + default=None, description="Additional descriptive information" + ) + + model_config = ConfigDict(extra="allow") + + @model_validator(mode="after") + def warn_extra_fields(self): + """Warn about any extra fields not in the specification.""" + known_fields = { + "temperature_celsius", + "cultivation_method", + "growth_phase_at_harvest", + "media", + "chemical_treatment", + "drug_treatment", + "heat_treatment", + "temperature_shift", + "induction", + "incubation_duration_hours", + "incubation_duration_minutes", + "description", + } + extra = set(self.__dict__.keys()) - known_fields + if extra: + warnings.warn( + f"EnvironmentalConditions contains non-standard fields: {extra}. " + "Consider adding to specification.", + UserWarning, + ) + return self + + +class ExperimentalConditions(BaseModel): + """Experimental conditions including environmental and other parameters.""" + + environmental_conditions: EnvironmentalConditions | None = Field( + default=None, description="Environmental cultivation conditions" + ) + strain_background: str | dict[str, Any] | None = Field( + default=None, + description="Strain background information (string or flexible dict structure)", + ) + + model_config = ConfigDict(extra="allow") + + @model_validator(mode="after") + def warn_extra_fields(self): + """Warn about any extra fields not in the specification.""" + known_fields = {"environmental_conditions", "strain_background"} + extra = set(self.__dict__.keys()) - known_fields + if extra: + warnings.warn( + f"ExperimentalConditions contains non-standard fields: {extra}. " + "Consider adding to specification.", + UserWarning, + ) + return self class ClassLabelType(BaseModel): @@ -27,12 +339,19 @@ class FeatureInfo(BaseModel): name: str = Field(..., description="Column name in the data") dtype: str | dict[str, ClassLabelType] = Field( ..., - description="Data type (string, int64, float64, etc.) or categorical class labels", + description="Data type (string, int64, float64, etc.) " + "or categorical class labels", ) description: str = Field(..., description="Detailed description of the field") - role: str | None = Field( + role: FieldRole | None = Field( + default=None, + description="Semantic role of the feature (e.g., 'target_identifier', " + "'regulator_identifier', 'quantitative_measure')", + ) + definitions: dict[str, Any] | None = Field( default=None, - description="Semantic role of the feature (e.g., 'target_identifier', 'regulator_identifier', 'quantitative_measure')", + description="Definitions for categorical field values " + "with experimental conditions", ) @field_validator("dtype", mode="before") @@ -50,15 +369,18 @@ def validate_dtype(cls, v): return {"class_label": ClassLabelType(**class_label_data)} else: raise ValueError( - f"Invalid class_label structure: expected dict with 'names' key, got {class_label_data}" + "Invalid class_label structure: expected dict " + f"with 'names' key, got {class_label_data}" ) else: raise ValueError( - f"Unknown dtype structure: expected 'class_label' key in dict, got keys: {list(v.keys())}" + "Unknown dtype structure: expected 'class_label' key " + f"in dict, got keys: {list(v.keys())}" ) else: raise ValueError( - f"dtype must be a string or dict with class_label info, got {type(v)}: {v}" + "dtype must be a string or dict with " + f"class_label info, got {type(v)}: {v}" ) def get_dtype_summary(self) -> str: @@ -115,18 +437,22 @@ class DatasetConfig(BaseModel): metadata_fields: list[str] | None = Field( default=None, description="Fields for embedded metadata extraction" ) + experimental_conditions: ExperimentalConditions | None = Field( + default=None, description="Experimental conditions for this config" + ) data_files: list[DataFileInfo] = Field(..., description="Data file information") dataset_info: DatasetInfo = Field(..., description="Dataset structure information") @field_validator("applies_to") @classmethod def applies_to_only_for_metadata(cls, v, info): - """Validate that applies_to is only used for metadata configs.""" + """Validate that applies_to is only used for metadata or qc_data configs.""" if v is not None: dataset_type = info.data.get("dataset_type") - if dataset_type != DatasetType.METADATA: + if dataset_type not in (DatasetType.METADATA, DatasetType.QC_DATA): raise ValueError( - "applies_to field is only valid for metadata dataset types" + "applies_to field is only valid " + "for metadata and qc_data dataset types" ) return v @@ -157,6 +483,9 @@ class DatasetCard(BaseModel): """Complete dataset card model.""" configs: list[DatasetConfig] = Field(..., description="Dataset configurations") + experimental_conditions: ExperimentalConditions | None = Field( + default=None, description="Top-level experimental conditions for all configs" + ) license: str | None = Field(default=None, description="Dataset license") language: list[str] | None = Field(default=None, description="Dataset languages") tags: list[str] | None = Field(default=None, description="Descriptive tags") @@ -166,6 +495,11 @@ class DatasetCard(BaseModel): size_categories: list[str] | None = Field( default=None, description="Dataset size categories" ) + strain_information: dict[str, Any] | None = Field( + default=None, description="Strain background information" + ) + + model_config = ConfigDict(extra="allow") @field_validator("configs") @classmethod diff --git a/tfbpapi/tests/datainfo/example_datacards.py b/tfbpapi/tests/datainfo/example_datacards.py new file mode 100644 index 0000000..1d732de --- /dev/null +++ b/tfbpapi/tests/datainfo/example_datacards.py @@ -0,0 +1,509 @@ +""" +Three diverse datacard examples for testing datacard parsing and database construction. + +These examples capture different patterns of experimental condition specification: +1. Top-level conditions with field-level variations (minimal media) +2. Complex field-level definitions with multiple environmental conditions +3. Partitioned dataset with separate metadata configs using applies_to + +""" + +EXAMPLE_1_SIMPLE_TOPLEVEL = """--- +license: mit +language: + - en +tags: + - genomics + - yeast + - transcription +pretty_name: "Example Dataset 1 - TF Perturbation" +size_categories: + - 100K- + Systematic gene identifier of the ChIP-targeted transcription factor + role: regulator_identifier + - name: regulator_symbol + dtype: string + description: Standard gene symbol of the ChIP-targeted transcription factor + role: regulator_identifier + - name: target_locus_tag + dtype: string + description: Systematic gene identifier of the target gene + role: target_identifier + - name: target_symbol + dtype: string + description: Standard gene symbol of the target gene + role: target_identifier + - name: condition + dtype: + class_label: + names: ["YPD", "galactose", "heat_shock", "oxidative_stress", + "amino_acid_starvation"] + description: Environmental or stress condition of the experiment + role: experimental_condition + definitions: + YPD: + description: Rich media baseline condition + environmental_conditions: + temperature_celsius: 30 + cultivation_method: liquid_culture + growth_phase_at_harvest: + od600: 0.6 + stage: mid_log_phase + media: + name: YPD + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: 1 + - compound: peptone + concentration_percent: 2 + galactose: + description: Alternative carbon source condition + environmental_conditions: + temperature_celsius: 30 + cultivation_method: liquid_culture + growth_phase_at_harvest: + od600: 0.6 + stage: mid_log_phase + media: + name: YPD + carbon_source: + - compound: D-galactose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: 1 + - compound: peptone + concentration_percent: 2 + heat_shock: + description: Temperature stress condition + environmental_conditions: + temperature_celsius: 37 + cultivation_method: liquid_culture + growth_phase_at_harvest: + od600: 0.6 + stage: mid_log_phase + media: + name: YPD + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: 1 + - compound: peptone + concentration_percent: 2 + heat_treatment: + duration_minutes: 15 + oxidative_stress: + description: Hydrogen peroxide stress condition + environmental_conditions: + temperature_celsius: 30 + cultivation_method: liquid_culture + growth_phase_at_harvest: + od600: 0.6 + stage: mid_log_phase + media: + name: YPD + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: 1 + - compound: peptone + concentration_percent: 2 + chemical_treatment: + compound: hydrogen_peroxide + concentration_percent: 0.004 + duration_minutes: 20 + amino_acid_starvation: + description: Amino acid starvation via chemical inhibition + environmental_conditions: + temperature_celsius: 30 + cultivation_method: liquid_culture + growth_phase_at_harvest: + od600: 0.5 + stage: mid_log_phase + media: + name: synthetic_complete + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_nitrogen_base + # 6.71 g/L + concentration_percent: 0.671 + specifications: + - without_amino_acids + - without_ammonium_sulfate + - compound: ammonium_sulfate + # 5 g/L + concentration_percent: 0.5 + - compound: amino_acid_dropout_mix + # 2 g/L + concentration_percent: 0.2 + chemical_treatment: + compound: 3-amino-1,2,4-triazole + concentration_percent: 0.01 + duration_hours: 1 + - name: binding_score + dtype: float64 + description: ChIP-seq binding enrichment score + role: quantitative_measure + - name: peak_pvalue + dtype: float64 + description: Statistical significance of binding peak + role: quantitative_measure + - name: peak_qvalue + dtype: float64 + description: FDR-adjusted p-value for binding peak + role: quantitative_measure +--- +""" + + +EXAMPLE_3_PARTITIONED_WITH_METADATA = """--- +license: mit +language: + - en +tags: + - genomics + - yeast + - binding + - genome-wide + - chec-seq +pretty_name: "Example Dataset 3 - Genome Coverage Compendium" +size_categories: + - 10M- + unique identifier for a specific sample. The sample ID identifies a unique + (regulator_locus_tag, time, mechanism, restriction, date, strain) tuple. + - name: db_id + dtype: integer + description: >- + an old unique identifer, for use internally only. Deprecated and will be removed eventually. + Do not use in analysis. db_id = 0, for GEV and Z3EV, means that those samples are not + included in the original DB. + - name: regulator_locus_tag + dtype: string + description: >- + induced transcriptional regulator systematic ID. + See hf/BrentLab/yeast_genome_resources + role: regulator_identifier + - name: regulator_symbol + dtype: string + description: >- + induced transcriptional regulator common name. If no common name exists, + then the `regulator_locus_tag` is used. + role: regulator_identifier + - name: target_locus_tag + dtype: string + description: >- + The systematic ID of the feature to which the effect/pvalue is assigned. + See hf/BrentLab/yeast_genome_resources + role: target_identifier + - name: target_symbol + dtype: string + description: >- + The common name of the feature to which the effect/pvalue is assigned. + If there is no common name, the `target_locus_tag` is used. + role: target_identifier + - name: time + dtype: float + description: time point (minutes) + role: experimental_condition + - name: mechanism + dtype: + class_label: + names: ["GEV", "ZEV"] + description: Synthetic TF induction system (GEV or ZEV) + role: experimental_condition + - name: restriction + dtype: + class_label: + names: ["M", "N", "P"] + description: >- + nutrient limitation, one of P (phosphate limitation (20 mg/l).), + N (Nitrogen‐limited cultures were maintained at 40 mg/l ammonium sulfate) or + M (Not defined in the paper or on the Calico website) + role: experimental_condition + definitions: + P: + environmental_conditions: + media: + nitrogen_source: + - compound: ammonium_sulfate + # Saldanha et al 2004: 5 g/l + concentration_percent: 0.5 + phosphate_source: + - compound: potassium_phosphate_monobasic + # Hackett et al 2020: 20 mg/l + concentration_percent: 0.002 + N: + environmental_conditions: + media: + nitrogen_source: + - compound: ammonium_sulfate + # Hackett et al 2020: 40 mg/l + concentration_percent: 0.004 + M: + description: "Not defined in the paper or on the Calico website" + - name: date + dtype: string + description: date performed + role: experimental_condition + - name: strain + dtype: string + description: strain name + role: experimental_condition + - name: green_median + dtype: float + description: median of green (reference) channel fluorescence + role: quantitative_measure + - name: red_median + dtype: float + description: median of red (experimental) channel fluorescence + role: quantitative_measure + - name: log2_ratio + dtype: float + description: log2(red / green) subtracting value at time zero + role: quantitative_measure + - name: log2_cleaned_ratio + dtype: float + description: Non-specific stress response and prominent outliers removed + role: quantitative_measure + - name: log2_noise_model + dtype: float + description: estimated noise standard deviation + role: quantitative_measure + - name: log2_cleaned_ratio_zth2d + dtype: float + description: >- + cleaned timecourses hard-thresholded based on + multiple observations (or last observation) passing the noise model + role: quantitative_measure + - name: log2_selected_timecourses + dtype: float + description: >- + cleaned timecourses hard-thresholded based on single observations + passing noise model and impulse evaluation of biological feasibility + role: quantitative_measure + - name: log2_shrunken_timecourses + dtype: float + description: >- + selected timecourses with observation-level shrinkage based on + local FDR (false discovery rate). Most users of the data will want + to use this column. + role: quantitative_measure +--- + +# harbison_2004 +--- +license: mit +language: + - en +tags: + - genomics + - yeast + - transcription + - binding +pretty_name: "Harbison, 2004 ChIP-chip" +size_categories: + - 1M- + Environmental condition of the experiment. Nearly all of the 204 regulators + have a YPD condition, and some have others in addition. + role: experimental_condition + definitions: + YPD: + description: Rich media baseline condition + environmental_conditions: + # Harbison et al 2004: grown at 30°C (from HEAT condition context) + temperature_celsius: 30 + cultivation_method: unspecified + growth_phase_at_harvest: + # Harbison et al 2004: OD600 ~0.8 + od600: 0.8 + media: + # Harbison et al 2004: 1% yeast extract / 2% peptone / 2% glucose + name: YPD + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: 1 + - compound: peptone + concentration_percent: 2 + SM: + description: Amino acid starvation stress condition + environmental_conditions: + temperature_celsius: 30 + cultivation_method: unspecified + growth_phase_at_harvest: + # Harbison et al 2004: OD600 ~0.6 + od600: 0.6 + media: + # Harbison et al 2004: synthetic complete medium + name: synthetic_complete + carbon_source: unspecified + nitrogen_source: unspecified + chemical_treatment: + compound: sulfometuron_methyl + # Harbison et al 2004: 0.2 mg/ml + concentration_percent: 0.02 + duration_hours: 2 + RAPA: + description: Nutrient deprivation via TOR inhibition + environmental_conditions: + temperature_celsius: 30 + cultivation_method: unspecified + growth_phase_at_harvest: + # Harbison et al 2004: OD600 ~0.8 + od600: 0.8 + media: + name: YPD + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: 1 + - compound: peptone + concentration_percent: 2 + chemical_treatment: + compound: rapamycin + # Harbison et al 2004: 100 nM + concentration_percent: 9.142e-6 + duration_minutes: 20 + H2O2Hi: + description: High oxidative stress condition + environmental_conditions: + temperature_celsius: 30 + cultivation_method: unspecified + growth_phase_at_harvest: + # Harbison et al 2004: OD600 ~0.5 + od600: 0.5 + media: + name: YPD + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: 1 + - compound: peptone + concentration_percent: 2 + chemical_treatment: + compound: hydrogen_peroxide + # Harbison et al 2004: 4 mM + concentration_percent: 0.0136 + duration_minutes: 30 + H2O2Lo: + description: Moderate oxidative stress condition + environmental_conditions: + temperature_celsius: 30 + cultivation_method: unspecified + growth_phase_at_harvest: + # Harbison et al 2004: OD600 ~0.5 + od600: 0.5 + media: + name: YPD + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: 1 + - compound: peptone + concentration_percent: 2 + chemical_treatment: + compound: hydrogen_peroxide + # Harbison et al 2004: 0.4 mM + concentration_percent: 0.00136 + duration_minutes: 20 + Acid: + description: Acidic pH stress condition + environmental_conditions: + temperature_celsius: 30 + cultivation_method: unspecified + growth_phase_at_harvest: + # Harbison et al 2004: OD600 ~0.5 + od600: 0.5 + media: + name: YPD + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: 1 + - compound: peptone + concentration_percent: 2 + chemical_treatment: + compound: succinic_acid + # Harbison et al 2004: 0.05 M to reach pH 4.0 + concentration_percent: 0.59 + target_pH: 4.0 + duration_minutes: 30 + Alpha: + description: Mating pheromone induction condition + environmental_conditions: + temperature_celsius: 30 + cultivation_method: unspecified + growth_phase_at_harvest: + # Harbison et al 2004: OD600 ~0.8 + od600: 0.8 + media: + name: YPD + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: 1 + - compound: peptone + concentration_percent: 2 + chemical_treatment: + compound: alpha_factor_pheromone + # Harbison et al 2004: 5 mg/ml + concentration_percent: 0.5 + duration_minutes: 30 + BUT14: + description: Long-term filamentation induction with butanol + environmental_conditions: + temperature_celsius: 30 + cultivation_method: unspecified + growth_phase_at_harvest: + # Harbison et al 2004: OD600 ~0.8 + od600: 0.8 + media: + # Harbison et al 2004: YPD containing 1% butanol + name: YPD + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: 1 + - compound: peptone + concentration_percent: 2 + additives: + - compound: butanol + concentration_percent: 1 + incubation_duration_hours: 14 + BUT90: + description: Short-term filamentation induction with butanol + environmental_conditions: + temperature_celsius: 30 + cultivation_method: unspecified + growth_phase_at_harvest: + # Harbison et al 2004: OD600 ~0.8 + od600: 0.8 + media: + # Harbison et al 2004: YPD containing 1% butanol + name: YPD + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: 1 + - compound: peptone + concentration_percent: 2 + additives: + - compound: butanol + concentration_percent: 1 + incubation_duration_minutes: 90 + "Thi-": + description: Vitamin B1 deprivation stress condition + environmental_conditions: + temperature_celsius: 30 + cultivation_method: unspecified + growth_phase_at_harvest: + # Harbison et al 2004: OD600 ~0.8 + od600: 0.8 + media: + # Harbison et al 2004: synthetic complete medium lacking thiamin + name: synthetic_complete_minus_thiamine + carbon_source: unspecified + nitrogen_source: unspecified + GAL: + description: Galactose-based growth medium condition + environmental_conditions: + temperature_celsius: 30 + cultivation_method: unspecified + growth_phase_at_harvest: + # Harbison et al 2004: OD600 ~0.8 + od600: 0.8 + media: + # Harbison et al 2004: YEP medium supplemented with galactose (2%) + name: yeast_extract_peptone + carbon_source: + - compound: D-galactose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: unspecified + - compound: peptone + concentration_percent: unspecified + HEAT: + description: Heat shock stress condition + environmental_conditions: + # Harbison et al 2004: grown at 30°C, shifted to 37°C for 45 min + initial_temperature_celsius: 30 + temperature_shift_celsius: 37 + temperature_shift_duration_minutes: 45 + cultivation_method: unspecified + growth_phase_at_harvest: + # Harbison et al 2004: OD600 ~0.5 + od600: 0.5 + media: + # Harbison et al 2004: YPD + name: YPD + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: 1 + - compound: peptone + concentration_percent: 2 + "Pi-": + description: Phosphate deprivation stress condition + environmental_conditions: + temperature_celsius: 30 + cultivation_method: unspecified + growth_phase_at_harvest: + # Harbison et al 2004: OD600 ~0.8 + od600: 0.8 + media: + # Harbison et al 2004: synthetic complete medium lacking phosphate + name: synthetic_complete_minus_phosphate + carbon_source: unspecified + nitrogen_source: unspecified + RAFF: + description: Raffinose-based growth medium condition + environmental_conditions: + temperature_celsius: 30 + cultivation_method: unspecified + growth_phase_at_harvest: + # Harbison et al 2004: OD600 ~0.8 + od600: 0.8 + media: + # Harbison et al 2004: YEP medium supplemented with raffinose (2%) + name: yeast_extract_peptone + carbon_source: + - compound: D-raffinose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_extract + concentration_percent: unspecified + - compound: peptone + concentration_percent: unspecified + - name: regulator_locus_tag + dtype: string + description: Systematic gene name (ORF identifier) of the ChIPd transcription factor + role: regulator_identifier + - name: regulator_symbol + dtype: string + description: Standard gene symbol of the ChIPd transcription factor + role: regulator_identifier + - name: target_locus_tag + dtype: string + description: Systematic gene name (ORF identifier) of the target gene measured + role: target_identifier + - name: target_symbol + dtype: string + description: Standard gene symbol of the target gene measured + role: target_identifier + - name: effect + dtype: float64 + description: The chip channel ratio (effect size) + role: quantitative_measure + - name: pvalue + dtype: float64 + description: pvalue of the chip channel ratio (effect) + role: quantitative_measure +--- + +# hu_2007_reimand_2010 +--- +license: mit +language: + - en +tags: + - genomics + - yeast + - transcription + - perturbation + - response + - knockout + - TFKO +pretty_name: Hu 2007/Reimand 2010 TFKO +size_categories: + - 1M- + an old unique identifer, for use internally only. Deprecated and will be removed eventually. + Do not use in analysis. + - name: regulator_locus_tag + dtype: string + description: induced transcriptional regulator systematic ID. See hf/BrentLab/yeast_genome_resources + role: regulator_identifier + - name: regulator_symbol + dtype: string + description: induced transcriptional regulator common name. If no common name exists, then the `regulator_locus_tag` is used. + role: regulator_identifier + - name: target_locus_tag + dtype: string + description: The systematic ID of the feature to which the effect/pvalue is assigned. See hf/BrentLab/yeast_genome_resources + role: target_identifier + - name: target_symbol + dtype: string + description: The common name of the feature to which the effect/pvalue is assigned. If there is no common name, the `target_locus_tag` is used. + role: target_identifier + - name: effect + dtype: float + description: >- + log fold change of mutant vs wt. From the remaind methods: Differential expression + was calculated using a moderated eBayes t-test as implemented in the Limma + Bioconductor package + role: quantitative_measure + - name: pval + dtype: float + description: P-values were FDR-adjusted across the whole microarray dataset to correct for multiple testing + role: quantitative_measure + - name: average_od_of_replicates + dtype: float + description: average OD of the replicates at harvest + - name: heat_shock + dtype: bool + description: >- + `True` if the regulator strain was subjected to heat shock treatment. + Applied to 22 transcription factors implicated in heat shock response. + `False` otherwise + role: experimental_condition + definitions: + true: + environmental_conditions: + # Hu et al 2007: "15-min heat shock at 39°C" + temperature_celsius: 39 + duration_minutes: 15 + strain_background: + genotype: BY4741 + mating_type: MATa + markers: + - his3Δ1 + - leu2Δ0 + - met15Δ0 + - ura3Δ0 + source: Open_Biosystems + description: Knockout strains for nonessential transcription factors + false: + environmental_conditions: + description: Standard growth conditions at 30°C + strain_background: + genotype: BY4741 + mating_type: MATa + markers: + - his3Δ1 + - leu2Δ0 + - met15Δ0 + - ura3Δ0 + source: Open_Biosystems + description: Knockout strains for nonessential transcription factors + - name: tetracycline_treatment + dtype: bool + description: >- + `True` if the regulator strain was treated with doxycycline to repress + TetO7-promoter regulated essential transcription factors. Applied to 6 + essential transcription factors. `False` for untreated control condition. + role: experimental_condition + definitions: + true: + environmental_conditions: + drug_treatment: + compound: doxycycline + # Hu et al 2007: 10 mg/ml + concentration_percent: 1 + duration_hours_min: 14 + duration_hours_max: 16 + strain_background: + genotype: BY4741_derivative + mating_type: MATa + markers: + - URA3::CMV-tTA + - his3Δ1 + - leu2Δ0 + - met15Δ0 + source: Open_Biosystems + description: Essential transcription factors with TetO7-promoter regulation + false: + environmental_conditions: + description: No doxycycline treatment; TetO7 promoter active + strain_background: + genotype: BY4741_derivative + mating_type: MATa + markers: + - URA3::CMV-tTA + - his3Δ1 + - leu2Δ0 + - met15Δ0 + source: Open_Biosystems + description: Essential transcription factors with TetO7-promoter regulation +--- + +# hughes_2006 +--- +license: mit +language: +- en +tags: +- biology +- genomics +- yeast +- transcription-factors +- gene-expression +- perturbation-screen +- overexpression +- knockout +- microarray +- functional-genomics +pretty_name: "Hughes 2006 Yeast Transcription Factor Perturbation Dataset" +size_categories: +- 100K- + unique identifier for a specific sample. The sample ID identifies + a unique regulator_locus_tag and can be used to join to the + other datasets in this repo, including the metadata + - name: regulator_locus_tag + dtype: string + role: identifier + description: >- + Systematic gene name (ORF identifier) of the + transcription factor + - name: regulator_symbol + dtype: string + description: Standard gene symbol of the transcription factor + - name: found_domain + dtype: string + description: >- + Identified DNA-binding domain(s) or protein family classification + - name: sgd_description + dtype: string + description: >- + Functional description from Saccharomyces Genome Database (SGD) + - name: essential + dtype: bool + description: >- + Boolean indicating whether the gene is essential for viability + - name: oe_passed_qc + dtype: bool + description: >- + Boolean indicating whether overexpression experiments passed + quality control + - name: del_passed_qc + dtype: bool + description: >- + Boolean indicating whether deletion experiments passed + quality control + +- config_name: overexpression + description: Overexpression perturbation normalized log2 fold changes + dataset_type: annotated_features + data_files: + - split: train + path: overexpression.parquet + experimental_conditions: + environmental_conditions: + temperature_celsius: unspecified + cultivation_method: unspecified + media: + # Hughes et al 2006: "selective medium supplemented with 2% raffinose" + name: selective_medium + carbon_source: + - compound: D-raffinose + # Hughes et al 2006: 2% raffinose + concentration_percent: 2 + nitrogen_source: unspecified + induction: + # Hughes et al 2006: "induction with 2% galactose for 3 h" + inducer: + compound: D-galactose + concentration_percent: 2 + duration_hours: 3 + dataset_info: + features: + - name: sample_id + dtype: integer + description: >- + unique identifier for a specific sample. The sample ID identifies + a unique regulator_locus_tag and can be used to join to the + other datasets in this repo, including the metadata + - name: regulator_locus_tag + dtype: string + description: >- + Systematic gene name (ORF identifier) of the + perturbed transcription factor + role: regulator_identifier + - name: regulator_symbol + dtype: string + description: Standard gene symbol of the perturbed transcription factor + - name: target_locus_tag + dtype: string + description: >- + Systematic gene name (ORF identifier) of the + target gene measured + role: target_identifier + - name: target_symbol + dtype: string + description: Standard gene symbol of the target gene measured + role: target_identifier + - name: dye_plus + dtype: float64 + role: quantitative_measure + description: >- + Normalized log2 fold change for positive (+) dye orientation. + Positive values indicate upregulation in response to overexpression. + - name: dye_minus + dtype: float64 + role: quantitative_measure + description: >- + Normalized log2 fold change for negative (-) dye orientation. + Positive values indicate upregulation in response to overexpression. + - name: mean_norm_log2fc + dtype: float64 + role: quantitative_measure + description: >- + Average log2 fold change across dye orientations, + providing a dye-independent estimate of gene expression + change upon transcription factor overexpression. + +- config_name: knockout + description: Deletion/knockout perturbation normalized log2 fold changes + dataset_type: annotated_features + data_files: + - split: train + path: knockout.parquet + experimental_conditions: + environmental_conditions: + temperature_celsius: unspecified + cultivation_method: unspecified + media: + # Hughes et al 2006: "synthetic medium supplemented with 2% dextrose" + name: synthetic_medium + carbon_source: + - compound: D-glucose + # Hughes et al 2006: 2% dextrose + concentration_percent: 2 + nitrogen_source: unspecified + dataset_info: + features: + - name: sample_id + dtype: integer + description: >- + unique identifier for a specific sample. The sample ID identifies + a unique regulator_locus_tag and can be used to join to the + other datasets in this repo, including the metadata + - name: regulator_locus_tag + dtype: string + description: >- + Systematic gene name (ORF identifier) of the perturbed + transcription factor + role: regulator_identifier + - name: regulator_symbol + dtype: string + description: Standard gene symbol of the perturbed transcription factor + role: regulator_identifier + - name: target_locus_tag + dtype: string + description: >- + Systematic gene name (ORF identifier) of the + target gene measured + role: target_identifier + - name: target_symbol + dtype: string + description: Standard gene symbol of the target gene measured + role: target_identifier + - name: dye_plus + dtype: float64 + description: >- + Normalized log2 fold change for positive (+) dye orientation. + Positive values indicate upregulation in response to deletion. + role: quantitative_measure + - name: dye_minus + dtype: float64 + description: >- + Normalized log2 fold change for negative (-) dye orientation. + Positive values indicate upregulation in response to deletion. + role: quantitative_measure + - name: mean_norm_log2fc + dtype: float64 + description: >- + Average log2 fold change across dye orientations, providing a + dye-independent estimate of gene expression change upon + transcription factor deletion. + role: quantitative_measure +--- + +# kemmeren_2014 +--- +license: mit +language: +- en +tags: +- genomics +- yeast +- transcription +- perturbation +- response +- knockout +- TFKO +pretty_name: "Kemmeren, 2014 Overexpression" +size_categories: +- 1M- + Transcriptional regulator overexpression perturbation data with + differential expression measurements + dataset_type: annotated_features + default: true + metadata_fields: ["regulator_locus_tag", "regulator_symbol"] + data_files: + - split: train + path: kemmeren_2014.parquet + dataset_info: + features: + - name: sample_id + dtype: integer + description: >- + unique identifier for a specific sample. + The sample ID identifies a unique regulator. + - name: db_id + dtype: integer + description: >- + an old unique identifer, for use internally only. Deprecated and will be removed eventually. + Do not use in analysis. db_id = 0 for loci that were originally parsed incorrectly. + - name: regulator_locus_tag + dtype: string + description: >- + induced transcriptional regulator systematic ID. + See hf/BrentLab/yeast_genome_resources + role: regulator_identifier + - name: regulator_symbol + dtype: string + description: >- + induced transcriptional regulator common name. + If no common name exists, then the `regulator_locus_tag` is used. + role: regulator_identifier + - name: reporterId + dtype: string + description: probe ID as reported from the original data + - name: target_locus_tag + dtype: string + description: >- + The systematic ID of the feature to which the effect/pvalue is assigned. + See hf/BrentLab/yeast_genome_resources + role: target_identifier + - name: target_symbol + dtype: string + description: >- + The common name of the feature to which the effect/pvalue is assigned. + If there is no common name, the `target_locus_tag` is used. + role: target_identifier + - name: M + dtype: float64 + description: log₂ fold change (mutant vs wildtype) + role: quantitative_measure + - name: Madj + dtype: float64 + description: >- + M value with the cell cycle signal removed + (see paper cited in the introduction above) + role: quantitative_measure + - name: A + dtype: float64 + description: >- + average log2 intensity of the two channels, a proxy for expression level + (This is a guess based on microarray convention -- not specified on holstege site) + role: quantitative_measure + - name: pval + dtype: float64 + description: significance of the modeled effect (M), from limma + role: quantitative_measure + - name: variable_in_wt + dtype: string + description: >- + True if the given locus is variable in the WT condition. + Recommended to remove these from analysis. False otherwise. + See Holstege website for more information + role: experimental_condition + - name: multiple_probes + dtype: string + description: >- + True if there is more than one probe associated with + the same genomic locus. False otherwise + role: experimental_condition + - name: kemmeren_regulator + dtype: string + description: >- + True if the regulator is one of the regulators studied in the + original Kemmeren et al. (2014) global regulator study. False otherwise + role: experimental_condition + - name: regulator_desc + dtype: string + description: >- + functional description of the induced regulator + from the original paper supplement + role: experimental_condition + - name: functional_category + dtype: string + description: functional classification of the regulator from the original paper supplement + role: experimental_condition + - name: slides + dtype: string + description: identifier(s) for the microarray slide(s) used in this experiment + role: experimental_condition + - name: mating_type + dtype: string + description: mating type of the strain background used in the experiment + role: experimental_condition + - name: source_of_deletion_mutants + dtype: string + description: origin of the strain + role: experimental_condition + - name: primary_hybsets + dtype: string + description: identifier for the primary hybridization set to which this sample belongs + role: experimental_condition + - name: responsive_non_responsive + dtype: string + description: >- + classification of the regulator as responsive or not to the + deletion from the original paper supplement + role: experimental_condition + - name: nr_sign_changes + dtype: integer + description: >- + number of significant changes in expression detected for the regulator locus tag (abs(M) > log2(1.7) & pval < 0.05). + Note that there is a slight difference when calculating from the data provided here, I believe due to a difference in + the way the targets are parsed and filtered (some ORFs that have since been removed from the annotations are removed). + I didn't investigate this closely, though. + role: experimental_condition + - name: profile_first_published + dtype: string + description: citation or reference indicating where this expression profile was first published + role: experimental_condition + - name: chase_notes + dtype: string + description: notes added during data curation and parsing +--- + +# mahendrawada_2025 +--- +license: mit +language: +- en +tags: +- biology +- genomics +- yeast +- transcription-factors +- gene-expression +- binding +- chec +- perturbation +- rnaseq +- nascent rnaseq +pretty_name: "Mahendrawada 2025 ChEC-seq and Nascent RNA-seq data" +size_categories: +- 100K- + unique identifier for a specific sample, which uniquely identifies one of the 178 TFs. + Across datasets in this repo, the a given sample_id identifies the same regulator. + - name: regulator_locus_tag + dtype: string + description: Systematic gene name (ORF identifier) of the transcription factor + - name: regulator_symbol + dtype: string + description: Standard gene symbol of the transcription factor + - name: target_locus_tag + dtype: string + description: Systematic gene name (ORF identifier) of the target gene + - name: target_symbol + dtype: string + description: Standard gene symbol of the target gene + - name: peak_score + dtype: float64 + description: ChEC signal around peak center (sum of ChEC signal from -150 to +150 bp from peak summit) normalized to Drosophila spike-in control + - name: processing_method + dtype: string + description: Method used for peak calling and quantification (original authors) + +- config_name: reprocessed_chec_seq + description: ChEC-seq transcription factor binding data reprocessed with updated peak calling methodology + dataset_type: annotated_features + data_files: + - split: train + path: chec_reprocessed_mahendrawada_2025.parquet + dataset_info: + features: + - name: sample_id + dtype: integer + description: >- + unique identifier for a specific sample, which uniquely identifies one of the 178 TFs. + Across datasets in this repo, the a given sample_id identifies the same regulator. + - name: regulator_locus_tag + dtype: string + description: Systematic gene name (ORF identifier) of the transcription factor + - name: regulator_symbol + dtype: string + description: Standard gene symbol of the transcription factor + - name: target_locus_tag + dtype: string + description: Systematic gene name (ORF identifier) of the target gene + - name: target_symbol + dtype: string + description: Standard gene symbol of the target gene + - name: enrichment + dtype: float64 + description: ratio of experimental insertions to background insertions + - name: poisson_pval + dtype: float64 + description: enrichment poisson pvalue + +- config_name: reprocessed_diffcontrol_5prime + description: Comparing two different sets of control replicates, m2025 from the Mahendrawada 2025 paper, and h2021 from a previous paper from the Hahn lab + dataset_type: annotated_features + metadata_fields: + - control_source + - condition + - regulator_locus_tag + experimental_conditions: + environmental_conditions: + # Mahendrawada et al 2025: "30 °C culture" + temperature_celsius: 30 + cultivation_method: unspecified + growth_phase_at_harvest: + # Mahendrawada et al 2025: "A600 of ~1.0" + od600: 1.0 + media: + # Mahendrawada et al 2025: "synthetic complete (SC) media" + name: synthetic_complete + carbon_source: unspecified + nitrogen_source: + - compound: yeast_nitrogen_base + # Mahendrawada et al 2025: 1.7 g/L (without ammonium sulfate or amino acids (BD Difco)) + concentration_percent: 0.17 + specifications: + - without_ammonium_sulfate + - without_amino_acids + - compound: ammonium_sulfate + # Mahendrawada et al 2025: 5 g/L + concentration_percent: 0.5 + - compound: amino_acid_dropout_mix + # Mahendrawada et al 2025: 0.6 g/L + concentration_percent: 0.06 + - compound: adenine_sulfate + # Mahendrawada et al 2025: 40 μg/ml = 0.04 g/L + concentration_percent: 0.004 + - compound: uracil + # Mahendrawada et al 2025: 2 μg/ml = 0.002 g/L + concentration_percent: 0.0002 + data_files: + - split: train + path: reprocess_diffcontrol_5prime.parquet + dataset_info: + features: + - name: control_source + dtype: string + description: Source identifier for the control dataset (m2025 or h2021) + - name: condition + dtype: string + description: Experimental condition. 'standard' is YPD. + - name: regulator_locus_tag + dtype: string + description: Systematic gene name (ORF identifier) of the transcription factor + - name: target_locus_tag + dtype: string + description: Systematic gene name (ORF identifier) of the target gene + - name: chr + dtype: string + description: Chromosome name of the promoter/target region + - name: start + dtype: int64 + description: Start coordinate of the promoter region + - name: end + dtype: int64 + description: End coordinate of the promoter region + - name: strand + dtype: string + description: Strand orientation (+ or -) of the promoter/target + - name: input_vs_target_log2_fold_change + dtype: float64 + description: Log2 fold change of TF-tagged sample vs control (from DESeq2) + - name: input_vs_target_p_value + dtype: float64 + description: P-value for differential enrichment (from DESeq2) + - name: input_vs_target_adj_p_value + dtype: float64 + description: Adjusted p-value (FDR-corrected) for differential enrichment (from DESeq2) + +- config_name: rna_seq + description: Nascent RNA-seq differential expression data following transcription factor depletion using 4TU metabolic labeling + dataset_type: annotated_features + metadata_fields: + - regulator_locus_tag + - regulator_symbol + data_files: + - split: train + path: rnaseq_mahendrawada_2025.parquet + dataset_info: + features: + - name: sample_id + dtype: integer + description: >- + unique identifier for a specific sample, which uniquely identifies one of the 178 TFs. + Across datasets in this repo, the a given sample_id identifies the same regulator. + - name: db_id + dtype: integer + description: >- + an old unique identifer, for use internally only. Deprecated and will be removed eventually. + Do not use in analysis. + - name: regulator_locus_tag + dtype: string + description: Systematic gene name (ORF identifier) of the depleted transcription factor + - name: regulator_symbol + dtype: string + description: Standard gene symbol of the depleted transcription factor + - name: target_locus_tag + dtype: string + description: Systematic gene name (ORF identifier) of the differentially expressed target gene + - name: target_symbol + dtype: string + description: Standard gene symbol of the differentially expressed target gene + - name: log2fc + dtype: float64 + description: Log2 fold change (IAA/DMSO) for significantly affected genes (DESeq2, padj <0.1, FC >= 1.3) +--- + +# rossi_2021 +--- +license: mit +tags: +- transcription-factor +- binding +- chipexo +- genomics +- biology +language: +- en +pretty_name: Rossi ChIP-exo 2021 +experimental_conditions: + environmental_conditions: + temperature_celsius: 25 + cultivation_method: unspecified + growth_phase_at_harvest: + phase: mid_log + od600: 0.8 + media: + name: yeast_peptone_dextrose + carbon_source: + - compound: D-glucose + concentration_percent: unspecified + nitrogen_source: + - compound: yeast_extract + concentration_percent: unspecified + - compound: peptone + concentration_percent: unspecified + + # Heat shock applied only to SAGA strains + # note that im not sure which strains this + # applies to -- it is a TODO to better + # document this + heat_shock: + induced: true + temperature_celsius: 37 + duration_minutes: 6 + pre_induction_temperature_celsius: 25 + method: equal_volume_medium_transfer +configs: +- config_name: metadata + description: Metadata describing the tagged regulator in each experiment + dataset_type: metadata + data_files: + - split: train + path: rossi_2021_metadata.parquet + dataset_info: + features: + - name: regulator_locus_tag + dtype: string + description: Systematic gene name (ORF identifier) of the transcription factor + - name: regulator_symbol + dtype: string + description: Standard gene symbol of the transcription factor + - name: run_accession + dtype: string + description: GEO run accession identifier for the sample + - name: yeastepigenome_id + dtype: string + description: Sample identifier used by yeastepigenome.org +- config_name: genome_map + description: "ChIP-exo 5' tag coverage data partitioned by sample accession" + dataset_type: genome_map + data_files: + - split: train + path: genome_map/*/*.parquet + dataset_info: + features: + - name: chr + dtype: string + description: Chromosome name (e.g., chrI, chrII, etc.) + - name: pos + dtype: int32 + description: "Genomic position of the 5' tag" + - name: pileup + dtype: int32 + description: "Depth of coverage (number of 5' tags) at this genomic position" +- config_name: rossi_annotated_features + description: ChIP-exo regulator-target binding features with peak statistics + dataset_type: annotated_features + default: true + metadata_fields: + - regulator_locus_tag + - regulator_symbol + - target_locus_tag + data_files: + - split: train + path: yeastepigenome_annotatedfeatures.parquet + dataset_info: + features: + - name: sample_id + dtype: int32 + description: >- + Unique identifier for each ChIP-exo experimental sample. + - name: pss_id + dtype: float64 + description: >- + Current brentlab promotersetsig table id. This will eventually be removed. + - name: binding_id + dtype: float64 + description: >- + Current brentlab binding table id. This will eventually be removed. + - name: yeastepigenome_id + dtype: float64 + description: >- + Unique identifier in the yeastepigenome database. + - name: regulator_locus_tag + dtype: string + description: >- + Systematic ORF name of the regulator. + role: regulator_identifier + - name: regulator_symbol + dtype: string + description: >- + Common gene name of the regulator. + role: regulator_identifier + - name: target_locus_tag + dtype: string + description: >- + The systematic ID of the feature to which the effect/pvalue is + assigned. See hf/BrentLab/yeast_genome_resources + role: target_identifier + - name: target_symbol + dtype: string + description: >- + The common name of the feature to which the effect/pvalue is + assigned. If there is no common name, the `target_locus_tag` is + used. + role: target_identifier + - name: n_sig_peaks + dtype: float64 + description: >- + Number of peaks in the promoter region of the the target gene + role: quantitative_measure + - name: max_fc + dtype: float64 + description: >- + If there are multiple peaks in the promoter region, then the maximum is + reported. Otherwise, it is the fold change of the single peak in the + promoter. + role: quantitative_measure + - name: min_pval + dtype: float64 + description: >- + The most significant p-value among peaks for this interaction. + role: quantitative_measure +- config_name: reprocess_annotatedfeatures + description: >- + Annotated features reprocessed with updated peak + calling methodology + dataset_type: annotated_features + data_files: + - split: train + path: reprocess_annotatedfeatures.parquet + dataset_info: + features: + - name: regulator_locus_tag + dtype: string + description: Systematic gene name (ORF identifier) of the transcription factor + - name: regulator_symbol + dtype: string + description: Standard gene symbol of the transcription factor + - name: target_locus_tag + dtype: string + description: Systematic gene name (ORF identifier) of the target gene + - name: target_symbol + dtype: string + description: Standard gene symbol of the target gene + - name: baseMean + dtype: float64 + description: Average of normalized count values, dividing by size factors, taken over all samples + - name: log2FoldChange + dtype: float64 + description: Log2 fold change between comparison and control groups + - name: lfcSE + dtype: float64 + description: Standard error estimate for the log2 fold change estimate + - name: stat + dtype: float64 + description: Value of the test statistic for the gene + - name: pvalue + dtype: float64 + description: P-value of the test for the gene + - name: padj + dtype: float64 + description: Adjusted p-value for multiple testing for the gene +- config_name: reprocess_annotatedfeatures_tagcounts + description: Another version of the reprocessed data, quantified similarly to Calling Cards + dataset_type: annotated_features + data_files: + - split: train + path: reprocess_annotatedfeatures_tagcounts.parquet + dataset_info: + features: + - name: regulator_locus_tag + dtype: string + description: Systematic gene name (ORF identifier) of the transcription factor + role: regulator_identifier + - name: target_locus_tag + dtype: string + description: Systematic gene name (ORF identifier) of the target gene + role: target_identifier + - name: rank + dtype: int64 + description: Rank (ties method min rank) of the peak based on pvalue with ties broken by enrichment. Largest rank is most significant. + - name: control_count + dtype: int64 + description: Number of tags in the control condition + - name: experimental_count + dtype: int64 + description: Number of tags in the experimental condition + - name: mu + dtype: float64 + description: Expected count under the null hypothesis (control_count + 1) * (experimental_total_tags / control_total_tags) + - name: enrichment + dtype: float64 + description: Enrichment ratio of experimental over control. (experimental_counts / experimental_total) / (control_counts + pseudocount) / control_total + role: quantitative_measure + - name: log2_enrichment + dtype: float64 + description: Log2-transformed enrichment ratio + role: quantitative_measure + - name: neg_log10_pvalue + dtype: float64 + description: Negative log10 of the p-value for binding significance + role: quantitative_measure + - name: neg_log10_qvalue + dtype: float64 + description: Negative log10 of the FDR-adjusted q-value + role: quantitative_measure +--- + +# yeast_genome_resources +--- +license: mit +pretty_name: BrentLab Yeast Genome Resources +language: + - en +dataset_info: + features: + - name: start + dtype: int32 + description: Start coordinate (1-based, **inclusive**) + - name: end + dtype: int32 + description: End coordinate (1-based, **inclusive**) + - name: strand + dtype: string + levels: + - + + - "-" + description: Strand of feature + - name: type + dtype: string + levels: + - gene + - ncRNA_gene + - tRNA_gene + - snoRNA_gene + - transposable_element_gene + - pseudogene + - telomerase_RNA_gene + - snRNA_gene + - rRNA_gene + - blocked_reading_frame + description: classification of feature + - name: locus_tag + dtype: string + description: Systematic ID of feature + - name: symbol + dtype: string + description: Common name of feature + - name: alias + dtype: string + description: Alternative names of feature, typically alternative symbols + - name: source + dtype: string + description: Annotation file version/origin of the feature + - name: note + dtype: string + description: Additional feature information, typically the description from the + SGD gff/gtf + partitioning: + keys: + - name: chr + dtype: string + levels: + - chrI + - chrII + - chrVII + - chrV + - chrIII + - chrIV + - chrVIII + - chrVI + - chrX + - chrIX + - chrXI + - chrXIV + - chrXII + - chrXIII + - chrXV + - chrXVI + - chrM +configs: + - config_name: features + default: true + data_files: + - split: train + path: + - features/*/part-0.parquet +--- diff --git a/tfbpapi/tests/datainfo/test_datacard.py b/tfbpapi/tests/datainfo/test_datacard.py index ce22578..098d27f 100644 --- a/tfbpapi/tests/datainfo/test_datacard.py +++ b/tfbpapi/tests/datainfo/test_datacard.py @@ -3,7 +3,6 @@ from unittest.mock import Mock, patch import pytest -from pydantic import ValidationError from tfbpapi.datainfo import DataCard from tfbpapi.datainfo.models import DatasetType diff --git a/tfbpapi/tests/datainfo/test_datacard_parsing.py b/tfbpapi/tests/datainfo/test_datacard_parsing.py new file mode 100644 index 0000000..c7a35d0 --- /dev/null +++ b/tfbpapi/tests/datainfo/test_datacard_parsing.py @@ -0,0 +1,169 @@ +"""Test script to verify datacard parsing with new environmental_conditions.""" + +import yaml + +from tfbpapi.datainfo.models import DatasetCard +from tfbpapi.tests.datainfo.example_datacards import ( + EXAMPLE_1_SIMPLE_TOPLEVEL, + EXAMPLE_2_COMPLEX_FIELD_DEFINITIONS, + EXAMPLE_3_PARTITIONED_WITH_METADATA, +) + + +def test_example_1(): + """Test parsing example 1: simple top-level conditions.""" + print("=" * 80) + print("Testing Example 1: Simple Top-Level Conditions") + print("=" * 80) + + # Extract YAML from markdown + yaml_content = EXAMPLE_1_SIMPLE_TOPLEVEL.split("---")[1] + data = yaml.safe_load(yaml_content) + + try: + card = DatasetCard(**data) + print("✓ Successfully parsed Example 1") + print(f" - Configs: {len(card.configs)}") + print( + " - Top-level experimental_conditions: " + f"{card.experimental_conditions is not None}" + ) + + if card.experimental_conditions: + env_cond = card.experimental_conditions.environmental_conditions + if env_cond: + print(f" - Temperature: {env_cond.temperature_celsius}°C") + print(f" - Cultivation: {env_cond.cultivation_method}") + if env_cond.media: + print(f" - Media: {env_cond.media.name}") + print(f" - Carbon sources: {len(env_cond.media.carbon_source)}") + print( + f" - Nitrogen sources: {len(env_cond.media.nitrogen_source)}" + ) + + # Check field-level definitions + config = card.configs[0] + for feature in config.dataset_info.features: + if feature.definitions: + print( + f" - Feature '{feature.name}' has " + f"{len(feature.definitions)} definitions" + ) + for def_name in feature.definitions.keys(): + print(f" - {def_name}") + + print() + return True + except Exception as e: + print(f"✗ Failed to parse Example 1: {e}") + import traceback + + traceback.print_exc() + print() + return False + + +def test_example_2(): + """Test parsing example 2: complex field-level definitions.""" + print("=" * 80) + print("Testing Example 2: Complex Field-Level Definitions") + print("=" * 80) + + yaml_content = EXAMPLE_2_COMPLEX_FIELD_DEFINITIONS.split("---")[1] + data = yaml.safe_load(yaml_content) + + try: + card = DatasetCard(**data) + print("✓ Successfully parsed Example 2") + print(f" - Configs: {len(card.configs)}") + print(f" - Strain information: {card.strain_information is not None}") + + # Check field-level definitions + config = card.configs[0] + for feature in config.dataset_info.features: + if feature.definitions: + print( + f" - Feature '{feature.name}' has " + f"{len(feature.definitions)} definitions:" + ) + for def_name, def_value in feature.definitions.items(): + print(f" - {def_name}") + if "environmental_conditions" in def_value: + env = def_value["environmental_conditions"] + if "temperature_celsius" in env: + print(f" Temperature: {env['temperature_celsius']}°C") + if "media" in env: + print(f" Media: {env['media']['name']}") + + print() + return True + except Exception as e: + print(f"✗ Failed to parse Example 2: {e}") + import traceback + + traceback.print_exc() + print() + return False + + +def test_example_3(): + """Test parsing example 3: partitioned with metadata.""" + print("=" * 80) + print("Testing Example 3: Partitioned with Metadata") + print("=" * 80) + + yaml_content = EXAMPLE_3_PARTITIONED_WITH_METADATA.split("---")[1] + data = yaml.safe_load(yaml_content) + + try: + card = DatasetCard(**data) + print("✓ Successfully parsed Example 3") + print(f" - Configs: {len(card.configs)}") + print( + " - Top-level experimental_conditions: " + f"{card.experimental_conditions is not None}" + ) + + if card.experimental_conditions: + env_cond = card.experimental_conditions.environmental_conditions + if env_cond and env_cond.media: + print(f" - Top-level media: {env_cond.media.name}") + + # Check config-level experimental_conditions + for config in card.configs: + if config.experimental_conditions: + print(f" - Config '{config.config_name}' has experimental_conditions") + env_cond = config.experimental_conditions.environmental_conditions + if env_cond and env_cond.media: + print(f" - Media: {env_cond.media.name}") + print(f" - Temperature: {env_cond.temperature_celsius}°C") + + print() + return True + except Exception as e: + print(f"✗ Failed to parse Example 3: {e}") + import traceback + + traceback.print_exc() + print() + return False + + +if __name__ == "__main__": + results = [] + + results.append(test_example_1()) + results.append(test_example_2()) + results.append(test_example_3()) + + print("=" * 80) + print("Summary") + print("=" * 80) + print(f"Passed: {sum(results)}/{len(results)}") + + if all(results): + print("\n✓ All tests passed!") + exit(0) + else: + print("\n✗ Some tests failed") + exit(1) diff --git a/tfbpapi/tests/datainfo/test_metadata_manager.py b/tfbpapi/tests/datainfo/test_metadata_manager.py new file mode 100644 index 0000000..887a599 --- /dev/null +++ b/tfbpapi/tests/datainfo/test_metadata_manager.py @@ -0,0 +1,319 @@ +"""Tests for MetadataManager.""" + +import pandas as pd +import pytest + +from tfbpapi.datainfo import DataCard, MetadataManager +from tfbpapi.datainfo.models import FieldRole + + +class TestMetadataManagerBasic: + """Basic tests for MetadataManager instantiation and structure.""" + + def test_instantiation(self): + """Test MetadataManager can be instantiated.""" + mgr = MetadataManager() + assert mgr is not None + assert hasattr(mgr, "_conn") + assert hasattr(mgr, "_registered_datasets") + assert hasattr(mgr, "_view_names") + + def test_instantiation_with_cache(self): + """Test MetadataManager with cache enabled.""" + from pathlib import Path + + cache_dir = Path("/tmp/metadata_cache") + mgr = MetadataManager(cache_dir=cache_dir, cache=True) + assert mgr._cache_dir == cache_dir + assert mgr._cache_enabled is True + + def test_get_active_configs_empty(self): + """Test get_active_configs with no registered datasets.""" + mgr = MetadataManager() + configs = mgr.get_active_configs() + assert configs == [] + + def test_get_summary_empty(self): + """Test get_summary with no registered datasets.""" + mgr = MetadataManager() + summary = mgr.get_summary() + assert isinstance(summary, pd.DataFrame) + assert len(summary) == 0 + + +class TestDataCardExtractMetadataSchema: + """Tests for DataCard.extract_metadata_schema method.""" + + def test_extract_metadata_schema_structure(self): + """Test that extract_metadata_schema returns correct structure.""" + # Note: This test uses a mock since we need a real datacard + # In actual testing, we'd use a real HuggingFace dataset + # For now, we just test the structure + + # Mock test - verify the method exists + from tfbpapi.datainfo.datacard import DataCard + + assert hasattr(DataCard, "extract_metadata_schema") + + +class TestMetadataManagerHelpers: + """Tests for MetadataManager helper methods.""" + + def test_sanitize_view_name(self): + """Test view name sanitization.""" + mgr = MetadataManager() + view_name = mgr._sanitize_view_name("BrentLab/dataset-name", "config_name") + assert view_name == "BrentLab_dataset_name_config_name_metadata" + assert " " not in view_name + assert "/" not in view_name + assert "-" not in view_name + + def test_format_compound_simple(self): + """Test formatting compound as simple string.""" + mgr = MetadataManager() + result = mgr._format_compound("carbon_source", "D-glucose") + assert result == "carbon_source:D-glucose" + + def test_format_compound_with_percent(self): + """Test formatting compound with concentration percent.""" + mgr = MetadataManager() + compound = {"name": "D-glucose", "concentration_percent": 2.0} + result = mgr._format_compound("carbon_source", compound) + assert result == "carbon_source:D-glucose@2.0%" + + def test_format_compound_with_grams(self): + """Test formatting compound with g/L concentration.""" + mgr = MetadataManager() + compound = {"name": "ammonium_sulfate", "concentration_g_per_l": 5.0} + result = mgr._format_compound("nitrogen_source", compound) + assert result == "nitrogen_source:ammonium_sulfate@5.0g/L" + + def test_flatten_condition_definition_empty(self): + """Test flattening empty definition.""" + mgr = MetadataManager() + result = mgr._flatten_condition_definition({}) + assert result["growth_media"] == "unspecified" + assert result["components"] == "" + + def test_flatten_condition_definition_with_media(self): + """Test flattening definition with media.""" + mgr = MetadataManager() + definition = { + "environmental_conditions": { + "media": { + "name": "YPD", + "carbon_source": [ + {"name": "D-glucose", "concentration_percent": 2.0} + ], + "nitrogen_source": ["yeast_extract", "peptone"], + } + } + } + result = mgr._flatten_condition_definition(definition) + assert result["growth_media"] == "YPD" + assert "carbon_source:D-glucose@2.0%" in result["components"] + assert "nitrogen_source:yeast_extract" in result["components"] + assert "nitrogen_source:peptone" in result["components"] + + +class TestComponentSeparators: + """Tests for separator conventions.""" + + def test_separator_constants(self): + """Test that separator constants are defined.""" + from tfbpapi.datainfo.metadata_manager import COMPONENT_SEPARATORS + + assert COMPONENT_SEPARATORS["type_value"] == ":" + assert COMPONENT_SEPARATORS["value_conc"] == "@" + assert COMPONENT_SEPARATORS["components"] == ";" + assert COMPONENT_SEPARATORS["types"] == "|" + + +class TestThreeLevelConditionHierarchy: + """Tests for three-level experimental condition hierarchy support.""" + + def test_flatten_experimental_conditions_empty(self): + """Test flattening empty ExperimentalConditions.""" + mgr = MetadataManager() + + # Create a simple mock object with no conditions + class MockExpConditions: + environmental_conditions = None + strain_background = None + + result = mgr._flatten_experimental_conditions(MockExpConditions()) + assert isinstance(result, dict) + assert len(result) == 0 + + def test_flatten_experimental_conditions_with_temperature(self): + """Test flattening conditions with temperature.""" + mgr = MetadataManager() + + class MockEnv: + temperature_celsius = 30 + cultivation_method = None + media = None + growth_phase = None + chemical_treatments = None + drug_treatments = None + heat_treatment = None + induction = None + + class MockExpConditions: + environmental_conditions = MockEnv() + strain_background = None + + result = mgr._flatten_experimental_conditions(MockExpConditions()) + assert result["temperature_celsius"] == 30 + + def test_flatten_experimental_conditions_with_cultivation_method(self): + """Test flattening conditions with cultivation method.""" + mgr = MetadataManager() + + class MockEnv: + temperature_celsius = None + cultivation_method = "chemostat" + media = None + growth_phase = None + chemical_treatments = None + drug_treatments = None + heat_treatment = None + induction = None + + class MockExpConditions: + environmental_conditions = MockEnv() + strain_background = None + + result = mgr._flatten_experimental_conditions(MockExpConditions()) + assert result["cultivation_method"] == "chemostat" + + def test_flatten_experimental_conditions_with_media(self): + """Test flattening conditions with media information.""" + mgr = MetadataManager() + + class MockCompound: + compound = "D-glucose" + concentration_percent = 1.0 + + def model_dump(self): + return { + "name": self.compound, + "concentration_percent": self.concentration_percent, + } + + class MockMedia: + name = "minimal" + carbon_source = [MockCompound()] + nitrogen_source = None + phosphate_source = None + additives = None + + class MockEnv: + temperature_celsius = None + cultivation_method = None + media = MockMedia() + growth_phase = None + chemical_treatments = None + drug_treatments = None + heat_treatment = None + induction = None + + class MockExpConditions: + environmental_conditions = MockEnv() + strain_background = None + + result = mgr._flatten_experimental_conditions(MockExpConditions()) + assert result["growth_media"] == "minimal" + assert "carbon_source:D-glucose@1.0%" in result["components"] + + def test_flatten_experimental_conditions_with_strain_background(self): + """Test flattening conditions with strain background.""" + mgr = MetadataManager() + + class MockExpConditions: + environmental_conditions = None + strain_background = "BY4741" + + result = mgr._flatten_experimental_conditions(MockExpConditions()) + assert result["strain_background"] == "BY4741" + + def test_datacard_extract_schema_includes_top_level_conditions(self): + """Test that extract_metadata_schema includes top_level_conditions.""" + # This test needs to use real datacards with experimental conditions + # For now, we verify the keys exist in the schema + from unittest.mock import MagicMock, Mock + + # Create a mock DataCard with experimental conditions + mock_card = Mock() + mock_card.repo_id = "test/repo" + + # Mock dataset card with experimental conditions + mock_dataset_card = Mock() + mock_dataset_card.experimental_conditions = Mock() # Non-None + mock_card.dataset_card = mock_dataset_card + + # Mock config with no config-level conditions + mock_config = Mock() + mock_config.experimental_conditions = None + mock_config.dataset_info = Mock() + mock_config.dataset_info.features = [] + + mock_card.get_config = Mock(return_value=mock_config) + + # Call the method via the actual DataCard class + from tfbpapi.datainfo.datacard import DataCard + + schema = DataCard.extract_metadata_schema(mock_card, "test_config") + + # Verify top_level_conditions is in schema + assert "top_level_conditions" in schema + assert "config_level_conditions" in schema + + def test_datacard_extract_schema_includes_config_level_conditions(self): + """Test that extract_metadata_schema includes config_level_conditions.""" + from unittest.mock import Mock + + # Create a mock DataCard + mock_card = Mock() + mock_card.repo_id = "test/repo" + + # Mock dataset card with NO repo-level conditions + mock_dataset_card = Mock() + mock_dataset_card.experimental_conditions = None + mock_card.dataset_card = mock_dataset_card + + # Mock config WITH config-level conditions + mock_config = Mock() + mock_config.experimental_conditions = Mock() # Non-None + mock_config.dataset_info = Mock() + mock_config.dataset_info.features = [] + + mock_card.get_config = Mock(return_value=mock_config) + + # Call the method + from tfbpapi.datainfo.datacard import DataCard + + schema = DataCard.extract_metadata_schema(mock_card, "test_config") + + # Verify config_level_conditions is populated + assert schema["config_level_conditions"] is not None + + +# Integration test placeholder +class TestMetadataManagerIntegration: + """Integration tests for MetadataManager (require real HF datasets).""" + + @pytest.mark.skip(reason="Requires real HuggingFace dataset access") + def test_register_real_dataset(self): + """Test registering a real HuggingFace dataset.""" + # This would test with a real dataset like: + # mgr = MetadataManager() + # mgr.register("BrentLab/some_real_dataset") + # assert len(mgr.get_active_configs()) > 0 + pass + + @pytest.mark.skip(reason="Requires real HuggingFace dataset access") + def test_query_across_datasets(self): + """Test querying across multiple datasets.""" + # This would test cross-dataset queries + pass diff --git a/tfbpapi/tests/datainfo/test_models.py b/tfbpapi/tests/datainfo/test_models.py index 618a392..8bcee9b 100644 --- a/tfbpapi/tests/datainfo/test_models.py +++ b/tfbpapi/tests/datainfo/test_models.py @@ -4,13 +4,20 @@ from pydantic import ValidationError from tfbpapi.datainfo.models import ( + ChemicalTreatmentInfo, + CompoundInfo, DataFileInfo, DatasetCard, DatasetConfig, DatasetInfo, DatasetType, + EnvironmentalConditions, + ExperimentalConditions, ExtractedMetadata, FeatureInfo, + GrowthPhaseInfo, + HeatTreatmentInfo, + MediaInfo, MetadataRelationship, PartitioningInfo, ) @@ -38,6 +45,328 @@ def test_invalid_dataset_type(self): DatasetType("invalid_type") +class TestCompoundInfo: + """Test CompoundInfo model.""" + + def test_compound_info_with_percentage(self): + """Test CompoundInfo with percentage concentration.""" + compound = CompoundInfo(compound="glucose", concentration_percent=2.0) + assert compound.compound == "glucose" + assert compound.concentration_percent == 2.0 + assert compound.concentration_g_per_l is None + assert compound.specifications is None + + def test_compound_info_with_g_per_l(self): + """Test CompoundInfo with g/L concentration.""" + compound = CompoundInfo(compound="dextrose", concentration_g_per_l=20.0) + assert compound.compound == "dextrose" + assert compound.concentration_g_per_l == 20.0 + assert compound.concentration_percent is None + + def test_compound_info_with_specifications(self): + """Test CompoundInfo with specifications.""" + compound = CompoundInfo( + compound="yeast_nitrogen_base", + concentration_g_per_l=6.7, + specifications=["without_amino_acids"], + ) + assert compound.compound == "yeast_nitrogen_base" + assert compound.specifications == ["without_amino_acids"] + + def test_compound_info_minimal(self): + """Test CompoundInfo with only required fields.""" + compound = CompoundInfo(compound="ethanol") + assert compound.compound == "ethanol" + assert compound.concentration_percent is None + assert compound.concentration_g_per_l is None + + +class TestMediaInfo: + """Test MediaInfo model.""" + + def test_media_info_minimal(self): + """Test MediaInfo with required fields only.""" + media = MediaInfo( + name="minimal", + carbon_source=[CompoundInfo(compound="glucose", concentration_percent=2.0)], + nitrogen_source=[ + CompoundInfo(compound="ammonium_sulfate", concentration_g_per_l=5.0) + ], + ) + assert media.name == "minimal" + assert len(media.carbon_source) == 1 + assert len(media.nitrogen_source) == 1 + assert media.phosphate_source is None + + def test_media_info_with_phosphate(self): + """Test MediaInfo with phosphate source.""" + media = MediaInfo( + name="synthetic_complete", + carbon_source=[CompoundInfo(compound="glucose", concentration_percent=2.0)], + nitrogen_source=[ + CompoundInfo(compound="yeast_nitrogen_base", concentration_g_per_l=6.7) + ], + phosphate_source=[ + CompoundInfo(compound="potassium_phosphate", concentration_g_per_l=1.0) + ], + ) + assert media.phosphate_source is not None + assert len(media.phosphate_source) == 1 + + def test_media_info_multiple_compounds(self): + """Test MediaInfo with multiple carbon/nitrogen sources.""" + media = MediaInfo( + name="YPD", + carbon_source=[ + CompoundInfo(compound="glucose", concentration_percent=2.0), + CompoundInfo(compound="glycerol", concentration_percent=3.0), + ], + nitrogen_source=[ + CompoundInfo(compound="yeast_extract", concentration_percent=1.0), + CompoundInfo(compound="peptone", concentration_percent=2.0), + ], + ) + assert len(media.carbon_source) == 2 + assert len(media.nitrogen_source) == 2 + + +class TestGrowthPhaseInfo: + """Test GrowthPhaseInfo model.""" + + def test_growth_phase_with_od600(self): + """Test GrowthPhaseInfo with OD600.""" + phase = GrowthPhaseInfo(od600=0.5, stage="mid_log_phase") + assert phase.od600 == 0.5 + assert phase.stage == "mid_log_phase" + + def test_growth_phase_od600_only(self): + """Test GrowthPhaseInfo with only OD600.""" + phase = GrowthPhaseInfo(od600=0.8) + assert phase.od600 == 0.8 + assert phase.stage is None + + def test_growth_phase_stage_only(self): + """Test GrowthPhaseInfo with only stage.""" + phase = GrowthPhaseInfo(stage="stationary_phase") + assert phase.stage == "stationary_phase" + assert phase.od600 is None + + def test_growth_phase_empty(self): + """Test GrowthPhaseInfo with no values.""" + phase = GrowthPhaseInfo() + assert phase.od600 is None + assert phase.stage is None + + +class TestChemicalTreatmentInfo: + """Test ChemicalTreatmentInfo model.""" + + def test_chemical_treatment_with_percent(self): + """Test ChemicalTreatmentInfo with percentage concentration.""" + treatment = ChemicalTreatmentInfo( + compound="ethanol", + concentration_percent=5.0, + duration_minutes=30, + ) + assert treatment.compound == "ethanol" + assert treatment.concentration_percent == 5.0 + assert treatment.duration_minutes == 30 + assert treatment.concentration_molar is None + + def test_chemical_treatment_with_molar(self): + """Test ChemicalTreatmentInfo with molar concentration.""" + treatment = ChemicalTreatmentInfo( + compound="rapamycin", + concentration_molar=0.0002, + duration_hours=2.0, + ) + assert treatment.compound == "rapamycin" + assert treatment.concentration_molar == 0.0002 + assert treatment.duration_hours == 2.0 + + def test_chemical_treatment_minimal(self): + """Test ChemicalTreatmentInfo with only compound.""" + treatment = ChemicalTreatmentInfo(compound="hydrogen_peroxide") + assert treatment.compound == "hydrogen_peroxide" + assert treatment.concentration_percent is None + assert treatment.duration_minutes is None + + +class TestHeatTreatmentInfo: + """Test HeatTreatmentInfo model.""" + + def test_heat_treatment_basic(self): + """Test HeatTreatmentInfo with required field.""" + treatment = HeatTreatmentInfo(duration_minutes=30) + assert treatment.duration_minutes == 30 + assert treatment.description is None + + def test_heat_treatment_with_description(self): + """Test HeatTreatmentInfo with description.""" + treatment = HeatTreatmentInfo( + duration_minutes=15, description="Heat shock at 37C" + ) + assert treatment.duration_minutes == 15 + assert treatment.description == "Heat shock at 37C" + + def test_heat_treatment_missing_duration(self): + """Test HeatTreatmentInfo requires duration_minutes.""" + with pytest.raises(ValidationError): + HeatTreatmentInfo(description="Some treatment") + + +class TestEnvironmentalConditions: + """Test EnvironmentalConditions model.""" + + def test_environmental_conditions_minimal(self): + """Test EnvironmentalConditions with no fields.""" + env = EnvironmentalConditions() + assert env.temperature_celsius is None + assert env.cultivation_method is None + assert env.growth_phase_at_harvest is None + assert env.media is None + assert env.chemical_treatment is None + assert env.heat_treatment is None + + def test_environmental_conditions_temperature(self): + """Test EnvironmentalConditions with temperature.""" + env = EnvironmentalConditions(temperature_celsius=30.0) + assert env.temperature_celsius == 30.0 + + def test_environmental_conditions_with_media(self): + """Test EnvironmentalConditions with media info.""" + media = MediaInfo( + name="YPD", + carbon_source=[CompoundInfo(compound="glucose", concentration_percent=2.0)], + nitrogen_source=[ + CompoundInfo(compound="peptone", concentration_percent=2.0) + ], + ) + env = EnvironmentalConditions( + temperature_celsius=30.0, + cultivation_method="batch_culture", + media=media, + ) + assert env.media is not None + assert env.media.name == "YPD" + + def test_environmental_conditions_with_growth_phase(self): + """Test EnvironmentalConditions with growth phase.""" + growth_phase = GrowthPhaseInfo(od600=0.5, stage="mid_log_phase") + env = EnvironmentalConditions(growth_phase_at_harvest=growth_phase) + assert env.growth_phase_at_harvest is not None + assert env.growth_phase_at_harvest.od600 == 0.5 + + def test_environmental_conditions_with_chemical_treatment(self): + """Test EnvironmentalConditions with chemical treatment.""" + treatment = ChemicalTreatmentInfo( + compound="rapamycin", + concentration_molar=0.0002, + duration_minutes=120, + ) + env = EnvironmentalConditions(chemical_treatment=treatment) + assert env.chemical_treatment is not None + assert env.chemical_treatment.compound == "rapamycin" + + def test_environmental_conditions_with_heat_treatment(self): + """Test EnvironmentalConditions with heat treatment.""" + treatment = HeatTreatmentInfo(duration_minutes=30, description="Heat shock") + env = EnvironmentalConditions(heat_treatment=treatment) + assert env.heat_treatment is not None + assert env.heat_treatment.duration_minutes == 30 + + def test_environmental_conditions_complete(self): + """Test EnvironmentalConditions with all fields.""" + media = MediaInfo( + name="minimal", + carbon_source=[CompoundInfo(compound="glucose", concentration_percent=2.0)], + nitrogen_source=[ + CompoundInfo(compound="ammonium_sulfate", concentration_g_per_l=5.0) + ], + ) + growth_phase = GrowthPhaseInfo(od600=0.6, stage="mid_log_phase") + chemical = ChemicalTreatmentInfo( + compound="rapamycin", concentration_molar=0.0002, duration_minutes=60 + ) + heat = HeatTreatmentInfo(duration_minutes=15, description="Brief heat shock") + + env = EnvironmentalConditions( + temperature_celsius=30.0, + cultivation_method="batch_culture", + growth_phase_at_harvest=growth_phase, + media=media, + chemical_treatment=chemical, + heat_treatment=heat, + ) + + assert env.temperature_celsius == 30.0 + assert env.cultivation_method == "batch_culture" + assert env.growth_phase_at_harvest.od600 == 0.6 + assert env.media.name == "minimal" + assert env.chemical_treatment.compound == "rapamycin" + assert env.heat_treatment.duration_minutes == 15 + + def test_environmental_conditions_extra_fields_allowed(self): + """Test that EnvironmentalConditions allows extra fields.""" + env = EnvironmentalConditions( + temperature_celsius=30.0, custom_field="custom_value" + ) + assert env.temperature_celsius == 30.0 + + +class TestExperimentalConditions: + """Test ExperimentalConditions model.""" + + def test_experimental_conditions_minimal(self): + """Test ExperimentalConditions with no fields.""" + exp = ExperimentalConditions() + assert exp.environmental_conditions is None + assert exp.strain_background is None + + def test_experimental_conditions_with_strain(self): + """Test ExperimentalConditions with strain background.""" + exp = ExperimentalConditions(strain_background="BY4741") + assert exp.strain_background == "BY4741" + + def test_experimental_conditions_with_environmental(self): + """Test ExperimentalConditions with environmental conditions.""" + env = EnvironmentalConditions( + temperature_celsius=30.0, cultivation_method="batch_culture" + ) + exp = ExperimentalConditions(environmental_conditions=env) + assert exp.environmental_conditions is not None + assert exp.environmental_conditions.temperature_celsius == 30.0 + + def test_experimental_conditions_complete(self): + """Test ExperimentalConditions with all fields.""" + env = EnvironmentalConditions( + temperature_celsius=30.0, + cultivation_method="batch_culture", + media=MediaInfo( + name="YPD", + carbon_source=[ + CompoundInfo(compound="glucose", concentration_percent=2.0) + ], + nitrogen_source=[ + CompoundInfo(compound="peptone", concentration_percent=2.0) + ], + ), + ) + exp = ExperimentalConditions( + environmental_conditions=env, strain_background="BY4741" + ) + assert exp.strain_background == "BY4741" + assert exp.environmental_conditions.temperature_celsius == 30.0 + assert exp.environmental_conditions.media.name == "YPD" + + def test_experimental_conditions_extra_fields_allowed(self): + """Test that ExperimentalConditions allows extra fields.""" + exp = ExperimentalConditions( + strain_background="BY4741", custom_metadata="custom_value" + ) + assert exp.strain_background == "BY4741" + + class TestFeatureInfo: """Test FeatureInfo model.""" @@ -244,7 +573,8 @@ def test_dataset_config_applies_to_validation_error( with pytest.raises( ValidationError, - match="applies_to field is only valid for metadata dataset types", + match="applies_to field is only valid for " + "metadata and qc_data dataset types", ): DatasetConfig( config_name="invalid_config", diff --git a/tfbpapi/tests/datainfo/test_real_datacards.py b/tfbpapi/tests/datainfo/test_real_datacards.py new file mode 100644 index 0000000..32bdaf6 --- /dev/null +++ b/tfbpapi/tests/datainfo/test_real_datacards.py @@ -0,0 +1,697 @@ +""" +Test real datacards from the HuggingFace collection. + +This test suite validates that all real datacards from the BrentLab collection parse +correctly with the updated models.py and specification. + +""" + +import warnings + +import pytest +import yaml + +from tfbpapi.datainfo.models import DatasetCard + +# Real datacard YAML strings from the collection +BARKAI_COMPENDIUM = """ +license: mit +language: +- en +tags: +- transcription-factor +- binding +- chec-seq +- genomics +- biology +pretty_name: Barkai ChEC-seq Compendium +size_categories: + - 100M 0 + + # Verify config has required fields + config = card.configs[0] + assert config.config_name is not None + assert config.dataset_type is not None + assert config.dataset_info is not None + assert config.dataset_info.features is not None + assert len(config.dataset_info.features) > 0 + + +def test_harbison_2004_condition_definitions(): + """Test that harbison_2004 field-level definitions parse correctly.""" + data = yaml.safe_load(HARBISON_2004) + card = DatasetCard(**data) + + # Find the config + config = card.configs[0] + assert config.config_name == "harbison_2004" + + # Find condition feature + condition_feature = next( + f for f in config.dataset_info.features if f.name == "condition" + ) + + # Should have definitions + assert condition_feature.definitions is not None + assert "YPD" in condition_feature.definitions + assert "Acid" in condition_feature.definitions + assert "BUT14" in condition_feature.definitions + + # YPD definition should have environmental conditions + ypd_def = condition_feature.definitions["YPD"] + assert "environmental_conditions" in ypd_def + + # Acid definition should have target_pH in chemical_treatment + acid_def = condition_feature.definitions["Acid"] + assert "environmental_conditions" in acid_def + assert "chemical_treatment" in acid_def["environmental_conditions"] + assert "target_pH" in acid_def["environmental_conditions"]["chemical_treatment"] + + # BUT14 should have media additives + but14_def = condition_feature.definitions["BUT14"] + assert "environmental_conditions" in but14_def + assert "media" in but14_def["environmental_conditions"] + assert "additives" in but14_def["environmental_conditions"]["media"] + + +def test_hughes_2006_induction(): + """Test that hughes_2006 induction field parses correctly.""" + data = yaml.safe_load(HUGHES_2006) + card = DatasetCard(**data) + + # Check experimental conditions + assert card.configs[0].experimental_conditions is not None + exp_conds = card.configs[0].experimental_conditions + assert exp_conds.environmental_conditions is not None + env_conds = exp_conds.environmental_conditions + + # Check induction field + assert env_conds.induction is not None + assert env_conds.induction.inducer is not None + assert env_conds.induction.inducer.compound == "D-galactose" + assert env_conds.induction.duration_hours == 3 + + +def test_kemmeren_2014_growth_phase(): + """Test that kemmeren_2014 growth phase with od600_tolerance parses correctly.""" + data = yaml.safe_load(KEMMEREN_2014) + card = DatasetCard(**data) + + # Check growth phase + exp_conds = card.experimental_conditions + assert exp_conds is not None + assert exp_conds.environmental_conditions is not None + env_conds = exp_conds.environmental_conditions + + assert env_conds.growth_phase_at_harvest is not None + growth_phase = env_conds.growth_phase_at_harvest + assert growth_phase.phase == "early_mid_log" + assert growth_phase.od600 == 0.6 + assert growth_phase.od600_tolerance == 0.1 + + +def test_hu_2007_strain_background_in_definitions(): + """Test that strain_background in field definitions parses correctly.""" + data = yaml.safe_load(HU_2007) + card = DatasetCard(**data) + + # Find heat_shock feature + config = card.configs[0] + heat_shock_feature = next( + f for f in config.dataset_info.features if f.name == "heat_shock" + ) + + # Check definitions + assert heat_shock_feature.definitions is not None + assert "true" in heat_shock_feature.definitions + + # Check strain_background in definition + true_def = heat_shock_feature.definitions["true"] + assert "strain_background" in true_def + + +def test_field_role_validation(): + """Test that FieldRole enum validation works correctly.""" + # This should parse successfully with valid roles + data = yaml.safe_load(CALLINGCARDS) + card = DatasetCard(**data) + + # Find a feature with a role + config = card.configs[0] + regulator_feature = next( + f for f in config.dataset_info.features if f.name == "regulator_locus_tag" + ) + + # Verify role is a FieldRole enum + from tfbpapi.datainfo.models import FieldRole + + assert regulator_feature.role == FieldRole.REGULATOR_IDENTIFIER + + +def test_concentration_fields(): + """Test that various concentration fields parse correctly.""" + data = yaml.safe_load(KEMMEREN_2014) + card = DatasetCard(**data) + + # Check media compounds + env_conds = card.experimental_conditions.environmental_conditions + assert env_conds.media is not None + + # Check carbon source + assert len(env_conds.media.carbon_source) > 0 + carbon = env_conds.media.carbon_source[0] + assert carbon.concentration_percent is not None + + # Check nitrogen source with specifications + assert len(env_conds.media.nitrogen_source) > 0 + nitrogen = env_conds.media.nitrogen_source[0] + assert nitrogen.specifications is not None + assert "without_amino_acids" in nitrogen.specifications + + +def test_extra_fields_do_not_raise_errors(): + """Test that extra fields are accepted (with warnings) but don't raise errors.""" + # All real datacards should parse without ValidationError + # even if they have extra fields + datacards = [ + BARKAI_COMPENDIUM, + CALLINGCARDS, + HARBISON_2004, + HU_2007, + HUGHES_2006, + KEMMEREN_2014, + MAHENDRAWADA_2025, + ROSSI_2021, + ] + + for datacard_yaml in datacards: + data = yaml.safe_load(datacard_yaml) + # This should not raise ValidationError + card = DatasetCard(**data) + assert card is not None + + +def test_empty_nitrogen_source_list(): + """Test that empty nitrogen_source lists are accepted.""" + data = yaml.safe_load(BARKAI_COMPENDIUM) + card = DatasetCard(**data) + + # Check that nitrogen_source is an empty list + env_conds = card.experimental_conditions.environmental_conditions + assert env_conds.media is not None + assert env_conds.media.nitrogen_source == [] + + +def test_media_additives(): + """Test that media additives parse correctly.""" + data = yaml.safe_load(HARBISON_2004) + card = DatasetCard(**data) + + # Find BUT14 condition definition + config = card.configs[0] + condition_feature = next( + f for f in config.dataset_info.features if f.name == "condition" + ) + but14_def = condition_feature.definitions["BUT14"] + + # Check additives + env_conds_dict = but14_def["environmental_conditions"] + media = env_conds_dict["media"] + assert "additives" in media + additives = media["additives"] + assert len(additives) > 0 + assert additives[0]["compound"] == "butanol" + assert additives[0]["concentration_percent"] == 1 + + +def test_strain_background_formats(): + """Test that strain_background accepts both string and dict formats.""" + # String format + data1 = yaml.safe_load(BARKAI_COMPENDIUM) + card1 = DatasetCard(**data1) + assert card1.experimental_conditions is not None + assert card1.experimental_conditions.strain_background == "BY4741" + + # String format in rossi + data2 = yaml.safe_load(ROSSI_2021) + card2 = DatasetCard(**data2) + assert card2.experimental_conditions is not None + assert card2.experimental_conditions.strain_background == "W303" + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) From 3ce7c81051336ffc3532267aaed22aa4d42cc882 Mon Sep 17 00:00:00 2001 From: chasem Date: Tue, 9 Dec 2025 13:26:48 -0600 Subject: [PATCH 38/49] rendered metadata explorer notebook --- .../metadata_explorer_tutorial.ipynb | 465 +++++++++++++++++- 1 file changed, 440 insertions(+), 25 deletions(-) diff --git a/docs/tutorials/metadata_explorer_tutorial.ipynb b/docs/tutorials/metadata_explorer_tutorial.ipynb index 0ba9cda..bcea184 100644 --- a/docs/tutorials/metadata_explorer_tutorial.ipynb +++ b/docs/tutorials/metadata_explorer_tutorial.ipynb @@ -61,10 +61,74 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, - "outputs": [], - "source": "# Load harbison_2004 datacard\nharbison_card = DataCard(repo_id=\"BrentLab/harbison_2004\")\n\n# Extract metadata schema for the harbison_2004 config\nharbison_schema = harbison_card.extract_metadata_schema(\"harbison_2004\")\n\nprint(\"Harbison 2004 Metadata Schema\")\nprint(\"=\" * 60)\nprint(f\"Regulator Fields: {harbison_schema['regulator_fields']}\")\nprint(f\"Target Fields: {harbison_schema['target_fields']}\")\nprint(f\"Condition Fields: {harbison_schema['condition_fields']}\")\n\n# Check for repo-level conditions\nif harbison_schema.get('top_level_conditions'):\n print(\"\\nRepo-Level Conditions (apply to ALL samples):\")\n top_cond = harbison_schema['top_level_conditions']\n if top_cond.strain_background:\n print(f\" - Strain background: {top_cond.strain_background}\")\n if top_cond.environmental_conditions:\n print(\" - Environmental conditions defined\")\nelse:\n print(\"\\nRepo-Level Conditions: None\")\n\n# Check for config-level conditions\nif harbison_schema.get('config_level_conditions'):\n print(\"\\nConfig-Level Conditions:\")\n print(\" (Defined for this config)\")\nelse:\n print(\"\\nConfig-Level Conditions: None\")\n\n# Show field-level condition definitions\nprint(f\"\\nField-Level Conditions (vary by sample):\")\nprint(f\" - condition: {len(harbison_schema['condition_definitions'].get('condition', {}))} conditions defined\")\n\n# Show first few condition definitions\nif 'condition' in harbison_schema['condition_definitions']:\n print(\"\\n Sample Conditions:\")\n for cond_name in list(harbison_schema['condition_definitions']['condition'].keys())[:3]:\n print(f\" - {cond_name}\")" + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Harbison 2004 Metadata Schema\n", + "============================================================\n", + "Regulator Fields: ['regulator_locus_tag', 'regulator_symbol']\n", + "Target Fields: ['target_locus_tag', 'target_symbol']\n", + "Condition Fields: ['condition']\n", + "\n", + "Repo-Level Conditions: None\n", + "\n", + "Config-Level Conditions: None\n", + "\n", + "Field-Level Conditions (vary by sample):\n", + " - condition: 14 conditions defined\n", + "\n", + " Sample Conditions:\n", + " - YPD\n", + " - SM\n", + " - RAPA\n" + ] + } + ], + "source": [ + "# Load harbison_2004 datacard\n", + "harbison_card = DataCard(repo_id=\"BrentLab/harbison_2004\")\n", + "\n", + "# Extract metadata schema for the harbison_2004 config\n", + "harbison_schema = harbison_card.extract_metadata_schema(\"harbison_2004\")\n", + "\n", + "print(\"Harbison 2004 Metadata Schema\")\n", + "print(\"=\" * 60)\n", + "print(f\"Regulator Fields: {harbison_schema['regulator_fields']}\")\n", + "print(f\"Target Fields: {harbison_schema['target_fields']}\")\n", + "print(f\"Condition Fields: {harbison_schema['condition_fields']}\")\n", + "\n", + "# Check for repo-level conditions\n", + "if harbison_schema.get('top_level_conditions'):\n", + " print(\"\\nRepo-Level Conditions (apply to ALL samples):\")\n", + " top_cond = harbison_schema['top_level_conditions']\n", + " if top_cond.strain_background:\n", + " print(f\" - Strain background: {top_cond.strain_background}\")\n", + " if top_cond.environmental_conditions:\n", + " print(\" - Environmental conditions defined\")\n", + "else:\n", + " print(\"\\nRepo-Level Conditions: None\")\n", + "\n", + "# Check for config-level conditions\n", + "if harbison_schema.get('config_level_conditions'):\n", + " print(\"\\nConfig-Level Conditions:\")\n", + " print(\" (Defined for this config)\")\n", + "else:\n", + " print(\"\\nConfig-Level Conditions: None\")\n", + "\n", + "# Show field-level condition definitions\n", + "print(f\"\\nField-Level Conditions (vary by sample):\")\n", + "print(f\" - condition: {len(harbison_schema['condition_definitions'].get('condition', {}))} conditions defined\")\n", + "\n", + "# Show first few condition definitions\n", + "if 'condition' in harbison_schema['condition_definitions']:\n", + " print(\"\\n Sample Conditions:\")\n", + " for cond_name in list(harbison_schema['condition_definitions']['condition'].keys())[:3]:\n", + " print(f\" - {cond_name}\")" + ] }, { "cell_type": "markdown", @@ -75,10 +139,78 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, - "outputs": [], - "source": "# Load hackett_2020 datacard\nhackett_card = DataCard(repo_id=\"BrentLab/hackett_2020\")\n\n# Extract metadata schema\nhackett_schema = hackett_card.extract_metadata_schema(\"hackett_2020\")\n\nprint(\"Hackett 2020 Metadata Schema\")\nprint(\"=\" * 60)\nprint(f\"Regulator Fields: {hackett_schema['regulator_fields']}\")\nprint(f\"Target Fields: {hackett_schema['target_fields']}\")\nprint(f\"Condition Fields: {hackett_schema['condition_fields']}\")\n\n# Show repo-level (top-level) experimental conditions\nif hackett_schema.get('top_level_conditions'):\n print(\"\\nRepo-Level Conditions (apply to ALL samples):\")\n top_cond = hackett_schema['top_level_conditions']\n \n # Check for structured environmental_conditions\n if top_cond.environmental_conditions:\n env = top_cond.environmental_conditions\n if env.temperature_celsius is not None:\n print(f\" - Temperature: {env.temperature_celsius} C\")\n if env.cultivation_method:\n print(f\" - Cultivation: {env.cultivation_method}\")\n if env.media:\n print(f\" - Media: {env.media.name}\")\n if env.media.carbon_source:\n for cs in env.media.carbon_source:\n print(f\" - Carbon source: {cs.compound} @ {cs.concentration_percent}%\")\n \n # Check for extra fields (alternate structure)\n if hasattr(top_cond, 'model_extra') and top_cond.model_extra:\n for key, value in top_cond.model_extra.items():\n print(f\" - {key}: {value}\")\n\n# Show config-level conditions\nif hackett_schema.get('config_level_conditions'):\n print(\"\\nConfig-Level Conditions:\")\n print(\" (Conditions defined for this config)\")\nelse:\n print(\"\\nConfig-Level Conditions: None\")\n\n# Show field-level condition definitions\nif hackett_schema['condition_definitions']:\n print(\"\\nField-Level Conditions (vary by sample):\")\n for field_name, definitions in hackett_schema['condition_definitions'].items():\n print(f\" - {field_name}: {len(definitions)} levels defined\")\n print(f\" Levels: {list(definitions.keys())}\")" + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hackett 2020 Metadata Schema\n", + "============================================================\n", + "Regulator Fields: ['regulator_locus_tag', 'regulator_symbol']\n", + "Target Fields: ['target_locus_tag', 'target_symbol']\n", + "Condition Fields: ['time', 'mechanism', 'restriction', 'date', 'strain']\n", + "\n", + "Repo-Level Conditions (apply to ALL samples):\n", + " - growth_medium: minimal\n", + " - temperature: 30C\n", + " - cultivation_method: chemostat\n", + "\n", + "Config-Level Conditions: None\n" + ] + } + ], + "source": [ + "# Load hackett_2020 datacard\n", + "hackett_card = DataCard(repo_id=\"BrentLab/hackett_2020\")\n", + "\n", + "# Extract metadata schema\n", + "hackett_schema = hackett_card.extract_metadata_schema(\"hackett_2020\")\n", + "\n", + "print(\"Hackett 2020 Metadata Schema\")\n", + "print(\"=\" * 60)\n", + "print(f\"Regulator Fields: {hackett_schema['regulator_fields']}\")\n", + "print(f\"Target Fields: {hackett_schema['target_fields']}\")\n", + "print(f\"Condition Fields: {hackett_schema['condition_fields']}\")\n", + "\n", + "# Show repo-level (top-level) experimental conditions\n", + "if hackett_schema.get('top_level_conditions'):\n", + " print(\"\\nRepo-Level Conditions (apply to ALL samples):\")\n", + " top_cond = hackett_schema['top_level_conditions']\n", + " \n", + " # Check for structured environmental_conditions\n", + " if top_cond.environmental_conditions:\n", + " env = top_cond.environmental_conditions\n", + " if env.temperature_celsius is not None:\n", + " print(f\" - Temperature: {env.temperature_celsius} C\")\n", + " if env.cultivation_method:\n", + " print(f\" - Cultivation: {env.cultivation_method}\")\n", + " if env.media:\n", + " print(f\" - Media: {env.media.name}\")\n", + " if env.media.carbon_source:\n", + " for cs in env.media.carbon_source:\n", + " print(f\" - Carbon source: {cs.compound} @ {cs.concentration_percent}%\")\n", + " \n", + " # Check for extra fields (alternate structure)\n", + " if hasattr(top_cond, 'model_extra') and top_cond.model_extra:\n", + " for key, value in top_cond.model_extra.items():\n", + " print(f\" - {key}: {value}\")\n", + "\n", + "# Show config-level conditions\n", + "if hackett_schema.get('config_level_conditions'):\n", + " print(\"\\nConfig-Level Conditions:\")\n", + " print(\" (Conditions defined for this config)\")\n", + "else:\n", + " print(\"\\nConfig-Level Conditions: None\")\n", + "\n", + "# Show field-level condition definitions\n", + "if hackett_schema['condition_definitions']:\n", + " print(\"\\nField-Level Conditions (vary by sample):\")\n", + " for field_name, definitions in hackett_schema['condition_definitions'].items():\n", + " print(f\" - {field_name}: {len(definitions)} levels defined\")\n", + " print(f\" Levels: {list(definitions.keys())}\")" + ] }, { "cell_type": "markdown", @@ -230,7 +362,7 @@ "type": "string" } ], - "ref": "e30d951e-7e84-475f-be44-50ac556c86b1", + "ref": "1c9696e3-e620-4f07-b246-226e4093863f", "rows": [ [ "0", @@ -316,10 +448,105 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, - "outputs": [], - "source": "print(\"Three-Level Experimental Condition Hierarchy\")\nprint(\"=\" * 80)\n\n# Show hackett_2020 as example\nprint(\"\\nExample: hackett_2020 dataset\")\nprint(\"-\" * 80)\n\n# 1. Repo-level (applies to ALL samples)\nprint(\"\\n[1] REPO-LEVEL (DatasetCard.experimental_conditions)\")\nprint(\" Applies to: ALL configs and ALL samples in the repository\")\nif hackett_schema.get('top_level_conditions'):\n top_cond = hackett_schema['top_level_conditions']\n \n # Check structured environmental_conditions\n if top_cond.environmental_conditions:\n env = top_cond.environmental_conditions\n print(f\" - temperature_celsius: {env.temperature_celsius}\")\n print(f\" - cultivation_method: {env.cultivation_method}\")\n print(f\" - media.name: {env.media.name}\")\n print(f\" - media.carbon_source: {env.media.carbon_source[0].compound} @ {env.media.carbon_source[0].concentration_percent}%\")\n \n # Check extra fields (alternate structure used by some datacards)\n if hasattr(top_cond, 'model_extra') and top_cond.model_extra:\n for key, value in top_cond.model_extra.items():\n print(f\" - {key}: {value}\")\n\n# 2. Config-level (applies to samples in this config)\nprint(\"\\n[2] CONFIG-LEVEL (DatasetConfig.experimental_conditions)\")\nprint(\" Applies to: All samples in the 'hackett_2020' config\")\nif hackett_schema.get('config_level_conditions'):\n print(\" - (Specific conditions defined)\")\nelse:\n print(\" - (None - all repo-level conditions inherited)\")\n\n# 3. Field-level (varies by sample)\nprint(\"\\n[3] FIELD-LEVEL (FeatureInfo.definitions)\")\nprint(\" Applies to: Individual samples based on field value\")\nprint(\" - restriction field: 3 levels (P, N, M)\")\nprint(\" - P: Phosphate limitation\")\nprint(\" - N: Nitrogen limitation\")\nprint(\" - M: Magnesium limitation\")\nprint(\" - time field: Various time points (30.0, 60.0, etc.)\")\n\nprint(\"\\n\" + \"=\" * 80)\nprint(\"KEY CONCEPT: Hierarchy Merging\")\nprint(\"-\" * 80)\nprint(\"When MetadataManager creates metadata tables, it merges all three levels:\")\nprint(\" - Repo-level conditions -> columns for ALL samples (temperature: 30C, etc.)\")\nprint(\" - Config-level conditions -> columns for config samples (override repo-level if present)\")\nprint(\" - Field-level conditions -> vary by sample (restriction: P/N/M)\")\nprint(\"\\nPrecedence: Field-level > Config-level > Repo-level\")\nprint(\"\\nResult: Each sample gets columns from all applicable levels!\")" + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Three-Level Experimental Condition Hierarchy\n", + "================================================================================\n", + "\n", + "Example: hackett_2020 dataset\n", + "--------------------------------------------------------------------------------\n", + "\n", + "[1] REPO-LEVEL (DatasetCard.experimental_conditions)\n", + " Applies to: ALL configs and ALL samples in the repository\n", + " - growth_medium: minimal\n", + " - temperature: 30C\n", + " - cultivation_method: chemostat\n", + "\n", + "[2] CONFIG-LEVEL (DatasetConfig.experimental_conditions)\n", + " Applies to: All samples in the 'hackett_2020' config\n", + " - (None - all repo-level conditions inherited)\n", + "\n", + "[3] FIELD-LEVEL (FeatureInfo.definitions)\n", + " Applies to: Individual samples based on field value\n", + " - restriction field: 3 levels (P, N, M)\n", + " - P: Phosphate limitation\n", + " - N: Nitrogen limitation\n", + " - M: Magnesium limitation\n", + " - time field: Various time points (30.0, 60.0, etc.)\n", + "\n", + "================================================================================\n", + "KEY CONCEPT: Hierarchy Merging\n", + "--------------------------------------------------------------------------------\n", + "When MetadataManager creates metadata tables, it merges all three levels:\n", + " - Repo-level conditions -> columns for ALL samples (temperature: 30C, etc.)\n", + " - Config-level conditions -> columns for config samples (override repo-level if present)\n", + " - Field-level conditions -> vary by sample (restriction: P/N/M)\n", + "\n", + "Precedence: Field-level > Config-level > Repo-level\n", + "\n", + "Result: Each sample gets columns from all applicable levels!\n" + ] + } + ], + "source": [ + "print(\"Three-Level Experimental Condition Hierarchy\")\n", + "print(\"=\" * 80)\n", + "\n", + "# Show hackett_2020 as example\n", + "print(\"\\nExample: hackett_2020 dataset\")\n", + "print(\"-\" * 80)\n", + "\n", + "# 1. Repo-level (applies to ALL samples)\n", + "print(\"\\n[1] REPO-LEVEL (DatasetCard.experimental_conditions)\")\n", + "print(\" Applies to: ALL configs and ALL samples in the repository\")\n", + "if hackett_schema.get('top_level_conditions'):\n", + " top_cond = hackett_schema['top_level_conditions']\n", + " \n", + " # Check structured environmental_conditions\n", + " if top_cond.environmental_conditions:\n", + " env = top_cond.environmental_conditions\n", + " print(f\" - temperature_celsius: {env.temperature_celsius}\")\n", + " print(f\" - cultivation_method: {env.cultivation_method}\")\n", + " print(f\" - media.name: {env.media.name}\")\n", + " print(f\" - media.carbon_source: {env.media.carbon_source[0].compound} @ {env.media.carbon_source[0].concentration_percent}%\")\n", + " \n", + " # Check extra fields (alternate structure used by some datacards)\n", + " if hasattr(top_cond, 'model_extra') and top_cond.model_extra:\n", + " for key, value in top_cond.model_extra.items():\n", + " print(f\" - {key}: {value}\")\n", + "\n", + "# 2. Config-level (applies to samples in this config)\n", + "print(\"\\n[2] CONFIG-LEVEL (DatasetConfig.experimental_conditions)\")\n", + "print(\" Applies to: All samples in the 'hackett_2020' config\")\n", + "if hackett_schema.get('config_level_conditions'):\n", + " print(\" - (Specific conditions defined)\")\n", + "else:\n", + " print(\" - (None - all repo-level conditions inherited)\")\n", + "\n", + "# 3. Field-level (varies by sample)\n", + "print(\"\\n[3] FIELD-LEVEL (FeatureInfo.definitions)\")\n", + "print(\" Applies to: Individual samples based on field value\")\n", + "print(\" - restriction field: 3 levels (P, N, M)\")\n", + "print(\" - P: Phosphate limitation\")\n", + "print(\" - N: Nitrogen limitation\")\n", + "print(\" - M: Magnesium limitation\")\n", + "print(\" - time field: Various time points (30.0, 60.0, etc.)\")\n", + "\n", + "print(\"\\n\" + \"=\" * 80)\n", + "print(\"KEY CONCEPT: Hierarchy Merging\")\n", + "print(\"-\" * 80)\n", + "print(\"When MetadataManager creates metadata tables, it merges all three levels:\")\n", + "print(\" - Repo-level conditions -> columns for ALL samples (temperature: 30C, etc.)\")\n", + "print(\" - Config-level conditions -> columns for config samples (override repo-level if present)\")\n", + "print(\" - Field-level conditions -> vary by sample (restriction: P/N/M)\")\n", + "print(\"\\nPrecedence: Field-level > Config-level > Repo-level\")\n", + "print(\"\\nResult: Each sample gets columns from all applicable levels!\")" + ] }, { "cell_type": "markdown", @@ -343,10 +570,39 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, - "outputs": [], - "source": "# Example query (will work once parquet loading is implemented)\n# \n# regulators_query = \"\"\"\n# SELECT DISTINCT \n# dataset,\n# config_name,\n# regulator_symbol,\n# regulator_locus_tag\n# FROM unified_metadata\n# WHERE regulator_symbol != 'unspecified'\n# ORDER BY dataset, regulator_symbol\n# \"\"\"\n# \n# regulators_df = mgr.query(regulators_query)\n# print(f\"\\nFound {len(regulators_df)} unique regulators across datasets\")\n# display(regulators_df.head(10))\n\nprint(\"WARNING: Parquet file loading not yet implemented\")\nprint(\" This query will work once HuggingFace parquet integration is added\")" + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "WARNING: Parquet file loading not yet implemented\n", + " This query will work once HuggingFace parquet integration is added\n" + ] + } + ], + "source": [ + "# Example query (will work once parquet loading is implemented)\n", + "# \n", + "# regulators_query = \"\"\"\n", + "# SELECT DISTINCT \n", + "# dataset,\n", + "# config_name,\n", + "# regulator_symbol,\n", + "# regulator_locus_tag\n", + "# FROM unified_metadata\n", + "# WHERE regulator_symbol != 'unspecified'\n", + "# ORDER BY dataset, regulator_symbol\n", + "# \"\"\"\n", + "# \n", + "# regulators_df = mgr.query(regulators_query)\n", + "# print(f\"\\nFound {len(regulators_df)} unique regulators across datasets\")\n", + "# display(regulators_df.head(10))\n", + "\n", + "print(\"WARNING: Parquet file loading not yet implemented\")\n", + "print(\" This query will work once HuggingFace parquet integration is added\")" + ] }, { "cell_type": "markdown", @@ -357,10 +613,40 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": {}, - "outputs": [], - "source": "# Example: Find GLN3 samples across both datasets\n#\n# gln3_query = \"\"\"\n# SELECT \n# dataset,\n# sample_id,\n# regulator_symbol,\n# condition,\n# time,\n# restriction,\n# growth_media,\n# components\n# FROM unified_metadata\n# WHERE regulator_symbol = 'GLN3'\n# \"\"\"\n# \n# gln3_samples = mgr.query(gln3_query)\n# print(f\"\\nGLN3 samples: {len(gln3_samples)}\")\n# display(gln3_samples)\n\nprint(\"WARNING: Parquet file loading not yet implemented\")" + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "WARNING: Parquet file loading not yet implemented\n" + ] + } + ], + "source": [ + "# Example: Find GLN3 samples across both datasets\n", + "#\n", + "# gln3_query = \"\"\"\n", + "# SELECT \n", + "# dataset,\n", + "# sample_id,\n", + "# regulator_symbol,\n", + "# condition,\n", + "# time,\n", + "# restriction,\n", + "# growth_media,\n", + "# components\n", + "# FROM unified_metadata\n", + "# WHERE regulator_symbol = 'GLN3'\n", + "# \"\"\"\n", + "# \n", + "# gln3_samples = mgr.query(gln3_query)\n", + "# print(f\"\\nGLN3 samples: {len(gln3_samples)}\")\n", + "# display(gln3_samples)\n", + "\n", + "print(\"WARNING: Parquet file loading not yet implemented\")" + ] }, { "cell_type": "markdown", @@ -371,10 +657,38 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "metadata": {}, - "outputs": [], - "source": "# Example: Find samples with D-glucose at 2%\n#\n# glucose_query = \"\"\"\n# SELECT \n# dataset,\n# COUNT(*) as sample_count,\n# growth_media\n# FROM unified_metadata\n# WHERE components LIKE '%carbon_source:D-glucose@2%'\n# GROUP BY dataset, growth_media\n# \"\"\"\n# \n# glucose_samples = mgr.query(glucose_query)\n# print(\"\\nSamples with D-glucose at 2%:\")\n# display(glucose_samples)\n\nprint(\"WARNING: Parquet file loading not yet implemented\")\nprint(\" This demonstrates searching by specific media components with concentrations\")" + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "WARNING: Parquet file loading not yet implemented\n", + " This demonstrates searching by specific media components with concentrations\n" + ] + } + ], + "source": [ + "# Example: Find samples with D-glucose at 2%\n", + "#\n", + "# glucose_query = \"\"\"\n", + "# SELECT \n", + "# dataset,\n", + "# COUNT(*) as sample_count,\n", + "# growth_media\n", + "# FROM unified_metadata\n", + "# WHERE components LIKE '%carbon_source:D-glucose@2%'\n", + "# GROUP BY dataset, growth_media\n", + "# \"\"\"\n", + "# \n", + "# glucose_samples = mgr.query(glucose_query)\n", + "# print(\"\\nSamples with D-glucose at 2%:\")\n", + "# display(glucose_samples)\n", + "\n", + "print(\"WARNING: Parquet file loading not yet implemented\")\n", + "print(\" This demonstrates searching by specific media components with concentrations\")" + ] }, { "cell_type": "markdown", @@ -394,10 +708,50 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "metadata": {}, - "outputs": [], - "source": "from tfbpapi.datainfo.metadata_manager import COMPONENT_SEPARATORS\n\nprint(\"\\nComponent Separator Conventions\")\nprint(\"=\" * 60)\nfor key, sep in COMPONENT_SEPARATORS.items():\n print(f\" {key:20s} '{sep}'\")\n \nprint(\"\\nUsage Examples:\")\nprint(\" growth_media: 'YPD' (simple name)\")\nprint(\" components: 'carbon_source:D-glucose@2%|nitrogen_source:yeast_extract@1%;peptone@2%'\")\nprint(\"\\n Breakdown:\")\nprint(\" - ':' separates type from value (carbon_source:D-glucose)\")\nprint(\" - '@' separates value from concentration (D-glucose@2%)\")\nprint(\" - ';' separates multiple compounds of same type (yeast_extract@1%;peptone@2%)\")\nprint(\" - '|' separates different component types (carbon | nitrogen)\")" + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Component Separator Conventions\n", + "============================================================\n", + " type_value ':'\n", + " value_conc '@'\n", + " components ';'\n", + " types '|'\n", + "\n", + "Usage Examples:\n", + " growth_media: 'YPD' (simple name)\n", + " components: 'carbon_source:D-glucose@2%|nitrogen_source:yeast_extract@1%;peptone@2%'\n", + "\n", + " Breakdown:\n", + " - ':' separates type from value (carbon_source:D-glucose)\n", + " - '@' separates value from concentration (D-glucose@2%)\n", + " - ';' separates multiple compounds of same type (yeast_extract@1%;peptone@2%)\n", + " - '|' separates different component types (carbon | nitrogen)\n" + ] + } + ], + "source": [ + "from tfbpapi.datainfo.metadata_manager import COMPONENT_SEPARATORS\n", + "\n", + "print(\"\\nComponent Separator Conventions\")\n", + "print(\"=\" * 60)\n", + "for key, sep in COMPONENT_SEPARATORS.items():\n", + " print(f\" {key:20s} '{sep}'\")\n", + " \n", + "print(\"\\nUsage Examples:\")\n", + "print(\" growth_media: 'YPD' (simple name)\")\n", + "print(\" components: 'carbon_source:D-glucose@2%|nitrogen_source:yeast_extract@1%;peptone@2%'\")\n", + "print(\"\\n Breakdown:\")\n", + "print(\" - ':' separates type from value (carbon_source:D-glucose)\")\n", + "print(\" - '@' separates value from concentration (D-glucose@2%)\")\n", + "print(\" - ';' separates multiple compounds of same type (yeast_extract@1%;peptone@2%)\")\n", + "print(\" - '|' separates different component types (carbon | nitrogen)\")" + ] }, { "cell_type": "markdown", @@ -554,7 +908,46 @@ { "cell_type": "markdown", "metadata": {}, - "source": "## Summary\n\nThis tutorial demonstrated:\n\n- **Metadata schema extraction** - Using `DataCard.extract_metadata_schema()` to understand field roles\n\n- **Three-level condition hierarchy** - Repo-level, config-level, and field-level experimental conditions\n\n- **Cross-dataset registration** - Registering multiple datasets in `MetadataManager`\n\n- **Condition flattening** - How definitions are flattened into searchable fields with known separators\n\n- **Schema heterogeneity** - How different metadata schemas are aligned by role with \"unspecified\" defaults\n\n- **Future: SQL queries** - Once parquet loading is implemented, full cross-dataset SQL queries will work\n\n### Key Takeaways\n\n1. **Role-based alignment**: Fields are grouped by semantic role (regulator_identifier, experimental_condition), not by name\n2. **Three-level hierarchy**: Conditions can be specified at repo-level (all samples), config-level (config samples), or field-level (individual samples)\n3. **Hierarchy merging**: MetadataManager merges all three levels with precedence: field > config > repo\n4. **Structured separators**: Media components use `:`, `@`, `;`, `|` for predictable parsing\n5. **Low memory**: DuckDB temp views mean no data loading until you query\n6. **Session-only default**: No caching by default (set `cache=True` if needed)\n7. **Dataset + config uniqueness**: Samples identified by (dataset, config_name, sample_id) tuple\n\n### Understanding the Condition Hierarchy\n\nThe three-level hierarchy allows flexible experimental condition specification:\n\n- **Repo-level**: Common conditions shared by all experiments (e.g., standard temperature, base strain)\n- **Config-level**: Conditions specific to a dataset configuration (e.g., growth phase for a time course)\n- **Field-level**: Sample-specific conditions that vary within an experiment (e.g., different media, treatments)\n\nThis design enables:\n- Efficient specification without repetition\n- Clear inheritance and overriding semantics\n- Consistent querying across heterogeneous datasets" + "source": [ + "## Summary\n", + "\n", + "This tutorial demonstrated:\n", + "\n", + "- **Metadata schema extraction** - Using `DataCard.extract_metadata_schema()` to understand field roles\n", + "\n", + "- **Three-level condition hierarchy** - Repo-level, config-level, and field-level experimental conditions\n", + "\n", + "- **Cross-dataset registration** - Registering multiple datasets in `MetadataManager`\n", + "\n", + "- **Condition flattening** - How definitions are flattened into searchable fields with known separators\n", + "\n", + "- **Schema heterogeneity** - How different metadata schemas are aligned by role with \"unspecified\" defaults\n", + "\n", + "- **Future: SQL queries** - Once parquet loading is implemented, full cross-dataset SQL queries will work\n", + "\n", + "### Key Takeaways\n", + "\n", + "1. **Role-based alignment**: Fields are grouped by semantic role (regulator_identifier, experimental_condition), not by name\n", + "2. **Three-level hierarchy**: Conditions can be specified at repo-level (all samples), config-level (config samples), or field-level (individual samples)\n", + "3. **Hierarchy merging**: MetadataManager merges all three levels with precedence: field > config > repo\n", + "4. **Structured separators**: Media components use `:`, `@`, `;`, `|` for predictable parsing\n", + "5. **Low memory**: DuckDB temp views mean no data loading until you query\n", + "6. **Session-only default**: No caching by default (set `cache=True` if needed)\n", + "7. **Dataset + config uniqueness**: Samples identified by (dataset, config_name, sample_id) tuple\n", + "\n", + "### Understanding the Condition Hierarchy\n", + "\n", + "The three-level hierarchy allows flexible experimental condition specification:\n", + "\n", + "- **Repo-level**: Common conditions shared by all experiments (e.g., standard temperature, base strain)\n", + "- **Config-level**: Conditions specific to a dataset configuration (e.g., growth phase for a time course)\n", + "- **Field-level**: Sample-specific conditions that vary within an experiment (e.g., different media, treatments)\n", + "\n", + "This design enables:\n", + "- Efficient specification without repetition\n", + "- Clear inheritance and overriding semantics\n", + "- Consistent querying across heterogeneous datasets" + ] }, { "cell_type": "markdown", @@ -592,7 +985,29 @@ { "cell_type": "markdown", "metadata": {}, - "source": "## Summary\n\nThis tutorial demonstrated:\n\n- **Metadata schema extraction** - Using `DataCard.extract_metadata_schema()` to understand field roles\n\n- **Cross-dataset registration** - Registering multiple datasets in `MetadataManager`\n\n- **Condition flattening** - How definitions are flattened into searchable fields with known separators\n\n- **Schema heterogeneity** - How different metadata schemas are aligned by role with \"unspecified\" defaults\n\n- **Future: SQL queries** - Once parquet loading is implemented, full cross-dataset SQL queries will work\n\n### Key Takeaways\n\n1. **Role-based alignment**: Fields are grouped by semantic role (regulator_identifier, experimental_condition), not by name\n2. **Structured separators**: Media components use `:`, `@`, `;`, `|` for predictable parsing\n3. **Low memory**: DuckDB temp views mean no data loading until you query\n4. **Session-only default**: No caching by default (set `cache=True` if needed)\n5. **Dataset + config uniqueness**: Samples identified by (dataset, config_name, sample_id) tuple" + "source": [ + "## Summary\n", + "\n", + "This tutorial demonstrated:\n", + "\n", + "- **Metadata schema extraction** - Using `DataCard.extract_metadata_schema()` to understand field roles\n", + "\n", + "- **Cross-dataset registration** - Registering multiple datasets in `MetadataManager`\n", + "\n", + "- **Condition flattening** - How definitions are flattened into searchable fields with known separators\n", + "\n", + "- **Schema heterogeneity** - How different metadata schemas are aligned by role with \"unspecified\" defaults\n", + "\n", + "- **Future: SQL queries** - Once parquet loading is implemented, full cross-dataset SQL queries will work\n", + "\n", + "### Key Takeaways\n", + "\n", + "1. **Role-based alignment**: Fields are grouped by semantic role (regulator_identifier, experimental_condition), not by name\n", + "2. **Structured separators**: Media components use `:`, `@`, `;`, `|` for predictable parsing\n", + "3. **Low memory**: DuckDB temp views mean no data loading until you query\n", + "4. **Session-only default**: No caching by default (set `cache=True` if needed)\n", + "5. **Dataset + config uniqueness**: Samples identified by (dataset, config_name, sample_id) tuple" + ] } ], "metadata": { @@ -616,4 +1031,4 @@ }, "nbformat": 4, "nbformat_minor": 4 -} \ No newline at end of file +} From 7c8eb72889b079637349e41bd54b9ef4df287c79 Mon Sep 17 00:00:00 2001 From: chasem Date: Tue, 9 Dec 2025 13:51:50 -0600 Subject: [PATCH 39/49] continuing to refine metadata manager --- .../metadata_explorer_tutorial.ipynb | 22 +++++++++++++------ tfbpapi/datainfo/models.py | 18 ++++++++------- .../huggingface_collection_datacards.txt | 13 +++++++++++ 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/docs/tutorials/metadata_explorer_tutorial.ipynb b/docs/tutorials/metadata_explorer_tutorial.ipynb index bcea184..adf8e35 100644 --- a/docs/tutorials/metadata_explorer_tutorial.ipynb +++ b/docs/tutorials/metadata_explorer_tutorial.ipynb @@ -153,11 +153,18 @@ "Condition Fields: ['time', 'mechanism', 'restriction', 'date', 'strain']\n", "\n", "Repo-Level Conditions (apply to ALL samples):\n", - " - growth_medium: minimal\n", - " - temperature: 30C\n", - " - cultivation_method: chemostat\n", + " - Temperature: 30.0 C\n", + " - Cultivation: chemostat\n", + " - Media: minimal\n", + " - Carbon source: D-glucose @ 1.0%\n", + "\n", + "Config-Level Conditions: None\n", "\n", - "Config-Level Conditions: None\n" + "Field-Level Conditions (vary by sample):\n", + " - mechanism: 2 levels defined\n", + " Levels: ['GEV', 'ZEV']\n", + " - restriction: 3 levels defined\n", + " Levels: ['P', 'N', 'M']\n" ] } ], @@ -362,7 +369,7 @@ "type": "string" } ], - "ref": "1c9696e3-e620-4f07-b246-226e4093863f", + "ref": "70ec99fc-d265-426c-84c6-9039ccaa0cda", "rows": [ [ "0", @@ -463,9 +470,10 @@ "\n", "[1] REPO-LEVEL (DatasetCard.experimental_conditions)\n", " Applies to: ALL configs and ALL samples in the repository\n", - " - growth_medium: minimal\n", - " - temperature: 30C\n", + " - temperature_celsius: 30.0\n", " - cultivation_method: chemostat\n", + " - media.name: minimal\n", + " - media.carbon_source: D-glucose @ 1.0%\n", "\n", "[2] CONFIG-LEVEL (DatasetConfig.experimental_conditions)\n", " Applies to: All samples in the 'hackett_2020' config\n", diff --git a/tfbpapi/datainfo/models.py b/tfbpapi/datainfo/models.py index ce5441f..0e76fc0 100644 --- a/tfbpapi/datainfo/models.py +++ b/tfbpapi/datainfo/models.py @@ -78,13 +78,14 @@ class MediaInfo(BaseModel): name: str = Field( ..., - description="Canonical or descriptive media name (minimal, synthetic_complete, YPD, etc.)", + description="Canonical or descriptive media name " + "(minimal, synthetic_complete, YPD, etc.)", ) - carbon_source: list[CompoundInfo] = Field( - ..., description="Carbon source compounds and concentrations" + carbon_source: list[CompoundInfo] | None = Field( + default=None, description="Carbon source compounds and concentrations" ) - nitrogen_source: list[CompoundInfo] = Field( - ..., description="Nitrogen source compounds and concentrations" + nitrogen_source: list[CompoundInfo] | None = Field( + default=None, description="Nitrogen source compounds and concentrations" ) phosphate_source: list[CompoundInfo] | None = Field( default=None, description="Phosphate source compounds and concentrations" @@ -99,7 +100,7 @@ class MediaInfo(BaseModel): def validate_compound_list(cls, v): """Validate compound lists and handle 'unspecified' strings.""" if v is None: - return [] + return None if isinstance(v, str): if v == "unspecified": warnings.warn( @@ -107,7 +108,7 @@ def validate_compound_list(cls, v): "Should be null/omitted or a structured list.", UserWarning, ) - return [] + return None # Try to parse as single compound return [{"compound": v}] return v @@ -153,7 +154,8 @@ def check_stage_phase_consistency(self): """Ensure stage and phase are consistent if both provided.""" if self.stage and self.phase and self.stage != self.phase: raise ValueError( - f"Inconsistent growth phase: stage='{self.stage}' vs phase='{self.phase}'" + "Inconsistent growth phase: " + f"stage='{self.stage}' vs phase='{self.phase}'" ) return self diff --git a/tfbpapi/tests/datainfo/huggingface_collection_datacards.txt b/tfbpapi/tests/datainfo/huggingface_collection_datacards.txt index 2d4d780..d5d3394 100644 --- a/tfbpapi/tests/datainfo/huggingface_collection_datacards.txt +++ b/tfbpapi/tests/datainfo/huggingface_collection_datacards.txt @@ -443,6 +443,19 @@ configs: names: ["GEV", "ZEV"] description: Synthetic TF induction system (GEV or ZEV) role: experimental_condition + definitions: + GEV: + perturbation_method: + type: inducible_overexpression + system: GEV + inducer: beta-estradiol + description: "Galactose-inducible estrogen receptor-VP16 fusion system" + ZEV: + perturbation_method: + type: inducible_overexpression + system: ZEV + inducer: beta-estradiol + description: "Z3 (synthetic zinc finger)-estrogen receptor-VP16 fusion system" - name: restriction dtype: class_label: From 235dc50a21eb70b23c5d00907736d3fb91088254 Mon Sep 17 00:00:00 2001 From: chasem Date: Tue, 9 Dec 2025 14:04:52 -0600 Subject: [PATCH 40/49] revising how cond cols are displayed --- .../metadata_explorer_tutorial.ipynb | 81 ++++++++----------- tfbpapi/datainfo/datacard.py | 63 +++++++++++++-- 2 files changed, 90 insertions(+), 54 deletions(-) diff --git a/docs/tutorials/metadata_explorer_tutorial.ipynb b/docs/tutorials/metadata_explorer_tutorial.ipynb index adf8e35..0d4104a 100644 --- a/docs/tutorials/metadata_explorer_tutorial.ipynb +++ b/docs/tutorials/metadata_explorer_tutorial.ipynb @@ -26,18 +26,9 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 14, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chase/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", - " from .autonotebook import tqdm as notebook_tqdm\n" - ] - } - ], + "outputs": [], "source": [ "import pandas as pd\n", "from tfbpapi.datainfo import DataCard, MetadataManager" @@ -61,7 +52,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 15, "metadata": {}, "outputs": [ { @@ -139,7 +130,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -150,7 +141,6 @@ "============================================================\n", "Regulator Fields: ['regulator_locus_tag', 'regulator_symbol']\n", "Target Fields: ['target_locus_tag', 'target_symbol']\n", - "Condition Fields: ['time', 'mechanism', 'restriction', 'date', 'strain']\n", "\n", "Repo-Level Conditions (apply to ALL samples):\n", " - Temperature: 30.0 C\n", @@ -158,13 +148,12 @@ " - Media: minimal\n", " - Carbon source: D-glucose @ 1.0%\n", "\n", - "Config-Level Conditions: None\n", - "\n", - "Field-Level Conditions (vary by sample):\n", - " - mechanism: 2 levels defined\n", - " Levels: ['GEV', 'ZEV']\n", - " - restriction: 3 levels defined\n", - " Levels: ['P', 'N', 'M']\n" + "Experimental Condition Fields:\n", + " - time: values from data\n", + " - mechanism: 2 defined levels\n", + " - restriction: 3 defined levels\n", + " - date: values from data\n", + " - strain: values from data\n" ] } ], @@ -179,13 +168,12 @@ "print(\"=\" * 60)\n", "print(f\"Regulator Fields: {hackett_schema['regulator_fields']}\")\n", "print(f\"Target Fields: {hackett_schema['target_fields']}\")\n", - "print(f\"Condition Fields: {hackett_schema['condition_fields']}\")\n", "\n", "# Show repo-level (top-level) experimental conditions\n", "if hackett_schema.get('top_level_conditions'):\n", " print(\"\\nRepo-Level Conditions (apply to ALL samples):\")\n", " top_cond = hackett_schema['top_level_conditions']\n", - " \n", + "\n", " # Check for structured environmental_conditions\n", " if top_cond.environmental_conditions:\n", " env = top_cond.environmental_conditions\n", @@ -198,25 +186,22 @@ " if env.media.carbon_source:\n", " for cs in env.media.carbon_source:\n", " print(f\" - Carbon source: {cs.compound} @ {cs.concentration_percent}%\")\n", - " \n", + "\n", " # Check for extra fields (alternate structure)\n", " if hasattr(top_cond, 'model_extra') and top_cond.model_extra:\n", " for key, value in top_cond.model_extra.items():\n", " print(f\" - {key}: {value}\")\n", "\n", - "# Show config-level conditions\n", - "if hackett_schema.get('config_level_conditions'):\n", - " print(\"\\nConfig-Level Conditions:\")\n", - " print(\" (Conditions defined for this config)\")\n", - "else:\n", - " print(\"\\nConfig-Level Conditions: None\")\n", - "\n", - "# Show field-level condition definitions\n", - "if hackett_schema['condition_definitions']:\n", - " print(\"\\nField-Level Conditions (vary by sample):\")\n", - " for field_name, definitions in hackett_schema['condition_definitions'].items():\n", - " print(f\" - {field_name}: {len(definitions)} levels defined\")\n", - " print(f\" Levels: {list(definitions.keys())}\")" + "# Show all experimental condition fields\n", + "if hackett_schema['condition_fields']:\n", + " print(\"\\nExperimental Condition Fields:\")\n", + " for field_name in hackett_schema['condition_fields']:\n", + " # Check if field has definitions (structured factor levels)\n", + " if field_name in hackett_schema['condition_definitions']:\n", + " num_levels = len(hackett_schema['condition_definitions'][field_name])\n", + " print(f\" - {field_name}: {num_levels} defined levels\")\n", + " else:\n", + " print(f\" - {field_name}: values from data\")" ] }, { @@ -255,7 +240,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 17, "metadata": {}, "outputs": [ { @@ -292,7 +277,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 18, "metadata": {}, "outputs": [ { @@ -333,7 +318,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 19, "metadata": {}, "outputs": [ { @@ -369,7 +354,7 @@ "type": "string" } ], - "ref": "70ec99fc-d265-426c-84c6-9039ccaa0cda", + "ref": "963ab794-8ad8-4c6c-967b-17439c5c0921", "rows": [ [ "0", @@ -455,7 +440,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 20, "metadata": {}, "outputs": [ { @@ -578,7 +563,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 21, "metadata": {}, "outputs": [ { @@ -621,7 +606,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 22, "metadata": {}, "outputs": [ { @@ -665,7 +650,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 23, "metadata": {}, "outputs": [ { @@ -716,7 +701,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 24, "metadata": {}, "outputs": [ { @@ -772,7 +757,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 25, "metadata": {}, "outputs": [ { @@ -845,7 +830,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 26, "metadata": {}, "outputs": [ { diff --git a/tfbpapi/datainfo/datacard.py b/tfbpapi/datainfo/datacard.py index 279b548..9b60c22 100644 --- a/tfbpapi/datainfo/datacard.py +++ b/tfbpapi/datainfo/datacard.py @@ -1,7 +1,7 @@ """DataCard class for easy exploration of HuggingFace dataset metadata.""" import logging -from typing import Any, Dict, List, Optional, Set, Union +from typing import Any from pydantic import ValidationError @@ -83,8 +83,10 @@ def _load_and_validate_card(self) -> None: if "dtype" in field_path and error_type == "string_type": error_details.append( - f"Field '{field_path}': Expected a simple data type string (like 'string', 'int64', 'float64') " - f"but got a complex structure. This might be a categorical field with class labels. " + f"Field '{field_path}': Expected a simple data type " + "string (like 'string', 'int64', 'float64') " + "but got a complex structure. This might be a categorical " + "field with class labels. " f"Actual value: {input_value}" ) else: @@ -185,11 +187,13 @@ def _extract_field_values(self, config: DatasetConfig, field_name: str) -> set[s values.update(partition_values) # For embedded metadata fields, we would need to query the actual data - # This is a placeholder - in practice, you might use the HF datasets server API + # This is a placeholder - in practice, you might use the + # HF datasets server API if config.metadata_fields and field_name in config.metadata_fields: # Placeholder for actual data extraction self.logger.debug( - f"Would extract embedded metadata for {field_name} in {config.config_name}" + "Would extract embedded metadata for " + f"{field_name} in {config.config_name}" ) except Exception as e: @@ -253,7 +257,8 @@ def get_metadata_relationships( ) ) - # Check for embedded metadata (always runs regardless of explicit relationships) + # Check for embedded metadata (always runs regardless of + # explicit relationships) if data_config.metadata_fields: relationships.append( MetadataRelationship( @@ -378,6 +383,52 @@ def extract_metadata_schema(self, config_name: str) -> dict[str, Any]: return schema + def get_condition_levels( + self, config_name: str, field_name: str + ) -> dict[str, Any] | list[str]: + """ + Get factor levels for an experimental condition field. + + Returns definitions if available (structured dict with descriptions), otherwise + queries distinct values from the parquet file. + + :param config_name: Configuration name + :param field_name: Experimental condition field name + :return: Dict of definitions if available, otherwise list of distinct values + :raises DataCardError: If config or field not found, or field is not an + experimental condition + + """ + config = self.get_config(config_name) + if not config: + raise DataCardError(f"Configuration '{config_name}' not found") + + # Find the feature and verify it's an experimental condition + feature = None + for f in config.dataset_info.features: + if f.name == field_name: + feature = f + break + + if not feature: + raise DataCardError( + f"Field '{field_name}' not found in config '{config_name}'" + ) + + if feature.role != FieldRole.EXPERIMENTAL_CONDITION: + raise DataCardError( + f"Field '{field_name}' is not an experimental condition " + f"(role={feature.role})" + ) + + # If field has definitions, return those + if feature.definitions: + return feature.definitions + + # Otherwise, query distinct values from parquet file + values = self.get_field_values(config_name, field_name) + return sorted(list(values)) + def summary(self) -> str: """Get a human-readable summary of the dataset.""" card = self.dataset_card From cb165e70647880812cb963a9957af75d3b95652d Mon Sep 17 00:00:00 2001 From: chasem Date: Thu, 11 Dec 2025 14:45:18 -0600 Subject: [PATCH 41/49] tmp --- docs/brentlab_yeastresources_collection.md | 358 ++++++ docs/huggingface_datacard.md | 486 +------ docs/tutorials/datacard_tutorial.ipynb | 766 +++++------ .../metadata_explorer_tutorial.ipynb | 1027 --------------- .../tutorials/metadata_manager_tutorial.ipynb | 586 +++++++++ docs/tutorials/sample_manager_tutorial.ipynb | 603 +++++++++ tfbpapi/datainfo/datacard.py | 457 ++++++- tfbpapi/datainfo/metadata_manager.py | 763 +++-------- tfbpapi/datainfo/models.py | 450 +------ tfbpapi/datainfo/sample_manager.py | 774 +++++++++++ .../huggingface_collection_datacards.txt | 843 ++++++------ tfbpapi/tests/datainfo/test_datacard.py | 158 +++ .../tests/datainfo/test_metadata_manager.py | 92 +- tfbpapi/tests/datainfo/test_models.py | 1140 +++++++---------- tfbpapi/tests/datainfo/test_real_datacards.py | 175 +-- 15 files changed, 4470 insertions(+), 4208 deletions(-) create mode 100644 docs/brentlab_yeastresources_collection.md delete mode 100644 docs/tutorials/metadata_explorer_tutorial.ipynb create mode 100644 docs/tutorials/metadata_manager_tutorial.ipynb create mode 100644 docs/tutorials/sample_manager_tutorial.ipynb create mode 100644 tfbpapi/datainfo/sample_manager.py diff --git a/docs/brentlab_yeastresources_collection.md b/docs/brentlab_yeastresources_collection.md new file mode 100644 index 0000000..29d3561 --- /dev/null +++ b/docs/brentlab_yeastresources_collection.md @@ -0,0 +1,358 @@ +# BrentLab Yeast Resources Collection + +This document describes the BrentLab yeast resources collection on HuggingFace as an example implementation of the [datacard specifications](huggingface_datacard.md). This collection demonstrates best practices for organizing transcription factor binding and perturbation datasets for *Saccharomyces cerevisiae*. + +## Collection Overview + +The BrentLab yeast resources collection contains 10 datasets related to yeast transcription factor binding and gene expression regulation: + +1. **barkai_compendium** - ChEC-seq binding data across multiple GEO series +2. **callingcards** - Calling Cards transposon-based binding data +3. **hackett_2020** - TF overexpression with nutrient limitation +4. **harbison_2004** - ChIP-chip binding across 14 environmental conditions +5. **hu_2007_reimand_2010** - TF knockout expression data +6. **hughes_2006** - TF perturbation screen (overexpression and knockout) +7. **kemmeren_2014** - TF deletion expression profiling +8. **mahendrawada_2025** - ChEC-seq and nascent RNA-seq data +9. **rossi_2021** - ChIP-exo binding data +10. **yeast_genome_resources** - Reference genomic features + +## Standardized Media Names + +The collection uses standardized media names to facilitate cross-dataset queries. When specifying media in datacards, use these canonical names: + +### Rich Media + +- **YPD** (Yeast extract Peptone Dextrose) + - Carbon source: 2% D-glucose + - Nitrogen sources: 1% yeast extract, 2% peptone + - Standard rich medium for yeast growth + +- **yeast_extract_peptone** + - Base medium without specified carbon source + - Used with galactose (YPGal) or raffinose (YPRaff) + +### Minimal/Defined Media + +- **minimal** or **minimal_glucose** + - Minimal defined medium with glucose as carbon source + - Nitrogen source varies by experiment + +- **synthetic_complete** or **synthetic_complete_dextrose** + - Defined medium with complete amino acid supplementation + - Carbon source: typically 2% D-glucose + - Nitrogen source: yeast nitrogen base + amino acid dropout mix + +- **synthetic_complete_minus_X** + - Synthetic complete medium lacking specific nutrient(s) + - Examples: `synthetic_complete_minus_thiamine`, `synthetic_complete_minus_phosphate` + - Used for nutrient deprivation experiments + +- **selective_medium** + - Defined medium for plasmid selection + - Specific composition varies by selection markers + +## Standardized Strain Backgrounds + +The collection primarily uses these strain backgrounds: + +- **BY4741** - MATa his3Δ1 leu2Δ0 met15Δ0 ura3Δ0 + - Used in: hu_2007_reimand_2010, kemmeren_2014 + +- **W303** - Common alternative strain background + - Used in: harbison_2004 (derivative Z1256) + +- **S288C** - Reference genome strain + - Used in: Various datasets + +Strain background can be specified as a string or detailed object: + +```yaml +# Simple string +experimental_conditions: + strain_background: BY4741 + +# Detailed specification +experimental_conditions: + strain_background: + genotype: BY4741 + mating_type: MATa + markers: + - his3Δ1 + - leu2Δ0 + - met15Δ0 + - ura3Δ0 + source: Open_Biosystems + description: Knockout strains for nonessential transcription factors +``` + +## Standard Experimental Conditions + +### Growth Temperature + +Standard growth temperature across the collection is **30°C** unless otherwise noted. + +Exceptions: +- **rossi_2021**: 25°C baseline with 37°C heat shock for some samples +- **hu_2007_reimand_2010**: Heat shock at 39°C for heat shock response TFs +- **callingcards**: the experiments are performed at room temperature (~22-25°C) + +### Growth Phase + +Common growth phase specifications: + +These labels are taken from the original publications. In some cases the OD600 +is noted + +- **early_log_phase** +- **mid_log_phase** +- **late_log_phase** +- **stationary_phase** - eg barkai_compendium, which are allowed to grow overnight. The + cells are harvested at a very high density (OD600 4.0). + +Example: +```yaml +experimental_conditions: + growth_phase_at_harvest: + stage: mid_log_phase + od600: 0.6 + od600_tolerance: 0.1 +``` + +### Cultivation Methods + +Standard cultivation methods used: + +- **liquid_culture** - Standard batch culture in flasks +- **batch** - Batch culture +- **plate** - Growth on agar plates +- **chemostat** - Continuous culture (hackett_2020) + +## Concentration Specifications + +**Always use `concentration_percent`** for all concentration specifications. +Convert other units to percentage: + +- **mg/ml to percent**: divide by 10 (e.g., 5 mg/ml = 0.5%) +- **g/L to percent**: divide by 10 (e.g., 6.71 g/L = 0.671%) +- **Molar to percent**: convert using molecular weight + - Example: 100 nM rapamycin = 9.142e-6% + +### Examples from the Collection + +```yaml +# Yeast nitrogen base: 6.71 g/L = 0.671% +- compound: yeast_nitrogen_base + concentration_percent: 0.671 + +# Alpha factor: 5 mg/ml = 0.5% +- compound: alpha_factor_pheromone + concentration_percent: 0.5 + +# Rapamycin: 100 nM = 9.142e-6% +chemical_treatment: + compound: rapamycin + concentration_percent: 9.142e-6 +``` + +## Field Naming Conventions + +The collection follows these field naming conventions: + +### Gene/Feature Identifiers + +- **regulator_locus_tag**: Systematic ID of regulatory factor (e.g., "YJR060W") +- **regulator_symbol**: Common name of regulatory factor (e.g., "CBF1") +- **target_locus_tag**: Systematic ID of target gene +- **target_symbol**: Common name of target gene + +All locus tags and symbols join to **yeast_genome_resources** dataset. + +### Quantitative Measurements Examples + +Common measurement field names: + +- **effect**, **log2fc**, **log2_ratio** - Log fold change measurements +- **pvalue**, **pval**, **p_value** - Statistical significance +- **padj**, **adj_p_value** - FDR-adjusted p-values +- **binding_score**, **peak_score** - Binding strength metrics +- **enrichment** - Enrichment ratios + +### Experimental Metadata Examples + +- **sample_id** - Unique sample identifier (integer) +- **db_id** - Legacy database identifier (deprecated, do not use) +- **batch** - Experimental batch identifier +- **replicate** - Biological replicate number +- **time** - Timepoint in timecourse experiments + +## Dataset Type Usage Examples + +### genomic_features + +**yeast_genome_resources** provides reference annotations: +- Gene coordinates and strand information +- Systematic IDs (locus_tag) and common names (symbol) +- Feature types (gene, ncRNA_gene, tRNA_gene, etc.) + +Used for joining regulator/target identifiers across all other datasets. + +### annotated_features + +Most common dataset type in the collection. Examples: + +- **hackett_2020**: TF overexpression with timecourse measurements +- **harbison_2004**: ChIP-chip binding with condition field definitions +- **kemmeren_2014**: TF deletion expression data +- **mahendrawada_2025**: ChEC-seq binding scores + +Typical structure: regulator × target × measurements, with optional condition fields. + +### genome_map + +Position-level data, typically partitioned by sample or accession: + +- **barkai_compendium**: ChEC-seq pileup data partitioned by Series/Accession +- **rossi_2021**: ChIP-exo 5' tag coverage partitioned by sample +- **callingcards**: Transposon insertion density partitioned by batch + +### metadata + +Separate metadata configs or embedded metadata via `metadata_fields`: + +**Separate config example** (barkai_compendium): +```yaml +- config_name: GSE178430_metadata + dataset_type: metadata + applies_to: ["genomic_coverage"] +``` + +**Embedded metadata example** (harbison_2004): +```yaml +- config_name: harbison_2004 + dataset_type: annotated_features + metadata_fields: ["regulator_locus_tag", "regulator_symbol", "condition"] +``` + +## Categorical Condition Definitions + +Many datasets define categorical experimental conditions using the `definitions` field. + +### harbison_2004 Environmental Conditions + +14 conditions with detailed specifications: +- **YPD** (rich media baseline) +- **SM** (amino acid starvation) +- **RAPA** (rapamycin treatment) +- **H2O2Hi**, **H2O2Lo** (oxidative stress) +- **HEAT** (heat shock) +- **GAL**, **RAFF** (alternative carbon sources) +- And 6 more... + +Each condition definition includes media composition, temperature, growth phase, and treatments. + +### hackett_2020 Nutrient Limitations + +```yaml +restriction: + definitions: + P: # Phosphate limitation + media: + phosphate_source: + - compound: potassium_phosphate_monobasic + concentration_percent: 0.002 + N: # Nitrogen limitation + media: + nitrogen_source: + - compound: ammonium_sulfate + concentration_percent: 0.004 + M: # Undefined limitation + description: "Not defined in the paper" +``` + +### hu_2007_reimand_2010 Treatment Conditions + +```yaml +heat_shock: + definitions: + true: + temperature_celsius: 39 + duration_minutes: 15 + false: + description: Standard growth conditions at 30°C +``` + +## Partitioning Strategies + +Large genome_map datasets use partitioning: + +**barkai_compendium** - Two-level partitioning: +```yaml +partitioning: + partition_by: ["Series", "Accession"] + path_template: "genome_map/*/*/part-0.parquet" +``` + +**callingcards** - Batch partitioning: +```yaml +partitioning: + enabled: true + partition_by: ["batch"] + path_template: "genome_map/batch={batch}/*.parquet" +``` + +## Collection-Wide Best Practices + +### 1. Omit unspecified fields with a comment + +`tfbpapi` will handle adding "unspecified" to fields which are not common across +datasets. + +```yaml +# CORRECT +experimental_conditions: + temperature_celsius: 30 + # cultivation_method is note noted in the paper and is omitted + +# INCORRECT +experimental_conditions: + temperature_celsius: unspecified +``` + +### 2. Document Source Publications + +If the original paper used something like g/L, then convert that to +`concentration_percent` and add a comment with the original value and units. + +```yaml +carbon_source: + - compound: D-glucose + # Saldanha et al 2004: 10 g/L + concentration_percent: 1 +``` + +### 3. Use Standard Field Roles + +Apply semantic roles consistently: +- `regulator_identifier` - for regulator fields +- `target_identifier` - for target fields +- `quantitative_measure` - for measurements +- `experimental_condition` - for condition fields +- `genomic_coordinate` - for positional data + +### 4. Provide sample_id + +All annotated_features datasets should include `sample_id` to uniquely identify experimental samples. This enables cross-dataset joining and metadata management. + +### 5. Specify metadata_fields or applies_to + +For datasets with metadata, either: +- Use `metadata_fields` to extract from the data itself, OR +- Create separate metadata config with `applies_to` field + +### 6. Use Consistent Gene Identifiers + +All regulator/target identifiers must be joinable to **yeast_genome_resources**: +- Use current systematic IDs (ORF names) +- Include both locus_tag and symbol fields +- Mark with appropriate roles diff --git a/docs/huggingface_datacard.md b/docs/huggingface_datacard.md index de8dd41..5cb30d7 100644 --- a/docs/huggingface_datacard.md +++ b/docs/huggingface_datacard.md @@ -78,22 +78,25 @@ Experimental conditions can be specified in three ways: 3. **Field-level** with `role: experimental_condition` ([feature-roles](#feature-roles)): For per-sample or per-measurement variation in experimental conditions stored as data columns. This is specified in the - `dataset_info.features` ([feature-definitions](#feature-definitions)) section of a config. + `dataset_info.features` ([feature-definitions](#feature-definitions)) + section of a config. `experimental_condition` fields which are categorical can are + specifically defined in [categorical fields with value definitions](#categorical-fields-with-value-definitions). + +The priority of experimental conditions is: + +field-level > config-level > top-level **Example of all three methods:** ```yaml -# Top-level experimental conditions (apply to all configs) +# Top-level experimental conditions (apply to all [datasets](#dataset) in the repo) experimental_conditions: - environmental_conditions: - temperature_celsius: 30 + temperature_celsius: 30 configs: -# The overexpression_data dataset has an additional experimental condition that is -# specific to this dataset and applied to all samples (strain_background) in addition -# to a field (mechanism) that varies per sample and is identified by the -# role experimental_condition. - config_name: overexpression_data description: TF overexpression perturbation data dataset_type: annotated_features + # The overexpression_data [dataset](#dataset) has an additional experimental + # condition that is specific to this dataset experimental_conditions: strain_background: "BY4741" data_files: @@ -109,359 +112,26 @@ configs: dtype: string description: Induction mechanism (GEV or ZEV) role: experimental_condition + definitions: + GEV: + perturbation_method: + type: inducible_overexpression + system: GEV + inducer: beta-estradiol + description: "Galactose-inducible estrogen receptor-VP16 fusion system" + ZEV: + perturbation_method: + type: inducible_overexpression + system: ZEV + inducer: beta-estradiol + description: >- + "Z3 (synthetic zinc finger)-estrogen receptor-VP16 fusion system" - name: log2_ratio dtype: float description: Log2 fold change role: quantitative_measure ``` -### Environmental Conditions - -Environmental conditions are nested under `experimental_conditions` and describe the -physical and chemical environment in which samples were cultivated. This includes -growth media specifications, temperature, cultivation method, and other environmental -parameters. - -#### Core Environmental Fields - -The following fields are supported within `environmental_conditions`: - -- **temperature_celsius** (float): Growth temperature in Celsius -- **cultivation_method** (string): Method of cultivation (e.g., "liquid_culture", "plate", "chemostat") -- **growth_phase_at_harvest** (object): Growth phase information (see [Growth Phase Specification](#growth-phase-specification)) -- **media** (object): Growth medium specification (see [Growth Media Specification](#growth-media-specification)) -- **chemical_treatment** (object): Chemical treatment information (see [Chemical Treatments](#chemical-treatments)) -- **drug_treatment** (object): Drug treatment (same structure as chemical_treatment) -- **heat_treatment** (object): Heat treatment specification -- **temperature_shift** (object): Temperature shift for heat shock experiments (see [Temperature Shifts](#temperature-shifts)) -- **induction** (object): Induction system for expression experiments (see [Induction Systems](#induction-systems)) -- **incubation_duration_hours** (float): Total incubation duration in hours -- **incubation_duration_minutes** (int): Total incubation duration in minutes -- **description** (string): Additional descriptive information - -#### Growth Phase Specification - -Growth phase at harvest can be specified using: - -```yaml -growth_phase_at_harvest: - stage: mid_log_phase # or: early_log_phase, late_log_phase, stationary_phase, etc. - od600: 0.6 # Optical density at 600nm - od600_tolerance: 0.1 # Optional: measurement tolerance - description: "Additional context" -``` - -**Note**: The field `phase` is accepted as an alias for `stage` for backward compatibility. - -Recognized stage values: -- `mid_log_phase`, `early_log_phase`, `late_log_phase` -- `stationary_phase`, `early_stationary_phase`, `overnight_stationary_phase` -- `mid_log`, `early_log`, `late_log`, `exponential_phase` - -#### Chemical Treatments - -Chemical treatments (including drugs) are specified with: - -```yaml -chemical_treatment: - compound: rapamycin # Chemical compound name - concentration_percent: 0.001 # Concentration as percentage - duration_minutes: 20 # Treatment duration in minutes - duration_hours: 0.33 # Alternative: duration in hours - target_pH: 4.0 # Optional: target pH for pH adjustments - description: "TOR inhibition" # Optional: additional context -``` - -The `drug_treatment` field uses the same structure and is interchangeable with `chemical_treatment`. - -#### Temperature Shifts - -For heat shock and temperature shift experiments: - -```yaml -temperature_shift: - initial_temperature_celsius: 30 - temperature_shift_celsius: 37 - temperature_shift_duration_minutes: 45 - description: "Heat shock treatment" -``` - -#### Induction Systems - -For expression induction systems (e.g., GAL, estradiol-inducible): - -```yaml -induction: - inducer: - compound: D-galactose - concentration_percent: 2 - duration_hours: 3 - duration_minutes: 180 # Alternative to duration_hours - description: "GAL promoter induction" -``` - -#### Growth Media Specification - -The `media` field specifies the growth medium used in an experiment. Media is nested -under `environmental_conditions` and can be specified at the top-level, config-level, -or within field-level definitions depending on whether they are common across all -datasets, specific to a config, or vary per sample. - -##### Media Structure - -Each media specification has the following required structure: - -```yaml -experimental_conditions: - environmental_conditions: - media: - name: string # Canonical or descriptive media name (see below) - carbon_source: # Required - - compound: string # Chemical compound name - concentration_percent: float - nitrogen_source: # Required - - compound: string # Chemical compound name - concentration_percent: float - phosphate_source: # Optional - - compound: string - concentration_percent: float - additives: # Optional: for additional media components - - compound: string # e.g., butanol for filamentation - concentration_percent: float - description: string -``` - -Both `carbon_source` and `nitrogen_source` are **required fields**. Each can contain -one or more compound entries with their respective concentrations specified as a -percentage. - -**Handling Unknown Values**: When a component is truly unknown or not reported in the -source publication, omit the field or use `null`. Do NOT use the string `"unspecified"` as -a compound name, as this will generate validation warnings. - -##### Canonical Media Names - -Three base media types are standardized across the collection: - -1. **minimal** - - Minimal defined medium with inorganic nitrogen source - - Typically used for targeted nutrient deprivation studies - - Example: Hackett 2020 - -2. **synthetic_complete** - - Defined medium with amino acid supplements - - Contains yeast nitrogen base (without amino acids) plus amino acid dropout mix - - Used as baseline in many stress studies - - Example: Kemmeren 2014, Mahendrawada 2025, Harbison 2004 - -3. **YPD** (yeast peptone dextrose) - - Rich, complex medium with yeast extract and peptone as nitrogen sources - - Used as standard rich-media baseline condition - - Also known as: yeast_peptone_dextrose, yeast_extract_peptone (context-dependent) - - Example: Hu Reimand 2010, Harbison 2004, Rossi 2021, Barkai compendium - -**Descriptive Media Names**: While the canonical names above are preferred, descriptive -variations that provide additional specificity are acceptable (e.g., -`synthetic_complete_dextrose`, `selective_medium`, `synthetic_complete_minus_uracil`). -The key requirement is that the actual media composition be fully specified in the -`carbon_source`, `nitrogen_source`, and optional `phosphate_source` and `additives` fields. - -##### Specifying Carbon and Nitrogen Sources - -###### Carbon Sources - -Common carbon sources in yeast media: - -```yaml -carbon_source: - - compound: D-glucose - concentration_percent: 2 -``` - -Typical values: D-glucose, D-galactose, D-raffinose, D-dextrose - -Concentrations are expressed as a percentage (e.g., 2% glucose). - -###### Nitrogen Sources - -Nitrogen sources vary by media type: - -**For synthetic_complete and minimal media:** -```yaml -nitrogen_source: - - compound: yeast_nitrogen_base - concentration_g_per_l: 6.71 - specifications: - - without_amino_acids - - without_ammonium_sulfate - - compound: ammonium_sulfate - # if specified differently in the paper, add the authors' - # specification in a comment - concentration_percent: 0.5 - - compound: amino_acid_dropout_mix - # lastname et al 2025 used 20 g/L - concentration_percent: 2 -``` - -**For YPD media:** -```yaml -nitrogen_source: - - compound: yeast_extract - concentration_percent: 1 - - compound: peptone - concentration_percent: 2 -``` - -##### Media Examples - -**Minimal Medium** -```yaml -experimental_conditions: - environmental_conditions: - media: - name: minimal - carbon_source: - - compound: D-glucose - concentration_percent: 2 - nitrogen_source: - - compound: ammonium_sulfate - concentration_g_per_l: 5 -``` - -**Synthetic Complete (Base)** -```yaml -experimental_conditions: - environmental_conditions: - media: - name: synthetic_complete - carbon_source: - - compound: D-glucose - concentration_percent: 2 - nitrogen_source: - - compound: yeast_nitrogen_base - # lastname et al 2025 used 6.71 g/L - concentration_percent: 0.671 - specifications: - - without_amino_acids - - without_ammonium_sulfate - - compound: ammonium_sulfate - # lastname et al 2025 used 5 g/L - concentration_percent: 0.5 - - compound: amino_acid_dropout_mix - # lastname et al 2025 used 2 g/L - concentration_percent: 0.2 -``` - -**Synthetic Complete with Alternative Carbon Source** -```yaml -experimental_conditions: - environmental_conditions: - media: - name: synthetic_complete - carbon_source: - - compound: D-galactose - concentration_percent: 2 - nitrogen_source: - - compound: yeast_nitrogen_base - # lastname et al 2025 used 6.71 g/L - concentration_percent: 0.671 - specifications: - - without_amino_acids - - without_ammonium_sulfate - - compound: ammonium_sulfate - # lastname et al 2025 used 5 g/L - concentration_percent: 0.5 - - compound: amino_acid_dropout_mix - # lastname et al 2025 used 2 g/L - concentration_percent: 0.2 -``` - -**YPD** -```yaml -experimental_conditions: - environmental_conditions: - media: - name: YPD - carbon_source: - - compound: D-glucose - concentration_percent: 2 - nitrogen_source: - - compound: yeast_extract - concentration_percent: 1 - - compound: peptone - concentration_percent: 2 -``` - -##### Selective/Dropout Media Variants - -When a dataset uses a selective medium with specific amino acid or nutrient dropouts, -specify this using the base `synthetic_complete` name and adjust the `nitrogen_source` -to reflect the modified composition: - -```yaml -experimental_conditions: - environmental_conditions: - media: - name: synthetic_complete - carbon_source: - - compound: D-glucose - concentration_percent: 2 - nitrogen_source: - - compound: yeast_nitrogen_base - # lastname et al 2025 used 6.71 g/L - concentration_percent: 0.671 - specifications: - - without_amino_acids - - without_ammonium_sulfate - - compound: ammonium_sulfate - # lastname et al 2025 used 5 g/L - concentration_percent: 0.5 - - compound: amino_acid_dropout_mix - # lastname et al 2025 used 2 g/L - concentration_percent: 0.2 - specifications: - - minus_uracil - - minus_histidine - - minus_leucine -``` - -##### Media in Field-Level Definitions - -When media varies per sample and is captured in a categorical field with definitions: - -```yaml -- name: condition - dtype: - class_label: - names: ["standard", "galactose"] - role: experimental_condition - definitions: - standard: - environmental_conditions: - media: - name: YPD - carbon_source: - - compound: D-glucose - concentration_percent: 2 - nitrogen_source: - - compound: yeast_extract - concentration_percent: 1 - - compound: peptone - concentration_percent: 2 - galactose: - environmental_conditions: - media: - name: YPD - carbon_source: - - compound: D-galactose - concentration_percent: 2 - nitrogen_source: - - compound: yeast_extract - concentration_percent: 1 - - compound: peptone - concentration_percent: 2 -``` - ## Feature Definitions Each config must include detailed feature definitions in `dataset_info.features`: @@ -488,26 +158,27 @@ machine-parsable specification of what each condition value means experimentally description: Growth condition of the sample definitions: standard: - environmental_conditions: - media: - name: synthetic_complete - carbon_source: - - compound: D-glucose - concentration_percent: 2 - nitrogen_source: - - compound: yeast_nitrogen_base - concentration_g_per_l: 6.71 - specifications: - - without_amino_acids - - without_ammonium_sulfate - - compound: ammonium_sulfate - concentration_g_per_l: 5 - - compound: amino_acid_dropout_mix - concentration_g_per_l: 2 + media: + name: synthetic_complete + carbon_source: + - compound: D-glucose + concentration_percent: 2 + nitrogen_source: + - compound: yeast_nitrogen_base + # lastname et al 2025 used 6.71 g/L + concentration_percent: 0.671 + specifications: + - without_amino_acids + - without_ammonium_sulfate + - compound: ammonium_sulfate + # lastname et al 2025 used 5 g/L + concentration_percent: 0.5 + - compound: amino_acid_dropout_mix + # lastname et al 2025 used 2 g/L + concentration_percent: 0.2 heat_shock: - environmental_conditions: - temperature_celsius: 37 - duration_minutes: 10 + temperature_celsius: 37 + duration_minutes: 10 ``` Each key in `definitions` must correspond to a possible value in the field. @@ -536,53 +207,9 @@ Unless otherwise noted, assume that coordinates are 0-based, half-open intervals ## Feature Roles The optional `role` field provides semantic meaning to features, especially useful -for annotated_features datasets. The following roles are recognized by tfbpapi: - -- **target_identifier**: Identifies target genes/features (e.g., `target_locus_tag`, - `target_symbol`) -- **regulator_identifier**: Identifies regulatory factors (e.g., `regulator_locus_tag`, - `regulator_symbol`) -- **quantitative_measure**: Quantitative measurements (e.g., `binding_score`, - `expression_level`, `p_value`) -- **experimental_condition**: Experimental conditions or metadata - (can include `definitions` field for categorical values) -- **genomic_coordinate**: Positional information (`chr`, `start`, `end`, `pos`) - -**Validation**: Only these specific role values are accepted. Other values (e.g., `"identifier"`) -will cause validation errors. - -## Strain Background and Definitions - -The `strain_background` field can appear in two locations: - -1. **Top-level or config-level** `experimental_conditions`: - ```yaml - experimental_conditions: - strain_background: - genotype: BY4741 - mating_type: MATa - markers: [his3Δ1, leu2Δ0, met15Δ0, ura3Δ0] - ``` - -2. **Within field-level definitions** for condition-specific strain information: - ```yaml - - name: heat_shock - dtype: - class_label: - names: ["control", "treated"] - role: experimental_condition - definitions: - treated: - environmental_conditions: - temperature_celsius: 37 - strain_background: - genotype: W303_derivative - description: "Heat-sensitive strain" - ``` - -The `strain_background` field accepts flexible structure as a dictionary to accommodate -varying levels of detail about strain information. - +for annotated_features datasets. The following roles are recognized by tfbpapi. +**NOTE** `experimental_condition` is a reserved role with additional behavior +as described above. ## Partitioned Datasets @@ -685,23 +312,6 @@ For partitioned datasets, partition values are extracted from directory structur partition_by: ["run_accession", "regulator_symbol"] ``` -### How Embedded Metadata Works - -1. **Partition Fields**: For partitioned datasets, values are extracted from directory - names (e.g., `accession=SRR123` to `SRR123`) -2. **Data Fields**: For single files, distinct values are extracted via HuggingFace - Datasets Server API -3. **Synthetic Config**: A synthetic metadata config is created with extracted values -4. **Automatic Pairing**: The synthetic metadata automatically applies to the source - config - -### Metadata Extraction Priority - -The system tries metadata sources in this order: -1. **Explicit metadata configs** with `applies_to` field -2. **Automatic type-based pairing** between data configs and metadata configs -3. **Embedded metadata extraction** from `metadata_fields` - ## Data File Organization ### Single Files diff --git a/docs/tutorials/datacard_tutorial.ipynb b/docs/tutorials/datacard_tutorial.ipynb index 47df9b6..055f8fe 100644 --- a/docs/tutorials/datacard_tutorial.ipynb +++ b/docs/tutorials/datacard_tutorial.ipynb @@ -4,25 +4,23 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# DataCard Tutorial: Exploring HuggingFace Genomics Datasets\n", + "# DataCard Tutorial: Exploring HuggingFace Dataset Metadata\n", "\n", - "The `DataCard` class provides an easy-to-use interface for exploring HuggingFace dataset metadata without loading the actual genomic data. This is particularly useful for:\n", + "The `DataCard` class provides an interface for exploring HuggingFace dataset metadata without loading the actual genomic data. This is particularly useful for:\n", "\n", "- Understanding dataset structure and available configurations\n", - "- Exploring experimental conditions and regulators\n", + "- Exploring experimental conditions at all hierarchy levels\n", "- Discovering metadata relationships\n", - "- Planning data analysis workflows\n", + "- Planning data analysis workflows and metadata table creation\n", "\n", - "In this tutorial, we'll explore the **BrentLab/rossi_2021** dataset, which contains ChIP-exo data for transcription factor binding in yeast." + "In this tutorial, we'll explore the **BrentLab/harbison_2004** dataset, which contains ChIP-chip data for transcription factor binding across 14 environmental conditions in yeast." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## 1. Getting Started\n", - "\n", - "First, let's import the DataCard class and initialize it with our target dataset." + "## 1. Instantiating a DataCard Object" ] }, { @@ -31,27 +29,25 @@ "metadata": {}, "outputs": [ { - "name": "stdout", + "name": "stderr", "output_type": "stream", "text": [ - "Repository: BrentLab/mahendrawada_2025\n" + "/home/chase/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" ] }, { - "name": "stderr", + "name": "stdout", "output_type": "stream", "text": [ - "/home/chase/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", - " from .autonotebook import tqdm as notebook_tqdm\n" + "Repository: BrentLab/harbison_2004\n" ] } ], "source": [ "from tfbpapi.datainfo import DataCard\n", "\n", - "# Initialize DataCard with the Rossi 2021 dataset\n", - "# try this with mahendrawada_2025, which is more complex\n", - "card = DataCard('BrentLab/mahendrawada_2025')\n", + "card = DataCard('BrentLab/harbison_2004')\n", "\n", "print(f\"Repository: {card.repo_id}\")" ] @@ -76,16 +72,16 @@ "text": [ "Repository Information:\n", "========================================\n", - "repo_id : BrentLab/mahendrawada_2025\n", - "pretty_name : Mahendrawada 2025 ChEC-seq and Nascent RNA-seq data\n", + "repo_id : BrentLab/harbison_2004\n", + "pretty_name : Harbison, 2004 ChIP-chip\n", "license : mit\n", - "tags : ['biology', 'genomics', 'yeast', 'transcription-factors', 'gene-expression', 'binding', 'chec', 'perturbation', 'rnaseq', 'nascent rnaseq']\n", + "tags : ['genomics', 'yeast', 'transcription', 'binding']\n", "language : ['en']\n", - "size_categories : ['100K \u001b[39m\u001b[32m2\u001b[39m metadata_info = \u001b[43mcard\u001b[49m\u001b[43m.\u001b[49m\u001b[43mexplore_config\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43mmetadata\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[32m 4\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33mMetadata Configuration Details:\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 5\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33m=\u001b[39m\u001b[33m\"\u001b[39m * \u001b[32m40\u001b[39m)\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/code/tfbp/tfbpapi/tfbpapi/datainfo/datacard.py:291\u001b[39m, in \u001b[36mDataCard.explore_config\u001b[39m\u001b[34m(self, config_name)\u001b[39m\n\u001b[32m 289\u001b[39m config = \u001b[38;5;28mself\u001b[39m.get_config(config_name)\n\u001b[32m 290\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m config:\n\u001b[32m--> \u001b[39m\u001b[32m291\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m DataCardError(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mConfiguration \u001b[39m\u001b[33m'\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mconfig_name\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m'\u001b[39m\u001b[33m not found\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 293\u001b[39m info: \u001b[38;5;28mdict\u001b[39m[\u001b[38;5;28mstr\u001b[39m, Any] = {\n\u001b[32m 294\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mconfig_name\u001b[39m\u001b[33m\"\u001b[39m: config.config_name,\n\u001b[32m 295\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mdescription\u001b[39m\u001b[33m\"\u001b[39m: config.description,\n\u001b[32m (...)\u001b[39m\u001b[32m 305\u001b[39m ],\n\u001b[32m 306\u001b[39m }\n\u001b[32m 308\u001b[39m \u001b[38;5;66;03m# Add partitioning info if present\u001b[39;00m\n", - "\u001b[31mDataCardError\u001b[39m: Configuration 'metadata' not found" + "name": "stdout", + "output_type": "stream", + "text": [ + "Main Configuration Details:\n", + "========================================\n", + "Config name: harbison_2004\n", + "Dataset type: annotated_features\n", + "Number of features: 7\n", + "\n", + "Features:\n", + " • condition [experimental_condition]\n", + " Environmental condition of the experiment. Nearly all of the 204 regulators have a YPD condition, and some have others in addition.\n", + " • regulator_locus_tag [regulator_identifier]\n", + " Systematic gene name (ORF identifier) of the ChIPd transcription factor\n", + " • regulator_symbol [regulator_identifier]\n", + " Standard gene symbol of the ChIPd transcription factor\n", + " • target_locus_tag [target_identifier]\n", + " Systematic gene name (ORF identifier) of the target gene measured\n", + " • target_symbol [target_identifier]\n", + " Standard gene symbol of the target gene measured\n", + " • effect [quantitative_measure]\n", + " The chip channel ratio (effect size)\n", + " • pvalue [quantitative_measure]\n", + " pvalue of the chip channel ratio (effect)\n" ] } ], "source": [ - "# Explore the metadata configuration\n", - "metadata_info = card.explore_config('metadata')\n", + "# Explore the main data configuration in detail\n", + "config_info = card.explore_config('harbison_2004')\n", "\n", - "print(\"Metadata Configuration Details:\")\n", + "print(\"Main Configuration Details:\")\n", "print(\"=\" * 40)\n", - "print(f\"Config name: {metadata_info['config_name']}\")\n", - "print(f\"Dataset type: {metadata_info['dataset_type']}\")\n", - "print(f\"Number of features: {metadata_info['num_features']}\")\n", - "print(f\"Default config: {metadata_info['is_default']}\")\n", - "\n", - "print(\"\\nFeatures in metadata config:\")\n", - "for feature in metadata_info['features']:\n", - " print(f\" • {feature['name']:20} ({feature['dtype']:10}): {feature['description']}\")" + "print(f\"Config name: {config_info['config_name']}\")\n", + "print(f\"Dataset type: {config_info['dataset_type']}\")\n", + "print(f\"Number of features: {config_info['num_features']}\")\n", + "\n", + "print(\"\\nFeatures:\")\n", + "for feature in config_info['features']:\n", + " role = f\" [{feature.get('role', 'no role')}]\" if 'role' in feature else \"\"\n", + " print(f\" • {feature['name']:25} {role}\")\n", + " print(f\" {feature['description']}\")" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Genome Map Configuration Details:\n", - "========================================\n", - "Config name: genome_map\n", - "Dataset type: genome_map\n", - "Number of features: 3\n", - "\n", - "Features in genome_map config:\n", - " • chr (string ): Chromosome name (e.g., chrI, chrII, etc.)\n", - " • pos (int32 ): Genomic position of the 5' tag\n", - " • pileup (int32 ): Depth of coverage (number of 5' tags) at this genomic position\n", - "\n", - "Partitioning Information:\n", - " Enabled: True\n", - " Partition by: ['run_accession']\n", - " Path template: genome_map/accession={run_accession}/*.parquet\n" - ] - } - ], "source": [ - "# Explore the genome_map configuration\n", - "genome_map_info = card.explore_config('genome_map')\n", + "## 4. Understanding Experimental Conditions: The Three-Level Hierarchy\n", "\n", - "print(\"Genome Map Configuration Details:\")\n", - "print(\"=\" * 40)\n", - "print(f\"Config name: {genome_map_info['config_name']}\")\n", - "print(f\"Dataset type: {genome_map_info['dataset_type']}\")\n", - "print(f\"Number of features: {genome_map_info['num_features']}\")\n", - "\n", - "print(\"\\nFeatures in genome_map config:\")\n", - "for feature in genome_map_info['features']:\n", - " print(f\" • {feature['name']:15} ({feature['dtype']:10}): {feature['description']}\")\n", - "\n", - "# Check if this config has partitioning\n", - "if 'partitioning' in genome_map_info:\n", - " print(\"\\nPartitioning Information:\")\n", - " partitioning = genome_map_info['partitioning']\n", - " print(f\" Enabled: {partitioning['enabled']}\")\n", - " print(f\" Partition by: {partitioning['partition_by']}\")\n", - " print(f\" Path template: {partitioning['path_template']}\")" + "The tfbpapi system supports experimental conditions at three hierarchy levels:\n", + "\n", + "1. **Top-level (repo-wide)**: Conditions common to all datasets/samples\n", + "2. **Config-level**: Conditions specific to a dataset configuration\n", + "3. **Field-level**: Conditions that vary per sample, defined in field definitions\n", + "\n", + "Let's explore each level for the Harbison 2004 dataset." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## 4. Understanding Data Relationships\n", + "### Level 1: Top-Level Conditions\n", "\n", - "The DataCard can help you understand how different configurations relate to each other, particularly metadata relationships." + "Top-level conditions apply to all experiments in the repository." ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Found 1 metadata relationships:\n", - "\n", - "Relationship details:\n", - " • genome_map -> metadata\n", - " Type: explicit\n", - " (Metadata config explicitly specifies which data configs it applies to)\n" + "Top-Level Experimental Conditions:\n", + "========================================\n", + "No top-level conditions defined for this repository\n", + "(All conditions are defined at config or field level)\n" ] } ], "source": [ - "# Explore metadata relationships\n", - "relationships = card.get_metadata_relationships()\n", - "\n", - "print(f\"Found {len(relationships)} metadata relationships:\")\n", - "print(\"\\nRelationship details:\")\n", + "# Get top-level experimental conditions\n", + "top_conditions = card.get_experimental_conditions()\n", "\n", - "for rel in relationships:\n", - " print(f\" • {rel.data_config} -> {rel.metadata_config}\")\n", - " print(f\" Type: {rel.relationship_type}\")\n", + "print(\"Top-Level Experimental Conditions:\")\n", + "print(\"=\" * 40)\n", "\n", - " if rel.relationship_type == \"explicit\":\n", - " print(\" (Metadata config explicitly specifies which data configs it applies to)\")\n", - " elif rel.relationship_type == \"embedded\":\n", - " print(\" (Metadata is embedded within the data config itself)\")" + "if top_conditions:\n", + " for key, value in top_conditions.items():\n", + " print(f\"{key}: {value}\")\n", + "else:\n", + " print(\"No top-level conditions defined for this repository\")\n", + " print(\"(All conditions are defined at config or field level)\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## 5. Exploring Dataset Contents\n", + "### Level 2: Config-Level Conditions\n", "\n", - "Now let's explore what experimental data is available in this dataset." + "Config-level conditions apply to all samples in a specific configuration." ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Metadata configurations: ['metadata']\n", - "Data configurations: ['genome_map']\n", - "\n", - "Default configuration: metadata\n" + "Config-Level Experimental Conditions:\n", + "========================================\n", + "No config-level conditions defined\n", + "(Conditions vary per sample at field level)\n" ] } ], "source": [ - "# Get different config types\n", - "from tfbpapi.datainfo.models import DatasetType\n", - "\n", - "# Find metadata configs\n", - "metadata_configs = card.get_configs_by_type(DatasetType.METADATA)\n", - "print(f\"Metadata configurations: {[c.config_name for c in metadata_configs]}\")\n", + "# Get config-level conditions (merged with top-level)\n", + "config_conditions = card.get_experimental_conditions('harbison_2004')\n", "\n", - "# Find data configs\n", - "data_configs = card.get_configs_by_type(DatasetType.GENOME_MAP)\n", - "print(f\"Data configurations: {[c.config_name for c in data_configs]}\")\n", + "print(\"Config-Level Experimental Conditions:\")\n", + "print(\"=\" * 40)\n", "\n", - "# Get the default config\n", - "default_config = card.dataset_card.get_default_config()\n", - "if default_config:\n", - " print(f\"\\nDefault configuration: {default_config.config_name}\")" + "if config_conditions:\n", + " for key, value in config_conditions.items():\n", + " print(f\"{key}: {value}\")\n", + "else:\n", + " print(\"No config-level conditions defined\")\n", + " print(\"(Conditions vary per sample at field level)\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Extracting Field Values\n", + "### Level 3: Field-Level Conditions\n", "\n", - "For exploration purposes, we can extract unique values from specific fields. This is particularly useful for understanding what experimental conditions or regulators are available." + "Field-level conditions vary per sample and are defined in field definitions." ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Found 0 unique run accessions:\n", - "No accessions found (might require partition-based extraction)\n" + "Fields with Experimental Conditions:\n", + "========================================\n", + "Found 1 condition field(s): ['condition']\n" ] } ], "source": [ - "# Try to extract run accession information\n", - "try:\n", - " accessions = card.get_field_values('metadata', 'run_accession')\n", - " print(f\"Found {len(accessions)} unique run accessions:\")\n", - " if accessions:\n", - " sample_accessions = sorted(list(accessions))[:5]\n", - " print(f\"Sample accessions: {sample_accessions}...\")\n", - " else:\n", - " print(\"No accessions found (might require partition-based extraction)\")\n", - "except Exception as e:\n", - " print(f\"Could not extract accession values: {e}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 6. Working with Partitioned Data\n", + "# List all experimental condition fields\n", + "condition_fields = card.list_experimental_condition_fields('harbison_2004')\n", "\n", - "Many genomics datasets are partitioned for efficient storage and querying. Let's explore how partitioning works in this dataset." + "print(\"Fields with Experimental Conditions:\")\n", + "print(\"=\" * 40)\n", + "print(f\"Found {len(condition_fields)} condition field(s): {condition_fields}\")" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Partitioning Details:\n", - "==============================\n", - "Enabled: True\n", - "Partition columns: ['run_accession']\n", - "Path template: genome_map/accession={run_accession}/*.parquet\n", + "Condition Field Definitions:\n", + "========================================\n", + "Found 14 defined conditions:\n", "\n", - "This means:\n", - "• The genome map data is split into separate files for each run_accession\n", - "• Files are organized as: genome_map/accession={run_accession}/*.parquet\n", - "• This allows efficient querying of specific experimental runs\n" + " • Acid\n", + " • Alpha\n", + " • BUT14\n", + " • BUT90\n", + " • GAL\n", + " • H2O2Hi\n", + " • H2O2Lo\n", + " • HEAT\n", + " • Pi-\n", + " • RAFF\n", + " • RAPA\n", + " • SM\n", + " • Thi-\n", + " • YPD\n" ] } ], "source": [ - "# Check partitioning details for the genome_map config\n", - "genome_map_config = card.get_config('genome_map')\n", - "\n", - "if genome_map_config and genome_map_config.dataset_info.partitioning:\n", - " part_info = genome_map_config.dataset_info.partitioning\n", - "\n", - " print(\"Partitioning Details:\")\n", - " print(\"=\" * 30)\n", - " print(f\"Enabled: {part_info.enabled}\")\n", - " print(f\"Partition columns: {part_info.partition_by}\")\n", - " print(f\"Path template: {part_info.path_template}\")\n", - "\n", - " print(\"\\nThis means:\")\n", - " print(\"• The genome map data is split into separate files for each run_accession\")\n", - " print(\"• Files are organized as: genome_map/accession={run_accession}/*.parquet\")\n", - " print(\"• This allows efficient querying of specific experimental runs\")\n", - "else:\n", - " print(\"No partitioning information found.\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 7. Understanding Data Files\n", + "# Get definitions for the 'condition' field\n", + "# This maps each condition value to its detailed specification\n", + "condition_defs = card.get_field_definitions('harbison_2004', 'condition')\n", "\n", - "Let's examine how the data files are organized within each configuration." + "print(f\"Condition Field Definitions:\")\n", + "print(\"=\" * 40)\n", + "print(f\"Found {len(condition_defs)} defined conditions:\\n\")\n", + "\n", + "# Show all condition names\n", + "for cond_name in sorted(condition_defs.keys()):\n", + " print(f\" • {cond_name}\")" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "\n", - "Data files for 'metadata' config:\n", - "----------------------------------------\n", - " File 1:\n", - " Split: train\n", - " Path: rossi_2021_metadata.parquet\n", - "\n", - "Data files for 'genome_map' config:\n", - "----------------------------------------\n", - " File 1:\n", - " Split: train\n", - " Path: genome_map/*/*.parquet\n", - " - This is a glob pattern that matches multiple files\n" + "YPD Condition Definition:\n", + "========================================\n", + "{\n", + " \"description\": \"Rich media baseline condition\",\n", + " \"temperature_celsius\": 30,\n", + " \"cultivation_method\": \"unspecified\",\n", + " \"growth_phase_at_harvest\": {\n", + " \"od600\": 0.8\n", + " },\n", + " \"media\": {\n", + " \"name\": \"YPD\",\n", + " \"carbon_source\": [\n", + " {\n", + " \"compound\": \"D-glucose\",\n", + " \"concentration_percent\": 2\n", + " }\n", + " ],\n", + " \"nitrogen_source\": [\n", + " {\n", + " \"compound\": \"yeast_extract\",\n", + " \"concentration_percent\": 1\n", + " },\n", + " {\n", + " \"compound\": \"peptone\",\n", + " \"concentration_percent\": 2\n", + " }\n", + " ]\n", + " }\n", + "}\n" ] } ], "source": [ - "# Examine data files for each configuration\n", - "for config in card.configs:\n", - " print(f\"\\nData files for '{config.config_name}' config:\")\n", - " print(\"-\" * 40)\n", - "\n", - " for i, data_file in enumerate(config.data_files):\n", - " print(f\" File {i+1}:\")\n", - " print(f\" Split: {data_file.split}\")\n", - " print(f\" Path: {data_file.path}\")\n", - "\n", - " # Explain path patterns\n", - " if '*' in data_file.path:\n", - " print(f\" - This is a glob pattern that matches multiple files\")\n", - " if '=' in data_file.path:\n", - " print(f\" - This uses partitioned directory structure\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 8. Practical Use Cases\n", + "# Explore a specific condition in detail\n", + "import json\n", "\n", - "Here are some common scenarios where DataCard is useful:" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Use Case 1: Finding Datasets with Specific Data Types" + "# Let's look at the YPD baseline condition\n", + "ypd_def = condition_defs.get('YPD', {})\n", + "\n", + "print(\"YPD Condition Definition:\")\n", + "print(\"=\" * 40)\n", + "print(json.dumps(ypd_def, indent=2))" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Available dataset types: ['metadata', 'genome_map']\n", - "\n", - "Has genome-wide binding data: True\n" + "HEAT Condition Definition:\n", + "========================================\n", + "{\n", + " \"description\": \"Heat shock stress condition\",\n", + " \"initial_temperature_celsius\": 30,\n", + " \"temperature_shift_celsius\": 37,\n", + " \"temperature_shift_duration_minutes\": 45,\n", + " \"cultivation_method\": \"unspecified\",\n", + " \"growth_phase_at_harvest\": {\n", + " \"od600\": 0.5\n", + " },\n", + " \"media\": {\n", + " \"name\": \"YPD\",\n", + " \"carbon_source\": [\n", + " {\n", + " \"compound\": \"D-glucose\",\n", + " \"concentration_percent\": 2\n", + " }\n", + " ],\n", + " \"nitrogen_source\": [\n", + " {\n", + " \"compound\": \"yeast_extract\",\n", + " \"concentration_percent\": 1\n", + " },\n", + " {\n", + " \"compound\": \"peptone\",\n", + " \"concentration_percent\": 2\n", + " }\n", + " ]\n", + " }\n", + "}\n" ] } ], "source": [ - "# Check what types of data are available\n", - "available_types = [config.dataset_type.value for config in card.configs]\n", - "print(f\"Available dataset types: {available_types}\")\n", + "# Let's look at a treatment condition (HEAT shock)\n", + "heat_def = condition_defs.get('HEAT', {})\n", "\n", - "# Check if this dataset has genome-wide binding data\n", - "has_genome_map = any(config.dataset_type == DatasetType.GENOME_MAP for config in card.configs)\n", - "print(f\"\\nHas genome-wide binding data: {has_genome_map}\")" + "print(\"HEAT Condition Definition:\")\n", + "print(\"=\" * 40)\n", + "print(json.dumps(heat_def, indent=2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Use Case 2: Understanding Data Schema Before Loading" + "## 5. Working with Condition Definitions\n", + "\n", + "Now let's see how to extract specific information from condition definitions." ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Genome Map Data Schema:\n", - "==============================\n", - "Column: chr\n", - " Type: string\n", - " Description: Chromosome name (e.g., chrI, chrII, etc.)\n", - "\n", - "Column: pos\n", - " Type: int32\n", - " Description: Genomic position of the 5' tag\n", - "\n", - "Column: pileup\n", - " Type: int32\n", - " Description: Depth of coverage (number of 5' tags) at this genomic position\n", - "\n", - "This tells us:\n", - "• 'chr' column contains chromosome names (string)\n", - "• 'pos' column contains genomic positions (int32)\n", - "• 'pileup' column contains tag counts (int32)\n", - "• Data represents 5' tag coverage from ChIP-exo experiments\n" + "Growth Media Across Conditions:\n", + "========================================\n", + " Acid : YPD\n", + " Alpha : YPD\n", + " BUT14 : YPD\n", + " BUT90 : YPD\n", + " GAL : yeast_extract_peptone\n", + " H2O2Hi : YPD\n", + " H2O2Lo : YPD\n", + " HEAT : YPD\n", + " Pi- : synthetic_complete_minus_phosphate\n", + " RAFF : yeast_extract_peptone\n", + " RAPA : YPD\n", + " SM : synthetic_complete\n", + " Thi- : synthetic_complete_minus_thiamine\n", + " YPD : YPD\n" ] } ], "source": [ - "# Before loading large genome map data, understand its structure\n", - "genome_config = card.get_config('genome_map')\n", - "\n", - "if genome_config:\n", - " print(\"Genome Map Data Schema:\")\n", - " print(\"=\" * 30)\n", - "\n", - " for feature in genome_config.dataset_info.features:\n", - " print(f\"Column: {feature.name}\")\n", - " print(f\" Type: {feature.dtype}\")\n", - " print(f\" Description: {feature.description}\")\n", - " print()\n", - "\n", - " print(\"This tells us:\")\n", - " print(\"• 'chr' column contains chromosome names (string)\")\n", - " print(\"• 'pos' column contains genomic positions (int32)\")\n", - " print(\"• 'pileup' column contains tag counts (int32)\")\n", - " print(\"• Data represents 5' tag coverage from ChIP-exo experiments\")" + "# Extract growth media names for all conditions\n", + "print(\"Growth Media Across Conditions:\")\n", + "print(\"=\" * 40)\n", + "\n", + "for cond_name, cond_def in sorted(condition_defs.items()):\n", + " # Navigate the nested structure\n", + " media = cond_def.get('media', {})\n", + " media_name = media.get('name', 'unspecified')\n", + "\n", + " print(f\" {cond_name:10}: {media_name}\")" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": 13, "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'description': 'Rich media baseline condition',\n", + " 'temperature_celsius': 30,\n", + " 'cultivation_method': 'unspecified',\n", + " 'growth_phase_at_harvest': {'od600': 0.8},\n", + " 'media': {'name': 'YPD',\n", + " 'carbon_source': [{'compound': 'D-glucose', 'concentration_percent': 2}],\n", + " 'nitrogen_source': [{'compound': 'yeast_extract',\n", + " 'concentration_percent': 1},\n", + " {'compound': 'peptone', 'concentration_percent': 2}]}}" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "### Use Case 3: Planning Efficient Data Access" + "condition_defs.get(\"YPD\")" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Data Access Strategy:\n", - "=========================\n", - "• Data is partitioned by 'run_accession'\n", - "• To load data for a specific experiment, filter by run_accession\n", - "• This avoids loading data from all experiments\n", - "• Path pattern: genome_map/accession={run_accession}/*.parquet\n", - "\n", - "Example workflow:\n", - "1. Use metadata config to find interesting run_accessions\n", - "2. Load only genome_map data for those specific accessions\n", - "3. Analyze position-level binding data for selected experiments\n" + "Temperature Across Conditions:\n", + "========================================\n", + " Acid : not specified°C\n", + " Alpha : not specified°C\n", + " BUT14 : not specified°C\n", + " BUT90 : not specified°C\n", + " GAL : not specified°C\n", + " H2O2Hi : not specified°C\n", + " H2O2Lo : not specified°C\n", + " HEAT : not specified°C\n", + " Pi- : not specified°C\n", + " RAFF : not specified°C\n", + " RAPA : not specified°C\n", + " SM : not specified°C\n", + " Thi- : not specified°C\n", + " YPD : not specified°C\n" ] } ], "source": [ - "# Understanding partitioning helps plan efficient queries\n", - "if genome_config and genome_config.dataset_info.partitioning:\n", - " print(\"Data Access Strategy:\")\n", - " print(\"=\" * 25)\n", - " print(\"• Data is partitioned by 'run_accession'\")\n", - " print(\"• To load data for a specific experiment, filter by run_accession\")\n", - " print(\"• This avoids loading data from all experiments\")\n", - " print(\"• Path pattern: genome_map/accession={run_accession}/*.parquet\")\n", - "\n", - " print(\"\\nExample workflow:\")\n", - " print(\"1. Use metadata config to find interesting run_accessions\")\n", - " print(\"2. Load only genome_map data for those specific accessions\")\n", - " print(\"3. Analyze position-level binding data for selected experiments\")" + "# Extract temperature conditions\n", + "print(\"Temperature Across Conditions:\")\n", + "print(\"=\" * 40)\n", + "\n", + "for cond_name, cond_def in sorted(condition_defs.items()):\n", + " env_conds = cond_def.get('environmental_conditions', {})\n", + " temp = env_conds.get('temperature_celsius', 'not specified')\n", + "\n", + " # Also check for temperature shifts\n", + " temp_shift = env_conds.get('temperature_shift')\n", + " if temp_shift:\n", + " from_temp = temp_shift.get('from_celsius', '?')\n", + " to_temp = temp_shift.get('to_celsius', '?')\n", + " print(f\" {cond_name:10}: {from_temp}°C → {to_temp}°C\")\n", + " else:\n", + " print(f\" {cond_name:10}: {temp}°C\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## 9. Error Handling and Troubleshooting\n", + "## 6. Using extract_metadata_schema for Metadata Table Planning\n", "\n", - "The DataCard class includes validation and error handling. Here are some common scenarios:" + "The `extract_metadata_schema` method provides all condition information in one call, which is useful for planning metadata table creation." ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Non-existent config result: None\n", - "Error accessing non-existent field: Field 'nonexistent_field' not found in config 'metadata'\n", - "Config 'some_config' not found in this dataset\n" + "Metadata Schema Summary:\n", + "========================================\n", + "Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", + "Target fields: ['target_locus_tag', 'target_symbol']\n", + "Condition fields: ['condition']\n", + "\n", + "Top-level conditions: None\n", + "Config-level conditions: None\n", + "Field definitions available for: ['condition']\n" ] } ], "source": [ - "# Handling missing configurations\n", - "missing_config = card.get_config('nonexistent_config')\n", - "print(f\"Non-existent config result: {missing_config}\")\n", - "\n", - "# Handling missing fields\n", - "try:\n", - " invalid_field = card.get_field_values('metadata', 'nonexistent_field')\n", - "except Exception as e:\n", - " print(f\"Error accessing non-existent field: {e}\")\n", - "\n", - "# Checking if config exists before using\n", - "config_name = 'some_config'\n", - "if card.get_config(config_name):\n", - " print(f\"Config '{config_name}' exists\")\n", - "else:\n", - " print(f\"Config '{config_name}' not found in this dataset\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 10. Summary and Next Steps\n", - "\n", - "The DataCard class provides a powerful way to explore HuggingFace genomics datasets before committing to loading large amounts of data. \n", - "\n", - "### Key Takeaways:\n", + "# Extract complete metadata schema\n", + "schema = card.extract_metadata_schema('harbison_2004')\n", "\n", - "1. **Dataset Structure**: The Rossi 2021 dataset contains both experimental metadata and genome-wide ChIP-exo binding data\n", - "2. **Partitioning**: Data is efficiently partitioned by experimental run for fast access\n", - "3. **Metadata Relationships**: The system automatically understands how metadata relates to data configs\n", - "4. **Schema Discovery**: You can understand data types and structure before loading\n", - "\n", - "### Next Steps:\n", - "\n", - "- Use `HfQueryAPI` to load specific subsets of the data based on your exploration\n", - "- Apply filters based on experimental conditions discovered through DataCard\n", - "- Combine multiple datasets that have compatible schemas\n", - "\n", - "### Example Integration with HfQueryAPI:\n", - "\n", - "```python\n", - "from tfbpapi import HfQueryAPI\n", - "\n", - "# After exploring with DataCard, load specific data\n", - "query_api = HfQueryAPI('BrentLab/rossi_2021')\n", - "\n", - "# Load metadata for planning\n", - "metadata_df = query_api.get_pandas('metadata')\n", - "\n", - "# Load genome map data for specific experiments\n", - "# (using partition filters based on DataCard exploration)\n", - "genome_data = query_api.get_pandas('genome_map', \n", - " filters={'run_accession': 'SRR123456'})\n", - "```" + "print(\"Metadata Schema Summary:\")\n", + "print(\"=\" * 40)\n", + "print(f\"Regulator fields: {schema['regulator_fields']}\")\n", + "print(f\"Target fields: {schema['target_fields']}\")\n", + "print(f\"Condition fields: {schema['condition_fields']}\")\n", + "print(f\"\\nTop-level conditions: {schema['top_level_conditions']}\")\n", + "print(f\"Config-level conditions: {schema['config_level_conditions']}\")\n", + "print(f\"Field definitions available for: {list(schema['condition_definitions'].keys())}\")" ] } ], diff --git a/docs/tutorials/metadata_explorer_tutorial.ipynb b/docs/tutorials/metadata_explorer_tutorial.ipynb deleted file mode 100644 index 0d4104a..0000000 --- a/docs/tutorials/metadata_explorer_tutorial.ipynb +++ /dev/null @@ -1,1027 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Cross-Dataset Metadata Exploration Tutorial\n", - "\n", - "This tutorial demonstrates how to use the `MetadataManager` to extract and query metadata across multiple HuggingFace datasets from different repositories.\n", - "\n", - "## Overview\n", - "\n", - "The `MetadataManager` enables:\n", - "- **Cross-dataset queries**: Filter metadata across MULTIPLE datasets from MULTIPLE repos\n", - "- **Low memory usage**: DuckDB temporary views over parquet files (no data loading)\n", - "- **Role-based alignment**: Heterogeneous schemas aligned by field roles (regulator_identifier, experimental_condition, etc.)\n", - "- **Searchable conditions**: Factor level names + flattened definitions for filtering\n", - "\n", - "## Datasets Used\n", - "\n", - "We'll explore two transcription factor binding datasets:\n", - "\n", - "1. **harbison_2004**: ChIP-chip TF binding across 14 environmental conditions (YPD, RAPA, HEAT, etc.)\n", - "2. **hackett_2020**: TF overexpression with nutrient limitation conditions (P, N, M restrictions)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "from tfbpapi.datainfo import DataCard, MetadataManager" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Part 1: Exploring Individual Dataset Metadata Schemas\n", - "\n", - "Before cross-dataset queries, let's understand each dataset's metadata structure using `DataCard.extract_metadata_schema()`." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Harbison 2004 Metadata Schema" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Harbison 2004 Metadata Schema\n", - "============================================================\n", - "Regulator Fields: ['regulator_locus_tag', 'regulator_symbol']\n", - "Target Fields: ['target_locus_tag', 'target_symbol']\n", - "Condition Fields: ['condition']\n", - "\n", - "Repo-Level Conditions: None\n", - "\n", - "Config-Level Conditions: None\n", - "\n", - "Field-Level Conditions (vary by sample):\n", - " - condition: 14 conditions defined\n", - "\n", - " Sample Conditions:\n", - " - YPD\n", - " - SM\n", - " - RAPA\n" - ] - } - ], - "source": [ - "# Load harbison_2004 datacard\n", - "harbison_card = DataCard(repo_id=\"BrentLab/harbison_2004\")\n", - "\n", - "# Extract metadata schema for the harbison_2004 config\n", - "harbison_schema = harbison_card.extract_metadata_schema(\"harbison_2004\")\n", - "\n", - "print(\"Harbison 2004 Metadata Schema\")\n", - "print(\"=\" * 60)\n", - "print(f\"Regulator Fields: {harbison_schema['regulator_fields']}\")\n", - "print(f\"Target Fields: {harbison_schema['target_fields']}\")\n", - "print(f\"Condition Fields: {harbison_schema['condition_fields']}\")\n", - "\n", - "# Check for repo-level conditions\n", - "if harbison_schema.get('top_level_conditions'):\n", - " print(\"\\nRepo-Level Conditions (apply to ALL samples):\")\n", - " top_cond = harbison_schema['top_level_conditions']\n", - " if top_cond.strain_background:\n", - " print(f\" - Strain background: {top_cond.strain_background}\")\n", - " if top_cond.environmental_conditions:\n", - " print(\" - Environmental conditions defined\")\n", - "else:\n", - " print(\"\\nRepo-Level Conditions: None\")\n", - "\n", - "# Check for config-level conditions\n", - "if harbison_schema.get('config_level_conditions'):\n", - " print(\"\\nConfig-Level Conditions:\")\n", - " print(\" (Defined for this config)\")\n", - "else:\n", - " print(\"\\nConfig-Level Conditions: None\")\n", - "\n", - "# Show field-level condition definitions\n", - "print(f\"\\nField-Level Conditions (vary by sample):\")\n", - "print(f\" - condition: {len(harbison_schema['condition_definitions'].get('condition', {}))} conditions defined\")\n", - "\n", - "# Show first few condition definitions\n", - "if 'condition' in harbison_schema['condition_definitions']:\n", - " print(\"\\n Sample Conditions:\")\n", - " for cond_name in list(harbison_schema['condition_definitions']['condition'].keys())[:3]:\n", - " print(f\" - {cond_name}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Hackett 2020 Metadata Schema" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hackett 2020 Metadata Schema\n", - "============================================================\n", - "Regulator Fields: ['regulator_locus_tag', 'regulator_symbol']\n", - "Target Fields: ['target_locus_tag', 'target_symbol']\n", - "\n", - "Repo-Level Conditions (apply to ALL samples):\n", - " - Temperature: 30.0 C\n", - " - Cultivation: chemostat\n", - " - Media: minimal\n", - " - Carbon source: D-glucose @ 1.0%\n", - "\n", - "Experimental Condition Fields:\n", - " - time: values from data\n", - " - mechanism: 2 defined levels\n", - " - restriction: 3 defined levels\n", - " - date: values from data\n", - " - strain: values from data\n" - ] - } - ], - "source": [ - "# Load hackett_2020 datacard\n", - "hackett_card = DataCard(repo_id=\"BrentLab/hackett_2020\")\n", - "\n", - "# Extract metadata schema\n", - "hackett_schema = hackett_card.extract_metadata_schema(\"hackett_2020\")\n", - "\n", - "print(\"Hackett 2020 Metadata Schema\")\n", - "print(\"=\" * 60)\n", - "print(f\"Regulator Fields: {hackett_schema['regulator_fields']}\")\n", - "print(f\"Target Fields: {hackett_schema['target_fields']}\")\n", - "\n", - "# Show repo-level (top-level) experimental conditions\n", - "if hackett_schema.get('top_level_conditions'):\n", - " print(\"\\nRepo-Level Conditions (apply to ALL samples):\")\n", - " top_cond = hackett_schema['top_level_conditions']\n", - "\n", - " # Check for structured environmental_conditions\n", - " if top_cond.environmental_conditions:\n", - " env = top_cond.environmental_conditions\n", - " if env.temperature_celsius is not None:\n", - " print(f\" - Temperature: {env.temperature_celsius} C\")\n", - " if env.cultivation_method:\n", - " print(f\" - Cultivation: {env.cultivation_method}\")\n", - " if env.media:\n", - " print(f\" - Media: {env.media.name}\")\n", - " if env.media.carbon_source:\n", - " for cs in env.media.carbon_source:\n", - " print(f\" - Carbon source: {cs.compound} @ {cs.concentration_percent}%\")\n", - "\n", - " # Check for extra fields (alternate structure)\n", - " if hasattr(top_cond, 'model_extra') and top_cond.model_extra:\n", - " for key, value in top_cond.model_extra.items():\n", - " print(f\" - {key}: {value}\")\n", - "\n", - "# Show all experimental condition fields\n", - "if hackett_schema['condition_fields']:\n", - " print(\"\\nExperimental Condition Fields:\")\n", - " for field_name in hackett_schema['condition_fields']:\n", - " # Check if field has definitions (structured factor levels)\n", - " if field_name in hackett_schema['condition_definitions']:\n", - " num_levels = len(hackett_schema['condition_definitions'][field_name])\n", - " print(f\" - {field_name}: {num_levels} defined levels\")\n", - " else:\n", - " print(f\" - {field_name}: values from data\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Comparing Schemas\n", - "\n", - "Notice:\n", - "- **Common fields**: Both have `regulator_locus_tag` and `regulator_symbol` (aligned by `role=regulator_identifier`)\n", - "- **Different condition fields**: harbison has `condition`, hackett has `time`, `mechanism`, `restriction`\n", - "- **Three-level condition hierarchy**:\n", - " - **Repo-level**: Conditions in `dataset_card.experimental_conditions` apply to ALL configs/samples\n", - " - Example: hackett_2020 has temperature (30°C), cultivation method (chemostat), base media (minimal)\n", - " - **Config-level**: Conditions in `config.experimental_conditions` apply to all samples in that config\n", - " - Example: harbison_2004 has strain background defined at config level\n", - " - **Field-level**: Conditions in field `definitions` vary per sample (factor levels)\n", - " - Example: harbison_2004's \"condition\" field with YPD, RAPA, HEAT definitions" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Part 2: Cross-Dataset Metadata with MetadataManager\n", - "\n", - "Now let's use `MetadataManager` to query metadata across both datasets." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Initialize MetadataManager" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "MetadataManager initialized\n", - "Cache enabled: False\n", - "Registered datasets: 0\n" - ] - } - ], - "source": [ - "# Create metadata manager (session-only by default, no caching)\n", - "mgr = MetadataManager()\n", - "\n", - "print(\"MetadataManager initialized\")\n", - "print(f\"Cache enabled: {mgr._cache_enabled}\")\n", - "print(f\"Registered datasets: {len(mgr._registered_datasets)}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Register Datasets\n", - "\n", - "Register both datasets for cross-dataset querying. The manager will:\n", - "1. Load each DataCard\n", - "2. Extract metadata schema\n", - "3. Create DuckDB temporary views\n", - "4. Build unified view with column alignment" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Registering BrentLab/harbison_2004...\n", - "Registering BrentLab/hackett_2020...\n", - "\n", - "Registration complete!\n", - "Total registered datasets: 2\n", - "Active configs: [('BrentLab/harbison_2004', 'harbison_2004'), ('BrentLab/hackett_2020', 'hackett_2020')]\n" - ] - } - ], - "source": [ - "# Register harbison_2004\n", - "print(\"Registering BrentLab/harbison_2004...\")\n", - "mgr.register(\"BrentLab/harbison_2004\")\n", - "\n", - "# Register hackett_2020 \n", - "print(\"Registering BrentLab/hackett_2020...\")\n", - "mgr.register(\"BrentLab/hackett_2020\")\n", - "\n", - "print(\"\\nRegistration complete!\")\n", - "print(f\"Total registered datasets: {len(mgr._registered_datasets)}\")\n", - "print(f\"Active configs: {mgr.get_active_configs()}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### View Summary\n", - "\n", - "Get an overview of registered datasets and their configs." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Registered Datasets Summary\n", - "============================================================\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "dataset", - "rawType": "object", - "type": "string" - }, - { - "name": "config_name", - "rawType": "object", - "type": "string" - }, - { - "name": "view_name", - "rawType": "object", - "type": "string" - } - ], - "ref": "963ab794-8ad8-4c6c-967b-17439c5c0921", - "rows": [ - [ - "0", - "BrentLab/harbison_2004", - "harbison_2004", - "BrentLab_harbison_2004_harbison_2004_metadata" - ], - [ - "1", - "BrentLab/hackett_2020", - "hackett_2020", - "BrentLab_hackett_2020_hackett_2020_metadata" - ] - ], - "shape": { - "columns": 3, - "rows": 2 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
datasetconfig_nameview_name
0BrentLab/harbison_2004harbison_2004BrentLab_harbison_2004_harbison_2004_metadata
1BrentLab/hackett_2020hackett_2020BrentLab_hackett_2020_hackett_2020_metadata
\n", - "
" - ], - "text/plain": [ - " dataset config_name \\\n", - "0 BrentLab/harbison_2004 harbison_2004 \n", - "1 BrentLab/hackett_2020 hackett_2020 \n", - "\n", - " view_name \n", - "0 BrentLab_harbison_2004_harbison_2004_metadata \n", - "1 BrentLab_hackett_2020_hackett_2020_metadata " - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Get summary of registered datasets\n", - "summary = mgr.get_summary()\n", - "\n", - "print(\"Registered Datasets Summary\")\n", - "print(\"=\" * 60)\n", - "display(summary)" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Three-Level Experimental Condition Hierarchy\n", - "================================================================================\n", - "\n", - "Example: hackett_2020 dataset\n", - "--------------------------------------------------------------------------------\n", - "\n", - "[1] REPO-LEVEL (DatasetCard.experimental_conditions)\n", - " Applies to: ALL configs and ALL samples in the repository\n", - " - temperature_celsius: 30.0\n", - " - cultivation_method: chemostat\n", - " - media.name: minimal\n", - " - media.carbon_source: D-glucose @ 1.0%\n", - "\n", - "[2] CONFIG-LEVEL (DatasetConfig.experimental_conditions)\n", - " Applies to: All samples in the 'hackett_2020' config\n", - " - (None - all repo-level conditions inherited)\n", - "\n", - "[3] FIELD-LEVEL (FeatureInfo.definitions)\n", - " Applies to: Individual samples based on field value\n", - " - restriction field: 3 levels (P, N, M)\n", - " - P: Phosphate limitation\n", - " - N: Nitrogen limitation\n", - " - M: Magnesium limitation\n", - " - time field: Various time points (30.0, 60.0, etc.)\n", - "\n", - "================================================================================\n", - "KEY CONCEPT: Hierarchy Merging\n", - "--------------------------------------------------------------------------------\n", - "When MetadataManager creates metadata tables, it merges all three levels:\n", - " - Repo-level conditions -> columns for ALL samples (temperature: 30C, etc.)\n", - " - Config-level conditions -> columns for config samples (override repo-level if present)\n", - " - Field-level conditions -> vary by sample (restriction: P/N/M)\n", - "\n", - "Precedence: Field-level > Config-level > Repo-level\n", - "\n", - "Result: Each sample gets columns from all applicable levels!\n" - ] - } - ], - "source": [ - "print(\"Three-Level Experimental Condition Hierarchy\")\n", - "print(\"=\" * 80)\n", - "\n", - "# Show hackett_2020 as example\n", - "print(\"\\nExample: hackett_2020 dataset\")\n", - "print(\"-\" * 80)\n", - "\n", - "# 1. Repo-level (applies to ALL samples)\n", - "print(\"\\n[1] REPO-LEVEL (DatasetCard.experimental_conditions)\")\n", - "print(\" Applies to: ALL configs and ALL samples in the repository\")\n", - "if hackett_schema.get('top_level_conditions'):\n", - " top_cond = hackett_schema['top_level_conditions']\n", - " \n", - " # Check structured environmental_conditions\n", - " if top_cond.environmental_conditions:\n", - " env = top_cond.environmental_conditions\n", - " print(f\" - temperature_celsius: {env.temperature_celsius}\")\n", - " print(f\" - cultivation_method: {env.cultivation_method}\")\n", - " print(f\" - media.name: {env.media.name}\")\n", - " print(f\" - media.carbon_source: {env.media.carbon_source[0].compound} @ {env.media.carbon_source[0].concentration_percent}%\")\n", - " \n", - " # Check extra fields (alternate structure used by some datacards)\n", - " if hasattr(top_cond, 'model_extra') and top_cond.model_extra:\n", - " for key, value in top_cond.model_extra.items():\n", - " print(f\" - {key}: {value}\")\n", - "\n", - "# 2. Config-level (applies to samples in this config)\n", - "print(\"\\n[2] CONFIG-LEVEL (DatasetConfig.experimental_conditions)\")\n", - "print(\" Applies to: All samples in the 'hackett_2020' config\")\n", - "if hackett_schema.get('config_level_conditions'):\n", - " print(\" - (Specific conditions defined)\")\n", - "else:\n", - " print(\" - (None - all repo-level conditions inherited)\")\n", - "\n", - "# 3. Field-level (varies by sample)\n", - "print(\"\\n[3] FIELD-LEVEL (FeatureInfo.definitions)\")\n", - "print(\" Applies to: Individual samples based on field value\")\n", - "print(\" - restriction field: 3 levels (P, N, M)\")\n", - "print(\" - P: Phosphate limitation\")\n", - "print(\" - N: Nitrogen limitation\")\n", - "print(\" - M: Magnesium limitation\")\n", - "print(\" - time field: Various time points (30.0, 60.0, etc.)\")\n", - "\n", - "print(\"\\n\" + \"=\" * 80)\n", - "print(\"KEY CONCEPT: Hierarchy Merging\")\n", - "print(\"-\" * 80)\n", - "print(\"When MetadataManager creates metadata tables, it merges all three levels:\")\n", - "print(\" - Repo-level conditions -> columns for ALL samples (temperature: 30C, etc.)\")\n", - "print(\" - Config-level conditions -> columns for config samples (override repo-level if present)\")\n", - "print(\" - Field-level conditions -> vary by sample (restriction: P/N/M)\")\n", - "print(\"\\nPrecedence: Field-level > Config-level > Repo-level\")\n", - "print(\"\\nResult: Each sample gets columns from all applicable levels!\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Part 2.5: Understanding the Three-Level Condition Hierarchy\n", - "\n", - "Experimental conditions can be specified at three different levels in the hierarchy. Let's examine how this works with a concrete example from hackett_2020." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Part 3: Querying Across Datasets (Placeholder)\n", - "\n", - "**Note**: The current implementation has a placeholder for actual parquet file loading from HuggingFace. The examples below show the intended usage once parquet integration is complete.\n", - "\n", - "### Example Query 1: Find all regulators across datasets" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "WARNING: Parquet file loading not yet implemented\n", - " This query will work once HuggingFace parquet integration is added\n" - ] - } - ], - "source": [ - "# Example query (will work once parquet loading is implemented)\n", - "# \n", - "# regulators_query = \"\"\"\n", - "# SELECT DISTINCT \n", - "# dataset,\n", - "# config_name,\n", - "# regulator_symbol,\n", - "# regulator_locus_tag\n", - "# FROM unified_metadata\n", - "# WHERE regulator_symbol != 'unspecified'\n", - "# ORDER BY dataset, regulator_symbol\n", - "# \"\"\"\n", - "# \n", - "# regulators_df = mgr.query(regulators_query)\n", - "# print(f\"\\nFound {len(regulators_df)} unique regulators across datasets\")\n", - "# display(regulators_df.head(10))\n", - "\n", - "print(\"WARNING: Parquet file loading not yet implemented\")\n", - "print(\" This query will work once HuggingFace parquet integration is added\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Example Query 2: Filter by specific regulator" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "WARNING: Parquet file loading not yet implemented\n" - ] - } - ], - "source": [ - "# Example: Find GLN3 samples across both datasets\n", - "#\n", - "# gln3_query = \"\"\"\n", - "# SELECT \n", - "# dataset,\n", - "# sample_id,\n", - "# regulator_symbol,\n", - "# condition,\n", - "# time,\n", - "# restriction,\n", - "# growth_media,\n", - "# components\n", - "# FROM unified_metadata\n", - "# WHERE regulator_symbol = 'GLN3'\n", - "# \"\"\"\n", - "# \n", - "# gln3_samples = mgr.query(gln3_query)\n", - "# print(f\"\\nGLN3 samples: {len(gln3_samples)}\")\n", - "# display(gln3_samples)\n", - "\n", - "print(\"WARNING: Parquet file loading not yet implemented\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Example Query 3: Search by media components" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "WARNING: Parquet file loading not yet implemented\n", - " This demonstrates searching by specific media components with concentrations\n" - ] - } - ], - "source": [ - "# Example: Find samples with D-glucose at 2%\n", - "#\n", - "# glucose_query = \"\"\"\n", - "# SELECT \n", - "# dataset,\n", - "# COUNT(*) as sample_count,\n", - "# growth_media\n", - "# FROM unified_metadata\n", - "# WHERE components LIKE '%carbon_source:D-glucose@2%'\n", - "# GROUP BY dataset, growth_media\n", - "# \"\"\"\n", - "# \n", - "# glucose_samples = mgr.query(glucose_query)\n", - "# print(\"\\nSamples with D-glucose at 2%:\")\n", - "# display(glucose_samples)\n", - "\n", - "print(\"WARNING: Parquet file loading not yet implemented\")\n", - "print(\" This demonstrates searching by specific media components with concentrations\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Part 4: Understanding Metadata Flattening\n", - "\n", - "The MetadataManager flattens condition definitions into searchable fields using known separators." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Separator Conventions" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Component Separator Conventions\n", - "============================================================\n", - " type_value ':'\n", - " value_conc '@'\n", - " components ';'\n", - " types '|'\n", - "\n", - "Usage Examples:\n", - " growth_media: 'YPD' (simple name)\n", - " components: 'carbon_source:D-glucose@2%|nitrogen_source:yeast_extract@1%;peptone@2%'\n", - "\n", - " Breakdown:\n", - " - ':' separates type from value (carbon_source:D-glucose)\n", - " - '@' separates value from concentration (D-glucose@2%)\n", - " - ';' separates multiple compounds of same type (yeast_extract@1%;peptone@2%)\n", - " - '|' separates different component types (carbon | nitrogen)\n" - ] - } - ], - "source": [ - "from tfbpapi.datainfo.metadata_manager import COMPONENT_SEPARATORS\n", - "\n", - "print(\"\\nComponent Separator Conventions\")\n", - "print(\"=\" * 60)\n", - "for key, sep in COMPONENT_SEPARATORS.items():\n", - " print(f\" {key:20s} '{sep}'\")\n", - " \n", - "print(\"\\nUsage Examples:\")\n", - "print(\" growth_media: 'YPD' (simple name)\")\n", - "print(\" components: 'carbon_source:D-glucose@2%|nitrogen_source:yeast_extract@1%;peptone@2%'\")\n", - "print(\"\\n Breakdown:\")\n", - "print(\" - ':' separates type from value (carbon_source:D-glucose)\")\n", - "print(\" - '@' separates value from concentration (D-glucose@2%)\")\n", - "print(\" - ';' separates multiple compounds of same type (yeast_extract@1%;peptone@2%)\")\n", - "print(\" - '|' separates different component types (carbon | nitrogen)\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Example: Flattening a Condition Definition\n", - "\n", - "Let's manually flatten a condition definition to see how it works." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Original YPD Condition Definition\n", - "============================================================\n", - "Description: Rich media baseline condition\n", - "\n", - "Flattened Representation\n", - "============================================================\n", - "growth_media: 'unspecified'\n", - "components: ''\n", - "\n", - "This format enables SQL searches like:\n", - " WHERE growth_media = 'YPD'\n", - " WHERE components LIKE '%carbon_source:D-glucose@2%'\n", - " WHERE components LIKE '%nitrogen_source:yeast_extract%'\n" - ] - } - ], - "source": [ - "# Get YPD condition definition from harbison_2004\n", - "ypd_definition = harbison_schema['condition_definitions']['condition']['YPD']\n", - "\n", - "print(\"Original YPD Condition Definition\")\n", - "print(\"=\" * 60)\n", - "print(f\"Description: {ypd_definition.get('description', 'N/A')}\")\n", - "\n", - "if 'environmental_conditions' in ypd_definition:\n", - " env = ypd_definition['environmental_conditions']\n", - " print(f\"\\nTemperature: {env.get('temperature_celsius', 'N/A')}°C\")\n", - " \n", - " if 'media' in env:\n", - " media = env['media']\n", - " print(f\"Media Name: {media.get('name', 'N/A')}\")\n", - " print(f\"\\nCarbon Source:\")\n", - " for cs in media.get('carbon_source', []):\n", - " print(f\" - {cs.get('compound', cs)}: {cs.get('concentration_percent', 'N/A')}%\")\n", - " print(f\"\\nNitrogen Source:\")\n", - " for ns in media.get('nitrogen_source', []):\n", - " print(f\" - {ns.get('compound', ns)}: {ns.get('concentration_percent', 'N/A')}%\")\n", - "\n", - "# Flatten using MetadataManager method\n", - "flattened = mgr._flatten_condition_definition(ypd_definition)\n", - "\n", - "print(\"\\nFlattened Representation\")\n", - "print(\"=\" * 60)\n", - "print(f\"growth_media: '{flattened['growth_media']}'\")\n", - "print(f\"components: '{flattened['components']}'\")\n", - "\n", - "print(\"\\nThis format enables SQL searches like:\")\n", - "print(\" WHERE growth_media = 'YPD'\")\n", - "print(\" WHERE components LIKE '%carbon_source:D-glucose@2%'\")\n", - "print(\" WHERE components LIKE '%nitrogen_source:yeast_extract%'\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Part 5: Schema Heterogeneity Handling\n", - "\n", - "Different datasets have different metadata fields. MetadataManager handles this by:\n", - "1. **Role-based alignment**: Fields grouped by semantic role (regulator_identifier, experimental_condition)\n", - "2. **Column defaulting**: Missing columns default to \"unspecified\" in unified view\n", - "3. **UNION ALL**: All views combined with column alignment" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Schema Heterogeneity Example\n", - "============================================================\n", - "\n", - "Harbison 2004 has:\n", - " - condition field (14 levels: YPD, RAPA, HEAT, etc.)\n", - " - NO time, mechanism, restriction fields\n", - "\n", - "Hackett 2020 has:\n", - " - time field (time points in minutes)\n", - " - mechanism field (GEV, ZEV induction systems)\n", - " - restriction field (P, N, M nutrient limitations)\n", - " - NO condition field\n", - "\n", - "In unified view:\n", - " - Harbison rows: condition='YPD', time='unspecified', restriction='unspecified'\n", - " - Hackett rows: condition='unspecified', time=30.0, restriction='P'\n", - "\n", - " Both can be queried together:\n", - " SELECT * FROM unified_metadata WHERE regulator_symbol = 'GLN3'\n", - " Returns samples from BOTH datasets!\n" - ] - } - ], - "source": [ - "print(\"Schema Heterogeneity Example\")\n", - "print(\"=\" * 60)\n", - "\n", - "print(\"\\nHarbison 2004 has:\")\n", - "print(f\" - condition field (14 levels: YPD, RAPA, HEAT, etc.)\")\n", - "print(f\" - NO time, mechanism, restriction fields\")\n", - "\n", - "print(\"\\nHackett 2020 has:\")\n", - "print(f\" - time field (time points in minutes)\")\n", - "print(f\" - mechanism field (GEV, ZEV induction systems)\")\n", - "print(f\" - restriction field (P, N, M nutrient limitations)\")\n", - "print(f\" - NO condition field\")\n", - "\n", - "print(\"\\nIn unified view:\")\n", - "print(\" - Harbison rows: condition='YPD', time='unspecified', restriction='unspecified'\")\n", - "print(\" - Hackett rows: condition='unspecified', time=30.0, restriction='P'\")\n", - "print(\"\\n Both can be queried together:\")\n", - "print(\" SELECT * FROM unified_metadata WHERE regulator_symbol = 'GLN3'\")\n", - "print(\" Returns samples from BOTH datasets!\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Part 6: Cleanup and Next Steps" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Unregister Datasets\n", - "\n", - "You can remove datasets from the manager when done." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Summary\n", - "\n", - "This tutorial demonstrated:\n", - "\n", - "- **Metadata schema extraction** - Using `DataCard.extract_metadata_schema()` to understand field roles\n", - "\n", - "- **Three-level condition hierarchy** - Repo-level, config-level, and field-level experimental conditions\n", - "\n", - "- **Cross-dataset registration** - Registering multiple datasets in `MetadataManager`\n", - "\n", - "- **Condition flattening** - How definitions are flattened into searchable fields with known separators\n", - "\n", - "- **Schema heterogeneity** - How different metadata schemas are aligned by role with \"unspecified\" defaults\n", - "\n", - "- **Future: SQL queries** - Once parquet loading is implemented, full cross-dataset SQL queries will work\n", - "\n", - "### Key Takeaways\n", - "\n", - "1. **Role-based alignment**: Fields are grouped by semantic role (regulator_identifier, experimental_condition), not by name\n", - "2. **Three-level hierarchy**: Conditions can be specified at repo-level (all samples), config-level (config samples), or field-level (individual samples)\n", - "3. **Hierarchy merging**: MetadataManager merges all three levels with precedence: field > config > repo\n", - "4. **Structured separators**: Media components use `:`, `@`, `;`, `|` for predictable parsing\n", - "5. **Low memory**: DuckDB temp views mean no data loading until you query\n", - "6. **Session-only default**: No caching by default (set `cache=True` if needed)\n", - "7. **Dataset + config uniqueness**: Samples identified by (dataset, config_name, sample_id) tuple\n", - "\n", - "### Understanding the Condition Hierarchy\n", - "\n", - "The three-level hierarchy allows flexible experimental condition specification:\n", - "\n", - "- **Repo-level**: Common conditions shared by all experiments (e.g., standard temperature, base strain)\n", - "- **Config-level**: Conditions specific to a dataset configuration (e.g., growth phase for a time course)\n", - "- **Field-level**: Sample-specific conditions that vary within an experiment (e.g., different media, treatments)\n", - "\n", - "This design enables:\n", - "- Efficient specification without repetition\n", - "- Clear inheritance and overriding semantics\n", - "- Consistent querying across heterogeneous datasets" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Next Steps\n", - "\n", - "Once parquet loading is implemented, you'll be able to:\n", - "\n", - "1. **Filter by regulators**: Find all samples for specific TFs across datasets\n", - "2. **Search by conditions**: Query by media, temperature, treatments, etc.\n", - "3. **Cross-dataset analysis**: Compare experimental designs across studies\n", - "4. **Metadata-driven data loading**: Use `get_active_configs()` to load actual expression/binding data\n", - "\n", - "### Integration with HfQueryAPI (Future)\n", - "\n", - "The intended workflow:\n", - "```python\n", - "# 1. Filter metadata across datasets\n", - "mgr = MetadataManager()\n", - "mgr.register(\"BrentLab/harbison_2004\")\n", - "mgr.register(\"BrentLab/hackett_2020\")\n", - "mgr.filter_by_regulator([\"GLN3\", \"GCN4\"])\n", - "mgr.filter_by_conditions(growth_media=\"YPD\")\n", - "\n", - "# 2. Get active configs\n", - "active_configs = mgr.get_active_configs()\n", - "# Returns: [('BrentLab/harbison_2004', 'harbison_2004'), ...]\n", - "\n", - "# 3. Load actual data (requires HfQueryAPI refactor)\n", - "# TBD - design after MetadataManager is complete\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Summary\n", - "\n", - "This tutorial demonstrated:\n", - "\n", - "- **Metadata schema extraction** - Using `DataCard.extract_metadata_schema()` to understand field roles\n", - "\n", - "- **Cross-dataset registration** - Registering multiple datasets in `MetadataManager`\n", - "\n", - "- **Condition flattening** - How definitions are flattened into searchable fields with known separators\n", - "\n", - "- **Schema heterogeneity** - How different metadata schemas are aligned by role with \"unspecified\" defaults\n", - "\n", - "- **Future: SQL queries** - Once parquet loading is implemented, full cross-dataset SQL queries will work\n", - "\n", - "### Key Takeaways\n", - "\n", - "1. **Role-based alignment**: Fields are grouped by semantic role (regulator_identifier, experimental_condition), not by name\n", - "2. **Structured separators**: Media components use `:`, `@`, `;`, `|` for predictable parsing\n", - "3. **Low memory**: DuckDB temp views mean no data loading until you query\n", - "4. **Session-only default**: No caching by default (set `cache=True` if needed)\n", - "5. **Dataset + config uniqueness**: Samples identified by (dataset, config_name, sample_id) tuple" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "tfbpapi-py3.11", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.9" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/tutorials/metadata_manager_tutorial.ipynb b/docs/tutorials/metadata_manager_tutorial.ipynb new file mode 100644 index 0000000..891df65 --- /dev/null +++ b/docs/tutorials/metadata_manager_tutorial.ipynb @@ -0,0 +1,586 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# MetadataManager Tutorial: Enriching Data with Condition Metadata\n", + "\n", + "This tutorial demonstrates how to create enriched metadata views by combining:\n", + "1. Actual data fields from HuggingFace datasets (via HfQueryAPI)\n", + "2. Condition metadata from DataCard definitions\n", + "\n", + "We'll use the **BrentLab/harbison_2004** ChIP-chip dataset, which has 14 experimental conditions with detailed metadata about media composition, temperature, and other environmental factors.\n", + "\n", + "## Why Enrich Metadata?\n", + "\n", + "The raw data contains a `condition` field with values like \"YPD\", \"HEAT\", \"GAL\". But to analyze data by media type or carbon source, we need to:\n", + "- Extract nested information from condition definitions\n", + "- Create queryable columns for filtering and grouping\n", + "- Join this enriched metadata with the actual measurements\n", + "\n", + "This tutorial teaches you how to:\n", + "1. Use DataCard to explore nested condition metadata\n", + "2. Manually construct SQL CASE expressions to enrich your data\n", + "3. Create metadata views that combine data with DataCard information" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1. Setup and Imports" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [], + "source": [ + "from tfbpapi.datainfo import DataCard\n", + "from tfbpapi import HfQueryAPI\n", + "import duckdb\n", + "import pandas as pd\n", + "import json\n", + "\n", + "pd.set_option('display.max_columns', None)\n", + "pd.set_option('display.width', None)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2. Explore Condition Metadata with DataCard\n", + "\n", + "First, let's load the DataCard and explore what condition metadata is available." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dataset type: DatasetType.ANNOTATED_FEATURES\n", + "Number of features: 7\n", + "\n", + "Feature names:\n", + " - condition ({'class_label': {'names': ['YPD', 'SM', 'RAPA', 'H2O2Hi', 'H2O2Lo', 'Acid', 'Alpha', 'BUT14', 'BUT90', 'Thi-', 'GAL', 'HEAT', 'Pi-', 'RAFF']}})\n", + " - regulator_locus_tag (string)\n", + " - regulator_symbol (string)\n", + " - target_locus_tag (string)\n", + " - target_symbol (string)\n", + " - effect (float64)\n", + " - pvalue (float64)\n" + ] + } + ], + "source": [ + "# Load DataCard\n", + "card = DataCard('BrentLab/harbison_2004')\n", + "\n", + "# Get the config to understand the data structure\n", + "config = card.get_config('harbison_2004')\n", + "print(f\"Dataset type: {config.dataset_type}\")\n", + "print(f\"Number of features: {len(config.dataset_info.features)}\")\n", + "print(f\"\\nFeature names:\")\n", + "for feature in config.dataset_info.features:\n", + " print(f\" - {feature.name} ({feature.dtype})\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Extract Media Specifications Using get_field_attribute()\n", + "\n", + "The new `get_field_attribute()` method makes it easy to extract nested specifications from field definitions. Let's use it to explore the media specifications for each condition." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'DataCard' object has no attribute 'get_field_attribute'", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mAttributeError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[34]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;66;03m# Extract media specifications for all conditions\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m media_specs = \u001b[43mcard\u001b[49m\u001b[43m.\u001b[49m\u001b[43mget_field_attribute\u001b[49m(\u001b[33m'\u001b[39m\u001b[33mharbison_2004\u001b[39m\u001b[33m'\u001b[39m, \u001b[33m'\u001b[39m\u001b[33mcondition\u001b[39m\u001b[33m'\u001b[39m, \u001b[33m'\u001b[39m\u001b[33mmedia\u001b[39m\u001b[33m'\u001b[39m)\n\u001b[32m 4\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mFound media specifications for \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mlen\u001b[39m(media_specs)\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m conditions\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m\"\u001b[39m)\n\u001b[32m 6\u001b[39m \u001b[38;5;66;03m# Display them in a readable format\u001b[39;00m\n", + "\u001b[31mAttributeError\u001b[39m: 'DataCard' object has no attribute 'get_field_attribute'" + ] + } + ], + "source": [ + "# Extract media specifications for all conditions\n", + "media_specs = card.get_field_attribute('harbison_2004', 'condition', 'media')\n", + "\n", + "print(f\"Found media specifications for {len(media_specs)} conditions\\n\")\n", + "\n", + "# Display them in a readable format\n", + "for condition in sorted(media_specs.keys()):\n", + " print(f\"\\n{condition}:\")\n", + " print(json.dumps(media_specs[condition], indent=2))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Understanding the Nested Structure\n", + "\n", + "Notice that each media specification contains:\n", + "- `name`: The media name (e.g., \"YPD\", \"synthetic_complete\")\n", + "- `carbon_source`: Either a list of dicts with compound/concentration, or \"unspecified\"\n", + "- `nitrogen_source`: Either a list of dicts with compound/concentration, or \"unspecified\"\n", + "- Sometimes `additives`: Additional compounds like butanol\n", + "\n", + "To create queryable metadata columns, we need to flatten these nested structures." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3. Load Data with HfQueryAPI\n", + "\n", + "Now let's load the actual data using HfQueryAPI. We'll create a shared DuckDB connection that we can use for building our enriched metadata view." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Create shared DuckDB connection\n", + "conn = duckdb.connect(':memory:')\n", + "\n", + "# Initialize HfQueryAPI with shared connection\n", + "api = HfQueryAPI('BrentLab/harbison_2004', duckdb_conn=conn)\n", + "\n", + "# Load the harbison_2004 config data\n", + "api.load_config('harbison_2004')\n", + "\n", + "print(\"Data loaded successfully!\")\n", + "print(\"\\nAvailable views in DuckDB:\")\n", + "views = conn.execute(\"SHOW TABLES\").fetchall()\n", + "for view in views:\n", + " print(f\" - {view[0]}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Examine the Base Data\n", + "\n", + "Let's look at the structure of the base data to see what fields are available." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Query the base view\n", + "base_df = conn.execute(\"\"\"\n", + " SELECT *\n", + " FROM harbison_2004_train\n", + " LIMIT 5\n", + "\"\"\").df()\n", + "\n", + "print(f\"Base data shape: {base_df.shape}\")\n", + "print(f\"\\nColumn names: {list(base_df.columns)}\")\n", + "print(\"\\nSample rows:\")\n", + "display(base_df)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Check unique conditions in the data\n", + "conditions_df = conn.execute(\"\"\"\n", + " SELECT DISTINCT condition\n", + " FROM harbison_2004_train\n", + " ORDER BY condition\n", + "\"\"\").df()\n", + "\n", + "print(f\"Unique conditions in data: {len(conditions_df)}\")\n", + "print(conditions_df['condition'].tolist())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4. Flatten Media Specifications\n", + "\n", + "Before we can build SQL CASE expressions, we need to flatten the nested media specifications into simple strings. We'll create helper functions to extract:\n", + "- Media name\n", + "- Carbon source (as comma-separated compound names)\n", + "- Nitrogen source (as comma-separated compound names)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def flatten_carbon_source(media_spec):\n", + " \"\"\"Extract carbon source as comma-separated string.\"\"\"\n", + " if media_spec == 'unspecified':\n", + " return 'unspecified'\n", + " \n", + " carbon = media_spec.get('carbon_source', 'unspecified')\n", + " if carbon == 'unspecified':\n", + " return 'unspecified'\n", + " \n", + " if isinstance(carbon, list):\n", + " compounds = [c['compound'] for c in carbon]\n", + " return ', '.join(compounds)\n", + " \n", + " return str(carbon)\n", + "\n", + "def flatten_nitrogen_source(media_spec):\n", + " \"\"\"Extract nitrogen source as comma-separated string.\"\"\"\n", + " if media_spec == 'unspecified':\n", + " return 'unspecified'\n", + " \n", + " nitrogen = media_spec.get('nitrogen_source', 'unspecified')\n", + " if nitrogen == 'unspecified':\n", + " return 'unspecified'\n", + " \n", + " if isinstance(nitrogen, list):\n", + " compounds = [n['compound'] for n in nitrogen]\n", + " return ', '.join(compounds)\n", + " \n", + " return str(nitrogen)\n", + "\n", + "# Create flattened mapping for all conditions\n", + "flattened_media = {}\n", + "for condition, media_spec in media_specs.items():\n", + " flattened_media[condition] = {\n", + " 'media_name': media_spec.get('name', 'unspecified') if media_spec != 'unspecified' else 'unspecified',\n", + " 'carbon_source': flatten_carbon_source(media_spec),\n", + " 'nitrogen_source': flatten_nitrogen_source(media_spec)\n", + " }\n", + "\n", + "# Display flattened mapping\n", + "print(\"Flattened media specifications:\\n\")\n", + "for condition in sorted(flattened_media.keys()):\n", + " print(f\"{condition}:\")\n", + " for key, value in flattened_media[condition].items():\n", + " print(f\" {key}: {value}\")\n", + " print()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 5. Build SQL CASE Expressions Manually\n", + "\n", + "Now we'll construct SQL CASE expressions to map each condition value to its media specifications. This is the core of creating enriched metadata." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Build CASE expression for media_name\n", + "media_name_cases = []\n", + "for condition, specs in sorted(flattened_media.items()):\n", + " media_name_cases.append(f\" WHEN '{condition}' THEN '{specs['media_name']}'\")\n", + "media_name_sql = \"\\n\".join(media_name_cases)\n", + "\n", + "# Build CASE expression for carbon_source\n", + "carbon_source_cases = []\n", + "for condition, specs in sorted(flattened_media.items()):\n", + " carbon_source_cases.append(f\" WHEN '{condition}' THEN '{specs['carbon_source']}'\")\n", + "carbon_source_sql = \"\\n\".join(carbon_source_cases)\n", + "\n", + "# Build CASE expression for nitrogen_source\n", + "nitrogen_source_cases = []\n", + "for condition, specs in sorted(flattened_media.items()):\n", + " nitrogen_source_cases.append(f\" WHEN '{condition}' THEN '{specs['nitrogen_source']}'\")\n", + "nitrogen_source_sql = \"\\n\".join(nitrogen_source_cases)\n", + "\n", + "# Construct the complete CREATE VIEW SQL\n", + "create_view_sql = f\"\"\"\n", + "CREATE OR REPLACE VIEW harbison_2004_enriched_metadata AS\n", + "SELECT\n", + " sample_id,\n", + " regulator_locus_tag,\n", + " regulator_symbol,\n", + " condition,\n", + " CASE condition\n", + "{media_name_sql}\n", + " END AS media_name,\n", + " CASE condition\n", + "{carbon_source_sql}\n", + " END AS carbon_source,\n", + " CASE condition\n", + "{nitrogen_source_sql}\n", + " END AS nitrogen_source\n", + "FROM harbison_2004_train\n", + "\"\"\"\n", + "\n", + "print(\"Generated SQL:\")\n", + "print(\"=\"*80)\n", + "print(create_view_sql)\n", + "print(\"=\"*80)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 6. Execute SQL and Create Enriched Metadata View\n", + "\n", + "Now let's execute the SQL to create our enriched metadata view." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Execute the CREATE VIEW statement\n", + "conn.execute(create_view_sql)\n", + "\n", + "print(\"Enriched metadata view created successfully!\")\n", + "print(\"\\nAvailable views:\")\n", + "views = conn.execute(\"SHOW TABLES\").fetchall()\n", + "for view in views:\n", + " print(f\" - {view[0]}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Query the Enriched Metadata View\n", + "\n", + "Let's examine the enriched metadata to see the results." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Query enriched metadata\n", + "enriched_df = conn.execute(\"\"\"\n", + " SELECT *\n", + " FROM harbison_2004_enriched_metadata\n", + " LIMIT 10\n", + "\"\"\").df()\n", + "\n", + "print(f\"Enriched metadata shape: {enriched_df.shape}\")\n", + "print(\"\\nSample rows:\")\n", + "display(enriched_df)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 7. Practical Examples: Querying with Enriched Metadata\n", + "\n", + "Now that we have enriched metadata, we can perform analyses that wouldn't be possible with just the raw condition values." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example 1: Find All Experiments with D-glucose as Carbon Source" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "glucose_df = conn.execute(\"\"\"\n", + " SELECT DISTINCT\n", + " condition,\n", + " media_name,\n", + " carbon_source,\n", + " nitrogen_source,\n", + " COUNT(*) as sample_count\n", + " FROM harbison_2004_enriched_metadata\n", + " WHERE carbon_source LIKE '%D-glucose%'\n", + " GROUP BY condition, media_name, carbon_source, nitrogen_source\n", + " ORDER BY condition\n", + "\"\"\").df()\n", + "\n", + "print(f\"Found {len(glucose_df)} condition(s) with D-glucose\")\n", + "display(glucose_df)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example 2: Compare Rich Media vs Synthetic Media Experiments" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "media_comparison_df = conn.execute(\"\"\"\n", + " SELECT\n", + " CASE\n", + " WHEN media_name LIKE '%YPD%' OR media_name LIKE '%yeast_extract%'\n", + " THEN 'Rich Media'\n", + " WHEN media_name LIKE '%synthetic%'\n", + " THEN 'Synthetic Media'\n", + " ELSE 'Other'\n", + " END AS media_type,\n", + " COUNT(DISTINCT condition) as num_conditions,\n", + " COUNT(DISTINCT sample_id) as num_samples,\n", + " COUNT(DISTINCT regulator_symbol) as num_regulators\n", + " FROM harbison_2004_enriched_metadata\n", + " GROUP BY media_type\n", + " ORDER BY media_type\n", + "\"\"\").df()\n", + "\n", + "print(\"Media type comparison:\")\n", + "display(media_comparison_df)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example 3: Analyze Carbon Source Diversity" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "carbon_diversity_df = conn.execute(\"\"\"\n", + " SELECT\n", + " carbon_source,\n", + " COUNT(DISTINCT condition) as num_conditions,\n", + " COUNT(DISTINCT sample_id) as num_samples,\n", + " STRING_AGG(DISTINCT condition, ', ' ORDER BY condition) as conditions\n", + " FROM harbison_2004_enriched_metadata\n", + " GROUP BY carbon_source\n", + " ORDER BY num_conditions DESC\n", + "\"\"\").df()\n", + "\n", + "print(\"Carbon source diversity:\")\n", + "display(carbon_diversity_df)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example 4: Find Regulators Tested Across Multiple Carbon Sources" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "regulator_diversity_df = conn.execute(\"\"\"\n", + " SELECT\n", + " regulator_symbol,\n", + " regulator_locus_tag,\n", + " COUNT(DISTINCT carbon_source) as num_carbon_sources,\n", + " COUNT(DISTINCT condition) as num_conditions,\n", + " STRING_AGG(DISTINCT carbon_source, ', ') as carbon_sources_tested\n", + " FROM harbison_2004_enriched_metadata\n", + " GROUP BY regulator_symbol, regulator_locus_tag\n", + " HAVING COUNT(DISTINCT carbon_source) > 1\n", + " ORDER BY num_carbon_sources DESC\n", + " LIMIT 20\n", + "\"\"\").df()\n", + "\n", + "print(f\"Found {len(regulator_diversity_df)} regulators tested across multiple carbon sources\")\n", + "print(\"\\nTop 20 regulators by carbon source diversity:\")\n", + "display(regulator_diversity_df)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 8. Summary and Key Takeaways\n", + "\n", + "In this tutorial, we learned how to:\n", + "\n", + "1. **Explore nested metadata** using DataCard's `get_field_attribute()` method\n", + "2. **Flatten nested structures** into queryable strings\n", + "3. **Build SQL CASE expressions** manually to map condition values to metadata attributes\n", + "4. **Create enriched metadata views** that combine data fields with DataCard information\n", + "5. **Query by metadata attributes** that aren't present in the raw data\n", + "\n", + "### When to Use This Approach\n", + "\n", + "Use this pattern when:\n", + "- You need to analyze data by attributes that are defined in the DataCard but not in the data itself\n", + "- You want full control over the SQL being generated\n", + "- You're working with a single dataset and need custom metadata columns\n", + "\n", + "### Next Steps\n", + "\n", + "- Explore other attributes like `temperature_celsius` or `cultivation_method`\n", + "- Combine multiple attributes in your enriched views\n", + "- Use this pattern with other datasets in the BrentLab collection\n", + "- Build reusable helper functions for your specific analysis needs" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.0" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/tutorials/sample_manager_tutorial.ipynb b/docs/tutorials/sample_manager_tutorial.ipynb new file mode 100644 index 0000000..f049afa --- /dev/null +++ b/docs/tutorials/sample_manager_tutorial.ipynb @@ -0,0 +1,603 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# SampleManager Tutorial: Node-Based Filtering Across Heterogeneous Datasets\n", + "\n", + "This tutorial introduces `SampleManager`, a flexible node-based system for filtering samples across multiple datasets with varying experimental condition structures.\n", + "\n", + "## Why SampleManager?\n", + "\n", + "Traditional table-based approaches struggle with heterogeneous metadata:\n", + "- Different datasets structure conditions differently (e.g., `media.carbon_source` vs `environmental_conditions.media.carbon_source`)\n", + "- Missing fields and optional properties vary by dataset\n", + "- Multi-level hierarchies (repo/config/field) require complex joins\n", + "\n", + "**SampleManager** solves this by:\n", + "- Representing each sample as a node with dynamically discovered properties\n", + "- Supporting flexible MongoDB-style queries across heterogeneous structures\n", + "- Enabling cross-dataset filtering and set operations\n", + "- Respecting hierarchical overrides (field > config > repo)\n", + "\n", + "## Key Concepts\n", + "\n", + "- **SampleNode**: A sample with flattened properties (experimental conditions + metadata)\n", + "- **ActiveSet**: A filtered collection of sample IDs supporting set operations\n", + "- **Query Language**: MongoDB-style filters with operators like `$contains`, `$gte`, `$or`\n", + "- **Property Discovery**: No hardcoded schemas - properties are discovered from the data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1. Setup and Basic Usage" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from tfbpapi.datainfo.sample_manager import SampleManager, SampleNode\n", + "from tfbpapi.datainfo import DataCard\n", + "import json\n", + "\n", + "# Initialize manager\n", + "manager = SampleManager()\n", + "print(manager)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2. Understanding Property Discovery\n", + "\n", + "Let's explore how SampleManager discovers properties from a DataCard without hardcoded schemas." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Load a DataCard to see its experimental conditions structure\n", + "card = DataCard('BrentLab/harbison_2004')\n", + "\n", + "# Get repo-level conditions\n", + "repo_conditions = card.get_experimental_conditions('harbison_2004')\n", + "print(\"Repo-level conditions:\")\n", + "print(json.dumps(repo_conditions, indent=2, default=str))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Get field-level condition definitions\n", + "field_defs = card.get_field_definitions('harbison_2004', 'condition')\n", + "\n", + "# Show one condition definition\n", + "print(\"\\nField-level definition for 'YPD':\")\n", + "print(json.dumps(field_defs.get('YPD', {}), indent=2, default=str))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### How Properties Are Flattened\n", + "\n", + "SampleManager recursively flattens nested structures using dot notation:\n", + "\n", + "```python\n", + "# Original nested structure:\n", + "{\n", + " \"environmental_conditions\": {\n", + " \"media\": {\n", + " \"name\": \"YPD\",\n", + " \"carbon_source\": [{\"compound\": \"D-glucose\", \"concentration_percent\": 2}]\n", + " },\n", + " \"temperature_celsius\": 30\n", + " }\n", + "}\n", + "\n", + "# Becomes flattened properties:\n", + "{\n", + " \"environmental_conditions.media.name\": \"YPD\",\n", + " \"environmental_conditions.media.carbon_source\": \"D-glucose\", # Simple string\n", + " \"_environmental_conditions.media.carbon_source_structured\": [{...}], # Full structure\n", + " \"environmental_conditions.temperature_celsius\": 30\n", + "}\n", + "```\n", + "\n", + "**Key Points:**\n", + "- Nested dicts become dot-notation keys\n", + "- Lists of dicts (compounds) get both simple and `_structured` versions\n", + "- No hardcoded field names - discovers whatever exists\n", + "- Different datasets can have different structures" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3. Creating Sample Nodes Manually\n", + "\n", + "Before we load from DataCard, let's manually create nodes to understand the structure." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from tfbpapi.datainfo.sample_manager import SampleNode, ConditionFlattener\n", + "\n", + "# Simulate experimental conditions from different levels\n", + "repo_conditions = {\n", + " \"environmental_conditions\": {\n", + " \"temperature_celsius\": 30,\n", + " \"cultivation_method\": \"liquid_culture\"\n", + " },\n", + " \"strain_background\": \"BY4741\"\n", + "}\n", + "\n", + "field_conditions = {\n", + " \"environmental_conditions\": {\n", + " \"media\": {\n", + " \"name\": \"YPD\",\n", + " \"carbon_source\": [\n", + " {\"compound\": \"D-glucose\", \"concentration_percent\": 2}\n", + " ],\n", + " \"nitrogen_source\": [\n", + " {\"compound\": \"yeast_extract\", \"concentration_percent\": 1},\n", + " {\"compound\": \"peptone\", \"concentration_percent\": 2}\n", + " ]\n", + " }\n", + " }\n", + "}\n", + "\n", + "# Flatten conditions\n", + "properties, sources = ConditionFlattener.flatten_conditions(\n", + " repo_conditions, None, field_conditions\n", + ")\n", + "\n", + "print(\"Flattened properties:\")\n", + "for key, value in sorted(properties.items()):\n", + " source = sources.get(key, 'unknown')\n", + " # Skip structured versions for cleaner output\n", + " if not key.startswith('_'):\n", + " print(f\" {key}: {value} (from {source})\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Create a sample node\n", + "node = SampleNode(\n", + " sample_id=\"sample_001\",\n", + " repo_id=\"BrentLab/harbison_2004\",\n", + " config_name=\"harbison_2004\",\n", + " properties=properties,\n", + " property_sources=sources\n", + ")\n", + "\n", + "print(f\"Node: {node}\")\n", + "print(f\"Global ID: {node.global_id()}\")\n", + "print(f\"\\nSample properties:\")\n", + "print(f\" Temperature: {node.get_property('environmental_conditions.temperature_celsius')}\")\n", + "print(f\" Carbon source: {node.get_property('environmental_conditions.media.carbon_source')}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4. Filtering with the Query Language\n", + "\n", + "SampleManager uses MongoDB-style queries for flexible filtering." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from tfbpapi.datainfo.sample_manager import SampleFilter\n", + "\n", + "# Simple equality\n", + "query1 = {\"environmental_conditions.temperature_celsius\": 30}\n", + "print(f\"Query 1 matches: {SampleFilter.matches(node, query1)}\")\n", + "\n", + "# Contains check (case-insensitive)\n", + "query2 = {\"environmental_conditions.media.carbon_source\": {\"$contains\": \"glucose\"}}\n", + "print(f\"Query 2 matches: {SampleFilter.matches(node, query2)}\")\n", + "\n", + "# Numeric comparison\n", + "query3 = {\"environmental_conditions.temperature_celsius\": {\"$gte\": 25, \"$lte\": 35}}\n", + "print(f\"Query 3 matches: {SampleFilter.matches(node, query3)}\")\n", + "\n", + "# Logical OR\n", + "query4 = {\n", + " \"$or\": [\n", + " {\"environmental_conditions.media.carbon_source\": {\"$contains\": \"galactose\"}},\n", + " {\"environmental_conditions.media.carbon_source\": {\"$contains\": \"glucose\"}}\n", + " ]\n", + "}\n", + "print(f\"Query 4 matches: {SampleFilter.matches(node, query4)}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Available Query Operators\n", + "\n", + "| Operator | Description | Example |\n", + "|----------|-------------|----------|\n", + "| `$eq` | Equal (default) | `{\"temp\": 30}` or `{\"temp\": {\"$eq\": 30}}` |\n", + "| `$ne` | Not equal | `{\"temp\": {\"$ne\": 30}}` |\n", + "| `$gt` | Greater than | `{\"temp\": {\"$gt\": 30}}` |\n", + "| `$gte` | Greater than or equal | `{\"temp\": {\"$gte\": 30}}` |\n", + "| `$lt` | Less than | `{\"temp\": {\"$lt\": 30}}` |\n", + "| `$lte` | Less than or equal | `{\"temp\": {\"$lte\": 30}}` |\n", + "| `$in` | In list | `{\"strain\": {\"$in\": [\"BY4741\", \"W303\"]}}` |\n", + "| `$nin` | Not in list | `{\"strain\": {\"$nin\": [\"BY4741\"]}}` |\n", + "| `$contains` | String contains (case-insensitive) | `{\"carbon_source\": {\"$contains\": \"glucose\"}}` |\n", + "| `$exists` | Field exists | `{\"temperature\": {\"$exists\": true}}` |\n", + "| `$and` | Logical AND | `{\"$and\": [{...}, {...}]}` |\n", + "| `$or` | Logical OR | `{\"$or\": [{...}, {...}]}` |" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 5. Working with ActiveSets\n", + "\n", + "ActiveSets represent filtered collections of samples and support set operations." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from tfbpapi.datainfo.sample_manager import ActiveSet\n", + "\n", + "# Create some example active sets\n", + "set1 = ActiveSet(\n", + " sample_ids={\"BrentLab:harbison_2004:sample_001\", \"BrentLab:harbison_2004:sample_002\", \"BrentLab:harbison_2004:sample_003\"},\n", + " name=\"glucose_samples\",\n", + " description=\"Samples grown on glucose\"\n", + ")\n", + "\n", + "set2 = ActiveSet(\n", + " sample_ids={\"BrentLab:harbison_2004:sample_002\", \"BrentLab:harbison_2004:sample_003\", \"BrentLab:harbison_2004:sample_004\"},\n", + " name=\"heat_stress_samples\",\n", + " description=\"Samples with heat stress\"\n", + ")\n", + "\n", + "print(f\"Set 1: {set1}\")\n", + "print(f\"Set 2: {set2}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Set operations\n", + "\n", + "# Union - samples in either set\n", + "union = set1.union(set2, name=\"glucose_or_heat\")\n", + "print(f\"Union: {union}\")\n", + "print(f\" Sample IDs: {union.to_sample_ids()}\")\n", + "\n", + "# Intersection - samples in both sets\n", + "intersection = set1.intersection(set2, name=\"glucose_and_heat\")\n", + "print(f\"\\nIntersection: {intersection}\")\n", + "print(f\" Sample IDs: {intersection.to_sample_ids()}\")\n", + "\n", + "# Difference - samples in set1 but not set2\n", + "difference = set1.difference(set2, name=\"glucose_no_heat\")\n", + "print(f\"\\nDifference: {difference}\")\n", + "print(f\" Sample IDs: {difference.to_sample_ids()}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 6. Real-World Example: Exploring Harbison 2004\n", + "\n", + "Now let's work with real data. Note that we're manually creating nodes here since the `load_from_datacard` method needs to be implemented. This demonstrates the intended workflow." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# For this tutorial, we'll manually create nodes from DataCard information\n", + "# In production, you would use: manager.load_from_datacard(\"BrentLab/harbison_2004\", \"harbison_2004\")\n", + "\n", + "from tfbpapi.datainfo import DataCard\n", + "\n", + "card = DataCard('BrentLab/harbison_2004')\n", + "\n", + "# Get repo-level and field-level conditions\n", + "repo_conds = card.get_experimental_conditions('harbison_2004')\n", + "field_defs = card.get_field_definitions('harbison_2004', 'condition')\n", + "\n", + "print(f\"Found {len(field_defs)} condition definitions\")\n", + "print(f\"Conditions: {sorted(field_defs.keys())}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Manually create nodes for each condition (simulating samples)\n", + "# This demonstrates what load_from_datacard will do automatically\n", + "\n", + "demo_manager = SampleManager()\n", + "\n", + "for condition_name, condition_def in field_defs.items():\n", + " # Flatten conditions\n", + " properties, sources = ConditionFlattener.flatten_conditions(\n", + " repo_conds, None, condition_def\n", + " )\n", + " \n", + " # Create node (using condition name as sample_id for demo)\n", + " node = SampleNode(\n", + " sample_id=condition_name,\n", + " repo_id=\"BrentLab/harbison_2004\",\n", + " config_name=\"harbison_2004\",\n", + " properties=properties,\n", + " property_sources=sources\n", + " )\n", + " \n", + " # Add to manager's collection\n", + " demo_manager._collection.add_node(node)\n", + "\n", + "print(f\"\\nLoaded {demo_manager._collection.count_total_nodes()} sample nodes\")\n", + "print(demo_manager)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Explore what properties are available\n", + "sample_node = demo_manager.get_sample(\"BrentLab/harbison_2004:harbison_2004:YPD\")\n", + "\n", + "print(\"Properties available in YPD sample:\")\n", + "for key in sorted(sample_node.properties.keys()):\n", + " if not key.startswith('_'): # Skip structured versions\n", + " print(f\" {key}: {sample_node.properties[key]}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 7. Cross-Condition Filtering\n", + "\n", + "Now we can filter samples based on their experimental conditions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Find all samples with D-glucose as carbon source\n", + "glucose_samples = demo_manager.filter_all({\n", + " \"environmental_conditions.media.carbon_source\": {\"$contains\": \"D-glucose\"}\n", + "}, name=\"glucose_conditions\")\n", + "\n", + "print(f\"Found {len(glucose_samples)} conditions with D-glucose\")\n", + "print(f\"Conditions: {[sid.split(':')[-1] for sid in glucose_samples.to_sample_ids()]}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Find samples with alternative carbon sources\n", + "alt_carbon = demo_manager.filter_all({\n", + " \"$or\": [\n", + " {\"environmental_conditions.media.carbon_source\": {\"$contains\": \"galactose\"}},\n", + " {\"environmental_conditions.media.carbon_source\": {\"$contains\": \"raffinose\"}}\n", + " ]\n", + "}, name=\"alternative_carbon\")\n", + "\n", + "print(f\"\\nFound {len(alt_carbon)} conditions with alternative carbon sources\")\n", + "print(f\"Conditions: {[sid.split(':')[-1] for sid in alt_carbon.to_sample_ids()]}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Find samples with media additives (like butanol)\n", + "with_additives = demo_manager.filter_all({\n", + " \"environmental_conditions.media.additives\": {\"$contains\": \"butanol\"}\n", + "}, name=\"additive_conditions\")\n", + "\n", + "print(f\"\\nFound {len(with_additives)} conditions with additives\")\n", + "print(f\"Conditions: {[sid.split(':')[-1] for sid in with_additives.to_sample_ids()]}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 8. Property Distribution Analysis" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Analyze carbon source distribution\n", + "carbon_dist = demo_manager.get_property_distribution(\n", + " \"environmental_conditions.media.carbon_source\"\n", + ")\n", + "\n", + "print(\"Carbon source distribution:\")\n", + "for value, count in sorted(carbon_dist.items(), key=lambda x: x[1], reverse=True):\n", + " print(f\" {value}: {count} samples\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Analyze media names\n", + "media_dist = demo_manager.get_property_distribution(\n", + " \"environmental_conditions.media.name\"\n", + ")\n", + "\n", + "print(\"\\nMedia type distribution:\")\n", + "for value, count in sorted(media_dist.items(), key=lambda x: x[1], reverse=True):\n", + " print(f\" {value}: {count} samples\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 9. Summary Information" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Get summary of loaded data\n", + "summary = demo_manager.get_summary()\n", + "print(\"\\nSummary of loaded datasets:\")\n", + "print(summary)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 10. Key Takeaways\n", + "\n", + "### Advantages of Node-Based Approach\n", + "\n", + "1. **Flexible Schema**: No hardcoded field names - discovers whatever exists in the data\n", + "2. **Heterogeneity Handling**: Different datasets can have different structures (e.g., `media.carbon_source` vs `environmental_conditions.media.carbon_source`)\n", + "3. **Cross-Dataset Queries**: Filter samples across multiple datasets with varying structures\n", + "4. **Set Operations**: Build complex filters incrementally using union/intersection/difference\n", + "5. **Property Discovery**: Automatically flattens nested structures using dot notation\n", + "6. **Dual Representation**: Compound lists get both simple strings and structured versions\n", + "\n", + "### How to Query Different Property Paths\n", + "\n", + "Since different datasets structure their conditions differently, you need to use the actual property path:\n", + "\n", + "```python\n", + "# Dataset A (harbison_2004): media nested under environmental_conditions\n", + "manager.filter_all({\"environmental_conditions.media.carbon_source\": {\"$contains\": \"glucose\"}})\n", + "\n", + "# Dataset B (hypothetical): media at top level \n", + "manager.filter_all({\"media.carbon_source\": {\"$contains\": \"glucose\"}})\n", + "\n", + "# To query across both, use $or:\n", + "manager.filter_all({\n", + " \"$or\": [\n", + " {\"environmental_conditions.media.carbon_source\": {\"$contains\": \"glucose\"}},\n", + " {\"media.carbon_source\": {\"$contains\": \"glucose\"}}\n", + " ]\n", + "})\n", + "```\n", + "\n", + "### Future: External Property Mappings\n", + "\n", + "For cross-dataset harmonization, you can provide external YAML mappings:\n", + "\n", + "```yaml\n", + "# property_mappings.yaml\n", + "carbon_source:\n", + " paths:\n", + " - environmental_conditions.media.carbon_source\n", + " - media.carbon_source\n", + " value_aliases:\n", + " D-glucose: [glucose, dextrose, D-dextrose]\n", + "```\n", + "\n", + "This would enable canonical queries like `{\"carbon_source\": {\"$contains\": \"glucose\"}}` to work across all datasets." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Next Steps\n", + "\n", + "- Implement `load_from_datacard()` to automatically load samples from HuggingFace\n", + "- Implement `load_from_duckdb()` for integration with HfQueryAPI\n", + "- Implement `export_to_duckdb()` to convert ActiveSets back to SQL views\n", + "- Add external property mapping support via YAML configuration\n", + "- Build UI for interactive filtering and set operations" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.0" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/tfbpapi/datainfo/datacard.py b/tfbpapi/datainfo/datacard.py index 9b60c22..d11e378 100644 --- a/tfbpapi/datainfo/datacard.py +++ b/tfbpapi/datainfo/datacard.py @@ -1,4 +1,20 @@ -"""DataCard class for easy exploration of HuggingFace dataset metadata.""" +""" +DataCard class for parsing and exploring HuggingFace dataset metadata. + +This module provides the DataCard class for parsing HuggingFace dataset cards +into structured Python objects that can be easily explored. The focus is on +enabling users to drill down into the YAML structure to understand: + +- Dataset configurations and their types +- Feature definitions and roles +- Experimental conditions at all hierarchy levels (top/config/field) +- Field-level condition definitions +- Metadata relationships + +Users can then use this information to plan metadata table structures and +data loading strategies. + +""" import logging from typing import Any @@ -12,17 +28,48 @@ DatasetConfig, DatasetType, ExtractedMetadata, - FieldRole, + FeatureInfo, MetadataRelationship, ) class DataCard: """ - Easy-to-use interface for exploring HuggingFace dataset metadata. - - Provides methods to discover and explore dataset contents, configurations, and - metadata without loading the actual genomic data. + Parser and explorer for HuggingFace dataset metadata. + + DataCard parses HuggingFace dataset cards into flexible Python objects, + enabling users to drill down into the YAML structure to understand dataset + organization, experimental conditions, and metadata relationships. + + The parsed structure uses Pydantic models with `extra="allow"` to accept + arbitrary fields (like experimental_conditions) without requiring code + changes. This makes the system flexible enough to handle domain-specific + metadata variations. + + Key capabilities: + - Parse dataset card YAML into structured objects + - Navigate experimental conditions at 3 levels (top/config/field) + - Explore field definitions and roles + - Extract metadata schema for table design + - Discover metadata relationships + + Example (new API): + >>> card = DataCard("BrentLab/harbison_2004") + >>> # Use context manager for config exploration + >>> with card.config("harbison_2004") as cfg: + ... # Get all experimental conditions + ... conds = cfg.experimental_conditions() + ... # Get condition fields with definitions + ... fields = cfg.condition_fields() + ... # Drill down into specific field + ... for name, info in fields.items(): + ... for value, definition in info['definitions'].items(): + ... print(f"{name}={value}: {definition}") + + Example (legacy API still supported): + >>> card = DataCard("BrentLab/harbison_2004") + >>> conditions = card.get_experimental_conditions("harbison_2004") + >>> defs = card.get_field_definitions("harbison_2004", "condition") """ @@ -120,25 +167,97 @@ def get_configs_by_type( dataset_type = DatasetType(dataset_type) return self.dataset_card.get_configs_by_type(dataset_type) - def get_regulators(self, config_name: str | None = None) -> set[str]: + def get_card_metadata(self) -> dict[str, Any]: + """ + Get all top-level metadata fields from the dataset card. + + Returns all fields stored in model_extra (e.g., license, tags, pretty_name, + etc.) as a dict. This gives direct access to the raw YAML structure at the card + level. + + :return: Dict of all extra metadata fields + + """ + if self.dataset_card.model_extra: + return dict(self.dataset_card.model_extra) + return {} + + def get_config_metadata(self, config_name: str) -> dict[str, Any]: + """ + Get all extra metadata fields from a specific config. + + Returns all fields stored in the config's model_extra (e.g., + experimental_conditions, custom fields, etc.) as a dict. + + :param config_name: Configuration name + :return: Dict of all extra metadata fields for this config + :raises DataCardError: If config not found + """ - Get all regulators mentioned in the dataset. + config = self.get_config(config_name) + if not config: + raise DataCardError(f"Configuration '{config_name}' not found") - :param config_name: Optional specific config to search, otherwise searches all - :return: Set of regulator identifiers found + if config.model_extra: + return dict(config.model_extra) + return {} + def get_features(self, config_name: str) -> list[FeatureInfo]: """ - raise NotImplementedError("Method not yet implemented") + Get all feature definitions for a configuration. + + :param config_name: Configuration name + :return: List of FeatureInfo objects + :raises DataCardError: If config not found - def get_experimental_conditions(self, config_name: str | None = None) -> set[str]: """ - Get all experimental conditions mentioned in the dataset. + config = self.get_config(config_name) + if not config: + raise DataCardError(f"Configuration '{config_name}' not found") - :param config_name: Optional specific config to search, otherwise searches all - :return: Set of experimental conditions found + return config.dataset_info.features + def get_features_by_role( + self, config_name: str, role: str | None = None + ) -> dict[str, list[str]]: """ - raise NotImplementedError("Method not yet implemented") + Get features grouped by role. + + If role is specified, returns only features with that role. + If role is None, returns all features grouped by role. + + :param config_name: Configuration name + :param role: Optional specific role to filter by + :return: Dict mapping role -> list of field names + :raises DataCardError: If config not found + + Example: + >>> # Get all features by role + >>> by_role = card.get_features_by_role("config_name") + >>> # {'regulator_identifier': ['regulator_locus_tag'], + >>> # 'target_identifier': ['target_locus_tag'], ...} + >>> + >>> # Get only experimental condition features + >>> cond_fields = card.get_features_by_role("config_name", + ... "experimental_condition") + >>> # {'experimental_condition': ['condition', 'treatment']} + + """ + features = self.get_features(config_name) + + # Group by role + by_role: dict[str, list[str]] = {} + for feature in features: + feature_role = feature.role if feature.role else "no_role" + if feature_role not in by_role: + by_role[feature_role] = [] + by_role[feature_role].append(feature.name) + + # Filter by specific role if requested + if role is not None: + return {role: by_role.get(role, [])} + + return by_role def get_field_values(self, config_name: str, field_name: str) -> set[str]: """ @@ -164,7 +283,7 @@ def get_field_values(self, config_name: str, field_name: str) -> set[str]: return self._extract_field_values(config, field_name) def _extract_field_values(self, config: DatasetConfig, field_name: str) -> set[str]: - """Extract unique values for a field from various sources.""" + """Extract unique values for a field from partition structure only.""" values = set() # Check cache first @@ -185,19 +304,27 @@ def _extract_field_values(self, config: DatasetConfig, field_name: str) -> set[s partition_values = self._extract_partition_values(config, field_name) if partition_values: values.update(partition_values) + # Cache the result + self._metadata_cache[cache_key] = [ + ExtractedMetadata( + config_name=config.config_name, + field_name=field_name, + values=values, + extraction_method="partition_structure", + ) + ] + return values - # For embedded metadata fields, we would need to query the actual data - # This is a placeholder - in practice, you might use the - # HF datasets server API - if config.metadata_fields and field_name in config.metadata_fields: - # Placeholder for actual data extraction - self.logger.debug( - "Would extract embedded metadata for " - f"{field_name} in {config.config_name}" - ) + # For non-partitioned fields, we can no longer query parquet files + self.logger.debug( + f"Cannot extract values for {field_name} in {config.config_name}: " + "field is not partitioned and parquet querying is not supported" + ) except Exception as e: self.logger.warning(f"Failed to extract values for {field_name}: {e}") + # Return empty set on failure instead of raising + # This maintains backward compatibility return values @@ -296,8 +423,21 @@ def get_repository_info(self) -> dict[str, Any]: "has_default_config": self.dataset_card.get_default_config() is not None, } - def explore_config(self, config_name: str) -> dict[str, Any]: - """Get detailed information about a specific configuration.""" + def explore_config( + self, config_name: str, include_extra: bool = True + ) -> dict[str, Any]: + """ + Get detailed information about a specific configuration. + + Returns a comprehensive dict with config structure including features, data + files, partitioning, and optionally all extra metadata fields. + + :param config_name: Configuration name + :param include_extra: If True, include all fields from model_extra + :return: Dict with config details + :raises DataCardError: If config not found + + """ config = self.get_config(config_name) if not config: raise DataCardError(f"Configuration '{config_name}' not found") @@ -309,7 +449,13 @@ def explore_config(self, config_name: str) -> dict[str, Any]: "is_default": config.default, "num_features": len(config.dataset_info.features), "features": [ - {"name": f.name, "dtype": f.dtype, "description": f.description} + { + "name": f.name, + "dtype": f.dtype, + "description": f.description, + "role": f.role, + "has_definitions": f.definitions is not None, + } for f in config.dataset_info.features ], "data_files": [ @@ -332,23 +478,53 @@ def explore_config(self, config_name: str) -> dict[str, Any]: if config.metadata_fields: info["metadata_fields"] = config.metadata_fields + # Add all extra fields from model_extra + if include_extra and config.model_extra: + info["extra_fields"] = dict(config.model_extra) + return info def extract_metadata_schema(self, config_name: str) -> dict[str, Any]: """ - Extract metadata schema for a configuration. + Extract complete metadata schema for planning metadata table structure. + + This is the primary method for understanding what metadata is available and + how to structure it into a metadata table. It consolidates information from + all sources: - Returns structured schema identifying metadata fields by their role, - suitable for metadata extraction by MetadataManager. Includes - experimental conditions at all three hierarchy levels: - - Top-level (repo-wide, applies to all configs/samples) - - Config-level (applies to this config's samples) - - Field-level (varies per sample, from field definitions) + - **Field roles**: Which fields are regulators, targets, conditions, etc. + - **Top-level conditions**: Repo-wide conditions (constant for all samples) + - **Config-level conditions**: Config-specific conditions (constant for this config) + - **Field-level definitions**: Per-sample condition definitions + + The returned schema provides all the information needed to: + 1. Identify sample identifier fields (regulator_identifier, etc.) + 2. Determine which conditions are constant vs. variable + 3. Access condition definitions for creating flattened columns + 4. Plan metadata table structure :param config_name: Configuration name to extract schema for - :return: Dict with field lists grouped by role and condition definitions + :return: Dict with comprehensive schema including: + - regulator_fields: List of regulator identifier field names + - target_fields: List of target identifier field names + - condition_fields: List of experimental_condition field names + - condition_definitions: Dict mapping field -> value -> definition + - top_level_conditions: Dict of repo-wide conditions + - config_level_conditions: Dict of config-specific conditions :raises DataCardError: If configuration not found + Example: + >>> schema = card.extract_metadata_schema('harbison_2004') + >>> # Identify identifier fields + >>> print(f"Regulator fields: {schema['regulator_fields']}") + >>> # Check for constant conditions + >>> if schema['top_level_conditions']: + ... print("Has repo-wide constant conditions") + >>> # Get field-level definitions for metadata table + >>> for field in schema['condition_fields']: + ... defs = schema['condition_definitions'][field] + ... print(f"{field} has {len(defs)} levels") + """ config = self.get_config(config_name) if not config: @@ -364,22 +540,28 @@ def extract_metadata_schema(self, config_name: str) -> dict[str, Any]: } for feature in config.dataset_info.features: - if feature.role == FieldRole.REGULATOR_IDENTIFIER: + if feature.role == "regulator_identifier": schema["regulator_fields"].append(feature.name) - elif feature.role == FieldRole.TARGET_IDENTIFIER: + elif feature.role == "target_identifier": schema["target_fields"].append(feature.name) - elif feature.role == FieldRole.EXPERIMENTAL_CONDITION: + elif feature.role == "experimental_condition": schema["condition_fields"].append(feature.name) if feature.definitions: schema["condition_definitions"][feature.name] = feature.definitions # Add top-level conditions (applies to all configs/samples) - if self.dataset_card.experimental_conditions: - schema["top_level_conditions"] = self.dataset_card.experimental_conditions + # Stored in model_extra as dict + if self.dataset_card.model_extra: + top_level = self.dataset_card.model_extra.get("experimental_conditions") + if top_level: + schema["top_level_conditions"] = top_level # Add config-level conditions (applies to this config's samples) - if config.experimental_conditions: - schema["config_level_conditions"] = config.experimental_conditions + # Stored in model_extra as dict + if config.model_extra: + config_level = config.model_extra.get("experimental_conditions") + if config_level: + schema["config_level_conditions"] = config_level return schema @@ -415,7 +597,7 @@ def get_condition_levels( f"Field '{field_name}' not found in config '{config_name}'" ) - if feature.role != FieldRole.EXPERIMENTAL_CONDITION: + if feature.role != "experimental_condition": raise DataCardError( f"Field '{field_name}' is not an experimental condition " f"(role={feature.role})" @@ -429,6 +611,191 @@ def get_condition_levels( values = self.get_field_values(config_name, field_name) return sorted(list(values)) + def get_experimental_conditions( + self, config_name: str | None = None + ) -> dict[str, Any]: + """ + Get experimental conditions with proper hierarchy handling. + + This method enables drilling down into the experimental conditions hierarchy: + - Top-level (repo-wide): Common to all configs/samples + - Config-level: Specific to a config, common to its samples + - Field-level: Per-sample variation (use get_field_definitions instead) + + Returns experimental conditions at the appropriate level: + - If config_name is None: returns top-level (repo-wide) conditions only + - If config_name is provided: returns merged (top + config) conditions + + All conditions are returned as flexible dicts that preserve the original + YAML structure. Navigate nested dicts to access specific values. + + :param config_name: Optional config name. If provided, merges top and config levels + :return: Dict of experimental conditions (empty dict if none defined) + + Example: + >>> # Get top-level conditions + >>> top = card.get_experimental_conditions() + >>> temp = top.get('temperature_celsius', 30) + >>> + >>> # Get merged conditions for a config + >>> merged = card.get_experimental_conditions('config_name') + >>> media = merged.get('media', {}) + >>> media_name = media.get('name', 'unspecified') + + """ + # Get top-level conditions (stored in model_extra) + top_level = ( + self.dataset_card.model_extra.get("experimental_conditions", {}) + if self.dataset_card.model_extra + else {} + ) + + # If no config specified, return top-level only + if config_name is None: + return top_level.copy() if isinstance(top_level, dict) else {} + + # Get config-level conditions + config = self.get_config(config_name) + if not config: + raise DataCardError(f"Configuration '{config_name}' not found") + + config_level = ( + config.model_extra.get("experimental_conditions", {}) + if config.model_extra + else {} + ) + + # Merge: config-level overrides top-level + merged = {} + if isinstance(top_level, dict): + merged.update(top_level) + if isinstance(config_level, dict): + merged.update(config_level) + + return merged + + def get_field_definitions( + self, config_name: str, field_name: str + ) -> dict[str, Any]: + """ + Get definitions for a specific field (field-level conditions). + + This is the third level of the experimental conditions hierarchy - conditions + that vary per sample. Returns a dict mapping each possible field value to its + detailed specification. + + For fields with role=experimental_condition, the definitions typically include + nested structures like media composition, temperature, treatments, etc. that + define what each categorical value means experimentally. + + :param config_name: Configuration name + :param field_name: Field name (typically has role=experimental_condition) + :return: Dict mapping field values to their definition dicts (empty if no definitions) + :raises DataCardError: If config or field not found + + Example: + >>> # Get condition definitions + >>> defs = card.get_field_definitions('harbison_2004', 'condition') + >>> # defs = {'YPD': {...}, 'HEAT': {...}, ...} + >>> + >>> # Drill down into a specific condition + >>> ypd = defs['YPD'] + >>> env_conds = ypd.get('environmental_conditions', {}) + >>> media = env_conds.get('media', {}) + >>> media_name = media.get('name') + + """ + config = self.get_config(config_name) + if not config: + raise DataCardError(f"Configuration '{config_name}' not found") + + # Find the feature + feature = None + for f in config.dataset_info.features: + if f.name == field_name: + feature = f + break + + if not feature: + raise DataCardError( + f"Field '{field_name}' not found in config '{config_name}'" + ) + + # Return definitions if present, otherwise empty dict + return feature.definitions if feature.definitions else {} + + def get_field_attribute( + self, config_name: str, field_name: str, attribute: str + ) -> dict[str, Any]: + """ + Extract a specific attribute from field definitions. + + This is useful for exploring nested attributes in condition definitions, + such as media composition, temperature parameters, or growth phases. + + :param config_name: Configuration name + :param field_name: Field with definitions (e.g., 'condition') + :param attribute: Attribute to extract (e.g., 'media', 'temperature_celsius') + :return: Dict mapping field values to their attribute specifications. + Returns 'unspecified' if attribute doesn't exist for a value. + + Example: + >>> card = DataCard('BrentLab/harbison_2004') + >>> media = card.get_field_attribute('harbison_2004', 'condition', 'media') + >>> print(media['YPD']) + {'name': 'YPD', 'carbon_source': [...], 'nitrogen_source': [...]} + + """ + # Get all field definitions + definitions = self.get_field_definitions(config_name, field_name) + + # Extract attribute for each definition + result = {} + for field_value, definition in definitions.items(): + if attribute in definition: + result[field_value] = definition[attribute] + else: + result[field_value] = "unspecified" + + return result + + def list_experimental_condition_fields(self, config_name: str) -> list[str]: + """ + List all fields with role=experimental_condition in a config. + + These are fields that contain per-sample experimental condition variation. + They represent the field-level (third level) of the experimental conditions + hierarchy. + + Fields with this role typically have `definitions` that map each value to + its detailed experimental specification. Use `get_field_definitions()` to + access these definitions. + + :param config_name: Configuration name + :return: List of field names with experimental_condition role + :raises DataCardError: If config not found + + Example: + >>> # Find all condition fields + >>> cond_fields = card.list_experimental_condition_fields('config_name') + >>> # ['condition', 'treatment', 'time_point'] + >>> + >>> # Then get definitions for each + >>> for field in cond_fields: + ... defs = card.get_field_definitions('config_name', field) + ... print(f"{field}: {len(defs)} levels") + + """ + config = self.get_config(config_name) + if not config: + raise DataCardError(f"Configuration '{config_name}' not found") + + return [ + f.name + for f in config.dataset_info.features + if f.role == "experimental_condition" + ] + def summary(self) -> str: """Get a human-readable summary of the dataset.""" card = self.dataset_card diff --git a/tfbpapi/datainfo/metadata_manager.py b/tfbpapi/datainfo/metadata_manager.py index fabdd9a..da1efc3 100644 --- a/tfbpapi/datainfo/metadata_manager.py +++ b/tfbpapi/datainfo/metadata_manager.py @@ -1,623 +1,278 @@ -"""MetadataManager for cross-dataset metadata filtering and querying.""" +""" +MetadataManager for creating metadata table views from DataCard information. + +This module provides the MetadataManager class for generating SQL commands that create +metadata views from HuggingFace dataset information extracted via DataCard. The manager +enables users to: + +- Specify which columns to include in metadata views +- Add constant columns from top-level or config-level experimental conditions +- Flatten nested condition definitions into queryable columns +- Generate SQL CREATE VIEW statements + +The design follows a user-driven workflow: +1. User explores DataCard to understand available fields and conditions +2. User downloads data via HfCacheManager/HfQueryAPI (creates base views) +3. User specifies column selection for metadata view +4. MetadataManager generates and executes SQL to create the view + +""" -import logging -import re from pathlib import Path -from typing import Any, Optional +from typing import Any import duckdb import pandas as pd -from ..errors import DataCardError -from .datacard import DataCard -from .models import FieldRole - -# Separator conventions for concatenated fields +# Separator conventions for formatting compound metadata values COMPONENT_SEPARATORS = { - "type_value": ":", # Separates component type from value - "value_conc": "@", # Separates value from concentration - "components": ";", # Separates multiple components of same type - "types": "|", # Separates different component types (future use) + "type_value": ":", # Separates type from value (e.g., "type:value") + "value_conc": "@", # Separates value from concentration (e.g., "compound@0.5%") + "components": ";", # Separates multiple components (e.g., "comp1;comp2;comp3") + "types": "|", # Separates component types (e.g., "type1|type2|type3") } class MetadataManager: """ - Cross-dataset metadata query manager using DuckDB temp views. - - Stores metadata field values as-is from parquet files, plus optionally expands field - definitions into searchable columns for experimental conditions. + Manager for creating metadata table views from DataCard information. + + MetadataManager provides a flexible interface for generating SQL views that combine + data fields with experimental condition metadata. Users specify which columns to + include and how to handle conditions at different hierarchy levels. + + Example: + >>> # Step 1: Explore schema with DataCard + >>> card = DataCard("BrentLab/harbison_2004") + >>> schema = card.extract_metadata_schema("harbison_2004") + >>> + >>> # Step 2: Create metadata view + >>> mgr = MetadataManager() + >>> view = mgr.create_metadata_view( + ... base_view="harbison_2004_train", + ... view_name="harbison_2004_metadata", + ... include_fields=["regulator_locus_tag", "target_locus_tag", "condition"], + ... constant_columns={ + ... "temperature_celsius": 30, + ... "strain_background": "BY4741" + ... } + ... ) """ - def __init__(self, cache_dir: Path | None = None, cache: bool = False): + def __init__( + self, + cache_dir: Path | None = None, + cache: bool = False, + duckdb_conn: duckdb.DuckDBPyConnection | None = None, + ): """ - Initialize MetadataManager with optional caching. + Initialize MetadataManager. - :param cache_dir: Directory for cached metadata (if cache=True) - :param cache: If True, persist metadata extractions. Default: False + :param cache_dir: Optional directory for caching metadata views + :param cache: Whether to enable persistent caching (not yet implemented) + :param duckdb_conn: Optional DuckDB connection to use. If None, creates new + in-memory connection. Pass a shared connection to work with views created + by HfQueryAPI or other tools. """ - self.logger = logging.getLogger(self.__class__.__name__) - self._conn = duckdb.connect(":memory:") - self._registered_datasets: dict[str, DataCard] = {} - self._view_names: dict[tuple[str, str], str] = {} # (repo_id, config) -> view + self._conn = duckdb_conn if duckdb_conn is not None else duckdb.connect(":memory:") self._cache_dir = cache_dir self._cache_enabled = cache + self._registered_datasets: dict[str, Any] = {} + self._view_names: dict[tuple[str, str], str] = {} - def register(self, repo_id: str, config_names: list[str] | None = None) -> None: - """ - Register a dataset for cross-dataset queries. - - :param repo_id: HuggingFace repository identifier - :param config_names: Optional list of config names to register. If None, - registers all configs. - + def _sanitize_view_name(self, repo_id: str, config_name: str) -> str: """ - self.logger.info(f"Registering dataset: {repo_id}") + Convert repo_id and config_name to valid SQL identifier. - # Load datacard - if repo_id not in self._registered_datasets: - datacard = DataCard(repo_id=repo_id) - self._registered_datasets[repo_id] = datacard - else: - datacard = self._registered_datasets[repo_id] - - # Determine which configs to register - if config_names is None: - configs_to_register = [c.config_name for c in datacard.configs] - else: - configs_to_register = config_names + Replaces special characters (/, -, spaces) with underscores and appends + '_metadata' suffix. - # Register each config - for config_name in configs_to_register: - self._register_config(datacard, config_name) + :param repo_id: Repository ID (e.g., "BrentLab/harbison_2004") + :param config_name: Configuration name (e.g., "harbison_2004") + :return: Sanitized view name (e.g., "BrentLab_harbison_2004_harbison_2004_metadata") - # Recreate unified view - self._create_unified_view() + Example: + >>> mgr = MetadataManager() + >>> mgr._sanitize_view_name("BrentLab/dataset-name", "config_name") + 'BrentLab_dataset_name_config_name_metadata' - def _register_config(self, datacard: DataCard, config_name: str) -> None: """ - Register a single config for querying. + sanitized = f"{repo_id}_{config_name}" + sanitized = sanitized.replace("/", "_").replace("-", "_").replace(" ", "_") + return f"{sanitized}_metadata" - :param datacard: DataCard instance - :param config_name: Configuration name to register - - """ - self.logger.debug(f"Registering config: {datacard.repo_id}/{config_name}") - - # Extract metadata schema - try: - schema = datacard.extract_metadata_schema(config_name) - except DataCardError as e: - self.logger.error(f"Failed to extract schema for {config_name}: {e}") - raise - - # Extract metadata from parquet file - try: - metadata_df = self._extract_metadata_from_config( - datacard, config_name, schema - ) - except Exception as e: - self.logger.error(f"Failed to extract metadata for {config_name}: {e}") - raise - - # Create temp view - view_name = self._sanitize_view_name(datacard.repo_id, config_name) - self._create_temp_view(datacard.repo_id, config_name, metadata_df, view_name) - self._view_names[(datacard.repo_id, config_name)] = view_name - - def unregister(self, repo_id: str) -> None: + def _flatten_condition_definition(self, definition: dict) -> dict: """ - Remove dataset from cross-dataset queries. - - :param repo_id: HuggingFace repository identifier to unregister + Flatten nested condition definition dict into flat key-value pairs. + + Extracts common experimental condition fields: + - media.name -> growth_media + - temperature_celsius -> temperature_celsius + - cultivation_method -> cultivation_method + - strain_background -> strain_background + + :param definition: Nested definition dict from field.definitions[value] + :return: Flat dict with standardized keys + + Example: + >>> mgr = MetadataManager() + >>> definition = { + ... "media": {"name": "YPD"}, + ... "temperature_celsius": 30 + ... } + >>> mgr._flatten_condition_definition(definition) + {'growth_media': 'YPD', 'temperature_celsius': 30} """ - if repo_id not in self._registered_datasets: - self.logger.warning(f"Dataset {repo_id} not registered") - return - - # Remove all views for this repo - views_to_remove = [ - (rid, cfg) for rid, cfg in self._view_names.keys() if rid == repo_id - ] - - for rid, cfg in views_to_remove: - view_name = self._view_names.pop((rid, cfg)) - try: - self._conn.execute(f"DROP VIEW IF EXISTS {view_name}") - except Exception as e: - self.logger.warning(f"Failed to drop view {view_name}: {e}") - - # Remove datacard - del self._registered_datasets[repo_id] - - # Recreate unified view - self._create_unified_view() - - def _extract_metadata_from_config( - self, datacard: DataCard, config_name: str, schema: dict[str, Any] - ) -> pd.DataFrame: - """ - Extract metadata from parquet file for a config. - - Processes experimental conditions at all three hierarchy levels: - - Top-level (repo-wide): Applies to ALL samples - - Config-level: Applies to all samples in this config - - Field-level: Varies per sample (from field definitions) + result = {} + if not definition: + return result - Hierarchy merging: field-level > config-level > top-level + # Extract media name as growth_media + if "media" in definition and isinstance(definition["media"], dict): + if "name" in definition["media"]: + result["growth_media"] = definition["media"]["name"] - :param datacard: DataCard instance - :param config_name: Configuration name - :param schema: Metadata schema from extract_metadata_schema() + # Extract other top-level condition fields + for key in ["temperature_celsius", "cultivation_method", "strain_background"]: + if key in definition: + result[key] = definition[key] - :return: DataFrame with metadata field values + expanded columns + return result + def _flatten_experimental_conditions(self, exp_conds: Any) -> dict: """ - # Get config to access data files - config = datacard.get_config(config_name) - if not config: - raise DataCardError(f"Configuration '{config_name}' not found") - - # Determine which fields to extract - metadata_fields = ( - schema["regulator_fields"] - + schema["target_fields"] - + schema["condition_fields"] - ) - - # Add sample_id if not already in list - if "sample_id" not in metadata_fields: - metadata_fields.append("sample_id") - - # TODO: For MVP, we need actual parquet file path from HuggingFace - # This requires integration with HF datasets or direct file access - # For now, create a minimal placeholder that will work with tests - - # Create placeholder DataFrame with required structure - metadata_df = pd.DataFrame() - - # Add computed columns - metadata_df["dataset"] = datacard.repo_id - metadata_df["config_name"] = config_name - - # Add metadata fields as empty for now - for field in metadata_fields: - metadata_df[field] = "unspecified" - - # Process experimental conditions in hierarchy order (low to high priority) - # Start with top-level (repo-wide) conditions - if schema.get("top_level_conditions"): - top_conditions = self._flatten_experimental_conditions( - schema["top_level_conditions"] - ) - for col_name, col_value in top_conditions.items(): - metadata_df[col_name] = col_value - - # Apply config-level conditions (overrides top-level) - if schema.get("config_level_conditions"): - config_conditions = self._flatten_experimental_conditions( - schema["config_level_conditions"] - ) - for col_name, col_value in config_conditions.items(): - metadata_df[col_name] = col_value - - # Expand field-level condition definitions (overrides config/top-level) - if schema["condition_definitions"]: - for field_name, definitions in schema["condition_definitions"].items(): - # Add expanded columns for each condition - metadata_df["growth_media"] = "unspecified" - metadata_df["components"] = "unspecified" - - return metadata_df - - def _flatten_experimental_conditions(self, exp_conditions: Any) -> dict[str, Any]: - """ - Flatten ExperimentalConditions object into column name/value pairs. - - Processes both environmental_conditions and strain_background from repo-level or - config-level ExperimentalConditions. Also handles extra fields stored in - model_extra (Pydantic's extra='allow'). + Extract attributes from ExperimentalConditions object or dict. - :param exp_conditions: ExperimentalConditions instance - :return: Dict mapping column names to values + Handles both Pydantic model objects and plain dicts. Extracts: + - temperature_celsius + - cultivation_method + - strain_background + - media.name (as growth_media) - """ - flattened: dict[str, Any] = {} - - # Handle strain_background - if ( - hasattr(exp_conditions, "strain_background") - and exp_conditions.strain_background - ): - strain = exp_conditions.strain_background - if isinstance(strain, str): - flattened["strain_background"] = strain - elif isinstance(strain, dict): - flattened["strain_background"] = strain.get("name", "unspecified") - - # Handle extra fields (for models with extra='allow') - # Some datacards store conditions directly as extra fields - if hasattr(exp_conditions, "model_extra") and exp_conditions.model_extra: - for key, value in exp_conditions.model_extra.items(): - # Store extra fields with their original names - flattened[key] = value - - # Handle environmental_conditions - if ( - hasattr(exp_conditions, "environmental_conditions") - and exp_conditions.environmental_conditions - ): - env = exp_conditions.environmental_conditions - - # Temperature - if ( - hasattr(env, "temperature_celsius") - and env.temperature_celsius is not None - ): - flattened["temperature_celsius"] = env.temperature_celsius - - # Cultivation method - if hasattr(env, "cultivation_method") and env.cultivation_method: - flattened["cultivation_method"] = env.cultivation_method - - # Media information - if hasattr(env, "media") and env.media: - media = env.media - if hasattr(media, "name") and media.name: - flattened["growth_media"] = media.name - - # Build components string from media composition - components = [] - - # Carbon source - if hasattr(media, "carbon_source") and media.carbon_source: - for compound in media.carbon_source: - if hasattr(compound, "compound"): - components.append( - self._format_compound( - "carbon_source", compound.model_dump() - ) - ) - - # Nitrogen source - if hasattr(media, "nitrogen_source") and media.nitrogen_source: - for compound in media.nitrogen_source: - if hasattr(compound, "compound"): - components.append( - self._format_compound( - "nitrogen_source", compound.model_dump() - ) - ) - - # Phosphate source - if hasattr(media, "phosphate_source") and media.phosphate_source: - for compound in media.phosphate_source: - if hasattr(compound, "compound"): - components.append( - self._format_compound( - "phosphate_source", compound.model_dump() - ) - ) - - # Additives - if hasattr(media, "additives") and media.additives: - for additive in media.additives: - if hasattr(additive, "name"): - components.append(f"additive:{additive.name}") - - if components: - flattened["components"] = "|".join(components) - - # Growth phase - if hasattr(env, "growth_phase") and env.growth_phase: - gp = env.growth_phase - if hasattr(gp, "stage") and gp.stage: - flattened["growth_stage"] = gp.stage - if hasattr(gp, "od600") and gp.od600 is not None: - flattened["od600"] = gp.od600 - - # Chemical treatments - if hasattr(env, "chemical_treatments") and env.chemical_treatments: - treatments = [] - for treatment in env.chemical_treatments: - if hasattr(treatment, "compound") and treatment.compound: - treatments.append(treatment.compound) - if treatments: - flattened["chemical_treatments"] = ";".join(treatments) - - # Drug treatments - if hasattr(env, "drug_treatments") and env.drug_treatments: - drugs = [] - for drug in env.drug_treatments: - if hasattr(drug, "compound") and drug.compound: - drugs.append(drug.compound) - if drugs: - flattened["drug_treatments"] = ";".join(drugs) - - # Heat treatment - if hasattr(env, "heat_treatment") and env.heat_treatment: - ht = env.heat_treatment - if ( - hasattr(ht, "temperature_celsius") - and ht.temperature_celsius is not None - ): - flattened["heat_treatment_temp"] = ht.temperature_celsius - - # Induction - if hasattr(env, "induction") and env.induction: - ind = env.induction - if hasattr(ind, "system") and ind.system: - flattened["induction_system"] = ind.system - - return flattened - - def _flatten_condition_definition( - self, definition: dict[str, Any] - ) -> dict[str, str]: - """ - Flatten a single condition definition into searchable fields. + :param exp_conds: ExperimentalConditions object or dict + :return: Flat dict with condition values - :param definition: Condition definition dict (e.g., YPD definition) - :return: Dict with flattened fields (growth_media, components) + Example: + >>> mgr = MetadataManager() + >>> class MockConditions: + ... temperature_celsius = 30 + ... strain_background = "BY4741" + >>> mgr._flatten_experimental_conditions(MockConditions()) + {'temperature_celsius': 30, 'strain_background': 'BY4741'} """ - flattened: dict[str, str] = { - "growth_media": "unspecified", - "components": "", - } - - # Extract environmental conditions if present - if "environmental_conditions" in definition: - env_conds = definition["environmental_conditions"] - - # Extract media information - if "media" in env_conds: - media = env_conds["media"] - if isinstance(media, dict): - # Extract media name - if "name" in media: - flattened["growth_media"] = media["name"] - - # Build components string - components = [] - - # Extract carbon source - if "carbon_source" in media: - carbon = media["carbon_source"] - if isinstance(carbon, list): - for compound in carbon: - components.append( - self._format_compound("carbon_source", compound) - ) - elif isinstance(carbon, dict): - components.append( - self._format_compound("carbon_source", carbon) - ) - - # Extract nitrogen source - if "nitrogen_source" in media: - nitrogen = media["nitrogen_source"] - if isinstance(nitrogen, list): - for compound in nitrogen: - components.append( - self._format_compound("nitrogen_source", compound) - ) - elif isinstance(nitrogen, dict): - components.append( - self._format_compound("nitrogen_source", compound) - ) - - # Extract phosphate source - if "phosphate_source" in media: - phosphate = media["phosphate_source"] - if isinstance(phosphate, list): - for compound in phosphate: - components.append( - self._format_compound("phosphate_source", compound) - ) - elif isinstance(phosphate, dict): - components.append( - self._format_compound("phosphate_source", compound) - ) - - # Join components - if components: - flattened["components"] = "|".join(components) - - return flattened - - def _format_compound(self, component_type: str, compound: dict[str, Any]) -> str: - """ - Format a compound dict into searchable string. - - :param component_type: Type of component (carbon_source, nitrogen_source, etc.) - :paramcompound: Compound info dict with name and concentration - :return: Formatted string (e.g., "carbon_source:D-glucose@2%") - - """ - if isinstance(compound, str): - return f"{component_type}:{compound}" + result = {} + if exp_conds is None: + return result - name = compound.get("name", "unknown") - result = f"{component_type}:{name}" + # Handle both objects (with attributes) and dicts (with keys) + for attr in ["temperature_celsius", "cultivation_method", "strain_background"]: + if hasattr(exp_conds, attr): + val = getattr(exp_conds, attr, None) + else: + val = exp_conds.get(attr) if isinstance(exp_conds, dict) else None - # Add concentration if present - if "concentration_percent" in compound: - result += f"@{compound['concentration_percent']}%" - elif "concentration_g_per_l" in compound: - result += f"@{compound['concentration_g_per_l']}g/L" - elif "concentration_molar" in compound: - result += f"@{compound['concentration_molar']}M" + if val is not None: + result[attr] = val - return result + # Extract media.name if present + if hasattr(exp_conds, "media"): + media = getattr(exp_conds, "media", None) + else: + media = exp_conds.get("media") if isinstance(exp_conds, dict) else None - def _create_temp_view( - self, repo_id: str, config_name: str, metadata_df: pd.DataFrame, view_name: str - ) -> None: - """ - Create DuckDB temp view from metadata DataFrame. + if media: + if hasattr(media, "name"): + name = getattr(media, "name", None) + else: + name = media.get("name") if isinstance(media, dict) else None - :param repo_id: Repository identifier - :param config_name: Configuration name - :param metadata_df: Metadata DataFrame - :param view_name: Sanitized view name + if name: + result["growth_media"] = name - """ - try: - # Register DataFrame as temp view in DuckDB - self._conn.register(view_name, metadata_df) - self.logger.debug(f"Created temp view: {view_name}") - except Exception as e: - self.logger.error(f"Failed to create view {view_name}: {e}") - raise + return result - def _sanitize_view_name(self, repo_id: str, config_name: str) -> str: + def create_metadata_view( + self, + base_view: str, + view_name: str, + include_fields: list[str], + constant_columns: dict[str, Any] | None = None, + ) -> str: """ - Create a sanitized view name from repo_id and config_name. - - :param repo_id: Repository identifier - :param config_name: Configuration name - :return: Sanitized view name safe for SQL + Create metadata view from base view with user-specified columns. + + Generates SQL CREATE OR REPLACE VIEW statement that selects specified fields + from an existing base view and adds constant columns as literals. + + :param base_view: Name of existing DuckDB view (created by HfCacheManager) + :param view_name: Name for the new metadata view + :param include_fields: List of field names to select from base view + :param constant_columns: Optional dict of {column_name: value} to add as constants + :return: Name of created view + + Example: + >>> mgr = MetadataManager() + >>> view = mgr.create_metadata_view( + ... base_view="harbison_2004_train", + ... view_name="harbison_2004_metadata", + ... include_fields=["regulator_locus_tag", "target_locus_tag"], + ... constant_columns={"temperature_celsius": 30} + ... ) + >>> view + 'harbison_2004_metadata' """ - # Replace non-alphanumeric with underscores - safe_repo = re.sub(r"[^a-zA-Z0-9]+", "_", repo_id) - safe_config = re.sub(r"[^a-zA-Z0-9]+", "_", config_name) - return f"{safe_repo}_{safe_config}_metadata" - - def _create_unified_view(self) -> None: - """Create unified_metadata view combining all registered datasets.""" - if not self._view_names: - self.logger.debug("No views registered, skipping unified view creation") - return - - # Drop existing unified view if present - try: - self._conn.execute("DROP VIEW IF EXISTS unified_metadata") - except Exception: - pass - - # Get all column names across all views - all_columns = set() - for view_name in self._view_names.values(): - try: - cols = ( - self._conn.execute(f"DESCRIBE {view_name}") - .fetchdf()["column_name"] - .tolist() - ) - all_columns.update(cols) - except Exception as e: - self.logger.warning(f"Failed to get columns from {view_name}: {e}") - - if not all_columns: - self.logger.warning("No columns found in registered views") - return - - # Build UNION ALL query with column alignment - union_queries = [] - for view_name in self._view_names.values(): - # Get columns in this view - view_cols = ( - self._conn.execute(f"DESCRIBE {view_name}") - .fetchdf()["column_name"] - .tolist() - ) - - # Build SELECT with coalesce for missing columns - select_parts = [] - for col in sorted(all_columns): - if col in view_cols: - select_parts.append(f'"{col}"') + # Build SELECT clause with included fields + select_parts = include_fields.copy() + + # Add constant columns as literals + if constant_columns: + for col_name, col_value in constant_columns.items(): + if isinstance(col_value, str): + select_parts.append(f"'{col_value}' AS {col_name}") else: - select_parts.append(f"'unspecified' AS \"{col}\"") - - union_queries.append(f"SELECT {', '.join(select_parts)} FROM {view_name}") - - # Create unified view - unified_query = " UNION ALL ".join(union_queries) - try: - self._conn.execute(f"CREATE VIEW unified_metadata AS {unified_query}") - self.logger.debug( - f"Created unified view from {len(self._view_names)} views with {len(all_columns)} columns" - ) - except Exception as e: - self.logger.error(f"Failed to create unified view: {e}") - raise - - def query(self, sql: str) -> pd.DataFrame: - """ - Execute SQL query across all registered datasets. + select_parts.append(f"{col_value} AS {col_name}") - :param sql: SQL query string to execute - :return: Query results as pandas DataFrame + select_clause = ",\n ".join(select_parts) - """ - try: - result = self._conn.execute(sql).fetchdf() - return result - except Exception as e: - self.logger.error(f"Query failed: {e}") - raise - - def filter_by_regulator(self, regulators: list[str]) -> "MetadataManager": - """ - Filter metadata to specific regulators. - - :param regulators: List of regulator symbols to filter by - :return: Self for method chaining - - """ - # TODO: Implement filtering logic - # This will be implemented in Phase 3 - raise NotImplementedError("filter_by_regulator not yet implemented") - - def filter_by_conditions(self, **kwargs: Any) -> "MetadataManager": - """ - Filter by experimental conditions. + # Generate SQL + sql = f"""\ + CREATE OR REPLACE VIEW {view_name} AS + SELECT {select_clause} + FROM {base_view} +""" - :param kwargs: Condition filters (e.g., media="YPD", temperature=30) - :return: Self for method chaining + # Execute + self._conn.execute(sql) - """ - # TODO: Implement filtering logic - # This will be implemented in Phase 3 - raise NotImplementedError("filter_by_conditions not yet implemented") + return view_name def get_active_configs(self) -> list[tuple[str, str]]: """ - Get (repo_id, config_name) pairs that match active filters. + Get list of registered (repo_id, config_name) tuples. + + :return: List of active config tuples (empty for minimal implementation) - :return: List of (repo_id, config_name) tuples + Note: + This is a placeholder method for future multi-dataset support. + Currently returns empty list. """ - # TODO: Implement with filter support - # For now, return all registered configs - return list(self._view_names.keys()) + return [] def get_summary(self) -> pd.DataFrame: """ - Get summary stats for registered datasets. + Get summary DataFrame of registered datasets. + + :return: Empty DataFrame for minimal implementation - :return: DataFrame with summary statistics + Note: + This is a placeholder method for future multi-dataset support. + Currently returns empty DataFrame. """ - if not self._view_names: - return pd.DataFrame() - - # TODO: Implement summary statistics - # This will be implemented in Phase 3 - summary_data = [] - for (repo_id, config_name), view_name in self._view_names.items(): - summary_data.append( - { - "dataset": repo_id, - "config_name": config_name, - "view_name": view_name, - } - ) - - return pd.DataFrame(summary_data) + return pd.DataFrame() diff --git a/tfbpapi/datainfo/models.py b/tfbpapi/datainfo/models.py index 0e76fc0..0e3230b 100644 --- a/tfbpapi/datainfo/models.py +++ b/tfbpapi/datainfo/models.py @@ -1,10 +1,16 @@ -"""Pydantic models for dataset card validation.""" +""" +Pydantic models for dataset card validation. + +These models provide minimal structure for parsing HuggingFace dataset cards while +remaining flexible enough to accommodate diverse experimental systems. Most fields use +extra="allow" to accept domain-specific additions without requiring code changes. + +""" -import warnings from enum import Enum from typing import Any -from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator +from pydantic import BaseModel, ConfigDict, Field, field_validator class DatasetType(str, Enum): @@ -17,384 +23,29 @@ class DatasetType(str, Enum): QC_DATA = "qc_data" -class FieldRole(str, Enum): - """Valid role values for feature fields.""" - - REGULATOR_IDENTIFIER = "regulator_identifier" - TARGET_IDENTIFIER = "target_identifier" - QUANTITATIVE_MEASURE = "quantitative_measure" - EXPERIMENTAL_CONDITION = "experimental_condition" - GENOMIC_COORDINATE = "genomic_coordinate" - - -class GrowthStage(str, Enum): - """Common growth stages.""" - - MID_LOG_PHASE = "mid_log_phase" - EARLY_LOG_PHASE = "early_log_phase" - LATE_LOG_PHASE = "late_log_phase" - STATIONARY_PHASE = "stationary_phase" - EARLY_STATIONARY_PHASE = "early_stationary_phase" - OVERNIGHT_STATIONARY_PHASE = "overnight_stationary_phase" - MID_LOG = "mid_log" - EARLY_LOG = "early_log" - LATE_LOG = "late_log" - EXPONENTIAL_PHASE = "exponential_phase" - - -class CompoundInfo(BaseModel): - """Information about a chemical compound in media.""" - - compound: str = Field(..., description="Chemical compound name") - concentration_percent: float | None = Field( - default=None, description="Concentration as percentage (w/v)" - ) - concentration_g_per_l: float | None = Field( - default=None, description="Concentration in grams per liter" - ) - concentration_molar: float | None = Field( - default=None, description="Concentration in molar (M)" - ) - specifications: list[str] | None = Field( - default=None, - description="Additional specifications (e.g., 'without_amino_acids')", - ) - - -class MediaAdditiveInfo(BaseModel): - """Information about media additives (e.g., butanol for filamentation).""" - - compound: str = Field(..., description="Additive compound name") - concentration_percent: float | None = Field( - default=None, description="Concentration as percentage (w/v)" - ) - description: str | None = Field( - default=None, description="Additional context about the additive" - ) - - -class MediaInfo(BaseModel): - """Growth media specification.""" - - name: str = Field( - ..., - description="Canonical or descriptive media name " - "(minimal, synthetic_complete, YPD, etc.)", - ) - carbon_source: list[CompoundInfo] | None = Field( - default=None, description="Carbon source compounds and concentrations" - ) - nitrogen_source: list[CompoundInfo] | None = Field( - default=None, description="Nitrogen source compounds and concentrations" - ) - phosphate_source: list[CompoundInfo] | None = Field( - default=None, description="Phosphate source compounds and concentrations" - ) - additives: list[MediaAdditiveInfo] | None = Field( - default=None, - description="Additional media components (e.g., butanol for filamentation)", - ) - - @field_validator("carbon_source", "nitrogen_source", mode="before") - @classmethod - def validate_compound_list(cls, v): - """Validate compound lists and handle 'unspecified' strings.""" - if v is None: - return None - if isinstance(v, str): - if v == "unspecified": - warnings.warn( - "Compound source specified as string 'unspecified'. " - "Should be null/omitted or a structured list.", - UserWarning, - ) - return None - # Try to parse as single compound - return [{"compound": v}] - return v - - -class GrowthPhaseInfo(BaseModel): - """Growth phase information at harvest.""" - - od600: float | None = Field( - default=None, description="Optical density at 600nm at harvest" - ) - od600_tolerance: float | None = Field( - default=None, description="Measurement tolerance for OD600" - ) - stage: str | None = Field( - default=None, description="Growth stage (preferred field name)" - ) - phase: str | None = Field( - default=None, - description="Growth stage (alias for 'stage' for backward compatibility)", - ) - description: str | None = Field( - default=None, description="Additional context about growth phase" - ) - - @field_validator("stage", "phase", mode="before") - @classmethod - def validate_stage(cls, v): - """Validate stage and warn if not a common value.""" - if v is None: - return None - # Get the list of known stages from the enum - known_stages = {stage.value for stage in GrowthStage} - if v not in known_stages: - warnings.warn( - f"Growth stage '{v}' not in recognized stages: {known_stages}", - UserWarning, - ) - return v - - @model_validator(mode="after") - def check_stage_phase_consistency(self): - """Ensure stage and phase are consistent if both provided.""" - if self.stage and self.phase and self.stage != self.phase: - raise ValueError( - "Inconsistent growth phase: " - f"stage='{self.stage}' vs phase='{self.phase}'" - ) - return self - - -class ChemicalTreatmentInfo(BaseModel): - """Chemical treatment applied to cultures.""" - - compound: str = Field(..., description="Chemical compound name") - concentration_percent: float | None = Field( - default=None, description="Concentration as percentage" - ) - concentration_molar: float | None = Field( - default=None, description="Concentration in molar (M)" - ) - duration_minutes: int | None = Field( - default=None, description="Duration in minutes" - ) - duration_hours: float | None = Field(default=None, description="Duration in hours") - target_pH: float | None = Field( - default=None, description="Target pH for pH adjustments" - ) - description: str | None = Field( - default=None, description="Additional context about the treatment" - ) - - -class DrugTreatmentInfo(ChemicalTreatmentInfo): - """Drug treatment - same structure as chemical treatment.""" - - pass - - -class HeatTreatmentInfo(BaseModel): - """Heat treatment information.""" - - duration_minutes: int = Field( - ..., description="Duration of heat treatment in minutes" - ) - description: str | None = Field( - default=None, description="Additional description of treatment" - ) - - -class TemperatureShiftInfo(BaseModel): - """Temperature shift for heat shock experiments.""" - - initial_temperature_celsius: float = Field( - ..., description="Initial cultivation temperature in Celsius" - ) - temperature_shift_celsius: float = Field( - ..., description="Temperature after shift in Celsius" - ) - temperature_shift_duration_minutes: int | None = Field( - default=None, description="Duration of temperature shift in minutes" - ) - description: str | None = Field( - default=None, description="Additional context about the temperature shift" - ) - - -class InductionInfo(BaseModel): - """Induction information for expression systems.""" - - inducer: CompoundInfo = Field(..., description="Inducer compound and concentration") - duration_hours: float | None = Field( - default=None, description="Duration of induction in hours" - ) - duration_minutes: int | None = Field( - default=None, description="Duration of induction in minutes" - ) - description: str | None = Field( - default=None, description="Additional context about the induction" - ) - - -class EnvironmentalConditions(BaseModel): - """Environmental conditions for sample cultivation.""" - - temperature_celsius: float | None = Field( - default=None, description="Cultivation temperature in Celsius" - ) - cultivation_method: str | None = Field( - default=None, - description="Cultivation method (e.g., 'batch_culture', 'chemostat')", - ) - growth_phase_at_harvest: GrowthPhaseInfo | None = Field( - default=None, description="Growth phase at time of harvest" - ) - media: MediaInfo | None = Field( - default=None, description="Growth media specification" - ) - chemical_treatment: ChemicalTreatmentInfo | None = Field( - default=None, description="Chemical treatment applied" - ) - drug_treatment: DrugTreatmentInfo | None = Field( - default=None, - description="Drug treatment applied (same structure as chemical_treatment)", - ) - heat_treatment: HeatTreatmentInfo | None = Field( - default=None, description="Heat treatment applied" - ) - temperature_shift: TemperatureShiftInfo | None = Field( - default=None, description="Temperature shift for heat shock experiments" - ) - induction: InductionInfo | None = Field( - default=None, description="Induction system for expression experiments" - ) - incubation_duration_hours: float | None = Field( - default=None, description="Total incubation duration in hours" - ) - incubation_duration_minutes: int | None = Field( - default=None, description="Total incubation duration in minutes" - ) - description: str | None = Field( - default=None, description="Additional descriptive information" - ) - - model_config = ConfigDict(extra="allow") - - @model_validator(mode="after") - def warn_extra_fields(self): - """Warn about any extra fields not in the specification.""" - known_fields = { - "temperature_celsius", - "cultivation_method", - "growth_phase_at_harvest", - "media", - "chemical_treatment", - "drug_treatment", - "heat_treatment", - "temperature_shift", - "induction", - "incubation_duration_hours", - "incubation_duration_minutes", - "description", - } - extra = set(self.__dict__.keys()) - known_fields - if extra: - warnings.warn( - f"EnvironmentalConditions contains non-standard fields: {extra}. " - "Consider adding to specification.", - UserWarning, - ) - return self - - -class ExperimentalConditions(BaseModel): - """Experimental conditions including environmental and other parameters.""" - - environmental_conditions: EnvironmentalConditions | None = Field( - default=None, description="Environmental cultivation conditions" - ) - strain_background: str | dict[str, Any] | None = Field( - default=None, - description="Strain background information (string or flexible dict structure)", - ) - - model_config = ConfigDict(extra="allow") - - @model_validator(mode="after") - def warn_extra_fields(self): - """Warn about any extra fields not in the specification.""" - known_fields = {"environmental_conditions", "strain_background"} - extra = set(self.__dict__.keys()) - known_fields - if extra: - warnings.warn( - f"ExperimentalConditions contains non-standard fields: {extra}. " - "Consider adding to specification.", - UserWarning, - ) - return self - - -class ClassLabelType(BaseModel): - """Categorical data type with class labels.""" - - names: list[str] = Field(..., description="List of possible class names") +class FeatureInfo(BaseModel): + """ + Information about a dataset feature/column. + Minimal required fields with flexible dtype handling. -class FeatureInfo(BaseModel): - """Information about a dataset feature/column.""" + """ name: str = Field(..., description="Column name in the data") - dtype: str | dict[str, ClassLabelType] = Field( + dtype: str | dict[str, Any] = Field( ..., - description="Data type (string, int64, float64, etc.) " - "or categorical class labels", + description="Data type (string, int64, float64, etc.) or class_label dict", ) - description: str = Field(..., description="Detailed description of the field") - role: FieldRole | None = Field( + description: str = Field(..., description="Description of the field") + role: str | None = Field( default=None, - description="Semantic role of the feature (e.g., 'target_identifier', " - "'regulator_identifier', 'quantitative_measure')", + description="Optional semantic role. 'experimental_condition' has special behavior.", ) definitions: dict[str, Any] | None = Field( default=None, - description="Definitions for categorical field values " - "with experimental conditions", + description="For experimental_condition fields: definitions per value", ) - @field_validator("dtype", mode="before") - @classmethod - def validate_dtype(cls, v): - """Validate and normalize dtype field.""" - if isinstance(v, str): - return v - elif isinstance(v, dict): - # Handle class_label structure - if "class_label" in v: - # Convert to our ClassLabelType structure - class_label_data = v["class_label"] - if isinstance(class_label_data, dict) and "names" in class_label_data: - return {"class_label": ClassLabelType(**class_label_data)} - else: - raise ValueError( - "Invalid class_label structure: expected dict " - f"with 'names' key, got {class_label_data}" - ) - else: - raise ValueError( - "Unknown dtype structure: expected 'class_label' key " - f"in dict, got keys: {list(v.keys())}" - ) - else: - raise ValueError( - "dtype must be a string or dict with " - f"class_label info, got {type(v)}: {v}" - ) - - def get_dtype_summary(self) -> str: - """Get a human-readable summary of the data type.""" - if isinstance(self.dtype, str): - return self.dtype - elif isinstance(self.dtype, dict) and "class_label" in self.dtype: - names = self.dtype["class_label"].names - return f"categorical ({len(names)} classes: {', '.join(names)})" - else: - return str(self.dtype) - class PartitioningInfo(BaseModel): """Partitioning configuration for datasets.""" @@ -408,13 +59,6 @@ class PartitioningInfo(BaseModel): ) -class DataFileInfo(BaseModel): - """Information about data files.""" - - split: str = Field(default="train", description="Dataset split name") - path: str = Field(..., description="Path to data file(s)") - - class DatasetInfo(BaseModel): """Dataset structure information.""" @@ -424,8 +68,20 @@ class DatasetInfo(BaseModel): ) +class DataFileInfo(BaseModel): + """Information about data files.""" + + split: str = Field(default="train", description="Dataset split name") + path: str = Field(..., description="Path to data file(s)") + + class DatasetConfig(BaseModel): - """Configuration for a dataset within a repository.""" + """ + Configuration for a dataset within a repository. + + Uses extra="allow" to accept arbitrary experimental_conditions and other fields. + + """ config_name: str = Field(..., description="Unique configuration identifier") description: str = Field(..., description="Human-readable description") @@ -439,12 +95,11 @@ class DatasetConfig(BaseModel): metadata_fields: list[str] | None = Field( default=None, description="Fields for embedded metadata extraction" ) - experimental_conditions: ExperimentalConditions | None = Field( - default=None, description="Experimental conditions for this config" - ) data_files: list[DataFileInfo] = Field(..., description="Data file information") dataset_info: DatasetInfo = Field(..., description="Dataset structure information") + model_config = ConfigDict(extra="allow") + @field_validator("applies_to") @classmethod def applies_to_only_for_metadata(cls, v, info): @@ -467,39 +122,16 @@ def metadata_fields_validation(cls, v): return v -class BasicMetadata(BaseModel): - """Basic dataset metadata.""" - - license: str | None = Field(default=None, description="Dataset license") - language: list[str] | None = Field(default=None, description="Dataset languages") - tags: list[str] | None = Field(default=None, description="Descriptive tags") - pretty_name: str | None = Field( - default=None, description="Human-readable dataset name" - ) - size_categories: list[str] | None = Field( - default=None, description="Dataset size categories" - ) +class DatasetCard(BaseModel): + """ + Complete dataset card model. + Uses extra="allow" to accept arbitrary top-level metadata and + experimental_conditions. -class DatasetCard(BaseModel): - """Complete dataset card model.""" + """ configs: list[DatasetConfig] = Field(..., description="Dataset configurations") - experimental_conditions: ExperimentalConditions | None = Field( - default=None, description="Top-level experimental conditions for all configs" - ) - license: str | None = Field(default=None, description="Dataset license") - language: list[str] | None = Field(default=None, description="Dataset languages") - tags: list[str] | None = Field(default=None, description="Descriptive tags") - pretty_name: str | None = Field( - default=None, description="Human-readable dataset name" - ) - size_categories: list[str] | None = Field( - default=None, description="Dataset size categories" - ) - strain_information: dict[str, Any] | None = Field( - default=None, description="Strain background information" - ) model_config = ConfigDict(extra="allow") diff --git a/tfbpapi/datainfo/sample_manager.py b/tfbpapi/datainfo/sample_manager.py new file mode 100644 index 0000000..271a0ee --- /dev/null +++ b/tfbpapi/datainfo/sample_manager.py @@ -0,0 +1,774 @@ +""" +Node-based sample representation for flexible filtering across heterogeneous datasets. + +This module provides a lightweight, NoSQL-inspired approach to managing samples from +multiple datasets with varying experimental condition structures. Each sample is +represented as a node with flattened properties, enabling flexible filtering across +datasets with different metadata schemas. + +Key Components: +- SampleNode: Represents a single sample with flattened properties +- SampleNodeCollection: In-memory storage with efficient indexing +- ActiveSet: Filtered collection of samples supporting set operations +- SampleFilter: MongoDB-style query language for filtering +- ConditionFlattener: Handles heterogeneous experimental condition structures +- SampleManager: Main API for loading, filtering, and managing samples + +Example Usage: + >>> manager = SampleManager() + >>> manager.load_from_datacard("BrentLab/harbison_2004", "harbison_2004") + >>> active = manager.filter_all({"carbon_source": {"$contains": "glucose"}}) + >>> print(f"Found {len(active)} glucose-grown samples") +""" + +from __future__ import annotations + +from dataclasses import dataclass, field +from datetime import datetime +from pathlib import Path +from typing import Any, Iterator + +import duckdb +import pandas as pd + + +def get_nested_value(data: dict, path: str) -> Any: + """ + Navigate nested dict using dot notation. + + Handles missing intermediate keys gracefully by returning None. + + :param data: Dictionary to navigate + :param path: Dot-separated path (e.g., "environmental_conditions.media.name") + :return: Value at path or None if not found + """ + if not isinstance(data, dict): + return None + + keys = path.split(".") + current = data + + for key in keys: + if not isinstance(current, dict) or key not in current: + return None + current = current[key] + + return current + + +def flatten_compound_list(compounds: list[dict] | str | None) -> str: + """ + Flatten compound list to comma-separated string. + + Handles various representations: + - List of dicts: Extract compound names + - String "unspecified": Return as-is + - None or empty list: Return "unspecified" + + :param compounds: Compound list or string + :return: Comma-separated compound names or "unspecified" + """ + if compounds is None or compounds == "unspecified": + return "unspecified" + + if isinstance(compounds, str): + return compounds + + if isinstance(compounds, list): + if not compounds: + return "unspecified" + compound_names = [ + c.get("compound", "") for c in compounds if isinstance(c, dict) + ] + return ", ".join(compound_names) if compound_names else "unspecified" + + return "unspecified" + + +@dataclass +class SampleNode: + """ + Represents a single sample with flattened experimental condition metadata. + + A sample is uniquely identified by (repo_id, config_name, sample_id) and contains + flattened properties from the 3-level experimental conditions hierarchy plus + selected metadata field values. + + Attributes: + sample_id: Unique identifier within config + repo_id: Dataset repository (e.g., "BrentLab/harbison_2004") + config_name: Configuration name (e.g., "harbison_2004") + properties: Flattened experimental condition properties + metadata_fields: Selected data fields (regulator, target, etc.) + property_sources: Tracks which level each property came from (repo/config/field/missing) + """ + + sample_id: str + repo_id: str + config_name: str + properties: dict[str, Any] = field(default_factory=dict) + metadata_fields: dict[str, Any] = field(default_factory=dict) + property_sources: dict[str, str] = field(default_factory=dict) + + def global_id(self) -> str: + """ + Generate unique identifier across all datasets. + + Format: {repo_id}:{config_name}:{sample_id} + + :return: Global sample ID + """ + return f"{self.repo_id}:{self.config_name}:{self.sample_id}" + + def get_property(self, key: str, default: Any = None) -> Any: + """ + Get property value with default fallback. + + :param key: Property name + :param default: Default value if property not found + :return: Property value or default + """ + return self.properties.get(key, default) + + def __repr__(self) -> str: + """String representation showing global ID and property count.""" + return f"SampleNode({self.global_id()}, {len(self.properties)} properties)" + + +class ConditionFlattener: + """ + Flattens experimental conditions from 3-level hierarchy into node properties. + + Handles heterogeneity in experimental condition structures across datasets: + - Variable nesting depths + - Missing/optional fields + - Compound lists + - Multi-level hierarchical overrides + + Resolution order: repo-level -> config-level -> field-level (field overrides all) + + This flattener dynamically discovers all properties without hardcoded schemas. + It recursively flattens nested structures using dot notation for keys. + """ + + @staticmethod + def _flatten_dict(d: dict, parent_key: str = "", sep: str = ".") -> dict[str, Any]: + """ + Recursively flatten nested dict using dot notation. + + :param d: Dictionary to flatten + :param parent_key: Parent key for recursion + :param sep: Separator for nested keys + :return: Flattened dict with dot-notation keys + """ + items = [] + for k, v in d.items(): + new_key = f"{parent_key}{sep}{k}" if parent_key else k + + if isinstance(v, dict): + # Recursively flatten nested dicts + items.extend( + ConditionFlattener._flatten_dict(v, new_key, sep=sep).items() + ) + elif isinstance(v, list) and v and isinstance(v[0], dict): + # Handle list of dicts (like compound lists) + # Store both flattened string and structured version + items.append((new_key, flatten_compound_list(v))) + items.append((f"_{new_key}_structured", v)) + else: + # Store primitive values as-is + items.append((new_key, v)) + + return dict(items) + + @classmethod + def flatten_conditions( + cls, + repo_conditions: dict | None, + config_conditions: dict | None, + field_conditions: dict | None, + ) -> tuple[dict[str, Any], dict[str, str]]: + """ + Flatten 3-level hierarchy into properties dict. + + Dynamically discovers and flattens all properties without hardcoded schemas. + Nested structures are flattened using dot notation (e.g., "media.carbon_source"). + + :param repo_conditions: Top-level experimental_conditions + :param config_conditions: Config-level experimental_conditions + :param field_conditions: Field-level condition definition + :return: (properties, property_sources) tuple + """ + properties = {} + sources = {} + + # Flatten each level + levels = [ + (repo_conditions, "repo"), + (config_conditions, "config"), + (field_conditions, "field"), + ] + + for conditions, level_name in levels: + if conditions is None: + continue + + # Flatten this level + flattened = cls._flatten_dict(conditions) + + # Merge into properties (later levels override earlier) + for key, value in flattened.items(): + properties[key] = value + sources[key] = level_name + + return properties, sources + + +class SampleNodeCollection: + """ + In-memory collection of sample nodes with efficient indexing. + + Storage strategy: + - Primary index: {(repo_id, config_name): {sample_id: SampleNode}} + - Enables fast dataset-level and cross-dataset operations + - Memory-efficient for typical workloads (1K-100K samples) + + Attributes: + _nodes: Two-level dict storing all nodes + """ + + def __init__(self): + """Initialize empty collection.""" + self._nodes: dict[tuple[str, str], dict[str, SampleNode]] = {} + + def add_node(self, node: SampleNode): + """ + Add sample node to collection. + + :param node: SampleNode to add + """ + key = (node.repo_id, node.config_name) + if key not in self._nodes: + self._nodes[key] = {} + self._nodes[key][node.sample_id] = node + + def get_node( + self, repo_id: str, config_name: str, sample_id: str + ) -> SampleNode | None: + """ + Get specific node. + + :param repo_id: Repository ID + :param config_name: Config name + :param sample_id: Sample ID + :return: SampleNode or None if not found + """ + key = (repo_id, config_name) + if key not in self._nodes: + return None + return self._nodes[key].get(sample_id) + + def get_node_by_global_id(self, global_id: str) -> SampleNode | None: + """ + Get node by global ID. + + :param global_id: Global ID in format {repo_id}:{config_name}:{sample_id} + :return: SampleNode or None if not found + """ + parts = global_id.split(":", 2) + if len(parts) != 3: + return None + return self.get_node(parts[0], parts[1], parts[2]) + + def iter_dataset_nodes( + self, repo_id: str, config_name: str + ) -> Iterator[SampleNode]: + """ + Iterate over nodes in specific dataset. + + :param repo_id: Repository ID + :param config_name: Config name + :return: Iterator over SampleNodes + """ + key = (repo_id, config_name) + if key in self._nodes: + yield from self._nodes[key].values() + + def iter_all_nodes(self) -> Iterator[SampleNode]: + """ + Iterate over all nodes in collection. + + :return: Iterator over all SampleNodes + """ + for dataset_nodes in self._nodes.values(): + yield from dataset_nodes.values() + + def get_dataset_keys(self) -> list[tuple[str, str]]: + """ + Get list of loaded (repo_id, config_name) pairs. + + :return: List of dataset keys + """ + return list(self._nodes.keys()) + + def count_total_nodes(self) -> int: + """ + Count total nodes in collection. + + :return: Total number of nodes + """ + return sum(len(nodes) for nodes in self._nodes.values()) + + def count_dataset_nodes(self, repo_id: str, config_name: str) -> int: + """ + Count nodes in specific dataset. + + :param repo_id: Repository ID + :param config_name: Config name + :return: Number of nodes in dataset + """ + key = (repo_id, config_name) + return len(self._nodes.get(key, {})) + + +@dataclass +class ActiveSet: + """ + Represents a collection of active samples with provenance tracking. + + ActiveSet supports set operations (union, intersection, difference) and + maintains metadata about how the set was created. This enables building + complex filters incrementally and tracking analysis provenance. + + Attributes: + sample_ids: Set of global sample IDs + name: Optional name for this set + description: Optional description + source_filter: Filter query that created this set + created_at: Timestamp + parent_set: ID of parent set (for tracking lineage) + """ + + sample_ids: set[str] = field(default_factory=set) + name: str | None = None + description: str | None = None + source_filter: dict | None = None + created_at: datetime = field(default_factory=datetime.now) + parent_set: str | None = None + + def __len__(self) -> int: + """Return number of samples in set.""" + return len(self.sample_ids) + + def union(self, other: ActiveSet, name: str | None = None) -> ActiveSet: + """ + Create new set with samples from both sets. + + :param other: Another ActiveSet + :param name: Optional name for new set + :return: New ActiveSet with union + """ + return ActiveSet( + sample_ids=self.sample_ids | other.sample_ids, + name=name or f"{self.name}_union_{other.name}", + description=f"Union of {self.name} and {other.name}", + ) + + def intersection(self, other: ActiveSet, name: str | None = None) -> ActiveSet: + """ + Create new set with samples in both sets. + + :param other: Another ActiveSet + :param name: Optional name for new set + :return: New ActiveSet with intersection + """ + return ActiveSet( + sample_ids=self.sample_ids & other.sample_ids, + name=name or f"{self.name}_intersect_{other.name}", + description=f"Intersection of {self.name} and {other.name}", + ) + + def difference(self, other: ActiveSet, name: str | None = None) -> ActiveSet: + """ + Create new set with samples in this set but not other. + + :param other: Another ActiveSet + :param name: Optional name for new set + :return: New ActiveSet with difference + """ + return ActiveSet( + sample_ids=self.sample_ids - other.sample_ids, + name=name or f"{self.name}_minus_{other.name}", + description=f"Samples in {self.name} but not {other.name}", + ) + + def to_sample_ids(self) -> list[str]: + """ + Export as list of global sample IDs. + + :return: Sorted list of global IDs + """ + return sorted(self.sample_ids) + + def __repr__(self) -> str: + """String representation showing name and size.""" + return f"ActiveSet(name={self.name}, size={len(self)})" + + +class SampleFilter: + """ + Evaluate filter expressions against sample nodes. + + Implements MongoDB-style query language for filtering heterogeneous sample data. + + Supported operators: + - $eq, $ne: Equality/inequality (default is $eq) + - $gt, $gte, $lt, $lte: Numeric comparisons + - $in, $nin: List membership + - $contains: String/list containment (case-insensitive) + - $exists: Field presence check + - $and, $or: Logical operators + + Example queries: + {"temperature_celsius": 30} # Simple equality + {"carbon_source": {"$contains": "glucose"}} # Contains check + {"$and": [{"temp": {"$gte": 25}}, {"temp": {"$lte": 35}}]} # Range + """ + + @staticmethod + def _matches_operator(value: Any, operator: str, target: Any) -> bool: + """ + Check if value matches operator condition. + + :param value: Actual property value + :param operator: Operator string (e.g., "$eq", "$contains") + :param target: Target value to compare against + :return: True if condition matches + """ + # Handle None values + if value is None: + if operator == "$exists": + return not target # $exists: false matches None + return operator == "$eq" and target is None + + # Equality operators + if operator == "$eq": + return value == target + if operator == "$ne": + return value != target + + # Numeric comparisons + if operator in ["$gt", "$gte", "$lt", "$lte"]: + try: + value_num = ( + float(value) if not isinstance(value, (int, float)) else value + ) + target_num = ( + float(target) if not isinstance(target, (int, float)) else target + ) + if operator == "$gt": + return value_num > target_num + if operator == "$gte": + return value_num >= target_num + if operator == "$lt": + return value_num < target_num + if operator == "$lte": + return value_num <= target_num + except (ValueError, TypeError): + return False + + # List membership + if operator == "$in": + return value in target if isinstance(target, (list, set, tuple)) else False + if operator == "$nin": + return ( + value not in target if isinstance(target, (list, set, tuple)) else True + ) + + # Contains check (case-insensitive for strings) + if operator == "$contains": + if isinstance(value, str) and isinstance(target, str): + return target.lower() in value.lower() + if isinstance(value, (list, tuple)): + return target in value + return False + + # Existence check + if operator == "$exists": + return bool(target) # $exists: true matches any non-None value + + return False + + @classmethod + def _matches_condition(cls, node: SampleNode, field: str, condition: Any) -> bool: + """ + Check if node matches a single field condition. + + :param node: SampleNode to check + :param field: Property name + :param condition: Condition value or operator dict + :return: True if matches + """ + # Get value from node + value = node.get_property(field) + + # Simple equality check + if not isinstance(condition, dict): + return value == condition + + # Operator-based checks + for operator, target in condition.items(): + if not cls._matches_operator(value, operator, target): + return False + + return True + + @classmethod + def matches(cls, node: SampleNode, query: dict) -> bool: + """ + Check if node matches filter query. + + :param node: SampleNode to check + :param query: Filter query dict + :return: True if node matches all conditions + """ + # Handle logical operators + if "$and" in query: + return all(cls.matches(node, sub_query) for sub_query in query["$and"]) + + if "$or" in query: + return any(cls.matches(node, sub_query) for sub_query in query["$or"]) + + # Check all field conditions (implicit AND) + for field, condition in query.items(): + if field.startswith("$"): # Skip logical operators + continue + if not cls._matches_condition(node, field, condition): + return False + + return True + + @classmethod + def filter_nodes( + cls, nodes: Iterator[SampleNode], query: dict + ) -> Iterator[SampleNode]: + """ + Filter nodes matching query (generator for memory efficiency). + + :param nodes: Iterator of SampleNodes + :param query: Filter query dict + :return: Iterator of matching nodes + """ + for node in nodes: + if cls.matches(node, query): + yield node + + +class SampleManager: + """ + Main interface for node-based sample management. + + SampleManager provides a flexible, NoSQL-inspired approach to managing samples + from multiple heterogeneous datasets. It replaces the table-based MetadataManager + with a node-based system that handles varying experimental condition structures. + + Key Features: + - Load samples from DataCard or DuckDB + - Filter using MongoDB-style queries + - Create and manage ActiveSets + - Export to DuckDB for SQL analysis + - Handle multi-dataset scenarios + + Example: + >>> manager = SampleManager() + >>> manager.load_from_datacard("BrentLab/harbison_2004", "harbison_2004") + >>> active = manager.filter_all({"carbon_source": {"$contains": "glucose"}}) + >>> print(f"Found {len(active)} samples") + """ + + def __init__( + self, + duckdb_conn: duckdb.DuckDBPyConnection | None = None, + cache_dir: Path | None = None, + ): + """ + Initialize SampleManager. + + :param duckdb_conn: Optional shared DuckDB connection for integration with HfQueryAPI + :param cache_dir: Optional cache directory for DataCard fetches + """ + self._collection = SampleNodeCollection() + self._active_sets: dict[str, ActiveSet] = {} + self._duckdb_conn = ( + duckdb_conn if duckdb_conn is not None else duckdb.connect(":memory:") + ) + self._cache_dir = cache_dir + self._flattener = ConditionFlattener() + + def get_active_configs(self) -> list[tuple[str, str]]: + """ + Get list of loaded (repo_id, config_name) pairs. + + :return: List of dataset keys + """ + return self._collection.get_dataset_keys() + + def get_summary(self) -> pd.DataFrame: + """ + Get summary of loaded samples. + + Returns DataFrame with columns: + - repo_id: Repository ID + - config_name: Configuration name + - sample_count: Number of samples + - properties: Common properties available + + :return: Summary DataFrame + """ + rows = [] + for repo_id, config_name in self.get_active_configs(): + count = self._collection.count_dataset_nodes(repo_id, config_name) + # Sample first node to get property names + first_node = next( + self._collection.iter_dataset_nodes(repo_id, config_name), None + ) + properties = list(first_node.properties.keys()) if first_node else [] + + rows.append( + { + "repo_id": repo_id, + "config_name": config_name, + "sample_count": count, + "properties": properties, + } + ) + + return pd.DataFrame(rows) + + def filter_all(self, query: dict, name: str | None = None) -> ActiveSet: + """ + Filter across all loaded samples. + + :param query: Filter query dict (MongoDB-style) + :param name: Optional name for ActiveSet + :return: ActiveSet with matching samples + """ + matching_nodes = SampleFilter.filter_nodes( + self._collection.iter_all_nodes(), query + ) + sample_ids = {node.global_id() for node in matching_nodes} + + return ActiveSet( + sample_ids=sample_ids, + name=name or "filtered_samples", + source_filter=query, + ) + + def filter_dataset( + self, + repo_id: str, + config_name: str, + query: dict, + name: str | None = None, + ) -> ActiveSet: + """ + Filter samples within specific dataset. + + :param repo_id: Repository ID + :param config_name: Config name + :param query: Filter query dict + :param name: Optional name for ActiveSet + :return: ActiveSet with matching samples + """ + matching_nodes = SampleFilter.filter_nodes( + self._collection.iter_dataset_nodes(repo_id, config_name), query + ) + sample_ids = {node.global_id() for node in matching_nodes} + + return ActiveSet( + sample_ids=sample_ids, + name=name or f"{config_name}_filtered", + source_filter=query, + ) + + def get_sample(self, global_id: str) -> SampleNode | None: + """ + Retrieve specific sample by global ID. + + :param global_id: Global ID in format {repo_id}:{config_name}:{sample_id} + :return: SampleNode or None if not found + """ + return self._collection.get_node_by_global_id(global_id) + + def get_samples_by_ids(self, sample_ids: list[str]) -> list[SampleNode]: + """ + Batch retrieve samples. + + :param sample_ids: List of global IDs + :return: List of SampleNodes (may be shorter if some not found) + """ + nodes = [] + for global_id in sample_ids: + node = self.get_sample(global_id) + if node is not None: + nodes.append(node) + return nodes + + def save_active_set(self, name: str, active_set: ActiveSet): + """ + Save named active set for later use. + + :param name: Name to save under + :param active_set: ActiveSet to save + """ + self._active_sets[name] = active_set + + def get_active_set(self, name: str) -> ActiveSet | None: + """ + Retrieve saved active set. + + :param name: Name of saved set + :return: ActiveSet or None if not found + """ + return self._active_sets.get(name) + + def list_active_sets(self) -> list[str]: + """ + List all saved active set names. + + :return: List of set names + """ + return list(self._active_sets.keys()) + + def get_property_distribution( + self, + property_name: str, + dataset_filter: tuple[str, str] | None = None, + ) -> dict[Any, int]: + """ + Get value distribution for a property. + + :param property_name: Property name to analyze + :param dataset_filter: Optional (repo_id, config_name) to limit to specific dataset + :return: Dict mapping values to counts + """ + distribution: dict[Any, int] = {} + + if dataset_filter: + nodes = self._collection.iter_dataset_nodes( + dataset_filter[0], dataset_filter[1] + ) + else: + nodes = self._collection.iter_all_nodes() + + for node in nodes: + value = node.get_property(property_name, "missing") + distribution[value] = distribution.get(value, 0) + 1 + + return distribution + + def __repr__(self) -> str: + """String representation showing loaded datasets and sample count.""" + total = self._collection.count_total_nodes() + datasets = len(self.get_active_configs()) + return f"SampleManager({datasets} datasets, {total} samples)" diff --git a/tfbpapi/tests/datainfo/huggingface_collection_datacards.txt b/tfbpapi/tests/datainfo/huggingface_collection_datacards.txt index d5d3394..68d5482 100644 --- a/tfbpapi/tests/datainfo/huggingface_collection_datacards.txt +++ b/tfbpapi/tests/datainfo/huggingface_collection_datacards.txt @@ -13,18 +13,17 @@ pretty_name: Barkai ChEC-seq Compendium size_categories: - 100M- @@ -1611,36 +1582,35 @@ configs: - condition - regulator_locus_tag experimental_conditions: - environmental_conditions: - # Mahendrawada et al 2025: "30 °C culture" - temperature_celsius: 30 - cultivation_method: unspecified - growth_phase_at_harvest: - # Mahendrawada et al 2025: "A600 of ~1.0" - od600: 1.0 - media: - # Mahendrawada et al 2025: "synthetic complete (SC) media" - name: synthetic_complete - carbon_source: unspecified - nitrogen_source: - - compound: yeast_nitrogen_base - # Mahendrawada et al 2025: 1.7 g/L (without ammonium sulfate or amino acids (BD Difco)) - concentration_percent: 0.17 - specifications: - - without_ammonium_sulfate - - without_amino_acids - - compound: ammonium_sulfate - # Mahendrawada et al 2025: 5 g/L - concentration_percent: 0.5 - - compound: amino_acid_dropout_mix - # Mahendrawada et al 2025: 0.6 g/L - concentration_percent: 0.06 - - compound: adenine_sulfate - # Mahendrawada et al 2025: 40 μg/ml = 0.04 g/L - concentration_percent: 0.004 - - compound: uracil - # Mahendrawada et al 2025: 2 μg/ml = 0.002 g/L - concentration_percent: 0.0002 + # Mahendrawada et al 2025: "30 °C culture" + temperature_celsius: 30 + cultivation_method: unspecified + growth_phase_at_harvest: + # Mahendrawada et al 2025: "A600 of ~1.0" + od600: 1.0 + media: + # Mahendrawada et al 2025: "synthetic complete (SC) media" + name: synthetic_complete + carbon_source: unspecified + nitrogen_source: + - compound: yeast_nitrogen_base + # Mahendrawada et al 2025: 1.7 g/L (without ammonium sulfate or amino acids (BD Difco)) + concentration_percent: 0.17 + specifications: + - without_ammonium_sulfate + - without_amino_acids + - compound: ammonium_sulfate + # Mahendrawada et al 2025: 5 g/L + concentration_percent: 0.5 + - compound: amino_acid_dropout_mix + # Mahendrawada et al 2025: 0.6 g/L + concentration_percent: 0.06 + - compound: adenine_sulfate + # Mahendrawada et al 2025: 40 μg/ml = 0.04 g/L + concentration_percent: 0.004 + - compound: uracil + # Mahendrawada et al 2025: 2 μg/ml = 0.002 g/L + concentration_percent: 0.0002 data_files: - split: train path: reprocess_diffcontrol_5prime.parquet @@ -1731,22 +1701,21 @@ language: - en pretty_name: Rossi ChIP-exo 2021 experimental_conditions: - environmental_conditions: - temperature_celsius: 25 - cultivation_method: unspecified - growth_phase_at_harvest: - phase: mid_log - od600: 0.8 - media: - name: yeast_peptone_dextrose - carbon_source: - - compound: D-glucose - concentration_percent: unspecified - nitrogen_source: - - compound: yeast_extract - concentration_percent: unspecified - - compound: peptone - concentration_percent: unspecified + temperature_celsius: 25 + cultivation_method: unspecified + growth_phase_at_harvest: + phase: mid_log + od600: 0.8 + media: + name: yeast_peptone_dextrose + carbon_source: + - compound: D-glucose + concentration_percent: unspecified + nitrogen_source: + - compound: yeast_extract + concentration_percent: unspecified + - compound: peptone + concentration_percent: unspecified # Heat shock applied only to SAGA strains # note that im not sure which strains this diff --git a/tfbpapi/tests/datainfo/test_datacard.py b/tfbpapi/tests/datainfo/test_datacard.py index 098d27f..1e061ee 100644 --- a/tfbpapi/tests/datainfo/test_datacard.py +++ b/tfbpapi/tests/datainfo/test_datacard.py @@ -615,3 +615,161 @@ def test_extract_partition_values_fetch_error( # Should return empty set on error assert values == set() + + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_get_field_attribute( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + ): + """Test extracting specific attributes from field definitions.""" + # Create sample card data with condition definitions + card_data = { + "configs": [ + { + "config_name": "test_config", + "description": "Test configuration", + "dataset_type": "annotated_features", + "data_files": [{"split": "train", "path": "test.parquet"}], + "dataset_info": { + "features": [ + { + "name": "condition", + "dtype": "string", + "description": "Experimental condition", + "role": "experimental_condition", + "definitions": { + "YPD": { + "media": { + "name": "YPD", + "carbon_source": [ + { + "compound": "D-glucose", + "concentration_percent": 2, + } + ], + "nitrogen_source": [ + { + "compound": "yeast_extract", + "concentration_percent": 1, + }, + { + "compound": "peptone", + "concentration_percent": 2, + }, + ], + }, + "temperature_celsius": 30, + }, + "HEAT": { + "media": { + "name": "YPD", + "carbon_source": [ + { + "compound": "D-glucose", + "concentration_percent": 2, + } + ], + }, + "temperature_celsius": 37, + }, + "SM": { + "media": { + "name": "synthetic_complete", + "carbon_source": "unspecified", + "nitrogen_source": "unspecified", + } + }, + }, + } + ] + }, + } + ] + } + + mock_card_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_card_fetcher_instance + mock_card_fetcher_instance.fetch.return_value = card_data + + datacard = DataCard(test_repo_id) + + # Test extracting media attribute + media_specs = datacard.get_field_attribute( + "test_config", "condition", "media" + ) + + assert "YPD" in media_specs + assert "HEAT" in media_specs + assert "SM" in media_specs + + # Check YPD media specification + assert media_specs["YPD"]["name"] == "YPD" + assert len(media_specs["YPD"]["carbon_source"]) == 1 + assert media_specs["YPD"]["carbon_source"][0]["compound"] == "D-glucose" + assert len(media_specs["YPD"]["nitrogen_source"]) == 2 + + # Check HEAT media specification + assert media_specs["HEAT"]["name"] == "YPD" + assert len(media_specs["HEAT"]["carbon_source"]) == 1 + + # Check SM media specification + assert media_specs["SM"]["name"] == "synthetic_complete" + assert media_specs["SM"]["carbon_source"] == "unspecified" + + # Test extracting temperature attribute + temp_specs = datacard.get_field_attribute( + "test_config", "condition", "temperature_celsius" + ) + + assert temp_specs["YPD"] == 30 + assert temp_specs["HEAT"] == 37 + assert temp_specs["SM"] == "unspecified" # SM doesn't have temperature + + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_get_field_attribute_invalid_config( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + minimal_dataset_card_data, + ): + """Test get_field_attribute with invalid config name.""" + mock_card_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_card_fetcher_instance + mock_card_fetcher_instance.fetch.return_value = minimal_dataset_card_data + + datacard = DataCard(test_repo_id) + + with pytest.raises(DataCardError, match="Configuration 'invalid' not found"): + datacard.get_field_attribute("invalid", "condition", "media") + + @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") + @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") + def test_get_field_attribute_invalid_field( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + minimal_dataset_card_data, + ): + """Test get_field_attribute with invalid field name.""" + mock_card_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_card_fetcher_instance + mock_card_fetcher_instance.fetch.return_value = minimal_dataset_card_data + + datacard = DataCard(test_repo_id) + + with pytest.raises( + DataCardError, match="Field 'invalid_field' not found in config" + ): + datacard.get_field_attribute("test_config", "invalid_field", "media") diff --git a/tfbpapi/tests/datainfo/test_metadata_manager.py b/tfbpapi/tests/datainfo/test_metadata_manager.py index 887a599..ad03c5a 100644 --- a/tfbpapi/tests/datainfo/test_metadata_manager.py +++ b/tfbpapi/tests/datainfo/test_metadata_manager.py @@ -4,7 +4,6 @@ import pytest from tfbpapi.datainfo import DataCard, MetadataManager -from tfbpapi.datainfo.models import FieldRole class TestMetadataManagerBasic: @@ -68,52 +67,23 @@ def test_sanitize_view_name(self): assert "/" not in view_name assert "-" not in view_name - def test_format_compound_simple(self): - """Test formatting compound as simple string.""" - mgr = MetadataManager() - result = mgr._format_compound("carbon_source", "D-glucose") - assert result == "carbon_source:D-glucose" - - def test_format_compound_with_percent(self): - """Test formatting compound with concentration percent.""" - mgr = MetadataManager() - compound = {"name": "D-glucose", "concentration_percent": 2.0} - result = mgr._format_compound("carbon_source", compound) - assert result == "carbon_source:D-glucose@2.0%" - - def test_format_compound_with_grams(self): - """Test formatting compound with g/L concentration.""" - mgr = MetadataManager() - compound = {"name": "ammonium_sulfate", "concentration_g_per_l": 5.0} - result = mgr._format_compound("nitrogen_source", compound) - assert result == "nitrogen_source:ammonium_sulfate@5.0g/L" - def test_flatten_condition_definition_empty(self): """Test flattening empty definition.""" mgr = MetadataManager() result = mgr._flatten_condition_definition({}) - assert result["growth_media"] == "unspecified" - assert result["components"] == "" + assert isinstance(result, dict) + assert len(result) == 0 def test_flatten_condition_definition_with_media(self): - """Test flattening definition with media.""" + """Test flattening definition with media name.""" mgr = MetadataManager() definition = { - "environmental_conditions": { - "media": { - "name": "YPD", - "carbon_source": [ - {"name": "D-glucose", "concentration_percent": 2.0} - ], - "nitrogen_source": ["yeast_extract", "peptone"], - } + "media": { + "name": "YPD", } } result = mgr._flatten_condition_definition(definition) assert result["growth_media"] == "YPD" - assert "carbon_source:D-glucose@2.0%" in result["components"] - assert "nitrogen_source:yeast_extract" in result["components"] - assert "nitrogen_source:peptone" in result["components"] class TestComponentSeparators: @@ -138,7 +108,6 @@ def test_flatten_experimental_conditions_empty(self): # Create a simple mock object with no conditions class MockExpConditions: - environmental_conditions = None strain_background = None result = mgr._flatten_experimental_conditions(MockExpConditions()) @@ -149,18 +118,8 @@ def test_flatten_experimental_conditions_with_temperature(self): """Test flattening conditions with temperature.""" mgr = MetadataManager() - class MockEnv: - temperature_celsius = 30 - cultivation_method = None - media = None - growth_phase = None - chemical_treatments = None - drug_treatments = None - heat_treatment = None - induction = None - class MockExpConditions: - environmental_conditions = MockEnv() + temperature_celsius = 30 strain_background = None result = mgr._flatten_experimental_conditions(MockExpConditions()) @@ -170,18 +129,9 @@ def test_flatten_experimental_conditions_with_cultivation_method(self): """Test flattening conditions with cultivation method.""" mgr = MetadataManager() - class MockEnv: + class MockExpConditions: temperature_celsius = None cultivation_method = "chemostat" - media = None - growth_phase = None - chemical_treatments = None - drug_treatments = None - heat_treatment = None - induction = None - - class MockExpConditions: - environmental_conditions = MockEnv() strain_background = None result = mgr._flatten_experimental_conditions(MockExpConditions()) @@ -191,47 +141,21 @@ def test_flatten_experimental_conditions_with_media(self): """Test flattening conditions with media information.""" mgr = MetadataManager() - class MockCompound: - compound = "D-glucose" - concentration_percent = 1.0 - - def model_dump(self): - return { - "name": self.compound, - "concentration_percent": self.concentration_percent, - } - class MockMedia: name = "minimal" - carbon_source = [MockCompound()] - nitrogen_source = None - phosphate_source = None - additives = None - - class MockEnv: - temperature_celsius = None - cultivation_method = None - media = MockMedia() - growth_phase = None - chemical_treatments = None - drug_treatments = None - heat_treatment = None - induction = None class MockExpConditions: - environmental_conditions = MockEnv() + media = MockMedia() strain_background = None result = mgr._flatten_experimental_conditions(MockExpConditions()) assert result["growth_media"] == "minimal" - assert "carbon_source:D-glucose@1.0%" in result["components"] def test_flatten_experimental_conditions_with_strain_background(self): """Test flattening conditions with strain background.""" mgr = MetadataManager() class MockExpConditions: - environmental_conditions = None strain_background = "BY4741" result = mgr._flatten_experimental_conditions(MockExpConditions()) diff --git a/tfbpapi/tests/datainfo/test_models.py b/tfbpapi/tests/datainfo/test_models.py index 8bcee9b..d4701e4 100644 --- a/tfbpapi/tests/datainfo/test_models.py +++ b/tfbpapi/tests/datainfo/test_models.py @@ -1,806 +1,569 @@ -"""Tests for datainfo Pydantic models.""" +""" +Tests for datainfo Pydantic models. + +These tests validate the minimal, flexible models that parse HuggingFace dataset cards. + +""" import pytest from pydantic import ValidationError from tfbpapi.datainfo.models import ( - ChemicalTreatmentInfo, - CompoundInfo, DataFileInfo, DatasetCard, DatasetConfig, DatasetInfo, DatasetType, - EnvironmentalConditions, - ExperimentalConditions, ExtractedMetadata, FeatureInfo, - GrowthPhaseInfo, - HeatTreatmentInfo, - MediaInfo, MetadataRelationship, PartitioningInfo, ) class TestDatasetType: - """Test DatasetType enum.""" + """Tests for DatasetType enum.""" def test_dataset_type_values(self): - """Test all dataset type enum values.""" + """Test that all expected dataset types are defined.""" assert DatasetType.GENOMIC_FEATURES == "genomic_features" assert DatasetType.ANNOTATED_FEATURES == "annotated_features" assert DatasetType.GENOME_MAP == "genome_map" assert DatasetType.METADATA == "metadata" + assert DatasetType.QC_DATA == "qc_data" def test_dataset_type_from_string(self): """Test creating DatasetType from string.""" - assert DatasetType("genomic_features") == DatasetType.GENOMIC_FEATURES - assert DatasetType("annotated_features") == DatasetType.ANNOTATED_FEATURES - assert DatasetType("genome_map") == DatasetType.GENOME_MAP + dt = DatasetType("genomic_features") + assert dt == DatasetType.GENOMIC_FEATURES def test_invalid_dataset_type(self): - """Test invalid dataset type raises error.""" + """Test that invalid dataset type raises error.""" with pytest.raises(ValueError): DatasetType("invalid_type") -class TestCompoundInfo: - """Test CompoundInfo model.""" - - def test_compound_info_with_percentage(self): - """Test CompoundInfo with percentage concentration.""" - compound = CompoundInfo(compound="glucose", concentration_percent=2.0) - assert compound.compound == "glucose" - assert compound.concentration_percent == 2.0 - assert compound.concentration_g_per_l is None - assert compound.specifications is None - - def test_compound_info_with_g_per_l(self): - """Test CompoundInfo with g/L concentration.""" - compound = CompoundInfo(compound="dextrose", concentration_g_per_l=20.0) - assert compound.compound == "dextrose" - assert compound.concentration_g_per_l == 20.0 - assert compound.concentration_percent is None - - def test_compound_info_with_specifications(self): - """Test CompoundInfo with specifications.""" - compound = CompoundInfo( - compound="yeast_nitrogen_base", - concentration_g_per_l=6.7, - specifications=["without_amino_acids"], - ) - assert compound.compound == "yeast_nitrogen_base" - assert compound.specifications == ["without_amino_acids"] - - def test_compound_info_minimal(self): - """Test CompoundInfo with only required fields.""" - compound = CompoundInfo(compound="ethanol") - assert compound.compound == "ethanol" - assert compound.concentration_percent is None - assert compound.concentration_g_per_l is None - - -class TestMediaInfo: - """Test MediaInfo model.""" - - def test_media_info_minimal(self): - """Test MediaInfo with required fields only.""" - media = MediaInfo( - name="minimal", - carbon_source=[CompoundInfo(compound="glucose", concentration_percent=2.0)], - nitrogen_source=[ - CompoundInfo(compound="ammonium_sulfate", concentration_g_per_l=5.0) - ], - ) - assert media.name == "minimal" - assert len(media.carbon_source) == 1 - assert len(media.nitrogen_source) == 1 - assert media.phosphate_source is None - - def test_media_info_with_phosphate(self): - """Test MediaInfo with phosphate source.""" - media = MediaInfo( - name="synthetic_complete", - carbon_source=[CompoundInfo(compound="glucose", concentration_percent=2.0)], - nitrogen_source=[ - CompoundInfo(compound="yeast_nitrogen_base", concentration_g_per_l=6.7) - ], - phosphate_source=[ - CompoundInfo(compound="potassium_phosphate", concentration_g_per_l=1.0) - ], - ) - assert media.phosphate_source is not None - assert len(media.phosphate_source) == 1 - - def test_media_info_multiple_compounds(self): - """Test MediaInfo with multiple carbon/nitrogen sources.""" - media = MediaInfo( - name="YPD", - carbon_source=[ - CompoundInfo(compound="glucose", concentration_percent=2.0), - CompoundInfo(compound="glycerol", concentration_percent=3.0), - ], - nitrogen_source=[ - CompoundInfo(compound="yeast_extract", concentration_percent=1.0), - CompoundInfo(compound="peptone", concentration_percent=2.0), - ], - ) - assert len(media.carbon_source) == 2 - assert len(media.nitrogen_source) == 2 - - -class TestGrowthPhaseInfo: - """Test GrowthPhaseInfo model.""" - - def test_growth_phase_with_od600(self): - """Test GrowthPhaseInfo with OD600.""" - phase = GrowthPhaseInfo(od600=0.5, stage="mid_log_phase") - assert phase.od600 == 0.5 - assert phase.stage == "mid_log_phase" - - def test_growth_phase_od600_only(self): - """Test GrowthPhaseInfo with only OD600.""" - phase = GrowthPhaseInfo(od600=0.8) - assert phase.od600 == 0.8 - assert phase.stage is None - - def test_growth_phase_stage_only(self): - """Test GrowthPhaseInfo with only stage.""" - phase = GrowthPhaseInfo(stage="stationary_phase") - assert phase.stage == "stationary_phase" - assert phase.od600 is None - - def test_growth_phase_empty(self): - """Test GrowthPhaseInfo with no values.""" - phase = GrowthPhaseInfo() - assert phase.od600 is None - assert phase.stage is None - - -class TestChemicalTreatmentInfo: - """Test ChemicalTreatmentInfo model.""" - - def test_chemical_treatment_with_percent(self): - """Test ChemicalTreatmentInfo with percentage concentration.""" - treatment = ChemicalTreatmentInfo( - compound="ethanol", - concentration_percent=5.0, - duration_minutes=30, - ) - assert treatment.compound == "ethanol" - assert treatment.concentration_percent == 5.0 - assert treatment.duration_minutes == 30 - assert treatment.concentration_molar is None - - def test_chemical_treatment_with_molar(self): - """Test ChemicalTreatmentInfo with molar concentration.""" - treatment = ChemicalTreatmentInfo( - compound="rapamycin", - concentration_molar=0.0002, - duration_hours=2.0, - ) - assert treatment.compound == "rapamycin" - assert treatment.concentration_molar == 0.0002 - assert treatment.duration_hours == 2.0 - - def test_chemical_treatment_minimal(self): - """Test ChemicalTreatmentInfo with only compound.""" - treatment = ChemicalTreatmentInfo(compound="hydrogen_peroxide") - assert treatment.compound == "hydrogen_peroxide" - assert treatment.concentration_percent is None - assert treatment.duration_minutes is None - - -class TestHeatTreatmentInfo: - """Test HeatTreatmentInfo model.""" - - def test_heat_treatment_basic(self): - """Test HeatTreatmentInfo with required field.""" - treatment = HeatTreatmentInfo(duration_minutes=30) - assert treatment.duration_minutes == 30 - assert treatment.description is None - - def test_heat_treatment_with_description(self): - """Test HeatTreatmentInfo with description.""" - treatment = HeatTreatmentInfo( - duration_minutes=15, description="Heat shock at 37C" - ) - assert treatment.duration_minutes == 15 - assert treatment.description == "Heat shock at 37C" +class TestFeatureInfo: + """Tests for FeatureInfo model.""" - def test_heat_treatment_missing_duration(self): - """Test HeatTreatmentInfo requires duration_minutes.""" - with pytest.raises(ValidationError): - HeatTreatmentInfo(description="Some treatment") - - -class TestEnvironmentalConditions: - """Test EnvironmentalConditions model.""" - - def test_environmental_conditions_minimal(self): - """Test EnvironmentalConditions with no fields.""" - env = EnvironmentalConditions() - assert env.temperature_celsius is None - assert env.cultivation_method is None - assert env.growth_phase_at_harvest is None - assert env.media is None - assert env.chemical_treatment is None - assert env.heat_treatment is None - - def test_environmental_conditions_temperature(self): - """Test EnvironmentalConditions with temperature.""" - env = EnvironmentalConditions(temperature_celsius=30.0) - assert env.temperature_celsius == 30.0 - - def test_environmental_conditions_with_media(self): - """Test EnvironmentalConditions with media info.""" - media = MediaInfo( - name="YPD", - carbon_source=[CompoundInfo(compound="glucose", concentration_percent=2.0)], - nitrogen_source=[ - CompoundInfo(compound="peptone", concentration_percent=2.0) - ], - ) - env = EnvironmentalConditions( - temperature_celsius=30.0, - cultivation_method="batch_culture", - media=media, - ) - assert env.media is not None - assert env.media.name == "YPD" - - def test_environmental_conditions_with_growth_phase(self): - """Test EnvironmentalConditions with growth phase.""" - growth_phase = GrowthPhaseInfo(od600=0.5, stage="mid_log_phase") - env = EnvironmentalConditions(growth_phase_at_harvest=growth_phase) - assert env.growth_phase_at_harvest is not None - assert env.growth_phase_at_harvest.od600 == 0.5 - - def test_environmental_conditions_with_chemical_treatment(self): - """Test EnvironmentalConditions with chemical treatment.""" - treatment = ChemicalTreatmentInfo( - compound="rapamycin", - concentration_molar=0.0002, - duration_minutes=120, - ) - env = EnvironmentalConditions(chemical_treatment=treatment) - assert env.chemical_treatment is not None - assert env.chemical_treatment.compound == "rapamycin" - - def test_environmental_conditions_with_heat_treatment(self): - """Test EnvironmentalConditions with heat treatment.""" - treatment = HeatTreatmentInfo(duration_minutes=30, description="Heat shock") - env = EnvironmentalConditions(heat_treatment=treatment) - assert env.heat_treatment is not None - assert env.heat_treatment.duration_minutes == 30 - - def test_environmental_conditions_complete(self): - """Test EnvironmentalConditions with all fields.""" - media = MediaInfo( - name="minimal", - carbon_source=[CompoundInfo(compound="glucose", concentration_percent=2.0)], - nitrogen_source=[ - CompoundInfo(compound="ammonium_sulfate", concentration_g_per_l=5.0) - ], - ) - growth_phase = GrowthPhaseInfo(od600=0.6, stage="mid_log_phase") - chemical = ChemicalTreatmentInfo( - compound="rapamycin", concentration_molar=0.0002, duration_minutes=60 - ) - heat = HeatTreatmentInfo(duration_minutes=15, description="Brief heat shock") - - env = EnvironmentalConditions( - temperature_celsius=30.0, - cultivation_method="batch_culture", - growth_phase_at_harvest=growth_phase, - media=media, - chemical_treatment=chemical, - heat_treatment=heat, + def test_minimal_feature_info(self): + """Test creating FeatureInfo with minimal fields.""" + feature = FeatureInfo( + name="gene_id", dtype="string", description="Gene identifier" ) + assert feature.name == "gene_id" + assert feature.dtype == "string" + assert feature.description == "Gene identifier" + assert feature.role is None + assert feature.definitions is None - assert env.temperature_celsius == 30.0 - assert env.cultivation_method == "batch_culture" - assert env.growth_phase_at_harvest.od600 == 0.6 - assert env.media.name == "minimal" - assert env.chemical_treatment.compound == "rapamycin" - assert env.heat_treatment.duration_minutes == 15 - - def test_environmental_conditions_extra_fields_allowed(self): - """Test that EnvironmentalConditions allows extra fields.""" - env = EnvironmentalConditions( - temperature_celsius=30.0, custom_field="custom_value" + def test_feature_info_with_role(self): + """Test FeatureInfo with role field.""" + feature = FeatureInfo( + name="condition", + dtype="string", + description="Experimental condition", + role="experimental_condition", ) - assert env.temperature_celsius == 30.0 - - -class TestExperimentalConditions: - """Test ExperimentalConditions model.""" - - def test_experimental_conditions_minimal(self): - """Test ExperimentalConditions with no fields.""" - exp = ExperimentalConditions() - assert exp.environmental_conditions is None - assert exp.strain_background is None + assert feature.role == "experimental_condition" - def test_experimental_conditions_with_strain(self): - """Test ExperimentalConditions with strain background.""" - exp = ExperimentalConditions(strain_background="BY4741") - assert exp.strain_background == "BY4741" - - def test_experimental_conditions_with_environmental(self): - """Test ExperimentalConditions with environmental conditions.""" - env = EnvironmentalConditions( - temperature_celsius=30.0, cultivation_method="batch_culture" - ) - exp = ExperimentalConditions(environmental_conditions=env) - assert exp.environmental_conditions is not None - assert exp.environmental_conditions.temperature_celsius == 30.0 - - def test_experimental_conditions_complete(self): - """Test ExperimentalConditions with all fields.""" - env = EnvironmentalConditions( - temperature_celsius=30.0, - cultivation_method="batch_culture", - media=MediaInfo( - name="YPD", - carbon_source=[ - CompoundInfo(compound="glucose", concentration_percent=2.0) - ], - nitrogen_source=[ - CompoundInfo(compound="peptone", concentration_percent=2.0) - ], - ), - ) - exp = ExperimentalConditions( - environmental_conditions=env, strain_background="BY4741" - ) - assert exp.strain_background == "BY4741" - assert exp.environmental_conditions.temperature_celsius == 30.0 - assert exp.environmental_conditions.media.name == "YPD" - - def test_experimental_conditions_extra_fields_allowed(self): - """Test that ExperimentalConditions allows extra fields.""" - exp = ExperimentalConditions( - strain_background="BY4741", custom_metadata="custom_value" + def test_feature_info_with_definitions(self): + """Test FeatureInfo with definitions for experimental_condition.""" + feature = FeatureInfo( + name="condition", + dtype={"class_label": {"names": ["control", "treated"]}}, + description="Treatment condition", + role="experimental_condition", + definitions={ + "control": {"temperature_celsius": 30}, + "treated": {"temperature_celsius": 37}, + }, + ) + assert feature.definitions is not None + assert "control" in feature.definitions + assert feature.definitions["control"]["temperature_celsius"] == 30 + + def test_feature_info_with_dict_dtype(self): + """Test FeatureInfo with class_label dtype.""" + feature = FeatureInfo( + name="category", + dtype={"class_label": {"names": ["A", "B", "C"]}}, + description="Categorical field", ) - assert exp.strain_background == "BY4741" - - -class TestFeatureInfo: - """Test FeatureInfo model.""" - - def test_valid_feature_info(self, sample_feature_info): - """Test creating valid FeatureInfo.""" - feature = FeatureInfo(**sample_feature_info) - assert feature.name == "gene_symbol" - assert feature.dtype == "string" - assert feature.description == "Standard gene symbol (e.g., HO, GAL1)" - - def test_feature_info_required_fields(self): - """Test that all fields are required.""" - # Test missing name - with pytest.raises(ValidationError): - FeatureInfo(dtype="string", description="test") - - # Test missing dtype - with pytest.raises(ValidationError): - FeatureInfo(name="test", description="test") - - # Test missing description - with pytest.raises(ValidationError): - FeatureInfo(name="test", dtype="string") - - def test_feature_info_serialization(self, sample_feature_info): - """Test FeatureInfo serialization.""" - feature = FeatureInfo(**sample_feature_info) - data = feature.model_dump() - assert data["name"] == "gene_symbol" - assert data["dtype"] == "string" - assert data["description"] == "Standard gene symbol (e.g., HO, GAL1)" - - def test_feature_info_categorical_dtype(self): - """Test FeatureInfo with categorical dtype.""" - categorical_feature_data = { - "name": "mechanism", - "dtype": {"class_label": {"names": ["GEV", "ZEV"]}}, - "description": "Induction system", - } - - feature = FeatureInfo(**categorical_feature_data) - assert feature.name == "mechanism" assert isinstance(feature.dtype, dict) assert "class_label" in feature.dtype - assert feature.dtype["class_label"].names == ["GEV", "ZEV"] - assert feature.get_dtype_summary() == "categorical (2 classes: GEV, ZEV)" - - def test_feature_info_simple_string_dtype(self): - """Test FeatureInfo with simple string dtype.""" - feature = FeatureInfo( - name="test_field", dtype="string", description="Test field" - ) - assert feature.dtype == "string" - assert feature.get_dtype_summary() == "string" - - def test_feature_info_invalid_categorical_dtype(self): - """Test FeatureInfo with invalid categorical dtype structure.""" - with pytest.raises(ValidationError, match="Invalid class_label structure"): - FeatureInfo( - name="test_field", - dtype={"class_label": "invalid"}, # Should be dict with names - description="Test field", - ) - - def test_feature_info_unknown_dtype_structure(self): - """Test FeatureInfo with unknown dtype structure.""" - with pytest.raises(ValidationError, match="Unknown dtype structure"): - FeatureInfo( - name="test_field", - dtype={"unknown_key": "value"}, - description="Test field", - ) class TestPartitioningInfo: - """Test PartitioningInfo model.""" + """Tests for PartitioningInfo model.""" def test_default_partitioning_info(self): - """Test default PartitioningInfo values.""" + """Test PartitioningInfo with defaults.""" partitioning = PartitioningInfo() assert partitioning.enabled is False assert partitioning.partition_by is None assert partitioning.path_template is None - def test_enabled_partitioning_info(self, sample_partitioning_info): - """Test enabled partitioning with all fields.""" - partitioning = PartitioningInfo(**sample_partitioning_info) - assert partitioning.enabled is True - assert partitioning.partition_by == ["regulator", "condition"] - assert ( - partitioning.path_template is not None - and "regulator={regulator}" in partitioning.path_template + def test_enabled_partitioning_info(self): + """Test PartitioningInfo with partitioning enabled.""" + partitioning = PartitioningInfo( + enabled=True, + partition_by=["accession"], + path_template="data/accession={accession}/*.parquet", ) - - def test_partial_partitioning_info(self): - """Test partitioning with only some fields set.""" - partitioning = PartitioningInfo(enabled=True, partition_by=["field1"]) assert partitioning.enabled is True - assert partitioning.partition_by == ["field1"] - assert partitioning.path_template is None + assert partitioning.partition_by == ["accession"] + assert partitioning.path_template == "data/accession={accession}/*.parquet" class TestDataFileInfo: - """Test DataFileInfo model.""" + """Tests for DataFileInfo model.""" def test_default_data_file_info(self): """Test DataFileInfo with default split.""" - data_file = DataFileInfo(path="test.parquet") - assert data_file.split == "train" - assert data_file.path == "test.parquet" - - def test_custom_data_file_info(self, sample_data_file_info): - """Test DataFileInfo with custom values.""" - data_file = DataFileInfo(**sample_data_file_info) + data_file = DataFileInfo(path="data.parquet") assert data_file.split == "train" - assert data_file.path == "genomic_features.parquet" + assert data_file.path == "data.parquet" - def test_data_file_info_required_path(self): - """Test that path is required.""" - with pytest.raises(ValidationError): - DataFileInfo(split="train") + def test_custom_data_file_info(self): + """Test DataFileInfo with custom split.""" + data_file = DataFileInfo(split="test", path="test_data.parquet") + assert data_file.split == "test" + assert data_file.path == "test_data.parquet" class TestDatasetInfo: - """Test DatasetInfo model.""" + """Tests for DatasetInfo model.""" - def test_minimal_dataset_info(self, sample_feature_info): - """Test minimal DatasetInfo with just features.""" - features = [FeatureInfo(**sample_feature_info)] - dataset_info = DatasetInfo(features=features) + def test_minimal_dataset_info(self): + """Test DatasetInfo with minimal features.""" + dataset_info = DatasetInfo( + features=[ + FeatureInfo( + name="gene_id", dtype="string", description="Gene identifier" + ) + ] + ) assert len(dataset_info.features) == 1 assert dataset_info.partitioning is None - def test_dataset_info_with_partitioning( - self, sample_feature_info, sample_partitioning_info - ): + def test_dataset_info_with_partitioning(self): """Test DatasetInfo with partitioning.""" - features = [FeatureInfo(**sample_feature_info)] - partitioning = PartitioningInfo(**sample_partitioning_info) - - dataset_info = DatasetInfo(features=features, partitioning=partitioning) - assert len(dataset_info.features) == 1 - assert ( - dataset_info.partitioning is not None - and dataset_info.partitioning.enabled is True + dataset_info = DatasetInfo( + features=[ + FeatureInfo(name="chr", dtype="string", description="Chromosome"), + FeatureInfo(name="pos", dtype="int32", description="Position"), + ], + partitioning=PartitioningInfo(enabled=True, partition_by=["chr"]), ) - - def test_dataset_info_empty_features_error(self): - """Test that empty features list is allowed.""" - # Pydantic allows empty lists, so this should succeed - dataset_info = DatasetInfo(features=[]) - assert len(dataset_info.features) == 0 + assert len(dataset_info.features) == 2 + assert dataset_info.partitioning.enabled is True class TestDatasetConfig: - """Test DatasetConfig model.""" - - def test_minimal_dataset_config(self, sample_feature_info, sample_data_file_info): - """Test minimal valid DatasetConfig.""" - features = [FeatureInfo(**sample_feature_info)] - data_files = [DataFileInfo(**sample_data_file_info)] - dataset_info = DatasetInfo(features=features) + """Tests for DatasetConfig model.""" + def test_minimal_dataset_config(self): + """Test DatasetConfig with minimal required fields.""" config = DatasetConfig( - config_name="test_config", - description="Test configuration", - dataset_type=DatasetType.GENOMIC_FEATURES, - data_files=data_files, - dataset_info=dataset_info, + config_name="test_data", + description="Test dataset", + dataset_type=DatasetType.ANNOTATED_FEATURES, + data_files=[DataFileInfo(path="data.parquet")], + dataset_info=DatasetInfo( + features=[FeatureInfo(name="id", dtype="string", description="ID")] + ), ) - - assert config.config_name == "test_config" - assert config.dataset_type == DatasetType.GENOMIC_FEATURES + assert config.config_name == "test_data" + assert config.dataset_type == DatasetType.ANNOTATED_FEATURES assert config.default is False assert config.applies_to is None assert config.metadata_fields is None - def test_dataset_config_with_applies_to_metadata( - self, sample_feature_info, sample_data_file_info - ): - """Test DatasetConfig with applies_to for metadata types.""" - features = [FeatureInfo(**sample_feature_info)] - data_files = [DataFileInfo(**sample_data_file_info)] - dataset_info = DatasetInfo(features=features) - + def test_dataset_config_with_applies_to(self): + """Test DatasetConfig with applies_to for metadata.""" config = DatasetConfig( - config_name="metadata_config", - description="Metadata configuration", + config_name="metadata", + description="Metadata", dataset_type=DatasetType.METADATA, - applies_to=["data_config1", "data_config2"], - data_files=data_files, - dataset_info=dataset_info, + applies_to=["data_config_1", "data_config_2"], + data_files=[DataFileInfo(path="metadata.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo( + name="sample_id", dtype="string", description="Sample ID" + ) + ] + ), ) + assert config.applies_to == ["data_config_1", "data_config_2"] - assert config.applies_to == ["data_config1", "data_config2"] - - def test_dataset_config_applies_to_validation_error( - self, sample_feature_info, sample_data_file_info - ): - """Test that applies_to is only valid for metadata types.""" - features = [FeatureInfo(**sample_feature_info)] - data_files = [DataFileInfo(**sample_data_file_info)] - dataset_info = DatasetInfo(features=features) - - with pytest.raises( - ValidationError, - match="applies_to field is only valid for " - "metadata and qc_data dataset types", - ): - DatasetConfig( - config_name="invalid_config", - description="Invalid configuration", - dataset_type=DatasetType.GENOMIC_FEATURES, # Not a metadata type - applies_to=["some_config"], # This should cause validation error - data_files=data_files, - dataset_info=dataset_info, - ) - - def test_dataset_config_empty_metadata_fields_error( - self, sample_feature_info, sample_data_file_info - ): - """Test that empty metadata_fields list raises error.""" - features = [FeatureInfo(**sample_feature_info)] - data_files = [DataFileInfo(**sample_data_file_info)] - dataset_info = DatasetInfo(features=features) - - with pytest.raises( - ValidationError, match="metadata_fields cannot be empty list" - ): + def test_dataset_config_applies_to_validation_error(self): + """Test that applies_to raises error for non-metadata configs.""" + with pytest.raises(ValidationError): DatasetConfig( - config_name="test_config", - description="Test configuration", + config_name="data", + description="Data", dataset_type=DatasetType.ANNOTATED_FEATURES, - metadata_fields=[], # Empty list should cause error - data_files=data_files, - dataset_info=dataset_info, + applies_to=["other_config"], + data_files=[DataFileInfo(path="data.parquet")], + dataset_info=DatasetInfo( + features=[FeatureInfo(name="id", dtype="string", description="ID")] + ), ) - def test_dataset_config_with_metadata_fields( - self, sample_feature_info, sample_data_file_info - ): - """Test DatasetConfig with valid metadata_fields.""" - features = [FeatureInfo(**sample_feature_info)] - data_files = [DataFileInfo(**sample_data_file_info)] - dataset_info = DatasetInfo(features=features) - + def test_dataset_config_with_metadata_fields(self): + """Test DatasetConfig with metadata_fields.""" config = DatasetConfig( - config_name="test_config", - description="Test configuration", + config_name="data", + description="Data", dataset_type=DatasetType.ANNOTATED_FEATURES, - metadata_fields=["field1", "field2"], - data_files=data_files, - dataset_info=dataset_info, + metadata_fields=["regulator_symbol", "condition"], + data_files=[DataFileInfo(path="data.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo( + name="regulator_symbol", dtype="string", description="TF symbol" + ), + FeatureInfo( + name="condition", dtype="string", description="Condition" + ), + ] + ), ) + assert config.metadata_fields == ["regulator_symbol", "condition"] - assert config.metadata_fields == ["field1", "field2"] + def test_dataset_config_empty_metadata_fields_error(self): + """Test that empty metadata_fields raises error.""" + with pytest.raises(ValidationError): + DatasetConfig( + config_name="data", + description="Data", + dataset_type=DatasetType.ANNOTATED_FEATURES, + metadata_fields=[], + data_files=[DataFileInfo(path="data.parquet")], + dataset_info=DatasetInfo( + features=[FeatureInfo(name="id", dtype="string", description="ID")] + ), + ) + def test_dataset_config_accepts_extra_fields(self): + """Test that DatasetConfig accepts extra fields like experimental_conditions.""" + config_data = { + "config_name": "data", + "description": "Data", + "dataset_type": "annotated_features", + "experimental_conditions": { + "temperature_celsius": 30, + "media": {"name": "YPD"}, + }, + "data_files": [{"path": "data.parquet"}], + "dataset_info": { + "features": [{"name": "id", "dtype": "string", "description": "ID"}] + }, + } + config = DatasetConfig(**config_data) + assert hasattr(config, "model_extra") + assert "experimental_conditions" in config.model_extra -class TestDatasetCard: - """Test DatasetCard model.""" - def test_minimal_dataset_card(self, minimal_dataset_card_data): - """Test minimal valid DatasetCard.""" - card = DatasetCard(**minimal_dataset_card_data) +class TestDatasetCard: + """Tests for DatasetCard model.""" + + def test_minimal_dataset_card(self): + """Test DatasetCard with minimal structure.""" + card = DatasetCard( + configs=[ + DatasetConfig( + config_name="data", + description="Data", + dataset_type=DatasetType.ANNOTATED_FEATURES, + data_files=[DataFileInfo(path="data.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo(name="id", dtype="string", description="ID") + ] + ), + ) + ] + ) assert len(card.configs) == 1 - assert card.configs[0].config_name == "test_config" - assert card.license is None - assert card.pretty_name is None - - def test_full_dataset_card(self, sample_dataset_card_data): - """Test full DatasetCard with all fields.""" - card = DatasetCard(**sample_dataset_card_data) - assert len(card.configs) == 4 - assert card.license == "mit" - assert card.pretty_name == "Test Genomics Dataset" - assert card.tags is not None and "biology" in card.tags + + def test_dataset_card_accepts_extra_fields(self): + """Test that DatasetCard accepts extra top-level fields.""" + card_data = { + "license": "mit", + "pretty_name": "Test Dataset", + "tags": ["biology", "genomics"], + "experimental_conditions": {"strain_background": "BY4741"}, + "configs": [ + { + "config_name": "data", + "description": "Data", + "dataset_type": "annotated_features", + "data_files": [{"path": "data.parquet"}], + "dataset_info": { + "features": [ + {"name": "id", "dtype": "string", "description": "ID"} + ] + }, + } + ], + } + card = DatasetCard(**card_data) + assert hasattr(card, "model_extra") + assert "license" in card.model_extra + assert "experimental_conditions" in card.model_extra def test_empty_configs_error(self): - """Test that empty configs list raises error.""" - with pytest.raises( - ValidationError, match="At least one dataset configuration is required" - ): + """Test that empty configs raises error.""" + with pytest.raises(ValidationError): DatasetCard(configs=[]) - def test_duplicate_config_names_error( - self, sample_feature_info, sample_data_file_info - ): - """Test that duplicate config names raise error.""" - features = [FeatureInfo(**sample_feature_info)] - data_files = [DataFileInfo(**sample_data_file_info)] - dataset_info = DatasetInfo(features=features) - - config1 = DatasetConfig( - config_name="duplicate_name", - description="First config", - dataset_type=DatasetType.GENOMIC_FEATURES, - data_files=data_files, - dataset_info=dataset_info, - ) - - config2 = DatasetConfig( - config_name="duplicate_name", # Same name - description="Second config", - dataset_type=DatasetType.ANNOTATED_FEATURES, - data_files=data_files, - dataset_info=dataset_info, - ) - - with pytest.raises(ValidationError, match="Configuration names must be unique"): - DatasetCard(configs=[config1, config2]) - - def test_multiple_default_configs_error( - self, sample_feature_info, sample_data_file_info - ): - """Test that multiple default configs raise error.""" - features = [FeatureInfo(**sample_feature_info)] - data_files = [DataFileInfo(**sample_data_file_info)] - dataset_info = DatasetInfo(features=features) - - config1 = DatasetConfig( - config_name="config1", - description="First config", - dataset_type=DatasetType.GENOMIC_FEATURES, - default=True, - data_files=data_files, - dataset_info=dataset_info, - ) - - config2 = DatasetConfig( - config_name="config2", - description="Second config", - dataset_type=DatasetType.ANNOTATED_FEATURES, - default=True, # Another default - data_files=data_files, - dataset_info=dataset_info, - ) - - with pytest.raises( - ValidationError, match="At most one configuration can be marked as default" - ): - DatasetCard(configs=[config1, config2]) + def test_duplicate_config_names_error(self): + """Test that duplicate config names raises error.""" + with pytest.raises(ValidationError): + DatasetCard( + configs=[ + DatasetConfig( + config_name="data", + description="Data 1", + dataset_type=DatasetType.ANNOTATED_FEATURES, + data_files=[DataFileInfo(path="data1.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo(name="id", dtype="string", description="ID") + ] + ), + ), + DatasetConfig( + config_name="data", + description="Data 2", + dataset_type=DatasetType.ANNOTATED_FEATURES, + data_files=[DataFileInfo(path="data2.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo(name="id", dtype="string", description="ID") + ] + ), + ), + ] + ) - def test_get_config_by_name(self, sample_dataset_card_data): - """Test getting config by name.""" - card = DatasetCard(**sample_dataset_card_data) + def test_multiple_default_configs_error(self): + """Test that multiple default configs raises error.""" + with pytest.raises(ValidationError): + DatasetCard( + configs=[ + DatasetConfig( + config_name="data1", + description="Data 1", + dataset_type=DatasetType.ANNOTATED_FEATURES, + default=True, + data_files=[DataFileInfo(path="data1.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo(name="id", dtype="string", description="ID") + ] + ), + ), + DatasetConfig( + config_name="data2", + description="Data 2", + dataset_type=DatasetType.ANNOTATED_FEATURES, + default=True, + data_files=[DataFileInfo(path="data2.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo(name="id", dtype="string", description="ID") + ] + ), + ), + ] + ) - config = card.get_config_by_name("binding_data") + def test_get_config_by_name(self): + """Test get_config_by_name method.""" + card = DatasetCard( + configs=[ + DatasetConfig( + config_name="data1", + description="Data 1", + dataset_type=DatasetType.ANNOTATED_FEATURES, + data_files=[DataFileInfo(path="data1.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo(name="id", dtype="string", description="ID") + ] + ), + ), + DatasetConfig( + config_name="data2", + description="Data 2", + dataset_type=DatasetType.METADATA, + data_files=[DataFileInfo(path="data2.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo(name="id", dtype="string", description="ID") + ] + ), + ), + ] + ) + config = card.get_config_by_name("data1") assert config is not None - assert config.config_name == "binding_data" - - # Test non-existent config + assert config.config_name == "data1" assert card.get_config_by_name("nonexistent") is None - def test_get_configs_by_type(self, sample_dataset_card_data): - """Test getting configs by type.""" - card = DatasetCard(**sample_dataset_card_data) - - genomic_configs = card.get_configs_by_type(DatasetType.GENOMIC_FEATURES) - assert len(genomic_configs) == 1 - assert genomic_configs[0].config_name == "genomic_features" - - metadata_configs = card.get_configs_by_type(DatasetType.METADATA) - assert len(metadata_configs) == 1 - assert metadata_configs[0].config_name == "experiment_metadata" - - def test_get_default_config(self, sample_dataset_card_data): - """Test getting default config.""" - card = DatasetCard(**sample_dataset_card_data) - - default_config = card.get_default_config() - assert default_config is not None - assert default_config.config_name == "genomic_features" - assert default_config.default is True - - def test_get_data_configs(self, sample_dataset_card_data): - """Test getting non-metadata configs.""" - card = DatasetCard(**sample_dataset_card_data) - + def test_get_configs_by_type(self): + """Test get_configs_by_type method.""" + card = DatasetCard( + configs=[ + DatasetConfig( + config_name="data", + description="Data", + dataset_type=DatasetType.ANNOTATED_FEATURES, + data_files=[DataFileInfo(path="data.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo(name="id", dtype="string", description="ID") + ] + ), + ), + DatasetConfig( + config_name="metadata", + description="Metadata", + dataset_type=DatasetType.METADATA, + data_files=[DataFileInfo(path="metadata.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo(name="id", dtype="string", description="ID") + ] + ), + ), + ] + ) + data_configs = card.get_configs_by_type(DatasetType.ANNOTATED_FEATURES) + assert len(data_configs) == 1 + assert data_configs[0].config_name == "data" + + def test_get_default_config(self): + """Test get_default_config method.""" + card = DatasetCard( + configs=[ + DatasetConfig( + config_name="data1", + description="Data 1", + dataset_type=DatasetType.ANNOTATED_FEATURES, + data_files=[DataFileInfo(path="data1.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo(name="id", dtype="string", description="ID") + ] + ), + ), + DatasetConfig( + config_name="data2", + description="Data 2", + dataset_type=DatasetType.ANNOTATED_FEATURES, + default=True, + data_files=[DataFileInfo(path="data2.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo(name="id", dtype="string", description="ID") + ] + ), + ), + ] + ) + default = card.get_default_config() + assert default is not None + assert default.config_name == "data2" + + def test_get_data_configs(self): + """Test get_data_configs method.""" + card = DatasetCard( + configs=[ + DatasetConfig( + config_name="data", + description="Data", + dataset_type=DatasetType.ANNOTATED_FEATURES, + data_files=[DataFileInfo(path="data.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo(name="id", dtype="string", description="ID") + ] + ), + ), + DatasetConfig( + config_name="metadata", + description="Metadata", + dataset_type=DatasetType.METADATA, + data_files=[DataFileInfo(path="metadata.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo(name="id", dtype="string", description="ID") + ] + ), + ), + ] + ) data_configs = card.get_data_configs() - assert len(data_configs) == 3 # genomic_features, binding_data, genome_map_data - config_names = [config.config_name for config in data_configs] - assert "genomic_features" in config_names - assert "binding_data" in config_names - assert "genome_map_data" in config_names - assert "experiment_metadata" not in config_names - - def test_get_metadata_configs(self, sample_dataset_card_data): - """Test getting metadata configs.""" - card = DatasetCard(**sample_dataset_card_data) - + assert len(data_configs) == 1 + assert data_configs[0].dataset_type != DatasetType.METADATA + + def test_get_metadata_configs(self): + """Test get_metadata_configs method.""" + card = DatasetCard( + configs=[ + DatasetConfig( + config_name="data", + description="Data", + dataset_type=DatasetType.ANNOTATED_FEATURES, + data_files=[DataFileInfo(path="data.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo(name="id", dtype="string", description="ID") + ] + ), + ), + DatasetConfig( + config_name="metadata", + description="Metadata", + dataset_type=DatasetType.METADATA, + data_files=[DataFileInfo(path="metadata.parquet")], + dataset_info=DatasetInfo( + features=[ + FeatureInfo(name="id", dtype="string", description="ID") + ] + ), + ), + ] + ) metadata_configs = card.get_metadata_configs() assert len(metadata_configs) == 1 - assert metadata_configs[0].config_name == "experiment_metadata" + assert metadata_configs[0].dataset_type == DatasetType.METADATA class TestExtractedMetadata: - """Test ExtractedMetadata model.""" + """Tests for ExtractedMetadata model.""" def test_extracted_metadata_creation(self): """Test creating ExtractedMetadata.""" metadata = ExtractedMetadata( config_name="test_config", field_name="regulator_symbol", - values={"TF1", "TF2", "TF3"}, - extraction_method="partition_values", + values={"CBF1", "GAL4", "GCN4"}, + extraction_method="distinct", ) - assert metadata.config_name == "test_config" assert metadata.field_name == "regulator_symbol" - assert metadata.values == {"TF1", "TF2", "TF3"} - assert metadata.extraction_method == "partition_values" - - def test_extracted_metadata_serialization(self): - """Test ExtractedMetadata JSON serialization.""" - metadata = ExtractedMetadata( - config_name="test_config", - field_name="condition", - values={"control", "treatment"}, - extraction_method="embedded", - ) - - # Test basic serialization (sets remain as sets in model_dump) - data = metadata.model_dump() - assert isinstance(data["values"], set) - assert data["values"] == {"control", "treatment"} - - # Test JSON mode serialization where sets should become lists - json_data = metadata.model_dump(mode="json") - assert isinstance(json_data["values"], list) - assert set(json_data["values"]) == {"control", "treatment"} + assert len(metadata.values) == 3 + assert "CBF1" in metadata.values class TestMetadataRelationship: - """Test MetadataRelationship model.""" + """Tests for MetadataRelationship model.""" def test_metadata_relationship_creation(self): """Test creating MetadataRelationship.""" @@ -809,23 +572,6 @@ def test_metadata_relationship_creation(self): metadata_config="experiment_metadata", relationship_type="explicit", ) - assert relationship.data_config == "binding_data" assert relationship.metadata_config == "experiment_metadata" assert relationship.relationship_type == "explicit" - - def test_metadata_relationship_types(self): - """Test different relationship types.""" - # Test explicit relationship - explicit = MetadataRelationship( - data_config="data1", metadata_config="meta1", relationship_type="explicit" - ) - assert explicit.relationship_type == "explicit" - - # Test embedded relationship - embedded = MetadataRelationship( - data_config="data3", - metadata_config="data3_embedded", - relationship_type="embedded", - ) - assert embedded.relationship_type == "embedded" diff --git a/tfbpapi/tests/datainfo/test_real_datacards.py b/tfbpapi/tests/datainfo/test_real_datacards.py index 32bdaf6..0220797 100644 --- a/tfbpapi/tests/datainfo/test_real_datacards.py +++ b/tfbpapi/tests/datainfo/test_real_datacards.py @@ -28,18 +28,17 @@ size_categories: - 100M 0 - carbon = env_conds.media.carbon_source[0] - assert carbon.concentration_percent is not None + assert "carbon_source" in media + carbon_sources = media["carbon_source"] + assert len(carbon_sources) > 0 + carbon = carbon_sources[0] + assert carbon["concentration_percent"] is not None # Check nitrogen source with specifications - assert len(env_conds.media.nitrogen_source) > 0 - nitrogen = env_conds.media.nitrogen_source[0] - assert nitrogen.specifications is not None - assert "without_amino_acids" in nitrogen.specifications + assert "nitrogen_source" in media + nitrogen_sources = media["nitrogen_source"] + assert len(nitrogen_sources) > 0 + nitrogen = nitrogen_sources[0] + assert nitrogen["specifications"] is not None + assert "without_amino_acids" in nitrogen["specifications"] def test_extra_fields_do_not_raise_errors(): @@ -650,10 +652,13 @@ def test_empty_nitrogen_source_list(): data = yaml.safe_load(BARKAI_COMPENDIUM) card = DatasetCard(**data) - # Check that nitrogen_source is an empty list - env_conds = card.experimental_conditions.environmental_conditions - assert env_conds.media is not None - assert env_conds.media.nitrogen_source == [] + # Check that nitrogen_source is an empty list (stored as dict in model_extra) + assert card.model_extra is not None + assert "experimental_conditions" in card.model_extra + exp_conds = card.model_extra["experimental_conditions"] + assert "media" in exp_conds + media = exp_conds["media"] + assert media["nitrogen_source"] == [] def test_media_additives(): @@ -683,14 +688,18 @@ def test_strain_background_formats(): # String format data1 = yaml.safe_load(BARKAI_COMPENDIUM) card1 = DatasetCard(**data1) - assert card1.experimental_conditions is not None - assert card1.experimental_conditions.strain_background == "BY4741" + assert card1.model_extra is not None + assert "experimental_conditions" in card1.model_extra + exp_conds1 = card1.model_extra["experimental_conditions"] + assert exp_conds1["strain_background"] == "BY4741" # String format in rossi data2 = yaml.safe_load(ROSSI_2021) card2 = DatasetCard(**data2) - assert card2.experimental_conditions is not None - assert card2.experimental_conditions.strain_background == "W303" + assert card2.model_extra is not None + assert "experimental_conditions" in card2.model_extra + exp_conds2 = card2.model_extra["experimental_conditions"] + assert exp_conds2["strain_background"] == "W303" if __name__ == "__main__": From e894cdee478556290b84c1892214fe0c739fdcc1 Mon Sep 17 00:00:00 2001 From: chasem Date: Thu, 11 Dec 2025 17:03:58 -0600 Subject: [PATCH 42/49] filter_resolver --- docs/tutorials/filter_resolver_tutorial.ipynb | 1701 +++++++++++++++++ docs/tutorials/my_filter_config.yaml | 24 + docs/tutorials/sample_manager_tutorial.ipynb | 283 ++- example_filter_config.yaml | 88 + tfbpapi/datainfo/filter_resolver.py | 601 ++++++ .../tests/datainfo/test_filter_resolver.py | 385 ++++ 6 files changed, 3046 insertions(+), 36 deletions(-) create mode 100644 docs/tutorials/filter_resolver_tutorial.ipynb create mode 100644 docs/tutorials/my_filter_config.yaml create mode 100644 example_filter_config.yaml create mode 100644 tfbpapi/datainfo/filter_resolver.py create mode 100644 tfbpapi/tests/datainfo/test_filter_resolver.py diff --git a/docs/tutorials/filter_resolver_tutorial.ipynb b/docs/tutorials/filter_resolver_tutorial.ipynb new file mode 100644 index 0000000..b52449a --- /dev/null +++ b/docs/tutorials/filter_resolver_tutorial.ipynb @@ -0,0 +1,1701 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# DatasetFilterResolver Tutorial\n", + "\n", + "This tutorial demonstrates how to use the `DatasetFilterResolver` to filter samples across heterogeneous datasets using external YAML configuration.\n", + "\n", + "## Overview\n", + "\n", + "The `DatasetFilterResolver` solves a common problem: filtering experimental data across multiple datasets where experimental conditions are structured differently. Instead of hardcoding dataset-specific logic, you specify:\n", + "\n", + "1. **Filter criteria**: Which values are acceptable (e.g., carbon_source: [\"D-glucose\", \"D-galactose\"])\n", + "2. **Property mappings**: Where to find each property in each dataset (e.g., \"environmental_conditions.media.carbon_source\")\n", + "\n", + "## Key Features\n", + "\n", + "- **Two-level filtering**:\n", + " - Level 1: Repo/config level - excludes entire datasets that don't match\n", + " - Level 2: Field level - returns matching field values within datasets\n", + "- **Three output modes**:\n", + " - `conditions`: Just matching field values (no data retrieval)\n", + " - `samples`: Sample-level metadata (one row per sample_id)\n", + " - `full_data`: All measurements for matching samples\n", + "- **External configuration**: No hardcoded field specifications in codebase\n", + "- **Automatic path resolution**: Handles different nesting levels (repo vs field level)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "import yaml\n", + "import pandas as pd\n", + "from tfbpapi.datainfo.filter_resolver import DatasetFilterResolver" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 1: Basic Filtering by Carbon Source\n", + "\n", + "Let's start with a simple example: finding all samples grown on D-glucose." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Understanding Field-Level vs Repo-Level Properties\n", + "\n", + "A key challenge with heterogeneous datasets is that experimental conditions can be located in different places:\n", + "\n", + "**Field-Level Definitions** (e.g., Harbison 2004):\n", + "- Properties are defined within a specific field's `definitions`\n", + "- Example: The `condition` field has definitions for \"YPD\", \"HEAT\", etc.\n", + "- Each definition contains `media`, `temperature_celsius`, etc.\n", + "- **Use the `field` parameter** to specify which field contains the definitions\n", + "\n", + "**Repo/Config-Level Properties** (e.g., Kemmeren 2014):\n", + "- Properties are defined at the top level of the datacard\n", + "- All samples in the dataset share the same conditions\n", + "- Found in `experimental_conditions` at repo or config level\n", + "- **No `field` parameter needed** - just specify the path\n", + "\n", + "Let's examine actual datacards to see these differences:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Configuration Implications\n", + "\n", + "Based on the actual datacard structures above:\n", + "\n", + "**For Harbison 2004** (field-level):\n", + "```yaml\n", + "\"BrentLab/harbison_2004\":\n", + " datasets:\n", + " harbison_2004:\n", + " carbon_source:\n", + " field: condition # Must specify which field\n", + " path: media.carbon_source # Path within each field value's definition\n", + "```\n", + "\n", + "**For Kemmeren 2014** (repo-level):\n", + "```yaml\n", + "\"BrentLab/kemmeren_2014\":\n", + " repo_level:\n", + " carbon_source:\n", + " path: environmental_conditions.media.carbon_source # No 'field' needed\n", + "```\n", + "\n", + "### When to Use Which Format\n", + "\n", + "**Use `field` specification when**:\n", + "- The property varies between samples (different values in the dataset)\n", + "- The property is defined in a field's `definitions` section\n", + "- You need to filter to specific field values (e.g., only \"YPD\" condition)\n", + "- Example: Harbison 2004 where each condition has its own media/temperature\n", + "\n", + "**Use repo-level (no `field`) when**:\n", + "- The property is the same for all samples in the dataset\n", + "- The property is in the top-level `experimental_conditions`\n", + "- You're filtering entire datasets in/out based on shared properties\n", + "- Example: Kemmeren 2014 where all samples share the same growth conditions" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================================================================================\n", + "KEMMEREN 2014 - Repo-Level Experimental Conditions\n", + "================================================================================\n", + "\n", + "Repo-level experimental_conditions exists: True\n", + "\n", + "Temperature: 30°C\n", + "Media name: synthetic_complete\n", + "Carbon source: ['D-glucose']\n", + "Concentrations: [2]%\n", + "\n", + "No field-level definitions found\n", + "\n", + "================================================================================\n", + "Key Observation: Properties are at repo level, shared by all samples\n", + "================================================================================\n" + ] + } + ], + "source": [ + "# Load Kemmeren 2014 (repo-level example)\n", + "from tfbpapi.datainfo import DataCard\n", + "\n", + "kemmeren_card = DataCard(\"BrentLab/kemmeren_2014\")\n", + "\n", + "print(\"=\" * 80)\n", + "print(\"KEMMEREN 2014 - Repo-Level Experimental Conditions\")\n", + "print(\"=\" * 80)\n", + "\n", + "# Get repo-level experimental conditions (returns a dict)\n", + "exp_conds = kemmeren_card.get_experimental_conditions(\"kemmeren_2014\")\n", + "print(f\"\\nRepo-level experimental_conditions exists: {bool(exp_conds)}\")\n", + "\n", + "if exp_conds:\n", + " # exp_conds is a dict, not a Pydantic model\n", + " print(f\"\\nTemperature: {exp_conds.get('temperature_celsius')}°C\")\n", + " \n", + " media = exp_conds.get('media', {})\n", + " print(f\"Media name: {media.get('name')}\")\n", + " \n", + " carbon_source = media.get('carbon_source', [])\n", + " if carbon_source:\n", + " compounds = [cs.get('compound') for cs in carbon_source]\n", + " concentrations = [cs.get('concentration_percent') for cs in carbon_source]\n", + " print(f\"Carbon source: {compounds}\")\n", + " print(f\"Concentrations: {concentrations}%\")\n", + "\n", + "# Check if there are field-level definitions\n", + "config = kemmeren_card.get_config(\"kemmeren_2014\")\n", + "has_field_defs = False\n", + "for feature in config.dataset_info.features:\n", + " if feature.definitions:\n", + " has_field_defs = True\n", + " print(f\"\\nField '{feature.name}' has definitions: {list(feature.definitions.keys())}\")\n", + "\n", + "if not has_field_defs:\n", + " print(\"\\nNo field-level definitions found\")\n", + "\n", + "print(\"\\n\" + \"=\" * 80)\n", + "print(\"Key Observation: Properties are at repo level, shared by all samples\")\n", + "print(\"=\" * 80)" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================================================================================\n", + "HARBISON 2004 - Field-Level Definitions\n", + "================================================================================\n", + "\n", + "Repo-level experimental_conditions: {}\n", + "\n", + "Field 'condition' has 14 definitions\n", + "Condition values: ['YPD', 'SM', 'RAPA', 'H2O2Hi', 'H2O2Lo', 'Acid', 'Alpha', 'BUT14', 'BUT90', 'Thi-', 'GAL', 'HEAT', 'Pi-', 'RAFF']\n", + "\n", + "Example definitions:\n", + "\n", + "YPD:\n", + " temperature_celsius: 30\n", + " media.name: YPD\n", + " media.carbon_source: ['D-glucose']\n", + "\n", + "HEAT:\n", + " temperature_celsius: None\n", + " media.name: YPD\n", + " media.carbon_source: ['D-glucose']\n", + "\n", + "GAL:\n", + " temperature_celsius: 30\n", + " media.name: yeast_extract_peptone\n", + " media.carbon_source: ['D-galactose']\n", + "\n", + "================================================================================\n", + "Key Observation: Properties are in field definitions, NOT at repo level\n", + "================================================================================\n" + ] + } + ], + "source": [ + "from tfbpapi.datainfo import DataCard\n", + "import json\n", + "\n", + "# Load Harbison 2004 (field-level example)\n", + "harbison_card = DataCard(\"BrentLab/harbison_2004\")\n", + "\n", + "print(\"=\" * 80)\n", + "print(\"HARBISON 2004 - Field-Level Definitions\")\n", + "print(\"=\" * 80)\n", + "\n", + "# Check if there are repo-level experimental conditions\n", + "exp_conds = harbison_card.get_experimental_conditions(\"harbison_2004\")\n", + "print(f\"\\nRepo-level experimental_conditions: {exp_conds}\")\n", + "\n", + "# Get field definitions for the 'condition' field\n", + "condition_defs = harbison_card.get_field_definitions(\"harbison_2004\", \"condition\")\n", + "print(f\"\\nField 'condition' has {len(condition_defs)} definitions\")\n", + "print(f\"Condition values: {list(condition_defs.keys())}\")\n", + "\n", + "# Show a few example definitions\n", + "print(\"\\nExample definitions:\")\n", + "for cond_name in [\"YPD\", \"HEAT\", \"GAL\"]:\n", + " if cond_name in condition_defs:\n", + " definition = condition_defs[cond_name]\n", + " print(f\"\\n{cond_name}:\")\n", + " print(f\" temperature_celsius: {definition.get('temperature_celsius')}\")\n", + " media = definition.get('media', {})\n", + " print(f\" media.name: {media.get('name')}\")\n", + " carbon_source = media.get('carbon_source', [])\n", + " if carbon_source:\n", + " compounds = [cs.get('compound') for cs in carbon_source]\n", + " print(f\" media.carbon_source: {compounds}\")\n", + "\n", + "print(\"\\n\" + \"=\" * 80)\n", + "print(\"Key Observation: Properties are in field definitions, NOT at repo level\")\n", + "print(\"=\" * 80)" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Configuration:\n", + "dataset_mappings:\n", + " BrentLab/harbison_2004:\n", + " datasets:\n", + " harbison_2004:\n", + " carbon_source:\n", + " field: condition\n", + " path: media.carbon_source\n", + "filters:\n", + " carbon_source:\n", + " - D-glucose\n", + "\n" + ] + } + ], + "source": [ + "# Create a filter configuration\n", + "glucose_config = {\n", + " \"filters\": {\n", + " \"carbon_source\": [\"D-glucose\"]\n", + " },\n", + " \"dataset_mappings\": {\n", + " \"BrentLab/harbison_2004\": {\n", + " \"datasets\": {\n", + " \"harbison_2004\": {\n", + " \"carbon_source\": {\n", + " \"field\": \"condition\",\n", + " \"path\": \"media.carbon_source\"\n", + " }\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "# Save to temporary file\n", + "config_path = Path(\"/tmp/glucose_filter.yaml\")\n", + "with open(config_path, 'w') as f:\n", + " yaml.dump(glucose_config, f)\n", + "\n", + "print(\"Configuration:\")\n", + "print(yaml.dump(glucose_config, default_flow_style=False))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Mode 0: Conditions Only\n", + "\n", + "Get just the matching field values without retrieving any data." + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Results:\n", + "\n", + "BrentLab/harbison_2004:\n", + " Included: True\n", + " Matching conditions:\n", + " condition: ['YPD', 'RAPA', 'H2O2Hi', 'H2O2Lo', 'Acid', 'Alpha', 'BUT14', 'BUT90', 'HEAT']\n" + ] + } + ], + "source": [ + "# Create resolver\n", + "resolver = DatasetFilterResolver(config_path)\n", + "\n", + "# Get matching conditions\n", + "results = resolver.resolve_filters(\n", + " repos=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", + " mode=\"conditions\"\n", + ")\n", + "\n", + "print(\"\\nResults:\")\n", + "for repo_id, result in results.items():\n", + " print(f\"\\n{repo_id}:\")\n", + " if result[\"included\"]:\n", + " print(f\" Included: True\")\n", + " print(f\" Matching conditions:\")\n", + " for field, values in result[\"matching_field_values\"].items():\n", + " print(f\" {field}: {values}\")\n", + " else:\n", + " print(f\" Included: False\")\n", + " print(f\" Reason: {result['reason']}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Understanding the Results\n", + "\n", + "The `matching_field_values` shows which values of the `condition` field have D-glucose:\n", + "- YPD: Rich media with 2% glucose\n", + "- HEAT: Heat stress with glucose\n", + "- H2O2Hi/H2O2Lo: Oxidative stress with glucose\n", + "- Acid: Acidic stress with glucose\n", + "- Alpha: Alpha factor arrest with glucose\n", + "- BUT14/BUT90: Butanol treatment with glucose\n", + "\n", + "Notice that GAL (galactose media) is NOT in the list - it was correctly filtered out." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 2: Multiple Filter Criteria\n", + "\n", + "Let's filter for samples with both D-glucose AND 30C temperature." + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Conditions with D-glucose AND 30C:\n", + "['YPD', 'RAPA', 'H2O2Hi', 'H2O2Lo', 'Acid', 'Alpha', 'BUT14', 'BUT90']\n" + ] + } + ], + "source": [ + "# Config with multiple filters\n", + "multi_filter_config = {\n", + " \"filters\": {\n", + " \"carbon_source\": [\"D-glucose\"],\n", + " \"temperature_celsius\": [30]\n", + " },\n", + " \"dataset_mappings\": {\n", + " \"BrentLab/harbison_2004\": {\n", + " \"datasets\": {\n", + " \"harbison_2004\": {\n", + " \"carbon_source\": {\n", + " \"field\": \"condition\",\n", + " \"path\": \"media.carbon_source\"\n", + " },\n", + " \"temperature_celsius\": {\n", + " \"field\": \"condition\",\n", + " \"path\": \"temperature_celsius\"\n", + " }\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "config_path = Path(\"/tmp/multi_filter.yaml\")\n", + "with open(config_path, 'w') as f:\n", + " yaml.dump(multi_filter_config, f)\n", + "\n", + "resolver = DatasetFilterResolver(config_path)\n", + "results = resolver.resolve_filters(\n", + " repos=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", + " mode=\"conditions\"\n", + ")\n", + "\n", + "print(\"Conditions with D-glucose AND 30C:\")\n", + "matching = results[\"BrentLab/harbison_2004\"][\"matching_field_values\"][\"condition\"]\n", + "print(matching)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Notice that HEAT is now excluded - it has glucose but is at 37C, not 30C. Multiple filter criteria use AND logic." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 3: Mode 1 - Sample-Level Metadata\n", + "\n", + "Retrieve actual sample metadata (one row per sample_id) for matching conditions." + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Retrieved 304 samples\n", + "\n", + "DataFrame shape: (304, 9)\n", + "\n", + "Columns: ['sample_id', 'db_id', 'regulator_locus_tag', 'regulator_symbol', 'condition', 'target_locus_tag', 'target_symbol', 'effect', 'pvalue']\n", + "\n", + "First few rows:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "sample_id", + "rawType": "int32", + "type": "integer" + }, + { + "name": "db_id", + "rawType": "float64", + "type": "float" + }, + { + "name": "regulator_locus_tag", + "rawType": "object", + "type": "string" + }, + { + "name": "regulator_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "condition", + "rawType": "object", + "type": "string" + }, + { + "name": "target_locus_tag", + "rawType": "object", + "type": "string" + }, + { + "name": "target_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "effect", + "rawType": "float64", + "type": "float" + }, + { + "name": "pvalue", + "rawType": "float64", + "type": "float" + } + ], + "ref": "a9bc83e5-ec64-4c54-a0b5-b8efb910402a", + "rows": [ + [ + "0", + "1", + "0.0", + "YSC0017", + "MATA1", + "YPD", + "YAL023C", + "PMT2", + "0.74920592", + "0.83707059" + ], + [ + "1", + "2", + "1.0", + "YAL051W", + "OAF1", + "YPD", + "YAL047C", + "SPC72", + "1.9661134", + "0.0076229595" + ], + [ + "2", + "3", + "2.0", + "YBL005W", + "PDR3", + "YPD", + "YBR001C", + "NTH2", + "0.97462425", + "0.56597448" + ], + [ + "3", + "4", + "3.0", + "YBL008W", + "HIR1", + "YPD", + "YBL037W", + "APL3", + "1.0555759", + "0.35839913" + ], + [ + "4", + "5", + "4.0", + "YBL021C", + "HAP3", + "YPD", + "YAL001C", + "TFC3", + "0.84731661", + "0.81162232" + ] + ], + "shape": { + "columns": 9, + "rows": 5 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sample_iddb_idregulator_locus_tagregulator_symbolconditiontarget_locus_tagtarget_symboleffectpvalue
010.0YSC0017MATA1YPDYAL023CPMT20.7492060.837071
121.0YAL051WOAF1YPDYAL047CSPC721.9661130.007623
232.0YBL005WPDR3YPDYBR001CNTH20.9746240.565974
343.0YBL008WHIR1YPDYBL037WAPL31.0555760.358399
454.0YBL021CHAP3YPDYAL001CTFC30.8473170.811622
\n", + "
" + ], + "text/plain": [ + " sample_id db_id regulator_locus_tag regulator_symbol condition \\\n", + "0 1 0.0 YSC0017 MATA1 YPD \n", + "1 2 1.0 YAL051W OAF1 YPD \n", + "2 3 2.0 YBL005W PDR3 YPD \n", + "3 4 3.0 YBL008W HIR1 YPD \n", + "4 5 4.0 YBL021C HAP3 YPD \n", + "\n", + " target_locus_tag target_symbol effect pvalue \n", + "0 YAL023C PMT2 0.749206 0.837071 \n", + "1 YAL047C SPC72 1.966113 0.007623 \n", + "2 YBR001C NTH2 0.974624 0.565974 \n", + "3 YBL037W APL3 1.055576 0.358399 \n", + "4 YAL001C TFC3 0.847317 0.811622 " + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Use same config but change mode to 'samples'\n", + "results_samples = resolver.resolve_filters(\n", + " repos=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", + " mode=\"samples\"\n", + ")\n", + "\n", + "# Get the DataFrame\n", + "df_samples = results_samples[\"BrentLab/harbison_2004\"][\"data\"]\n", + "\n", + "print(f\"Retrieved {len(df_samples)} samples\")\n", + "print(f\"\\nDataFrame shape: {df_samples.shape}\")\n", + "print(f\"\\nColumns: {list(df_samples.columns)}\")\n", + "print(f\"\\nFirst few rows:\")\n", + "df_samples.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Condition distribution:\n", + "condition\n", + "YPD 204\n", + "H2O2Hi 39\n", + "H2O2Lo 28\n", + "RAPA 14\n", + "BUT14 8\n", + "Alpha 5\n", + "BUT90 4\n", + "Acid 2\n", + "Name: count, dtype: int64\n" + ] + } + ], + "source": [ + "# Check the distribution of conditions in the retrieved samples\n", + "print(\"Condition distribution:\")\n", + "print(df_samples['condition'].value_counts())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 4: Mode 2 - Full Data\n", + "\n", + "Retrieve all measurements for matching samples (many rows per sample_id)." + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Retrieved 1,892,704 rows (measurements)\n", + "\n", + "DataFrame shape: (1892704, 9)\n", + "\n", + "Columns: ['sample_id', 'db_id', 'regulator_locus_tag', 'regulator_symbol', 'condition', 'target_locus_tag', 'target_symbol', 'effect', 'pvalue']\n", + "\n", + "First few rows:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "sample_id", + "rawType": "int32", + "type": "integer" + }, + { + "name": "db_id", + "rawType": "float64", + "type": "float" + }, + { + "name": "regulator_locus_tag", + "rawType": "object", + "type": "string" + }, + { + "name": "regulator_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "condition", + "rawType": "object", + "type": "string" + }, + { + "name": "target_locus_tag", + "rawType": "object", + "type": "string" + }, + { + "name": "target_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "effect", + "rawType": "float64", + "type": "float" + }, + { + "name": "pvalue", + "rawType": "float64", + "type": "float" + } + ], + "ref": "a828c111-7829-4bcd-a77c-ff5dbc95ba4a", + "rows": [ + [ + "0", + "1", + "0.0", + "YSC0017", + "MATA1", + "YPD", + "YAL001C", + "TFC3", + "1.697754", + "0.068704735" + ], + [ + "1", + "1", + "0.0", + "YSC0017", + "MATA1", + "YPD", + "YAL002W", + "VPS8", + null, + null + ], + [ + "2", + "1", + "0.0", + "YSC0017", + "MATA1", + "YPD", + "YAL003W", + "EFB1", + null, + null + ], + [ + "3", + "1", + "0.0", + "YSC0017", + "MATA1", + "YPD", + "YAL004W", + "YAL004W", + "0.74534215", + "0.83592938" + ], + [ + "4", + "1", + "0.0", + "YSC0017", + "MATA1", + "YPD", + "YAL005C", + "SSA1", + null, + null + ] + ], + "shape": { + "columns": 9, + "rows": 5 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sample_iddb_idregulator_locus_tagregulator_symbolconditiontarget_locus_tagtarget_symboleffectpvalue
010.0YSC0017MATA1YPDYAL001CTFC31.6977540.068705
110.0YSC0017MATA1YPDYAL002WVPS8NaNNaN
210.0YSC0017MATA1YPDYAL003WEFB1NaNNaN
310.0YSC0017MATA1YPDYAL004WYAL004W0.7453420.835929
410.0YSC0017MATA1YPDYAL005CSSA1NaNNaN
\n", + "
" + ], + "text/plain": [ + " sample_id db_id regulator_locus_tag regulator_symbol condition \\\n", + "0 1 0.0 YSC0017 MATA1 YPD \n", + "1 1 0.0 YSC0017 MATA1 YPD \n", + "2 1 0.0 YSC0017 MATA1 YPD \n", + "3 1 0.0 YSC0017 MATA1 YPD \n", + "4 1 0.0 YSC0017 MATA1 YPD \n", + "\n", + " target_locus_tag target_symbol effect pvalue \n", + "0 YAL001C TFC3 1.697754 0.068705 \n", + "1 YAL002W VPS8 NaN NaN \n", + "2 YAL003W EFB1 NaN NaN \n", + "3 YAL004W YAL004W 0.745342 0.835929 \n", + "4 YAL005C SSA1 NaN NaN " + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Use same config but change mode to 'full_data'\n", + "results_full = resolver.resolve_filters(\n", + " repos=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", + " mode=\"full_data\"\n", + ")\n", + "\n", + "# Get the DataFrame\n", + "df_full = results_full[\"BrentLab/harbison_2004\"][\"data\"]\n", + "\n", + "print(f\"Retrieved {len(df_full):,} rows (measurements)\")\n", + "print(f\"\\nDataFrame shape: {df_full.shape}\")\n", + "print(f\"\\nColumns: {list(df_full.columns)}\")\n", + "print(f\"\\nFirst few rows:\")\n", + "df_full.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Sample-level metadata: 304 samples\n", + "Full data: 1,892,704 measurements\n", + "Average measurements per sample: 6,226\n" + ] + } + ], + "source": [ + "# Compare to samples mode\n", + "n_samples = df_samples['sample_id'].nunique() if 'sample_id' in df_samples.columns else len(df_samples)\n", + "n_measurements = len(df_full)\n", + "measurements_per_sample = n_measurements / n_samples if n_samples > 0 else 0\n", + "\n", + "print(f\"Sample-level metadata: {n_samples} samples\")\n", + "print(f\"Full data: {n_measurements:,} measurements\")\n", + "print(f\"Average measurements per sample: {measurements_per_sample:,.0f}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 5: Filtering Across Multiple Datasets\n", + "\n", + "The real power comes from filtering across multiple datasets with different structures." + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Results across multiple datasets:\n", + "\n", + "BrentLab/harbison_2004:\n", + " Included: True\n", + " condition: ['YPD', 'RAPA', 'H2O2Hi', 'H2O2Lo', 'Acid', 'Alpha', 'BUT14', 'BUT90', 'GAL', 'HEAT']\n", + "\n", + "BrentLab/kemmeren_2014:\n", + " Included: True\n", + " No field-level conditions (all samples match at repo level)\n", + "\n" + ] + } + ], + "source": [ + "# Config for multiple datasets\n", + "multi_dataset_config = {\n", + " \"filters\": {\n", + " \"carbon_source\": [\"D-glucose\", \"D-galactose\"]\n", + " },\n", + " \"dataset_mappings\": {\n", + " \"BrentLab/harbison_2004\": {\n", + " \"datasets\": {\n", + " \"harbison_2004\": {\n", + " \"carbon_source\": {\n", + " \"field\": \"condition\",\n", + " \"path\": \"media.carbon_source\"\n", + " }\n", + " }\n", + " }\n", + " },\n", + " \"BrentLab/kemmeren_2014\": {\n", + " \"repo_level\": {\n", + " \"carbon_source\": {\n", + " \"path\": \"environmental_conditions.media.carbon_source\"\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "config_path = Path(\"/tmp/multi_dataset_filter.yaml\")\n", + "with open(config_path, 'w') as f:\n", + " yaml.dump(multi_dataset_config, f)\n", + "\n", + "resolver = DatasetFilterResolver(config_path)\n", + "\n", + "# Filter multiple datasets\n", + "results = resolver.resolve_filters(\n", + " repos=[\n", + " (\"BrentLab/harbison_2004\", \"harbison_2004\"),\n", + " (\"BrentLab/kemmeren_2014\", \"kemmeren_2014\")\n", + " ],\n", + " mode=\"conditions\"\n", + ")\n", + "\n", + "print(\"Results across multiple datasets:\\n\")\n", + "for repo_id, result in results.items():\n", + " print(f\"{repo_id}:\")\n", + " if result[\"included\"]:\n", + " print(f\" Included: True\")\n", + " if \"matching_field_values\" in result and result[\"matching_field_values\"]:\n", + " for field, values in result[\"matching_field_values\"].items():\n", + " print(f\" {field}: {values}\")\n", + " else:\n", + " print(\" No field-level conditions (all samples match at repo level)\")\n", + " else:\n", + " print(f\" Included: False\")\n", + " print(f\" Reason: {result['reason']}\")\n", + " print()\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 6: Galactose-Only Filter\n", + "\n", + "Let's filter for just galactose to see dataset exclusion in action." + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Galactose-only filter results:\n", + "Included: True\n", + "Matching conditions: ['GAL']\n" + ] + } + ], + "source": [ + "galactose_config = {\n", + " \"filters\": {\n", + " \"carbon_source\": [\"D-galactose\"]\n", + " },\n", + " \"dataset_mappings\": {\n", + " \"BrentLab/harbison_2004\": {\n", + " \"datasets\": {\n", + " \"harbison_2004\": {\n", + " \"carbon_source\": {\n", + " \"field\": \"condition\",\n", + " \"path\": \"media.carbon_source\"\n", + " }\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "config_path = Path(\"/tmp/galactose_filter.yaml\")\n", + "with open(config_path, 'w') as f:\n", + " yaml.dump(galactose_config, f)\n", + "\n", + "resolver = DatasetFilterResolver(config_path)\n", + "results = resolver.resolve_filters(\n", + " repos=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", + " mode=\"conditions\"\n", + ")\n", + "\n", + "print(\"Galactose-only filter results:\")\n", + "result = results[\"BrentLab/harbison_2004\"]\n", + "print(f\"Included: {result['included']}\")\n", + "if result['included']:\n", + " matching = result[\"matching_field_values\"][\"condition\"]\n", + " print(f\"Matching conditions: {matching}\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Only the GAL condition should match - it's the only one with galactose as the carbon source." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## How Path Resolution Works\n", + "\n", + "The `DatasetFilterResolver` uses a two-phase approach to handle heterogeneous dataset structures:\n", + "\n", + "### Phase 1: Property Location (Field vs Repo Level)\n", + "\n", + "**Field-Level Resolution** (when `field` is specified):\n", + "```python\n", + "# Configuration\n", + "carbon_source:\n", + " field: condition # Look in the 'condition' field\n", + " path: media.carbon_source # Path within each field value's definition\n", + "```\n", + "\n", + "The resolver:\n", + "1. Gets all definitions for the `condition` field\n", + "2. For each field value (YPD, HEAT, GAL, etc.), extracts the value at `media.carbon_source`\n", + "3. Returns field values where the extracted value matches the filter\n", + "\n", + "**Repo-Level Resolution** (no `field` specified):\n", + "```python\n", + "# Configuration\n", + "carbon_source:\n", + " path: environmental_conditions.media.carbon_source # Top-level path\n", + "```\n", + "\n", + "The resolver:\n", + "1. Looks at the top-level `experimental_conditions` in the datacard\n", + "2. Extracts the value at the specified path\n", + "3. Includes/excludes the entire dataset based on the match\n", + "\n", + "### Phase 2: Path Navigation\n", + "\n", + "Once the resolver knows WHERE to look (field vs repo level), it navigates to the property using dot notation.\n", + "\n", + "**Automatic Fallback for Nested Paths**:\n", + "\n", + "The resolver tries multiple path variants to handle different nesting depths:\n", + "\n", + "1. **Try full path first**: `environmental_conditions.media.carbon_source`\n", + "2. **If not found, try without prefix**: `media.carbon_source`\n", + "\n", + "This allows configs to work with both:\n", + "- Repo-level: `experimental_conditions.media.carbon_source` (finds it at step 1)\n", + "- Field-level: `media.carbon_source` (finds it at step 2)\n", + "\n", + "**Example**: Harbison 2004 field definitions don't have an `environmental_conditions` wrapper, so:\n", + "- Path specified: `media.carbon_source`\n", + "- Directly finds `media` in the field definition\n", + "- No fallback needed\n", + "\n", + "### Compound Extraction\n", + "\n", + "For carbon source and similar properties, the resolver handles list-of-dict format:\n", + "\n", + "```yaml\n", + "carbon_source:\n", + " - compound: D-glucose\n", + " concentration_percent: 2\n", + " - compound: D-galactose \n", + " concentration_percent: 1\n", + "```\n", + "\n", + "Automatically extracts to: `[\"D-glucose\", \"D-galactose\"]`\n", + "\n", + "### Why This Design?\n", + "\n", + "This two-phase approach (field specification + path navigation) allows:\n", + "\n", + "1. **No hardcoded field names** in the codebase\n", + "2. **Explicit configuration** - clear which field to examine \n", + "3. **Flexibility** - works with any field name (`condition`, `treatment`, `strain`, etc.)\n", + "4. **Automatic nesting handling** - paths work at different hierarchy levels\n", + "5. **Heterogeneous datasets** - each dataset can specify its own structure\n", + "\n", + "### Configuration Debugging Tips\n", + "\n", + "If your filter isn't working:\n", + "\n", + "1. **Check the datacard structure** - is the property at field or repo level?\n", + "2. **Verify the field name** - does the field exist? Is the name correct?\n", + "3. **Test the path** - use `DataCard.get_field_definitions()` to inspect\n", + "4. **Try simpler paths** - remove `environmental_conditions.` if not needed\n", + "5. **Use `conditions` mode first** - see which values match before retrieving data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Practical Example: Creating a Filter Config File\n", + "\n", + "Here's a complete example of a reusable filter configuration file." + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Saved configuration to my_filter_config.yaml\n", + "\n", + "Configuration contents:\n", + "dataset_mappings:\n", + " BrentLab/harbison_2004:\n", + " datasets:\n", + " harbison_2004:\n", + " carbon_source:\n", + " field: condition\n", + " path: media.carbon_source\n", + " temperature_celsius:\n", + " field: condition\n", + " path: temperature_celsius\n", + " BrentLab/kemmeren_2014:\n", + " repo_level:\n", + " carbon_source:\n", + " path: environmental_conditions.media.carbon_source\n", + " temperature_celsius:\n", + " path: environmental_conditions.temperature_celsius\n", + "filters:\n", + " carbon_source:\n", + " - D-glucose\n", + " - D-galactose\n", + " - D-raffinose\n", + " temperature_celsius:\n", + " - 30\n", + " - 37\n", + "\n" + ] + } + ], + "source": [ + "# Create a comprehensive filter config\n", + "comprehensive_config = {\n", + " \"filters\": {\n", + " \"carbon_source\": [\"D-glucose\", \"D-galactose\", \"D-raffinose\"],\n", + " \"temperature_celsius\": [30, 37]\n", + " },\n", + " \"dataset_mappings\": {\n", + " \"BrentLab/harbison_2004\": {\n", + " \"datasets\": {\n", + " \"harbison_2004\": {\n", + " \"carbon_source\": {\n", + " \"field\": \"condition\",\n", + " \"path\": \"media.carbon_source\"\n", + " },\n", + " \"temperature_celsius\": {\n", + " \"field\": \"condition\",\n", + " \"path\": \"temperature_celsius\"\n", + " }\n", + " }\n", + " }\n", + " },\n", + " \"BrentLab/kemmeren_2014\": {\n", + " \"repo_level\": {\n", + " \"carbon_source\": {\n", + " \"path\": \"environmental_conditions.media.carbon_source\"\n", + " },\n", + " \"temperature_celsius\": {\n", + " \"path\": \"environmental_conditions.temperature_celsius\"\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "# Save to a persistent location\n", + "config_file = Path(\"my_filter_config.yaml\")\n", + "with open(config_file, 'w') as f:\n", + " yaml.dump(comprehensive_config, f)\n", + "\n", + "print(f\"Saved configuration to {config_file}\")\n", + "print(\"\\nConfiguration contents:\")\n", + "print(yaml.dump(comprehensive_config, default_flow_style=False))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Simplifying Single-Dataset Repositories\n", + "\n", + "For repositories with a single dataset or when all datasets share the same structure, you can use `repo_level` for brevity:\n", + "\n", + "```yaml\n", + "dataset_mappings:\n", + " \"BrentLab/kemmeren_2014\":\n", + " repo_level:\n", + " carbon_source:\n", + " path: environmental_conditions.media.carbon_source\n", + " temperature_celsius:\n", + " path: environmental_conditions.temperature_celsius\n", + "```\n", + "\n", + "This applies the mappings to all datasets in the repo. For kemmeren_2014 (which has repo-level experimental_conditions), this is more concise than explicitly listing each dataset." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Advanced: Hierarchical Configuration for Multi-Dataset Repositories\n", + "\n", + "When a repository contains multiple datasets, you can use hierarchical configuration to:\n", + "1. Define repo-level mappings that apply to all datasets\n", + "2. Override or extend with dataset-specific mappings\n", + "\n", + "This is especially useful when datasets share common properties but have some differences." + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hierarchical configuration:\n", + "dataset_mappings:\n", + " BrentLab/multi_dataset_repo:\n", + " datasets:\n", + " dataset1:\n", + " temperature_celsius:\n", + " path: environmental_conditions.temperature_celsius\n", + " dataset2:\n", + " temperature_celsius:\n", + " path: custom.temp.location\n", + " dataset3:\n", + " carbon_source:\n", + " path: media.carbon\n", + " temperature_celsius:\n", + " path: temp\n", + " repo_level:\n", + " carbon_source:\n", + " path: environmental_conditions.media.carbon_source\n", + "filters:\n", + " carbon_source:\n", + " - D-glucose\n", + " temperature_celsius:\n", + " - 30\n", + "\n" + ] + } + ], + "source": [ + "# Hierarchical configuration example\n", + "hierarchical_config = {\n", + " \"filters\": {\n", + " \"carbon_source\": [\"D-glucose\"],\n", + " \"temperature_celsius\": [30]\n", + " },\n", + " \"dataset_mappings\": {\n", + " # Repository with multiple datasets\n", + " \"BrentLab/multi_dataset_repo\": {\n", + " # Repo-level mappings apply to ALL datasets in this repo\n", + " \"repo_level\": {\n", + " \"carbon_source\": {\n", + " \"path\": \"environmental_conditions.media.carbon_source\"\n", + " }\n", + " },\n", + " # Dataset-specific overrides\n", + " \"datasets\": {\n", + " \"dataset1\": {\n", + " # Inherits carbon_source from repo_level\n", + " # Adds temperature mapping for this specific dataset\n", + " \"temperature_celsius\": {\n", + " \"path\": \"environmental_conditions.temperature_celsius\"\n", + " }\n", + " },\n", + " \"dataset2\": {\n", + " # Inherits carbon_source from repo_level \n", + " # Uses different temperature path\n", + " \"temperature_celsius\": {\n", + " \"path\": \"custom.temp.location\"\n", + " }\n", + " },\n", + " \"dataset3\": {\n", + " # Completely overrides carbon_source for this dataset\n", + " \"carbon_source\": {\n", + " \"path\": \"media.carbon\" # Different structure\n", + " },\n", + " \"temperature_celsius\": {\n", + " \"path\": \"temp\" # Top-level property\n", + " }\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "print(\"Hierarchical configuration:\")\n", + "print(yaml.dump(hierarchical_config, default_flow_style=False))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Key Benefits of Hierarchical Configuration\n", + "\n", + "1. **DRY Principle**: Define common mappings once at repo level\n", + "2. **Flexibility**: Override for specific datasets that differ\n", + "3. **Maintainability**: Update repo-level mappings to affect all datasets at once\n", + "4. **Clarity**: Explicitly shows which datasets inherit vs override\n", + "\n", + "### How Merging Works\n", + "\n", + "For each dataset, the resolver:\n", + "1. Starts with repo-level mappings\n", + "2. Overlays dataset-specific mappings (these take precedence)\n", + "3. Returns the merged mapping for that dataset\n", + "\n", + "In the example above:\n", + "- `dataset1` gets `carbon_source` from repo_level + its own `temperature_celsius`\n", + "- `dataset2` gets `carbon_source` from repo_level + its own (different) `temperature_celsius`\n", + "- `dataset3` overrides both properties completely" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "The `DatasetFilterResolver` provides:\n", + "\n", + "1. **Flexible filtering** across heterogeneous datasets\n", + "2. **External configuration** - no code changes needed for new filters\n", + "3. **Two-level filtering** - dataset exclusion and field-level matching\n", + "4. **Three output modes** - from lightweight (conditions only) to full data\n", + "5. **Field-aware resolution** - explicit specification of where properties are located\n", + "6. **Automatic path resolution** - handles different nesting levels gracefully\n", + "\n", + "### When to Use Each Mode\n", + "\n", + "- **`conditions`**: Quick exploration - which datasets have matching samples? Which field values match?\n", + "- **`samples`**: Sample metadata analysis - how many samples match? What are their properties?\n", + "- **`full_data`**: Full analysis - retrieve all measurements for downstream processing\n", + "\n", + "### Best Practices for Configuration\n", + "\n", + "#### 1. Field Specification\n", + "- **Always specify `field`** when the property is in field-level definitions\n", + "- **Never specify `field`** when the property is at repo/config level\n", + "- When in doubt, check the datacard: if the property is in a field's `definitions`, use `field`\n", + "\n", + "#### 2. Property Paths\n", + "- Use the simplest path that works (avoid unnecessary nesting)\n", + "- For field-level properties, typically: `media.carbon_source` (not `environmental_conditions.media.carbon_source`)\n", + "- For repo-level properties, include full path: `environmental_conditions.media.carbon_source`\n", + "\n", + "#### 3. Hierarchical Configuration\n", + "- Use `repo_level` for properties shared across all datasets in a repository\n", + "- Use `datasets` to override or extend for specific datasets\n", + "- This keeps configuration DRY and maintainable\n", + "\n", + "#### 4. Filter Development Workflow\n", + "1. **Start with `conditions` mode** to verify your filters work correctly\n", + "2. **Check the results** - do the field values make sense?\n", + "3. **Use `samples` mode** to understand sample counts before downloading full data\n", + "4. **Only use `full_data`** when you're ready to retrieve the actual measurements\n", + "\n", + "#### 5. Configuration Management\n", + "- Create reusable YAML configs for common filter scenarios\n", + "- Document your filter configs with comments explaining the scientific rationale\n", + "- Version control your configs alongside your analysis code\n", + "- Test configs with `conditions` mode before using in production pipelines\n", + "\n", + "#### 6. Debugging\n", + "- If a dataset shows `included: False`, check the `reason` field\n", + "- Use `DataCard.get_field_definitions()` to inspect field structure\n", + "- Use `DataCard.get_experimental_conditions()` to inspect repo-level conditions\n", + "- Start simple (one filter, one dataset) then expand\n", + "\n", + "### Common Patterns\n", + "\n", + "**Pattern 1: Single property, field-level**\n", + "```yaml\n", + "filters:\n", + " carbon_source: [\"D-glucose\"]\n", + "dataset_mappings:\n", + " \"BrentLab/harbison_2004\":\n", + " datasets:\n", + " harbison_2004:\n", + " carbon_source:\n", + " field: condition\n", + " path: media.carbon_source\n", + "```\n", + "\n", + "**Pattern 2: Multiple properties, repo-level**\n", + "```yaml\n", + "filters:\n", + " carbon_source: [\"D-glucose\"]\n", + " temperature_celsius: [30]\n", + "dataset_mappings:\n", + " \"BrentLab/kemmeren_2014\":\n", + " repo_level:\n", + " carbon_source:\n", + " path: environmental_conditions.media.carbon_source\n", + " temperature_celsius:\n", + " path: environmental_conditions.temperature_celsius\n", + "```\n", + "\n", + "**Pattern 3: Mixed (repo-level + dataset overrides)**\n", + "```yaml\n", + "dataset_mappings:\n", + " \"BrentLab/multi_dataset_repo\":\n", + " repo_level:\n", + " carbon_source:\n", + " path: environmental_conditions.media.carbon_source\n", + " datasets:\n", + " dataset1:\n", + " temperature_celsius:\n", + " field: condition\n", + " path: temperature_celsius\n", + "```\n", + "\n", + "### Next Steps\n", + "\n", + "- Explore the `example_filter_config.yaml` file for more examples\n", + "- Try filtering across multiple datasets with different structures\n", + "- Integrate with downstream analysis pipelines using the exported DataFrames\n", + "- Consider creating a library of reusable filter configs for your common use cases" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "tfbpapi-py3.11", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/tutorials/my_filter_config.yaml b/docs/tutorials/my_filter_config.yaml new file mode 100644 index 0000000..c9b4f7e --- /dev/null +++ b/docs/tutorials/my_filter_config.yaml @@ -0,0 +1,24 @@ +dataset_mappings: + BrentLab/harbison_2004: + datasets: + harbison_2004: + carbon_source: + field: condition + path: media.carbon_source + temperature_celsius: + field: condition + path: temperature_celsius + BrentLab/kemmeren_2014: + repo_level: + carbon_source: + path: environmental_conditions.media.carbon_source + temperature_celsius: + path: environmental_conditions.temperature_celsius +filters: + carbon_source: + - D-glucose + - D-galactose + - D-raffinose + temperature_celsius: + - 30 + - 37 diff --git a/docs/tutorials/sample_manager_tutorial.ipynb b/docs/tutorials/sample_manager_tutorial.ipynb index f049afa..1c73ef2 100644 --- a/docs/tutorials/sample_manager_tutorial.ipynb +++ b/docs/tutorials/sample_manager_tutorial.ipynb @@ -38,9 +38,17 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 35, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "SampleManager(0 datasets, 0 samples)\n" + ] + } + ], "source": [ "from tfbpapi.datainfo.sample_manager import SampleManager, SampleNode\n", "from tfbpapi.datainfo import DataCard\n", @@ -62,9 +70,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 36, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Repo-level conditions:\n", + "{}\n" + ] + } + ], "source": [ "# Load a DataCard to see its experimental conditions structure\n", "card = DataCard('BrentLab/harbison_2004')\n", @@ -77,9 +94,45 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 37, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Field-level definition for 'YPD':\n", + "{\n", + " \"description\": \"Rich media baseline condition\",\n", + " \"temperature_celsius\": 30,\n", + " \"cultivation_method\": \"unspecified\",\n", + " \"growth_phase_at_harvest\": {\n", + " \"od600\": 0.8\n", + " },\n", + " \"media\": {\n", + " \"name\": \"YPD\",\n", + " \"carbon_source\": [\n", + " {\n", + " \"compound\": \"D-glucose\",\n", + " \"concentration_percent\": 2\n", + " }\n", + " ],\n", + " \"nitrogen_source\": [\n", + " {\n", + " \"compound\": \"yeast_extract\",\n", + " \"concentration_percent\": 1\n", + " },\n", + " {\n", + " \"compound\": \"peptone\",\n", + " \"concentration_percent\": 2\n", + " }\n", + " ]\n", + " }\n", + "}\n" + ] + } + ], "source": [ "# Get field-level condition definitions\n", "field_defs = card.get_field_definitions('harbison_2004', 'condition')\n", @@ -136,9 +189,23 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 38, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Flattened properties:\n", + " environmental_conditions.cultivation_method: liquid_culture (from repo)\n", + " environmental_conditions.media.carbon_source: D-glucose (from field)\n", + " environmental_conditions.media.name: YPD (from field)\n", + " environmental_conditions.media.nitrogen_source: yeast_extract, peptone (from field)\n", + " environmental_conditions.temperature_celsius: 30 (from repo)\n", + " strain_background: BY4741 (from repo)\n" + ] + } + ], "source": [ "from tfbpapi.datainfo.sample_manager import SampleNode, ConditionFlattener\n", "\n", @@ -181,9 +248,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 39, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Node: SampleNode(BrentLab/harbison_2004:harbison_2004:sample_001, 8 properties)\n", + "Global ID: BrentLab/harbison_2004:harbison_2004:sample_001\n", + "\n", + "Sample properties:\n", + " Temperature: 30\n", + " Carbon source: D-glucose\n" + ] + } + ], "source": [ "# Create a sample node\n", "node = SampleNode(\n", @@ -212,9 +292,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 40, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Query 1 matches: True\n", + "Query 2 matches: True\n", + "Query 3 matches: True\n", + "Query 4 matches: True\n" + ] + } + ], "source": [ "from tfbpapi.datainfo.sample_manager import SampleFilter\n", "\n", @@ -273,9 +364,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 41, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Set 1: ActiveSet(name=glucose_samples, size=3)\n", + "Set 2: ActiveSet(name=heat_stress_samples, size=3)\n" + ] + } + ], "source": [ "from tfbpapi.datainfo.sample_manager import ActiveSet\n", "\n", @@ -298,9 +398,24 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 42, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Union: ActiveSet(name=glucose_or_heat, size=4)\n", + " Sample IDs: ['BrentLab:harbison_2004:sample_001', 'BrentLab:harbison_2004:sample_002', 'BrentLab:harbison_2004:sample_003', 'BrentLab:harbison_2004:sample_004']\n", + "\n", + "Intersection: ActiveSet(name=glucose_and_heat, size=2)\n", + " Sample IDs: ['BrentLab:harbison_2004:sample_002', 'BrentLab:harbison_2004:sample_003']\n", + "\n", + "Difference: ActiveSet(name=glucose_no_heat, size=1)\n", + " Sample IDs: ['BrentLab:harbison_2004:sample_001']\n" + ] + } + ], "source": [ "# Set operations\n", "\n", @@ -331,9 +446,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 43, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 14 condition definitions\n", + "Conditions: ['Acid', 'Alpha', 'BUT14', 'BUT90', 'GAL', 'H2O2Hi', 'H2O2Lo', 'HEAT', 'Pi-', 'RAFF', 'RAPA', 'SM', 'Thi-', 'YPD']\n" + ] + } + ], "source": [ "# For this tutorial, we'll manually create nodes from DataCard information\n", "# In production, you would use: manager.load_from_datacard(\"BrentLab/harbison_2004\", \"harbison_2004\")\n", @@ -352,9 +476,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 44, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Loaded 14 sample nodes\n", + "SampleManager(1 datasets, 14 samples)\n" + ] + } + ], "source": [ "# Manually create nodes for each condition (simulating samples)\n", "# This demonstrates what load_from_datacard will do automatically\n", @@ -385,9 +519,24 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 45, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Properties available in YPD sample:\n", + " cultivation_method: unspecified\n", + " description: Rich media baseline condition\n", + " growth_phase_at_harvest.od600: 0.8\n", + " media.carbon_source: D-glucose\n", + " media.name: YPD\n", + " media.nitrogen_source: yeast_extract, peptone\n", + " temperature_celsius: 30\n" + ] + } + ], "source": [ "# Explore what properties are available\n", "sample_node = demo_manager.get_sample(\"BrentLab/harbison_2004:harbison_2004:YPD\")\n", @@ -409,9 +558,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 46, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 0 conditions with D-glucose\n", + "Conditions: []\n" + ] + } + ], "source": [ "# Find all samples with D-glucose as carbon source\n", "glucose_samples = demo_manager.filter_all({\n", @@ -424,9 +582,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 47, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Found 0 conditions with alternative carbon sources\n", + "Conditions: []\n" + ] + } + ], "source": [ "# Find samples with alternative carbon sources\n", "alt_carbon = demo_manager.filter_all({\n", @@ -442,9 +610,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 48, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Found 0 conditions with additives\n", + "Conditions: []\n" + ] + } + ], "source": [ "# Find samples with media additives (like butanol)\n", "with_additives = demo_manager.filter_all({\n", @@ -464,9 +642,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 49, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Carbon source distribution:\n", + " missing: 14 samples\n" + ] + } + ], "source": [ "# Analyze carbon source distribution\n", "carbon_dist = demo_manager.get_property_distribution(\n", @@ -480,9 +667,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 50, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Media type distribution:\n", + " missing: 14 samples\n" + ] + } + ], "source": [ "# Analyze media names\n", "media_dist = demo_manager.get_property_distribution(\n", @@ -503,9 +700,23 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 51, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Summary of loaded datasets:\n", + " repo_id config_name sample_count \\\n", + "0 BrentLab/harbison_2004 harbison_2004 14 \n", + "\n", + " properties \n", + "0 [description, temperature_celsius, cultivation... \n" + ] + } + ], "source": [ "# Get summary of loaded data\n", "summary = demo_manager.get_summary()\n", @@ -581,7 +792,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "tfbpapi-py3.11", "language": "python", "name": "python3" }, @@ -595,7 +806,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.0" + "version": "3.11.9" } }, "nbformat": 4, diff --git a/example_filter_config.yaml b/example_filter_config.yaml new file mode 100644 index 0000000..bae0ac7 --- /dev/null +++ b/example_filter_config.yaml @@ -0,0 +1,88 @@ +# Example filter configuration for DatasetFilterResolver +# +# This file demonstrates how to specify filters and dataset-specific +# property mappings for heterogeneous datasets. +# +# Configuration supports two formats: +# 1. Legacy format: Direct mappings at repo level (backward compatible) +# 2. Hierarchical format: Repo-level + dataset-specific mappings +# +# Usage: +# from tfbpapi.datainfo.filter_resolver import DatasetFilterResolver +# resolver = DatasetFilterResolver("example_filter_config.yaml") +# results = resolver.resolve_filters([("BrentLab/harbison_2004", "harbison_2004")]) + +# Filters specify acceptable values for each property +# Multiple values = OR logic (match any) +# Multiple properties = AND logic (match all) +filters: + carbon_source: + - "D-glucose" + - "D-galactose" + - "D-raffinose" + + temperature_celsius: + - 30 + - 37 + +# Dataset mappings specify where to find each property in each dataset's DataCard +# The 'path' uses dot notation to navigate nested structures +# For field-level definitions, use 'field' to specify which field contains the property +dataset_mappings: + # FIELD-LEVEL FORMAT + # For datasets where properties are in field definitions (e.g., condition field) + "BrentLab/harbison_2004": + datasets: + harbison_2004: + carbon_source: + field: condition # Look in the 'condition' field's definitions + path: "media.carbon_source" + temperature_celsius: + field: condition + path: "temperature_celsius" + + # REPO-LEVEL FORMAT + # For datasets where properties are at repo/config level + "BrentLab/kemmeren_2014": + repo_level: + carbon_source: + path: "environmental_conditions.media.carbon_source" + temperature_celsius: + path: "environmental_conditions.temperature_celsius" + + # NEW HIERARCHICAL FORMAT + # Supports repo-level mappings that apply to all datasets + # and dataset-specific overrides + "BrentLab/example_multi_dataset_repo": + # Repo-level mappings apply to all datasets in this repository + repo_level: + carbon_source: + path: "environmental_conditions.media.carbon_source" + temperature_celsius: + path: "environmental_conditions.temperature_celsius" + + # Dataset-specific mappings override repo-level for particular datasets + datasets: + dataset1: + # This dataset has field-level definitions + # Override to look in 'condition' field instead of repo level + carbon_source: + field: condition + path: "media.carbon_source" + temperature_celsius: + field: condition + path: "temperature_celsius" + + dataset2: + # Inherits repo-level mappings (no field specification) + # Can add dataset-specific properties + growth_phase: + path: "growth_conditions.phase" + + dataset3: + # Mixed: some properties in 'treatment' field, some at repo level + carbon_source: + field: treatment + path: "media.carbon" + temperature_celsius: + path: "temp" # Direct repo/config level property diff --git a/tfbpapi/datainfo/filter_resolver.py b/tfbpapi/datainfo/filter_resolver.py new file mode 100644 index 0000000..f83f69f --- /dev/null +++ b/tfbpapi/datainfo/filter_resolver.py @@ -0,0 +1,601 @@ +""" +Dataset filter resolver with external configuration for heterogeneous datasets. + +This module provides a simple, configuration-driven approach to filtering samples +across datasets with varying experimental condition structures. Users specify +filters and dataset-specific property paths in external YAML files. + +Key Components: +- DatasetFilterResolver: Main class for applying filters across datasets +- Three output modes: conditions, samples, full_data +- External YAML configuration for filters and property mappings +- Automatic detection of property location (repo/config/field level) + +Example Configuration: + ```yaml + filters: + carbon_source: ["D-glucose", "D-galactose"] + temperature_celsius: [30, 37] + + dataset_mappings: + "BrentLab/harbison_2004": + carbon_source: + path: "environmental_conditions.media.carbon_source" + temperature_celsius: + path: "environmental_conditions.temperature_celsius" + ``` + +Example Usage: + >>> resolver = DatasetFilterResolver("filters.yaml") + >>> results = resolver.resolve_filters( + ... repos=[("BrentLab/harbison_2004", "harbison_2004")], + ... mode="conditions" + ... ) +""" + +from __future__ import annotations + +from pathlib import Path +from typing import Any + +import pandas as pd +import yaml + +from tfbpapi.datainfo import DataCard +from tfbpapi.errors import DataCardError +from tfbpapi.HfQueryAPI import HfQueryAPI + + +def get_nested_value(data: dict, path: str) -> Any: + """ + Navigate nested dict using dot notation. + + Handles missing intermediate keys gracefully by returning None. + If the full path is not found, tries removing common prefixes + like 'environmental_conditions' to handle different nesting levels. + + :param data: Dictionary to navigate + :param path: Dot-separated path (e.g., "environmental_conditions.media.name") + :return: Value at path or None if not found + """ + if not isinstance(data, dict): + return None + + keys = path.split(".") + current = data + + # Try the full path first + for key in keys: + if not isinstance(current, dict) or key not in current: + # Full path failed, try fallback paths + fallback_paths = [] + + # If path starts with environmental_conditions, try without it + if keys[0] == "environmental_conditions" and len(keys) > 1: + fallback_paths.append(".".join(keys[1:])) + + # Try each fallback + for fallback_path in fallback_paths: + fallback_keys = fallback_path.split(".") + fallback_current = data + fallback_success = True + + for fallback_key in fallback_keys: + if ( + not isinstance(fallback_current, dict) + or fallback_key not in fallback_current + ): + fallback_success = False + break + fallback_current = fallback_current[fallback_key] + + if fallback_success: + return fallback_current + + return None + current = current[key] + + return current + + +def extract_compound_names(value: Any) -> list[str]: + """ + Extract compound names from various representations. + + Handles: + - List of dicts: [{"compound": "D-glucose", ...}] -> ["D-glucose"] + - String: "D-glucose" -> ["D-glucose"] + - None or "unspecified" -> [] + + :param value: Value to extract from + :return: List of compound names + """ + if value is None or value == "unspecified": + return [] + + if isinstance(value, str): + return [value] + + if isinstance(value, list): + compounds = [] + for item in value: + if isinstance(item, dict) and "compound" in item: + compounds.append(item["compound"]) + elif isinstance(item, str): + compounds.append(item) + return compounds + + return [] + + +class DatasetFilterResolver: + """ + Resolve filters across heterogeneous datasets using external configuration. + + This class takes an external YAML configuration specifying: + 1. Filter criteria (which values are acceptable for each property) + 2. Repository and dataset-specific property paths (where to find each property) + + Configuration structure supports: + - Repo-level mappings: Apply to all datasets in a repo + - Dataset-level mappings: Override repo-level for specific datasets + + Example: + dataset_mappings: + "BrentLab/my_repo": + repo_level: + carbon_source: + path: environmental_conditions.media.carbon_source + datasets: + dataset1: + temperature_celsius: + path: custom.temp.path + dataset2: + temperature_celsius: + path: other.temp.path + + It then resolves filters across datasets with three output modes: + - conditions: Just which field values match (no data retrieval) + - samples: Sample-level metadata (one row per sample_id) + - full_data: All measurements for matching samples + + Attributes: + config: Loaded configuration dict + filters: Filter criteria from config + mappings: Repository/dataset-specific property paths + """ + + def __init__(self, config_path: Path | str): + """ + Initialize resolver with external configuration. + + :param config_path: Path to YAML configuration file + """ + self.config = self._load_config(Path(config_path)) + self.filters = self.config.get("filters", {}) + self.mappings = self.config.get("dataset_mappings", {}) + + def _load_config(self, config_path: Path) -> dict: + """ + Load YAML configuration file. + + :param config_path: Path to YAML file + :return: Configuration dict + :raises FileNotFoundError: If config file doesn't exist + :raises yaml.YAMLError: If config is invalid YAML + """ + if not config_path.exists(): + raise FileNotFoundError(f"Configuration file not found: {config_path}") + + with open(config_path) as f: + config = yaml.safe_load(f) + + if not isinstance(config, dict): + raise ValueError("Configuration must be a dict") + + return config + + def _get_property_mappings(self, repo_id: str, config_name: str) -> dict: + """ + Get property mappings for a specific repo/dataset combination. + + Merges repo-level and dataset-level mappings, with dataset-level taking precedence. + + Supports configuration formats: + 1. Field-specific path (for field-level definitions): + carbon_source: + field: condition + path: media.carbon_source + + 2. Direct path (for repo/config level): + carbon_source: + path: environmental_conditions.media.carbon_source + + 3. Hierarchical with repo_level and datasets: + repo_level: + carbon_source: + path: ... + datasets: + dataset1: + carbon_source: + field: condition + path: ... + + :param repo_id: Repository ID + :param config_name: Dataset/config name + :return: Merged property mappings dict + """ + if repo_id not in self.mappings: + return {} + + repo_config = self.mappings[repo_id] + + # Check if this is the new hierarchical format + if "repo_level" in repo_config or "datasets" in repo_config: + # New format: merge repo_level and dataset-specific + mappings = {} + + # Start with repo-level mappings (apply to all datasets) + if "repo_level" in repo_config: + mappings.update(repo_config["repo_level"]) + + # Override with dataset-specific mappings + if "datasets" in repo_config and config_name in repo_config["datasets"]: + mappings.update(repo_config["datasets"][config_name]) + + return mappings + else: + # Legacy format: direct property mappings + # This assumes the config at repo level applies to all datasets + return repo_config + + def resolve_filters( + self, + repos: list[tuple[str, str]], + mode: str = "conditions", + token: str | None = None, + ) -> dict[str, Any]: + """ + Resolve filters across datasets. + + :param repos: List of (repo_id, config_name) tuples to check + :param mode: Output mode - "conditions", "samples", or "full_data" + :param token: Optional HuggingFace token for private repos + :return: Dict mapping repo_id to results + :raises ValueError: If mode is invalid + """ + if mode not in ["conditions", "samples", "full_data"]: + raise ValueError( + f"Invalid mode: {mode}. Must be 'conditions', 'samples', or 'full_data'" + ) + + results = {} + + for repo_id, config_name in repos: + try: + # Load DataCard + card = DataCard(repo_id, token=token) + + # Check if this dataset has mappings + if repo_id not in self.mappings: + results[repo_id] = { + "included": False, + "reason": f"No property mappings defined for {repo_id}", + } + continue + + # Check repo/config level filters (Level 1) + included, reason = self._check_repo_config_level( + card, config_name, repo_id + ) + + if not included: + results[repo_id] = {"included": False, "reason": reason} + continue + + # Dataset passes Level 1, check field-level filters (Level 2) + matching_field_values = self._check_field_level( + card, config_name, repo_id + ) + + # Build result based on mode + result = { + "included": True, + "matching_field_values": matching_field_values, + } + + if mode == "conditions": + # Mode 0: Just conditions, no data + pass + elif mode == "samples": + # Mode 1: Sample-level metadata + result["data"] = self._get_sample_metadata( + repo_id, config_name, matching_field_values, token + ) + elif mode == "full_data": + # Mode 2: Full data with measurements + result["data"] = self._get_full_data( + repo_id, config_name, matching_field_values, token + ) + + results[repo_id] = result + + except Exception as e: + results[repo_id] = { + "included": False, + "reason": f"Error processing dataset: {str(e)}", + } + + return results + + def _check_repo_config_level( + self, + card: DataCard, + config_name: str, + repo_id: str, + ) -> tuple[bool, str]: + """ + Check if repo/config level conditions match filters (Level 1). + + :param card: DataCard instance + :param config_name: Configuration name + :param repo_id: Repository ID + :return: (included, reason) tuple + """ + # Get repo and config level conditions + try: + conditions = card.get_experimental_conditions(config_name) + except DataCardError: + conditions = {} + + if not conditions: + # No repo/config conditions to check, include by default + return True, "" + + # Check each filter property at repo/config level + property_mappings = self._get_property_mappings(repo_id, config_name) + + for filter_prop, acceptable_values in self.filters.items(): + if filter_prop not in property_mappings: + continue + + # Get path for this property + path = property_mappings[filter_prop]["path"] + + # Try to get value at this path + value = get_nested_value(conditions, path) + + if value is None: + # Property not specified at repo/config level, skip + continue + + # Extract compound names if this is a compound list + if isinstance(value, list) and value and isinstance(value[0], dict): + actual_values = extract_compound_names(value) + else: + actual_values = [value] if not isinstance(value, list) else value + + # Check if any actual value matches acceptable values + matches = False + for actual in actual_values: + if actual in acceptable_values: + matches = True + break + + if not matches: + return ( + False, + f"{filter_prop}: found {actual_values}, wanted {acceptable_values}", + ) + + return True, "" + + def _check_field_level( + self, + card: DataCard, + config_name: str, + repo_id: str, + ) -> dict[str, list[str]]: + """ + Check field-level conditions and return matching field values (Level 2). + + :param card: DataCard instance + :param config_name: Configuration name + :param repo_id: Repository ID + :return: Dict mapping field names to lists of matching values + """ + matching = {} + property_mappings = self._get_property_mappings(repo_id, config_name) + + # Get config to find fields with role=experimental_condition + config = card.get_config(config_name) + if not config: + return matching + + # Group property mappings by field (if field is specified) + # field_mappings: {field_name: {prop: path, ...}} + field_mappings = {} + general_mappings = {} # Properties without field specification + + for prop, mapping in property_mappings.items(): + if "field" in mapping: + # Field-specific mapping + field_name = mapping["field"] + if field_name not in field_mappings: + field_mappings[field_name] = {} + field_mappings[field_name][prop] = mapping["path"] + else: + # General mapping (repo/config level) + general_mappings[prop] = mapping["path"] + + # Process each field that has mappings + for field_name, prop_paths in field_mappings.items(): + # Check if this field has definitions + definitions = card.get_field_definitions(config_name, field_name) + if not definitions: + continue + + matching_values = [] + + for field_value, definition in definitions.items(): + # Check if this field value matches all filter criteria + matches_all = True + + for filter_prop, acceptable_values in self.filters.items(): + if filter_prop not in prop_paths: + continue + + # Get path for this property + path = prop_paths[filter_prop] + + # Get value from this field value's definition + value = get_nested_value(definition, path) + + if value is None: + # Property not in this definition, doesn't match + matches_all = False + break + + # Extract compound names if needed + if isinstance(value, list) and value and isinstance(value[0], dict): + actual_values = extract_compound_names(value) + else: + actual_values = ( + [value] if not isinstance(value, list) else value + ) + + # Check if any actual value matches acceptable values + matches = False + for actual in actual_values: + if actual in acceptable_values: + matches = True + break + + if not matches: + matches_all = False + break + + if matches_all: + matching_values.append(field_value) + + if matching_values: + matching[field_name] = matching_values + + return matching + + def _get_sample_metadata( + self, + repo_id: str, + config_name: str, + matching_field_values: dict[str, list[str]], + token: str | None = None, + ) -> pd.DataFrame: + """ + Get sample-level metadata (Mode 1). + + Returns one row per sample_id with metadata columns. + + :param repo_id: Repository ID + :param config_name: Configuration name + :param matching_field_values: Dict of field names to matching values + :param token: Optional HuggingFace token + :return: DataFrame with sample metadata + """ + # Initialize query API + api = HfQueryAPI(repo_id, token=token) + + # Build WHERE clause from matching field values + where_conditions = [] + for field_name, values in matching_field_values.items(): + if len(values) == 1: + # Single value: exact match + where_conditions.append(f"{field_name} = '{values[0]}'") + else: + # Multiple values: IN clause + values_str = "', '".join(values) + where_conditions.append(f"{field_name} IN ('{values_str}')") + + where_clause = " AND ".join(where_conditions) if where_conditions else "1=1" + + # Query for distinct sample_id with metadata fields + # Get all columns to understand structure + sql = f""" + SELECT DISTINCT * + FROM {config_name} + WHERE {where_clause} + """ + + try: + df = api.query(sql, config_name) + + # For sample-level, we want one row per sample_id + # Group by sample_id and take first value for other columns + if "sample_id" in df.columns: + # Get metadata columns (exclude measurement columns if possible) + # This is a heuristic - may need refinement + df_samples = df.groupby("sample_id").first().reset_index() + return df_samples + else: + # No sample_id column, return distinct rows + return df + + except Exception as e: + # Return error info + return pd.DataFrame( + {"error": [str(e)], "note": ["Failed to retrieve sample metadata"]} + ) + + def _get_full_data( + self, + repo_id: str, + config_name: str, + matching_field_values: dict[str, list[str]], + token: str | None = None, + ) -> pd.DataFrame: + """ + Get full data with all measurements (Mode 2). + + Returns many rows per sample_id (one per measured feature/target). + + :param repo_id: Repository ID + :param config_name: Configuration name + :param matching_field_values: Dict of field names to matching values + :param token: Optional HuggingFace token + :return: DataFrame with full data + """ + # Initialize query API + api = HfQueryAPI(repo_id, token=token) + + # Build WHERE clause from matching field values + where_conditions = [] + for field_name, values in matching_field_values.items(): + if len(values) == 1: + # Single value: exact match + where_conditions.append(f"{field_name} = '{values[0]}'") + else: + # Multiple values: IN clause + values_str = "', '".join(values) + where_conditions.append(f"{field_name} IN ('{values_str}')") + + where_clause = " AND ".join(where_conditions) if where_conditions else "1=1" + + # Query for all data matching the conditions + sql = f""" + SELECT * + FROM {config_name} + WHERE {where_clause} + """ + + try: + return api.query(sql, config_name) + except Exception as e: + # Return error info + return pd.DataFrame( + {"error": [str(e)], "note": ["Failed to retrieve full data"]} + ) + + def __repr__(self) -> str: + """String representation.""" + n_filters = len(self.filters) + n_datasets = len(self.mappings) + return f"DatasetFilterResolver({n_filters} filters, {n_datasets} datasets configured)" diff --git a/tfbpapi/tests/datainfo/test_filter_resolver.py b/tfbpapi/tests/datainfo/test_filter_resolver.py new file mode 100644 index 0000000..38cfa4b --- /dev/null +++ b/tfbpapi/tests/datainfo/test_filter_resolver.py @@ -0,0 +1,385 @@ +"""Tests for DatasetFilterResolver.""" + +from pathlib import Path +from tempfile import NamedTemporaryFile + +import pandas as pd +import pytest +import yaml + +from tfbpapi.datainfo.filter_resolver import ( + DatasetFilterResolver, + extract_compound_names, + get_nested_value, +) + + +class TestHelperFunctions: + """Test helper functions.""" + + def test_get_nested_value_simple(self): + """Test getting nested values with dot notation.""" + data = { + "environmental_conditions": { + "media": { + "name": "YPD", + "carbon_source": [{"compound": "D-glucose"}] + }, + "temperature_celsius": 30 + } + } + + assert get_nested_value(data, "environmental_conditions.temperature_celsius") == 30 + assert get_nested_value(data, "environmental_conditions.media.name") == "YPD" + assert get_nested_value(data, "nonexistent.path") is None + assert get_nested_value(data, "environmental_conditions.media.nonexistent") is None + + def test_get_nested_value_fallback(self): + """Test fallback path resolution for field-level definitions.""" + # Field-level definition (no environmental_conditions wrapper) + field_def = { + "media": { + "name": "YPD", + "carbon_source": [{"compound": "D-glucose"}] + }, + "temperature_celsius": 30 + } + + # Path with environmental_conditions should fallback to path without it + assert get_nested_value(field_def, "environmental_conditions.media.name") == "YPD" + assert get_nested_value(field_def, "environmental_conditions.temperature_celsius") == 30 + + # Direct path should still work + assert get_nested_value(field_def, "media.name") == "YPD" + assert get_nested_value(field_def, "temperature_celsius") == 30 + + def test_extract_compound_names(self): + """Test extracting compound names from various formats.""" + # List of dicts + value1 = [ + {"compound": "D-glucose", "concentration_percent": 2}, + {"compound": "D-galactose", "concentration_percent": 1} + ] + assert extract_compound_names(value1) == ["D-glucose", "D-galactose"] + + # String + assert extract_compound_names("D-glucose") == ["D-glucose"] + + # None + assert extract_compound_names(None) == [] + + # "unspecified" + assert extract_compound_names("unspecified") == [] + + # Empty list + assert extract_compound_names([]) == [] + + +class TestDatasetFilterResolver: + """Test DatasetFilterResolver class.""" + + @pytest.fixture + def simple_config(self): + """Create a simple test configuration.""" + return { + "filters": { + "carbon_source": ["D-glucose", "D-galactose"], + "temperature_celsius": [30] + }, + "dataset_mappings": { + "BrentLab/harbison_2004": { + "datasets": { + "harbison_2004": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source" + }, + "temperature_celsius": { + "field": "condition", + "path": "temperature_celsius" + } + } + } + } + } + } + + @pytest.fixture + def config_file(self, simple_config, tmp_path): + """Create a temporary config file.""" + config_path = tmp_path / "test_config.yaml" + with open(config_path, 'w') as f: + yaml.dump(simple_config, f) + return config_path + + def test_init(self, config_file): + """Test initialization.""" + resolver = DatasetFilterResolver(config_file) + + assert len(resolver.filters) == 2 + assert "carbon_source" in resolver.filters + assert resolver.filters["carbon_source"] == ["D-glucose", "D-galactose"] + assert len(resolver.mappings) == 1 + assert "BrentLab/harbison_2004" in resolver.mappings + + def test_init_missing_file(self): + """Test initialization with missing config file.""" + with pytest.raises(FileNotFoundError): + DatasetFilterResolver("nonexistent.yaml") + + def test_resolve_filters_mode_conditions(self, config_file): + """Test resolve_filters in conditions mode.""" + resolver = DatasetFilterResolver(config_file) + + # This will actually try to load the DataCard, so it's more of an integration test + # For now, test the structure + results = resolver.resolve_filters( + repos=[("BrentLab/harbison_2004", "harbison_2004")], + mode="conditions" + ) + + assert "BrentLab/harbison_2004" in results + result = results["BrentLab/harbison_2004"] + + # Should have included field + assert "included" in result + + # If included, should have matching_field_values + if result["included"]: + assert "matching_field_values" in result + + def test_resolve_filters_invalid_mode(self, config_file): + """Test resolve_filters with invalid mode.""" + resolver = DatasetFilterResolver(config_file) + + with pytest.raises(ValueError, match="Invalid mode"): + resolver.resolve_filters( + repos=[("BrentLab/harbison_2004", "harbison_2004")], + mode="invalid" + ) + + def test_repr(self, config_file): + """Test string representation.""" + resolver = DatasetFilterResolver(config_file) + repr_str = repr(resolver) + + assert "DatasetFilterResolver" in repr_str + assert "2 filters" in repr_str + assert "1 datasets" in repr_str + + def test_hierarchical_config(self, tmp_path): + """Test hierarchical configuration with repo_level and dataset-specific mappings.""" + config = { + "filters": { + "carbon_source": ["D-glucose"], + "temperature_celsius": [30] + }, + "dataset_mappings": { + "BrentLab/test_repo": { + "repo_level": { + "carbon_source": { + "path": "environmental_conditions.media.carbon_source" + } + }, + "datasets": { + "dataset1": { + "temperature_celsius": { + "field": "condition", + "path": "environmental_conditions.temperature_celsius" + } + }, + "dataset2": { + "temperature_celsius": { + "field": "condition", + "path": "custom.temp.path" + } + } + } + } + } + } + + config_path = tmp_path / "hierarchical_config.yaml" + with open(config_path, 'w') as f: + yaml.dump(config, f) + + resolver = DatasetFilterResolver(config_path) + + # Test property mapping resolution + mappings1 = resolver._get_property_mappings("BrentLab/test_repo", "dataset1") + assert "carbon_source" in mappings1 # From repo_level + assert "temperature_celsius" in mappings1 # From dataset-specific + assert mappings1["temperature_celsius"]["path"] == "environmental_conditions.temperature_celsius" + + mappings2 = resolver._get_property_mappings("BrentLab/test_repo", "dataset2") + assert "carbon_source" in mappings2 # From repo_level + assert "temperature_celsius" in mappings2 # Overridden by dataset-specific + assert mappings2["temperature_celsius"]["path"] == "custom.temp.path" + + +class TestRealDataCards: + """Integration tests with real datacards.""" + + def test_harbison_2004_glucose_filter(self, tmp_path): + """Test filtering harbison_2004 for glucose samples.""" + # Create config for glucose filtering + config = { + "filters": { + "carbon_source": ["D-glucose"] + }, + "dataset_mappings": { + "BrentLab/harbison_2004": { + "datasets": { + "harbison_2004": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source" + } + } + } + } + } + } + + config_path = tmp_path / "glucose_config.yaml" + with open(config_path, 'w') as f: + yaml.dump(config, f) + + resolver = DatasetFilterResolver(config_path) + results = resolver.resolve_filters( + repos=[("BrentLab/harbison_2004", "harbison_2004")], + mode="conditions" + ) + + assert "BrentLab/harbison_2004" in results + result = results["BrentLab/harbison_2004"] + + # Should be included + assert result["included"] is True + + # Should have matching field values + assert "matching_field_values" in result + + # Should have condition field with some matching values + if "condition" in result["matching_field_values"]: + matching = result["matching_field_values"]["condition"] + # YPD, HEAT, H2O2Hi, H2O2Lo, Acid, Alpha, BUT14, BUT90 all have D-glucose + expected_glucose_conditions = ["YPD", "HEAT", "H2O2Hi", "H2O2Lo", "Acid", "Alpha", "BUT14", "BUT90"] + for cond in expected_glucose_conditions: + assert cond in matching, f"{cond} should be in matching conditions" + + def test_harbison_2004_galactose_filter(self, tmp_path): + """Test filtering harbison_2004 for galactose samples.""" + config = { + "filters": { + "carbon_source": ["D-galactose"] + }, + "dataset_mappings": { + "BrentLab/harbison_2004": { + "datasets": { + "harbison_2004": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source" + } + } + } + } + } + } + + config_path = tmp_path / "galactose_config.yaml" + with open(config_path, 'w') as f: + yaml.dump(config, f) + + resolver = DatasetFilterResolver(config_path) + results = resolver.resolve_filters( + repos=[("BrentLab/harbison_2004", "harbison_2004")], + mode="conditions" + ) + + result = results["BrentLab/harbison_2004"] + assert result["included"] is True + + # GAL condition has D-galactose + if "condition" in result["matching_field_values"]: + matching = result["matching_field_values"]["condition"] + assert "GAL" in matching + + def test_harbison_2004_samples_mode(self, tmp_path): + """Test retrieving sample-level metadata.""" + config = { + "filters": { + "carbon_source": ["D-glucose"] + }, + "dataset_mappings": { + "BrentLab/harbison_2004": { + "datasets": { + "harbison_2004": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source" + } + } + } + } + } + } + + config_path = tmp_path / "samples_config.yaml" + with open(config_path, 'w') as f: + yaml.dump(config, f) + + resolver = DatasetFilterResolver(config_path) + results = resolver.resolve_filters( + repos=[("BrentLab/harbison_2004", "harbison_2004")], + mode="samples" + ) + + result = results["BrentLab/harbison_2004"] + assert result["included"] is True + assert "data" in result + + # Should have a DataFrame + df = result["data"] + assert isinstance(df, pd.DataFrame) + assert len(df) > 0 # Should have some samples + + def test_harbison_2004_full_data_mode(self, tmp_path): + """Test retrieving full data.""" + config = { + "filters": { + "carbon_source": ["D-glucose"] + }, + "dataset_mappings": { + "BrentLab/harbison_2004": { + "datasets": { + "harbison_2004": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source" + } + } + } + } + } + } + + config_path = tmp_path / "full_data_config.yaml" + with open(config_path, 'w') as f: + yaml.dump(config, f) + + resolver = DatasetFilterResolver(config_path) + results = resolver.resolve_filters( + repos=[("BrentLab/harbison_2004", "harbison_2004")], + mode="full_data" + ) + + result = results["BrentLab/harbison_2004"] + assert result["included"] is True + assert "data" in result + + # Should have a DataFrame + df = result["data"] + assert isinstance(df, pd.DataFrame) + assert len(df) > 0 # Should have data rows From a55234953fbdc480ef89ede4600f760245b14d4e Mon Sep 17 00:00:00 2001 From: chasem Date: Mon, 15 Dec 2025 11:21:31 -0600 Subject: [PATCH 43/49] intermediate --- docs/tutorials/filter_resolver_tutorial.ipynb | 1358 +++++++---------- docs/tutorials/my_comprehensive_filter.yaml | 22 + docs/tutorials/my_filter_config.yaml | 39 +- example_filter_config.yaml | 105 +- pyproject.toml | 1 + tfbpapi/datainfo/filter_resolver.py | 212 +-- tfbpapi/datainfo/metadata_manager.py | 20 +- tfbpapi/tests/datainfo/conftest.py | 1142 ++++++++++++++ .../tests/datainfo/test_filter_resolver.py | 491 +++--- 9 files changed, 2207 insertions(+), 1183 deletions(-) create mode 100644 docs/tutorials/my_comprehensive_filter.yaml diff --git a/docs/tutorials/filter_resolver_tutorial.ipynb b/docs/tutorials/filter_resolver_tutorial.ipynb index b52449a..16d95f2 100644 --- a/docs/tutorials/filter_resolver_tutorial.ipynb +++ b/docs/tutorials/filter_resolver_tutorial.ipynb @@ -10,22 +10,24 @@ "\n", "## Overview\n", "\n", - "The `DatasetFilterResolver` solves a common problem: filtering experimental data across multiple datasets where experimental conditions are structured differently. Instead of hardcoding dataset-specific logic, you specify:\n", + "The `DatasetFilterResolver` solves a common problem when working with multiple genomics datasets: **experimental conditions are structured differently across datasets**. Some datasets define conditions at the repository level (all samples share the same conditions), while others define conditions per-sample in field definitions.\n", "\n", - "1. **Filter criteria**: Which values are acceptable (e.g., carbon_source: [\"D-glucose\", \"D-galactose\"])\n", - "2. **Property mappings**: Where to find each property in each dataset (e.g., \"environmental_conditions.media.carbon_source\")\n", + "Instead of writing dataset-specific code, you specify:\n", + "\n", + "1. **Filter criteria**: Which values you want (e.g., `carbon_source: [\"D-glucose\", \"D-galactose\"]`)\n", + "2. **Property mappings**: Where to find each property in each dataset (e.g., repo-level vs field-level)\n", "\n", "## Key Features\n", "\n", "- **Two-level filtering**:\n", - " - Level 1: Repo/config level - excludes entire datasets that don't match\n", - " - Level 2: Field level - returns matching field values within datasets\n", + " - **Level 1** (Repo/Config): Excludes entire datasets that don't match\n", + " - **Level 2** (Field): Returns specific field values that match within datasets\n", "- **Three output modes**:\n", " - `conditions`: Just matching field values (no data retrieval)\n", " - `samples`: Sample-level metadata (one row per sample_id)\n", " - `full_data`: All measurements for matching samples\n", - "- **External configuration**: No hardcoded field specifications in codebase\n", - "- **Automatic path resolution**: Handles different nesting levels (repo vs field level)" + "- **External configuration**: No hardcoded dataset logic in your analysis code\n", + "- **Automatic path resolution**: Handles `experimental_conditions` prepending automatically" ] }, { @@ -37,244 +39,199 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chase/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" + ] + } + ], "source": [ "from pathlib import Path\n", "import yaml\n", "import pandas as pd\n", - "from tfbpapi.datainfo.filter_resolver import DatasetFilterResolver" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Example 1: Basic Filtering by Carbon Source\n", - "\n", - "Let's start with a simple example: finding all samples grown on D-glucose." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Understanding Field-Level vs Repo-Level Properties\n", - "\n", - "A key challenge with heterogeneous datasets is that experimental conditions can be located in different places:\n", - "\n", - "**Field-Level Definitions** (e.g., Harbison 2004):\n", - "- Properties are defined within a specific field's `definitions`\n", - "- Example: The `condition` field has definitions for \"YPD\", \"HEAT\", etc.\n", - "- Each definition contains `media`, `temperature_celsius`, etc.\n", - "- **Use the `field` parameter** to specify which field contains the definitions\n", - "\n", - "**Repo/Config-Level Properties** (e.g., Kemmeren 2014):\n", - "- Properties are defined at the top level of the datacard\n", - "- All samples in the dataset share the same conditions\n", - "- Found in `experimental_conditions` at repo or config level\n", - "- **No `field` parameter needed** - just specify the path\n", - "\n", - "Let's examine actual datacards to see these differences:" + "from tfbpapi.datainfo.filter_resolver import DatasetFilterResolver\n", + "from tfbpapi.datainfo import DataCard" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Configuration Implications\n", - "\n", - "Based on the actual datacard structures above:\n", - "\n", - "**For Harbison 2004** (field-level):\n", - "```yaml\n", - "\"BrentLab/harbison_2004\":\n", - " datasets:\n", - " harbison_2004:\n", - " carbon_source:\n", - " field: condition # Must specify which field\n", - " path: media.carbon_source # Path within each field value's definition\n", - "```\n", - "\n", - "**For Kemmeren 2014** (repo-level):\n", - "```yaml\n", - "\"BrentLab/kemmeren_2014\":\n", - " repo_level:\n", - " carbon_source:\n", - " path: environmental_conditions.media.carbon_source # No 'field' needed\n", - "```\n", + "## Understanding Dataset Structures\n", "\n", - "### When to Use Which Format\n", + "Before filtering, let's examine how experimental conditions are structured in two representative datasets.\n", "\n", - "**Use `field` specification when**:\n", - "- The property varies between samples (different values in the dataset)\n", - "- The property is defined in a field's `definitions` section\n", - "- You need to filter to specific field values (e.g., only \"YPD\" condition)\n", - "- Example: Harbison 2004 where each condition has its own media/temperature\n", + "### Repo-Level Conditions: Kemmeren 2014\n", "\n", - "**Use repo-level (no `field`) when**:\n", - "- The property is the same for all samples in the dataset\n", - "- The property is in the top-level `experimental_conditions`\n", - "- You're filtering entire datasets in/out based on shared properties\n", - "- Example: Kemmeren 2014 where all samples share the same growth conditions" + "All samples in this dataset share the same experimental conditions defined at the repository level." ] }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "================================================================================\n", - "KEMMEREN 2014 - Repo-Level Experimental Conditions\n", - "================================================================================\n", - "\n", - "Repo-level experimental_conditions exists: True\n", - "\n", + "Kemmeren 2014 - Repo-Level Experimental Conditions\n", + "============================================================\n", "Temperature: 30°C\n", - "Media name: synthetic_complete\n", + "Media: synthetic_complete\n", "Carbon source: ['D-glucose']\n", - "Concentrations: [2]%\n", "\n", - "No field-level definitions found\n", - "\n", - "================================================================================\n", - "Key Observation: Properties are at repo level, shared by all samples\n", - "================================================================================\n" + "All samples in this dataset share these conditions.\n" ] } ], "source": [ - "# Load Kemmeren 2014 (repo-level example)\n", - "from tfbpapi.datainfo import DataCard\n", - "\n", + "# Load Kemmeren 2014 datacard\n", "kemmeren_card = DataCard(\"BrentLab/kemmeren_2014\")\n", "\n", - "print(\"=\" * 80)\n", - "print(\"KEMMEREN 2014 - Repo-Level Experimental Conditions\")\n", - "print(\"=\" * 80)\n", - "\n", - "# Get repo-level experimental conditions (returns a dict)\n", + "# Get repo-level experimental conditions\n", "exp_conds = kemmeren_card.get_experimental_conditions(\"kemmeren_2014\")\n", - "print(f\"\\nRepo-level experimental_conditions exists: {bool(exp_conds)}\")\n", - "\n", - "if exp_conds:\n", - " # exp_conds is a dict, not a Pydantic model\n", - " print(f\"\\nTemperature: {exp_conds.get('temperature_celsius')}°C\")\n", - " \n", - " media = exp_conds.get('media', {})\n", - " print(f\"Media name: {media.get('name')}\")\n", - " \n", - " carbon_source = media.get('carbon_source', [])\n", - " if carbon_source:\n", - " compounds = [cs.get('compound') for cs in carbon_source]\n", - " concentrations = [cs.get('concentration_percent') for cs in carbon_source]\n", - " print(f\"Carbon source: {compounds}\")\n", - " print(f\"Concentrations: {concentrations}%\")\n", - "\n", - "# Check if there are field-level definitions\n", - "config = kemmeren_card.get_config(\"kemmeren_2014\")\n", - "has_field_defs = False\n", - "for feature in config.dataset_info.features:\n", - " if feature.definitions:\n", - " has_field_defs = True\n", - " print(f\"\\nField '{feature.name}' has definitions: {list(feature.definitions.keys())}\")\n", - "\n", - "if not has_field_defs:\n", - " print(\"\\nNo field-level definitions found\")\n", - "\n", - "print(\"\\n\" + \"=\" * 80)\n", - "print(\"Key Observation: Properties are at repo level, shared by all samples\")\n", - "print(\"=\" * 80)" + "\n", + "print(\"Kemmeren 2014 - Repo-Level Experimental Conditions\")\n", + "print(\"=\" * 60)\n", + "print(f\"Temperature: {exp_conds.get('temperature_celsius')}°C\")\n", + "print(f\"Media: {exp_conds.get('media', {}).get('name')}\")\n", + "\n", + "carbon_source = exp_conds.get('media', {}).get('carbon_source', [])\n", + "if carbon_source:\n", + " compounds = [cs.get('compound') for cs in carbon_source]\n", + " print(f\"Carbon source: {compounds}\")\n", + "\n", + "print(\"\\nAll samples in this dataset share these conditions.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Field-Level Conditions: Harbison 2004\n", + "\n", + "This dataset has different experimental conditions for different samples, defined in the `condition` field's definitions." ] }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "================================================================================\n", - "HARBISON 2004 - Field-Level Definitions\n", - "================================================================================\n", - "\n", "Repo-level experimental_conditions: {}\n", "\n", - "Field 'condition' has 14 definitions\n", - "Condition values: ['YPD', 'SM', 'RAPA', 'H2O2Hi', 'H2O2Lo', 'Acid', 'Alpha', 'BUT14', 'BUT90', 'Thi-', 'GAL', 'HEAT', 'Pi-', 'RAFF']\n", - "\n", - "Example definitions:\n", - "\n", - "YPD:\n", - " temperature_celsius: 30\n", - " media.name: YPD\n", - " media.carbon_source: ['D-glucose']\n", + "Harbison 2004 - Field-Level Definitions\n", + "============================================================\n", + "Number of condition definitions: 14\n", + "Condition names: ['YPD', 'SM', 'RAPA', 'H2O2Hi', 'H2O2Lo', 'Acid', 'Alpha', 'BUT14', 'BUT90', 'Thi-', 'GAL', 'HEAT', 'Pi-', 'RAFF']\n", "\n", - "HEAT:\n", - " temperature_celsius: None\n", - " media.name: YPD\n", - " media.carbon_source: ['D-glucose']\n", + "Example conditions:\n", + " YPD: ['D-glucose'], 30°C\n", + " GAL: ['D-galactose'], 30°C\n", + " HEAT: ['D-glucose'], varies°C\n", "\n", - "GAL:\n", - " temperature_celsius: 30\n", - " media.name: yeast_extract_peptone\n", - " media.carbon_source: ['D-galactose']\n", - "\n", - "================================================================================\n", - "Key Observation: Properties are in field definitions, NOT at repo level\n", - "================================================================================\n" + "Each sample can have a different condition value.\n" ] } ], "source": [ - "from tfbpapi.datainfo import DataCard\n", - "import json\n", - "\n", - "# Load Harbison 2004 (field-level example)\n", + "# Load Harbison 2004 datacard\n", "harbison_card = DataCard(\"BrentLab/harbison_2004\")\n", "\n", - "print(\"=\" * 80)\n", - "print(\"HARBISON 2004 - Field-Level Definitions\")\n", - "print(\"=\" * 80)\n", - "\n", - "# Check if there are repo-level experimental conditions\n", + "# Check for repo-level conditions\n", "exp_conds = harbison_card.get_experimental_conditions(\"harbison_2004\")\n", - "print(f\"\\nRepo-level experimental_conditions: {exp_conds}\")\n", + "print(f\"Repo-level experimental_conditions: {exp_conds}\")\n", "\n", - "# Get field definitions for the 'condition' field\n", + "# Get field-level definitions\n", "condition_defs = harbison_card.get_field_definitions(\"harbison_2004\", \"condition\")\n", - "print(f\"\\nField 'condition' has {len(condition_defs)} definitions\")\n", - "print(f\"Condition values: {list(condition_defs.keys())}\")\n", "\n", - "# Show a few example definitions\n", - "print(\"\\nExample definitions:\")\n", - "for cond_name in [\"YPD\", \"HEAT\", \"GAL\"]:\n", + "print(\"\\nHarbison 2004 - Field-Level Definitions\")\n", + "print(\"=\" * 60)\n", + "print(f\"Number of condition definitions: {len(condition_defs)}\")\n", + "print(f\"Condition names: {list(condition_defs.keys())}\")\n", + "\n", + "# Show examples\n", + "print(\"\\nExample conditions:\")\n", + "for cond_name in [\"YPD\", \"GAL\", \"HEAT\"]:\n", " if cond_name in condition_defs:\n", " definition = condition_defs[cond_name]\n", - " print(f\"\\n{cond_name}:\")\n", - " print(f\" temperature_celsius: {definition.get('temperature_celsius')}\")\n", " media = definition.get('media', {})\n", - " print(f\" media.name: {media.get('name')}\")\n", - " carbon_source = media.get('carbon_source', [])\n", - " if carbon_source:\n", - " compounds = [cs.get('compound') for cs in carbon_source]\n", - " print(f\" media.carbon_source: {compounds}\")\n", - "\n", - "print(\"\\n\" + \"=\" * 80)\n", - "print(\"Key Observation: Properties are in field definitions, NOT at repo level\")\n", - "print(\"=\" * 80)" + " carbon = media.get('carbon_source', [])\n", + " compounds = [cs.get('compound') for cs in carbon] if carbon else []\n", + " temp = definition.get('temperature_celsius', 'varies')\n", + " print(f\" {cond_name}: {compounds}, {temp}°C\")\n", + "\n", + "print(\"\\nEach sample can have a different condition value.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Configuration Format\n", + "\n", + "The filter configuration has two main sections:\n", + "\n", + "```yaml\n", + "# 1. Filter criteria - what values you want\n", + "filters:\n", + " carbon_source: [\"D-glucose\", \"D-galactose\"]\n", + " temperature_celsius: [30]\n", + "\n", + "# 2. Property mappings - where to find each property in each dataset\n", + "BrentLab/repo_name:\n", + " property1:\n", + " path: media.name # Repo-wide property\n", + " \n", + " dataset:\n", + " dataset_name:\n", + " property2:\n", + " path: temperature_celsius # Dataset-specific property\n", + " \n", + " property3:\n", + " field: condition # Field-level property\n", + " path: media.carbon_source\n", + "```\n", + "\n", + "### Three Types of Property Configurations\n", + "\n", + "1. **Repo-wide**: Property applies to ALL datasets in the repository\n", + " - Paths automatically get `experimental_conditions.` prepended\n", + " - Example: `path: media.name` → resolves to `experimental_conditions.media.name`\n", + "\n", + "2. **Dataset-specific**: Property applies only to a specific dataset (at config level)\n", + " - Also gets `experimental_conditions.` prepended\n", + " - Example: `path: temperature_celsius` → resolves to `experimental_conditions.temperature_celsius`\n", + "\n", + "3. **Field-level**: Property varies per sample, defined in field definitions\n", + " - Requires `field` parameter to specify which field\n", + " - Path is used directly on field definitions (NO prepending)\n", + " - Example: `field: condition, path: media.carbon_source` → looks in condition field definitions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 1: Basic Filtering by Carbon Source\n", + "\n", + "Let's filter for samples grown on D-glucose in Harbison 2004." ] }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -282,13 +239,12 @@ "output_type": "stream", "text": [ "Configuration:\n", - "dataset_mappings:\n", - " BrentLab/harbison_2004:\n", - " datasets:\n", - " harbison_2004:\n", - " carbon_source:\n", - " field: condition\n", - " path: media.carbon_source\n", + "BrentLab/harbison_2004:\n", + " dataset:\n", + " harbison_2004:\n", + " carbon_source:\n", + " field: condition\n", + " path: media.carbon_source\n", "filters:\n", " carbon_source:\n", " - D-glucose\n", @@ -297,32 +253,30 @@ } ], "source": [ - "# Create a filter configuration\n", + "# Create filter configuration\n", "glucose_config = {\n", " \"filters\": {\n", " \"carbon_source\": [\"D-glucose\"]\n", " },\n", - " \"dataset_mappings\": {\n", - " \"BrentLab/harbison_2004\": {\n", - " \"datasets\": {\n", - " \"harbison_2004\": {\n", - " \"carbon_source\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"media.carbon_source\"\n", - " }\n", + " \"BrentLab/harbison_2004\": {\n", + " \"dataset\": {\n", + " \"harbison_2004\": {\n", + " \"carbon_source\": {\n", + " \"field\": \"condition\",\n", + " \"path\": \"media.carbon_source\"\n", " }\n", " }\n", " }\n", " }\n", "}\n", "\n", - "# Save to temporary file\n", + "# Save to file\n", "config_path = Path(\"/tmp/glucose_filter.yaml\")\n", "with open(config_path, 'w') as f:\n", " yaml.dump(glucose_config, f)\n", "\n", "print(\"Configuration:\")\n", - "print(yaml.dump(glucose_config, default_flow_style=False))\n" + "print(yaml.dump(glucose_config, default_flow_style=False))" ] }, { @@ -331,66 +285,57 @@ "source": [ "### Mode 0: Conditions Only\n", "\n", - "Get just the matching field values without retrieving any data." + "Get just the matching field values without retrieving any data. This is the fastest mode for exploration." ] }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ + "Included: True\n", "\n", - "Results:\n", - "\n", - "BrentLab/harbison_2004:\n", - " Included: True\n", - " Matching conditions:\n", - " condition: ['YPD', 'RAPA', 'H2O2Hi', 'H2O2Lo', 'Acid', 'Alpha', 'BUT14', 'BUT90', 'HEAT']\n" + "Matching conditions:\n", + " condition: ['YPD', 'RAPA', 'H2O2Hi', 'H2O2Lo', 'Acid', 'Alpha', 'BUT14', 'BUT90', 'HEAT']\n" ] } ], "source": [ - "# Create resolver\n", + "# Create resolver and run filter\n", "resolver = DatasetFilterResolver(config_path)\n", "\n", - "# Get matching conditions\n", "results = resolver.resolve_filters(\n", " repos=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", " mode=\"conditions\"\n", ")\n", "\n", - "print(\"\\nResults:\")\n", - "for repo_id, result in results.items():\n", - " print(f\"\\n{repo_id}:\")\n", - " if result[\"included\"]:\n", - " print(f\" Included: True\")\n", - " print(f\" Matching conditions:\")\n", - " for field, values in result[\"matching_field_values\"].items():\n", - " print(f\" {field}: {values}\")\n", - " else:\n", - " print(f\" Included: False\")\n", - " print(f\" Reason: {result['reason']}\")" + "# Display results\n", + "result = results[\"BrentLab/harbison_2004\"]\n", + "print(f\"Included: {result['included']}\")\n", + "\n", + "if result['included']:\n", + " print(\"\\nMatching conditions:\")\n", + " for field, values in result['matching_field_values'].items():\n", + " print(f\" {field}: {values}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Understanding the Results\n", + "**Understanding the results:**\n", "\n", - "The `matching_field_values` shows which values of the `condition` field have D-glucose:\n", - "- YPD: Rich media with 2% glucose\n", + "The resolver found all condition field values where `media.carbon_source` contains D-glucose:\n", + "- YPD: Rich media with glucose\n", "- HEAT: Heat stress with glucose\n", "- H2O2Hi/H2O2Lo: Oxidative stress with glucose\n", - "- Acid: Acidic stress with glucose\n", - "- Alpha: Alpha factor arrest with glucose\n", - "- BUT14/BUT90: Butanol treatment with glucose\n", + "- Acid, Alpha, BUT14, BUT90, RAPA: Various stresses with glucose\n", "\n", - "Notice that GAL (galactose media) is NOT in the list - it was correctly filtered out." + "Notice GAL is NOT included - it uses galactose, not glucose." ] }, { @@ -399,42 +344,39 @@ "source": [ "## Example 2: Multiple Filter Criteria\n", "\n", - "Let's filter for samples with both D-glucose AND 30C temperature." + "Filter for samples with both D-glucose AND 30°C temperature." ] }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Conditions with D-glucose AND 30C:\n", + "Conditions with D-glucose AND 30°C:\n", "['YPD', 'RAPA', 'H2O2Hi', 'H2O2Lo', 'Acid', 'Alpha', 'BUT14', 'BUT90']\n" ] } ], "source": [ - "# Config with multiple filters\n", "multi_filter_config = {\n", " \"filters\": {\n", " \"carbon_source\": [\"D-glucose\"],\n", " \"temperature_celsius\": [30]\n", " },\n", - " \"dataset_mappings\": {\n", - " \"BrentLab/harbison_2004\": {\n", - " \"datasets\": {\n", - " \"harbison_2004\": {\n", - " \"carbon_source\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"media.carbon_source\"\n", - " },\n", - " \"temperature_celsius\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"temperature_celsius\"\n", - " }\n", + " \"BrentLab/harbison_2004\": {\n", + " \"dataset\": {\n", + " \"harbison_2004\": {\n", + " \"carbon_source\": {\n", + " \"field\": \"condition\",\n", + " \"path\": \"media.carbon_source\"\n", + " },\n", + " \"temperature_celsius\": {\n", + " \"field\": \"condition\",\n", + " \"path\": \"temperature_celsius\"\n", " }\n", " }\n", " }\n", @@ -451,40 +393,191 @@ " mode=\"conditions\"\n", ")\n", "\n", - "print(\"Conditions with D-glucose AND 30C:\")\n", "matching = results[\"BrentLab/harbison_2004\"][\"matching_field_values\"][\"condition\"]\n", - "print(matching)\n" + "print(\"Conditions with D-glucose AND 30°C:\")\n", + "print(matching)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Notice HEAT is now excluded - it has glucose but uses a temperature shift to 37°C, not 30°C.\n", + "\n", + "**Multiple filter criteria use AND logic** - all criteria must match." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 3: Repo-Level Filtering\n", + "\n", + "Filter Kemmeren 2014, which has repo-level experimental conditions (no field specification needed)." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Included: True\n", + "\n", + "No field-level filtering - entire dataset matches at repo level\n" + ] + } + ], + "source": [ + "repo_level_config = {\n", + " \"filters\": {\n", + " \"carbon_source\": [\"D-glucose\"],\n", + " \"temperature_celsius\": [30]\n", + " },\n", + " \"BrentLab/kemmeren_2014\": {\n", + " \"dataset\": {\n", + " \"kemmeren_2014\": {\n", + " \"carbon_source\": {\n", + " \"path\": \"media.carbon_source\" # No 'field' - repo-level property\n", + " },\n", + " \"temperature_celsius\": {\n", + " \"path\": \"temperature_celsius\"\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "config_path = Path(\"/tmp/repo_level_filter.yaml\")\n", + "with open(config_path, 'w') as f:\n", + " yaml.dump(repo_level_config, f)\n", + "\n", + "resolver = DatasetFilterResolver(config_path)\n", + "results = resolver.resolve_filters(\n", + " repos=[(\"BrentLab/kemmeren_2014\", \"kemmeren_2014\")],\n", + " mode=\"conditions\"\n", + ")\n", + "\n", + "result = results[\"BrentLab/kemmeren_2014\"]\n", + "print(f\"Included: {result['included']}\")\n", + "\n", + "if result['included']:\n", + " if result['matching_field_values']:\n", + " print(\"\\nMatching field values:\")\n", + " for field, values in result['matching_field_values'].items():\n", + " print(f\" {field}: {values}\")\n", + " else:\n", + " print(\"\\nNo field-level filtering - entire dataset matches at repo level\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Notice that HEAT is now excluded - it has glucose but is at 37C, not 30C. Multiple filter criteria use AND logic." + "**Key difference**: Repo-level filtering includes/excludes the entire dataset. Since kemmeren_2014 has D-glucose at 30°C at the repo level, ALL samples in the dataset match." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Example 3: Mode 1 - Sample-Level Metadata\n", + "## Example 4: Multiple Datasets\n", "\n", - "Retrieve actual sample metadata (one row per sample_id) for matching conditions." + "Filter across both harbison_2004 (field-level) and kemmeren_2014 (repo-level) in a single query." ] }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Retrieved 304 samples\n", + "Results across multiple datasets:\n", "\n", - "DataFrame shape: (304, 9)\n", + "BrentLab/harbison_2004:\n", + " Included: True\n", + " condition: ['YPD', 'RAPA', 'H2O2Hi']...\n", "\n", + "BrentLab/kemmeren_2014:\n", + " Included: True\n", + "\n" + ] + } + ], + "source": [ + "multi_dataset_config = {\n", + " \"filters\": {\n", + " \"carbon_source\": [\"D-glucose\"]\n", + " },\n", + " \"BrentLab/harbison_2004\": {\n", + " \"dataset\": {\n", + " \"harbison_2004\": {\n", + " \"carbon_source\": {\n", + " \"field\": \"condition\",\n", + " \"path\": \"media.carbon_source\"\n", + " }\n", + " }\n", + " }\n", + " },\n", + " \"BrentLab/kemmeren_2014\": {\n", + " \"dataset\": {\n", + " \"kemmeren_2014\": {\n", + " \"carbon_source\": {\n", + " \"path\": \"media.carbon_source\"\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "config_path = Path(\"/tmp/multi_dataset_filter.yaml\")\n", + "with open(config_path, 'w') as f:\n", + " yaml.dump(multi_dataset_config, f)\n", + "\n", + "resolver = DatasetFilterResolver(config_path)\n", + "results = resolver.resolve_filters(\n", + " repos=[\n", + " (\"BrentLab/harbison_2004\", \"harbison_2004\"),\n", + " (\"BrentLab/kemmeren_2014\", \"kemmeren_2014\")\n", + " ],\n", + " mode=\"conditions\"\n", + ")\n", + "\n", + "print(\"Results across multiple datasets:\\n\")\n", + "for repo_id, result in results.items():\n", + " print(f\"{repo_id}:\")\n", + " print(f\" Included: {result['included']}\")\n", + " if result['included'] and result.get('matching_field_values'):\n", + " for field, values in result['matching_field_values'].items():\n", + " print(f\" {field}: {values[:3]}...\") # Show first 3\n", + " print()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 5: Retrieving Sample Metadata (Mode 1)\n", + "\n", + "Once you've identified matching conditions, retrieve sample-level metadata." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Retrieved 310 samples\n", "Columns: ['sample_id', 'db_id', 'regulator_locus_tag', 'regulator_symbol', 'condition', 'target_locus_tag', 'target_symbol', 'effect', 'pvalue']\n", "\n", "First few rows:\n" @@ -545,7 +638,7 @@ "type": "float" } ], - "ref": "a9bc83e5-ec64-4c54-a0b5-b8efb910402a", + "ref": "b8896bc0-bb22-468e-9c3f-f8e1715f804c", "rows": [ [ "0", @@ -554,10 +647,10 @@ "YSC0017", "MATA1", "YPD", - "YAL023C", - "PMT2", - "0.74920592", - "0.83707059" + "YBL068W", + "PRS4", + "0.8599428", + "0.72834544" ], [ "1", @@ -566,10 +659,10 @@ "YAL051W", "OAF1", "YPD", - "YAL047C", - "SPC72", - "1.9661134", - "0.0076229595" + "YAL015C", + "NTG1", + "1.3079075", + "0.17689452" ], [ "2", @@ -578,10 +671,10 @@ "YBL005W", "PDR3", "YPD", - "YBR001C", - "NTH2", - "0.97462425", - "0.56597448" + "YAL005C", + "SSA1", + "0.76584559", + "0.92357641" ], [ "3", @@ -590,10 +683,10 @@ "YBL008W", "HIR1", "YPD", - "YBL037W", - "APL3", - "1.0555759", - "0.35839913" + "YAL030W", + "SNC1", + "0.92566636", + "0.69746121" ], [ "4", @@ -602,10 +695,10 @@ "YBL021C", "HAP3", "YPD", - "YAL001C", - "TFC3", - "0.84731661", - "0.81162232" + "YAR035W", + "YAT1", + "0.83719358", + "0.85707131" ] ], "shape": { @@ -651,10 +744,10 @@ " YSC0017\n", " MATA1\n", " YPD\n", - " YAL023C\n", - " PMT2\n", - " 0.749206\n", - " 0.837071\n", + " YBL068W\n", + " PRS4\n", + " 0.859943\n", + " 0.728345\n", " \n", " \n", " 1\n", @@ -663,10 +756,10 @@ " YAL051W\n", " OAF1\n", " YPD\n", - " YAL047C\n", - " SPC72\n", - " 1.966113\n", - " 0.007623\n", + " YAL015C\n", + " NTG1\n", + " 1.307908\n", + " 0.176895\n", " \n", " \n", " 2\n", @@ -675,10 +768,10 @@ " YBL005W\n", " PDR3\n", " YPD\n", - " YBR001C\n", - " NTH2\n", - " 0.974624\n", - " 0.565974\n", + " YAL005C\n", + " SSA1\n", + " 0.765846\n", + " 0.923576\n", " \n", " \n", " 3\n", @@ -687,10 +780,10 @@ " YBL008W\n", " HIR1\n", " YPD\n", - " YBL037W\n", - " APL3\n", - " 1.055576\n", - " 0.358399\n", + " YAL030W\n", + " SNC1\n", + " 0.925666\n", + " 0.697461\n", " \n", " \n", " 4\n", @@ -699,10 +792,10 @@ " YBL021C\n", " HAP3\n", " YPD\n", - " YAL001C\n", - " TFC3\n", - " 0.847317\n", - " 0.811622\n", + " YAR035W\n", + " YAT1\n", + " 0.837194\n", + " 0.857071\n", " \n", " \n", "\n", @@ -717,51 +810,73 @@ "4 5 4.0 YBL021C HAP3 YPD \n", "\n", " target_locus_tag target_symbol effect pvalue \n", - "0 YAL023C PMT2 0.749206 0.837071 \n", - "1 YAL047C SPC72 1.966113 0.007623 \n", - "2 YBR001C NTH2 0.974624 0.565974 \n", - "3 YBL037W APL3 1.055576 0.358399 \n", - "4 YAL001C TFC3 0.847317 0.811622 " + "0 YBL068W PRS4 0.859943 0.728345 \n", + "1 YAL015C NTG1 1.307908 0.176895 \n", + "2 YAL005C SSA1 0.765846 0.923576 \n", + "3 YAL030W SNC1 0.925666 0.697461 \n", + "4 YAR035W YAT1 0.837194 0.857071 " ] }, - "execution_count": 57, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "# Use same config but change mode to 'samples'\n", - "results_samples = resolver.resolve_filters(\n", + "# Reuse the glucose filter config\n", + "glucose_config = {\n", + " \"filters\": {\n", + " \"carbon_source\": [\"D-glucose\"]\n", + " },\n", + " \"BrentLab/harbison_2004\": {\n", + " \"dataset\": {\n", + " \"harbison_2004\": {\n", + " \"carbon_source\": {\n", + " \"field\": \"condition\",\n", + " \"path\": \"media.carbon_source\"\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "config_path = Path(\"/tmp/glucose_filter.yaml\")\n", + "with open(config_path, 'w') as f:\n", + " yaml.dump(glucose_config, f)\n", + "\n", + "resolver = DatasetFilterResolver(config_path)\n", + "\n", + "# Use samples mode\n", + "results = resolver.resolve_filters(\n", " repos=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", " mode=\"samples\"\n", ")\n", "\n", - "# Get the DataFrame\n", - "df_samples = results_samples[\"BrentLab/harbison_2004\"][\"data\"]\n", + "df_samples = results[\"BrentLab/harbison_2004\"][\"data\"]\n", "\n", "print(f\"Retrieved {len(df_samples)} samples\")\n", - "print(f\"\\nDataFrame shape: {df_samples.shape}\")\n", - "print(f\"\\nColumns: {list(df_samples.columns)}\")\n", - "print(f\"\\nFirst few rows:\")\n", + "print(f\"Columns: {list(df_samples.columns)}\")\n", + "print(\"\\nFirst few rows:\")\n", "df_samples.head()" ] }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Condition distribution:\n", + "Condition distribution in retrieved samples:\n", "condition\n", "YPD 204\n", "H2O2Hi 39\n", "H2O2Lo 28\n", "RAPA 14\n", "BUT14 8\n", + "HEAT 6\n", "Alpha 5\n", "BUT90 4\n", "Acid 2\n", @@ -770,8 +885,8 @@ } ], "source": [ - "# Check the distribution of conditions in the retrieved samples\n", - "print(\"Condition distribution:\")\n", + "# Analyze condition distribution\n", + "print(\"Condition distribution in retrieved samples:\")\n", "print(df_samples['condition'].value_counts())" ] }, @@ -779,25 +894,22 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Example 4: Mode 2 - Full Data\n", + "## Example 6: Retrieving Full Data (Mode 2)\n", "\n", - "Retrieve all measurements for matching samples (many rows per sample_id)." + "Retrieve all measurements for matching samples (many rows per sample)." ] }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Retrieved 1,892,704 rows (measurements)\n", - "\n", - "DataFrame shape: (1892704, 9)\n", - "\n", - "Columns: ['sample_id', 'db_id', 'regulator_locus_tag', 'regulator_symbol', 'condition', 'target_locus_tag', 'target_symbol', 'effect', 'pvalue']\n", + "Retrieved 1,930,060 rows (measurements)\n", + "DataFrame shape: (1930060, 9)\n", "\n", "First few rows:\n" ] @@ -857,7 +969,7 @@ "type": "float" } ], - "ref": "a828c111-7829-4bcd-a77c-ff5dbc95ba4a", + "ref": "94bbe0c4-757e-406b-aef0-b82efd0905fe", "rows": [ [ "0", @@ -1036,51 +1148,49 @@ "4 YAL005C SSA1 NaN NaN " ] }, - "execution_count": 59, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "# Use same config but change mode to 'full_data'\n", + "# Use full_data mode\n", "results_full = resolver.resolve_filters(\n", " repos=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", " mode=\"full_data\"\n", ")\n", "\n", - "# Get the DataFrame\n", "df_full = results_full[\"BrentLab/harbison_2004\"][\"data\"]\n", "\n", "print(f\"Retrieved {len(df_full):,} rows (measurements)\")\n", - "print(f\"\\nDataFrame shape: {df_full.shape}\")\n", - "print(f\"\\nColumns: {list(df_full.columns)}\")\n", - "print(f\"\\nFirst few rows:\")\n", + "print(f\"DataFrame shape: {df_full.shape}\")\n", + "print(\"\\nFirst few rows:\")\n", "df_full.head()" ] }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Sample-level metadata: 304 samples\n", - "Full data: 1,892,704 measurements\n", + "Samples mode: 310 samples\n", + "Full data mode: 1,930,060 measurements\n", "Average measurements per sample: 6,226\n" ] } ], "source": [ - "# Compare to samples mode\n", - "n_samples = df_samples['sample_id'].nunique() if 'sample_id' in df_samples.columns else len(df_samples)\n", + "# Compare modes\n", + "n_samples = len(df_samples)\n", "n_measurements = len(df_full)\n", "measurements_per_sample = n_measurements / n_samples if n_samples > 0 else 0\n", "\n", - "print(f\"Sample-level metadata: {n_samples} samples\")\n", - "print(f\"Full data: {n_measurements:,} measurements\")\n", + "print(f\"Samples mode: {n_samples} samples\")\n", + "print(f\"Full data mode: {n_measurements:,} measurements\")\n", "print(f\"Average measurements per sample: {measurements_per_sample:,.0f}\")" ] }, @@ -1088,295 +1198,91 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Example 5: Filtering Across Multiple Datasets\n", + "## Example 7: Using Repo-Wide Properties\n", "\n", - "The real power comes from filtering across multiple datasets with different structures." + "For repositories where a property applies to ALL datasets, use repo-wide configuration." ] }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Results across multiple datasets:\n", - "\n", - "BrentLab/harbison_2004:\n", - " Included: True\n", - " condition: ['YPD', 'RAPA', 'H2O2Hi', 'H2O2Lo', 'Acid', 'Alpha', 'BUT14', 'BUT90', 'GAL', 'HEAT']\n", - "\n", + "Configuration with repo-wide property:\n", "BrentLab/kemmeren_2014:\n", - " Included: True\n", - " No field-level conditions (all samples match at repo level)\n", + " dataset:\n", + " kemmeren_2014:\n", + " carbon_source:\n", + " path: media.carbon_source\n", + " temperature_celsius:\n", + " path: temperature_celsius\n", + "filters:\n", + " temperature_celsius:\n", + " - 30\n", "\n" ] } ], "source": [ - "# Config for multiple datasets\n", - "multi_dataset_config = {\n", + "repo_wide_config = {\n", " \"filters\": {\n", - " \"carbon_source\": [\"D-glucose\", \"D-galactose\"]\n", + " \"temperature_celsius\": [30]\n", " },\n", - " \"dataset_mappings\": {\n", - " \"BrentLab/harbison_2004\": {\n", - " \"datasets\": {\n", - " \"harbison_2004\": {\n", - " \"carbon_source\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"media.carbon_source\"\n", - " }\n", - " }\n", - " }\n", + " \"BrentLab/kemmeren_2014\": {\n", + " \"temperature_celsius\": { # Repo-wide property (no dataset key)\n", + " \"path\": \"temperature_celsius\"\n", " },\n", - " \"BrentLab/kemmeren_2014\": {\n", - " \"repo_level\": {\n", - " \"carbon_source\": {\n", - " \"path\": \"environmental_conditions.media.carbon_source\"\n", + " \"dataset\": {\n", + " \"kemmeren_2014\": {\n", + " \"carbon_source\": { # Dataset-specific property\n", + " \"path\": \"media.carbon_source\"\n", " }\n", " }\n", " }\n", " }\n", "}\n", "\n", - "config_path = Path(\"/tmp/multi_dataset_filter.yaml\")\n", - "with open(config_path, 'w') as f:\n", - " yaml.dump(multi_dataset_config, f)\n", - "\n", - "resolver = DatasetFilterResolver(config_path)\n", - "\n", - "# Filter multiple datasets\n", - "results = resolver.resolve_filters(\n", - " repos=[\n", - " (\"BrentLab/harbison_2004\", \"harbison_2004\"),\n", - " (\"BrentLab/kemmeren_2014\", \"kemmeren_2014\")\n", - " ],\n", - " mode=\"conditions\"\n", - ")\n", - "\n", - "print(\"Results across multiple datasets:\\n\")\n", - "for repo_id, result in results.items():\n", - " print(f\"{repo_id}:\")\n", - " if result[\"included\"]:\n", - " print(f\" Included: True\")\n", - " if \"matching_field_values\" in result and result[\"matching_field_values\"]:\n", - " for field, values in result[\"matching_field_values\"].items():\n", - " print(f\" {field}: {values}\")\n", - " else:\n", - " print(\" No field-level conditions (all samples match at repo level)\")\n", - " else:\n", - " print(f\" Included: False\")\n", - " print(f\" Reason: {result['reason']}\")\n", - " print()\n" + "print(\"Configuration with repo-wide property:\")\n", + "print(yaml.dump(repo_wide_config, default_flow_style=False))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Example 6: Galactose-Only Filter\n", - "\n", - "Let's filter for just galactose to see dataset exclusion in action." - ] - }, - { - "cell_type": "code", - "execution_count": 62, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Galactose-only filter results:\n", - "Included: True\n", - "Matching conditions: ['GAL']\n" - ] - } - ], - "source": [ - "galactose_config = {\n", - " \"filters\": {\n", - " \"carbon_source\": [\"D-galactose\"]\n", - " },\n", - " \"dataset_mappings\": {\n", - " \"BrentLab/harbison_2004\": {\n", - " \"datasets\": {\n", - " \"harbison_2004\": {\n", - " \"carbon_source\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"media.carbon_source\"\n", - " }\n", - " }\n", - " }\n", - " }\n", - " }\n", - "}\n", - "\n", - "config_path = Path(\"/tmp/galactose_filter.yaml\")\n", - "with open(config_path, 'w') as f:\n", - " yaml.dump(galactose_config, f)\n", - "\n", - "resolver = DatasetFilterResolver(config_path)\n", - "results = resolver.resolve_filters(\n", - " repos=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", - " mode=\"conditions\"\n", - ")\n", - "\n", - "print(\"Galactose-only filter results:\")\n", - "result = results[\"BrentLab/harbison_2004\"]\n", - "print(f\"Included: {result['included']}\")\n", - "if result['included']:\n", - " matching = result[\"matching_field_values\"][\"condition\"]\n", - " print(f\"Matching conditions: {matching}\")\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Only the GAL condition should match - it's the only one with galactose as the carbon source." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## How Path Resolution Works\n", - "\n", - "The `DatasetFilterResolver` uses a two-phase approach to handle heterogeneous dataset structures:\n", - "\n", - "### Phase 1: Property Location (Field vs Repo Level)\n", - "\n", - "**Field-Level Resolution** (when `field` is specified):\n", - "```python\n", - "# Configuration\n", - "carbon_source:\n", - " field: condition # Look in the 'condition' field\n", - " path: media.carbon_source # Path within each field value's definition\n", - "```\n", - "\n", - "The resolver:\n", - "1. Gets all definitions for the `condition` field\n", - "2. For each field value (YPD, HEAT, GAL, etc.), extracts the value at `media.carbon_source`\n", - "3. Returns field values where the extracted value matches the filter\n", - "\n", - "**Repo-Level Resolution** (no `field` specified):\n", - "```python\n", - "# Configuration\n", - "carbon_source:\n", - " path: environmental_conditions.media.carbon_source # Top-level path\n", - "```\n", - "\n", - "The resolver:\n", - "1. Looks at the top-level `experimental_conditions` in the datacard\n", - "2. Extracts the value at the specified path\n", - "3. Includes/excludes the entire dataset based on the match\n", - "\n", - "### Phase 2: Path Navigation\n", - "\n", - "Once the resolver knows WHERE to look (field vs repo level), it navigates to the property using dot notation.\n", - "\n", - "**Automatic Fallback for Nested Paths**:\n", + "In this example:\n", + "- `temperature_celsius` is defined at repo level - applies to ALL datasets in BrentLab/kemmeren_2014\n", + "- `carbon_source` is defined at dataset level - applies only to the kemmeren_2014 dataset\n", "\n", - "The resolver tries multiple path variants to handle different nesting depths:\n", - "\n", - "1. **Try full path first**: `environmental_conditions.media.carbon_source`\n", - "2. **If not found, try without prefix**: `media.carbon_source`\n", - "\n", - "This allows configs to work with both:\n", - "- Repo-level: `experimental_conditions.media.carbon_source` (finds it at step 1)\n", - "- Field-level: `media.carbon_source` (finds it at step 2)\n", - "\n", - "**Example**: Harbison 2004 field definitions don't have an `environmental_conditions` wrapper, so:\n", - "- Path specified: `media.carbon_source`\n", - "- Directly finds `media` in the field definition\n", - "- No fallback needed\n", - "\n", - "### Compound Extraction\n", - "\n", - "For carbon source and similar properties, the resolver handles list-of-dict format:\n", - "\n", - "```yaml\n", - "carbon_source:\n", - " - compound: D-glucose\n", - " concentration_percent: 2\n", - " - compound: D-galactose \n", - " concentration_percent: 1\n", - "```\n", - "\n", - "Automatically extracts to: `[\"D-glucose\", \"D-galactose\"]`\n", - "\n", - "### Why This Design?\n", - "\n", - "This two-phase approach (field specification + path navigation) allows:\n", - "\n", - "1. **No hardcoded field names** in the codebase\n", - "2. **Explicit configuration** - clear which field to examine \n", - "3. **Flexibility** - works with any field name (`condition`, `treatment`, `strain`, etc.)\n", - "4. **Automatic nesting handling** - paths work at different hierarchy levels\n", - "5. **Heterogeneous datasets** - each dataset can specify its own structure\n", - "\n", - "### Configuration Debugging Tips\n", - "\n", - "If your filter isn't working:\n", - "\n", - "1. **Check the datacard structure** - is the property at field or repo level?\n", - "2. **Verify the field name** - does the field exist? Is the name correct?\n", - "3. **Test the path** - use `DataCard.get_field_definitions()` to inspect\n", - "4. **Try simpler paths** - remove `environmental_conditions.` if not needed\n", - "5. **Use `conditions` mode first** - see which values match before retrieving data" + "This is useful when a repository has multiple datasets that share some common properties." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Practical Example: Creating a Filter Config File\n", + "## Creating a Reusable Configuration File\n", "\n", - "Here's a complete example of a reusable filter configuration file." + "For production use, save your configuration to a YAML file." ] }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Saved configuration to my_filter_config.yaml\n", + "Saved configuration to my_comprehensive_filter.yaml\n", "\n", - "Configuration contents:\n", - "dataset_mappings:\n", - " BrentLab/harbison_2004:\n", - " datasets:\n", - " harbison_2004:\n", - " carbon_source:\n", - " field: condition\n", - " path: media.carbon_source\n", - " temperature_celsius:\n", - " field: condition\n", - " path: temperature_celsius\n", - " BrentLab/kemmeren_2014:\n", - " repo_level:\n", - " carbon_source:\n", - " path: environmental_conditions.media.carbon_source\n", - " temperature_celsius:\n", - " path: environmental_conditions.temperature_celsius\n", - "filters:\n", - " carbon_source:\n", - " - D-glucose\n", - " - D-galactose\n", - " - D-raffinose\n", - " temperature_celsius:\n", - " - 30\n", - " - 37\n", - "\n" + "You can now use this config file in your analysis:\n", + " resolver = DatasetFilterResolver('my_comprehensive_filter.yaml')\n" ] } ], @@ -1384,189 +1290,45 @@ "# Create a comprehensive filter config\n", "comprehensive_config = {\n", " \"filters\": {\n", - " \"carbon_source\": [\"D-glucose\", \"D-galactose\", \"D-raffinose\"],\n", - " \"temperature_celsius\": [30, 37]\n", + " \"carbon_source\": [\"D-glucose\", \"D-galactose\"],\n", + " \"temperature_celsius\": [30]\n", " },\n", - " \"dataset_mappings\": {\n", - " \"BrentLab/harbison_2004\": {\n", - " \"datasets\": {\n", - " \"harbison_2004\": {\n", - " \"carbon_source\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"media.carbon_source\"\n", - " },\n", - " \"temperature_celsius\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"temperature_celsius\"\n", - " }\n", - " }\n", - " }\n", - " },\n", - " \"BrentLab/kemmeren_2014\": {\n", - " \"repo_level\": {\n", + " \"BrentLab/harbison_2004\": {\n", + " \"dataset\": {\n", + " \"harbison_2004\": {\n", " \"carbon_source\": {\n", - " \"path\": \"environmental_conditions.media.carbon_source\"\n", + " \"field\": \"condition\",\n", + " \"path\": \"media.carbon_source\"\n", " },\n", " \"temperature_celsius\": {\n", - " \"path\": \"environmental_conditions.temperature_celsius\"\n", + " \"field\": \"condition\",\n", + " \"path\": \"temperature_celsius\"\n", " }\n", " }\n", " }\n", - " }\n", - "}\n", - "\n", - "# Save to a persistent location\n", - "config_file = Path(\"my_filter_config.yaml\")\n", - "with open(config_file, 'w') as f:\n", - " yaml.dump(comprehensive_config, f)\n", - "\n", - "print(f\"Saved configuration to {config_file}\")\n", - "print(\"\\nConfiguration contents:\")\n", - "print(yaml.dump(comprehensive_config, default_flow_style=False))\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Simplifying Single-Dataset Repositories\n", - "\n", - "For repositories with a single dataset or when all datasets share the same structure, you can use `repo_level` for brevity:\n", - "\n", - "```yaml\n", - "dataset_mappings:\n", - " \"BrentLab/kemmeren_2014\":\n", - " repo_level:\n", - " carbon_source:\n", - " path: environmental_conditions.media.carbon_source\n", - " temperature_celsius:\n", - " path: environmental_conditions.temperature_celsius\n", - "```\n", - "\n", - "This applies the mappings to all datasets in the repo. For kemmeren_2014 (which has repo-level experimental_conditions), this is more concise than explicitly listing each dataset." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Advanced: Hierarchical Configuration for Multi-Dataset Repositories\n", - "\n", - "When a repository contains multiple datasets, you can use hierarchical configuration to:\n", - "1. Define repo-level mappings that apply to all datasets\n", - "2. Override or extend with dataset-specific mappings\n", - "\n", - "This is especially useful when datasets share common properties but have some differences." - ] - }, - { - "cell_type": "code", - "execution_count": 64, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hierarchical configuration:\n", - "dataset_mappings:\n", - " BrentLab/multi_dataset_repo:\n", - " datasets:\n", - " dataset1:\n", - " temperature_celsius:\n", - " path: environmental_conditions.temperature_celsius\n", - " dataset2:\n", - " temperature_celsius:\n", - " path: custom.temp.location\n", - " dataset3:\n", - " carbon_source:\n", - " path: media.carbon\n", - " temperature_celsius:\n", - " path: temp\n", - " repo_level:\n", - " carbon_source:\n", - " path: environmental_conditions.media.carbon_source\n", - "filters:\n", - " carbon_source:\n", - " - D-glucose\n", - " temperature_celsius:\n", - " - 30\n", - "\n" - ] - } - ], - "source": [ - "# Hierarchical configuration example\n", - "hierarchical_config = {\n", - " \"filters\": {\n", - " \"carbon_source\": [\"D-glucose\"],\n", - " \"temperature_celsius\": [30]\n", " },\n", - " \"dataset_mappings\": {\n", - " # Repository with multiple datasets\n", - " \"BrentLab/multi_dataset_repo\": {\n", - " # Repo-level mappings apply to ALL datasets in this repo\n", - " \"repo_level\": {\n", + " \"BrentLab/kemmeren_2014\": {\n", + " \"dataset\": {\n", + " \"kemmeren_2014\": {\n", " \"carbon_source\": {\n", - " \"path\": \"environmental_conditions.media.carbon_source\"\n", - " }\n", - " },\n", - " # Dataset-specific overrides\n", - " \"datasets\": {\n", - " \"dataset1\": {\n", - " # Inherits carbon_source from repo_level\n", - " # Adds temperature mapping for this specific dataset\n", - " \"temperature_celsius\": {\n", - " \"path\": \"environmental_conditions.temperature_celsius\"\n", - " }\n", - " },\n", - " \"dataset2\": {\n", - " # Inherits carbon_source from repo_level \n", - " # Uses different temperature path\n", - " \"temperature_celsius\": {\n", - " \"path\": \"custom.temp.location\"\n", - " }\n", + " \"path\": \"media.carbon_source\"\n", " },\n", - " \"dataset3\": {\n", - " # Completely overrides carbon_source for this dataset\n", - " \"carbon_source\": {\n", - " \"path\": \"media.carbon\" # Different structure\n", - " },\n", - " \"temperature_celsius\": {\n", - " \"path\": \"temp\" # Top-level property\n", - " }\n", + " \"temperature_celsius\": {\n", + " \"path\": \"temperature_celsius\"\n", " }\n", " }\n", " }\n", " }\n", "}\n", "\n", - "print(\"Hierarchical configuration:\")\n", - "print(yaml.dump(hierarchical_config, default_flow_style=False))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Key Benefits of Hierarchical Configuration\n", - "\n", - "1. **DRY Principle**: Define common mappings once at repo level\n", - "2. **Flexibility**: Override for specific datasets that differ\n", - "3. **Maintainability**: Update repo-level mappings to affect all datasets at once\n", - "4. **Clarity**: Explicitly shows which datasets inherit vs override\n", - "\n", - "### How Merging Works\n", - "\n", - "For each dataset, the resolver:\n", - "1. Starts with repo-level mappings\n", - "2. Overlays dataset-specific mappings (these take precedence)\n", - "3. Returns the merged mapping for that dataset\n", + "# Save to file\n", + "config_file = Path(\"my_comprehensive_filter.yaml\")\n", + "with open(config_file, 'w') as f:\n", + " yaml.dump(comprehensive_config, f)\n", "\n", - "In the example above:\n", - "- `dataset1` gets `carbon_source` from repo_level + its own `temperature_celsius`\n", - "- `dataset2` gets `carbon_source` from repo_level + its own (different) `temperature_celsius`\n", - "- `dataset3` overrides both properties completely" + "print(f\"Saved configuration to {config_file}\")\n", + "print(\"\\nYou can now use this config file in your analysis:\")\n", + "print(f\" resolver = DatasetFilterResolver('{config_file}')\")" ] }, { @@ -1575,105 +1337,69 @@ "source": [ "## Summary\n", "\n", - "The `DatasetFilterResolver` provides:\n", - "\n", - "1. **Flexible filtering** across heterogeneous datasets\n", - "2. **External configuration** - no code changes needed for new filters\n", - "3. **Two-level filtering** - dataset exclusion and field-level matching\n", - "4. **Three output modes** - from lightweight (conditions only) to full data\n", - "5. **Field-aware resolution** - explicit specification of where properties are located\n", - "6. **Automatic path resolution** - handles different nesting levels gracefully\n", - "\n", - "### When to Use Each Mode\n", - "\n", - "- **`conditions`**: Quick exploration - which datasets have matching samples? Which field values match?\n", - "- **`samples`**: Sample metadata analysis - how many samples match? What are their properties?\n", - "- **`full_data`**: Full analysis - retrieve all measurements for downstream processing\n", + "### Key Concepts\n", "\n", - "### Best Practices for Configuration\n", + "1. **Configuration Structure**:\n", + " - `filters`: What values you want\n", + " - Repository mappings: Where to find properties in each dataset\n", "\n", - "#### 1. Field Specification\n", - "- **Always specify `field`** when the property is in field-level definitions\n", - "- **Never specify `field`** when the property is at repo/config level\n", - "- When in doubt, check the datacard: if the property is in a field's `definitions`, use `field`\n", + "2. **Three Property Types**:\n", + " - **Repo-wide**: Applies to all datasets (paths get `experimental_conditions.` prepended)\n", + " - **Dataset-specific**: Applies to one dataset (paths get `experimental_conditions.` prepended)\n", + " - **Field-level**: Per-sample variation (requires `field`, no prepending)\n", "\n", - "#### 2. Property Paths\n", - "- Use the simplest path that works (avoid unnecessary nesting)\n", - "- For field-level properties, typically: `media.carbon_source` (not `environmental_conditions.media.carbon_source`)\n", - "- For repo-level properties, include full path: `environmental_conditions.media.carbon_source`\n", + "3. **Three Output Modes**:\n", + " - `conditions`: Fast exploration, no data download\n", + " - `samples`: Sample metadata, one row per sample\n", + " - `full_data`: All measurements, many rows per sample\n", "\n", - "#### 3. Hierarchical Configuration\n", - "- Use `repo_level` for properties shared across all datasets in a repository\n", - "- Use `datasets` to override or extend for specific datasets\n", - "- This keeps configuration DRY and maintainable\n", + "### Best Practices\n", "\n", - "#### 4. Filter Development Workflow\n", - "1. **Start with `conditions` mode** to verify your filters work correctly\n", - "2. **Check the results** - do the field values make sense?\n", - "3. **Use `samples` mode** to understand sample counts before downloading full data\n", - "4. **Only use `full_data`** when you're ready to retrieve the actual measurements\n", - "\n", - "#### 5. Configuration Management\n", - "- Create reusable YAML configs for common filter scenarios\n", - "- Document your filter configs with comments explaining the scientific rationale\n", - "- Version control your configs alongside your analysis code\n", - "- Test configs with `conditions` mode before using in production pipelines\n", - "\n", - "#### 6. Debugging\n", - "- If a dataset shows `included: False`, check the `reason` field\n", - "- Use `DataCard.get_field_definitions()` to inspect field structure\n", - "- Use `DataCard.get_experimental_conditions()` to inspect repo-level conditions\n", - "- Start simple (one filter, one dataset) then expand\n", + "1. **Start with `conditions` mode** to verify your filters work\n", + "2. **Use `field` parameter** when properties vary between samples\n", + "3. **Omit `field` parameter** when properties are at repo/config level\n", + "4. **Keep paths simple** - use minimal nesting that works\n", + "5. **Version control your configs** - they document your filter criteria\n", "\n", "### Common Patterns\n", "\n", - "**Pattern 1: Single property, field-level**\n", + "**Field-level filtering** (per-sample variation):\n", "```yaml\n", - "filters:\n", - " carbon_source: [\"D-glucose\"]\n", - "dataset_mappings:\n", - " \"BrentLab/harbison_2004\":\n", - " datasets:\n", - " harbison_2004:\n", - " carbon_source:\n", - " field: condition\n", - " path: media.carbon_source\n", - "```\n", - "\n", - "**Pattern 2: Multiple properties, repo-level**\n", - "```yaml\n", - "filters:\n", - " carbon_source: [\"D-glucose\"]\n", - " temperature_celsius: [30]\n", - "dataset_mappings:\n", - " \"BrentLab/kemmeren_2014\":\n", - " repo_level:\n", + "BrentLab/harbison_2004:\n", + " dataset:\n", + " harbison_2004:\n", " carbon_source:\n", - " path: environmental_conditions.media.carbon_source\n", - " temperature_celsius:\n", - " path: environmental_conditions.temperature_celsius\n", + " field: condition\n", + " path: media.carbon_source\n", "```\n", "\n", - "**Pattern 3: Mixed (repo-level + dataset overrides)**\n", + "**Repo-level filtering** (all samples share conditions):\n", "```yaml\n", - "dataset_mappings:\n", - " \"BrentLab/multi_dataset_repo\":\n", - " repo_level:\n", + "BrentLab/kemmeren_2014:\n", + " dataset:\n", + " kemmeren_2014:\n", " carbon_source:\n", - " path: environmental_conditions.media.carbon_source\n", - " datasets:\n", - " dataset1:\n", - " temperature_celsius:\n", - " field: condition\n", - " path: temperature_celsius\n", + " path: media.carbon_source\n", "```\n", "\n", - "### Next Steps\n", + "**Repo-wide property** (applies to all datasets in repo):\n", + "```yaml\n", + "BrentLab/kemmeren_2014:\n", + " temperature_celsius:\n", + " path: temperature_celsius\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Next Steps\n", "\n", - "- Explore the `example_filter_config.yaml` file for more examples\n", - "- Try filtering across multiple datasets with different structures\n", - "- Integrate with downstream analysis pipelines using the exported DataFrames\n", - "- Consider creating a library of reusable filter configs for your common use cases" + "- See [my_filter_config.yaml](my_filter_config.yaml) for a working example\n", + "- Explore the [experimental_conditions_tutorial.ipynb](experimental_conditions_tutorial.ipynb) to understand datacard structures\n", + "- Read the [DatasetFilterResolver API documentation](../../api/datainfo/filter_resolver.md) for detailed reference\n", + "- Try filtering across additional datasets in the BrentLab collection" ] } ], diff --git a/docs/tutorials/my_comprehensive_filter.yaml b/docs/tutorials/my_comprehensive_filter.yaml new file mode 100644 index 0000000..6bd4668 --- /dev/null +++ b/docs/tutorials/my_comprehensive_filter.yaml @@ -0,0 +1,22 @@ +BrentLab/harbison_2004: + dataset: + harbison_2004: + carbon_source: + field: condition + path: media.carbon_source + temperature_celsius: + field: condition + path: temperature_celsius +BrentLab/kemmeren_2014: + dataset: + kemmeren_2014: + carbon_source: + path: media.carbon_source + temperature_celsius: + path: temperature_celsius +filters: + carbon_source: + - D-glucose + - D-galactose + temperature_celsius: + - 30 diff --git a/docs/tutorials/my_filter_config.yaml b/docs/tutorials/my_filter_config.yaml index c9b4f7e..8c10ac6 100644 --- a/docs/tutorials/my_filter_config.yaml +++ b/docs/tutorials/my_filter_config.yaml @@ -1,24 +1,19 @@ -dataset_mappings: - BrentLab/harbison_2004: - datasets: - harbison_2004: - carbon_source: - field: condition - path: media.carbon_source - temperature_celsius: - field: condition - path: temperature_celsius - BrentLab/kemmeren_2014: - repo_level: - carbon_source: - path: environmental_conditions.media.carbon_source - temperature_celsius: - path: environmental_conditions.temperature_celsius filters: carbon_source: - - D-glucose - - D-galactose - - D-raffinose - temperature_celsius: - - 30 - - 37 + - "D-glucose" + - "D-galactose" + +BrentLab/harbison_2004: + dataset: + harbison_2004: + carbon_source: + field: condition + path: media.carbon_source + +BrentLab/kemmeren_2014: + temperature: + path: temperature_celsius + dataset: + kemmeren_2014: + carbon_source: + path: media.carbon_source diff --git a/example_filter_config.yaml b/example_filter_config.yaml index bae0ac7..a2c66ca 100644 --- a/example_filter_config.yaml +++ b/example_filter_config.yaml @@ -3,9 +3,7 @@ # This file demonstrates how to specify filters and dataset-specific # property mappings for heterogeneous datasets. # -# Configuration supports two formats: -# 1. Legacy format: Direct mappings at repo level (backward compatible) -# 2. Hierarchical format: Repo-level + dataset-specific mappings +# New format: repositories as top-level keys (not nested under dataset_mappings) # # Usage: # from tfbpapi.datainfo.filter_resolver import DatasetFilterResolver @@ -19,70 +17,59 @@ filters: carbon_source: - "D-glucose" - "D-galactose" - - "D-raffinose" temperature_celsius: - 30 - 37 -# Dataset mappings specify where to find each property in each dataset's DataCard -# The 'path' uses dot notation to navigate nested structures -# For field-level definitions, use 'field' to specify which field contains the property -dataset_mappings: - # FIELD-LEVEL FORMAT - # For datasets where properties are in field definitions (e.g., condition field) - "BrentLab/harbison_2004": - datasets: - harbison_2004: - carbon_source: - field: condition # Look in the 'condition' field's definitions - path: "media.carbon_source" - temperature_celsius: - field: condition - path: "temperature_celsius" +# Repository configurations specify where to find each property +# Top-level keys are repository IDs (e.g., "BrentLab/harbison_2004") - # REPO-LEVEL FORMAT - # For datasets where properties are at repo/config level - "BrentLab/kemmeren_2014": - repo_level: +# FIELD-LEVEL EXAMPLE: Harbison 2004 +# Properties are in field definitions (condition field) +BrentLab/harbison_2004: + dataset: + harbison_2004: carbon_source: - path: "environmental_conditions.media.carbon_source" + field: condition # Look in the 'condition' field's definitions + path: media.carbon_source temperature_celsius: - path: "environmental_conditions.temperature_celsius" + field: condition + path: temperature_celsius - # NEW HIERARCHICAL FORMAT - # Supports repo-level mappings that apply to all datasets - # and dataset-specific overrides - "BrentLab/example_multi_dataset_repo": - # Repo-level mappings apply to all datasets in this repository - repo_level: - carbon_source: - path: "environmental_conditions.media.carbon_source" - temperature_celsius: - path: "environmental_conditions.temperature_celsius" - - # Dataset-specific mappings override repo-level for particular datasets - datasets: - dataset1: - # This dataset has field-level definitions - # Override to look in 'condition' field instead of repo level - carbon_source: - field: condition - path: "media.carbon_source" - temperature_celsius: - field: condition - path: "temperature_celsius" +# REPO-WIDE + DATASET-SPECIFIC EXAMPLE: Kemmeren 2014 +# temperature_celsius is repo-wide (applies to all datasets) +# carbon_source is dataset-specific +BrentLab/kemmeren_2014: + # Repo-wide property (applies to all datasets in this repository) + temperature_celsius: + path: temperature_celsius - dataset2: - # Inherits repo-level mappings (no field specification) - # Can add dataset-specific properties - growth_phase: - path: "growth_conditions.phase" + # Dataset-specific properties + dataset: + kemmeren_2014: + carbon_source: + path: media.carbon_source - dataset3: - # Mixed: some properties in 'treatment' field, some at repo level - carbon_source: - field: treatment - path: "media.carbon" - temperature_celsius: - path: "temp" # Direct repo/config level property +# CONFIGURATION STRUCTURE EXPLAINED: +# +# Repo-wide properties: +# - Properties directly under repository ID +# - Apply to ALL datasets in that repository +# - Path is relative to experimental_conditions +# - Example: temperature_celsius: path: temperature_celsius +# -> looks in experimental_conditions.temperature_celsius +# +# Dataset-specific properties: +# - Properties under dataset.{dataset_name} +# - Override repo-wide for that specific dataset +# - Can use 'field' to specify field-level definitions +# - Example with field: carbon_source: field: condition, path: media.carbon_source +# -> looks in condition field definitions +# - Example without field: carbon_source: path: media.carbon_source +# -> looks in experimental_conditions.media.carbon_source +# +# Property name aliasing: +# - The filter property name (e.g., "temperature") can differ from the datacard property name +# - Example: temperature: path: temperature_celsius +# Filter uses "temperature", datacard has "temperature_celsius" diff --git a/pyproject.toml b/pyproject.toml index ee98517..b8024b0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,6 +37,7 @@ mkdocs-jupyter = "^0.25.1" mkdocstrings = {extras = ["python"], version = "^0.30.0"} matplotlib = "^3.10.6" seaborn = "^0.13.2" +types-pyyaml = "^6.0.12.20250915" [tool.pytest.ini_options] diff --git a/tfbpapi/datainfo/filter_resolver.py b/tfbpapi/datainfo/filter_resolver.py index f83f69f..13a53a5 100644 --- a/tfbpapi/datainfo/filter_resolver.py +++ b/tfbpapi/datainfo/filter_resolver.py @@ -31,6 +31,7 @@ ... repos=[("BrentLab/harbison_2004", "harbison_2004")], ... mode="conditions" ... ) + """ from __future__ import annotations @@ -39,7 +40,7 @@ from typing import Any import pandas as pd -import yaml +import yaml # type: ignore[import-untyped] from tfbpapi.datainfo import DataCard from tfbpapi.errors import DataCardError @@ -51,12 +52,11 @@ def get_nested_value(data: dict, path: str) -> Any: Navigate nested dict using dot notation. Handles missing intermediate keys gracefully by returning None. - If the full path is not found, tries removing common prefixes - like 'environmental_conditions' to handle different nesting levels. :param data: Dictionary to navigate - :param path: Dot-separated path (e.g., "environmental_conditions.media.name") + :param path: Dot-separated path (e.g., "media.carbon_source") :return: Value at path or None if not found + """ if not isinstance(data, dict): return None @@ -64,34 +64,8 @@ def get_nested_value(data: dict, path: str) -> Any: keys = path.split(".") current = data - # Try the full path first for key in keys: if not isinstance(current, dict) or key not in current: - # Full path failed, try fallback paths - fallback_paths = [] - - # If path starts with environmental_conditions, try without it - if keys[0] == "environmental_conditions" and len(keys) > 1: - fallback_paths.append(".".join(keys[1:])) - - # Try each fallback - for fallback_path in fallback_paths: - fallback_keys = fallback_path.split(".") - fallback_current = data - fallback_success = True - - for fallback_key in fallback_keys: - if ( - not isinstance(fallback_current, dict) - or fallback_key not in fallback_current - ): - fallback_success = False - break - fallback_current = fallback_current[fallback_key] - - if fallback_success: - return fallback_current - return None current = current[key] @@ -109,6 +83,7 @@ def extract_compound_names(value: Any) -> list[str]: :param value: Value to extract from :return: List of compound names + """ if value is None or value == "unspecified": return [] @@ -136,23 +111,62 @@ class DatasetFilterResolver: 1. Filter criteria (which values are acceptable for each property) 2. Repository and dataset-specific property paths (where to find each property) - Configuration structure supports: - - Repo-level mappings: Apply to all datasets in a repo - - Dataset-level mappings: Override repo-level for specific datasets + Configuration structure: + filters: + property_name: [value1, value2] + + BrentLab/repo_name: + # Repo-wide properties (apply to all datasets in this repository) + # Paths are relative to experimental_conditions at the repository level + property1: + path: media.name + + # Dataset-specific section + dataset: + dataset_name: + # Dataset-specific properties (apply only to this dataset) + # Paths are relative to experimental_conditions at the config level + property2: + path: temperature_celsius + + # Field-level properties (per-sample variation) + # Paths are relative to field definitions (NOT experimental_conditions) + property3: + field: condition + path: media.carbon_source + + Path Resolution: + - Repo-wide & dataset-specific: Paths automatically + prepended with "experimental_conditions." + Example: path "media.name" resolves to experimental_conditions.media.name + + - Field-level: Paths used directly on field definitions (no prepending) + Example: field "condition", path "media.carbon_source" + looks in condition field's definitions for media.carbon_source + + Examples: + # Repo-wide property (applies to all datasets in BrentLab/kemmeren_2014) + filters: + temperature_celsius: [30] + + BrentLab/kemmeren_2014: + temperature_celsius: + path: temperature_celsius + + # Dataset-specific property (applies only to kemmeren_2014 dataset) + BrentLab/kemmeren_2014: + dataset: + kemmeren_2014: + carbon_source: + path: media.carbon_source - Example: - dataset_mappings: - "BrentLab/my_repo": - repo_level: + # Field-level property (per-sample variation via condition field) + BrentLab/harbison_2004: + dataset: + harbison_2004: carbon_source: - path: environmental_conditions.media.carbon_source - datasets: - dataset1: - temperature_celsius: - path: custom.temp.path - dataset2: - temperature_celsius: - path: other.temp.path + field: condition + path: media.carbon_source It then resolves filters across datasets with three output modes: - conditions: Just which field values match (no data retrieval) @@ -163,6 +177,7 @@ class DatasetFilterResolver: config: Loaded configuration dict filters: Filter criteria from config mappings: Repository/dataset-specific property paths + """ def __init__(self, config_path: Path | str): @@ -170,10 +185,12 @@ def __init__(self, config_path: Path | str): Initialize resolver with external configuration. :param config_path: Path to YAML configuration file + """ self.config = self._load_config(Path(config_path)) self.filters = self.config.get("filters", {}) - self.mappings = self.config.get("dataset_mappings", {}) + # Extract mappings: all keys except 'filters' are repository IDs + self.mappings = {k: v for k, v in self.config.items() if k != "filters"} def _load_config(self, config_path: Path) -> dict: """ @@ -183,6 +200,7 @@ def _load_config(self, config_path: Path) -> dict: :return: Configuration dict :raises FileNotFoundError: If config file doesn't exist :raises yaml.YAMLError: If config is invalid YAML + """ if not config_path.exists(): raise FileNotFoundError(f"Configuration file not found: {config_path}") @@ -199,55 +217,54 @@ def _get_property_mappings(self, repo_id: str, config_name: str) -> dict: """ Get property mappings for a specific repo/dataset combination. - Merges repo-level and dataset-level mappings, with dataset-level taking precedence. + Merges repo-wide and dataset-specific mappings, + with dataset-specific taking precedence. - Supports configuration formats: - 1. Field-specific path (for field-level definitions): - carbon_source: - field: condition - path: media.carbon_source + Configuration format: + BrentLab/repo_name: + # Repo-wide properties (apply to all datasets) + property1: + path: path.in.experimental_conditions - 2. Direct path (for repo/config level): - carbon_source: - path: environmental_conditions.media.carbon_source + # Dataset-specific section + dataset: + dataset_name: + property2: + field: field_name # For field-level definitions + path: path.within.field - 3. Hierarchical with repo_level and datasets: - repo_level: + Examples: + 1. Field-specific path (for field-level definitions): carbon_source: - path: ... - datasets: - dataset1: - carbon_source: - field: condition - path: ... + field: condition + path: media.carbon_source + + 2. Repo-level path (for experimental_conditions): + temperature: + path: temperature_celsius :param repo_id: Repository ID :param config_name: Dataset/config name :return: Merged property mappings dict + """ if repo_id not in self.mappings: return {} repo_config = self.mappings[repo_id] + mappings = {} - # Check if this is the new hierarchical format - if "repo_level" in repo_config or "datasets" in repo_config: - # New format: merge repo_level and dataset-specific - mappings = {} + # Add repo-wide properties (all keys except 'dataset') + for key, value in repo_config.items(): + if key != "dataset": + mappings[key] = value - # Start with repo-level mappings (apply to all datasets) - if "repo_level" in repo_config: - mappings.update(repo_config["repo_level"]) + # Override with dataset-specific properties + if "dataset" in repo_config: + if config_name in repo_config["dataset"]: + mappings.update(repo_config["dataset"][config_name]) - # Override with dataset-specific mappings - if "datasets" in repo_config and config_name in repo_config["datasets"]: - mappings.update(repo_config["datasets"][config_name]) - - return mappings - else: - # Legacy format: direct property mappings - # This assumes the config at repo level applies to all datasets - return repo_config + return mappings def resolve_filters( self, @@ -263,10 +280,12 @@ def resolve_filters( :param token: Optional HuggingFace token for private repos :return: Dict mapping repo_id to results :raises ValueError: If mode is invalid + """ if mode not in ["conditions", "samples", "full_data"]: raise ValueError( - f"Invalid mode: {mode}. Must be 'conditions', 'samples', or 'full_data'" + f"Invalid mode: {mode}. Must be 'conditions', " + "'samples', or 'full_data'" ) results = {} @@ -341,6 +360,7 @@ def _check_repo_config_level( :param config_name: Configuration name :param repo_id: Repository ID :return: (included, reason) tuple + """ # Get repo and config level conditions try: @@ -359,11 +379,22 @@ def _check_repo_config_level( if filter_prop not in property_mappings: continue + mapping = property_mappings[filter_prop] + + # Check if this is a field-level mapping + # (should not be checked at repo level) + if "field" in mapping: + # Skip field-level properties at repo/config level + continue + # Get path for this property - path = property_mappings[filter_prop]["path"] + path = mapping["path"] + + # Prepend experimental_conditions for repo-level paths + full_path = f"experimental_conditions.{path}" # Try to get value at this path - value = get_nested_value(conditions, path) + value = get_nested_value(conditions, full_path) if value is None: # Property not specified at repo/config level, skip @@ -385,7 +416,8 @@ def _check_repo_config_level( if not matches: return ( False, - f"{filter_prop}: found {actual_values}, wanted {acceptable_values}", + f"{filter_prop}: found {actual_values}, " + f"wanted {acceptable_values}", ) return True, "" @@ -403,8 +435,9 @@ def _check_field_level( :param config_name: Configuration name :param repo_id: Repository ID :return: Dict mapping field names to lists of matching values + """ - matching = {} + matching: dict[str, list[str]] = {} property_mappings = self._get_property_mappings(repo_id, config_name) # Get config to find fields with role=experimental_condition @@ -414,8 +447,8 @@ def _check_field_level( # Group property mappings by field (if field is specified) # field_mappings: {field_name: {prop: path, ...}} - field_mappings = {} - general_mappings = {} # Properties without field specification + field_mappings: dict[str, dict[str, str]] = {} + general_mappings: dict[str, str] = {} # Properties without field specification for prop, mapping in property_mappings.items(): if "field" in mapping: @@ -500,6 +533,7 @@ def _get_sample_metadata( :param matching_field_values: Dict of field names to matching values :param token: Optional HuggingFace token :return: DataFrame with sample metadata + """ # Initialize query API api = HfQueryAPI(repo_id, token=token) @@ -562,6 +596,7 @@ def _get_full_data( :param matching_field_values: Dict of field names to matching values :param token: Optional HuggingFace token :return: DataFrame with full data + """ # Initialize query API api = HfQueryAPI(repo_id, token=token) @@ -598,4 +633,7 @@ def __repr__(self) -> str: """String representation.""" n_filters = len(self.filters) n_datasets = len(self.mappings) - return f"DatasetFilterResolver({n_filters} filters, {n_datasets} datasets configured)" + return ( + f"DatasetFilterResolver({n_filters} filters, " + f"{n_datasets} datasets configured)" + ) diff --git a/tfbpapi/datainfo/metadata_manager.py b/tfbpapi/datainfo/metadata_manager.py index da1efc3..d20b05a 100644 --- a/tfbpapi/datainfo/metadata_manager.py +++ b/tfbpapi/datainfo/metadata_manager.py @@ -71,12 +71,14 @@ def __init__( :param cache_dir: Optional directory for caching metadata views :param cache: Whether to enable persistent caching (not yet implemented) - :param duckdb_conn: Optional DuckDB connection to use. If None, creates new - in-memory connection. Pass a shared connection to work with views created - by HfQueryAPI or other tools. + :param duckdb_conn: Optional DuckDB connection to use. If None, creates new in- + memory connection. Pass a shared connection to work with views created by + HfQueryAPI or other tools. """ - self._conn = duckdb_conn if duckdb_conn is not None else duckdb.connect(":memory:") + self._conn = ( + duckdb_conn if duckdb_conn is not None else duckdb.connect(":memory:") + ) self._cache_dir = cache_dir self._cache_enabled = cache self._registered_datasets: dict[str, Any] = {} @@ -91,7 +93,8 @@ def _sanitize_view_name(self, repo_id: str, config_name: str) -> str: :param repo_id: Repository ID (e.g., "BrentLab/harbison_2004") :param config_name: Configuration name (e.g., "harbison_2004") - :return: Sanitized view name (e.g., "BrentLab_harbison_2004_harbison_2004_metadata") + :return: Sanitized view name + (e.g., "BrentLab_harbison_2004_harbison_2004_metadata") Example: >>> mgr = MetadataManager() @@ -126,7 +129,7 @@ def _flatten_condition_definition(self, definition: dict) -> dict: {'growth_media': 'YPD', 'temperature_celsius': 30} """ - result = {} + result: dict[str, Any] = {} if not definition: return result @@ -164,7 +167,7 @@ def _flatten_experimental_conditions(self, exp_conds: Any) -> dict: {'temperature_celsius': 30, 'strain_background': 'BY4741'} """ - result = {} + result: dict[str, Any] = {} if exp_conds is None: return result @@ -211,7 +214,8 @@ def create_metadata_view( :param base_view: Name of existing DuckDB view (created by HfCacheManager) :param view_name: Name for the new metadata view :param include_fields: List of field names to select from base view - :param constant_columns: Optional dict of {column_name: value} to add as constants + :param constant_columns: Optional dict of {column_name: value} + to add as constants :return: Name of created view Example: diff --git a/tfbpapi/tests/datainfo/conftest.py b/tfbpapi/tests/datainfo/conftest.py index 09aa58f..2b459ff 100644 --- a/tfbpapi/tests/datainfo/conftest.py +++ b/tfbpapi/tests/datainfo/conftest.py @@ -290,3 +290,1145 @@ def sample_partitioning_info(): def sample_data_file_info(): """Sample data file information.""" return {"split": "train", "path": "genomic_features.parquet"} + + +# ============================================================================ +# Filter Resolver Fixtures +# ============================================================================ + + +@pytest.fixture +def write_config(tmp_path): + """ + Helper to write config dict to temp file. + + :param tmp_path: pytest tmp_path fixture + :return: Function that writes config dict and returns Path + + """ + import yaml # type: ignore[import-untyped] + + def _write(config_dict): + config_path = tmp_path / "config.yaml" + with open(config_path, "w") as f: + yaml.dump(config_dict, f) + return config_path + + return _write + + +# ============================================================================ +# Datacard Fixtures (from huggingface_collection_datacards.txt) +# ============================================================================ + + +# Datacard fixtures copied directly from huggingface_collection_datacards.txt +# These contain the complete YAML metadata for each repository + + +@pytest.fixture +def hackett_2020_datacard(): + """Complete hackett_2020 datacard YAML from huggingface_collection_datacards.txt.""" + return { + "license": "mit", + "language": ["en"], + "tags": [ + "genomics", + "yeast", + "transcription", + "perturbation", + "response", + "overexpression", + ], + "pretty_name": "Hackett, 2020 Overexpression", + "size_categories": ["1M log2(1.7) & " + "pval < 0.05). Note that " + "there is a slight " + "difference when " + "calculating from the data " + "provided here, I believe " + "due to a difference in " + "the way the targets are " + "parsed and filtered (some " + "ORFs that have since been " + "removed from the " + "annotations are removed). " + "I didn't investigate this " + "closely, though.", + "role": "experimental_condition", + }, + { + "name": "profile_first_published", + "dtype": "string", + "description": "citation or reference " + "indicating where this " + "expression profile was " + "first published", + "role": "experimental_condition", + }, + { + "name": "chase_notes", + "dtype": "string", + "description": "notes added during data " + "curation and parsing", + }, + ] + }, + } + ], + } diff --git a/tfbpapi/tests/datainfo/test_filter_resolver.py b/tfbpapi/tests/datainfo/test_filter_resolver.py index 38cfa4b..a56d888 100644 --- a/tfbpapi/tests/datainfo/test_filter_resolver.py +++ b/tfbpapi/tests/datainfo/test_filter_resolver.py @@ -1,11 +1,7 @@ """Tests for DatasetFilterResolver.""" -from pathlib import Path -from tempfile import NamedTemporaryFile - import pandas as pd import pytest -import yaml from tfbpapi.datainfo.filter_resolver import ( DatasetFilterResolver, @@ -21,44 +17,64 @@ def test_get_nested_value_simple(self): """Test getting nested values with dot notation.""" data = { "environmental_conditions": { - "media": { - "name": "YPD", - "carbon_source": [{"compound": "D-glucose"}] - }, - "temperature_celsius": 30 + "media": {"name": "YPD", "carbon_source": [{"compound": "D-glucose"}]}, + "temperature_celsius": 30, } } - assert get_nested_value(data, "environmental_conditions.temperature_celsius") == 30 + assert ( + get_nested_value(data, "environmental_conditions.temperature_celsius") == 30 + ) assert get_nested_value(data, "environmental_conditions.media.name") == "YPD" assert get_nested_value(data, "nonexistent.path") is None - assert get_nested_value(data, "environmental_conditions.media.nonexistent") is None + assert ( + get_nested_value(data, "environmental_conditions.media.nonexistent") is None + ) - def test_get_nested_value_fallback(self): - """Test fallback path resolution for field-level definitions.""" - # Field-level definition (no environmental_conditions wrapper) + def test_get_nested_value_no_fallback(self): + """Test that paths must be correct - no automatic fallback.""" + # Field-level definition (no experimental_conditions wrapper) field_def = { - "media": { - "name": "YPD", - "carbon_source": [{"compound": "D-glucose"}] - }, - "temperature_celsius": 30 + "media": {"name": "YPD", "carbon_source": [{"compound": "D-glucose"}]}, + "temperature_celsius": 30, } - # Path with environmental_conditions should fallback to path without it - assert get_nested_value(field_def, "environmental_conditions.media.name") == "YPD" - assert get_nested_value(field_def, "environmental_conditions.temperature_celsius") == 30 + # Wrong path should return None (no fallback) + assert get_nested_value(field_def, "experimental_conditions.media.name") is None + assert ( + get_nested_value(field_def, "experimental_conditions.temperature_celsius") + is None + ) - # Direct path should still work + # Correct direct paths work assert get_nested_value(field_def, "media.name") == "YPD" assert get_nested_value(field_def, "temperature_celsius") == 30 + # Repo-level definition (with experimental_conditions wrapper) + repo_def = { + "experimental_conditions": { + "media": {"name": "SC", "carbon_source": [{"compound": "D-glucose"}]}, + "temperature_celsius": 30, + } + } + + # Full path works + assert get_nested_value(repo_def, "experimental_conditions.media.name") == "SC" + assert ( + get_nested_value(repo_def, "experimental_conditions.temperature_celsius") + == 30 + ) + + # Shortened path does NOT work (no fallback) + assert get_nested_value(repo_def, "media.name") is None + assert get_nested_value(repo_def, "temperature_celsius") is None + def test_extract_compound_names(self): """Test extracting compound names from various formats.""" # List of dicts value1 = [ {"compound": "D-glucose", "concentration_percent": 2}, - {"compound": "D-galactose", "concentration_percent": 1} + {"compound": "D-galactose", "concentration_percent": 1}, ] assert extract_compound_names(value1) == ["D-glucose", "D-galactose"] @@ -78,42 +94,29 @@ def test_extract_compound_names(self): class TestDatasetFilterResolver: """Test DatasetFilterResolver class.""" - @pytest.fixture - def simple_config(self): - """Create a simple test configuration.""" - return { + def test_init(self, write_config): + """Test initialization.""" + config = { "filters": { "carbon_source": ["D-glucose", "D-galactose"], - "temperature_celsius": [30] + "temperature_celsius": [30], }, - "dataset_mappings": { - "BrentLab/harbison_2004": { - "datasets": { - "harbison_2004": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source" - }, - "temperature_celsius": { - "field": "condition", - "path": "temperature_celsius" - } - } + "BrentLab/harbison_2004": { + "dataset": { + "harbison_2004": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source", + }, + "temperature_celsius": { + "field": "condition", + "path": "temperature_celsius", + }, } } - } + }, } - - @pytest.fixture - def config_file(self, simple_config, tmp_path): - """Create a temporary config file.""" - config_path = tmp_path / "test_config.yaml" - with open(config_path, 'w') as f: - yaml.dump(simple_config, f) - return config_path - - def test_init(self, config_file): - """Test initialization.""" + config_file = write_config(config) resolver = DatasetFilterResolver(config_file) assert len(resolver.filters) == 2 @@ -127,15 +130,35 @@ def test_init_missing_file(self): with pytest.raises(FileNotFoundError): DatasetFilterResolver("nonexistent.yaml") - def test_resolve_filters_mode_conditions(self, config_file): + def test_resolve_filters_mode_conditions(self, write_config): """Test resolve_filters in conditions mode.""" + config = { + "filters": { + "carbon_source": ["D-glucose", "D-galactose"], + "temperature_celsius": [30], + }, + "BrentLab/harbison_2004": { + "dataset": { + "harbison_2004": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source", + }, + "temperature_celsius": { + "field": "condition", + "path": "temperature_celsius", + }, + } + } + }, + } + config_file = write_config(config) resolver = DatasetFilterResolver(config_file) - # This will actually try to load the DataCard, so it's more of an integration test - # For now, test the structure + # This will actually try to load the DataCard, + # so it's more of an integration test For now, test the structure results = resolver.resolve_filters( - repos=[("BrentLab/harbison_2004", "harbison_2004")], - mode="conditions" + repos=[("BrentLab/harbison_2004", "harbison_2004")], mode="conditions" ) assert "BrentLab/harbison_2004" in results @@ -148,107 +171,89 @@ def test_resolve_filters_mode_conditions(self, config_file): if result["included"]: assert "matching_field_values" in result - def test_resolve_filters_invalid_mode(self, config_file): + def test_resolve_filters_invalid_mode(self, write_config): """Test resolve_filters with invalid mode.""" + config = { + "filters": { + "carbon_source": ["D-glucose", "D-galactose"], + "temperature_celsius": [30], + }, + "BrentLab/harbison_2004": { + "dataset": { + "harbison_2004": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source", + }, + "temperature_celsius": { + "field": "condition", + "path": "temperature_celsius", + }, + } + } + }, + } + config_file = write_config(config) resolver = DatasetFilterResolver(config_file) with pytest.raises(ValueError, match="Invalid mode"): resolver.resolve_filters( - repos=[("BrentLab/harbison_2004", "harbison_2004")], - mode="invalid" + repos=[("BrentLab/harbison_2004", "harbison_2004")], mode="invalid" ) - def test_repr(self, config_file): + def test_repr(self, write_config): """Test string representation.""" - resolver = DatasetFilterResolver(config_file) - repr_str = repr(resolver) - - assert "DatasetFilterResolver" in repr_str - assert "2 filters" in repr_str - assert "1 datasets" in repr_str - - def test_hierarchical_config(self, tmp_path): - """Test hierarchical configuration with repo_level and dataset-specific mappings.""" config = { "filters": { - "carbon_source": ["D-glucose"], - "temperature_celsius": [30] + "carbon_source": ["D-glucose", "D-galactose"], + "temperature_celsius": [30], }, - "dataset_mappings": { - "BrentLab/test_repo": { - "repo_level": { + "BrentLab/harbison_2004": { + "dataset": { + "harbison_2004": { "carbon_source": { - "path": "environmental_conditions.media.carbon_source" - } - }, - "datasets": { - "dataset1": { - "temperature_celsius": { - "field": "condition", - "path": "environmental_conditions.temperature_celsius" - } + "field": "condition", + "path": "media.carbon_source", + }, + "temperature_celsius": { + "field": "condition", + "path": "temperature_celsius", }, - "dataset2": { - "temperature_celsius": { - "field": "condition", - "path": "custom.temp.path" - } - } } } - } + }, } + config_file = write_config(config) + resolver = DatasetFilterResolver(config_file) + repr_str = repr(resolver) - config_path = tmp_path / "hierarchical_config.yaml" - with open(config_path, 'w') as f: - yaml.dump(config, f) - - resolver = DatasetFilterResolver(config_path) - - # Test property mapping resolution - mappings1 = resolver._get_property_mappings("BrentLab/test_repo", "dataset1") - assert "carbon_source" in mappings1 # From repo_level - assert "temperature_celsius" in mappings1 # From dataset-specific - assert mappings1["temperature_celsius"]["path"] == "environmental_conditions.temperature_celsius" - - mappings2 = resolver._get_property_mappings("BrentLab/test_repo", "dataset2") - assert "carbon_source" in mappings2 # From repo_level - assert "temperature_celsius" in mappings2 # Overridden by dataset-specific - assert mappings2["temperature_celsius"]["path"] == "custom.temp.path" + assert "DatasetFilterResolver" in repr_str + assert "2 filters" in repr_str + assert "1 datasets" in repr_str class TestRealDataCards: """Integration tests with real datacards.""" - def test_harbison_2004_glucose_filter(self, tmp_path): + def test_harbison_2004_glucose_filter(self, harbison_2004_datacard, write_config): """Test filtering harbison_2004 for glucose samples.""" - # Create config for glucose filtering config = { - "filters": { - "carbon_source": ["D-glucose"] - }, - "dataset_mappings": { - "BrentLab/harbison_2004": { - "datasets": { - "harbison_2004": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source" - } + "filters": {"carbon_source": ["D-glucose"]}, + "BrentLab/harbison_2004": { + "dataset": { + "harbison_2004": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source", } } } - } + }, } - - config_path = tmp_path / "glucose_config.yaml" - with open(config_path, 'w') as f: - yaml.dump(config, f) - + config_path = write_config(config) resolver = DatasetFilterResolver(config_path) results = resolver.resolve_filters( - repos=[("BrentLab/harbison_2004", "harbison_2004")], - mode="conditions" + repos=[("BrentLab/harbison_2004", "harbison_2004")], mode="conditions" ) assert "BrentLab/harbison_2004" in results @@ -264,38 +269,38 @@ def test_harbison_2004_glucose_filter(self, tmp_path): if "condition" in result["matching_field_values"]: matching = result["matching_field_values"]["condition"] # YPD, HEAT, H2O2Hi, H2O2Lo, Acid, Alpha, BUT14, BUT90 all have D-glucose - expected_glucose_conditions = ["YPD", "HEAT", "H2O2Hi", "H2O2Lo", "Acid", "Alpha", "BUT14", "BUT90"] + expected_glucose_conditions = [ + "YPD", + "HEAT", + "H2O2Hi", + "H2O2Lo", + "Acid", + "Alpha", + "BUT14", + "BUT90", + ] for cond in expected_glucose_conditions: assert cond in matching, f"{cond} should be in matching conditions" - def test_harbison_2004_galactose_filter(self, tmp_path): + def test_harbison_2004_galactose_filter(self, harbison_2004_datacard, write_config): """Test filtering harbison_2004 for galactose samples.""" config = { - "filters": { - "carbon_source": ["D-galactose"] - }, - "dataset_mappings": { - "BrentLab/harbison_2004": { - "datasets": { - "harbison_2004": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source" - } + "filters": {"carbon_source": ["D-galactose"]}, + "BrentLab/harbison_2004": { + "dataset": { + "harbison_2004": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source", } } } - } + }, } - - config_path = tmp_path / "galactose_config.yaml" - with open(config_path, 'w') as f: - yaml.dump(config, f) - + config_path = write_config(config) resolver = DatasetFilterResolver(config_path) results = resolver.resolve_filters( - repos=[("BrentLab/harbison_2004", "harbison_2004")], - mode="conditions" + repos=[("BrentLab/harbison_2004", "harbison_2004")], mode="conditions" ) result = results["BrentLab/harbison_2004"] @@ -306,34 +311,25 @@ def test_harbison_2004_galactose_filter(self, tmp_path): matching = result["matching_field_values"]["condition"] assert "GAL" in matching - def test_harbison_2004_samples_mode(self, tmp_path): + def test_harbison_2004_samples_mode(self, harbison_2004_datacard, write_config): """Test retrieving sample-level metadata.""" config = { - "filters": { - "carbon_source": ["D-glucose"] - }, - "dataset_mappings": { - "BrentLab/harbison_2004": { - "datasets": { - "harbison_2004": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source" - } + "filters": {"carbon_source": ["D-glucose"]}, + "BrentLab/harbison_2004": { + "dataset": { + "harbison_2004": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source", } } } - } + }, } - - config_path = tmp_path / "samples_config.yaml" - with open(config_path, 'w') as f: - yaml.dump(config, f) - + config_path = write_config(config) resolver = DatasetFilterResolver(config_path) results = resolver.resolve_filters( - repos=[("BrentLab/harbison_2004", "harbison_2004")], - mode="samples" + repos=[("BrentLab/harbison_2004", "harbison_2004")], mode="samples" ) result = results["BrentLab/harbison_2004"] @@ -345,34 +341,25 @@ def test_harbison_2004_samples_mode(self, tmp_path): assert isinstance(df, pd.DataFrame) assert len(df) > 0 # Should have some samples - def test_harbison_2004_full_data_mode(self, tmp_path): + def test_harbison_2004_full_data_mode(self, harbison_2004_datacard, write_config): """Test retrieving full data.""" config = { - "filters": { - "carbon_source": ["D-glucose"] - }, - "dataset_mappings": { - "BrentLab/harbison_2004": { - "datasets": { - "harbison_2004": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source" - } + "filters": {"carbon_source": ["D-glucose"]}, + "BrentLab/harbison_2004": { + "dataset": { + "harbison_2004": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source", } } } - } + }, } - - config_path = tmp_path / "full_data_config.yaml" - with open(config_path, 'w') as f: - yaml.dump(config, f) - + config_path = write_config(config) resolver = DatasetFilterResolver(config_path) results = resolver.resolve_filters( - repos=[("BrentLab/harbison_2004", "harbison_2004")], - mode="full_data" + repos=[("BrentLab/harbison_2004", "harbison_2004")], mode="full_data" ) result = results["BrentLab/harbison_2004"] @@ -383,3 +370,125 @@ def test_harbison_2004_full_data_mode(self, tmp_path): df = result["data"] assert isinstance(df, pd.DataFrame) assert len(df) > 0 # Should have data rows + + def test_hackett_2020_glucose_filter(self, hackett_2020_datacard, write_config): + """Test filtering hackett_2020 for glucose samples.""" + config = { + "filters": {"carbon_source": ["D-glucose"]}, + "BrentLab/hackett_2020": { + "dataset": { + "hackett_2020": {"carbon_source": {"path": "media.carbon_source"}} + } + }, + } + config_path = write_config(config) + resolver = DatasetFilterResolver(config_path) + results = resolver.resolve_filters( + repos=[("BrentLab/hackett_2020", "hackett_2020")], mode="conditions" + ) + + assert "BrentLab/hackett_2020" in results + result = results["BrentLab/hackett_2020"] + + # Should be included (hackett_2020 has D-glucose at repo level) + assert result["included"] is True + + def test_kemmeren_2014_glucose_filter(self, kemmeren_2014_datacard, write_config): + """Test filtering kemmeren_2014 for glucose samples.""" + config = { + "filters": {"carbon_source": ["D-glucose"]}, + "BrentLab/kemmeren_2014": { + "dataset": { + "kemmeren_2014": {"carbon_source": {"path": "media.carbon_source"}} + } + }, + } + config_path = write_config(config) + resolver = DatasetFilterResolver(config_path) + results = resolver.resolve_filters( + repos=[("BrentLab/kemmeren_2014", "kemmeren_2014")], mode="conditions" + ) + + assert "BrentLab/kemmeren_2014" in results + result = results["BrentLab/kemmeren_2014"] + + # Should be included (kemmeren_2014 has D-glucose at repo level) + assert result["included"] is True + + def test_kemmeren_2014_temperature_filter( + self, kemmeren_2014_datacard, write_config + ): + """Test filtering kemmeren_2014 for temperature.""" + config = { + "filters": {"temperature_celsius": [30]}, + "BrentLab/kemmeren_2014": { + "dataset": { + "kemmeren_2014": { + "temperature_celsius": {"path": "temperature_celsius"} + } + } + }, + } + config_path = write_config(config) + resolver = DatasetFilterResolver(config_path) + results = resolver.resolve_filters( + repos=[("BrentLab/kemmeren_2014", "kemmeren_2014")], mode="conditions" + ) + + assert "BrentLab/kemmeren_2014" in results + result = results["BrentLab/kemmeren_2014"] + + # Should be included (kemmeren_2014 has temperature_celsius: 30 at repo level) + assert result["included"] is True + + def test_multi_repo_glucose_filter( + self, + harbison_2004_datacard, + hackett_2020_datacard, + kemmeren_2014_datacard, + write_config, + ): + """Test filtering D-glucose across multiple repos.""" + config = { + "filters": {"carbon_source": ["D-glucose"]}, + "BrentLab/harbison_2004": { + "dataset": { + "harbison_2004": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source", + } + } + } + }, + "BrentLab/hackett_2020": { + "dataset": { + "hackett_2020": {"carbon_source": {"path": "media.carbon_source"}} + } + }, + "BrentLab/kemmeren_2014": { + "dataset": { + "kemmeren_2014": {"carbon_source": {"path": "media.carbon_source"}} + } + }, + } + config_path = write_config(config) + resolver = DatasetFilterResolver(config_path) + results = resolver.resolve_filters( + repos=[ + ("BrentLab/harbison_2004", "harbison_2004"), + ("BrentLab/hackett_2020", "hackett_2020"), + ("BrentLab/kemmeren_2014", "kemmeren_2014"), + ], + mode="conditions", + ) + + # All three repos should be included + assert "BrentLab/harbison_2004" in results + assert results["BrentLab/harbison_2004"]["included"] is True + + assert "BrentLab/hackett_2020" in results + assert results["BrentLab/hackett_2020"]["included"] is True + + assert "BrentLab/kemmeren_2014" in results + assert results["BrentLab/kemmeren_2014"]["included"] is True From ea90de061c8a1b275099738bc9298a119946fc87 Mon Sep 17 00:00:00 2001 From: chasem Date: Mon, 15 Dec 2025 20:44:26 -0600 Subject: [PATCH 44/49] tmp --- tfbpapi/datainfo/__init__.py | 6 + tfbpapi/datainfo/filter_resolver.py | 639 ------------------ tfbpapi/datainfo/metadata_builder.py | 551 +++++++++++++++ tfbpapi/datainfo/metadata_config_models.py | 312 +++++++++ .../tests/datainfo/test_filter_resolver.py | 494 -------------- .../tests/datainfo/test_metadata_builder.py | 332 +++++++++ .../datainfo/test_metadata_config_models.py | 386 +++++++++++ 7 files changed, 1587 insertions(+), 1133 deletions(-) delete mode 100644 tfbpapi/datainfo/filter_resolver.py create mode 100644 tfbpapi/datainfo/metadata_builder.py create mode 100644 tfbpapi/datainfo/metadata_config_models.py delete mode 100644 tfbpapi/tests/datainfo/test_filter_resolver.py create mode 100644 tfbpapi/tests/datainfo/test_metadata_builder.py create mode 100644 tfbpapi/tests/datainfo/test_metadata_config_models.py diff --git a/tfbpapi/datainfo/__init__.py b/tfbpapi/datainfo/__init__.py index 11f3387..c1643f6 100644 --- a/tfbpapi/datainfo/__init__.py +++ b/tfbpapi/datainfo/__init__.py @@ -1,5 +1,7 @@ from .datacard import DataCard from .fetchers import HfDataCardFetcher, HfRepoStructureFetcher, HfSizeInfoFetcher +from .metadata_builder import MetadataBuilder +from .metadata_config_models import MetadataConfig, PropertyMapping, RepositoryConfig from .metadata_manager import MetadataManager from .models import ( DatasetCard, @@ -15,6 +17,10 @@ "HfDataCardFetcher", "HfRepoStructureFetcher", "HfSizeInfoFetcher", + "MetadataBuilder", + "MetadataConfig", + "PropertyMapping", + "RepositoryConfig", "MetadataManager", "DatasetCard", "DatasetConfig", diff --git a/tfbpapi/datainfo/filter_resolver.py b/tfbpapi/datainfo/filter_resolver.py deleted file mode 100644 index 13a53a5..0000000 --- a/tfbpapi/datainfo/filter_resolver.py +++ /dev/null @@ -1,639 +0,0 @@ -""" -Dataset filter resolver with external configuration for heterogeneous datasets. - -This module provides a simple, configuration-driven approach to filtering samples -across datasets with varying experimental condition structures. Users specify -filters and dataset-specific property paths in external YAML files. - -Key Components: -- DatasetFilterResolver: Main class for applying filters across datasets -- Three output modes: conditions, samples, full_data -- External YAML configuration for filters and property mappings -- Automatic detection of property location (repo/config/field level) - -Example Configuration: - ```yaml - filters: - carbon_source: ["D-glucose", "D-galactose"] - temperature_celsius: [30, 37] - - dataset_mappings: - "BrentLab/harbison_2004": - carbon_source: - path: "environmental_conditions.media.carbon_source" - temperature_celsius: - path: "environmental_conditions.temperature_celsius" - ``` - -Example Usage: - >>> resolver = DatasetFilterResolver("filters.yaml") - >>> results = resolver.resolve_filters( - ... repos=[("BrentLab/harbison_2004", "harbison_2004")], - ... mode="conditions" - ... ) - -""" - -from __future__ import annotations - -from pathlib import Path -from typing import Any - -import pandas as pd -import yaml # type: ignore[import-untyped] - -from tfbpapi.datainfo import DataCard -from tfbpapi.errors import DataCardError -from tfbpapi.HfQueryAPI import HfQueryAPI - - -def get_nested_value(data: dict, path: str) -> Any: - """ - Navigate nested dict using dot notation. - - Handles missing intermediate keys gracefully by returning None. - - :param data: Dictionary to navigate - :param path: Dot-separated path (e.g., "media.carbon_source") - :return: Value at path or None if not found - - """ - if not isinstance(data, dict): - return None - - keys = path.split(".") - current = data - - for key in keys: - if not isinstance(current, dict) or key not in current: - return None - current = current[key] - - return current - - -def extract_compound_names(value: Any) -> list[str]: - """ - Extract compound names from various representations. - - Handles: - - List of dicts: [{"compound": "D-glucose", ...}] -> ["D-glucose"] - - String: "D-glucose" -> ["D-glucose"] - - None or "unspecified" -> [] - - :param value: Value to extract from - :return: List of compound names - - """ - if value is None or value == "unspecified": - return [] - - if isinstance(value, str): - return [value] - - if isinstance(value, list): - compounds = [] - for item in value: - if isinstance(item, dict) and "compound" in item: - compounds.append(item["compound"]) - elif isinstance(item, str): - compounds.append(item) - return compounds - - return [] - - -class DatasetFilterResolver: - """ - Resolve filters across heterogeneous datasets using external configuration. - - This class takes an external YAML configuration specifying: - 1. Filter criteria (which values are acceptable for each property) - 2. Repository and dataset-specific property paths (where to find each property) - - Configuration structure: - filters: - property_name: [value1, value2] - - BrentLab/repo_name: - # Repo-wide properties (apply to all datasets in this repository) - # Paths are relative to experimental_conditions at the repository level - property1: - path: media.name - - # Dataset-specific section - dataset: - dataset_name: - # Dataset-specific properties (apply only to this dataset) - # Paths are relative to experimental_conditions at the config level - property2: - path: temperature_celsius - - # Field-level properties (per-sample variation) - # Paths are relative to field definitions (NOT experimental_conditions) - property3: - field: condition - path: media.carbon_source - - Path Resolution: - - Repo-wide & dataset-specific: Paths automatically - prepended with "experimental_conditions." - Example: path "media.name" resolves to experimental_conditions.media.name - - - Field-level: Paths used directly on field definitions (no prepending) - Example: field "condition", path "media.carbon_source" - looks in condition field's definitions for media.carbon_source - - Examples: - # Repo-wide property (applies to all datasets in BrentLab/kemmeren_2014) - filters: - temperature_celsius: [30] - - BrentLab/kemmeren_2014: - temperature_celsius: - path: temperature_celsius - - # Dataset-specific property (applies only to kemmeren_2014 dataset) - BrentLab/kemmeren_2014: - dataset: - kemmeren_2014: - carbon_source: - path: media.carbon_source - - # Field-level property (per-sample variation via condition field) - BrentLab/harbison_2004: - dataset: - harbison_2004: - carbon_source: - field: condition - path: media.carbon_source - - It then resolves filters across datasets with three output modes: - - conditions: Just which field values match (no data retrieval) - - samples: Sample-level metadata (one row per sample_id) - - full_data: All measurements for matching samples - - Attributes: - config: Loaded configuration dict - filters: Filter criteria from config - mappings: Repository/dataset-specific property paths - - """ - - def __init__(self, config_path: Path | str): - """ - Initialize resolver with external configuration. - - :param config_path: Path to YAML configuration file - - """ - self.config = self._load_config(Path(config_path)) - self.filters = self.config.get("filters", {}) - # Extract mappings: all keys except 'filters' are repository IDs - self.mappings = {k: v for k, v in self.config.items() if k != "filters"} - - def _load_config(self, config_path: Path) -> dict: - """ - Load YAML configuration file. - - :param config_path: Path to YAML file - :return: Configuration dict - :raises FileNotFoundError: If config file doesn't exist - :raises yaml.YAMLError: If config is invalid YAML - - """ - if not config_path.exists(): - raise FileNotFoundError(f"Configuration file not found: {config_path}") - - with open(config_path) as f: - config = yaml.safe_load(f) - - if not isinstance(config, dict): - raise ValueError("Configuration must be a dict") - - return config - - def _get_property_mappings(self, repo_id: str, config_name: str) -> dict: - """ - Get property mappings for a specific repo/dataset combination. - - Merges repo-wide and dataset-specific mappings, - with dataset-specific taking precedence. - - Configuration format: - BrentLab/repo_name: - # Repo-wide properties (apply to all datasets) - property1: - path: path.in.experimental_conditions - - # Dataset-specific section - dataset: - dataset_name: - property2: - field: field_name # For field-level definitions - path: path.within.field - - Examples: - 1. Field-specific path (for field-level definitions): - carbon_source: - field: condition - path: media.carbon_source - - 2. Repo-level path (for experimental_conditions): - temperature: - path: temperature_celsius - - :param repo_id: Repository ID - :param config_name: Dataset/config name - :return: Merged property mappings dict - - """ - if repo_id not in self.mappings: - return {} - - repo_config = self.mappings[repo_id] - mappings = {} - - # Add repo-wide properties (all keys except 'dataset') - for key, value in repo_config.items(): - if key != "dataset": - mappings[key] = value - - # Override with dataset-specific properties - if "dataset" in repo_config: - if config_name in repo_config["dataset"]: - mappings.update(repo_config["dataset"][config_name]) - - return mappings - - def resolve_filters( - self, - repos: list[tuple[str, str]], - mode: str = "conditions", - token: str | None = None, - ) -> dict[str, Any]: - """ - Resolve filters across datasets. - - :param repos: List of (repo_id, config_name) tuples to check - :param mode: Output mode - "conditions", "samples", or "full_data" - :param token: Optional HuggingFace token for private repos - :return: Dict mapping repo_id to results - :raises ValueError: If mode is invalid - - """ - if mode not in ["conditions", "samples", "full_data"]: - raise ValueError( - f"Invalid mode: {mode}. Must be 'conditions', " - "'samples', or 'full_data'" - ) - - results = {} - - for repo_id, config_name in repos: - try: - # Load DataCard - card = DataCard(repo_id, token=token) - - # Check if this dataset has mappings - if repo_id not in self.mappings: - results[repo_id] = { - "included": False, - "reason": f"No property mappings defined for {repo_id}", - } - continue - - # Check repo/config level filters (Level 1) - included, reason = self._check_repo_config_level( - card, config_name, repo_id - ) - - if not included: - results[repo_id] = {"included": False, "reason": reason} - continue - - # Dataset passes Level 1, check field-level filters (Level 2) - matching_field_values = self._check_field_level( - card, config_name, repo_id - ) - - # Build result based on mode - result = { - "included": True, - "matching_field_values": matching_field_values, - } - - if mode == "conditions": - # Mode 0: Just conditions, no data - pass - elif mode == "samples": - # Mode 1: Sample-level metadata - result["data"] = self._get_sample_metadata( - repo_id, config_name, matching_field_values, token - ) - elif mode == "full_data": - # Mode 2: Full data with measurements - result["data"] = self._get_full_data( - repo_id, config_name, matching_field_values, token - ) - - results[repo_id] = result - - except Exception as e: - results[repo_id] = { - "included": False, - "reason": f"Error processing dataset: {str(e)}", - } - - return results - - def _check_repo_config_level( - self, - card: DataCard, - config_name: str, - repo_id: str, - ) -> tuple[bool, str]: - """ - Check if repo/config level conditions match filters (Level 1). - - :param card: DataCard instance - :param config_name: Configuration name - :param repo_id: Repository ID - :return: (included, reason) tuple - - """ - # Get repo and config level conditions - try: - conditions = card.get_experimental_conditions(config_name) - except DataCardError: - conditions = {} - - if not conditions: - # No repo/config conditions to check, include by default - return True, "" - - # Check each filter property at repo/config level - property_mappings = self._get_property_mappings(repo_id, config_name) - - for filter_prop, acceptable_values in self.filters.items(): - if filter_prop not in property_mappings: - continue - - mapping = property_mappings[filter_prop] - - # Check if this is a field-level mapping - # (should not be checked at repo level) - if "field" in mapping: - # Skip field-level properties at repo/config level - continue - - # Get path for this property - path = mapping["path"] - - # Prepend experimental_conditions for repo-level paths - full_path = f"experimental_conditions.{path}" - - # Try to get value at this path - value = get_nested_value(conditions, full_path) - - if value is None: - # Property not specified at repo/config level, skip - continue - - # Extract compound names if this is a compound list - if isinstance(value, list) and value and isinstance(value[0], dict): - actual_values = extract_compound_names(value) - else: - actual_values = [value] if not isinstance(value, list) else value - - # Check if any actual value matches acceptable values - matches = False - for actual in actual_values: - if actual in acceptable_values: - matches = True - break - - if not matches: - return ( - False, - f"{filter_prop}: found {actual_values}, " - f"wanted {acceptable_values}", - ) - - return True, "" - - def _check_field_level( - self, - card: DataCard, - config_name: str, - repo_id: str, - ) -> dict[str, list[str]]: - """ - Check field-level conditions and return matching field values (Level 2). - - :param card: DataCard instance - :param config_name: Configuration name - :param repo_id: Repository ID - :return: Dict mapping field names to lists of matching values - - """ - matching: dict[str, list[str]] = {} - property_mappings = self._get_property_mappings(repo_id, config_name) - - # Get config to find fields with role=experimental_condition - config = card.get_config(config_name) - if not config: - return matching - - # Group property mappings by field (if field is specified) - # field_mappings: {field_name: {prop: path, ...}} - field_mappings: dict[str, dict[str, str]] = {} - general_mappings: dict[str, str] = {} # Properties without field specification - - for prop, mapping in property_mappings.items(): - if "field" in mapping: - # Field-specific mapping - field_name = mapping["field"] - if field_name not in field_mappings: - field_mappings[field_name] = {} - field_mappings[field_name][prop] = mapping["path"] - else: - # General mapping (repo/config level) - general_mappings[prop] = mapping["path"] - - # Process each field that has mappings - for field_name, prop_paths in field_mappings.items(): - # Check if this field has definitions - definitions = card.get_field_definitions(config_name, field_name) - if not definitions: - continue - - matching_values = [] - - for field_value, definition in definitions.items(): - # Check if this field value matches all filter criteria - matches_all = True - - for filter_prop, acceptable_values in self.filters.items(): - if filter_prop not in prop_paths: - continue - - # Get path for this property - path = prop_paths[filter_prop] - - # Get value from this field value's definition - value = get_nested_value(definition, path) - - if value is None: - # Property not in this definition, doesn't match - matches_all = False - break - - # Extract compound names if needed - if isinstance(value, list) and value and isinstance(value[0], dict): - actual_values = extract_compound_names(value) - else: - actual_values = ( - [value] if not isinstance(value, list) else value - ) - - # Check if any actual value matches acceptable values - matches = False - for actual in actual_values: - if actual in acceptable_values: - matches = True - break - - if not matches: - matches_all = False - break - - if matches_all: - matching_values.append(field_value) - - if matching_values: - matching[field_name] = matching_values - - return matching - - def _get_sample_metadata( - self, - repo_id: str, - config_name: str, - matching_field_values: dict[str, list[str]], - token: str | None = None, - ) -> pd.DataFrame: - """ - Get sample-level metadata (Mode 1). - - Returns one row per sample_id with metadata columns. - - :param repo_id: Repository ID - :param config_name: Configuration name - :param matching_field_values: Dict of field names to matching values - :param token: Optional HuggingFace token - :return: DataFrame with sample metadata - - """ - # Initialize query API - api = HfQueryAPI(repo_id, token=token) - - # Build WHERE clause from matching field values - where_conditions = [] - for field_name, values in matching_field_values.items(): - if len(values) == 1: - # Single value: exact match - where_conditions.append(f"{field_name} = '{values[0]}'") - else: - # Multiple values: IN clause - values_str = "', '".join(values) - where_conditions.append(f"{field_name} IN ('{values_str}')") - - where_clause = " AND ".join(where_conditions) if where_conditions else "1=1" - - # Query for distinct sample_id with metadata fields - # Get all columns to understand structure - sql = f""" - SELECT DISTINCT * - FROM {config_name} - WHERE {where_clause} - """ - - try: - df = api.query(sql, config_name) - - # For sample-level, we want one row per sample_id - # Group by sample_id and take first value for other columns - if "sample_id" in df.columns: - # Get metadata columns (exclude measurement columns if possible) - # This is a heuristic - may need refinement - df_samples = df.groupby("sample_id").first().reset_index() - return df_samples - else: - # No sample_id column, return distinct rows - return df - - except Exception as e: - # Return error info - return pd.DataFrame( - {"error": [str(e)], "note": ["Failed to retrieve sample metadata"]} - ) - - def _get_full_data( - self, - repo_id: str, - config_name: str, - matching_field_values: dict[str, list[str]], - token: str | None = None, - ) -> pd.DataFrame: - """ - Get full data with all measurements (Mode 2). - - Returns many rows per sample_id (one per measured feature/target). - - :param repo_id: Repository ID - :param config_name: Configuration name - :param matching_field_values: Dict of field names to matching values - :param token: Optional HuggingFace token - :return: DataFrame with full data - - """ - # Initialize query API - api = HfQueryAPI(repo_id, token=token) - - # Build WHERE clause from matching field values - where_conditions = [] - for field_name, values in matching_field_values.items(): - if len(values) == 1: - # Single value: exact match - where_conditions.append(f"{field_name} = '{values[0]}'") - else: - # Multiple values: IN clause - values_str = "', '".join(values) - where_conditions.append(f"{field_name} IN ('{values_str}')") - - where_clause = " AND ".join(where_conditions) if where_conditions else "1=1" - - # Query for all data matching the conditions - sql = f""" - SELECT * - FROM {config_name} - WHERE {where_clause} - """ - - try: - return api.query(sql, config_name) - except Exception as e: - # Return error info - return pd.DataFrame( - {"error": [str(e)], "note": ["Failed to retrieve full data"]} - ) - - def __repr__(self) -> str: - """String representation.""" - n_filters = len(self.filters) - n_datasets = len(self.mappings) - return ( - f"DatasetFilterResolver({n_filters} filters, " - f"{n_datasets} datasets configured)" - ) diff --git a/tfbpapi/datainfo/metadata_builder.py b/tfbpapi/datainfo/metadata_builder.py new file mode 100644 index 0000000..66208eb --- /dev/null +++ b/tfbpapi/datainfo/metadata_builder.py @@ -0,0 +1,551 @@ +""" +Metadata builder for creating standardized tables across heterogeneous datasets. + +This module provides tools for building standardized metadata views by normalizing +factor levels across datasets with varying experimental condition structures. Users +specify optional alias mappings in external YAML files to standardize factor level +names (e.g., "D-glucose" -> "glucose"). + +Key Components: +- MetadataBuilder: Main class for building normalized metadata across datasets +- normalize_value(): Function for normalizing values using optional alias mappings +- Three output modes: conditions, samples, full_data +- External YAML configuration for aliases and property mappings + +Example Configuration: + ```yaml + factor_aliases: + carbon_source: + glucose: ["D-glucose", "dextrose"] + galactose: ["D-galactose", "Galactose"] + + BrentLab/harbison_2004: + dataset: + harbison_2004: + carbon_source: + field: condition + path: media.carbon_source + ``` + +Example Usage: + >>> builder = MetadataBuilder("metadata_config.yaml") + >>> results = builder.build_metadata( + ... repos=[("BrentLab/harbison_2004", "harbison_2004")], + ... mode="conditions" + ... ) + +""" + +from __future__ import annotations + +from pathlib import Path +from typing import Any, Optional + +import pandas as pd + +from tfbpapi.datainfo import DataCard +from tfbpapi.datainfo.metadata_config_models import MetadataConfig, PropertyMapping +from tfbpapi.errors import DataCardError +from tfbpapi.HfQueryAPI import HfQueryAPI + + +def get_nested_value(data: dict, path: str) -> Any: + """ + Navigate nested dict using dot notation. + + Handles missing intermediate keys gracefully by returning None. + + :param data: Dictionary to navigate + :param path: Dot-separated path (e.g., "media.carbon_source") + :return: Value at path or None if not found + + """ + if not isinstance(data, dict): + return None + + keys = path.split(".") + current = data + + for key in keys: + if not isinstance(current, dict) or key not in current: + return None + current = current[key] + + return current + + +def extract_compound_names(value: Any) -> list[str]: + """ + Extract compound names from various representations. + + Handles: + - List of dicts: [{"compound": "D-glucose", ...}] -> ["D-glucose"] + - String: "D-glucose" -> ["D-glucose"] + - None or "unspecified" -> [] + + :param value: Value to extract from + :return: List of compound names + + """ + if value is None or value == "unspecified": + return [] + + if isinstance(value, str): + return [value] + + if isinstance(value, list): + compounds = [] + for item in value: + if isinstance(item, dict) and "compound" in item: + compounds.append(item["compound"]) + elif isinstance(item, str): + compounds.append(item) + return compounds + + return [] + + +def normalize_value(actual_value: Any, aliases: dict[str, list[Any]] | None) -> str: + """ + Normalize a value using optional alias mappings (case-insensitive). + + Returns the alias name if a match is found, otherwise returns the + original value as a string. This enables standardizing factor level + names across heterogeneous datasets. + + :param actual_value: The value from the data to normalize + :param aliases: Optional dict mapping alias names to lists of actual values. + Example: {"glucose": ["D-glucose", "dextrose"]} + :return: Alias name if match found, otherwise str(actual_value) + + Examples: + # With aliases - exact match + normalize_value("D-glucose", {"glucose": ["D-glucose", "dextrose"]}) + -> "glucose" + + # With aliases - case-insensitive match + normalize_value("DEXTROSE", {"glucose": ["D-glucose", "dextrose"]}) + -> "glucose" + + # No alias match - pass through + normalize_value("maltose", {"glucose": ["D-glucose"]}) + -> "maltose" + + # No aliases provided - pass through + normalize_value("D-glucose", None) + -> "D-glucose" + + # Numeric value + normalize_value(30, {"thirty": [30, "30"]}) + -> "thirty" + + """ + if aliases is None: + return str(actual_value) + + # Convert to string for comparison (case-insensitive) + actual_str = str(actual_value).lower() + + # Check each alias mapping + for alias_name, actual_values in aliases.items(): + for val in actual_values: + if str(val).lower() == actual_str: + return alias_name + + # No match found - pass through original value + return str(actual_value) + + +class MetadataBuilder: + """ + Build standardized metadata tables across heterogeneous datasets. + + This class creates metadata views with normalized factor level names using + optional alias mappings. Unlike a filtering system, this includes ALL data + and simply normalizes the factor level names for standardization. + + Configuration specifies: + 1. Optional factor aliases (for normalizing factor level names) + 2. Repository and dataset-specific property paths (where to find each property) + + Configuration structure: + factor_aliases: # Optional + property_name: + alias_name: [actual_value1, actual_value2] + + BrentLab/repo_name: + # Repo-wide properties (apply to all datasets in this repository) + # Paths are relative to experimental_conditions at the repository level + property1: + path: media.name + + # Dataset-specific section + dataset: + dataset_name: + # Dataset-specific properties (apply only to this dataset) + # Paths are relative to experimental_conditions at the config level + property2: + path: temperature_celsius + + # Field-level properties (per-sample variation) + # Paths are relative to field definitions (NOT experimental_conditions) + property3: + field: condition + path: media.carbon_source + + Path Resolution: + - Repo-wide & dataset-specific: Paths automatically + prepended with "experimental_conditions." + Example: path "media.name" resolves to experimental_conditions.media.name + + - Field-level: Paths used directly on field definitions (no prepending) + Example: field "condition", path "media.carbon_source" + looks in condition field's definitions for media.carbon_source + + Three output modes: + - conditions: Extract normalized metadata (no data retrieval) + - samples: Sample-level metadata (one row per sample_id) + - full_data: All measurements with metadata + + Attributes: + config: Validated MetadataConfig instance + factor_aliases: Optional alias mappings for normalization + + """ + + def __init__(self, config_path: Path | str): + """ + Initialize metadata builder with external configuration. + + :param config_path: Path to YAML configuration file + :raises FileNotFoundError: If config file doesn't exist + :raises ValueError: If configuration is invalid + + """ + self.config = MetadataConfig.from_yaml(config_path) + self.factor_aliases = self.config.factor_aliases + + def _get_property_mappings( + self, repo_id: str, config_name: str + ) -> dict[str, PropertyMapping]: + """ + Get property mappings for a specific repo/dataset combination. + + Merges repo-wide and dataset-specific mappings, with dataset-specific taking + precedence. + + :param repo_id: Repository ID + :param config_name: Dataset/config name + :return: Dict mapping property names to PropertyMapping objects + + """ + return self.config.get_property_mappings(repo_id, config_name) + + def build_metadata( + self, + repos: list[tuple[str, str]], + mode: str = "conditions", + token: str | None = None, + ) -> dict[str, Any]: + """ + Build metadata tables across datasets with normalized factor levels. + + Note: ALL repositories are processed - no filtering/exclusion occurs. + Factor aliases are used to normalize factor level names, not to filter. + + :param repos: List of (repo_id, config_name) tuples to process + :param mode: Output mode - "conditions", "samples", or "full_data" + :param token: Optional HuggingFace token for private repos + :return: Dict mapping repo_id to metadata results + :raises ValueError: If mode is invalid + + """ + if mode not in ["conditions", "samples", "full_data"]: + raise ValueError( + f"Invalid mode: {mode}. Must be 'conditions', " + "'samples', or 'full_data'" + ) + + results = {} + + for repo_id, config_name in repos: + try: + # Load DataCard + card = DataCard(repo_id, token=token) + + # Check if this repository has configuration + if repo_id not in self.config.repositories: + results[repo_id] = { + "error": f"No property mappings defined for {repo_id}" + } + continue + + # Extract and normalize metadata for ALL data + metadata = self._extract_metadata(card, repo_id, config_name) + + # Build result based on mode + result = {"metadata": metadata} + + if mode == "conditions": + # Mode 0: Just metadata, no data + pass + elif mode == "samples": + # Mode 1: Sample-level metadata + result["data"] = self._get_sample_metadata( + repo_id, config_name, metadata, token + ) + elif mode == "full_data": + # Mode 2: Full data with measurements + result["data"] = self._get_full_data( + repo_id, config_name, metadata, token + ) + + results[repo_id] = result + + except Exception as e: + results[repo_id] = {"error": f"Error processing dataset: {str(e)}"} + + return results + + def _extract_metadata( + self, + card: DataCard, + repo_id: str, + config_name: str, + ) -> dict[str, Any]: + """ + Extract and normalize metadata from datacard. + + Extracts ALL metadata with normalized factor level names. + No filtering occurs - all data is included. + + :param card: DataCard instance + :param repo_id: Repository ID + :param config_name: Configuration name + :return: Dict with normalized metadata + + """ + metadata = {} + property_mappings = self._get_property_mappings(repo_id, config_name) + + # Extract repo/config level metadata + repo_metadata = self._extract_repo_level(card, config_name, property_mappings) + metadata.update(repo_metadata) + + # Extract field-level metadata + field_metadata = self._extract_field_level(card, config_name, property_mappings) + if field_metadata: + metadata["field_values"] = field_metadata + + return metadata + + def _extract_repo_level( + self, + card: DataCard, + config_name: str, + property_mappings: dict[str, PropertyMapping], + ) -> dict[str, list[str]]: + """ + Extract and normalize repo/config-level metadata. + + :param card: DataCard instance + :param config_name: Configuration name + :param property_mappings: Property mappings for this repo/dataset + :return: Dict mapping property names to normalized values + + """ + metadata = {} + + # Get repo and config level conditions + try: + conditions = card.get_experimental_conditions(config_name) + except DataCardError: + conditions = {} + + if not conditions: + return metadata + + # Extract each mapped property + for prop_name, mapping in property_mappings.items(): + # Skip field-level mappings (handled separately) + if mapping.field is not None: + continue + + # Get path for this property + path = mapping.path + full_path = f"experimental_conditions.{path}" + + # Get value at this path + value = get_nested_value(conditions, full_path) + + if value is None: + continue + + # Extract compound names if needed + if isinstance(value, list) and value and isinstance(value[0], dict): + actual_values = extract_compound_names(value) + else: + actual_values = [value] if not isinstance(value, list) else value + + # Normalize using aliases (if configured) + aliases = self.factor_aliases.get(prop_name) + normalized_values = [normalize_value(v, aliases) for v in actual_values] + + metadata[prop_name] = normalized_values + + return metadata + + def _extract_field_level( + self, + card: DataCard, + config_name: str, + property_mappings: dict[str, PropertyMapping], + ) -> dict[str, dict[str, Any]]: + """ + Extract and normalize field-level metadata. + + Returns metadata for ALL field values (no filtering). + + :param card: DataCard instance + :param config_name: Configuration name + :param property_mappings: Property mappings for this repo/dataset + :return: Dict mapping field values to their normalized metadata + + """ + field_metadata: dict[str, dict[str, Any]] = {} + + # Group property mappings by field + field_mappings: dict[str, dict[str, str]] = {} + for prop_name, mapping in property_mappings.items(): + if mapping.field is not None: + field_name = mapping.field + if field_name not in field_mappings: + field_mappings[field_name] = {} + field_mappings[field_name][prop_name] = mapping.path + + # Process each field that has mappings + for field_name, prop_paths in field_mappings.items(): + # Get field definitions + definitions = card.get_field_definitions(config_name, field_name) + if not definitions: + continue + + # Extract metadata for ALL field values (no filtering!) + for field_value, definition in definitions.items(): + if field_value not in field_metadata: + field_metadata[field_value] = {} + + for prop_name, path in prop_paths.items(): + # Get value at path + value = get_nested_value(definition, path) + + if value is None: + continue + + # Extract compound names if needed + if isinstance(value, list) and value and isinstance(value[0], dict): + actual_values = extract_compound_names(value) + else: + actual_values = ( + [value] if not isinstance(value, list) else value + ) + + # Normalize using aliases (if configured) + aliases = self.factor_aliases.get(prop_name) + normalized_values = [ + normalize_value(v, aliases) for v in actual_values + ] + + field_metadata[field_value][prop_name] = normalized_values + + return field_metadata + + def _get_sample_metadata( + self, + repo_id: str, + config_name: str, + metadata: dict[str, Any], + token: str | None = None, + ) -> pd.DataFrame: + """ + Get sample-level metadata (Mode 1). + + Returns one row per sample_id with metadata columns. Includes ALL samples (no + filtering). + + :param repo_id: Repository ID + :param config_name: Configuration name + :param metadata: Extracted metadata dict + :param token: Optional HuggingFace token + :return: DataFrame with sample metadata + + """ + # Initialize query API + api = HfQueryAPI(repo_id, token=token) + + # Query for all samples (no WHERE clause filtering) + sql = f""" + SELECT DISTINCT * + FROM {config_name} + """ + + try: + df = api.query(sql, config_name) + + # For sample-level, we want one row per sample_id + if "sample_id" in df.columns: + df_samples = df.groupby("sample_id").first().reset_index() + return df_samples + else: + # No sample_id column, return distinct rows + return df + + except Exception as e: + return pd.DataFrame( + {"error": [str(e)], "note": ["Failed to retrieve sample metadata"]} + ) + + def _get_full_data( + self, + repo_id: str, + config_name: str, + metadata: dict[str, Any], + token: str | None = None, + ) -> pd.DataFrame: + """ + Get full data with all measurements (Mode 2). + + Returns many rows per sample_id (one per measured feature/target). Includes ALL + data (no filtering). + + :param repo_id: Repository ID + :param config_name: Configuration name + :param metadata: Extracted metadata dict + :param token: Optional HuggingFace token + :return: DataFrame with full data + + """ + # Initialize query API + api = HfQueryAPI(repo_id, token=token) + + # Query for all data (no WHERE clause filtering) + sql = f""" + SELECT * + FROM {config_name} + """ + + try: + return api.query(sql, config_name) + except Exception as e: + return pd.DataFrame( + {"error": [str(e)], "note": ["Failed to retrieve full data"]} + ) + + def __repr__(self) -> str: + """String representation.""" + n_props = len(self.factor_aliases) + n_repos = len(self.config.repositories) + return ( + f"MetadataBuilder({n_props} properties with aliases, " + f"{n_repos} repositories configured)" + ) diff --git a/tfbpapi/datainfo/metadata_config_models.py b/tfbpapi/datainfo/metadata_config_models.py new file mode 100644 index 0000000..2cd4ded --- /dev/null +++ b/tfbpapi/datainfo/metadata_config_models.py @@ -0,0 +1,312 @@ +""" +Pydantic models for metadata normalization configuration. + +This module defines the schema for MetadataBuilder configuration files, providing +validation for factor alias mappings and repository configurations. + +""" + +from __future__ import annotations + +from pathlib import Path +from typing import Any, Dict, List, Optional + +import yaml # type: ignore[import-untyped] +from pydantic import BaseModel, Field, field_validator, model_validator + + +class PropertyMapping(BaseModel): + """ + Mapping specification for a single property. + + Attributes: + path: Dot-notation path to the property value. + For repo/config-level: relative to experimental_conditions + For field-level: relative to field definitions + field: Optional field name for field-level properties. + When specified, looks in this field's definitions. + When omitted, looks in repo/config-level experimental_conditions. + + Examples: + Field-level property: + PropertyMapping(field="condition", path="media.carbon_source") + + Repo/config-level property: + PropertyMapping(path="temperature_celsius") + + """ + + field: str | None = Field(None, description="Field name for field-level properties") + path: str = Field(..., min_length=1, description="Dot-notation path to property") + + @field_validator("path") + @classmethod + def validate_path(cls, v: str) -> str: + """Ensure path is not just whitespace.""" + if not v.strip(): + raise ValueError("path cannot be empty or whitespace") + return v.strip() + + @field_validator("field") + @classmethod + def validate_field(cls, v: str | None) -> str | None: + """Ensure field is not empty string if provided.""" + if v is not None and not v.strip(): + raise ValueError("field cannot be empty or whitespace") + return v.strip() if v else None + + +class RepositoryConfig(BaseModel): + """ + Configuration for a single repository. Eg BrentLab/harbison_2004. + + Attributes: + properties: Repo-wide property mappings that apply to all datasets + dataset: Dataset-specific property mappings (override repo-wide) + + Example: + ```python + config = RepositoryConfig( + properties={ + "temperature_celsius": PropertyMapping(path="temperature_celsius") + }, + dataset={ + "dataset_name": { + "carbon_source": PropertyMapping( + field="condition", + path="media.carbon_source" + ) + } + } + ) + ``` + + """ + + properties: dict[str, PropertyMapping] = Field( + default_factory=dict, description="Repo-wide property mappings" + ) + dataset: dict[str, dict[str, PropertyMapping]] | None = Field( + None, description="Dataset-specific property mappings" + ) + + @model_validator(mode="before") + @classmethod + def parse_structure(cls, data: Any) -> Any: + """Parse raw dict structure into typed PropertyMapping objects.""" + if not isinstance(data, dict): + return data + + # Extract and parse dataset section + dataset_section = data.get("dataset") + parsed_datasets: dict[str, dict[str, PropertyMapping]] | None = None + + if dataset_section: + if not isinstance(dataset_section, dict): + raise ValueError("'dataset' key must contain a dict") + + parsed_datasets = {} + for dataset_name, properties in dataset_section.items(): + if not isinstance(properties, dict): + raise ValueError( + f"Dataset '{dataset_name}' must contain a dict of properties" + ) + + # Parse each property mapping into PropertyMapping object + parsed_datasets[dataset_name] = {} + for prop_name, mapping in properties.items(): + try: + parsed_datasets[dataset_name][prop_name] = ( + PropertyMapping.model_validate(mapping) + ) + except Exception as e: + raise ValueError( + f"Invalid property '{prop_name}' in dataset " + f"'{dataset_name}': {e}" + ) from e + + # Parse repo-wide properties (all keys except 'dataset') + parsed_properties = {} + for key, value in data.items(): + if key == "dataset": + continue + + try: + parsed_properties[key] = PropertyMapping.model_validate(value) + except Exception as e: + raise ValueError(f"Invalid repo-wide property '{key}': {e}") from e + + return {"properties": parsed_properties, "dataset": parsed_datasets} + + +class MetadataConfig(BaseModel): + """ + Configuration for building standardized metadata tables. + + Specifies optional alias mappings for normalizing factor levels across + heterogeneous datasets, plus property path mappings for each repository. + + Attributes: + factor_aliases: Optional mappings of standardized names to actual values. + Example: {"carbon_source": {"glucose": ["D-glucose", "dextrose"]}} + repositories: Dict mapping repository IDs to their configurations + + Example: + ```yaml + factor_aliases: + carbon_source: + glucose: ["D-glucose", "dextrose"] + galactose: ["D-galactose", "Galactose"] + + BrentLab/harbison_2004: + dataset: + harbison_2004: + carbon_source: + field: condition + path: media.carbon_source + + BrentLab/kemmeren_2014: + temperature: + path: temperature_celsius + dataset: + kemmeren_2014: + carbon_source: + path: media.carbon_source + ``` + + """ + + factor_aliases: dict[str, dict[str, list[Any]]] = Field( + default_factory=dict, + description="Optional alias mappings for normalizing factor levels", + ) + repositories: dict[str, RepositoryConfig] = Field( + ..., description="Repository configurations keyed by repo ID" + ) + + @field_validator("factor_aliases") + @classmethod + def validate_factor_aliases( + cls, v: dict[str, dict[str, list[Any]]] + ) -> dict[str, dict[str, list[Any]]]: + """Validate factor alias structure.""" + # Empty is OK - aliases are optional + if not v: + return v + + for prop_name, aliases in v.items(): + if not isinstance(aliases, dict): + raise ValueError( + f"Property '{prop_name}' aliases must be a dict, " + f"got {type(aliases).__name__}" + ) + + # Validate each alias mapping + for alias_name, actual_values in aliases.items(): + if not isinstance(actual_values, list): + raise ValueError( + f"Alias '{alias_name}' for '{prop_name}' must map " + f"to a list of values" + ) + if not actual_values: + raise ValueError( + f"Alias '{alias_name}' for '{prop_name}' cannot " + f"have empty value list" + ) + for val in actual_values: + if not isinstance(val, (str, int, float, bool)): + raise ValueError( + f"Alias '{alias_name}' for '{prop_name}' contains " + f"invalid value type: {type(val).__name__}" + ) + + return v + + @model_validator(mode="before") + @classmethod + def parse_repositories(cls, data: Any) -> Any: + """Parse repository configurations from top-level keys.""" + if not isinstance(data, dict): + return data + + # Extract repositories (all keys except 'factor_aliases') + repositories = {} + for key, value in data.items(): + if key != "factor_aliases": + try: + repositories[key] = RepositoryConfig.model_validate(value) + except Exception as e: + raise ValueError( + f"Invalid configuration for repository '{key}': {e}" + ) from e + + if not repositories: + raise ValueError( + "Configuration must have at least one repository configuration" + ) + + return { + "factor_aliases": data.get("factor_aliases", {}), + "repositories": repositories, + } + + @classmethod + def from_yaml(cls, path: Path | str) -> MetadataConfig: + """ + Load and validate configuration from YAML file. + + :param path: Path to YAML configuration file + :return: Validated MetadataConfig instance + :raises FileNotFoundError: If file doesn't exist + :raises ValueError: If configuration is invalid + + """ + path = Path(path) + + if not path.exists(): + raise FileNotFoundError(f"Configuration file not found: {path}") + + with open(path) as f: + data = yaml.safe_load(f) + + if not isinstance(data, dict): + raise ValueError("Configuration must be a YAML dict") + + return cls.model_validate(data) + + def get_repository_config(self, repo_id: str) -> RepositoryConfig | None: + """ + Get configuration for a specific repository. + + :param repo_id: Repository ID (e.g., "BrentLab/harbison_2004") + :return: RepositoryConfig instance or None if not found + + """ + return self.repositories.get(repo_id) + + def get_property_mappings( + self, repo_id: str, config_name: str + ) -> dict[str, PropertyMapping]: + """ + Get merged property mappings for a repo/dataset combination. + + Merges repo-wide and dataset-specific mappings, with dataset-specific taking + precedence. + + :param repo_id: Repository ID + :param config_name: Dataset/config name + :return: Dict mapping property names to PropertyMapping objects + + """ + repo_config = self.get_repository_config(repo_id) + if not repo_config: + return {} + + # Start with repo-wide properties + mappings: dict[str, PropertyMapping] = dict(repo_config.properties) + + # Override with dataset-specific properties + if repo_config.dataset and config_name in repo_config.dataset: + mappings.update(repo_config.dataset[config_name]) + + return mappings diff --git a/tfbpapi/tests/datainfo/test_filter_resolver.py b/tfbpapi/tests/datainfo/test_filter_resolver.py deleted file mode 100644 index a56d888..0000000 --- a/tfbpapi/tests/datainfo/test_filter_resolver.py +++ /dev/null @@ -1,494 +0,0 @@ -"""Tests for DatasetFilterResolver.""" - -import pandas as pd -import pytest - -from tfbpapi.datainfo.filter_resolver import ( - DatasetFilterResolver, - extract_compound_names, - get_nested_value, -) - - -class TestHelperFunctions: - """Test helper functions.""" - - def test_get_nested_value_simple(self): - """Test getting nested values with dot notation.""" - data = { - "environmental_conditions": { - "media": {"name": "YPD", "carbon_source": [{"compound": "D-glucose"}]}, - "temperature_celsius": 30, - } - } - - assert ( - get_nested_value(data, "environmental_conditions.temperature_celsius") == 30 - ) - assert get_nested_value(data, "environmental_conditions.media.name") == "YPD" - assert get_nested_value(data, "nonexistent.path") is None - assert ( - get_nested_value(data, "environmental_conditions.media.nonexistent") is None - ) - - def test_get_nested_value_no_fallback(self): - """Test that paths must be correct - no automatic fallback.""" - # Field-level definition (no experimental_conditions wrapper) - field_def = { - "media": {"name": "YPD", "carbon_source": [{"compound": "D-glucose"}]}, - "temperature_celsius": 30, - } - - # Wrong path should return None (no fallback) - assert get_nested_value(field_def, "experimental_conditions.media.name") is None - assert ( - get_nested_value(field_def, "experimental_conditions.temperature_celsius") - is None - ) - - # Correct direct paths work - assert get_nested_value(field_def, "media.name") == "YPD" - assert get_nested_value(field_def, "temperature_celsius") == 30 - - # Repo-level definition (with experimental_conditions wrapper) - repo_def = { - "experimental_conditions": { - "media": {"name": "SC", "carbon_source": [{"compound": "D-glucose"}]}, - "temperature_celsius": 30, - } - } - - # Full path works - assert get_nested_value(repo_def, "experimental_conditions.media.name") == "SC" - assert ( - get_nested_value(repo_def, "experimental_conditions.temperature_celsius") - == 30 - ) - - # Shortened path does NOT work (no fallback) - assert get_nested_value(repo_def, "media.name") is None - assert get_nested_value(repo_def, "temperature_celsius") is None - - def test_extract_compound_names(self): - """Test extracting compound names from various formats.""" - # List of dicts - value1 = [ - {"compound": "D-glucose", "concentration_percent": 2}, - {"compound": "D-galactose", "concentration_percent": 1}, - ] - assert extract_compound_names(value1) == ["D-glucose", "D-galactose"] - - # String - assert extract_compound_names("D-glucose") == ["D-glucose"] - - # None - assert extract_compound_names(None) == [] - - # "unspecified" - assert extract_compound_names("unspecified") == [] - - # Empty list - assert extract_compound_names([]) == [] - - -class TestDatasetFilterResolver: - """Test DatasetFilterResolver class.""" - - def test_init(self, write_config): - """Test initialization.""" - config = { - "filters": { - "carbon_source": ["D-glucose", "D-galactose"], - "temperature_celsius": [30], - }, - "BrentLab/harbison_2004": { - "dataset": { - "harbison_2004": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source", - }, - "temperature_celsius": { - "field": "condition", - "path": "temperature_celsius", - }, - } - } - }, - } - config_file = write_config(config) - resolver = DatasetFilterResolver(config_file) - - assert len(resolver.filters) == 2 - assert "carbon_source" in resolver.filters - assert resolver.filters["carbon_source"] == ["D-glucose", "D-galactose"] - assert len(resolver.mappings) == 1 - assert "BrentLab/harbison_2004" in resolver.mappings - - def test_init_missing_file(self): - """Test initialization with missing config file.""" - with pytest.raises(FileNotFoundError): - DatasetFilterResolver("nonexistent.yaml") - - def test_resolve_filters_mode_conditions(self, write_config): - """Test resolve_filters in conditions mode.""" - config = { - "filters": { - "carbon_source": ["D-glucose", "D-galactose"], - "temperature_celsius": [30], - }, - "BrentLab/harbison_2004": { - "dataset": { - "harbison_2004": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source", - }, - "temperature_celsius": { - "field": "condition", - "path": "temperature_celsius", - }, - } - } - }, - } - config_file = write_config(config) - resolver = DatasetFilterResolver(config_file) - - # This will actually try to load the DataCard, - # so it's more of an integration test For now, test the structure - results = resolver.resolve_filters( - repos=[("BrentLab/harbison_2004", "harbison_2004")], mode="conditions" - ) - - assert "BrentLab/harbison_2004" in results - result = results["BrentLab/harbison_2004"] - - # Should have included field - assert "included" in result - - # If included, should have matching_field_values - if result["included"]: - assert "matching_field_values" in result - - def test_resolve_filters_invalid_mode(self, write_config): - """Test resolve_filters with invalid mode.""" - config = { - "filters": { - "carbon_source": ["D-glucose", "D-galactose"], - "temperature_celsius": [30], - }, - "BrentLab/harbison_2004": { - "dataset": { - "harbison_2004": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source", - }, - "temperature_celsius": { - "field": "condition", - "path": "temperature_celsius", - }, - } - } - }, - } - config_file = write_config(config) - resolver = DatasetFilterResolver(config_file) - - with pytest.raises(ValueError, match="Invalid mode"): - resolver.resolve_filters( - repos=[("BrentLab/harbison_2004", "harbison_2004")], mode="invalid" - ) - - def test_repr(self, write_config): - """Test string representation.""" - config = { - "filters": { - "carbon_source": ["D-glucose", "D-galactose"], - "temperature_celsius": [30], - }, - "BrentLab/harbison_2004": { - "dataset": { - "harbison_2004": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source", - }, - "temperature_celsius": { - "field": "condition", - "path": "temperature_celsius", - }, - } - } - }, - } - config_file = write_config(config) - resolver = DatasetFilterResolver(config_file) - repr_str = repr(resolver) - - assert "DatasetFilterResolver" in repr_str - assert "2 filters" in repr_str - assert "1 datasets" in repr_str - - -class TestRealDataCards: - """Integration tests with real datacards.""" - - def test_harbison_2004_glucose_filter(self, harbison_2004_datacard, write_config): - """Test filtering harbison_2004 for glucose samples.""" - config = { - "filters": {"carbon_source": ["D-glucose"]}, - "BrentLab/harbison_2004": { - "dataset": { - "harbison_2004": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source", - } - } - } - }, - } - config_path = write_config(config) - resolver = DatasetFilterResolver(config_path) - results = resolver.resolve_filters( - repos=[("BrentLab/harbison_2004", "harbison_2004")], mode="conditions" - ) - - assert "BrentLab/harbison_2004" in results - result = results["BrentLab/harbison_2004"] - - # Should be included - assert result["included"] is True - - # Should have matching field values - assert "matching_field_values" in result - - # Should have condition field with some matching values - if "condition" in result["matching_field_values"]: - matching = result["matching_field_values"]["condition"] - # YPD, HEAT, H2O2Hi, H2O2Lo, Acid, Alpha, BUT14, BUT90 all have D-glucose - expected_glucose_conditions = [ - "YPD", - "HEAT", - "H2O2Hi", - "H2O2Lo", - "Acid", - "Alpha", - "BUT14", - "BUT90", - ] - for cond in expected_glucose_conditions: - assert cond in matching, f"{cond} should be in matching conditions" - - def test_harbison_2004_galactose_filter(self, harbison_2004_datacard, write_config): - """Test filtering harbison_2004 for galactose samples.""" - config = { - "filters": {"carbon_source": ["D-galactose"]}, - "BrentLab/harbison_2004": { - "dataset": { - "harbison_2004": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source", - } - } - } - }, - } - config_path = write_config(config) - resolver = DatasetFilterResolver(config_path) - results = resolver.resolve_filters( - repos=[("BrentLab/harbison_2004", "harbison_2004")], mode="conditions" - ) - - result = results["BrentLab/harbison_2004"] - assert result["included"] is True - - # GAL condition has D-galactose - if "condition" in result["matching_field_values"]: - matching = result["matching_field_values"]["condition"] - assert "GAL" in matching - - def test_harbison_2004_samples_mode(self, harbison_2004_datacard, write_config): - """Test retrieving sample-level metadata.""" - config = { - "filters": {"carbon_source": ["D-glucose"]}, - "BrentLab/harbison_2004": { - "dataset": { - "harbison_2004": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source", - } - } - } - }, - } - config_path = write_config(config) - resolver = DatasetFilterResolver(config_path) - results = resolver.resolve_filters( - repos=[("BrentLab/harbison_2004", "harbison_2004")], mode="samples" - ) - - result = results["BrentLab/harbison_2004"] - assert result["included"] is True - assert "data" in result - - # Should have a DataFrame - df = result["data"] - assert isinstance(df, pd.DataFrame) - assert len(df) > 0 # Should have some samples - - def test_harbison_2004_full_data_mode(self, harbison_2004_datacard, write_config): - """Test retrieving full data.""" - config = { - "filters": {"carbon_source": ["D-glucose"]}, - "BrentLab/harbison_2004": { - "dataset": { - "harbison_2004": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source", - } - } - } - }, - } - config_path = write_config(config) - resolver = DatasetFilterResolver(config_path) - results = resolver.resolve_filters( - repos=[("BrentLab/harbison_2004", "harbison_2004")], mode="full_data" - ) - - result = results["BrentLab/harbison_2004"] - assert result["included"] is True - assert "data" in result - - # Should have a DataFrame - df = result["data"] - assert isinstance(df, pd.DataFrame) - assert len(df) > 0 # Should have data rows - - def test_hackett_2020_glucose_filter(self, hackett_2020_datacard, write_config): - """Test filtering hackett_2020 for glucose samples.""" - config = { - "filters": {"carbon_source": ["D-glucose"]}, - "BrentLab/hackett_2020": { - "dataset": { - "hackett_2020": {"carbon_source": {"path": "media.carbon_source"}} - } - }, - } - config_path = write_config(config) - resolver = DatasetFilterResolver(config_path) - results = resolver.resolve_filters( - repos=[("BrentLab/hackett_2020", "hackett_2020")], mode="conditions" - ) - - assert "BrentLab/hackett_2020" in results - result = results["BrentLab/hackett_2020"] - - # Should be included (hackett_2020 has D-glucose at repo level) - assert result["included"] is True - - def test_kemmeren_2014_glucose_filter(self, kemmeren_2014_datacard, write_config): - """Test filtering kemmeren_2014 for glucose samples.""" - config = { - "filters": {"carbon_source": ["D-glucose"]}, - "BrentLab/kemmeren_2014": { - "dataset": { - "kemmeren_2014": {"carbon_source": {"path": "media.carbon_source"}} - } - }, - } - config_path = write_config(config) - resolver = DatasetFilterResolver(config_path) - results = resolver.resolve_filters( - repos=[("BrentLab/kemmeren_2014", "kemmeren_2014")], mode="conditions" - ) - - assert "BrentLab/kemmeren_2014" in results - result = results["BrentLab/kemmeren_2014"] - - # Should be included (kemmeren_2014 has D-glucose at repo level) - assert result["included"] is True - - def test_kemmeren_2014_temperature_filter( - self, kemmeren_2014_datacard, write_config - ): - """Test filtering kemmeren_2014 for temperature.""" - config = { - "filters": {"temperature_celsius": [30]}, - "BrentLab/kemmeren_2014": { - "dataset": { - "kemmeren_2014": { - "temperature_celsius": {"path": "temperature_celsius"} - } - } - }, - } - config_path = write_config(config) - resolver = DatasetFilterResolver(config_path) - results = resolver.resolve_filters( - repos=[("BrentLab/kemmeren_2014", "kemmeren_2014")], mode="conditions" - ) - - assert "BrentLab/kemmeren_2014" in results - result = results["BrentLab/kemmeren_2014"] - - # Should be included (kemmeren_2014 has temperature_celsius: 30 at repo level) - assert result["included"] is True - - def test_multi_repo_glucose_filter( - self, - harbison_2004_datacard, - hackett_2020_datacard, - kemmeren_2014_datacard, - write_config, - ): - """Test filtering D-glucose across multiple repos.""" - config = { - "filters": {"carbon_source": ["D-glucose"]}, - "BrentLab/harbison_2004": { - "dataset": { - "harbison_2004": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source", - } - } - } - }, - "BrentLab/hackett_2020": { - "dataset": { - "hackett_2020": {"carbon_source": {"path": "media.carbon_source"}} - } - }, - "BrentLab/kemmeren_2014": { - "dataset": { - "kemmeren_2014": {"carbon_source": {"path": "media.carbon_source"}} - } - }, - } - config_path = write_config(config) - resolver = DatasetFilterResolver(config_path) - results = resolver.resolve_filters( - repos=[ - ("BrentLab/harbison_2004", "harbison_2004"), - ("BrentLab/hackett_2020", "hackett_2020"), - ("BrentLab/kemmeren_2014", "kemmeren_2014"), - ], - mode="conditions", - ) - - # All three repos should be included - assert "BrentLab/harbison_2004" in results - assert results["BrentLab/harbison_2004"]["included"] is True - - assert "BrentLab/hackett_2020" in results - assert results["BrentLab/hackett_2020"]["included"] is True - - assert "BrentLab/kemmeren_2014" in results - assert results["BrentLab/kemmeren_2014"]["included"] is True diff --git a/tfbpapi/tests/datainfo/test_metadata_builder.py b/tfbpapi/tests/datainfo/test_metadata_builder.py new file mode 100644 index 0000000..1419264 --- /dev/null +++ b/tfbpapi/tests/datainfo/test_metadata_builder.py @@ -0,0 +1,332 @@ +""" +Tests for MetadataBuilder. + +Tests normalization logic, metadata extraction, and builder functionality. + +""" + +from pathlib import Path + +import pytest +import yaml + +from tfbpapi.datainfo.metadata_builder import ( + MetadataBuilder, + extract_compound_names, + get_nested_value, + normalize_value, +) + + +class TestGetNestedValue: + """Test get_nested_value function.""" + + def test_simple_path(self): + """Test simple one-level path.""" + data = {"temperature": 30} + assert get_nested_value(data, "temperature") == 30 + + def test_nested_path(self): + """Test multi-level nested path.""" + data = {"media": {"carbon_source": "D-glucose"}} + assert get_nested_value(data, "media.carbon_source") == "D-glucose" + + def test_deeply_nested_path(self): + """Test deeply nested path.""" + data = {"level1": {"level2": {"level3": {"value": 42}}}} + assert get_nested_value(data, "level1.level2.level3.value") == 42 + + def test_missing_key(self): + """Test that missing keys return None.""" + data = {"temperature": 30} + assert get_nested_value(data, "pressure") is None + + def test_missing_intermediate_key(self): + """Test that missing intermediate keys return None.""" + data = {"media": {"temperature": 30}} + assert get_nested_value(data, "media.carbon_source.compound") is None + + def test_non_dict_input(self): + """Test that non-dict input returns None.""" + assert get_nested_value("not a dict", "path") is None + assert get_nested_value(None, "path") is None + + +class TestExtractCompoundNames: + """Test extract_compound_names function.""" + + def test_list_of_dicts(self): + """Test extracting from list of dicts with compound field.""" + value = [ + {"compound": "D-glucose", "concentration_percent": 2}, + {"compound": "D-galactose", "concentration_percent": 1}, + ] + assert extract_compound_names(value) == ["D-glucose", "D-galactose"] + + def test_string_value(self): + """Test extracting from simple string.""" + assert extract_compound_names("D-glucose") == ["D-glucose"] + + def test_none_value(self): + """Test that None returns empty list.""" + assert extract_compound_names(None) == [] + + def test_unspecified_value(self): + """Test that 'unspecified' returns empty list.""" + assert extract_compound_names("unspecified") == [] + + def test_list_of_strings(self): + """Test extracting from list of strings.""" + assert extract_compound_names(["glucose", "galactose"]) == [ + "glucose", + "galactose", + ] + + def test_mixed_list(self): + """Test extracting from mixed list.""" + value = [ + {"compound": "D-glucose"}, + "maltose", + ] + assert extract_compound_names(value) == ["D-glucose", "maltose"] + + +class TestNormalizeValue: + """Test normalize_value function.""" + + def test_normalize_with_alias_match(self): + """Test normalization when value matches an alias.""" + aliases = {"glucose": ["D-glucose", "dextrose"]} + assert normalize_value("D-glucose", aliases) == "glucose" + assert normalize_value("dextrose", aliases) == "glucose" + + def test_normalize_case_insensitive(self): + """Test that matching is case-insensitive.""" + aliases = {"glucose": ["D-glucose"]} + assert normalize_value("d-glucose", aliases) == "glucose" + assert normalize_value("D-GLUCOSE", aliases) == "glucose" + assert normalize_value("D-Glucose", aliases) == "glucose" + + def test_normalize_no_match_passthrough(self): + """Test pass-through when no alias matches.""" + aliases = {"glucose": ["D-glucose"]} + assert normalize_value("maltose", aliases) == "maltose" + assert normalize_value("galactose", aliases) == "galactose" + + def test_normalize_no_aliases(self): + """Test pass-through when no aliases provided.""" + assert normalize_value("D-glucose", None) == "D-glucose" + assert normalize_value("maltose", None) == "maltose" + + def test_normalize_empty_aliases(self): + """Test pass-through when empty aliases dict.""" + assert normalize_value("D-glucose", {}) == "D-glucose" + + def test_normalize_numeric_values(self): + """Test normalization with numeric actual values.""" + aliases = {"thirty": [30, "30"]} + assert normalize_value(30, aliases) == "thirty" + assert normalize_value("30", aliases) == "thirty" + + def test_normalize_numeric_passthrough(self): + """Test that unmatched numeric values pass through as strings.""" + aliases = {"thirty": [30]} + assert normalize_value(37, aliases) == "37" + + def test_normalize_boolean_values(self): + """Test normalization with boolean values.""" + aliases = {"present": [True, "true", "True"]} + assert normalize_value(True, aliases) == "present" + # Note: bool to str conversion makes this "True" + assert normalize_value("true", aliases) == "present" + + def test_normalize_multiple_aliases(self): + """Test with multiple alias mappings.""" + aliases = { + "glucose": ["D-glucose", "dextrose"], + "galactose": ["D-galactose", "Galactose"], + } + assert normalize_value("D-glucose", aliases) == "glucose" + assert normalize_value("DEXTROSE", aliases) == "glucose" + assert normalize_value("d-galactose", aliases) == "galactose" + assert normalize_value("Galactose", aliases) == "galactose" + # No match + assert normalize_value("maltose", aliases) == "maltose" + + +@pytest.fixture +def write_config(tmp_path): + """Fixture to write YAML config files.""" + + def _write(config_data): + config_path = tmp_path / "config.yaml" + with open(config_path, "w") as f: + yaml.dump(config_data, f) + return config_path + + return _write + + +class TestMetadataBuilder: + """Test MetadataBuilder class.""" + + def test_init_with_aliases(self, write_config): + """Test initialization with factor aliases.""" + config = { + "factor_aliases": {"carbon_source": {"glucose": ["D-glucose", "dextrose"]}}, + "BrentLab/test": { + "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + }, + } + config_file = write_config(config) + builder = MetadataBuilder(config_file) + + assert len(builder.factor_aliases) == 1 + assert "carbon_source" in builder.factor_aliases + assert "glucose" in builder.factor_aliases["carbon_source"] + + def test_init_without_aliases(self, write_config): + """Test initialization without factor aliases.""" + config = { + "BrentLab/test": { + "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + } + } + config_file = write_config(config) + builder = MetadataBuilder(config_file) + + assert builder.factor_aliases == {} + + def test_invalid_mode(self, write_config): + """Test that invalid mode raises ValueError.""" + config = { + "BrentLab/test": { + "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + } + } + config_file = write_config(config) + builder = MetadataBuilder(config_file) + + with pytest.raises(ValueError) as exc_info: + builder.build_metadata( + repos=[("BrentLab/test", "test")], mode="invalid_mode" + ) + assert "Invalid mode" in str(exc_info.value) + + def test_build_metadata_missing_repo_config(self, write_config): + """Test handling of missing repository configuration.""" + config = { + "BrentLab/test": { + "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + } + } + config_file = write_config(config) + builder = MetadataBuilder(config_file) + + results = builder.build_metadata( + repos=[("BrentLab/missing", "dataset")], mode="conditions" + ) + + assert "BrentLab/missing" in results + assert "error" in results["BrentLab/missing"] + assert "No property mappings" in results["BrentLab/missing"]["error"] + + def test_repr(self, write_config): + """Test string representation.""" + config = { + "factor_aliases": { + "carbon_source": {"glucose": ["D-glucose"]}, + "temperature": {"thirty": [30]}, + }, + "BrentLab/test1": { + "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + }, + "BrentLab/test2": { + "dataset": {"test": {"temperature": {"path": "temperature_celsius"}}} + }, + } + config_file = write_config(config) + builder = MetadataBuilder(config_file) + + repr_str = repr(builder) + assert "MetadataBuilder" in repr_str + assert "2 properties" in repr_str + assert "2 repositories" in repr_str + + def test_repr_no_aliases(self, write_config): + """Test string representation with no aliases.""" + config = { + "BrentLab/test": { + "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + } + } + config_file = write_config(config) + builder = MetadataBuilder(config_file) + + repr_str = repr(builder) + assert "MetadataBuilder" in repr_str + assert "0 properties" in repr_str + assert "1 repositories" in repr_str + + +class TestMetadataBuilderIntegration: + """Integration tests with real datacards (if available).""" + + def test_build_metadata_conditions_mode(self, write_config): + """Test building metadata in conditions mode.""" + # This is a minimal test that doesn't require actual datacards + # In practice, you'd use real datacards from HuggingFace + config = { + "factor_aliases": {"carbon_source": {"glucose": ["D-glucose"]}}, + "BrentLab/test": { + "dataset": { + "test": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source", + } + } + } + }, + } + config_file = write_config(config) + builder = MetadataBuilder(config_file) + + # This would fail without a real datacard, but tests the structure + # In actual usage, you'd mock DataCard or use real repos + # For now, just verify the method exists and can be called + assert hasattr(builder, "build_metadata") + assert callable(builder.build_metadata) + + def test_get_property_mappings(self, write_config): + """Test _get_property_mappings method.""" + config = { + "BrentLab/test": { + "temperature": {"path": "temperature_celsius"}, + "dataset": { + "test_dataset": {"carbon_source": {"path": "media.carbon_source"}} + }, + } + } + config_file = write_config(config) + builder = MetadataBuilder(config_file) + + mappings = builder._get_property_mappings("BrentLab/test", "test_dataset") + + assert "temperature" in mappings + assert "carbon_source" in mappings + assert mappings["temperature"].path == "temperature_celsius" + assert mappings["carbon_source"].path == "media.carbon_source" + + def test_get_property_mappings_missing_repo(self, write_config): + """Test _get_property_mappings with missing repository.""" + config = { + "BrentLab/test": { + "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + } + } + config_file = write_config(config) + builder = MetadataBuilder(config_file) + + mappings = builder._get_property_mappings("BrentLab/missing", "dataset") + assert mappings == {} diff --git a/tfbpapi/tests/datainfo/test_metadata_config_models.py b/tfbpapi/tests/datainfo/test_metadata_config_models.py new file mode 100644 index 0000000..9a82598 --- /dev/null +++ b/tfbpapi/tests/datainfo/test_metadata_config_models.py @@ -0,0 +1,386 @@ +""" +Tests for metadata configuration Pydantic models. + +Tests validation, error messages, and config loading for MetadataBuilder. + +""" + +from pathlib import Path + +import pytest +import yaml +from pydantic import ValidationError + +from tfbpapi.datainfo.metadata_config_models import ( + MetadataConfig, + PropertyMapping, + RepositoryConfig, +) + + +class TestPropertyMapping: + """Tests for PropertyMapping model.""" + + def test_valid_field_level_mapping(self): + """Test valid field-level property mapping.""" + mapping = PropertyMapping(field="condition", path="media.carbon_source") + assert mapping.field == "condition" + assert mapping.path == "media.carbon_source" + + def test_valid_repo_level_mapping(self): + """Test valid repo-level property mapping (no field).""" + mapping = PropertyMapping(path="temperature_celsius") + assert mapping.field is None + assert mapping.path == "temperature_celsius" + + def test_invalid_empty_path(self): + """Test that empty path is rejected.""" + with pytest.raises(ValidationError) as exc_info: + PropertyMapping(path="") + # Pydantic catches this with min_length=1 before our custom validator + assert "at least 1 character" in str(exc_info.value) + + def test_invalid_whitespace_path(self): + """Test that whitespace-only path is rejected.""" + with pytest.raises(ValidationError) as exc_info: + PropertyMapping(path=" ") + assert "path cannot be empty" in str(exc_info.value) + + def test_invalid_empty_field(self): + """Test that empty field string is rejected.""" + with pytest.raises(ValidationError) as exc_info: + PropertyMapping(field="", path="media.carbon_source") + assert "field cannot be empty" in str(exc_info.value) + + def test_path_whitespace_stripped(self): + """Test that path whitespace is stripped.""" + mapping = PropertyMapping(path=" media.carbon_source ") + assert mapping.path == "media.carbon_source" + + +class TestRepositoryConfig: + """Tests for RepositoryConfig model.""" + + def test_valid_repo_config_with_datasets(self): + """Test valid repository config with dataset section.""" + config_data = { + "temperature_celsius": {"path": "temperature_celsius"}, + "dataset": { + "dataset1": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source", + } + } + }, + } + config = RepositoryConfig.model_validate(config_data) + assert config.dataset is not None + assert "dataset1" in config.dataset + + def test_valid_repo_config_no_datasets(self): + """Test valid repository config without dataset section.""" + config_data = {"temperature_celsius": {"path": "temperature_celsius"}} + config = RepositoryConfig.model_validate(config_data) + assert config.dataset is None + + def test_invalid_dataset_not_dict(self): + """Test that dataset section must be a dict.""" + config_data = {"dataset": "not a dict"} + with pytest.raises(ValidationError) as exc_info: + RepositoryConfig.model_validate(config_data) + assert "'dataset' key must contain a dict" in str(exc_info.value) + + def test_invalid_property_missing_path(self): + """Test that properties must have 'path' field.""" + config_data = { + "dataset": {"dataset1": {"carbon_source": {"field": "condition"}}} + } + with pytest.raises(ValidationError) as exc_info: + RepositoryConfig.model_validate(config_data) + # Pydantic's Field required error comes before our custom validation + assert "Field required" in str( + exc_info.value + ) or "missing required 'path' field" in str(exc_info.value) + + def test_invalid_repo_wide_property_missing_path(self): + """Test that repo-wide properties must have 'path' field.""" + config_data = {"temperature_celsius": {"field": "something"}} + with pytest.raises(ValidationError) as exc_info: + RepositoryConfig.model_validate(config_data) + # Pydantic's Field required error comes before our custom validation + assert "Field required" in str( + exc_info.value + ) or "missing required 'path' field" in str(exc_info.value) + + +class TestMetadataConfig: + """Tests for MetadataConfig model.""" + + def test_valid_config_with_aliases(self, tmp_path): + """Test valid config with factor aliases.""" + config_data = { + "factor_aliases": { + "carbon_source": { + "glucose": ["D-glucose", "dextrose"], + "galactose": ["D-galactose", "Galactose"], + } + }, + "BrentLab/test": { + "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + }, + } + + config_path = tmp_path / "config.yaml" + with open(config_path, "w") as f: + yaml.dump(config_data, f) + + config = MetadataConfig.from_yaml(config_path) + assert "carbon_source" in config.factor_aliases + assert "glucose" in config.factor_aliases["carbon_source"] + assert config.factor_aliases["carbon_source"]["glucose"] == [ + "D-glucose", + "dextrose", + ] + + def test_valid_config_without_aliases(self, tmp_path): + """Test that factor_aliases is optional.""" + config_data = { + "BrentLab/test": { + "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + } + } + + config_path = tmp_path / "config.yaml" + with open(config_path, "w") as f: + yaml.dump(config_data, f) + + config = MetadataConfig.from_yaml(config_path) + assert config.factor_aliases == {} + + def test_valid_config_empty_aliases(self, tmp_path): + """Test that empty factor_aliases dict is allowed.""" + config_data = { + "factor_aliases": {}, + "BrentLab/test": { + "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + }, + } + + config_path = tmp_path / "config.yaml" + with open(config_path, "w") as f: + yaml.dump(config_data, f) + + config = MetadataConfig.from_yaml(config_path) + assert config.factor_aliases == {} + + def test_invalid_alias_not_dict(self): + """Test that property aliases must be a dict.""" + config_data = { + "factor_aliases": { + "carbon_source": ["D-glucose"] # Should be dict, not list + }, + "BrentLab/test": {"dataset": {"test": {"prop": {"path": "path"}}}}, + } + + with pytest.raises(ValidationError) as exc_info: + MetadataConfig.model_validate(config_data) + # Pydantic catches this with type validation before our custom validator + assert "valid dictionary" in str(exc_info.value) or "must be a dict" in str( + exc_info.value + ) + + def test_invalid_alias_value_not_list(self): + """Test that alias values must be lists.""" + config_data = { + "factor_aliases": { + "carbon_source": {"glucose": "D-glucose"} # Should be list, not string + }, + "BrentLab/test": {"dataset": {"test": {"prop": {"path": "path"}}}}, + } + + with pytest.raises(ValidationError) as exc_info: + MetadataConfig.model_validate(config_data) + # Pydantic catches this with type validation before our custom validator + assert "valid list" in str(exc_info.value) or "must map to a list" in str( + exc_info.value + ) + + def test_invalid_alias_empty_list(self): + """Test that alias value lists cannot be empty.""" + config_data = { + "factor_aliases": {"carbon_source": {"glucose": []}}, + "BrentLab/test": {"dataset": {"test": {"prop": {"path": "path"}}}}, + } + + with pytest.raises(ValidationError) as exc_info: + MetadataConfig.model_validate(config_data) + assert "cannot have empty value list" in str(exc_info.value) + + def test_aliases_allow_numeric_values(self): + """Test that aliases can map to numeric values.""" + config_data = { + "factor_aliases": { + "temperature_celsius": { + "thirty": [30, "30"], # Integer and string + "thirty_seven": [37, 37.0], # Integer and float + } + }, + "BrentLab/test": { + "dataset": {"test": {"temperature": {"path": "temperature_celsius"}}} + }, + } + + config = MetadataConfig.model_validate(config_data) + assert config.factor_aliases["temperature_celsius"]["thirty"] == [30, "30"] + assert config.factor_aliases["temperature_celsius"]["thirty_seven"] == [ + 37, + 37.0, + ] + + def test_invalid_no_repositories(self): + """Test that at least one repository is required.""" + config_data = {"factor_aliases": {"carbon_source": {"glucose": ["D-glucose"]}}} + with pytest.raises(ValidationError) as exc_info: + MetadataConfig.model_validate(config_data) + assert "at least one repository configuration" in str(exc_info.value) + + def test_get_repository_config(self, tmp_path): + """Test get_repository_config method.""" + config_data = { + "factor_aliases": {"carbon_source": {"glucose": ["D-glucose"]}}, + "BrentLab/harbison_2004": { + "dataset": { + "harbison_2004": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source", + } + } + } + }, + } + + config_path = tmp_path / "config.yaml" + with open(config_path, "w") as f: + yaml.dump(config_data, f) + + config = MetadataConfig.from_yaml(config_path) + repo_config = config.get_repository_config("BrentLab/harbison_2004") + assert repo_config is not None + assert isinstance(repo_config, RepositoryConfig) + assert repo_config.dataset is not None + assert "harbison_2004" in repo_config.dataset + + # Non-existent repo + assert config.get_repository_config("BrentLab/nonexistent") is None + + def test_get_property_mappings(self, tmp_path): + """Test get_property_mappings method.""" + config_data = { + "factor_aliases": { + "carbon_source": {"glucose": ["D-glucose"]}, + "temperature": {"thirty": [30]}, + }, + "BrentLab/kemmeren_2014": { + "temperature": {"path": "temperature_celsius"}, # Repo-wide + "dataset": { + "kemmeren_2014": {"carbon_source": {"path": "media.carbon_source"}} + }, + }, + } + + config_path = tmp_path / "config.yaml" + with open(config_path, "w") as f: + yaml.dump(config_data, f) + + config = MetadataConfig.from_yaml(config_path) + mappings = config.get_property_mappings( + "BrentLab/kemmeren_2014", "kemmeren_2014" + ) + + # Should have both repo-wide and dataset-specific + assert "temperature" in mappings + assert "carbon_source" in mappings + # Mappings are PropertyMapping objects, not dicts + assert isinstance(mappings["temperature"], PropertyMapping) + assert mappings["temperature"].path == "temperature_celsius" + assert mappings["carbon_source"].path == "media.carbon_source" + + def test_dataset_specific_overrides_repo_wide(self, tmp_path): + """Test that dataset-specific mappings override repo-wide.""" + config_data = { + "BrentLab/test": { + "carbon_source": {"path": "repo.level.path"}, # Repo-wide + "dataset": { + "test_dataset": { + "carbon_source": {"path": "dataset.level.path"} # Override + } + }, + }, + } + + config_path = tmp_path / "config.yaml" + with open(config_path, "w") as f: + yaml.dump(config_data, f) + + config = MetadataConfig.from_yaml(config_path) + mappings = config.get_property_mappings("BrentLab/test", "test_dataset") + + # Dataset-specific should win + assert mappings["carbon_source"].path == "dataset.level.path" + + def test_file_not_found(self): + """Test that FileNotFoundError is raised for missing file.""" + with pytest.raises(FileNotFoundError): + MetadataConfig.from_yaml("/nonexistent/path/config.yaml") + + def test_invalid_yaml_structure(self, tmp_path): + """Test that non-dict YAML is rejected.""" + config_path = tmp_path / "config.yaml" + with open(config_path, "w") as f: + f.write("- not\\n- a\\n- dict\\n") + + with pytest.raises(ValueError) as exc_info: + MetadataConfig.from_yaml(config_path) + assert "Configuration must be a YAML dict" in str(exc_info.value) + + def test_nested_alias_property_names(self, tmp_path): + """Test that alias property names can use dot notation.""" + config_data = { + "factor_aliases": { + "carbon_source": {"glucose": ["D-glucose"]}, + "carbon_source.concentration_percent": {"two_percent": [2]}, + "carbon_source.specifications": {"no_aa": ["without_amino_acids"]}, + }, + "BrentLab/test": { + "dataset": { + "test": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source", + } + } + } + }, + } + + config_path = tmp_path / "config.yaml" + with open(config_path, "w") as f: + yaml.dump(config_data, f) + + config = MetadataConfig.from_yaml(config_path) + + # All alias properties should be preserved + assert "carbon_source" in config.factor_aliases + assert "carbon_source.concentration_percent" in config.factor_aliases + assert "carbon_source.specifications" in config.factor_aliases + + # Values should be correct + assert config.factor_aliases["carbon_source"]["glucose"] == ["D-glucose"] + assert config.factor_aliases["carbon_source.concentration_percent"][ + "two_percent" + ] == [2] + assert config.factor_aliases["carbon_source.specifications"]["no_aa"] == [ + "without_amino_acids" + ] From 38e0b91740b5325a09cfe1fb58a38055ef8dfcad Mon Sep 17 00:00:00 2001 From: chasem Date: Tue, 16 Dec 2025 14:34:28 -0600 Subject: [PATCH 45/49] tmp --- docs/tutorials/filter_resolver_tutorial.ipynb | 1427 ----------------- .../tutorials/metadata_builder_tutorial.ipynb | 938 +++++++++++ ...er.yaml => my_comprehensive_metadata.yaml} | 0 docs/tutorials/my_filter_config.yaml | 19 - docs/tutorials/my_metadata_config.yaml | 35 + tfbpapi/datainfo/datacard.py | 90 ++ tfbpapi/datainfo/metadata_builder.py | 192 ++- .../tests/datainfo/test_metadata_builder.py | 113 +- 8 files changed, 1271 insertions(+), 1543 deletions(-) delete mode 100644 docs/tutorials/filter_resolver_tutorial.ipynb create mode 100644 docs/tutorials/metadata_builder_tutorial.ipynb rename docs/tutorials/{my_comprehensive_filter.yaml => my_comprehensive_metadata.yaml} (100%) delete mode 100644 docs/tutorials/my_filter_config.yaml create mode 100644 docs/tutorials/my_metadata_config.yaml diff --git a/docs/tutorials/filter_resolver_tutorial.ipynb b/docs/tutorials/filter_resolver_tutorial.ipynb deleted file mode 100644 index 16d95f2..0000000 --- a/docs/tutorials/filter_resolver_tutorial.ipynb +++ /dev/null @@ -1,1427 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# DatasetFilterResolver Tutorial\n", - "\n", - "This tutorial demonstrates how to use the `DatasetFilterResolver` to filter samples across heterogeneous datasets using external YAML configuration.\n", - "\n", - "## Overview\n", - "\n", - "The `DatasetFilterResolver` solves a common problem when working with multiple genomics datasets: **experimental conditions are structured differently across datasets**. Some datasets define conditions at the repository level (all samples share the same conditions), while others define conditions per-sample in field definitions.\n", - "\n", - "Instead of writing dataset-specific code, you specify:\n", - "\n", - "1. **Filter criteria**: Which values you want (e.g., `carbon_source: [\"D-glucose\", \"D-galactose\"]`)\n", - "2. **Property mappings**: Where to find each property in each dataset (e.g., repo-level vs field-level)\n", - "\n", - "## Key Features\n", - "\n", - "- **Two-level filtering**:\n", - " - **Level 1** (Repo/Config): Excludes entire datasets that don't match\n", - " - **Level 2** (Field): Returns specific field values that match within datasets\n", - "- **Three output modes**:\n", - " - `conditions`: Just matching field values (no data retrieval)\n", - " - `samples`: Sample-level metadata (one row per sample_id)\n", - " - `full_data`: All measurements for matching samples\n", - "- **External configuration**: No hardcoded dataset logic in your analysis code\n", - "- **Automatic path resolution**: Handles `experimental_conditions` prepending automatically" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Setup" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chase/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", - " from .autonotebook import tqdm as notebook_tqdm\n" - ] - } - ], - "source": [ - "from pathlib import Path\n", - "import yaml\n", - "import pandas as pd\n", - "from tfbpapi.datainfo.filter_resolver import DatasetFilterResolver\n", - "from tfbpapi.datainfo import DataCard" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Understanding Dataset Structures\n", - "\n", - "Before filtering, let's examine how experimental conditions are structured in two representative datasets.\n", - "\n", - "### Repo-Level Conditions: Kemmeren 2014\n", - "\n", - "All samples in this dataset share the same experimental conditions defined at the repository level." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Kemmeren 2014 - Repo-Level Experimental Conditions\n", - "============================================================\n", - "Temperature: 30°C\n", - "Media: synthetic_complete\n", - "Carbon source: ['D-glucose']\n", - "\n", - "All samples in this dataset share these conditions.\n" - ] - } - ], - "source": [ - "# Load Kemmeren 2014 datacard\n", - "kemmeren_card = DataCard(\"BrentLab/kemmeren_2014\")\n", - "\n", - "# Get repo-level experimental conditions\n", - "exp_conds = kemmeren_card.get_experimental_conditions(\"kemmeren_2014\")\n", - "\n", - "print(\"Kemmeren 2014 - Repo-Level Experimental Conditions\")\n", - "print(\"=\" * 60)\n", - "print(f\"Temperature: {exp_conds.get('temperature_celsius')}°C\")\n", - "print(f\"Media: {exp_conds.get('media', {}).get('name')}\")\n", - "\n", - "carbon_source = exp_conds.get('media', {}).get('carbon_source', [])\n", - "if carbon_source:\n", - " compounds = [cs.get('compound') for cs in carbon_source]\n", - " print(f\"Carbon source: {compounds}\")\n", - "\n", - "print(\"\\nAll samples in this dataset share these conditions.\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Field-Level Conditions: Harbison 2004\n", - "\n", - "This dataset has different experimental conditions for different samples, defined in the `condition` field's definitions." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Repo-level experimental_conditions: {}\n", - "\n", - "Harbison 2004 - Field-Level Definitions\n", - "============================================================\n", - "Number of condition definitions: 14\n", - "Condition names: ['YPD', 'SM', 'RAPA', 'H2O2Hi', 'H2O2Lo', 'Acid', 'Alpha', 'BUT14', 'BUT90', 'Thi-', 'GAL', 'HEAT', 'Pi-', 'RAFF']\n", - "\n", - "Example conditions:\n", - " YPD: ['D-glucose'], 30°C\n", - " GAL: ['D-galactose'], 30°C\n", - " HEAT: ['D-glucose'], varies°C\n", - "\n", - "Each sample can have a different condition value.\n" - ] - } - ], - "source": [ - "# Load Harbison 2004 datacard\n", - "harbison_card = DataCard(\"BrentLab/harbison_2004\")\n", - "\n", - "# Check for repo-level conditions\n", - "exp_conds = harbison_card.get_experimental_conditions(\"harbison_2004\")\n", - "print(f\"Repo-level experimental_conditions: {exp_conds}\")\n", - "\n", - "# Get field-level definitions\n", - "condition_defs = harbison_card.get_field_definitions(\"harbison_2004\", \"condition\")\n", - "\n", - "print(\"\\nHarbison 2004 - Field-Level Definitions\")\n", - "print(\"=\" * 60)\n", - "print(f\"Number of condition definitions: {len(condition_defs)}\")\n", - "print(f\"Condition names: {list(condition_defs.keys())}\")\n", - "\n", - "# Show examples\n", - "print(\"\\nExample conditions:\")\n", - "for cond_name in [\"YPD\", \"GAL\", \"HEAT\"]:\n", - " if cond_name in condition_defs:\n", - " definition = condition_defs[cond_name]\n", - " media = definition.get('media', {})\n", - " carbon = media.get('carbon_source', [])\n", - " compounds = [cs.get('compound') for cs in carbon] if carbon else []\n", - " temp = definition.get('temperature_celsius', 'varies')\n", - " print(f\" {cond_name}: {compounds}, {temp}°C\")\n", - "\n", - "print(\"\\nEach sample can have a different condition value.\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Configuration Format\n", - "\n", - "The filter configuration has two main sections:\n", - "\n", - "```yaml\n", - "# 1. Filter criteria - what values you want\n", - "filters:\n", - " carbon_source: [\"D-glucose\", \"D-galactose\"]\n", - " temperature_celsius: [30]\n", - "\n", - "# 2. Property mappings - where to find each property in each dataset\n", - "BrentLab/repo_name:\n", - " property1:\n", - " path: media.name # Repo-wide property\n", - " \n", - " dataset:\n", - " dataset_name:\n", - " property2:\n", - " path: temperature_celsius # Dataset-specific property\n", - " \n", - " property3:\n", - " field: condition # Field-level property\n", - " path: media.carbon_source\n", - "```\n", - "\n", - "### Three Types of Property Configurations\n", - "\n", - "1. **Repo-wide**: Property applies to ALL datasets in the repository\n", - " - Paths automatically get `experimental_conditions.` prepended\n", - " - Example: `path: media.name` → resolves to `experimental_conditions.media.name`\n", - "\n", - "2. **Dataset-specific**: Property applies only to a specific dataset (at config level)\n", - " - Also gets `experimental_conditions.` prepended\n", - " - Example: `path: temperature_celsius` → resolves to `experimental_conditions.temperature_celsius`\n", - "\n", - "3. **Field-level**: Property varies per sample, defined in field definitions\n", - " - Requires `field` parameter to specify which field\n", - " - Path is used directly on field definitions (NO prepending)\n", - " - Example: `field: condition, path: media.carbon_source` → looks in condition field definitions" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Example 1: Basic Filtering by Carbon Source\n", - "\n", - "Let's filter for samples grown on D-glucose in Harbison 2004." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Configuration:\n", - "BrentLab/harbison_2004:\n", - " dataset:\n", - " harbison_2004:\n", - " carbon_source:\n", - " field: condition\n", - " path: media.carbon_source\n", - "filters:\n", - " carbon_source:\n", - " - D-glucose\n", - "\n" - ] - } - ], - "source": [ - "# Create filter configuration\n", - "glucose_config = {\n", - " \"filters\": {\n", - " \"carbon_source\": [\"D-glucose\"]\n", - " },\n", - " \"BrentLab/harbison_2004\": {\n", - " \"dataset\": {\n", - " \"harbison_2004\": {\n", - " \"carbon_source\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"media.carbon_source\"\n", - " }\n", - " }\n", - " }\n", - " }\n", - "}\n", - "\n", - "# Save to file\n", - "config_path = Path(\"/tmp/glucose_filter.yaml\")\n", - "with open(config_path, 'w') as f:\n", - " yaml.dump(glucose_config, f)\n", - "\n", - "print(\"Configuration:\")\n", - "print(yaml.dump(glucose_config, default_flow_style=False))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Mode 0: Conditions Only\n", - "\n", - "Get just the matching field values without retrieving any data. This is the fastest mode for exploration." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Included: True\n", - "\n", - "Matching conditions:\n", - " condition: ['YPD', 'RAPA', 'H2O2Hi', 'H2O2Lo', 'Acid', 'Alpha', 'BUT14', 'BUT90', 'HEAT']\n" - ] - } - ], - "source": [ - "# Create resolver and run filter\n", - "resolver = DatasetFilterResolver(config_path)\n", - "\n", - "results = resolver.resolve_filters(\n", - " repos=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", - " mode=\"conditions\"\n", - ")\n", - "\n", - "# Display results\n", - "result = results[\"BrentLab/harbison_2004\"]\n", - "print(f\"Included: {result['included']}\")\n", - "\n", - "if result['included']:\n", - " print(\"\\nMatching conditions:\")\n", - " for field, values in result['matching_field_values'].items():\n", - " print(f\" {field}: {values}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Understanding the results:**\n", - "\n", - "The resolver found all condition field values where `media.carbon_source` contains D-glucose:\n", - "- YPD: Rich media with glucose\n", - "- HEAT: Heat stress with glucose\n", - "- H2O2Hi/H2O2Lo: Oxidative stress with glucose\n", - "- Acid, Alpha, BUT14, BUT90, RAPA: Various stresses with glucose\n", - "\n", - "Notice GAL is NOT included - it uses galactose, not glucose." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Example 2: Multiple Filter Criteria\n", - "\n", - "Filter for samples with both D-glucose AND 30°C temperature." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Conditions with D-glucose AND 30°C:\n", - "['YPD', 'RAPA', 'H2O2Hi', 'H2O2Lo', 'Acid', 'Alpha', 'BUT14', 'BUT90']\n" - ] - } - ], - "source": [ - "multi_filter_config = {\n", - " \"filters\": {\n", - " \"carbon_source\": [\"D-glucose\"],\n", - " \"temperature_celsius\": [30]\n", - " },\n", - " \"BrentLab/harbison_2004\": {\n", - " \"dataset\": {\n", - " \"harbison_2004\": {\n", - " \"carbon_source\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"media.carbon_source\"\n", - " },\n", - " \"temperature_celsius\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"temperature_celsius\"\n", - " }\n", - " }\n", - " }\n", - " }\n", - "}\n", - "\n", - "config_path = Path(\"/tmp/multi_filter.yaml\")\n", - "with open(config_path, 'w') as f:\n", - " yaml.dump(multi_filter_config, f)\n", - "\n", - "resolver = DatasetFilterResolver(config_path)\n", - "results = resolver.resolve_filters(\n", - " repos=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", - " mode=\"conditions\"\n", - ")\n", - "\n", - "matching = results[\"BrentLab/harbison_2004\"][\"matching_field_values\"][\"condition\"]\n", - "print(\"Conditions with D-glucose AND 30°C:\")\n", - "print(matching)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Notice HEAT is now excluded - it has glucose but uses a temperature shift to 37°C, not 30°C.\n", - "\n", - "**Multiple filter criteria use AND logic** - all criteria must match." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Example 3: Repo-Level Filtering\n", - "\n", - "Filter Kemmeren 2014, which has repo-level experimental conditions (no field specification needed)." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Included: True\n", - "\n", - "No field-level filtering - entire dataset matches at repo level\n" - ] - } - ], - "source": [ - "repo_level_config = {\n", - " \"filters\": {\n", - " \"carbon_source\": [\"D-glucose\"],\n", - " \"temperature_celsius\": [30]\n", - " },\n", - " \"BrentLab/kemmeren_2014\": {\n", - " \"dataset\": {\n", - " \"kemmeren_2014\": {\n", - " \"carbon_source\": {\n", - " \"path\": \"media.carbon_source\" # No 'field' - repo-level property\n", - " },\n", - " \"temperature_celsius\": {\n", - " \"path\": \"temperature_celsius\"\n", - " }\n", - " }\n", - " }\n", - " }\n", - "}\n", - "\n", - "config_path = Path(\"/tmp/repo_level_filter.yaml\")\n", - "with open(config_path, 'w') as f:\n", - " yaml.dump(repo_level_config, f)\n", - "\n", - "resolver = DatasetFilterResolver(config_path)\n", - "results = resolver.resolve_filters(\n", - " repos=[(\"BrentLab/kemmeren_2014\", \"kemmeren_2014\")],\n", - " mode=\"conditions\"\n", - ")\n", - "\n", - "result = results[\"BrentLab/kemmeren_2014\"]\n", - "print(f\"Included: {result['included']}\")\n", - "\n", - "if result['included']:\n", - " if result['matching_field_values']:\n", - " print(\"\\nMatching field values:\")\n", - " for field, values in result['matching_field_values'].items():\n", - " print(f\" {field}: {values}\")\n", - " else:\n", - " print(\"\\nNo field-level filtering - entire dataset matches at repo level\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Key difference**: Repo-level filtering includes/excludes the entire dataset. Since kemmeren_2014 has D-glucose at 30°C at the repo level, ALL samples in the dataset match." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Example 4: Multiple Datasets\n", - "\n", - "Filter across both harbison_2004 (field-level) and kemmeren_2014 (repo-level) in a single query." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Results across multiple datasets:\n", - "\n", - "BrentLab/harbison_2004:\n", - " Included: True\n", - " condition: ['YPD', 'RAPA', 'H2O2Hi']...\n", - "\n", - "BrentLab/kemmeren_2014:\n", - " Included: True\n", - "\n" - ] - } - ], - "source": [ - "multi_dataset_config = {\n", - " \"filters\": {\n", - " \"carbon_source\": [\"D-glucose\"]\n", - " },\n", - " \"BrentLab/harbison_2004\": {\n", - " \"dataset\": {\n", - " \"harbison_2004\": {\n", - " \"carbon_source\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"media.carbon_source\"\n", - " }\n", - " }\n", - " }\n", - " },\n", - " \"BrentLab/kemmeren_2014\": {\n", - " \"dataset\": {\n", - " \"kemmeren_2014\": {\n", - " \"carbon_source\": {\n", - " \"path\": \"media.carbon_source\"\n", - " }\n", - " }\n", - " }\n", - " }\n", - "}\n", - "\n", - "config_path = Path(\"/tmp/multi_dataset_filter.yaml\")\n", - "with open(config_path, 'w') as f:\n", - " yaml.dump(multi_dataset_config, f)\n", - "\n", - "resolver = DatasetFilterResolver(config_path)\n", - "results = resolver.resolve_filters(\n", - " repos=[\n", - " (\"BrentLab/harbison_2004\", \"harbison_2004\"),\n", - " (\"BrentLab/kemmeren_2014\", \"kemmeren_2014\")\n", - " ],\n", - " mode=\"conditions\"\n", - ")\n", - "\n", - "print(\"Results across multiple datasets:\\n\")\n", - "for repo_id, result in results.items():\n", - " print(f\"{repo_id}:\")\n", - " print(f\" Included: {result['included']}\")\n", - " if result['included'] and result.get('matching_field_values'):\n", - " for field, values in result['matching_field_values'].items():\n", - " print(f\" {field}: {values[:3]}...\") # Show first 3\n", - " print()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Example 5: Retrieving Sample Metadata (Mode 1)\n", - "\n", - "Once you've identified matching conditions, retrieve sample-level metadata." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Retrieved 310 samples\n", - "Columns: ['sample_id', 'db_id', 'regulator_locus_tag', 'regulator_symbol', 'condition', 'target_locus_tag', 'target_symbol', 'effect', 'pvalue']\n", - "\n", - "First few rows:\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "sample_id", - "rawType": "int32", - "type": "integer" - }, - { - "name": "db_id", - "rawType": "float64", - "type": "float" - }, - { - "name": "regulator_locus_tag", - "rawType": "object", - "type": "string" - }, - { - "name": "regulator_symbol", - "rawType": "object", - "type": "string" - }, - { - "name": "condition", - "rawType": "object", - "type": "string" - }, - { - "name": "target_locus_tag", - "rawType": "object", - "type": "string" - }, - { - "name": "target_symbol", - "rawType": "object", - "type": "string" - }, - { - "name": "effect", - "rawType": "float64", - "type": "float" - }, - { - "name": "pvalue", - "rawType": "float64", - "type": "float" - } - ], - "ref": "b8896bc0-bb22-468e-9c3f-f8e1715f804c", - "rows": [ - [ - "0", - "1", - "0.0", - "YSC0017", - "MATA1", - "YPD", - "YBL068W", - "PRS4", - "0.8599428", - "0.72834544" - ], - [ - "1", - "2", - "1.0", - "YAL051W", - "OAF1", - "YPD", - "YAL015C", - "NTG1", - "1.3079075", - "0.17689452" - ], - [ - "2", - "3", - "2.0", - "YBL005W", - "PDR3", - "YPD", - "YAL005C", - "SSA1", - "0.76584559", - "0.92357641" - ], - [ - "3", - "4", - "3.0", - "YBL008W", - "HIR1", - "YPD", - "YAL030W", - "SNC1", - "0.92566636", - "0.69746121" - ], - [ - "4", - "5", - "4.0", - "YBL021C", - "HAP3", - "YPD", - "YAR035W", - "YAT1", - "0.83719358", - "0.85707131" - ] - ], - "shape": { - "columns": 9, - "rows": 5 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sample_iddb_idregulator_locus_tagregulator_symbolconditiontarget_locus_tagtarget_symboleffectpvalue
010.0YSC0017MATA1YPDYBL068WPRS40.8599430.728345
121.0YAL051WOAF1YPDYAL015CNTG11.3079080.176895
232.0YBL005WPDR3YPDYAL005CSSA10.7658460.923576
343.0YBL008WHIR1YPDYAL030WSNC10.9256660.697461
454.0YBL021CHAP3YPDYAR035WYAT10.8371940.857071
\n", - "
" - ], - "text/plain": [ - " sample_id db_id regulator_locus_tag regulator_symbol condition \\\n", - "0 1 0.0 YSC0017 MATA1 YPD \n", - "1 2 1.0 YAL051W OAF1 YPD \n", - "2 3 2.0 YBL005W PDR3 YPD \n", - "3 4 3.0 YBL008W HIR1 YPD \n", - "4 5 4.0 YBL021C HAP3 YPD \n", - "\n", - " target_locus_tag target_symbol effect pvalue \n", - "0 YBL068W PRS4 0.859943 0.728345 \n", - "1 YAL015C NTG1 1.307908 0.176895 \n", - "2 YAL005C SSA1 0.765846 0.923576 \n", - "3 YAL030W SNC1 0.925666 0.697461 \n", - "4 YAR035W YAT1 0.837194 0.857071 " - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Reuse the glucose filter config\n", - "glucose_config = {\n", - " \"filters\": {\n", - " \"carbon_source\": [\"D-glucose\"]\n", - " },\n", - " \"BrentLab/harbison_2004\": {\n", - " \"dataset\": {\n", - " \"harbison_2004\": {\n", - " \"carbon_source\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"media.carbon_source\"\n", - " }\n", - " }\n", - " }\n", - " }\n", - "}\n", - "\n", - "config_path = Path(\"/tmp/glucose_filter.yaml\")\n", - "with open(config_path, 'w') as f:\n", - " yaml.dump(glucose_config, f)\n", - "\n", - "resolver = DatasetFilterResolver(config_path)\n", - "\n", - "# Use samples mode\n", - "results = resolver.resolve_filters(\n", - " repos=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", - " mode=\"samples\"\n", - ")\n", - "\n", - "df_samples = results[\"BrentLab/harbison_2004\"][\"data\"]\n", - "\n", - "print(f\"Retrieved {len(df_samples)} samples\")\n", - "print(f\"Columns: {list(df_samples.columns)}\")\n", - "print(\"\\nFirst few rows:\")\n", - "df_samples.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Condition distribution in retrieved samples:\n", - "condition\n", - "YPD 204\n", - "H2O2Hi 39\n", - "H2O2Lo 28\n", - "RAPA 14\n", - "BUT14 8\n", - "HEAT 6\n", - "Alpha 5\n", - "BUT90 4\n", - "Acid 2\n", - "Name: count, dtype: int64\n" - ] - } - ], - "source": [ - "# Analyze condition distribution\n", - "print(\"Condition distribution in retrieved samples:\")\n", - "print(df_samples['condition'].value_counts())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Example 6: Retrieving Full Data (Mode 2)\n", - "\n", - "Retrieve all measurements for matching samples (many rows per sample)." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Retrieved 1,930,060 rows (measurements)\n", - "DataFrame shape: (1930060, 9)\n", - "\n", - "First few rows:\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "sample_id", - "rawType": "int32", - "type": "integer" - }, - { - "name": "db_id", - "rawType": "float64", - "type": "float" - }, - { - "name": "regulator_locus_tag", - "rawType": "object", - "type": "string" - }, - { - "name": "regulator_symbol", - "rawType": "object", - "type": "string" - }, - { - "name": "condition", - "rawType": "object", - "type": "string" - }, - { - "name": "target_locus_tag", - "rawType": "object", - "type": "string" - }, - { - "name": "target_symbol", - "rawType": "object", - "type": "string" - }, - { - "name": "effect", - "rawType": "float64", - "type": "float" - }, - { - "name": "pvalue", - "rawType": "float64", - "type": "float" - } - ], - "ref": "94bbe0c4-757e-406b-aef0-b82efd0905fe", - "rows": [ - [ - "0", - "1", - "0.0", - "YSC0017", - "MATA1", - "YPD", - "YAL001C", - "TFC3", - "1.697754", - "0.068704735" - ], - [ - "1", - "1", - "0.0", - "YSC0017", - "MATA1", - "YPD", - "YAL002W", - "VPS8", - null, - null - ], - [ - "2", - "1", - "0.0", - "YSC0017", - "MATA1", - "YPD", - "YAL003W", - "EFB1", - null, - null - ], - [ - "3", - "1", - "0.0", - "YSC0017", - "MATA1", - "YPD", - "YAL004W", - "YAL004W", - "0.74534215", - "0.83592938" - ], - [ - "4", - "1", - "0.0", - "YSC0017", - "MATA1", - "YPD", - "YAL005C", - "SSA1", - null, - null - ] - ], - "shape": { - "columns": 9, - "rows": 5 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sample_iddb_idregulator_locus_tagregulator_symbolconditiontarget_locus_tagtarget_symboleffectpvalue
010.0YSC0017MATA1YPDYAL001CTFC31.6977540.068705
110.0YSC0017MATA1YPDYAL002WVPS8NaNNaN
210.0YSC0017MATA1YPDYAL003WEFB1NaNNaN
310.0YSC0017MATA1YPDYAL004WYAL004W0.7453420.835929
410.0YSC0017MATA1YPDYAL005CSSA1NaNNaN
\n", - "
" - ], - "text/plain": [ - " sample_id db_id regulator_locus_tag regulator_symbol condition \\\n", - "0 1 0.0 YSC0017 MATA1 YPD \n", - "1 1 0.0 YSC0017 MATA1 YPD \n", - "2 1 0.0 YSC0017 MATA1 YPD \n", - "3 1 0.0 YSC0017 MATA1 YPD \n", - "4 1 0.0 YSC0017 MATA1 YPD \n", - "\n", - " target_locus_tag target_symbol effect pvalue \n", - "0 YAL001C TFC3 1.697754 0.068705 \n", - "1 YAL002W VPS8 NaN NaN \n", - "2 YAL003W EFB1 NaN NaN \n", - "3 YAL004W YAL004W 0.745342 0.835929 \n", - "4 YAL005C SSA1 NaN NaN " - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Use full_data mode\n", - "results_full = resolver.resolve_filters(\n", - " repos=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", - " mode=\"full_data\"\n", - ")\n", - "\n", - "df_full = results_full[\"BrentLab/harbison_2004\"][\"data\"]\n", - "\n", - "print(f\"Retrieved {len(df_full):,} rows (measurements)\")\n", - "print(f\"DataFrame shape: {df_full.shape}\")\n", - "print(\"\\nFirst few rows:\")\n", - "df_full.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Samples mode: 310 samples\n", - "Full data mode: 1,930,060 measurements\n", - "Average measurements per sample: 6,226\n" - ] - } - ], - "source": [ - "# Compare modes\n", - "n_samples = len(df_samples)\n", - "n_measurements = len(df_full)\n", - "measurements_per_sample = n_measurements / n_samples if n_samples > 0 else 0\n", - "\n", - "print(f\"Samples mode: {n_samples} samples\")\n", - "print(f\"Full data mode: {n_measurements:,} measurements\")\n", - "print(f\"Average measurements per sample: {measurements_per_sample:,.0f}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Example 7: Using Repo-Wide Properties\n", - "\n", - "For repositories where a property applies to ALL datasets, use repo-wide configuration." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Configuration with repo-wide property:\n", - "BrentLab/kemmeren_2014:\n", - " dataset:\n", - " kemmeren_2014:\n", - " carbon_source:\n", - " path: media.carbon_source\n", - " temperature_celsius:\n", - " path: temperature_celsius\n", - "filters:\n", - " temperature_celsius:\n", - " - 30\n", - "\n" - ] - } - ], - "source": [ - "repo_wide_config = {\n", - " \"filters\": {\n", - " \"temperature_celsius\": [30]\n", - " },\n", - " \"BrentLab/kemmeren_2014\": {\n", - " \"temperature_celsius\": { # Repo-wide property (no dataset key)\n", - " \"path\": \"temperature_celsius\"\n", - " },\n", - " \"dataset\": {\n", - " \"kemmeren_2014\": {\n", - " \"carbon_source\": { # Dataset-specific property\n", - " \"path\": \"media.carbon_source\"\n", - " }\n", - " }\n", - " }\n", - " }\n", - "}\n", - "\n", - "print(\"Configuration with repo-wide property:\")\n", - "print(yaml.dump(repo_wide_config, default_flow_style=False))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this example:\n", - "- `temperature_celsius` is defined at repo level - applies to ALL datasets in BrentLab/kemmeren_2014\n", - "- `carbon_source` is defined at dataset level - applies only to the kemmeren_2014 dataset\n", - "\n", - "This is useful when a repository has multiple datasets that share some common properties." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Creating a Reusable Configuration File\n", - "\n", - "For production use, save your configuration to a YAML file." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saved configuration to my_comprehensive_filter.yaml\n", - "\n", - "You can now use this config file in your analysis:\n", - " resolver = DatasetFilterResolver('my_comprehensive_filter.yaml')\n" - ] - } - ], - "source": [ - "# Create a comprehensive filter config\n", - "comprehensive_config = {\n", - " \"filters\": {\n", - " \"carbon_source\": [\"D-glucose\", \"D-galactose\"],\n", - " \"temperature_celsius\": [30]\n", - " },\n", - " \"BrentLab/harbison_2004\": {\n", - " \"dataset\": {\n", - " \"harbison_2004\": {\n", - " \"carbon_source\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"media.carbon_source\"\n", - " },\n", - " \"temperature_celsius\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"temperature_celsius\"\n", - " }\n", - " }\n", - " }\n", - " },\n", - " \"BrentLab/kemmeren_2014\": {\n", - " \"dataset\": {\n", - " \"kemmeren_2014\": {\n", - " \"carbon_source\": {\n", - " \"path\": \"media.carbon_source\"\n", - " },\n", - " \"temperature_celsius\": {\n", - " \"path\": \"temperature_celsius\"\n", - " }\n", - " }\n", - " }\n", - " }\n", - "}\n", - "\n", - "# Save to file\n", - "config_file = Path(\"my_comprehensive_filter.yaml\")\n", - "with open(config_file, 'w') as f:\n", - " yaml.dump(comprehensive_config, f)\n", - "\n", - "print(f\"Saved configuration to {config_file}\")\n", - "print(\"\\nYou can now use this config file in your analysis:\")\n", - "print(f\" resolver = DatasetFilterResolver('{config_file}')\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Summary\n", - "\n", - "### Key Concepts\n", - "\n", - "1. **Configuration Structure**:\n", - " - `filters`: What values you want\n", - " - Repository mappings: Where to find properties in each dataset\n", - "\n", - "2. **Three Property Types**:\n", - " - **Repo-wide**: Applies to all datasets (paths get `experimental_conditions.` prepended)\n", - " - **Dataset-specific**: Applies to one dataset (paths get `experimental_conditions.` prepended)\n", - " - **Field-level**: Per-sample variation (requires `field`, no prepending)\n", - "\n", - "3. **Three Output Modes**:\n", - " - `conditions`: Fast exploration, no data download\n", - " - `samples`: Sample metadata, one row per sample\n", - " - `full_data`: All measurements, many rows per sample\n", - "\n", - "### Best Practices\n", - "\n", - "1. **Start with `conditions` mode** to verify your filters work\n", - "2. **Use `field` parameter** when properties vary between samples\n", - "3. **Omit `field` parameter** when properties are at repo/config level\n", - "4. **Keep paths simple** - use minimal nesting that works\n", - "5. **Version control your configs** - they document your filter criteria\n", - "\n", - "### Common Patterns\n", - "\n", - "**Field-level filtering** (per-sample variation):\n", - "```yaml\n", - "BrentLab/harbison_2004:\n", - " dataset:\n", - " harbison_2004:\n", - " carbon_source:\n", - " field: condition\n", - " path: media.carbon_source\n", - "```\n", - "\n", - "**Repo-level filtering** (all samples share conditions):\n", - "```yaml\n", - "BrentLab/kemmeren_2014:\n", - " dataset:\n", - " kemmeren_2014:\n", - " carbon_source:\n", - " path: media.carbon_source\n", - "```\n", - "\n", - "**Repo-wide property** (applies to all datasets in repo):\n", - "```yaml\n", - "BrentLab/kemmeren_2014:\n", - " temperature_celsius:\n", - " path: temperature_celsius\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Next Steps\n", - "\n", - "- See [my_filter_config.yaml](my_filter_config.yaml) for a working example\n", - "- Explore the [experimental_conditions_tutorial.ipynb](experimental_conditions_tutorial.ipynb) to understand datacard structures\n", - "- Read the [DatasetFilterResolver API documentation](../../api/datainfo/filter_resolver.md) for detailed reference\n", - "- Try filtering across additional datasets in the BrentLab collection" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "tfbpapi-py3.11", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.9" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/tutorials/metadata_builder_tutorial.ipynb b/docs/tutorials/metadata_builder_tutorial.ipynb new file mode 100644 index 0000000..774f413 --- /dev/null +++ b/docs/tutorials/metadata_builder_tutorial.ipynb @@ -0,0 +1,938 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# MetadataBuilder Tutorial\n", + "\n", + "This tutorial demonstrates how to use the `MetadataBuilder` to create standardized metadata tables across heterogeneous datasets using optional alias mappings.\n", + "\n", + "## Overview\n", + "\n", + "The `MetadataBuilder` solves a common problem when working with multiple genomics datasets: **factor levels are named inconsistently across datasets**. For example, carbon sources might be labeled as \"D-glucose\", \"dextrose\", \"glucose\", etc.\n", + "\n", + "**Key difference from filtering**: This system does NOT filter or exclude any data. Instead, it normalizes factor level names to create standardized metadata views. ALL data is included, just with standardized names.\n", + "\n", + "You specify:\n", + "\n", + "1. **Factor aliases** (optional): Mappings to normalize factor level names (e.g., `glucose: [\"D-glucose\", \"dextrose\"]`)\n", + "2. **Property mappings**: Where to find each property in each dataset (e.g., repo-level vs field-level)\n", + "\n", + "## Key Features\n", + "\n", + "- **No filtering**: ALL data is included with normalized names\n", + "- **Optional aliases**: If no alias is configured, original values pass through unchanged\n", + "- **Case-insensitive matching**: \"D-glucose\" matches \"d-glucose\"\n", + "- **Three output modes**:\n", + " - `conditions`: Just normalized metadata (no data retrieval)\n", + " - `samples`: Sample-level metadata (one row per sample_id)\n", + " - `full_data`: All measurements with normalized metadata\n", + "- **External configuration**: No hardcoded dataset logic in your analysis code" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chase/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" + ] + } + ], + "source": [ + "from pathlib import Path\n", + "import yaml\n", + "import pandas as pd\n", + "from tfbpapi.datainfo.metadata_builder import MetadataBuilder, normalize_value\n", + "from tfbpapi.datainfo import DataCard" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Understanding Dataset Structures\n", + "\n", + "Before normalizing, let's examine how experimental conditions are structured in two representative datasets.\n", + "\n", + "### Repo-Level Conditions: Kemmeren 2014\n", + "\n", + "All samples in this dataset share the same experimental conditions defined at the repository level." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Kemmeren 2014 - Repo-Level Experimental Conditions\n", + "============================================================\n", + "Temperature: 30°C\n", + "Media: synthetic_complete\n", + "Carbon source: ['D-glucose']\n", + "\n", + "All samples in this dataset share these conditions.\n" + ] + } + ], + "source": [ + "# Load Kemmeren 2014 datacard\n", + "kemmeren_card = DataCard(\"BrentLab/kemmeren_2014\")\n", + "\n", + "# Get repo-level experimental conditions\n", + "exp_conds = kemmeren_card.get_experimental_conditions(\"kemmeren_2014\")\n", + "\n", + "print(\"Kemmeren 2014 - Repo-Level Experimental Conditions\")\n", + "print(\"=\" * 60)\n", + "print(f\"Temperature: {exp_conds.get('temperature_celsius')}°C\")\n", + "print(f\"Media: {exp_conds.get('media', {}).get('name')}\")\n", + "\n", + "carbon_source = exp_conds.get('media', {}).get('carbon_source', [])\n", + "if carbon_source:\n", + " compounds = [cs.get('compound') for cs in carbon_source]\n", + " print(f\"Carbon source: {compounds}\")\n", + "\n", + "print(\"\\nAll samples in this dataset share these conditions.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Field-Level Conditions: Harbison 2004\n", + "\n", + "This dataset has different experimental conditions for different samples, defined in the `condition` field's definitions." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Field: condition\n", + "Levels: 14\n", + "\n", + "YPD:\n", + " media.name: YPD\n", + " media.carbon_source: [{'compound': 'D-glucose', 'concentration_percent': 2}]\n", + "\n", + "SM:\n", + " media.name: synthetic_complete\n", + " media.carbon_source: None\n", + "\n", + "RAPA:\n", + " media.name: YPD\n", + " media.carbon_source: [{'compound': 'D-glucose', 'concentration_percent': 2}]\n", + "\n", + "H2O2Hi:\n", + " media.name: YPD\n", + " media.carbon_source: [{'compound': 'D-glucose', 'concentration_percent': 2}]\n", + "\n", + "H2O2Lo:\n", + " media.name: YPD\n", + " media.carbon_source: [{'compound': 'D-glucose', 'concentration_percent': 2}]\n", + "\n", + "Acid:\n", + " media.name: YPD\n", + " media.carbon_source: [{'compound': 'D-glucose', 'concentration_percent': 2}]\n", + "\n", + "... and 8 more levels\n" + ] + } + ], + "source": [ + "# Load Harbison 2004 datacard\n", + "harbison_card = DataCard(\"BrentLab/harbison_2004\")\n", + "\n", + "# Get a summary of condition field levels\n", + "print(harbison_card.summarize_field_levels(\n", + " \"harbison_2004\",\n", + " \"condition\",\n", + " properties=[\"media.name\", \"media.carbon_source\"],\n", + " max_levels=6\n", + "))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 8: Extracting Properties from Field Definitions\n", + "\n", + "The MetadataBuilder can extract properties from field definitions and add them as new columns. This is useful for creating analysis-ready metadata tables.\n", + "\n", + "For harbison_2004, the base metadata has `condition` as a field, but we want to extract:\n", + "- `growth_media` from `media.name`\n", + "- `carbon_source` from `media.carbon_source.compound`\n", + "\n", + "And normalize carbon source names using aliases." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Fetching 1 files: 100%|██████████| 1/1 [00:00<00:00, 2.88it/s]\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "sample_id", + "rawType": "int32", + "type": "integer" + }, + { + "name": "regulator_locus_tag", + "rawType": "object", + "type": "string" + }, + { + "name": "regulator_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "condition", + "rawType": "object", + "type": "string" + }, + { + "name": "carbon_source", + "rawType": "object", + "type": "unknown" + }, + { + "name": "growth_media", + "rawType": "object", + "type": "string" + } + ], + "ref": "95a0f05b-86e0-4ac9-bda0-b91b5f5e4de5", + "rows": [ + [ + "0", + "1", + "YSC0017", + "MATA1", + "YPD", + "glucose", + "YPD" + ], + [ + "1", + "2", + "YAL051W", + "OAF1", + "YPD", + "glucose", + "YPD" + ], + [ + "2", + "3", + "YBL005W", + "PDR3", + "YPD", + "glucose", + "YPD" + ], + [ + "3", + "4", + "YBL008W", + "HIR1", + "YPD", + "glucose", + "YPD" + ], + [ + "4", + "5", + "YBL021C", + "HAP3", + "YPD", + "glucose", + "YPD" + ], + [ + "5", + "6", + "YBL054W", + "TOD6", + "YPD", + "glucose", + "YPD" + ], + [ + "6", + "7", + "YBL103C", + "RTG3", + "H2O2Hi", + "glucose", + "YPD" + ], + [ + "7", + "8", + "YBL103C", + "RTG3", + "H2O2Lo", + "glucose", + "YPD" + ], + [ + "8", + "9", + "YBL103C", + "RTG3", + "RAPA", + "glucose", + "YPD" + ], + [ + "9", + "10", + "YBL103C", + "RTG3", + "SM", + null, + "synthetic_complete" + ], + [ + "10", + "11", + "YBL103C", + "RTG3", + "YPD", + "glucose", + "YPD" + ], + [ + "11", + "12", + "YBR033W", + "EDS1", + "YPD", + "glucose", + "YPD" + ], + [ + "12", + "13", + "YBR049C", + "REB1", + "H2O2Hi", + "glucose", + "YPD" + ], + [ + "13", + "14", + "YBR049C", + "REB1", + "H2O2Lo", + "glucose", + "YPD" + ], + [ + "14", + "15", + "YBR049C", + "REB1", + "YPD", + "glucose", + "YPD" + ], + [ + "15", + "16", + "YBR083W", + "TEC1", + "Alpha", + "glucose", + "YPD" + ], + [ + "16", + "17", + "YBR083W", + "TEC1", + "BUT14", + "glucose", + "YPD" + ], + [ + "17", + "18", + "YBR083W", + "TEC1", + "YPD", + "glucose", + "YPD" + ], + [ + "18", + "19", + "YBR150C", + "TBS1", + "YPD", + "glucose", + "YPD" + ], + [ + "19", + "20", + "YBR182C", + "SMP1", + "YPD", + "glucose", + "YPD" + ], + [ + "20", + "21", + "YBR239C", + "ERT1", + "YPD", + "glucose", + "YPD" + ], + [ + "21", + "22", + "YBR240C", + "THI2", + "Thi-", + null, + "synthetic_complete_minus_thiamine" + ], + [ + "22", + "23", + "YBR240C", + "THI2", + "YPD", + "glucose", + "YPD" + ], + [ + "23", + "24", + "YBR267W", + "REI1", + "YPD", + "glucose", + "YPD" + ], + [ + "24", + "25", + "YBR297W", + "MAL33", + "H2O2Hi", + "glucose", + "YPD" + ], + [ + "25", + "26", + "YBR297W", + "MAL33", + "H2O2Lo", + "glucose", + "YPD" + ], + [ + "26", + "27", + "YBR297W", + "MAL33", + "YPD", + "glucose", + "YPD" + ], + [ + "27", + "28", + "YCR018C", + "SRD1", + "YPD", + "glucose", + "YPD" + ], + [ + "28", + "29", + "YCR106W", + "RDS1", + "H2O2Hi", + "glucose", + "YPD" + ], + [ + "29", + "30", + "YCR106W", + "RDS1", + "YPD", + "glucose", + "YPD" + ], + [ + "30", + "31", + "YDL020C", + "RPN4", + "H2O2Hi", + "glucose", + "YPD" + ], + [ + "31", + "32", + "YDL020C", + "RPN4", + "H2O2Lo", + "glucose", + "YPD" + ], + [ + "32", + "33", + "YDL020C", + "RPN4", + "YPD", + "glucose", + "YPD" + ], + [ + "33", + "34", + "YDL048C", + "STP4", + "YPD", + "glucose", + "YPD" + ], + [ + "34", + "35", + "YDL056W", + "MBP1", + "H2O2Hi", + "glucose", + "YPD" + ], + [ + "35", + "36", + "YDL056W", + "MBP1", + "H2O2Lo", + "glucose", + "YPD" + ], + [ + "36", + "37", + "YDL056W", + "MBP1", + "YPD", + "glucose", + "YPD" + ], + [ + "37", + "38", + "YDL106C", + "PHO2", + "H2O2Hi", + "glucose", + "YPD" + ], + [ + "38", + "39", + "YDL106C", + "PHO2", + "H2O2Lo", + "glucose", + "YPD" + ], + [ + "39", + "40", + "YDL106C", + "PHO2", + "Pi-", + null, + "synthetic_complete_minus_phosphate" + ], + [ + "40", + "41", + "YDL106C", + "PHO2", + "SM", + null, + "synthetic_complete" + ], + [ + "41", + "42", + "YDL106C", + "PHO2", + "YPD", + "glucose", + "YPD" + ], + [ + "42", + "43", + "YDL166C", + "FAP7", + "YPD", + "glucose", + "YPD" + ], + [ + "43", + "44", + "YDL170W", + "UGA3", + "RAPA", + "glucose", + "YPD" + ], + [ + "44", + "45", + "YDL170W", + "UGA3", + "SM", + null, + "synthetic_complete" + ], + [ + "45", + "46", + "YDL170W", + "UGA3", + "YPD", + "glucose", + "YPD" + ], + [ + "46", + "47", + "YDR009W", + "GAL3", + "YPD", + "glucose", + "YPD" + ], + [ + "47", + "48", + "YDR026C", + "NSI1", + "YPD", + "glucose", + "YPD" + ], + [ + "48", + "49", + "YDR043C", + "NRG1", + "H2O2Hi", + "glucose", + "YPD" + ], + [ + "49", + "50", + "YDR043C", + "NRG1", + "H2O2Lo", + "glucose", + "YPD" + ] + ], + "shape": { + "columns": 6, + "rows": 352 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sample_idregulator_locus_tagregulator_symbolconditioncarbon_sourcegrowth_media
01YSC0017MATA1YPDglucoseYPD
12YAL051WOAF1YPDglucoseYPD
23YBL005WPDR3YPDglucoseYPD
34YBL008WHIR1YPDglucoseYPD
45YBL021CHAP3YPDglucoseYPD
.....................
347348YPR104CFHL1YPDglucoseYPD
348349YPR196WYPR196WYPDglucoseYPD
349350YPR199CARR1H2O2HiglucoseYPD
350351YPR199CARR1YPDglucoseYPD
351352YKL043WPHD1BUT14glucoseYPD
\n", + "

352 rows × 6 columns

\n", + "
" + ], + "text/plain": [ + " sample_id regulator_locus_tag regulator_symbol condition carbon_source \\\n", + "0 1 YSC0017 MATA1 YPD glucose \n", + "1 2 YAL051W OAF1 YPD glucose \n", + "2 3 YBL005W PDR3 YPD glucose \n", + "3 4 YBL008W HIR1 YPD glucose \n", + "4 5 YBL021C HAP3 YPD glucose \n", + ".. ... ... ... ... ... \n", + "347 348 YPR104C FHL1 YPD glucose \n", + "348 349 YPR196W YPR196W YPD glucose \n", + "349 350 YPR199C ARR1 H2O2Hi glucose \n", + "350 351 YPR199C ARR1 YPD glucose \n", + "351 352 YKL043W PHD1 BUT14 glucose \n", + "\n", + " growth_media \n", + "0 YPD \n", + "1 YPD \n", + "2 YPD \n", + "3 YPD \n", + "4 YPD \n", + ".. ... \n", + "347 YPD \n", + "348 YPD \n", + "349 YPD \n", + "350 YPD \n", + "351 YPD \n", + "\n", + "[352 rows x 6 columns]" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Configuration with property extraction and aliases\n", + "extraction_config = {\n", + " \"factor_aliases\": {\n", + " \"carbon_source\": {\n", + " \"glucose\": [\"D-glucose\", \"Glucose\", \"dextrose\", \"Dextrose\"],\n", + " \"galactose\": [\"D-galactose\", \"Galactose\"],\n", + " \"raffinose\": [\"D-raffinose\", \"Raffinose\"]\n", + " }\n", + " },\n", + " \"BrentLab/harbison_2004\": {\n", + " \"dataset\": {\n", + " \"harbison_2004\": {\n", + " \"growth_media\": {\n", + " \"field\": \"condition\",\n", + " \"path\": \"media.name\"\n", + " },\n", + " \"carbon_source\": {\n", + " \"field\": \"condition\",\n", + " \"path\": \"media.carbon_source.compound\"\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "config_path_extraction = Path(\"/tmp/metadata_extraction.yaml\")\n", + "with open(config_path_extraction, 'w') as f:\n", + " yaml.dump(extraction_config, f)\n", + "\n", + "# Build metadata with extraction\n", + "builder_extraction = MetadataBuilder(config_path_extraction)\n", + "results_extraction = builder_extraction.build_metadata(\n", + " repos=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", + " mode=\"samples\"\n", + ")\n", + "\n", + "df_extracted = results_extraction[\"BrentLab/harbison_2004\"][\"data\"]\n", + "\n", + "df_extracted\n", + "\n", + "# print(f\"Columns: {list(df_extracted.columns)}\")\n", + "# print(f\"\\nShape: {df_extracted.shape}\")\n", + "# print(\"\\nSample data showing base + extracted columns:\")\n", + "# print(df_extracted[['sample_id', 'condition', 'growth_media', 'carbon_source']].head(10))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "tfbpapi-py3.11", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/tutorials/my_comprehensive_filter.yaml b/docs/tutorials/my_comprehensive_metadata.yaml similarity index 100% rename from docs/tutorials/my_comprehensive_filter.yaml rename to docs/tutorials/my_comprehensive_metadata.yaml diff --git a/docs/tutorials/my_filter_config.yaml b/docs/tutorials/my_filter_config.yaml deleted file mode 100644 index 8c10ac6..0000000 --- a/docs/tutorials/my_filter_config.yaml +++ /dev/null @@ -1,19 +0,0 @@ -filters: - carbon_source: - - "D-glucose" - - "D-galactose" - -BrentLab/harbison_2004: - dataset: - harbison_2004: - carbon_source: - field: condition - path: media.carbon_source - -BrentLab/kemmeren_2014: - temperature: - path: temperature_celsius - dataset: - kemmeren_2014: - carbon_source: - path: media.carbon_source diff --git a/docs/tutorials/my_metadata_config.yaml b/docs/tutorials/my_metadata_config.yaml new file mode 100644 index 0000000..2781954 --- /dev/null +++ b/docs/tutorials/my_metadata_config.yaml @@ -0,0 +1,35 @@ +BrentLab/harbison_2004: + dataset: + harbison_2004: + carbon_source: + field: condition + path: media.carbon_source + temperature_celsius: + field: condition + path: temperature_celsius +BrentLab/kemmeren_2014: + dataset: + kemmeren_2014: + carbon_source: + path: media.carbon_source + temperature_celsius: + path: temperature_celsius +factor_aliases: + carbon_source: + galactose: + - D-galactose + - Galactose + - galactose + glucose: + - D-glucose + - dextrose + - glucose + raffinose: + - D-raffinose + - raffinose + temperature_celsius: + heat_shock: + - 37 + - 39 + standard: + - 30 diff --git a/tfbpapi/datainfo/datacard.py b/tfbpapi/datainfo/datacard.py index d11e378..370a677 100644 --- a/tfbpapi/datainfo/datacard.py +++ b/tfbpapi/datainfo/datacard.py @@ -796,6 +796,96 @@ def list_experimental_condition_fields(self, config_name: str) -> list[str]: if f.role == "experimental_condition" ] + def summarize_field_levels( + self, + config_name: str, + field_name: str, + properties: list[str] | None = None, + max_levels: int | None = None, + ) -> str: + """ + Get a human-readable summary of field levels and their key properties. + + Provides a formatted summary showing each level (value) and selected properties + from its definition. Useful for quickly exploring experimental condition + structures without writing loops. + + :param config_name: Configuration name + :param field_name: Field name to summarize + :param properties: Optional list of property paths to display (e.g., ["media.name", "temperature_celsius"]). + If None, shows all top-level keys. + :param max_levels: Optional limit on number of levels to show + :return: Formatted string summary + :raises DataCardError: If config or field not found + + Example: + >>> card = DataCard("BrentLab/harbison_2004") + >>> # Show summary with specific properties + >>> print(card.summarize_field_levels( + ... "harbison_2004", + ... "condition", + ... properties=["media.carbon_source.compound", "temperature_celsius"] + ... )) + Field: condition + Levels: 14 + + YPD: + media.carbon_source.compound: ['D-glucose'] + temperature_celsius: 30 + + GAL: + media.carbon_source.compound: ['D-galactose'] + temperature_celsius: 30 + ... + + """ + from tfbpapi.datainfo.metadata_builder import get_nested_value + + # Get definitions + definitions = self.get_field_definitions(config_name, field_name) + + if not definitions: + return f"Field '{field_name}' has no definitions" + + lines = [ + f"Field: {field_name}", + f"Levels: {len(definitions)}", + "", + ] + + # Determine how many levels to show + levels_to_show = list(definitions.keys()) + if max_levels: + levels_to_show = levels_to_show[:max_levels] + + for level_name in levels_to_show: + definition = definitions[level_name] + lines.append(f"{level_name}:") + + if properties: + # Show only specified properties + for prop_path in properties: + value = get_nested_value(definition, prop_path) + lines.append(f" {prop_path}: {value}") + else: + # Show all top-level keys + if isinstance(definition, dict): + for key in definition.keys(): + value = definition[key] + # Truncate long values + value_str = str(value) + if len(value_str) > 80: + value_str = value_str[:77] + "..." + lines.append(f" {key}: {value_str}") + + lines.append("") + + if max_levels and len(definitions) > max_levels: + remaining = len(definitions) - max_levels + lines.append(f"... and {remaining} more levels") + + return "\n".join(lines) + def summary(self) -> str: """Get a human-readable summary of the dataset.""" card = self.dataset_card diff --git a/tfbpapi/datainfo/metadata_builder.py b/tfbpapi/datainfo/metadata_builder.py index 66208eb..112c46a 100644 --- a/tfbpapi/datainfo/metadata_builder.py +++ b/tfbpapi/datainfo/metadata_builder.py @@ -51,14 +51,34 @@ def get_nested_value(data: dict, path: str) -> Any: """ - Navigate nested dict using dot notation. + Navigate nested dict/list using dot notation. Handles missing intermediate keys gracefully by returning None. + Supports extracting properties from lists of dicts. :param data: Dictionary to navigate - :param path: Dot-separated path (e.g., "media.carbon_source") + :param path: Dot-separated path (e.g., "media.carbon_source.compound") :return: Value at path or None if not found + Examples: + # Simple nested dict + get_nested_value({"media": {"name": "YPD"}}, "media.name") + -> "YPD" + + # List of dicts - extract property from each item + get_nested_value( + {"media": {"carbon_source": [{"compound": "glucose"}, {"compound": "galactose"}]}}, + "media.carbon_source.compound" + ) + -> ["glucose", "galactose"] + + # List of dicts - get the list itself + get_nested_value( + {"media": {"carbon_source": [{"compound": "glucose"}]}}, + "media.carbon_source" + ) + -> [{"compound": "glucose"}] + """ if not isinstance(data, dict): return None @@ -66,43 +86,29 @@ def get_nested_value(data: dict, path: str) -> Any: keys = path.split(".") current = data - for key in keys: - if not isinstance(current, dict) or key not in current: + for i, key in enumerate(keys): + if isinstance(current, dict): + if key not in current: + return None + current = current[key] + elif isinstance(current, list): + # If current is a list and we have more keys, extract property from each item + if i < len(keys): + # Extract the remaining path from each list item + remaining_path = ".".join(keys[i:]) + results = [] + for item in current: + if isinstance(item, dict): + val = get_nested_value(item, remaining_path) + if val is not None: + results.append(val) + return results if results else None + else: return None - current = current[key] return current -def extract_compound_names(value: Any) -> list[str]: - """ - Extract compound names from various representations. - - Handles: - - List of dicts: [{"compound": "D-glucose", ...}] -> ["D-glucose"] - - String: "D-glucose" -> ["D-glucose"] - - None or "unspecified" -> [] - - :param value: Value to extract from - :return: List of compound names - - """ - if value is None or value == "unspecified": - return [] - - if isinstance(value, str): - return [value] - - if isinstance(value, list): - compounds = [] - for item in value: - if isinstance(item, dict) and "compound" in item: - compounds.append(item["compound"]) - elif isinstance(item, str): - compounds.append(item) - return compounds - - return [] def normalize_value(actual_value: Any, aliases: dict[str, list[Any]] | None) -> str: @@ -381,11 +387,8 @@ def _extract_repo_level( if value is None: continue - # Extract compound names if needed - if isinstance(value, list) and value and isinstance(value[0], dict): - actual_values = extract_compound_names(value) - else: - actual_values = [value] if not isinstance(value, list) else value + # Ensure value is a list for consistent processing + actual_values = [value] if not isinstance(value, list) else value # Normalize using aliases (if configured) aliases = self.factor_aliases.get(prop_name) @@ -442,13 +445,8 @@ def _extract_field_level( if value is None: continue - # Extract compound names if needed - if isinstance(value, list) and value and isinstance(value[0], dict): - actual_values = extract_compound_names(value) - else: - actual_values = ( - [value] if not isinstance(value, list) else value - ) + # Ensure value is a list for consistent processing + actual_values = [value] if not isinstance(value, list) else value # Normalize using aliases (if configured) aliases = self.factor_aliases.get(prop_name) @@ -471,40 +469,107 @@ def _get_sample_metadata( Get sample-level metadata (Mode 1). Returns one row per sample_id with metadata columns. Includes ALL samples (no - filtering). + filtering). Selects base metadata_fields columns plus adds extracted columns + from field-level property mappings. :param repo_id: Repository ID :param config_name: Configuration name - :param metadata: Extracted metadata dict + :param metadata: Extracted metadata dict with field_values :param token: Optional HuggingFace token - :return: DataFrame with sample metadata + :return: DataFrame with sample metadata including extracted columns """ - # Initialize query API - api = HfQueryAPI(repo_id, token=token) - - # Query for all samples (no WHERE clause filtering) - sql = f""" - SELECT DISTINCT * - FROM {config_name} - """ - try: + # Load DataCard to get metadata_fields + card = DataCard(repo_id, token=token) + config = card.get_config(config_name) + + # Initialize query API + api = HfQueryAPI(repo_id, token=token) + + # Determine which columns to select + if config and hasattr(config, 'metadata_fields') and config.metadata_fields: + # Select only metadata fields + columns = ", ".join(config.metadata_fields) + # Add sample_id if not already in metadata_fields + if "sample_id" not in config.metadata_fields: + columns = f"sample_id, {columns}" + sql = f""" + SELECT DISTINCT {columns} + FROM {config_name} + """ + else: + # No metadata_fields specified, select all + sql = f""" + SELECT DISTINCT * + FROM {config_name} + """ + df = api.query(sql, config_name) # For sample-level, we want one row per sample_id if "sample_id" in df.columns: df_samples = df.groupby("sample_id").first().reset_index() - return df_samples else: # No sample_id column, return distinct rows - return df + df_samples = df + + # Add extracted columns from field-level metadata + if "field_values" in metadata: + df_samples = self._add_extracted_columns(df_samples, metadata["field_values"]) + + return df_samples except Exception as e: return pd.DataFrame( {"error": [str(e)], "note": ["Failed to retrieve sample metadata"]} ) + def _add_extracted_columns( + self, + df: pd.DataFrame, + field_values: dict[str, dict[str, Any]] + ) -> pd.DataFrame: + """ + Add columns extracted from field-level property mappings. + + For each field that has property mappings, creates new columns by looking up + the field value in field_values and extracting the mapped properties. + + :param df: DataFrame with base metadata + :param field_values: Dict mapping field values to their extracted properties + :return: DataFrame with additional extracted columns + + """ + # Group properties by source field + # field_values is like: {"YPD": {"growth_media": ["YPD"], "carbon_source": ["glucose"]}} + + # We need to determine which field each row's value comes from + # For harbison_2004, the field is "condition" + + # Find fields that have definitions (these are the ones we can map) + for field_value, properties in field_values.items(): + # Each property becomes a new column + for prop_name, prop_values in properties.items(): + # Initialize column if it doesn't exist + if prop_name not in df.columns: + df[prop_name] = None + + # For each row where the field matches this field_value, set the property + # We need to find which column contains field_value + for col in df.columns: + if col in [prop_name, "sample_id"]: + continue + # Check if this column contains field_value + mask = df[col] == field_value + if mask.any(): + # Set the property value for matching rows + # Take first value from list (normalized values are lists) + value = prop_values[0] if prop_values else None + df.loc[mask, prop_name] = value + + return df + def _get_full_data( self, repo_id: str, @@ -516,13 +581,16 @@ def _get_full_data( Get full data with all measurements (Mode 2). Returns many rows per sample_id (one per measured feature/target). Includes ALL - data (no filtering). + data (no filtering) and ALL columns (both metadata and measurements). + + Unlike samples mode which respects metadata_fields, this mode returns the complete + dataset including quantitative measurements. :param repo_id: Repository ID :param config_name: Configuration name :param metadata: Extracted metadata dict :param token: Optional HuggingFace token - :return: DataFrame with full data + :return: DataFrame with full data (all columns) """ # Initialize query API diff --git a/tfbpapi/tests/datainfo/test_metadata_builder.py b/tfbpapi/tests/datainfo/test_metadata_builder.py index 1419264..cee3b71 100644 --- a/tfbpapi/tests/datainfo/test_metadata_builder.py +++ b/tfbpapi/tests/datainfo/test_metadata_builder.py @@ -12,7 +12,6 @@ from tfbpapi.datainfo.metadata_builder import ( MetadataBuilder, - extract_compound_names, get_nested_value, normalize_value, ) @@ -52,43 +51,87 @@ def test_non_dict_input(self): assert get_nested_value(None, "path") is None -class TestExtractCompoundNames: - """Test extract_compound_names function.""" +class TestGetNestedValueListExtraction: + """Test get_nested_value list extraction functionality.""" - def test_list_of_dicts(self): - """Test extracting from list of dicts with compound field.""" - value = [ - {"compound": "D-glucose", "concentration_percent": 2}, - {"compound": "D-galactose", "concentration_percent": 1}, - ] - assert extract_compound_names(value) == ["D-glucose", "D-galactose"] - - def test_string_value(self): - """Test extracting from simple string.""" - assert extract_compound_names("D-glucose") == ["D-glucose"] - - def test_none_value(self): - """Test that None returns empty list.""" - assert extract_compound_names(None) == [] - - def test_unspecified_value(self): - """Test that 'unspecified' returns empty list.""" - assert extract_compound_names("unspecified") == [] - - def test_list_of_strings(self): - """Test extracting from list of strings.""" - assert extract_compound_names(["glucose", "galactose"]) == [ - "glucose", - "galactose", - ] - - def test_mixed_list(self): - """Test extracting from mixed list.""" - value = [ + def test_extract_from_list_of_dicts(self): + """Test extracting property from list of dicts.""" + data = { + "media": { + "carbon_source": [ + {"compound": "D-glucose", "concentration_percent": 2}, + {"compound": "D-galactose", "concentration_percent": 1}, + ] + } + } + result = get_nested_value(data, "media.carbon_source.compound") + assert result == ["D-glucose", "D-galactose"] + + def test_extract_concentration_from_list(self): + """Test extracting numeric property from list of dicts.""" + data = { + "media": { + "carbon_source": [ + {"compound": "D-glucose", "concentration_percent": 2}, + {"compound": "D-galactose", "concentration_percent": 1}, + ] + } + } + result = get_nested_value(data, "media.carbon_source.concentration_percent") + assert result == [2, 1] + + def test_get_list_itself(self): + """Test getting the list without extracting a property.""" + data = { + "media": { + "carbon_source": [ + {"compound": "D-glucose"}, + {"compound": "D-galactose"}, + ] + } + } + result = get_nested_value(data, "media.carbon_source") + expected = [ {"compound": "D-glucose"}, - "maltose", + {"compound": "D-galactose"}, ] - assert extract_compound_names(value) == ["D-glucose", "maltose"] + assert result == expected + + def test_extract_from_single_item_list(self): + """Test extracting from list with single item.""" + data = { + "media": { + "carbon_source": [{"compound": "D-glucose"}] + } + } + result = get_nested_value(data, "media.carbon_source.compound") + assert result == ["D-glucose"] + + def test_extract_missing_property_from_list(self): + """Test extracting non-existent property from list items.""" + data = { + "media": { + "carbon_source": [ + {"compound": "D-glucose"}, + {"compound": "D-galactose"}, + ] + } + } + result = get_nested_value(data, "media.carbon_source.missing_key") + assert result is None + + def test_nested_list_extraction(self): + """Test extracting from nested structures with lists.""" + data = { + "level1": { + "level2": [ + {"level3": {"value": "a"}}, + {"level3": {"value": "b"}}, + ] + } + } + result = get_nested_value(data, "level1.level2.level3.value") + assert result == ["a", "b"] class TestNormalizeValue: From e91565b5601f1135b7a47602aeba29c9aee1854b Mon Sep 17 00:00:00 2001 From: chasem Date: Tue, 16 Dec 2025 15:17:20 -0600 Subject: [PATCH 46/49] tmp --- docs/tutorials/my_comprehensive_metadata.yaml | 22 ------------------- docs/tutorials/my_metadata_config.yaml | 22 +++++++++++-------- 2 files changed, 13 insertions(+), 31 deletions(-) delete mode 100644 docs/tutorials/my_comprehensive_metadata.yaml diff --git a/docs/tutorials/my_comprehensive_metadata.yaml b/docs/tutorials/my_comprehensive_metadata.yaml deleted file mode 100644 index 6bd4668..0000000 --- a/docs/tutorials/my_comprehensive_metadata.yaml +++ /dev/null @@ -1,22 +0,0 @@ -BrentLab/harbison_2004: - dataset: - harbison_2004: - carbon_source: - field: condition - path: media.carbon_source - temperature_celsius: - field: condition - path: temperature_celsius -BrentLab/kemmeren_2014: - dataset: - kemmeren_2014: - carbon_source: - path: media.carbon_source - temperature_celsius: - path: temperature_celsius -filters: - carbon_source: - - D-glucose - - D-galactose - temperature_celsius: - - 30 diff --git a/docs/tutorials/my_metadata_config.yaml b/docs/tutorials/my_metadata_config.yaml index 2781954..6338159 100644 --- a/docs/tutorials/my_metadata_config.yaml +++ b/docs/tutorials/my_metadata_config.yaml @@ -1,25 +1,31 @@ BrentLab/harbison_2004: + nitrogen_source: + path: media.nitrogen_source.name dataset: harbison_2004: + phosphate_source: + path: media.phosphate_source.compound carbon_source: field: condition - path: media.carbon_source + path: media.carbon_source.compound temperature_celsius: field: condition path: temperature_celsius + BrentLab/kemmeren_2014: dataset: kemmeren_2014: carbon_source: - path: media.carbon_source + path: media.carbon_source.compound temperature_celsius: path: temperature_celsius + factor_aliases: carbon_source: galactose: - D-galactose + # should not have to include the actual name of the column -- that can be inferred - Galactose - - galactose glucose: - D-glucose - dextrose @@ -27,9 +33,7 @@ factor_aliases: raffinose: - D-raffinose - raffinose - temperature_celsius: - heat_shock: - - 37 - - 39 - standard: - - 30 + +description: + carbon_source: + media: \ No newline at end of file From e3422e5260d3873b3f822caae584bf56c27f9e02 Mon Sep 17 00:00:00 2001 From: chasem Date: Wed, 17 Dec 2025 19:56:55 -0600 Subject: [PATCH 47/49] tmp --- docs/HfCacheManager.md | 1 - docs/HfQueryAPI.md | 1 - docs/HfRankResponse.md | 1 - docs/IncrementalAnalysisDB.md | 1 - docs/datacard.md | 6 + docs/datainfo.md | 133 - docs/errors.md | 29 +- docs/fetchers.md | 16 + docs/hf_cache_manager.md | 6 + docs/index.md | 14 +- docs/models.md | 41 + docs/tutorials/cache_manager_tutorial.ipynb | 266 +- docs/tutorials/correlation_tutorial.ipynb | 3420 ----------------- docs/tutorials/datacard_tutorial.ipynb | 74 +- docs/tutorials/hfqueryapi_tutorial.ipynb | 2214 ----------- .../tutorials/metadata_builder_tutorial.ipynb | 938 ----- .../tutorials/metadata_manager_tutorial.ipynb | 586 --- docs/tutorials/my_metadata_config.yaml | 39 - docs/tutorials/passing_callingcards.csv | 82 - docs/tutorials/rank_response_tutorial.ipynb | 968 ----- docs/tutorials/sample_manager_tutorial.ipynb | 814 ---- docs/tutorials/virtual_db_tutorial.ipynb | 721 ++++ docs/virtual_database_concepts.md | 295 ++ docs/virtual_db.md | 16 + example_filter_config.yaml | 75 - mkdocs.yml | 27 +- tfbpapi/HfQueryAPI.py | 739 ---- tfbpapi/HfRankResponse.py | 482 --- tfbpapi/IncrementalAnalysisDB.py | 340 -- tfbpapi/RankResponseAnalysis.py | 277 -- tfbpapi/__init__.py | 33 + tfbpapi/constants.py | 44 - tfbpapi/{datainfo => }/datacard.py | 387 +- tfbpapi/datainfo/__init__.py | 31 - tfbpapi/datainfo/metadata_builder.py | 619 --- tfbpapi/datainfo/metadata_config_models.py | 312 -- tfbpapi/datainfo/metadata_manager.py | 282 -- tfbpapi/datainfo/models.py | 222 -- tfbpapi/datainfo/sample_manager.py | 774 ---- tfbpapi/errors.py | 220 +- tfbpapi/{datainfo => }/fetchers.py | 4 +- ...{HfCacheManager.py => hf_cache_manager.py} | 66 +- tfbpapi/models.py | 584 +++ tfbpapi/tests/conftest.py | 1439 +++++++ tfbpapi/tests/datainfo/__init__.py | 1 - tfbpapi/tests/datainfo/conftest.py | 1434 ------- tfbpapi/tests/datainfo/test_datacard.py | 775 ---- .../tests/datainfo/test_metadata_builder.py | 375 -- .../tests/datainfo/test_metadata_manager.py | 243 -- .../tests/{datainfo => }/example_datacards.py | 0 .../huggingface_collection_datacards.txt | 0 tfbpapi/tests/test_HfQueryAPI.py | 859 ----- tfbpapi/tests/test_HfRankResponse.py | 505 --- tfbpapi/tests/test_IncrementalAnalysisDB.py | 341 -- tfbpapi/tests/test_datacard.py | 449 +++ .../{datainfo => }/test_datacard_parsing.py | 4 +- tfbpapi/tests/{datainfo => }/test_fetchers.py | 38 +- ...cheManager.py => test_hf_cache_manager.py} | 35 +- .../test_metadata_config_models.py | 142 +- tfbpapi/tests/{datainfo => }/test_models.py | 2 +- .../{datainfo => }/test_real_datacards.py | 2 +- tfbpapi/tests/test_virtual_db.py | 507 +++ tfbpapi/virtual_db.py | 796 ++++ 63 files changed, 5313 insertions(+), 18834 deletions(-) delete mode 100644 docs/HfCacheManager.md delete mode 100644 docs/HfQueryAPI.md delete mode 100644 docs/HfRankResponse.md delete mode 100644 docs/IncrementalAnalysisDB.md create mode 100644 docs/datacard.md delete mode 100644 docs/datainfo.md create mode 100644 docs/fetchers.md create mode 100644 docs/hf_cache_manager.md create mode 100644 docs/models.md delete mode 100644 docs/tutorials/correlation_tutorial.ipynb delete mode 100644 docs/tutorials/hfqueryapi_tutorial.ipynb delete mode 100644 docs/tutorials/metadata_builder_tutorial.ipynb delete mode 100644 docs/tutorials/metadata_manager_tutorial.ipynb delete mode 100644 docs/tutorials/my_metadata_config.yaml delete mode 100644 docs/tutorials/passing_callingcards.csv delete mode 100644 docs/tutorials/rank_response_tutorial.ipynb delete mode 100644 docs/tutorials/sample_manager_tutorial.ipynb create mode 100644 docs/tutorials/virtual_db_tutorial.ipynb create mode 100644 docs/virtual_database_concepts.md create mode 100644 docs/virtual_db.md delete mode 100644 example_filter_config.yaml delete mode 100644 tfbpapi/HfQueryAPI.py delete mode 100644 tfbpapi/HfRankResponse.py delete mode 100644 tfbpapi/IncrementalAnalysisDB.py delete mode 100644 tfbpapi/RankResponseAnalysis.py rename tfbpapi/{datainfo => }/datacard.py (59%) delete mode 100644 tfbpapi/datainfo/__init__.py delete mode 100644 tfbpapi/datainfo/metadata_builder.py delete mode 100644 tfbpapi/datainfo/metadata_config_models.py delete mode 100644 tfbpapi/datainfo/metadata_manager.py delete mode 100644 tfbpapi/datainfo/models.py delete mode 100644 tfbpapi/datainfo/sample_manager.py rename tfbpapi/{datainfo => }/fetchers.py (98%) rename tfbpapi/{HfCacheManager.py => hf_cache_manager.py} (88%) create mode 100644 tfbpapi/models.py delete mode 100644 tfbpapi/tests/datainfo/__init__.py delete mode 100644 tfbpapi/tests/datainfo/conftest.py delete mode 100644 tfbpapi/tests/datainfo/test_datacard.py delete mode 100644 tfbpapi/tests/datainfo/test_metadata_builder.py delete mode 100644 tfbpapi/tests/datainfo/test_metadata_manager.py rename tfbpapi/tests/{datainfo => }/example_datacards.py (100%) rename tfbpapi/tests/{datainfo => }/huggingface_collection_datacards.txt (100%) delete mode 100644 tfbpapi/tests/test_HfQueryAPI.py delete mode 100644 tfbpapi/tests/test_HfRankResponse.py delete mode 100644 tfbpapi/tests/test_IncrementalAnalysisDB.py create mode 100644 tfbpapi/tests/test_datacard.py rename tfbpapi/tests/{datainfo => }/test_datacard_parsing.py (98%) rename tfbpapi/tests/{datainfo => }/test_fetchers.py (93%) rename tfbpapi/tests/{test_HfCacheManager.py => test_hf_cache_manager.py} (94%) rename tfbpapi/tests/{datainfo => }/test_metadata_config_models.py (75%) rename tfbpapi/tests/{datainfo => }/test_models.py (99%) rename tfbpapi/tests/{datainfo => }/test_real_datacards.py (99%) create mode 100644 tfbpapi/tests/test_virtual_db.py create mode 100644 tfbpapi/virtual_db.py diff --git a/docs/HfCacheManager.md b/docs/HfCacheManager.md deleted file mode 100644 index 89d5c77..0000000 --- a/docs/HfCacheManager.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.HfCacheManager.HfCacheManager diff --git a/docs/HfQueryAPI.md b/docs/HfQueryAPI.md deleted file mode 100644 index 357ea30..0000000 --- a/docs/HfQueryAPI.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.HfQueryAPI.HfQueryAPI \ No newline at end of file diff --git a/docs/HfRankResponse.md b/docs/HfRankResponse.md deleted file mode 100644 index e2ffd53..0000000 --- a/docs/HfRankResponse.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.HfRankResponse.HfRankResponse \ No newline at end of file diff --git a/docs/IncrementalAnalysisDB.md b/docs/IncrementalAnalysisDB.md deleted file mode 100644 index 7dc07d7..0000000 --- a/docs/IncrementalAnalysisDB.md +++ /dev/null @@ -1 +0,0 @@ -::: tfbpapi.IncrementalAnalysisDB.IncrementalAnalysisDB \ No newline at end of file diff --git a/docs/datacard.md b/docs/datacard.md new file mode 100644 index 0000000..cfab1f1 --- /dev/null +++ b/docs/datacard.md @@ -0,0 +1,6 @@ +# DataCard + +::: tfbpapi.datacard.DataCard + options: + show_root_heading: true + show_source: true diff --git a/docs/datainfo.md b/docs/datainfo.md deleted file mode 100644 index e159e7b..0000000 --- a/docs/datainfo.md +++ /dev/null @@ -1,133 +0,0 @@ -# DataInfo Package - -The `datainfo` package provides dataset information management for HuggingFace datasets. It enables exploration of dataset metadata, structure, and relationships without loading actual genomic data. - -## Overview - -The datainfo package consists of three main components: - -- **DataCard**: High-level interface for exploring dataset metadata -- **Fetchers**: Low-level components for retrieving data from HuggingFace Hub -- **Models**: Pydantic models for validation and type safety - -## Main Interface - -### DataCard -::: tfbpapi.datainfo.datacard.DataCard - options: - show_root_heading: true - show_source: true - -The `DataCard` class is the primary interface for exploring HuggingFace datasets. It provides methods to: - -- Discover dataset configurations and types -- Explore feature schemas and data types -- Understand metadata relationships -- Extract field values and experimental conditions -- Navigate partitioned dataset structures - -## Data Models - -### Core Models -::: tfbpapi.datainfo.models.DatasetCard - options: - show_root_heading: true - -::: tfbpapi.datainfo.models.DatasetConfig - options: - show_root_heading: true - -::: tfbpapi.datainfo.models.FeatureInfo - options: - show_root_heading: true - -### Dataset Types -::: tfbpapi.datainfo.models.DatasetType - options: - show_root_heading: true - -### Relationship Models -::: tfbpapi.datainfo.models.MetadataRelationship - options: - show_root_heading: true - -::: tfbpapi.datainfo.models.ExtractedMetadata - options: - show_root_heading: true - -## Data Fetchers - -### HuggingFace Integration -::: tfbpapi.datainfo.fetchers.HfDataCardFetcher - options: - show_root_heading: true - -::: tfbpapi.datainfo.fetchers.HfRepoStructureFetcher - options: - show_root_heading: true - -::: tfbpapi.datainfo.fetchers.HfSizeInfoFetcher - options: - show_root_heading: true - -## Usage Examples - -### Basic Dataset Exploration - -```python -from tfbpapi.datainfo import DataCard - -# Initialize DataCard for a repository -card = DataCard('BrentLab/rossi_2021') - -# Get repository overview -repo_info = card.get_repository_info() -print(f"Dataset: {repo_info['pretty_name']}") -print(f"Configurations: {repo_info['num_configs']}") - -# Explore configurations -for config in card.configs: - print(f"{config.config_name}: {config.dataset_type.value}") -``` - -### Understanding Dataset Structure - -```python -# Get detailed config information -config_info = card.explore_config('metadata') -print(f"Features: {config_info['num_features']}") - -# Check for partitioned data -if 'partitioning' in config_info: - partition_info = config_info['partitioning'] - print(f"Partitioned by: {partition_info['partition_by']}") -``` - -### Metadata Relationships - -```python -# Discover metadata relationships -relationships = card.get_metadata_relationships() -for rel in relationships: - print(f"{rel.data_config} -> {rel.metadata_config} ({rel.relationship_type})") -``` - -## Integration with HfQueryAPI - -The datainfo package is designed to work seamlessly with `HfQueryAPI` for efficient data loading: - -```python -from tfbpapi import HfQueryAPI -from tfbpapi.datainfo import DataCard - -# Explore dataset structure first -card = DataCard('BrentLab/rossi_2021') -config_info = card.explore_config('genome_map') - -# Use insights to load data efficiently -query_api = HfQueryAPI('BrentLab/rossi_2021') -data = query_api.get_pandas('genome_map', - filters={'run_accession': 'SRR123456'}) -``` - -For a complete tutorial, see the [DataCard Tutorial](tutorials/datacard_tutorial.ipynb). \ No newline at end of file diff --git a/docs/errors.md b/docs/errors.md index cfbf1df..6ba92ff 100644 --- a/docs/errors.md +++ b/docs/errors.md @@ -1 +1,28 @@ -::: tfbpapi.errors \ No newline at end of file +# Custom Exceptions + +## HfDataFetchError + +::: tfbpapi.errors.HfDataFetchError + options: + show_root_heading: true + show_source: true + +Raised when HuggingFace API requests fail during data fetching operations. + +## DataCardError + +::: tfbpapi.errors.DataCardError + options: + show_root_heading: true + show_source: true + +Base exception for DataCard operations. + +## DataCardValidationError + +::: tfbpapi.errors.DataCardValidationError + options: + show_root_heading: true + show_source: true + +Raised when dataset card validation fails during parsing or loading. \ No newline at end of file diff --git a/docs/fetchers.md b/docs/fetchers.md new file mode 100644 index 0000000..2901a79 --- /dev/null +++ b/docs/fetchers.md @@ -0,0 +1,16 @@ +# Data Fetchers + +::: tfbpapi.fetchers.HfDataCardFetcher + options: + show_root_heading: true + show_source: true + +::: tfbpapi.fetchers.HfRepoStructureFetcher + options: + show_root_heading: true + show_source: true + +::: tfbpapi.fetchers.HfSizeInfoFetcher + options: + show_root_heading: true + show_source: true diff --git a/docs/hf_cache_manager.md b/docs/hf_cache_manager.md new file mode 100644 index 0000000..752b712 --- /dev/null +++ b/docs/hf_cache_manager.md @@ -0,0 +1,6 @@ +# HfCacheManager + +::: tfbpapi.hf_cache_manager.HfCacheManager + options: + show_root_heading: true + show_source: true diff --git a/docs/index.md b/docs/index.md index a50a31b..44b9d63 100644 --- a/docs/index.md +++ b/docs/index.md @@ -24,17 +24,17 @@ This is a Python package for interfacing with a collection of datasets hosted on ### Core Components -- **HfQueryAPI** (`tfbpapi/HfQueryAPI.py`): Main interface for querying HF datasets with intelligent downloading and SQL querying capabilities. Supports automatic dataset size detection, selective downloading, and DuckDB-based querying. +- **VirtualDB** (`tfbpapi/virtual_db.py`): Primary API for unified cross-dataset queries. Provides standardized query interface across heterogeneous datasets with varying experimental condition structures through external YAML configuration. -- **HfCacheManager** (`tfbpapi/HfCacheManager.py`): Manages HF cache with cleanup and size management features. Provides automatic cache cleanup based on age and size thresholds. +- **DataCard** (`tfbpapi/datacard.py`): Interface for exploring HuggingFace dataset metadata without loading actual data. Enables dataset structure discovery, experimental condition exploration, and query planning. -- **HfRankResponse** (`tfbpapi/HfRankResponse.py`): Response handling for HF-based ranking operations. Computes and analyzes "rank response" - the cumulative number of responsive targets binned by binding rank scores. +- **HfCacheManager** (`tfbpapi/hf_cache_manager.py`): Manages HuggingFace cache with intelligent downloading, DuckDB-based SQL querying, and automatic cleanup based on age/size thresholds. -- **IncrementalAnalysisDB** (`tfbpapi/IncrementalAnalysisDB.py`): Database management for incremental analysis workflows with shared result storage. +### Supporting Components -### Dataset Information Management +- **Models** (`tfbpapi/models.py`): Pydantic models for dataset cards, configurations, features, and VirtualDB configuration (MetadataConfig, PropertyMapping, RepositoryConfig). -- **datainfo package** (`tfbpapi/datainfo/`): Comprehensive dataset exploration and metadata management for HuggingFace datasets. Provides the `DataCard` class for exploring dataset structure, configurations, and relationships without loading actual data. Includes Pydantic models for validation and fetchers for HuggingFace Hub integration. +- **Fetchers** (`tfbpapi/fetchers.py`): Low-level components for retrieving data from HuggingFace Hub (HfDataCardFetcher, HfRepoStructureFetcher, HfSizeInfoFetcher). ### Data Types @@ -49,7 +49,7 @@ Data is stored in Apache Parquet format, either as single files or parquet datas ### Error Handling -- **errors.py** (`tfbpapi/errors.py`): Custom exception classes for dataset management including `DatasetError`, `RepoTooLargeError`, `DataCardParsingError`, `HfDataFetchError`, and more. +- **errors.py** (`tfbpapi/errors.py`): Custom exception classes for dataset management including `HfDataFetchError`, `DataCardError`, and `DataCardValidationError`. ## Configuration diff --git a/docs/models.md b/docs/models.md new file mode 100644 index 0000000..631494d --- /dev/null +++ b/docs/models.md @@ -0,0 +1,41 @@ +# Pydantic Models + +## VirtualDB Configuration Models + +::: tfbpapi.models.MetadataConfig + options: + show_root_heading: true + +::: tfbpapi.models.RepositoryConfig + options: + show_root_heading: true + +::: tfbpapi.models.PropertyMapping + options: + show_root_heading: true + +## DataCard Models + +::: tfbpapi.models.DatasetCard + options: + show_root_heading: true + +::: tfbpapi.models.DatasetConfig + options: + show_root_heading: true + +::: tfbpapi.models.DatasetType + options: + show_root_heading: true + +::: tfbpapi.models.FeatureInfo + options: + show_root_heading: true + +::: tfbpapi.models.MetadataRelationship + options: + show_root_heading: true + +::: tfbpapi.models.ExtractedMetadata + options: + show_root_heading: true diff --git a/docs/tutorials/cache_manager_tutorial.ipynb b/docs/tutorials/cache_manager_tutorial.ipynb index a043417..a905a8b 100644 --- a/docs/tutorials/cache_manager_tutorial.ipynb +++ b/docs/tutorials/cache_manager_tutorial.ipynb @@ -30,25 +30,33 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chase/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" + ] + }, { "name": "stdout", "output_type": "stream", "text": [ - "HfCacheManager initialized for: BrentLab/hackett_2020\n", + "HfCacheManager initialized for: BrentLab/mahendrawada_2025\n", "DuckDB connection: Active\n", "Logger configured: Yes\n", - "Current HF cache size: 4.6G\n", - "Cached repositories: 9\n" + "Current HF cache size: 5.2G\n", + "Cached repositories: 10\n" ] } ], "source": [ "import duckdb\n", "import logging\n", - "from tfbpapi.HfCacheManager import HfCacheManager\n", + "from tfbpapi.hf_cache_manager import HfCacheManager\n", "from huggingface_hub import scan_cache_dir\n", "\n", "# Set up logging to see cache management activities\n", @@ -78,16 +86,16 @@ }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "HFCacheInfo(size_on_disk=4576042428, repos=frozenset({CachedRepoInfo(repo_id='BrentLab/mahendrawada_2025', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025'), size_on_disk=1997004, nb_files=9, revisions=frozenset({CachedRevisionInfo(commit_hash='8bea431be57e21633be4174df291540b1fae212a', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/8bea431be57e21633be4174df291540b1fae212a'), size_on_disk=18603, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/8bea431be57e21633be4174df291540b1fae212a/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/6a2a5a6a8006e386c0e47a2a98151442046d3d41'), size_on_disk=18603, blob_last_accessed=1758400385.1923163, blob_last_modified=1758155849.9076133)}), refs=frozenset({'main'}), last_modified=1758155849.9076133), CachedRevisionInfo(commit_hash='d602c1651f3117dd8d3c7443440eb819b7aa2ec2', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2'), size_on_disk=1963211, files=frozenset({CachedFileInfo(file_name='rnaseq_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2/rnaseq_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/4411789f50aab449658c100f2758ceb410022a0a30f956fd5fe41152915580f9'), size_on_disk=286605, blob_last_accessed=1757439452.1947446, blob_last_modified=1756858618.0563447), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/1bf59ab7ca7d0595586675ff09c4767b04037f29'), size_on_disk=15355, blob_last_accessed=1758055881.95639, blob_last_modified=1757439229.7199314), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756858617.9613454, blob_last_modified=1756858618.033345), CachedFileInfo(file_name='parse_mahendrawada.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2/scripts/parse_mahendrawada.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/c1130d6f8e06f94b1441f73821e3ae727663d0fa'), size_on_disk=6546, blob_last_accessed=1757439229.7259312, blob_last_modified=1757439229.7949307), CachedFileInfo(file_name='chec_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2/chec_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/dd3e967d8c54c6056dc9fd6bdb921c3eed9d10df3a449273bd08faeb1b936765'), size_on_disk=1220601, blob_last_accessed=1757439235.362876, blob_last_modified=1756858618.0613449), CachedFileInfo(file_name='features_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2/features_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/618ea89d880c8a1b4c6d05cc3c13a70cbb05784d10bf2b6c4fc954705203096c'), size_on_disk=431436, blob_last_accessed=1757439459.1206765, blob_last_modified=1756858618.1383443), CachedFileInfo(file_name='checksums.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/d602c1651f3117dd8d3c7443440eb819b7aa2ec2/checksums.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/bee0d3fd2c2bd4f07ec16489cdba8d5b4b6a2ae2'), size_on_disk=207, blob_last_accessed=1757439229.7169313, blob_last_modified=1757439229.7729309)}), refs=frozenset(), last_modified=1757439229.7949307), CachedRevisionInfo(commit_hash='66e0cf1fc85e136990b8230d6fa80b64c1091c7c', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/66e0cf1fc85e136990b8230d6fa80b64c1091c7c'), size_on_disk=1956293, files=frozenset({CachedFileInfo(file_name='features_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/66e0cf1fc85e136990b8230d6fa80b64c1091c7c/features_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/618ea89d880c8a1b4c6d05cc3c13a70cbb05784d10bf2b6c4fc954705203096c'), size_on_disk=431436, blob_last_accessed=1757439459.1206765, blob_last_modified=1756858618.1383443), CachedFileInfo(file_name='rnaseq_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/66e0cf1fc85e136990b8230d6fa80b64c1091c7c/rnaseq_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/4411789f50aab449658c100f2758ceb410022a0a30f956fd5fe41152915580f9'), size_on_disk=286605, blob_last_accessed=1757439452.1947446, blob_last_modified=1756858618.0563447), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/66e0cf1fc85e136990b8230d6fa80b64c1091c7c/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756858617.9613454, blob_last_modified=1756858618.033345), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/66e0cf1fc85e136990b8230d6fa80b64c1091c7c/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/2ababbde8810cb0f65bfbceb4f2b5b4b65e491c1'), size_on_disk=15190, blob_last_accessed=1756858550.24473, blob_last_modified=1756858550.24373), CachedFileInfo(file_name='chec_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/66e0cf1fc85e136990b8230d6fa80b64c1091c7c/chec_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/dd3e967d8c54c6056dc9fd6bdb921c3eed9d10df3a449273bd08faeb1b936765'), size_on_disk=1220601, blob_last_accessed=1757439235.362876, blob_last_modified=1756858618.0613449)}), refs=frozenset(), last_modified=1756858618.1383443)}), last_accessed=1758400385.1923163, last_modified=1758155849.9076133), CachedRepoInfo(repo_id='BrentLab/yeast_genome_resources', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources'), size_on_disk=114475, nb_files=7, revisions=frozenset({CachedRevisionInfo(commit_hash='15fdb72f8c31ae58f3e3ce7c90be279383743a2f', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/15fdb72f8c31ae58f3e3ce7c90be279383743a2f'), size_on_disk=88406, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/15fdb72f8c31ae58f3e3ce7c90be279383743a2f/features/chr=chrII/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/d64638adb2290412a962abffe0fbd0546ff139b29064bbf313b0a768449454a5'), size_on_disk=82276, blob_last_accessed=1755812630.656903, blob_last_modified=1755812631.0999012), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/15fdb72f8c31ae58f3e3ce7c90be279383743a2f/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/645da8028fd6abe86ee305f76eab78cdf29be364'), size_on_disk=6130, blob_last_accessed=1755819093.2316637, blob_last_modified=1755819093.2306638)}), refs=frozenset(), last_modified=1755819093.2306638), CachedRevisionInfo(commit_hash='42beb28478e27c5d4c5bb2308b12100e6489ef07', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/42beb28478e27c5d4c5bb2308b12100e6489ef07'), size_on_disk=6125, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/42beb28478e27c5d4c5bb2308b12100e6489ef07/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/ab6cc475062a2767a41b2aaa006faf4d9d2d60d6'), size_on_disk=6125, blob_last_accessed=1758155946.5559895, blob_last_modified=1758155946.5549896)}), refs=frozenset({'main'}), last_modified=1758155946.5549896), CachedRevisionInfo(commit_hash='25b47a191e40728efc2437d906e84317f922b36b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/25b47a191e40728efc2437d906e84317f922b36b'), size_on_disk=5426, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/25b47a191e40728efc2437d906e84317f922b36b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/9cdf7b342b249f25f2c2cbe46a2e0a26ded4f692'), size_on_disk=5426, blob_last_accessed=1755815405.3308983, blob_last_modified=1755815405.3298984)}), refs=frozenset(), last_modified=1755815405.3298984), CachedRevisionInfo(commit_hash='aae6e5ea8139e23940d11b4d7e77684c78bb7f6b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/aae6e5ea8139e23940d11b4d7e77684c78bb7f6b'), size_on_disk=4585, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/aae6e5ea8139e23940d11b4d7e77684c78bb7f6b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/4320f8f18620d988a5fc32872a671ace406fd0a2'), size_on_disk=4585, blob_last_accessed=1755814342.1923234, blob_last_modified=1755814342.1913235)}), refs=frozenset(), last_modified=1755814342.1913235), CachedRevisionInfo(commit_hash='7441b9a8c858332e730e7ddb9d255460ae698626', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/7441b9a8c858332e730e7ddb9d255460ae698626'), size_on_disk=87714, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/7441b9a8c858332e730e7ddb9d255460ae698626/features/chr=chrII/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/d64638adb2290412a962abffe0fbd0546ff139b29064bbf313b0a768449454a5'), size_on_disk=82276, blob_last_accessed=1755812630.656903, blob_last_modified=1755812631.0999012), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/7441b9a8c858332e730e7ddb9d255460ae698626/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/e63c2087b2e56dc15e8ac6cf5693c2391127b732'), size_on_disk=5438, blob_last_accessed=1755816785.70087, blob_last_modified=1755816785.6988702)}), refs=frozenset(), last_modified=1755816785.6988702), CachedRevisionInfo(commit_hash='a5ecb243ba27ca4b4d6ad1b60cc160c033ca2be6', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/a5ecb243ba27ca4b4d6ad1b60cc160c033ca2be6'), size_on_disk=82276, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/a5ecb243ba27ca4b4d6ad1b60cc160c033ca2be6/features/chr=chrII/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/d64638adb2290412a962abffe0fbd0546ff139b29064bbf313b0a768449454a5'), size_on_disk=82276, blob_last_accessed=1755812630.656903, blob_last_modified=1755812631.0999012)}), refs=frozenset(), last_modified=1755812631.0999012), CachedRevisionInfo(commit_hash='6607f7be76546563db0a68a9365c0a858bb5f3a1', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/6607f7be76546563db0a68a9365c0a858bb5f3a1'), size_on_disk=4495, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/6607f7be76546563db0a68a9365c0a858bb5f3a1/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/a31c4647c502767c22b5fe725ec8de58686a02d4'), size_on_disk=4495, blob_last_accessed=1755813454.8008156, blob_last_modified=1755813454.7998157)}), refs=frozenset(), last_modified=1755813454.7998157)}), last_accessed=1758155946.5559895, last_modified=1758155946.5549896), CachedRepoInfo(repo_id='BrentLab/barkai_compendium', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium'), size_on_disk=3579068970, nb_files=504, revisions=frozenset({CachedRevisionInfo(commit_hash='a987ef37c72fcd07b18828d320bc4305480daade', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade'), size_on_disk=3579068970, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417769/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e640018be0b2de04e0ed77c7586c413990876e91348531f43511c20da5fe279a'), size_on_disk=11568261, blob_last_accessed=1756926331.59533, blob_last_modified=1756926333.0913236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417930/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62f17648a07c7675d6a101e3d12efeae247c742e37eaacf96f95e03dceedefde'), size_on_disk=20574235, blob_last_accessed=1756926361.2242014, blob_last_modified=1756926362.5701957), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417693/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/98a5478f5bd4feb033ac69510f2bf785c87777377b7b8b6dce24b8f89dbd6a03'), size_on_disk=18580692, blob_last_accessed=1756926319.7013817, blob_last_modified=1756926321.9783719), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417686/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6a42f24b250baf0d88696bc123f01494a400b3f03f2ff2a1fc828f9295721a16'), size_on_disk=12070748, blob_last_accessed=1756926318.2063882, blob_last_modified=1756926319.627382), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417618/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d2ce91297551bc97b4fca46007f80c8846e5f9e9c78d9c1d7ded02fc31b3ef38'), size_on_disk=9239111, blob_last_accessed=1756926302.3654573, blob_last_modified=1756926304.3714485), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417724/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e5beee12039dfe6b4dae180a31ac0897dea725754554db43e2df9f99da94db58'), size_on_disk=4535402, blob_last_accessed=1756926324.69836, blob_last_modified=1756926325.9703546), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417794/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f15be566c029c52cf46f6258e589529afb3a002f010710a54d04dd6151f8b684'), size_on_disk=10205023, blob_last_accessed=1756926335.9063113, blob_last_modified=1756926337.151306), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417788/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fd6f44471f1b86568bc059f25e5ccec7bda2b85b9f8be536aeaf2305fe015f5d'), size_on_disk=9112879, blob_last_accessed=1756926334.546317, blob_last_modified=1756926335.971311), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4331f53e52f628f99a9695fa70339b08eb69a011cc8a3cbdb08dfa8e6c5d3ddd'), size_on_disk=10779086, blob_last_accessed=1756926376.8381338, blob_last_modified=1756926377.9141293), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417777/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bd953fd8a3ee2a5c90382c1c9adc8091fbca801aa87f07a409b6dd5a9704421f'), size_on_disk=1267333, blob_last_accessed=1756926332.9623241, blob_last_modified=1756926333.6353211), CachedFileInfo(file_name='genome_map.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b8f0ed936cb43c4649126e9b7596cef0ab626a2d'), size_on_disk=142786, blob_last_accessed=1756926300.410466, blob_last_modified=1756926300.4954655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380950/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f508a5da82832b920577592b853da2c2d76ebf0adca587e10b63507f46dc5066'), size_on_disk=3452513, blob_last_accessed=1756926368.7781687, blob_last_modified=1756926369.7081647), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417846/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a3dc440aac6a69d75ec73242a30e59d8183023a2677f1576b5e73d037d6e6654'), size_on_disk=2680468, blob_last_accessed=1756926346.0562673, blob_last_modified=1756926346.7392642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417662/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/90008c75533e5a395bf92b2a4382cfb8d6c27af4921615cac22bc1965375efb5'), size_on_disk=12701661, blob_last_accessed=1756926313.0204108, blob_last_modified=1756926314.158406), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417781/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9dd7989f90f8762246868ccd7d11c9ce876b2b33099efc3a4c5bf1bc7cbcca80'), size_on_disk=6923944, blob_last_accessed=1756926333.4783218, blob_last_modified=1756926335.3743136), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417689/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e27d1d153e5b135a09ec5db80bb05ace721fd60ac90f0693ac34ca3160fdfbdd'), size_on_disk=5015020, blob_last_accessed=1756926319.2973835, blob_last_modified=1756926321.220375), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417909/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c5de46a74c16aee32dfb2be89a50dceaa5704c0c550076dcec483d1c21d983af'), size_on_disk=5969725, blob_last_accessed=1756926357.3362184, blob_last_modified=1756926358.5162132), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417856/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3dc4e868f31759c9ac3d43f40097f5ed2f0efb6307e3bc1ccbed70c50a16ee4e'), size_on_disk=6126482, blob_last_accessed=1756926347.3832614, blob_last_modified=1756926348.3162575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417734/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/10d4c6d9acc5b56370fc32062262ae3871008e20e960c6b352ac27a4e229c905'), size_on_disk=1495194, blob_last_accessed=1756926325.9453547, blob_last_modified=1756926326.745351), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417669/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8eaf08a45dbd72787c5250403f3d3079db9902e77527c7ca438abc740fdfb753'), size_on_disk=7376929, blob_last_accessed=1756926314.6724036, blob_last_modified=1756926316.0383978), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381044/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/89a79f2c5f6258380ff4776af49db6119ac0c04342686705cfde94feebd7c9ca'), size_on_disk=3576881, blob_last_accessed=1756926776.065759, blob_last_modified=1756926778.8987432), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380932/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f214422d02adb2b0a1037eb424d8574123cf27d8a5cd1418feb5733b0249f60c'), size_on_disk=6251591, blob_last_accessed=1756926365.6181824, blob_last_modified=1756926366.5551784), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417937/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e432985b09ba72eab54fc008b87b20a718dbae96d920a61b47de7e16d214227c'), size_on_disk=6445266, blob_last_accessed=1756926362.6341953, blob_last_modified=1756926363.6641908), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417754/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0d840353f1148a08928dfb3177a0abfe635c406fc77d6d9958743585ade6b872'), size_on_disk=1101891, blob_last_accessed=1756926329.758338, blob_last_modified=1756926330.215336), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417757/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6c9c5dbd0513759ace8599b2542a1f99fe0c30feb318665ebdb55bfb111c8c67'), size_on_disk=5834465, blob_last_accessed=1756926330.011337, blob_last_modified=1756926330.7623336), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380964/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3a63df07de544662fb04d5388f34b0c8068efbc19f464b673d8a860a57b5b422'), size_on_disk=956844, blob_last_accessed=1756926370.333162, blob_last_modified=1756926370.8741596), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380926/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fd708dfb8084d767fde32c52acb58e785083ce7becbd2e160277f31460719346'), size_on_disk=6579161, blob_last_accessed=1756926364.8451858, blob_last_modified=1756926365.5441828), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417874/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6674c316eab3b03bbbe3502565efff7650445f5e1453933336d74938e42a14bf'), size_on_disk=6201897, blob_last_accessed=1756926350.5772476, blob_last_modified=1756926351.5142436), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417610/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a93c708ade83c1c4521589a51835a1dcc98972ab900e0eefdb8ce34e64394bf4'), size_on_disk=2566580, blob_last_accessed=1756926301.328462, blob_last_modified=1756926302.4814568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417793/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8a51bd4d953e697022a85f9b527afd45126988ba4bbb796d71c9e5f6c8fffc44'), size_on_disk=10797388, blob_last_accessed=1756926335.4883132, blob_last_modified=1756926336.7013078), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381050/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f3c3bac37a1d4ec633edf53ba305c970eb3de412fd54dfbaf0cdf89f39574334'), size_on_disk=1518412, blob_last_accessed=1756926777.1687527, blob_last_modified=1756926778.028748), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381016/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0e597280417f8f8b5b476bc9d66033afc9412759f5e2f9bd9f3367932cc6f03f'), size_on_disk=820782, blob_last_accessed=1756926377.9791288, blob_last_modified=1756926378.4041271), CachedFileInfo(file_name='GSE178430_metadata.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE178430_metadata.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9673758f13ec096ec4a89642bbe34d60975f5f05'), size_on_disk=61, blob_last_accessed=1756926300.2374666, blob_last_modified=1756926300.2994664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417670/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5e36b63ef1fbb55f821ed2c3d61f0d0bf86cf00846e86ff9dbe6f4d9597f9dc8'), size_on_disk=10881106, blob_last_accessed=1756926314.7704034, blob_last_modified=1756926315.980398), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380944/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7331bfc92dc902a721083ac4ea027481c90ce204d7dca7c493fec8f284db0155'), size_on_disk=8432479, blob_last_accessed=1756926367.299175, blob_last_modified=1756926368.6141694), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380928/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/251ffcb8e06f2ac0db0791065248327a9d1eeef33a07f5c6b25946e04b6c46d8'), size_on_disk=2289693, blob_last_accessed=1756926365.037185, blob_last_modified=1756926366.1961799), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380992/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dbc6b1de202e7fb25701c02607facc3e8c332550907ca0b70ba177533a311d6d'), size_on_disk=2551903, blob_last_accessed=1756926374.2281451, blob_last_modified=1756926375.0341415), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381068/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/16e4412527449a3cb9d2da129c3309a71b2ec9b33a7a39cb9bb31d41ce5d2e04'), size_on_disk=14909067, blob_last_accessed=1756926780.5687337, blob_last_modified=1756926782.3187242), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380961/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fc4356aaa2a30217145b4aecc4f3fdd2575fcd696a2603fea3d8112998f770c7'), size_on_disk=798599, blob_last_accessed=1756926370.1791627, blob_last_modified=1756926370.589161), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417612/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ee40f7af39e729ee79ef18d6777bbd632b7cc7811041a32d54f4a02b42638464'), size_on_disk=3509045, blob_last_accessed=1756926301.5874608, blob_last_modified=1756926302.6274562), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380933/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/07c22e11c56ce5ecb6c22c910c3d3a0d9b54fd341bef0513b4bf42a9c53ec6a0'), size_on_disk=10138164, blob_last_accessed=1756926365.8761814, blob_last_modified=1756926366.7471776), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380955/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/369e0f1de51ea197e928618e562613eeed2ed59ce5c1389ce07ff6dd96d391b4'), size_on_disk=3596907, blob_last_accessed=1756926369.4561658, blob_last_modified=1756926370.2661622), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380934/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d264f2a91c87696f6a07f573f1d921c91e1d53e3afca59610ba4703b3683b617'), size_on_disk=4601537, blob_last_accessed=1756926366.1091802, blob_last_modified=1756926366.9191768), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417755/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f4ffef985aed69a80417b5179ef9923bd73378a93a97f2dff128b1f7716b4599'), size_on_disk=8437424, blob_last_accessed=1756926329.777338, blob_last_modified=1756926331.2423315), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380967/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3abcaaa4db306d580dc27882172ced53d591b8aa69a1805bd2a9eea0448ff941'), size_on_disk=1245882, blob_last_accessed=1756926370.6731606, blob_last_modified=1756926371.1021585), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417903/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3ddfaef9dc57d0f63b8613408aba3af803dabd5ee87a1584edd9391b2b41bec3'), size_on_disk=11218813, blob_last_accessed=1756926355.9432244, blob_last_modified=1756926357.2232187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417888/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/961a35161b04a0e892f46a0a90b6d4776d7fe07ef82fc7016c95b1a885c4274f'), size_on_disk=9651505, blob_last_accessed=1756926353.298236, blob_last_modified=1756926354.65023), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417666/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/38008595447ab722c1889d0e19babf74299822ac7733504a4f09e1af1e4ad1ac'), size_on_disk=7712422, blob_last_accessed=1756926313.945407, blob_last_modified=1756926315.073402), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417837/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/04d099809ca78bd87dcb9921398681ac64e827439049a37e97a71ba0828debfb'), size_on_disk=8348990, blob_last_accessed=1756926344.4012744, blob_last_modified=1756926345.6152692), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380939/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dc6382d9dba7855504a6c7fa2a4adc44f564e25ff5c54eedd082c3cbe0a520a0'), size_on_disk=9235430, blob_last_accessed=1756926366.630178, blob_last_modified=1756926367.5861738), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417667/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef18f0ec136ef4b0facd217b32604556a7c9f514d75e26d2be17eb8da9b74a14'), size_on_disk=7979245, blob_last_accessed=1756926314.2284057, blob_last_modified=1756926315.734399), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417814/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9f70b1469a6d0ad8758aa7a44e754e07ccc401906f8af403a72075d763919535'), size_on_disk=7618977, blob_last_accessed=1756926339.5142956, blob_last_modified=1756926341.0872889), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381058/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9dc97b5892fcc8af593c03fad2040396b9b10626a655319ad40b38acd75e1d7b'), size_on_disk=11534270, blob_last_accessed=1756926778.543745, blob_last_modified=1756926779.7317386), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417730/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f2554716083cfa1bb54c2098315b92e1b3537eb265da884c00ba7e7d4c050da'), size_on_disk=14980864, blob_last_accessed=1756926325.5143564, blob_last_modified=1756926326.803351), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bbfeb8571ee8915d793698c2116cef9f2ea61573c66b4ae7066078271c6172d6'), size_on_disk=5132301, blob_last_accessed=1756926362.6441953, blob_last_modified=1756926363.7271905), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417830/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/21e2dbb49f07cb7c2d7277f5bf1a442e216a2991677c3c4ece5c51316795ea77'), size_on_disk=12760016, blob_last_accessed=1756926342.9432807, blob_last_modified=1756926344.1602755), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417614/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dac699f09872b0c58b742c692b341f0b00b7d83284cec5ef838c276a6f48908c'), size_on_disk=1759475, blob_last_accessed=1756926301.7274601, blob_last_modified=1756926302.4774568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381026/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e41f2d7a1b084a404e7f5e9e003b27f88665a983ec73859f7eb42cd8bfa2bdd3'), size_on_disk=2857045, blob_last_accessed=1756926378.898125, blob_last_modified=1756926379.7281213), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417721/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5457a24eab864fb369ae0e6fbdb302876e2a6c65ae83444f18bc01ad08ecfc0c'), size_on_disk=2073045, blob_last_accessed=1756926324.3243616, blob_last_modified=1756926324.9983587), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417843/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6569e23f0eb660e9f7111b9cdd69e7ae797ba265647ee44e4cbf2d8babdeca9e'), size_on_disk=2706587, blob_last_accessed=1756926345.3402703, blob_last_modified=1756926346.125267), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417752/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/efc9f1ecf4a7b06b23da46fb8c7d1879b67a9d6603ed753d09e2028396ec05c5'), size_on_disk=3420764, blob_last_accessed=1756926329.26534, blob_last_modified=1756926330.0613368), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380988/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/27b6e71986cf2b7da20b64dbda0ba47e08753f7184ddd57ed288f11df88d21e1'), size_on_disk=16063760, blob_last_accessed=1756926373.7421472, blob_last_modified=1756926374.932142), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381054/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1a9f9c650973937d852d2de54c0d73336d1516184d29e203563fd9bf8d7fefa5'), size_on_disk=3346485, blob_last_accessed=1756926778.1077476, blob_last_modified=1756926778.8637433), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417665/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7c9b3d448e53fcac9334df092cc003f438cbc462e6785ac3d61202bd65a7125f'), size_on_disk=7481605, blob_last_accessed=1756926313.490409, blob_last_modified=1756926314.7744033), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417945/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6689ba375c993d00d1f0e716d3bc39604d68df654a67b2518c56f4d54c7f4ca6'), size_on_disk=11289854, blob_last_accessed=1756926364.0191894, blob_last_modified=1756926364.9011855), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380981/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/198a0b08846a11e72de25e0e9c44f495ef88c9260de2e036d240715004c04953'), size_on_disk=8535317, blob_last_accessed=1756926372.5771523, blob_last_modified=1756926373.8291469), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381040/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a7c622a8ae1f5083eef26ecbcc2444055a9265b674ae80002e5468c94c0eb070'), size_on_disk=9682671, blob_last_accessed=1756926774.782766, blob_last_modified=1756926776.7967548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381019/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3023e878b223b00b77d3411a6d20195b85aa7e61a8445fe725ff694c77816053'), size_on_disk=1022843, blob_last_accessed=1756926378.3191273, blob_last_modified=1756926378.864125), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381032/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/335a75b123ae38273f1b1a10f16e9547eec356f68a1d3078926f011d3332e2b5'), size_on_disk=656385, blob_last_accessed=1756926379.6781216, blob_last_modified=1756926775.4077625), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380979/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef743a2a6a8fe44b62963b981ef0429676e7854243ab07d450d9c3ab569c7e8e'), size_on_disk=3735979, blob_last_accessed=1756926372.2201538, blob_last_modified=1756926373.6791475), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417867/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f4783b00d4674df92d84f9a304fb9d143c7b9e4754e3efcb9c072a49a9b9298b'), size_on_disk=8536709, blob_last_accessed=1756926349.3882527, blob_last_modified=1756926350.2882488), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417644/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0c788a7ba70689a796af823df5cc14504dfdc854617952459367364c4498cdeb'), size_on_disk=949200, blob_last_accessed=1756926309.758425, blob_last_modified=1756926310.6444213), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417899/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b4349f94cb0679468e9c28a9439c1a71c88d7288adc3bdad0ca31a2590f1d4e0'), size_on_disk=7910137, blob_last_accessed=1756926355.3832269, blob_last_modified=1756926356.3022227), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380957/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5a2362d35ac90fb1926051e6313886a649074b0022b128741a22a5ee24bb095d'), size_on_disk=5388789, blob_last_accessed=1756926369.5311654, blob_last_modified=1756926370.8571596), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417876/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0713329434bfbd55f1998400d9d44c1d8c7fec6cb1b646c0fdb926f1046f9b47'), size_on_disk=11246388, blob_last_accessed=1756926350.971246, blob_last_modified=1756926352.0472412), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417636/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4a3b2c7076107dcc47a87179cecab9e2051350c94e951a7aadc2ff0cf6fdbaca'), size_on_disk=43479381, blob_last_accessed=1756926307.6494343, blob_last_modified=1756926311.4484177), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380968/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/549ef4e39a437adb99efecdc9b05fee3ca4b31c878b99f7a4cf1398f405ca049'), size_on_disk=3575926, blob_last_accessed=1756926370.7441602, blob_last_modified=1756926371.5941565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380925/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdf190d30101829153acfd62edd4d689ac65a7d737f0e80978c7d460819b5caa'), size_on_disk=9220100, blob_last_accessed=1756926364.6101868, blob_last_modified=1756926366.1331801), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381046/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a96c1819b1010c40fc4d92418e407cc5b5f48f9e24883d4b4cdf3e02d23b688a'), size_on_disk=3071378, blob_last_accessed=1756926776.6277559, blob_last_modified=1756926777.855749), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380974/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ea9eb19e91fedadd9e46d5d7dc9169995065af5b6b8e823f1667117be9d737d0'), size_on_disk=8889742, blob_last_accessed=1756926371.5481567, blob_last_modified=1756926372.2801535), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380931/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/be287270314fed3dded81e0c24a66d8bd991b219fd6a157f1aeaa1690262e563'), size_on_disk=4324179, blob_last_accessed=1756926365.1611843, blob_last_modified=1756926366.1241803), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380940/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4f59a7f9287c427bed27910f08a041e460eff2490c21e2fbdaa5cc9348f5921e'), size_on_disk=6062382, blob_last_accessed=1756926366.8141773, blob_last_modified=1756926368.3051708), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417635/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d2d1c241467bd5c55cf059f793b4236c99d48e59bec853d1052ff6c644e50d1b'), size_on_disk=3044373, blob_last_accessed=1756926307.2014363, blob_last_modified=1756926308.391431), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417855/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ac2218ccb9f80664ce377351ea1dc84bf3f70b36006a86cffdb761a40ddab791'), size_on_disk=1981158, blob_last_accessed=1756926347.3992615, blob_last_modified=1756926348.3062575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417653/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/59233342d3c3b71fce95d5295cf7c1e5ef3f4254dbc120229cb83b6c8115c759'), size_on_disk=12208357, blob_last_accessed=1756926311.136419, blob_last_modified=1756926312.3904135), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380977/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/74237b16fd38090785d203d96bae72e1f5ce919d423f793cfb9d3b86623358e6'), size_on_disk=11114601, blob_last_accessed=1756926371.6651561, blob_last_modified=1756926373.12615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417894/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/12f262a12783317d135ffc5e1d281a3d43b746a82ed8053c15de824b94bd5ef8'), size_on_disk=6449687, blob_last_accessed=1756926354.4772308, blob_last_modified=1756926355.4742265), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417771/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8fe5513ae86b28ecca3d4c70bdb73097f578dd31b4d08a072960b61550f582fa'), size_on_disk=3970195, blob_last_accessed=1756926331.9313285, blob_last_modified=1756926332.8643246), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381070/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/32c8f4c4efdbe6e137add61b0e31c27ae6a9eb5e8b463e9e4410a27cc51a57d3'), size_on_disk=29864184, blob_last_accessed=1756926780.7487328, blob_last_modified=1756926783.3167186), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417764/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8a30e54d96d5b83981e37a0d8f6cae9e6584819a76a200f2730d9de32e282d02'), size_on_disk=2641764, blob_last_accessed=1756926331.099332, blob_last_modified=1756926332.088328), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417749/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8460c24102627d611c8918a1620078ee1bdf9929c44cdca863cd0dbb23f9cf1f'), size_on_disk=4938551, blob_last_accessed=1756926328.814342, blob_last_modified=1756926329.7143383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417924/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f2053fcaa7f21b1703e3491e167b457c29759b2fe9535f0cb2bfa257a1b86cea'), size_on_disk=6988370, blob_last_accessed=1756926359.940207, blob_last_modified=1756926360.8152032), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417732/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f99cdb10c12a75e1d020125d68b5c5302263eaef050b5c6b0032e27a0ac9a23c'), size_on_disk=685742, blob_last_accessed=1756926325.7463555, blob_last_modified=1756926326.326353), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417631/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b4b8c985528935ec46c6c7e78482af1ed26238137fc63b2196c6631e2d3796ab'), size_on_disk=27651513, blob_last_accessed=1756926306.2814403, blob_last_modified=1756926309.1994276), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380929/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aeddf7cf3aee11ee08c90d305e66fc682e675959c171ecfaa591bc1d9015ebbc'), size_on_disk=6656759, blob_last_accessed=1756926365.1011846, blob_last_modified=1756926366.0181806), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380975/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ea018f4882a60f5fb07a4bbae51bd494e44e6df9b215d9da5e405687a567dcbf'), size_on_disk=8369150, blob_last_accessed=1756926371.6051564, blob_last_modified=1756926372.7471516), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417727/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/414c702fdf3dd97c536f3237d2c38e0ef5ad8d08bb7c991ee252e16d57b201b6'), size_on_disk=2636905, blob_last_accessed=1756926325.0683584, blob_last_modified=1756926325.8093553), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417673/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/535d074f70f4553e24f3171fe5cfbd1821635ce7ca6ef93f8665876de620d6c9'), size_on_disk=6559693, blob_last_accessed=1756926315.1844015, blob_last_modified=1756926317.0733933), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417659/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/20f21bb1ba86fdbee1918776793b1ff2275be49933c72eaba90b8be8582f7692'), size_on_disk=15311232, blob_last_accessed=1756926312.2364142, blob_last_modified=1756926313.8474073), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417826/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3532f474125bbdc0445e46fe8be83801ca9c079afb1dedd7cd95e25994c50d17'), size_on_disk=11492099, blob_last_accessed=1756926341.9132853, blob_last_modified=1756926343.2502794), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417609/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e3d8451242fe9ecf9f82e2ac1a406144840a6182178228e6e1fa67e6a04a318a'), size_on_disk=3176651, blob_last_accessed=1756926300.651465, blob_last_modified=1756926302.0864584), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380942/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a958d997c1aff07ec07304485ee818280bc7714dd9c485ea73ef7fda81f2fe63'), size_on_disk=11407010, blob_last_accessed=1756926367.109176, blob_last_modified=1756926368.6211693), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380982/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d1e44b0b82ea22fb6c513ce02b49facf5f38b64b2baa91b24942fc6858125e37'), size_on_disk=6977161, blob_last_accessed=1756926372.8241513, blob_last_modified=1756926373.4951482), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417792/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/196cd0cf279f6bc85ca0dbeb1f089721f63eb167644d999b45d8cfe9a1f52acf'), size_on_disk=11982413, blob_last_accessed=1756926335.4593132, blob_last_modified=1756926336.651308), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417681/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6e4ea424e9eab4dc3ea304621992e1db158565a8ce30374980b1084a653a0132'), size_on_disk=7689435, blob_last_accessed=1756926316.934394, blob_last_modified=1756926318.231388), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417859/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e4c5915df6cb5bb450e1948d8c43bbe031d09ec345688ab51f2d8ea60f3be09f'), size_on_disk=12709203, blob_last_accessed=1756926347.8872592, blob_last_modified=1756926348.9972544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417692/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/82405dc0a21bd225cc6a9e813e62d4dc0522719abf1e677c954a6ec38763b306'), size_on_disk=4873514, blob_last_accessed=1756926319.6253822, blob_last_modified=1756926320.6443777), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417875/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8dc3634dae23cad7f562d15903341a67d3048929bd2198e53f71529b00a3508a'), size_on_disk=11490812, blob_last_accessed=1756926350.6762471, blob_last_modified=1756926351.7832425), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381042/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/80a0c18e8d146043e9c95c6a86552d28e28267a489b3e5c6f2a11319cf47b877'), size_on_disk=4477936, blob_last_accessed=1756926775.660761, blob_last_modified=1756926776.9177542), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381053/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6ca1dc55841bbb6d8d8a83d1a87d00570c1eea7cf1963db8cb04ddd427711fb6'), size_on_disk=10277654, blob_last_accessed=1756926777.9197485, blob_last_modified=1756926779.9187374), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417746/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/432442dbdd5578acedcbf6eddc14cfe0d460facc7b0dea857225fbd4e6a4242c'), size_on_disk=2726090, blob_last_accessed=1756926328.6353428, blob_last_modified=1756926329.1983404), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417628/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/291f0e4d2d0fa1656de1583bd9e131fd4d2fc1934d0bb70a6774f7e7f90e62ae'), size_on_disk=15444530, blob_last_accessed=1756926305.1314452, blob_last_modified=1756926307.1334364), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417944/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/01e0e606e4e8b2203e6497ffc8c231bce8672a43d83098ba838c8edfdb052c27'), size_on_disk=4563261, blob_last_accessed=1756926363.89319, blob_last_modified=1756926364.6951864), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417883/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8e23cc97e00adfd244a989f83702f4ce7a998ba7998dd95908a1b4ec256bc054'), size_on_disk=10709227, blob_last_accessed=1756926352.4542394, blob_last_modified=1756926353.675234), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380980/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e36f58da696f0827c72fed8647c2a45a0499fdc5d1859386dbf025388f07e16d'), size_on_disk=2288305, blob_last_accessed=1756926372.371153, blob_last_modified=1756926373.2051497), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381035/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/410edf5a6097a77bcec6880ec2618b4aec4bc10c44607163fe2b27aca0a05117'), size_on_disk=1451911, blob_last_accessed=1756926379.8691208, blob_last_modified=1756926380.5191178), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417706/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4f1ea5592a855f3bf5739b0caae1c69c7606276d125c3dfe8e98379d12bedf1b'), size_on_disk=1154142, blob_last_accessed=1756926322.9053679, blob_last_modified=1756926323.5173652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417655/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/25d98ba17be9b354b0ec7a0e513a6ff9d4593dd1cadf269f28bcea1ca9c71565'), size_on_disk=5461224, blob_last_accessed=1756926311.5504172, blob_last_modified=1756926312.9474113), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417790/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f88bed27521de8285bbb92cf95242820116e6fa2c23ab578e6608a6a25e0c04e'), size_on_disk=10836858, blob_last_accessed=1756926335.078315, blob_last_modified=1756926336.1843102), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417679/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bc48687cfd5c03c1a71787981180295dd3a27cf4d2c46bd26bb68cdf8b35925a'), size_on_disk=17346576, blob_last_accessed=1756926316.1203973, blob_last_modified=1756926317.84439), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380954/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/338886924ec42501a99a4b842e18fdbed28200d52daaf43e00ee601a147e0188'), size_on_disk=4903852, blob_last_accessed=1756926369.2841666, blob_last_modified=1756926370.101163), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417696/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2e1bce702520ca07d9037bd4de4d21bb30ef6b7fdf7de9d75ea232acc036a72e'), size_on_disk=31303382, blob_last_accessed=1756926320.7203774, blob_last_modified=1756926777.0657532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417627/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4027ab6ecd21364ca365bbe7192797ed924c817e6edc841d750111b2e3f6f45a'), size_on_disk=21089243, blob_last_accessed=1756926305.0144458, blob_last_modified=1756926309.6264257), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417763/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/91d8a58ead35fc52f7c2649ce9509a92236a20ca8c26f5f2ef4ba1a38e64afb1'), size_on_disk=230495, blob_last_accessed=1756926330.8943331, blob_last_modified=1756926331.4653306), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417902/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2d72fc510cd348af7611ca170b7804b228b74bd38e7ff5e437375c1cc926fa95'), size_on_disk=8952511, blob_last_accessed=1756926355.9322243, blob_last_modified=1756926357.2422187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417939/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a0fc7804b026f95809f33087deb75f00beeb9635475b62864d3e58e948361bfa'), size_on_disk=9099632, blob_last_accessed=1756926362.9701939, blob_last_modified=1756926363.9411898), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381038/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/415f62b8333a02df8a87ba85e1d0de30a6cc9fdb07f7c8dc02d30f81ed4c14ef'), size_on_disk=2424198, blob_last_accessed=1756926379.8751206, blob_last_modified=1756926380.4921181), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417783/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9603c95097c451e616c54d52d3474b866453409905ea16365d019e77c6ca31b3'), size_on_disk=4176778, blob_last_accessed=1756926333.6983209, blob_last_modified=1756926334.4773176), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417947/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/50bb9cc74904c9d3b6cc3ec8bc12c7ad1fb89204be6f89a863e703a94d224512'), size_on_disk=4023877, blob_last_accessed=1756926364.0161893, blob_last_modified=1756926364.9561853), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417823/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1887333f71d11fe7c43748662f58d9ba3fb12f3f6ca30a61d7e1c7a0c95f7064'), size_on_disk=9444476, blob_last_accessed=1756926341.6372864, blob_last_modified=1756926342.8442812), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417678/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/825d9bcd10c008b68cc84e92f20608c0d52770ac028bbc4a9219f1cd210ab8c4'), size_on_disk=29320273, blob_last_accessed=1756926316.0503976, blob_last_modified=1756926318.3993874), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417860/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/72e751d246e9b938ce9ab4380e2a1114ecd3c36d5829e3298f391c1edfefae24'), size_on_disk=3996315, blob_last_accessed=1756926347.9312592, blob_last_modified=1756926348.819255), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417932/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f040d4f594fe69143a7c3f04f0498be2a30fb9b5e78d42ec464fee65efcc42dd'), size_on_disk=9231914, blob_last_accessed=1756926361.4262006, blob_last_modified=1756926362.5771956), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417731/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/39ec5969bc8f3acceb3aa30b72f0c0101785a36da340f3983ad00d8cc80160a4'), size_on_disk=1557745, blob_last_accessed=1756926325.658356, blob_last_modified=1756926326.2043536), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417759/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b2dad4cf4b03c846adf9d5229aeab340e58034c751bc68b37bb0c8402a7c071b'), size_on_disk=1372312, blob_last_accessed=1756926330.1243365, blob_last_modified=1756926331.1623318), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417791/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8d2eb1bdd1a5cae023abb271e5858cf111879953cb0d6267f4ebf21a3d30c4ad'), size_on_disk=5232904, blob_last_accessed=1756926335.2033143, blob_last_modified=1756926336.417309), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417919/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f5504428a85399cb212c74235abf9cec068dba03136a87ea79eae6d63a3cf430'), size_on_disk=7011211, blob_last_accessed=1756926358.6892123, blob_last_modified=1756926359.8312075), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417911/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3ca8b1b14564fbc90b44995a61a34d5a67482eb5fd867ce4f9f2196de2b73539'), size_on_disk=3051177, blob_last_accessed=1756926357.404218, blob_last_modified=1756926358.3022141), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381027/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ba9aba2cc5b9bd9e83599ccba5920f028ac9c6936436dac50f45c606f3abd841'), size_on_disk=6537548, blob_last_accessed=1756926379.0151243, blob_last_modified=1756926775.5467618), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417779/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/918376b5cb0b63e3e531ac990854e5a7b461e7251f5ed562c5a92cbf6a5a0141'), size_on_disk=9459105, blob_last_accessed=1756926333.1593232, blob_last_modified=1756926334.2033186), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417926/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/303d44a38fbb646cf211ce1bd03fe2d24bb004f385c63d05ab8e707c5b283f0c'), size_on_disk=10766909, blob_last_accessed=1756926360.6072042, blob_last_modified=1756926361.6751995), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380993/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d568d29486ad3ec6f67f87b78cacb8b7102c591f979ec2bdb17ff75bde789670'), size_on_disk=3031778, blob_last_accessed=1756926374.496144, blob_last_modified=1756926375.2661407), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417857/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/64f710e0b8a62bb6eddf3508f133b6b82af8bd21dd07f2319af6d07981085b1e'), size_on_disk=11109477, blob_last_accessed=1756926347.471261, blob_last_modified=1756926349.6202517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417929/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/85b194132215311994e48908799e31357d1094663a232e8170a4edcfa5a11eff'), size_on_disk=9155231, blob_last_accessed=1756926360.9862025, blob_last_modified=1756926361.9231985), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417923/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a32971eb62fe5c4c08298b1bdd97a916d8ef8f5898aee42aaf3fbaa8069e92d1'), size_on_disk=10004280, blob_last_accessed=1756926359.919207, blob_last_modified=1756926362.0851977), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417890/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3eca80a3220fd0294fdfca83740ba70a20af764d28833494948705c4d542ded3'), size_on_disk=9018889, blob_last_accessed=1756926353.7652338, blob_last_modified=1756926354.7252295), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417739/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1c9e3fcff1dc467673efe70177cb4d678d7eff7b5b2bb3ded247d7a458b466dc'), size_on_disk=21974051, blob_last_accessed=1756926326.8623507, blob_last_modified=1756926328.7393425), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bd149efcdaa899981b965dc424d16fb66668a7666a059c6bc5043572331394c2'), size_on_disk=4513644, blob_last_accessed=1756926377.1751323, blob_last_modified=1756926378.1161282), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417865/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/348bfc1685f79e9ec7f559e96b61b8c43628d611378b6c4ead8bffea44415f1d'), size_on_disk=9233047, blob_last_accessed=1756926348.8982549, blob_last_modified=1756926349.9182506), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380995/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e0cae334f1cb71be0be1e8ceb5fc6ad23d22e4db20cb64b1314c3d080f2bb5f7'), size_on_disk=6315860, blob_last_accessed=1756926374.931142, blob_last_modified=1756926375.7271385), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417941/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/30188c510d2d7041e1525e304edc8f25b569a1771cf00844cb27300a052441e8'), size_on_disk=7032333, blob_last_accessed=1756926363.2551925, blob_last_modified=1756926364.2891881), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417824/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/65d997db1339fc5a8380a61fcf1ab1a6d28fb9142fc7d78c89236dc218b91ea3'), size_on_disk=8218856, blob_last_accessed=1756926341.7612858, blob_last_modified=1756926343.0512803), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417626/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1981a2a1e321acc5c35bbeb9b61a12636c27f28d6e216de20093afdbee9f8689'), size_on_disk=12046611, blob_last_accessed=1756926304.4464483, blob_last_modified=1756926306.2084405), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417832/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/268a3b7a91ccc3f3909042a711454c336b1887705de99442bda022bcbc26a01e'), size_on_disk=7655304, blob_last_accessed=1756926343.2332795, blob_last_modified=1756926344.3042748), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380946/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62fe0084bf7a8f9f484e76366f203e1c68862a211d5d501c08e19b8f28678d6b'), size_on_disk=8090103, blob_last_accessed=1756926367.6871734, blob_last_modified=1756926368.7681687), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417722/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f29055ed874a3a40735526808a6739884efecd8d7c5e7e9eb1fcaa8e495ade3'), size_on_disk=345818, blob_last_accessed=1756926324.5753605, blob_last_modified=1756926325.2483575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417633/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0b38824aeb2fac57eca256e3652539deb4bc221eeb5afc3701a997885f9ad9d0'), size_on_disk=19311587, blob_last_accessed=1756926306.8324378, blob_last_modified=1756926308.847429), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417821/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c29db68a3430cf8cb01716d90a92105387777ed1361992eb9501271c35359cfb'), size_on_disk=6597050, blob_last_accessed=1756926341.1692884, blob_last_modified=1756926342.2702837), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417690/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/283af9b17d776b75f6ff6ad1f426d6d0e0e5aa1f2089a20910edcb6763191b69'), size_on_disk=12265448, blob_last_accessed=1756926319.171384, blob_last_modified=1756926321.4153743), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381069/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aa8f6186814bf973f8b4448a0c4b692c31594e03905c75d5691b982d6ccde97d'), size_on_disk=13705106, blob_last_accessed=1756926780.5807338, blob_last_modified=1756926782.319724), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381011/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/839fea471c67e08e0b68ae6344432c76da559e67c505c3f6120b20db2f1f753e'), size_on_disk=4378212, blob_last_accessed=1756926377.2971318, blob_last_modified=1756926378.0891285), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380952/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3811d6e6194d8b88c84fc0931cba844da23677e76e1e9a25e2890df968a7e529'), size_on_disk=1003172, blob_last_accessed=1756926368.8351684, blob_last_modified=1756926369.4641657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417660/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c394d7b0cfca3f3782dca86380e1a55903111d32efde98173af8a0b726ef5e7d'), size_on_disk=6441490, blob_last_accessed=1756926312.4744132, blob_last_modified=1756926313.4024093), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417811/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/da92b1c1579a2295da085050305367bd02a47fd52a74d1ec2a9f227b5e808b11'), size_on_disk=6127159, blob_last_accessed=1756926338.8782983, blob_last_modified=1756926340.1172931), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417829/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/63a9af789279180a07d8e8afe07bb0e9b8aa32c4340e01347a6f7ee809198308'), size_on_disk=5677879, blob_last_accessed=1756926342.904281, blob_last_modified=1756926343.7332773), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417716/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cd98cf97346b62e4f1efcace598c9c273d9ad9b8d8692e35f50b11d46f7ed8a6'), size_on_disk=5100666, blob_last_accessed=1756926323.8513637, blob_last_modified=1756926324.8793592), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381039/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5f95b6de8d0d73589c612313eaed6ce1e6adf734e795719f23df6f54cbbedc69'), size_on_disk=1656495, blob_last_accessed=1756926379.8751206, blob_last_modified=1756926380.283119), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417872/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7d71ca12594ab85797fca8c1b88947cb0f3ece93596f1a4c5adc04c11c0abf68'), size_on_disk=8522283, blob_last_accessed=1756926350.1742494, blob_last_modified=1756926351.180245), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417695/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d6ae8d39c2949235dd4d82e7a0e56d9f20c20ba5f1b7a445fbba1f0df29c88b7'), size_on_disk=7710772, blob_last_accessed=1756926320.4913783, blob_last_modified=1756926321.959372), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417887/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/58707813ec63af2eb1a72b0c8317b4701b4777df8f828f2e08a0cbb1ccfa534f'), size_on_disk=9080847, blob_last_accessed=1756926353.0712368, blob_last_modified=1756926354.412231), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417709/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/395b1dd171ab9759e06a22580672bef65aac2acd7d548e38098d207c80071d89'), size_on_disk=717131, blob_last_accessed=1756926322.9983675, blob_last_modified=1756926323.6373646), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381023/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/22e3e0f4cd1f2c755ec1ac13b99d2d6c98d0158319478f2deca4e89aefc5e88e'), size_on_disk=3860195, blob_last_accessed=1756926378.5651264, blob_last_modified=1756926379.6101217), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417949/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6123bc7bd312e6e37fabadee561c7dacad32d26bada92c9785c783f94fc50a37'), size_on_disk=1320707, blob_last_accessed=1756926364.4151876, blob_last_modified=1756926365.0891848), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417625/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/96b04f28e74e25bd1502a4ea9aca8047bfa8462e61cced0d726af4a336e1a585'), size_on_disk=17384720, blob_last_accessed=1756926304.4014485, blob_last_modified=1756926306.2074406), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417853/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5995033a04f017892b30c025a1527f9b6420bf1e7636c466cdd8d1462cf228c8'), size_on_disk=2186899, blob_last_accessed=1756926346.9012635, blob_last_modified=1756926347.8172596), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417713/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c5d75f4a32cadceee844da0c5ce703483f575fb927990a1615ee37205137d986'), size_on_disk=524444, blob_last_accessed=1756926323.533365, blob_last_modified=1756926324.045363), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417921/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/05fb4ff0001b88b711eee9f29ae30642d940667f7166121c9782f464057880f5'), size_on_disk=7891276, blob_last_accessed=1756926359.3782094, blob_last_modified=1756926361.2442014), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417611/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c82d882f6538d65316dd01e26a82af13e41717952c79e20dae6c3f1d9724ab60'), size_on_disk=3720891, blob_last_accessed=1756926301.4614613, blob_last_modified=1756926302.2704577), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417684/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eee25a9a91c54d0404db884532d1b85555f2e1de0c664abcbda4343f9d729842'), size_on_disk=10310520, blob_last_accessed=1756926317.7173905, blob_last_modified=1756926319.3473833), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417772/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a21b204a2da7654dd5bf4e7965a3c1e19159c16052232030787175a830ff0233'), size_on_disk=8204405, blob_last_accessed=1756926332.1323278, blob_last_modified=1756926333.453322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417725/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4195b9ed6506437a735fd8061cd1b79211c99169c4319c4f72b801a501f2b0f9'), size_on_disk=2233032, blob_last_accessed=1756926324.958359, blob_last_modified=1756926325.594356), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417820/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9d36f1c17151e3203186142abff734c0182377d5287b3f27d00917608501abb3'), size_on_disk=8116588, blob_last_accessed=1756926340.9562893, blob_last_modified=1756926342.0902843), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417869/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8dd08ab28007f90a664a6fbde23298ae2bb0ded917d5d75b1275c41e3eb5f7dd'), size_on_disk=16604892, blob_last_accessed=1756926349.6872516, blob_last_modified=1756926352.7202382), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/059cc8a8b1f7918ab1b6499d3d9843100b3b37fe6dcd57dfe9ae512c6030cad6'), size_on_disk=2109635, blob_last_accessed=1756926376.7021344, blob_last_modified=1756926377.469131), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380996/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/09e663ce2a60df5635f4e4ea77d6350e7752a9bb5e81fbdfd818869ac53ca805'), size_on_disk=13309208, blob_last_accessed=1756926375.0511415, blob_last_modified=1756926376.0221374), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417654/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/67d658902a2b91d0d36345be06dc00c85af1b88ba40c37deaa091705d97b3ffc'), size_on_disk=6323092, blob_last_accessed=1756926311.166419, blob_last_modified=1756926314.4344046), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417885/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6d5faeda491268a8ec88393883d743aac6cd14d4837698164ca32ca1052da36f'), size_on_disk=7668243, blob_last_accessed=1756926352.806238, blob_last_modified=1756926354.1422322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417707/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/89192f5007f730a90c823219ea107c799935f5e824963090abad813603160fa8'), size_on_disk=2615872, blob_last_accessed=1756926322.9183679, blob_last_modified=1756926323.9473634), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380987/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2eb045d9f88b952eed8f8354e77c682bc3003bd2e9eacb0eb0c2df5ca44a34cc'), size_on_disk=4463796, blob_last_accessed=1756926373.559148, blob_last_modified=1756926374.4241443), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417617/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9ad746ec09ed51ed497d87f3dd64b11916b70e6833ce66141bc8feb6ba73d1d3'), size_on_disk=23082279, blob_last_accessed=1756926302.1754582, blob_last_modified=1756926303.6944516), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8168980e59c50149bfb9866a1e8855c235f2882b988df99331c9247f65e9813e'), size_on_disk=2289686, blob_last_accessed=1756926376.4721353, blob_last_modified=1756926377.2151322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417917/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2118491b5ba6b4af1c0d394eec3139b0258cf3a4e42b4d1a02ab177247852081'), size_on_disk=14019020, blob_last_accessed=1756926358.4622135, blob_last_modified=1756926359.8632073), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381036/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ff76dabe83d2948240dd846fcc2a969380b7bd9f8d2eff2c71c299f0b14134cd'), size_on_disk=2593393, blob_last_accessed=1756926379.8661208, blob_last_modified=1756926380.5611176), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380984/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a15ab5e5a0ead07dc2a530a1e4abb714027a95ce090c7a0eda5ec2bb714d903f'), size_on_disk=8070704, blob_last_accessed=1756926373.2251494, blob_last_modified=1756926374.1601455), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417726/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/739e474fc54ffee0c7e6300d3abb1f83d33d48c78d137ed848f837fa186b9308'), size_on_disk=2430088, blob_last_accessed=1756926325.0663583, blob_last_modified=1756926325.6803558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417712/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/59cbfa83b9abe13f60d6a80b35afe857e3ffd761366133f60505312a2b5a72e2'), size_on_disk=641243, blob_last_accessed=1756926323.2163665, blob_last_modified=1756926323.7173643), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417934/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5816750a355a277e532d46ffa5159f8a2ec1ce66686fb12f07ccab4b4f7b3c98'), size_on_disk=7324082, blob_last_accessed=1756926361.995198, blob_last_modified=1756926363.89619), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417774/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ad00f794fb29d2a609375d63b09170f396f055ae392cd05a31d5f8fb5ede7a33'), size_on_disk=2914273, blob_last_accessed=1756926332.1763275, blob_last_modified=1756926333.217323), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380997/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/add73ab9723ef88a47192fa169b89aaf17c16a0841fc9e53c1c5d73cfdc2ad50'), size_on_disk=15536494, blob_last_accessed=1756926375.1291413, blob_last_modified=1756926377.6141305), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417661/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c6effb515a20066646b7863cb86e9dc012e14187e432ba7458cd6af18662bfd6'), size_on_disk=22122748, blob_last_accessed=1756926312.9054115, blob_last_modified=1756926315.6813993), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381037/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5cef560daffa3a5b1e841a312937eca595ced0bbdb3e6c641b13359a0340fea1'), size_on_disk=2090047, blob_last_accessed=1756926379.8821206, blob_last_modified=1756926380.4171183), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380976/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d27d3efdccd104ca208d69fe9d94fe9011cb6212b34739679b1c8f550c5e9df2'), size_on_disk=12889551, blob_last_accessed=1756926371.6141565, blob_last_modified=1756926372.8021512), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381066/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ae017c72cf3b91ea3fdd40f63a126f640e7049340baca502ff3228c6ee40a7b4'), size_on_disk=13139272, blob_last_accessed=1756926780.000737, blob_last_modified=1756926782.6217225), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417668/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b426513aab4e071a24b7d27100a4da4da8c71c8e9b3e82163808b0ec6a42b8d0'), size_on_disk=6011682, blob_last_accessed=1756926314.5374043, blob_last_modified=1756926315.4354005), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381022/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/980ed07a51dfad581fc2f7bce44031c92445822ba7e994cbfc9b5508cdf97c85'), size_on_disk=2103246, blob_last_accessed=1756926378.5151265, blob_last_modified=1756926378.9441247), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417862/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cea0cfbd611f8c6482ec33f8ea32f304a481d1c1225d3dacea432dd359fc976b'), size_on_disk=5244392, blob_last_accessed=1756926348.381257, blob_last_modified=1756926349.6162517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417868/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/52e3f224a0147264d4ce477d26f4e11ff0745c328eb3e2ac44209dba1c8c55eb'), size_on_disk=5039768, blob_last_accessed=1756926349.6852515, blob_last_modified=1756926350.6082475), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417845/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ca2b89837de406b10d72533a3e3d5457b3512b704f7cea395fb0e1b88828393e'), size_on_disk=4648586, blob_last_accessed=1756926345.6932688, blob_last_modified=1756926346.5862648), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417895/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2daca9b311da1afa40f578c6df7dc4b0039dd4857663c98cd87c665a547b7144'), size_on_disk=11915424, blob_last_accessed=1756926354.7232296, blob_last_modified=1756926355.8262248), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417908/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/128e54326b088977000f8caaba7d09e324281ed7fe0e3e5f2779783f0a82f293'), size_on_disk=3614805, blob_last_accessed=1756926357.1272192, blob_last_modified=1756926358.2542143), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417744/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/faf407f0b251cf5f58c8ac7becee4b995327fd9a7d6599c3045fde8a78e1e811'), size_on_disk=2548979, blob_last_accessed=1756926327.7843466, blob_last_modified=1756926328.7443423), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417736/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/671a67d7275810840cafc9db32e43385c4ba61b007d702e7eb5cb244a0e4afa9'), size_on_disk=13855124, blob_last_accessed=1756926326.1693537, blob_last_modified=1756926327.261349), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381030/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e5baa8386faa327b26d27d6c8e6b265dddac1d55565e81d724a1980b93d871a3'), size_on_disk=1867415, blob_last_accessed=1756926379.2021236, blob_last_modified=1756926379.6681216), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417841/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cb829be078dd8b533a90c2c548dfb2ce0455026be54c36572337e0a1ddeeb2cf'), size_on_disk=5791736, blob_last_accessed=1756926345.138271, blob_last_modified=1756926345.9902675), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380989/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3c8b646955209386a1d97882163b7c9816463d2dea68fbed2cc960817ffeb332'), size_on_disk=12022144, blob_last_accessed=1756926373.7761471, blob_last_modified=1756926375.1511412), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417634/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/529d984782684991b2eb7be2d345b5a4e8a6e08d3dbe30ad34495d8380464e6e'), size_on_disk=16306189, blob_last_accessed=1756926306.810438, blob_last_modified=1756926310.239423), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417789/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6bd6339b995a5730b6b65ec65f680b57195469ebee9f2e15de6e604b069d4b0e'), size_on_disk=9612750, blob_last_accessed=1756926334.6493168, blob_last_modified=1756926335.8183117), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417604/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e6dd3abdf75462f1f5d16f19383b98a8a4c817dc3b13c7cea2b4bea611c40c62'), size_on_disk=961320, blob_last_accessed=1756926300.390466, blob_last_modified=1756926301.3534617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417621/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/354c654ace9bc123bcd9009c56af518b98f1a547a7ccd79f348d0e533607a41e'), size_on_disk=20457307, blob_last_accessed=1756926302.5744565, blob_last_modified=1756926307.7874336), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/225bf60f7c9796682210dc48d07a30d3dabab8ebc13abb7a84834279fa131020'), size_on_disk=5307353, blob_last_accessed=1756926376.2401364, blob_last_modified=1756926377.3751316), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417622/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/28eed3b67a6206b453d8adb775edf2bea32cfd54ba5b37d51787f67280e0317f'), size_on_disk=20411775, blob_last_accessed=1756926302.674456, blob_last_modified=1756926304.8564465), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417646/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2b3d78fc052de8647b1ba6e83245b5f375b4e6c10952feff795855160458a1d5'), size_on_disk=396843, blob_last_accessed=1756926310.1544235, blob_last_modified=1756926311.0694194), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417786/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdd4770c9b3e18e86f9a46ff924e3a562e37b632d6f0e8ad11627956a3416e09'), size_on_disk=11354740, blob_last_accessed=1756926334.3243182, blob_last_modified=1756926335.4083135), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417704/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/477a83e4a5eace957a036c5ec65ef4539442c078058c1a44a877763cb850c86d'), size_on_disk=459034, blob_last_accessed=1756926322.1393712, blob_last_modified=1756926322.8993678), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417718/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3ba23a0e7c5b3a455e95fbf6a22b51687d5330c19207241bdfd189e3776f5ba3'), size_on_disk=3304954, blob_last_accessed=1756926324.0233629, blob_last_modified=1756926324.986359), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417683/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6131abcd72bd4c6a8627685c4df8a295af50e824f52a5cde156adae7cd694bba'), size_on_disk=15545733, blob_last_accessed=1756926317.382392, blob_last_modified=1756926319.0483847), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417918/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9f1cf3832969364b6cee1c817d3583a554b765bdbc1ff3f5b29692b7ea98ee7e'), size_on_disk=6936136, blob_last_accessed=1756926358.5802128, blob_last_modified=1756926360.9132028), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417922/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fba17c65fa210922e57c533f5146b4237677149a5022fb836c1cd52dc89855b2'), size_on_disk=10194446, blob_last_accessed=1756926359.5812085, blob_last_modified=1756926360.610204), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380951/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b9b68679dd2da94e4aa4852ae3de3ee5b823c1fb1483f4ece3eee5d947014785'), size_on_disk=1154119, blob_last_accessed=1756926368.8281684, blob_last_modified=1756926369.410166), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417801/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4bbb1b3faac1a4c15eaac8c81260f4f18493d2a082915cbe4c480ce76a94140e'), size_on_disk=4600965, blob_last_accessed=1756926337.090306, blob_last_modified=1756926337.9153025), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380947/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3d6062f97d18f4945c36590355a312df2a16206c26626392cfb44d9613aea49d'), size_on_disk=9201129, blob_last_accessed=1756926368.5321698, blob_last_modified=1756926369.383166), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417737/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2e1d5b2306dd24257d4ba261baa42b324c9f4f8ca3a1d30e0f920c4f4a5f99ba'), size_on_disk=1342511, blob_last_accessed=1756926326.2713532, blob_last_modified=1756926326.9043505), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381057/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4e6c617d22a655d32847df8b5334f4fb7236a15b8bf6e30aaabcbb0a4c55782f'), size_on_disk=17494867, blob_last_accessed=1756926778.5417452, blob_last_modified=1756926780.4997342), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380936/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5a931bc1181001314c3822051c493ba7a8b87b5bb26b8866783612f2bcf49c62'), size_on_disk=3467608, blob_last_accessed=1756926366.19118, blob_last_modified=1756926367.2261755), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381017/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1d9b2dbb5079eca871182c2dce1d681b943f1362072c18e255a4bd6d1068b840'), size_on_disk=2436549, blob_last_accessed=1756926378.161128, blob_last_modified=1756926378.7931254), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380971/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0147090dd5a74f558b67151503ff635423a8af661b56d1faf06bb50a59adfb3a'), size_on_disk=1276600, blob_last_accessed=1756926370.9411592, blob_last_modified=1756926371.462157), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381071/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cf15be48b47b6c369c6a4290b783b9b22b01d6cb89d04c738c275241548b811b'), size_on_disk=2543373, blob_last_accessed=1756926781.2297301, blob_last_modified=1756926782.309724), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417676/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/53c35af59283469a22148b2841cb8574faf80d59162a33d269560283c9228af2'), size_on_disk=12006227, blob_last_accessed=1756926315.8083987, blob_last_modified=1756926316.8563943), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417800/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8e563a7d64bf06e7fe26c2817271f626273eed4c7dd19db0b9fd8e14c25f73b3'), size_on_disk=9904697, blob_last_accessed=1756926336.7953074, blob_last_modified=1756926338.958298), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417893/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f3b9f2c711cb806350320b93dba528efd6433d0ca10c39e5e2dd6aa89a408bd'), size_on_disk=9896637, blob_last_accessed=1756926354.2372317, blob_last_modified=1756926355.535226), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417805/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/661f37ed294c22f6c3b02d5c1ea25794f5e33090c246185e891ee82153e2e5f2'), size_on_disk=3071166, blob_last_accessed=1756926337.8643029, blob_last_modified=1756926339.3292964), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417884/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ff7d3efa4a231f5130be299d8d0ff67fe4973856e4db18e8a6c53d47ba6b28a7'), size_on_disk=11684836, blob_last_accessed=1756926352.791238, blob_last_modified=1756926354.0932324), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417642/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/591f688d0d9f98350a1d636e6f93ed37563f3ea9765ca6b633490533b0cf32d4'), size_on_disk=11498542, blob_last_accessed=1756926309.2804272, blob_last_modified=1756926310.8714201), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417671/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bced5a56092fea7de5754bebc33caaab643f27236f8d9020e7bc6b30b9045d6e'), size_on_disk=5347180, blob_last_accessed=1756926314.7704034, blob_last_modified=1756926315.8293986), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417879/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6714b3f5155dd540962a7541625f45e14e6a93b4c01e913790b70818e50c821b'), size_on_disk=8601872, blob_last_accessed=1756926351.4612439, blob_last_modified=1756926352.8182378), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fb5958ec4e10f5c7e19ebc55b24f26012e6d8ccf'), size_on_disk=8004, blob_last_accessed=1756926300.2594666, blob_last_modified=1756926300.3214662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417815/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ee8a37cd03dfe154008718f2a0f7fd95b3fdf468f99b1fc73ca515433fa45930'), size_on_disk=6563669, blob_last_accessed=1756926339.6062953, blob_last_modified=1756926341.8062856), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417912/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4451877e599fa65e1f00f67b5fcd41fe74719d8bcb19ddacac419fb347c77cf5'), size_on_disk=4943909, blob_last_accessed=1756926357.7152166, blob_last_modified=1756926358.6302128), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380973/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/343eb57057b5af866386fb4c02d0c41b888e206f5473ed7b4041200cfb93dbd5'), size_on_disk=1511721, blob_last_accessed=1756926371.3611574, blob_last_modified=1756926372.4931526), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417700/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1240fbb2497074bb7ff98b1bc6399b8a8ad5d320246285abfb6738e27e2bbdeb'), size_on_disk=19312447, blob_last_accessed=1756926321.6763732, blob_last_modified=1756926322.8383682), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417877/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b170370a220db5677b9c6193707d32d1d14b9082ca574b9562c4ac8ea4c72c26'), size_on_disk=10449263, blob_last_accessed=1756926351.0392456, blob_last_modified=1756926352.32624), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381060/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1bb05aa71fe55db65fc3868b131854c1e6666dca8cc370874f517aa0756d21ac'), size_on_disk=9137992, blob_last_accessed=1756926778.9637427, blob_last_modified=1756926780.2307358), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417785/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/209799965b03585e1143e881d87c5cea80d37e06637d2fc3f7e1ad2eaadfd3bc'), size_on_disk=11557515, blob_last_accessed=1756926334.1813188, blob_last_modified=1756926335.1353147), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417762/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c8b34d14be6dd44af03432d758f705cf81e86a46831c6deb4b0944248709630a'), size_on_disk=3024339, blob_last_accessed=1756926330.943333, blob_last_modified=1756926331.59933), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417699/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0c64ecbe6b245299fe04c04239d792fbae0f1b288562fe0c7912a24de56f0ad5'), size_on_disk=5707915, blob_last_accessed=1756926321.6203735, blob_last_modified=1756926322.8483682), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417838/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0dcd4dc268902c8e0845a1ee81938a81a00412014d5b198a41a523cc3450dfc4'), size_on_disk=10178872, blob_last_accessed=1756926344.5452738, blob_last_modified=1756926345.3902702), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417702/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/41123195fa339a4ffabf6d686a284c343d0519089a18b828818c92ec43a26eae'), size_on_disk=6213776, blob_last_accessed=1756926322.0263717, blob_last_modified=1756926323.0323672), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4c4a265f9e3e8d373550ea58ca1cde6094c823f090336974fde3e7cee117fd99'), size_on_disk=1770007, blob_last_accessed=1756926376.6771345, blob_last_modified=1756926377.1011326), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417608/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d225f561536fca4d073d2418a32efc3df68e9666a3403c7d8b1ee0a520861322'), size_on_disk=873305, blob_last_accessed=1756926300.603465, blob_last_modified=1756926301.521461), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417778/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/54e8ea03e214967eb4a1c7b50a086c4d7083d2c9c95dd0419e545fb0e8f18422'), size_on_disk=9345257, blob_last_accessed=1756926333.1513233, blob_last_modified=1756926334.0903192), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417624/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/28c1de2a4bd6b421cd05c3bb211ffde83e4b830127f106ec7be42ad288e26ef6'), size_on_disk=16758887, blob_last_accessed=1756926303.7884512, blob_last_modified=1756926305.6184433), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417641/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/355d8669b1d4c5576d8aa900582800fdf1819360f0db91d5e0299342b49884fb'), size_on_disk=6322689, blob_last_accessed=1756926309.3414268, blob_last_modified=1756926310.7974205), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380949/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/65090ef93158269c98bc2e747e505cea9d460f0959d0b2957e58543cd1944163'), size_on_disk=2110872, blob_last_accessed=1756926368.705169, blob_last_modified=1756926369.142167), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417658/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b6cf4c024743be14fcd5730f0ddbb960dbcd83d07adf8668639f9a1cdf195368'), size_on_disk=8754108, blob_last_accessed=1756926311.5764172, blob_last_modified=1756926313.1134105), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381063/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1e4361a7d20f8e92e8efc238365e0ed06eb021243864321c93d4638fa21634c4'), size_on_disk=10916323, blob_last_accessed=1756926779.3497405, blob_last_modified=1756926780.685733), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417697/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/198a3cd31740f347f0308db2914df4fc0fc303b4a9f040fbaf79fb5ca0170e7f'), size_on_disk=5521438, blob_last_accessed=1756926321.4153743, blob_last_modified=1756926322.4743698), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417809/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/486100d34df42ce97476d9753f8d57edb5f53596eaeca2d0cf81f5f95a4f4e12'), size_on_disk=7376702, blob_last_accessed=1756926338.5872996, blob_last_modified=1756926339.9172938), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417711/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e30dbc09cd2dfb4722d60d0833123542e9c5096f70b97867cd15d466aa7b927a'), size_on_disk=96859, blob_last_accessed=1756926323.1863666, blob_last_modified=1756926323.7823641), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417925/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8cdb783613764a35fd3a73db89463db6876951d443f5651a9a46877a53917f3f'), size_on_disk=912848, blob_last_accessed=1756926360.409205, blob_last_modified=1756926361.1532018), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417640/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/411d9146359847b5805450f67cbde4f1cbd51e3c2fae4bbcd6295db7426cb2ca'), size_on_disk=7000528, blob_last_accessed=1756926308.9124289, blob_last_modified=1756926310.0344238), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380953/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5b3410430d33f3123b935982be90037f2d0a8e657f6b0b70a7042846d5ddd1b0'), size_on_disk=1192961, blob_last_accessed=1756926368.8401685, blob_last_modified=1756926369.627165), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417657/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62398c518588ad11a939f95736cf0ed01a39890567cd4a8f05e9c9ba1155f9c1'), size_on_disk=10144285, blob_last_accessed=1756926311.5634172, blob_last_modified=1756926313.1174104), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381014/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5c5106dfb24bcfb1436aeb87b8218df873417ca2809aed71ca58e9b48ffacd29'), size_on_disk=1425306, blob_last_accessed=1756926377.5811305, blob_last_modified=1756926378.2571278), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380994/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7c5f237d9c32233a005e39386360f02d9e1932f9ac267ac3458f96aaf59e01ef'), size_on_disk=2496053, blob_last_accessed=1756926374.8051426, blob_last_modified=1756926375.3471403), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417742/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1a87c492aab479dd0c013fbe80e006f18ba80de751da600b95babcb3f6e88dad'), size_on_disk=2054520, blob_last_accessed=1756926327.3293486, blob_last_modified=1756926328.4213438), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380999/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/acb2ee4711dab5800afb7ccbea72c8c80268b0a848723e7bd8ecf197decb982d'), size_on_disk=2605563, blob_last_accessed=1756926375.2281408, blob_last_modified=1756926376.1321368), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417729/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8383a0e86c2bfe15c46045980d6768eb4e7e3c4c157ff1ae7ce20144e245ca80'), size_on_disk=3058876, blob_last_accessed=1756926325.3113573, blob_last_modified=1756926326.103354), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381048/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/53cd017f084781da16e69ae01b406ac0d8824a4d991e39b22c428e664a3808f2'), size_on_disk=2181901, blob_last_accessed=1756926776.9907537, blob_last_modified=1756926777.7067497), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417740/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e91586589c967e7bcbd521989874b21c024da79365d0d9916ffc66b774043e01'), size_on_disk=11875677, blob_last_accessed=1756926326.8903506, blob_last_modified=1756926327.6843472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417835/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6245fc8d220aad556115d4b831f72ae6a9beac48ed6e2aba811284aa0ccab4f7'), size_on_disk=7888215, blob_last_accessed=1756926343.795277, blob_last_modified=1756926344.942272), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417705/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/92b4f5af6793c98b854bcca83c86ac0cb41ec4e40ec23d3ba9c4ff287852c63c'), size_on_disk=332184, blob_last_accessed=1756926322.5923693, blob_last_modified=1756926323.1503668), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381059/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/59daa71f55c9c0da6f8d56fc2497491477312fbdbcb9e92d4951874f2ed72686'), size_on_disk=13124154, blob_last_accessed=1756926778.926743, blob_last_modified=1756926781.8157268), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417852/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/43431a444b2761ed9b6fa4c7e6edfc8da31b64ff933408c517bc771e9f3e94dc'), size_on_disk=6967416, blob_last_accessed=1756926346.8272638, blob_last_modified=1756926347.8462594), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417748/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/db4c67c001a1f688f70918f0e25d3ddd996392b33b9f1144a14551bc0026d036'), size_on_disk=3246481, blob_last_accessed=1756926328.8123422, blob_last_modified=1756926329.6943383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417892/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7ac6b85018422c74c894467b7b53b1c2dc7c5298fc31654bdea5ca70493e0a87'), size_on_disk=8194725, blob_last_accessed=1756926354.176232, blob_last_modified=1756926354.9712286), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417906/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/34d3381dd45e579dcc298e91cc95174c4219cf548401b8f2392cf746fd508c5b'), size_on_disk=13795318, blob_last_accessed=1756926356.3642225, blob_last_modified=1756926357.640217), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380927/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/38e67ea2fef57610ff63c0c298a6db65ae1c0a159f2b1e61940f2e65306b8fa9'), size_on_disk=7297320, blob_last_accessed=1756926364.969185, blob_last_modified=1756926365.7781818), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417753/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a8969c75211310735ad33d5af57327f2babc86276adc7cefcda254cc9fc9baa5'), size_on_disk=6433095, blob_last_accessed=1756926329.577339, blob_last_modified=1756926330.4023352), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417817/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ccf81aa26021a009a212ef8f8bee71c12f5c8d0a2b97ffdb2c4d86d13c8b8133'), size_on_disk=7521086, blob_last_accessed=1756926340.0802932, blob_last_modified=1756926341.3222878), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417816/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/86256a1b98c6d44750fd21ac9ea099262f1f0ec03d182b65741d669fb80f289e'), size_on_disk=10618044, blob_last_accessed=1756926339.7852945, blob_last_modified=1756926341.8372855), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381041/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a53caa1961d9245d82db10989b8a8d601760ccf26f67666d5558937830c51e31'), size_on_disk=6621865, blob_last_accessed=1756926774.784766, blob_last_modified=1756926775.9747593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417878/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d3d105cc45280a5ea88c2bba754c0c5849d0cec7112a0926e9ed0c946e7f5cfd'), size_on_disk=8838404, blob_last_accessed=1756926351.2492447, blob_last_modified=1756926352.7352383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381043/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f6ded8e6496dcfdd499edc382d19da2071f25daebbb250b375b9a2706e9d8211'), size_on_disk=9139336, blob_last_accessed=1756926775.6057615, blob_last_modified=1756926777.1057532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417645/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/761eba472193b23cbf7735946e08aa1c4db5b1abd5125f1aad22ddabbf872f04'), size_on_disk=2080044, blob_last_accessed=1756926309.765425, blob_last_modified=1756926311.5084174), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417623/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/562a43ff381a2ca177fb1cc3dbe47fc2cf7cade495bad511ca7ed75aefca7a98'), size_on_disk=19049552, blob_last_accessed=1756926302.696456, blob_last_modified=1756926305.4934437), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417897/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/309aab637d037638082524649ef7b0828885a98e786c64af01072d68968b7d66'), size_on_disk=7857749, blob_last_accessed=1756926354.8022292, blob_last_modified=1756926355.9902241), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417873/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d282d59c3c191bd7b7fa7251108224d0ac3929bc98f96a2c43f283f8a5cd046a'), size_on_disk=7609540, blob_last_accessed=1756926350.3852484, blob_last_modified=1756926351.373244), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417825/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/efc4971b63b21d6aee96e17d9e9b2cc2e3fd34608cc3722e36032c594fc03e48'), size_on_disk=8550531, blob_last_accessed=1756926341.8732853, blob_last_modified=1756926345.0782714), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417927/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8e639025209cff8d34b7e35a5fa371282bb725173225097a39f8f2101a49deed'), size_on_disk=7241057, blob_last_accessed=1756926360.6762037, blob_last_modified=1756926362.1171975), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417913/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/35c1a160b767f9f5870216ed33a1dc24804690772a5baee58f623c6dc0632127'), size_on_disk=9135350, blob_last_accessed=1756926357.9462156, blob_last_modified=1756926359.3142097), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381064/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8569620c4d2efb8dd163aa69d8edf8f855b96eba637e754a126a88e6f0c73192'), size_on_disk=20598404, blob_last_accessed=1756926779.800738, blob_last_modified=1756926781.1587305), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381061/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d44d57e9c03bb6e6e750809eea88657c2960ed251b8acaa1352a07f4cb346a82'), size_on_disk=3789431, blob_last_accessed=1756926779.0527422, blob_last_modified=1756926779.8977375), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380958/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ef3849004b6c72205ea1b298cf6a586f1114061ca94925229eda191d4666885'), size_on_disk=3288900, blob_last_accessed=1756926369.617165, blob_last_modified=1756926370.303162), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381049/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c571bcc5f87a79c7cd38b9614ee83b0d5cc742094e17a7990c829394fca193be'), size_on_disk=3753119, blob_last_accessed=1756926777.145753, blob_last_modified=1756926778.2097468), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417915/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9fbfc50c3e1f756169d25442880ba33e5cf237e3c83a0d674b64ace47e8d3322'), size_on_disk=13801295, blob_last_accessed=1756926358.318214, blob_last_modified=1756926359.4592092), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381024/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5182750a0c8af8253cc98ecad8715519463d5c18adbe067488dfef91198c0d80'), size_on_disk=603678, blob_last_accessed=1756926378.7151258, blob_last_modified=1756926379.128124), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381052/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/652334151cb539b888d9cfcb60d9711d80bd74ec65cfa3799de10b2970ab9c09'), size_on_disk=5788579, blob_last_accessed=1756926777.7767494, blob_last_modified=1756926779.283741), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381065/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/99cc73bc4c0908da04c01c7c59eff995a478ffc023c31fb970c7bb9789b2d70b'), size_on_disk=14673323, blob_last_accessed=1756926780.0087368, blob_last_modified=1756926781.5847282), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417848/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eec4ffff5d145c0ee0850d5e68787ab90e6510cffa5803579db44e3a575822dc'), size_on_disk=13556826, blob_last_accessed=1756926346.1882668, blob_last_modified=1756926347.2852619), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380943/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/987771cb978dbc949eff5044071149a00223c6771efcc870850e3c8cbe0648c3'), size_on_disk=13397144, blob_last_accessed=1756926367.295175, blob_last_modified=1756926368.7601688), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417680/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aa9e972e7427b1db5556008756651933c8792891339607dee2ee231733072d1c'), size_on_disk=18443288, blob_last_accessed=1756926316.5853953, blob_last_modified=1756926321.4263742), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417750/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aedd94fe4b42547108a807242052397b6dfbfda0eb4e56a9f2e267b312251147'), size_on_disk=1531584, blob_last_accessed=1756926329.1973405, blob_last_modified=1756926329.9403372), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417813/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f7d5239d0d39e171aad1e84964f8cdc3c7a325fe8c38b6829e114cb3997e658'), size_on_disk=10238322, blob_last_accessed=1756926339.4142962, blob_last_modified=1756926340.8722897), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417933/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4fe347d88754646d39d93dd8a78e857d04d1903f7163a21495038b087d5f6b8a'), size_on_disk=12389872, blob_last_accessed=1756926361.7481992, blob_last_modified=1756926362.9061942), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417741/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/734d5947cb26e877b6ae9e9adaf84575974a4fe2fb464ba11c4320906f17a42a'), size_on_disk=4566889, blob_last_accessed=1756926326.96635, blob_last_modified=1756926328.5653431), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380930/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/734e9bc8119c7a507e9cf84f9926b1f9d06ccc8b0062a9b4dc36631f6f1bc6d1'), size_on_disk=4604638, blob_last_accessed=1756926365.1301844, blob_last_modified=1756926365.9251812), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380956/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/15047f69259f2cbc2cb08949d07ac6fde505bade6a0e3bb3912b183a48a4b288'), size_on_disk=2282680, blob_last_accessed=1756926369.4771657, blob_last_modified=1756926370.1431627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381045/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdc8f9e6447280a7fc713c2a8f2157eb9076a5c01a7eb8437176e9ee30d6d4ab'), size_on_disk=4939421, blob_last_accessed=1756926776.3437574, blob_last_modified=1756926777.2507522), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417807/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ebc732f5d06d745bb63af3c7f5966722972fa722ef9d0194d71cf6d9485fda83'), size_on_disk=14049230, blob_last_accessed=1756926338.268301, blob_last_modified=1756926339.5272956), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417898/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ced2c80d4f9d2aa28260b885a20b1c90b41d8eb54ee92f418ed592b97b954a9e'), size_on_disk=6422129, blob_last_accessed=1756926355.1262279, blob_last_modified=1756926356.0962236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417910/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/694d86a1e469836a53e1ce5dba0d64b77b8f68590a2034e2a1ef5310457dc7fb'), size_on_disk=4494010, blob_last_accessed=1756926357.3112185, blob_last_modified=1756926358.3942137), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417701/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a0dd9587c7525a610a3794cf41f6db24a6ba22a12ed852dc27381a68db3e8fca'), size_on_disk=8781514, blob_last_accessed=1756926321.6273735, blob_last_modified=1756926323.1263669), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417871/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e44cb883893b5e30baa752dfbd773b31795f2072a7ec4f473371f435989cad42'), size_on_disk=6264896, blob_last_accessed=1756926349.9852502, blob_last_modified=1756926350.937246), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417847/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4dd39704bc9c1d60a8271f16206887171ee92abde023c53501c1560533a688c1'), size_on_disk=4755120, blob_last_accessed=1756926346.125267, blob_last_modified=1756926346.8372638), CachedFileInfo(file_name='GSE209631_metadata.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE209631_metadata.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fe31b19357a717d3d51d1d201620d37fca79b1c8'), size_on_disk=61, blob_last_accessed=1756926300.2214668, blob_last_modified=1756926300.2814665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417728/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2be09708238c50a31b4e9b334c6202c26eaa6a950ebf3de4287651999d45097e'), size_on_disk=3385901, blob_last_accessed=1756926325.0603585, blob_last_modified=1756926325.869355), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417703/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e82b21d919b84770e2d141eaea09aff82ef57c3ff3dbd15c588c1b8cff212c9e'), size_on_disk=1462841, blob_last_accessed=1756926322.0533717, blob_last_modified=1756926322.898368), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417840/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7e32bef3c0f7f7520382325aba12f5cf9405c22ebce8554b1d877fe24d2da52b'), size_on_disk=9679209, blob_last_accessed=1756926345.0092719, blob_last_modified=1756926346.0542672), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417720/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/961772a081bde1795af5caea9d7562e2b1f889981bd3c3f1c792ad9093a758d7'), size_on_disk=659095, blob_last_accessed=1756926324.1233625, blob_last_modified=1756926324.6243603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417643/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/66dfa3feea4410ba34f5d5d37c7b01e1fa2cdd4ff38f9dfb10d20d55e40e449f'), size_on_disk=1012185, blob_last_accessed=1756926309.326427, blob_last_modified=1756926310.0784237), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417839/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a04fc093d2a4fe873de135165bd62a38a1cb9b29860881ed973022ff128dc6d8'), size_on_disk=6789775, blob_last_accessed=1756926344.5502737, blob_last_modified=1756926346.4242656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417606/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f76c81d98337c9e127bf5516af0dd135458fd622c01f2b5c9fd85ce6edfa4c7f'), size_on_disk=9502783, blob_last_accessed=1756926300.5794652, blob_last_modified=1756926301.6394606), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417882/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ac2bd9710196abc4469d4d78ccbde6ddc67e9ef39ade7d50c32b0d951866e653'), size_on_disk=10076041, blob_last_accessed=1756926352.116241, blob_last_modified=1756926353.223236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417733/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/542261ed8bf189205b611485f1a878f28cae5c90e452ba9cad687eacfdfc2814'), size_on_disk=2645461, blob_last_accessed=1756926325.8883548, blob_last_modified=1756926327.3873484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417776/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/01c4a97f92cd5e2919759d52d083eb824408a2fd7ade9413a060c6b20d233963'), size_on_disk=2759569, blob_last_accessed=1756926332.8593245, blob_last_modified=1756926333.629321), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380963/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c0a30240a0a99347e114928a15a24268495fd37480fde6822f503438bdf3ed48'), size_on_disk=579777, blob_last_accessed=1756926370.2681623, blob_last_modified=1756926370.81716), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417858/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2d7539fcb6297592fe3755ccc88e122441925fbaf9f5600d4daa88639630fd8b'), size_on_disk=1021831, blob_last_accessed=1756926347.8042595, blob_last_modified=1756926348.5072565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417866/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b0424cd7567723443472cb266e090a30e2b64ee6b09bb1b94d8dcfdb9f0b0c43'), size_on_disk=9685580, blob_last_accessed=1756926349.109254, blob_last_modified=1756926350.493248), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756926300.2174668, blob_last_modified=1756926300.2804666), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ad0e80a28e4cd2027893adee5afceab851576ed2637c1a8e94d0af2adae6ace1'), size_on_disk=5088900, blob_last_accessed=1756926375.41114, blob_last_modified=1756926376.3821359), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417842/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c763016dcbf1b53df840e3ea3c1b45234e2d9e797114000efaf692e2c394d487'), size_on_disk=9967694, blob_last_accessed=1756926345.1472712, blob_last_modified=1756926347.1812623), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417656/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/03f8707a323ef7563c8ec2d6352d24c6454d043c4bbbca417680df5c7db208f8'), size_on_disk=10029839, blob_last_accessed=1756926311.588417, blob_last_modified=1756926312.8104117), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417738/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c3d82a351044aa8cd5d43979369088ccf2f563022d4f7aa3683ef20923202770'), size_on_disk=19772535, blob_last_accessed=1756926326.3913527, blob_last_modified=1756926328.6913426), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417844/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ae034bb207abc0c649fb60aae78e798d46647f53b4e0352c6510fb7c8339a234'), size_on_disk=5408445, blob_last_accessed=1756926345.5212696, blob_last_modified=1756926346.4342656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380972/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/91a8106ca7ac2babcabd1549072b2b730909e1386f827e732428bff2bb5251cb'), size_on_disk=6712599, blob_last_accessed=1756926371.1721582, blob_last_modified=1756926372.0711544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417942/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f3a5b1eb0f954ddf66a8dfa1a38465726d2251730d3898865535269a43c7fa4a'), size_on_disk=6911573, blob_last_accessed=1756926363.415192, blob_last_modified=1756926364.3521879), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380978/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a1d811dc3a4bfd44683ddf8bd0401a71d4941497c5cac8d8040a597fc5d26587'), size_on_disk=5902739, blob_last_accessed=1756926372.160154, blob_last_modified=1756926373.09715), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417602/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/26ae91a914e9ad095615c88ac3c62c99e28ae2687ccd66007f13ac8fbbffa3fe'), size_on_disk=3717181, blob_last_accessed=1756926448.0367467, blob_last_modified=1756926301.5714607), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380966/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f27a802d18bf78781f3d168370e0c0e241a130d04a5871d7416442be3ce3e40f'), size_on_disk=4241927, blob_last_accessed=1756926370.3881617, blob_last_modified=1756926371.272158), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417831/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/53d810292e5a513a8ab543fe18783f8d493cedbadc7c97400dc103a4675954df'), size_on_disk=6767934, blob_last_accessed=1756926343.12528, blob_last_modified=1756926344.4612741), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417891/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cabef83ee373e37d528b127d813f1535faede4b98c5524d83e9906df83558b07'), size_on_disk=12596609, blob_last_accessed=1756926353.9052331, blob_last_modified=1756926355.2992272), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417946/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef43c84affe53bbdefcd70cb97a4a4d1e483824b8b6f91e3360a11094609f815'), size_on_disk=4853893, blob_last_accessed=1756926364.0141892, blob_last_modified=1756926365.0591848), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417798/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0ac62fa7573f4b3c756fd9c280be4b97ec0c7c756e8c76307d4fab56d29e1c08'), size_on_disk=10509245, blob_last_accessed=1756926336.5193086, blob_last_modified=1756926337.800303), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417664/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/91fc5dce8497d1fc5466fb5ac31e44a20b295d8f97825500f256f6830ea2f983'), size_on_disk=8203399, blob_last_accessed=1756926313.1894102, blob_last_modified=1756926314.6624038), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417796/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/52d8262071c9c9de8cd8fb52be9e67acf125b8ce67033749fb92989f0d1f9345'), size_on_disk=10184069, blob_last_accessed=1756926336.2803097, blob_last_modified=1756926337.4333048), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417851/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2d8a571fae50598143744a1f13d40cec2ea8c2198e87aae17f96d7c8a00460d0'), size_on_disk=11051527, blob_last_accessed=1756926346.6472647, blob_last_modified=1756926347.6582603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417745/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ab84f2e8e76311cfe45a4b525b239186e835cab37f1e5222fdc35c286064e5e'), size_on_disk=1270105, blob_last_accessed=1756926328.596343, blob_last_modified=1756926329.1233408), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380959/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4693c1876e880fee1ec277fd5ae9127aafa9f40d41a3d5ed482dfcfa8cef55f1'), size_on_disk=873902, blob_last_accessed=1756926369.7061646, blob_last_modified=1756926370.1731627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417896/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/71e393229e9071bad31abe5dc3d41bd5d719a5dcb68077827861b4b4e9a49a85'), size_on_disk=6055777, blob_last_accessed=1756926354.7882292, blob_last_modified=1756926355.8442247), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417652/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d38c028233981eb275c44995eafa73172388a9e0e704fe278b0cedbab49d429f'), size_on_disk=12947754, blob_last_accessed=1756926311.0344195, blob_last_modified=1756926312.1414146), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417827/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8bfd1221f930e86ffd6150c82aae85f00a23fff33c672e40f47d671f5a2e4d17'), size_on_disk=7291599, blob_last_accessed=1756926342.156284, blob_last_modified=1756926343.1502798), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417818/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3b30217b127ff88f165762c5054c392d9489b9007ba7ca8b86f18b44f9988e4d'), size_on_disk=8388230, blob_last_accessed=1756926340.2372925, blob_last_modified=1756926341.5632868), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417663/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cc7010c3ed70c3083d49d88c0f95dbe8f6eff873d4b5848eeae06a22df71a479'), size_on_disk=8938116, blob_last_accessed=1756926313.18441, blob_last_modified=1756926314.5674043), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417715/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ac5effc2ec07a8fb49bac4d77b7d4129a18ae2180807874ea02d1c252a3b68bd'), size_on_disk=520286, blob_last_accessed=1756926323.8693635, blob_last_modified=1756926324.477361), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381028/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8d40caa48e7b9d32d2a0548732ef91c01ede9d0f4f6d74f045817e818c52c808'), size_on_disk=18141950, blob_last_accessed=1756926378.9691246, blob_last_modified=1756926778.2687466), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417694/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/058b507f75cb01bb5912e14e7bbb4e25a1de945e578323bce9f920f31a9ced82'), size_on_disk=11980395, blob_last_accessed=1756926320.09838, blob_last_modified=1756926321.5623736), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417685/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/32edfbf9226b7ef1060ebe3e6986a1803d60408ccda969528cf4a59c5785c722'), size_on_disk=16967633, blob_last_accessed=1756926317.9163895, blob_last_modified=1756926319.5263824), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417828/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eb4987a8ef94e1691c74c1d828d7d9313da4691b577118a73524def2a90c6d0b'), size_on_disk=4827634, blob_last_accessed=1756926342.3472834, blob_last_modified=1756926343.2782793), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380937/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e31f54581c4a4a47033065033f3f9266913edad654543dd07b20d0ffe54847dd'), size_on_disk=2691574, blob_last_accessed=1756926366.2071798, blob_last_modified=1756926367.1951756), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417916/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2abad8555b89fbb5009e8f2a95e553a5ce579d1841ae4e71eec7b137f3282ef1'), size_on_disk=9236466, blob_last_accessed=1756926358.371214, blob_last_modified=1756926359.0582108), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417768/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8bb8e088eb5123049e418c868ab42d94fb48946f934a8313f4cf283e3bfa09ba'), size_on_disk=4124375, blob_last_accessed=1756926331.3153312, blob_last_modified=1756926332.2313273), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417770/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0e7437c90c616b78ccc0963e0388385028b70d66fbe73e0ceb4e30e2d0239816'), size_on_disk=8252889, blob_last_accessed=1756926331.6563299, blob_last_modified=1756926333.0863235), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fc6783c3340c869bc3838a9a9d5abb8bca67361feeef12c42b6cf478d73bfd66'), size_on_disk=9020794, blob_last_accessed=1756926366.2571797, blob_last_modified=1756926367.2561753), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417850/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c08fca103aa3dd1944dcf5c73b67397263842dc687b4101c8ab2c1a63d50205a'), size_on_disk=1182282, blob_last_accessed=1756926346.5122652, blob_last_modified=1756926347.3082619), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417863/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4aabb9d1a4b105c7669deb0f13cd635ab7433ed1ae02f5785aad014d604f1504'), size_on_disk=6900487, blob_last_accessed=1756926348.4532568, blob_last_modified=1756926349.2742534), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380991/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dd86db1779581d5853d4d6a0fa2aeacdb00f92c832683ad788743f17db625db4'), size_on_disk=2640507, blob_last_accessed=1756926374.030146, blob_last_modified=1756926374.8661423), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381018/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/878bf028aca9daeebe7ed0f8ee522580d512771ec4a5e35bfc62ac675176ad15'), size_on_disk=701951, blob_last_accessed=1756926378.2281277, blob_last_modified=1756926378.643126), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417773/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d7980069a740f581dbcfd9416372452203d636d8ad4c9352c3b2d6483cd27379'), size_on_disk=5178664, blob_last_accessed=1756926332.1683276, blob_last_modified=1756926333.411322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417904/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cf9194917da2f4c6c3f0411256b2604e8c14959373d7431047de2e23cffbb9bf'), size_on_disk=7269877, blob_last_accessed=1756926356.062224, blob_last_modified=1756926358.1762147), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417787/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/17f33e06b6bd77c0bbd3141b53fc2d9550b0c54e99b18fe79ddad931e3a283a9'), size_on_disk=12355842, blob_last_accessed=1756926334.5003173, blob_last_modified=1756926336.2953095), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417881/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bf380975f31e362e8964000688bff364809bbe2e8e3efa9ceaa71253ca6beefb'), size_on_disk=8717095, blob_last_accessed=1756926351.848242, blob_last_modified=1756926352.9732373), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417650/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c4d103888f768ff3cb0c68f4b747cb8d8aad8058b75174160cfc42b2504407f1'), size_on_disk=1337049, blob_last_accessed=1756926310.89842, blob_last_modified=1756926311.4924176), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/43f12fb4826be4c7a3dd64800fc72cbcab2a9ce5bed7f7b5b9aee974deb7c06d'), size_on_disk=1116877, blob_last_accessed=1756926376.094137, blob_last_modified=1756926376.6491346), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380945/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/51efeee4439e81d920c4d9e860de321d90c72703600044e52215c774e1540650'), size_on_disk=6902367, blob_last_accessed=1756926367.356175, blob_last_modified=1756926368.6011696), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417795/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b995a48f28ae3baa145438a39c9fed73fa8689eb31e07ff02a3bc6bc827eb76e'), size_on_disk=8032882, blob_last_accessed=1756926336.1053104, blob_last_modified=1756926336.9993064), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381025/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aea8c4d9f3783191d3e89f4566811f01089f3b0b76b736c5167bda54693b0bc4'), size_on_disk=339954, blob_last_accessed=1756926378.7781255, blob_last_modified=1756926379.125124), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417649/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6baf4bcd82d9fe55b8dcdc7602c0d39d20954675ef4c5484331ff25dac8ad3f4'), size_on_disk=2672976, blob_last_accessed=1756926310.8234205, blob_last_modified=1756926311.4704177), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417784/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fd3dd53cf34f48c235e4f771378f42a5605446bbc62d108a5637a042513fd75d'), size_on_disk=4826642, blob_last_accessed=1756926333.7233207, blob_last_modified=1756926334.576317), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c103eef2a9834296fb1328b7012b5b9184c5a3f131817dfca257843e863d34b2'), size_on_disk=9829417, blob_last_accessed=1756926362.1531975, blob_last_modified=1756926363.3531923), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381020/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c85f953439e9f6918c7f7c8194bcbbebe0ef8a96070119813025a42bde8c08fe'), size_on_disk=897644, blob_last_accessed=1756926378.3451273, blob_last_modified=1756926378.7061257), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417861/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef65c45e0e86b7d04e274c39408b332cc94879c0804f16acf7c537b14bb6498c'), size_on_disk=10436399, blob_last_accessed=1756926348.3832572, blob_last_modified=1756926349.6192517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417651/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b95b6be3a7a6a5b4af3bbf15c5a4a6b3893f3030ef6d1dcfe79e6c6554e3d3cb'), size_on_disk=13551797, blob_last_accessed=1756926310.9544199, blob_last_modified=1756926314.6814036), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380970/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bdec4a51127eaf09abb5d25d7f3a732afc3b561105fd18ba1df44d04e6dd90a2'), size_on_disk=3545790, blob_last_accessed=1756926370.9301593, blob_last_modified=1756926371.5451567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417775/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2ca9ba5e1394191206945f41168d555519fe3963d4c39f251c1ead57f2a323f4'), size_on_disk=1059891, blob_last_accessed=1756926332.3723266, blob_last_modified=1756926332.773325), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381029/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6431cec7b7404a0773d8715761e2b161f01ebd8471950e71e17dad8976d83f3c'), size_on_disk=7229691, blob_last_accessed=1756926379.0071244, blob_last_modified=1756926776.565756), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380960/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/60771297311bf96c3213c2a96321062a06067c91d2bba2fce69a7a6d7a0c980e'), size_on_disk=560607, blob_last_accessed=1756926369.7811644, blob_last_modified=1756926370.3121622), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417907/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b24926bd36cf33ea9107043b9a47db8239ad6208745c56002513225e8cb80a6e'), size_on_disk=7594380, blob_last_accessed=1756926356.7332208, blob_last_modified=1756926357.882216), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417717/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/689842dffacf7efa4ba6cd9b4314d195e89f9105eb71675f27019a9e25be63db'), size_on_disk=102777, blob_last_accessed=1756926323.9603631, blob_last_modified=1756926324.5793605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417889/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/295f9efb0d78e3a116ce4022a7830297570446e2017a3fcd3b3d423c0c20664b'), size_on_disk=9617979, blob_last_accessed=1756926353.3102357, blob_last_modified=1756926354.7322295), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417803/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1fd8b542fb1b0e18bc5346c50ad85da440ff6190a987467ec6876ed3ec51a4e8'), size_on_disk=4928668, blob_last_accessed=1756926337.5433042, blob_last_modified=1756926338.7182992), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417638/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c8cface311cd55cd1542ed94d609da72cd2b3087422c28ad26c60e1d81a3e6bd'), size_on_disk=993699, blob_last_accessed=1756926308.4724307, blob_last_modified=1756926309.2634273), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381067/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/da9c5ab3c0f970f9c2af4c0f5f0e9a95d47769e8096ff6b9d76092becbc8517e'), size_on_disk=9837433, blob_last_accessed=1756926780.2977352, blob_last_modified=1756926781.5777283), CachedFileInfo(file_name='GSE178430_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE178430_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/be2c4af55a502d7fa22abb28afe6db347950327bde1bc7e97dbb68fdb0c59f3d'), size_on_disk=9398, blob_last_accessed=1756926861.9192877, blob_last_modified=1756926300.5344653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380998/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6808b8e1fa74b807321826b2c54182a3b6e257f0a223be0a6a94caf9463959e9'), size_on_disk=16847111, blob_last_accessed=1756926375.2231407, blob_last_modified=1756926376.572135), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417928/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/97e74e01fd641bfef68b6206a3c62ab41b5c28d8ea8369ce4198df1b3387b266'), size_on_disk=2449472, blob_last_accessed=1756926360.9072027, blob_last_modified=1756926361.3542008), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381056/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/382194ca9758ddc18f059a649b13c5a21ead3a7f1fa5e012065315047a037a58'), size_on_disk=2351268, blob_last_accessed=1756926778.362746, blob_last_modified=1756926778.9827425), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380985/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2ec986412ef2a9e6e59f4ea3dc278b27ab3975b5025d00bd1cd38fe1867b0fea'), size_on_disk=7208882, blob_last_accessed=1756926373.2251494, blob_last_modified=1756926373.9591463), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417940/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/270559ca1db4605de241e8642461b4d7304d124e4fc624f7d8a8a59867301a4c'), size_on_disk=2328150, blob_last_accessed=1756926363.182193, blob_last_modified=1756926363.9021897), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417639/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/43c5fb0a4f049fa5895f8167928d047cc4954aa37db1fc8a9892fe9f99c401bf'), size_on_disk=782319, blob_last_accessed=1756926308.7694294, blob_last_modified=1756926309.6284256), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417674/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5970db11c3efe2bba4eb50d07480cb0b46ed74dfc3ddf12d611ecd61f1d5fb68'), size_on_disk=4619941, blob_last_accessed=1756926315.5114, blob_last_modified=1756926317.630391), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417691/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/48de5a2968610e5ab855522d27f535d40e1973c1835ec32fcfa5bcd91a0795dc'), size_on_disk=14481663, blob_last_accessed=1756926319.447383, blob_last_modified=1756926321.2093751), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417708/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b58038866c334daaf757a086f99c3a5c5962b8563769ee0a3205bcdcbb0144ec'), size_on_disk=250863, blob_last_accessed=1756926323.0003674, blob_last_modified=1756926323.366366), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381051/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ba017d941936d755013757711f5de4661acd187ef79524662d85485ba6588f34'), size_on_disk=8199472, blob_last_accessed=1756926777.322752, blob_last_modified=1756926778.4757454), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417808/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0251ae27bd961d6c962253c555963b42148092cdaa2616a78f6872cf1a4e5e4d'), size_on_disk=12900784, blob_last_accessed=1756926338.3973005, blob_last_modified=1756926339.439296), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380948/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6ac219aa7f5be77c21bde60c80ccad59edf825e2a009d6a4b7fc8bd4d8be83d4'), size_on_disk=6046323, blob_last_accessed=1756926368.6691692, blob_last_modified=1756926369.5471654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417901/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/69432386b347f5b364c02d270b38891358fa2068465354d1ada84d5450ea5bf0'), size_on_disk=9484337, blob_last_accessed=1756926355.6292257, blob_last_modified=1756926357.0582194), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417804/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/16ee23ae4dbe8a4d968a614f5e6de65028b6edce012231a50eb776d65f949d34'), size_on_disk=2107502, blob_last_accessed=1756926337.7883031, blob_last_modified=1756926338.5083), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380969/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/10410488c74f35a493cef2ffc4a64b28b336be1fb32a2b6dac692e0d7fbd4271'), size_on_disk=1973405, blob_last_accessed=1756926371.041159, blob_last_modified=1756926372.1191542), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380941/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9a23f03fee8d60d752bd0bc1e59e9205abea4f3a78e3272b9a03447f076383d5'), size_on_disk=3514837, blob_last_accessed=1756926366.9851766, blob_last_modified=1756926368.7591689), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381031/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b4705ac4577c8e7b116d1a1b677967f69412b0e4716e0de7fa7ee97bc9be4d74'), size_on_disk=4551217, blob_last_accessed=1756926379.2931232, blob_last_modified=1756926776.2707577), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417943/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0e4191a403f5070636bcb281bd80d39e11c70fb502fa2b18150032d58f2e0740'), size_on_disk=5396199, blob_last_accessed=1756926363.7271905, blob_last_modified=1756926364.542187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417747/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7011e7ff4b64ca9c57bdab93cfcfc52938e7b0574b48417feb4f6185f6c118b9'), size_on_disk=4791771, blob_last_accessed=1756926328.7593424, blob_last_modified=1756926329.9543371), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417948/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d43ea802a452aa9d57004381e1688222e61bd313b107077c1c663555547d1d44'), size_on_disk=1748124, blob_last_accessed=1756926364.3661878, blob_last_modified=1756926365.038185), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417620/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b973f1a83f111c4e114a40d3731ff24779cafcb7b71523fe8571cf2ed849b316'), size_on_disk=20780203, blob_last_accessed=1756926302.5494566, blob_last_modified=1756926306.7334383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417687/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fa4f79173f5a5ed278c7ef80d9fb220aa9c615de97bae7963969e27ecf0e587c'), size_on_disk=13352339, blob_last_accessed=1756926318.3003879, blob_last_modified=1756926320.3913789), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381062/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/76151e17f242c5108010debea961b0b13f3821be2fcb77fd923a36cc0e672695'), size_on_disk=4880117, blob_last_accessed=1756926779.3357406, blob_last_modified=1756926780.5167341), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417743/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d4eb93049648498d2fdc1f125574429a4dfdec65e626ad9fa16bdf0d0cf599d8'), size_on_disk=2280534, blob_last_accessed=1756926327.462348, blob_last_modified=1756926329.1343408), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417880/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b08db0a2399fc4e5b6f59c19b7af05b1294d0fac10b2c336b627234fa828f1be'), size_on_disk=9818886, blob_last_accessed=1756926351.5782433, blob_last_modified=1756926353.246236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381055/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0aad0d3724742845da7f0afae0c18ab5c992bec76c02e0ffda057f00d0894d35'), size_on_disk=3355008, blob_last_accessed=1756926778.3377461, blob_last_modified=1756926779.263741), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417819/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a1ba6808d672dbc9c58c26364c5cd9ed0b8a3041e6714844846648d3cd1aa050'), size_on_disk=3244517, blob_last_accessed=1756926340.7022905, blob_last_modified=1756926341.6952863), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417886/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62fe87e7519370c4ac46e6d9d0abf314a8f92d9a5d6804ebddae0ed095641219'), size_on_disk=7968977, blob_last_accessed=1756926352.9472373, blob_last_modified=1756926353.7992337), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417637/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ca1a954c7b2aca56529daf9a89249ad4e6ec2373e29ac7c19d2af1533afc4263'), size_on_disk=2776397, blob_last_accessed=1756926307.8804333, blob_last_modified=1756926309.1794276), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380990/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4e04122d2830db35f2b82b29efaca846fe10d7638c88c3e4d3cf78bf10cf245a'), size_on_disk=2093813, blob_last_accessed=1756926373.9261465, blob_last_modified=1756926375.1611412), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417714/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d6abeb724a8c8b23217f3465284564da5baffee59ee4e3af582e0a3705ae32c6'), size_on_disk=262443, blob_last_accessed=1756926323.756364, blob_last_modified=1756926324.256362), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417799/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cba2fb579c7706a7ff38fad3c7c649000560c1052e681adea986c5cc02c02cfb'), size_on_disk=5688906, blob_last_accessed=1756926336.7133079, blob_last_modified=1756926337.7073035), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417782/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d6624acaa7c4ec21ae02d6d1322b8669a148308095e714b41bff868483fe2fa9'), size_on_disk=11742567, blob_last_accessed=1756926333.5273216, blob_last_modified=1756926334.824316), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381033/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5fff567b96a4649d787e93ef8cdca2d8ce977b7f2eb25d6f78e50bc30c798bce'), size_on_disk=1712156, blob_last_accessed=1756926379.7391212, blob_last_modified=1756926380.3661187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417648/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fb706eb700e8fc003822b897accd3501360d737fd1a9ffcf26c3e9d9b25a5e35'), size_on_disk=1285054, blob_last_accessed=1756926310.3104227, blob_last_modified=1756926311.0174196), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417854/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d52c8b14e197b179a9b43fbc89bb3205d09d458161ee49ac15a62b7c1f7a39fe'), size_on_disk=6203801, blob_last_accessed=1756926347.311262, blob_last_modified=1756926348.387257), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/64a119c8028083521d3872e2daca1545f2f8753eb8acfc751bf0c6d25803a3f8'), size_on_disk=2003211, blob_last_accessed=1756926376.794134, blob_last_modified=1756926377.2761319), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417836/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4fb964c54f4a37387c7844902d6171011a9a81efff244f365efcb156f630158f'), size_on_disk=3312893, blob_last_accessed=1756926344.2532752, blob_last_modified=1756926345.0702715), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417677/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cfcdac65879c0c26771c1cd1c4a656d4db20e6adb2ed3ad833cf23cd35a9d848'), size_on_disk=13135194, blob_last_accessed=1756926315.9083984, blob_last_modified=1756926318.1283886), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417616/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0b0f3c9481fd0e254abd544ba803de0c9da9b3a686451975f981d02fc481148d'), size_on_disk=15058562, blob_last_accessed=1756926301.8254597, blob_last_modified=1756926305.0234458), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417723/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f397cef2d9c491fb034826727c63b3c224f6ed791c9b4dcf884c63c55c9961c'), size_on_disk=2800893, blob_last_accessed=1756926324.70836, blob_last_modified=1756926325.4453568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417812/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9e8445f413d26051897f0fe3e7566b116a9a55062b78201990de334f17aecb31'), size_on_disk=19905089, blob_last_accessed=1756926339.0272977, blob_last_modified=1756926340.6232908), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380962/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/72be01aa42db736ffac8236c23c16edea00bdf5da88c3114fc94d9eb3daa4ee9'), size_on_disk=1460258, blob_last_accessed=1756926370.2361624, blob_last_modified=1756926370.6811604), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380965/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6b384b53be751cec9056e74c2c8ce9fe2e532cfbf6a38e43bb09ce74ec817f3a'), size_on_disk=12974487, blob_last_accessed=1756926370.3761618, blob_last_modified=1756926371.4361572), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417629/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/29c9c86be94792d797005b106ab00d066634bea44fbf17e81738a00cbffa264b'), size_on_disk=22750780, blob_last_accessed=1756926305.5594435, blob_last_modified=1756926307.5804346), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417822/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62cbbb0172158a2c0776000136424032a794757ca9e9f971e5f445deb20e8579'), size_on_disk=6040431, blob_last_accessed=1756926341.3882875, blob_last_modified=1756926342.8302813), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4c942b91ac018330aeb4da512a3e47ae4531a434b6e9933cf3e2bb476a77f9ca'), size_on_disk=6312758, blob_last_accessed=1756926366.1041803, blob_last_modified=1756926367.0141764), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417615/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3b8b3c4c686238c7c642151bbdcb2ba7796de56c1922e558a1e7f08507dc5abb'), size_on_disk=2866472, blob_last_accessed=1756926301.8314598, blob_last_modified=1756926302.5744565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417756/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ede027f2c13a1e6669becf6194ace85af5800d5fe77db16ec47bfb3abaa735d3'), size_on_disk=8620421, blob_last_accessed=1756926329.968337, blob_last_modified=1756926331.0303326), CachedFileInfo(file_name='GSE209631_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE209631_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/be49eef90682fbd55a5e60ada8b4c1c554e1b5a2ec1209996269db020d3bb074'), size_on_disk=4258, blob_last_accessed=1756926300.173467, blob_last_modified=1756926300.594465), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417905/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b9d291469b7a414bab157f45bfd3a1f7ffaaf931de25fec9eb18012a18b70939'), size_on_disk=4418829, blob_last_accessed=1756926356.1612234, blob_last_modified=1756926357.2652185), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417936/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8ba89583b8c822b64968df9eca8273d12270429f0f820d6027f13c66504c383d'), size_on_disk=4552381, blob_last_accessed=1756926362.205197, blob_last_modified=1756926363.171193), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381021/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3e80794742d10b6d120e70c4e4f1d7f77123ddf8aa18499f686b4abc8eadd325'), size_on_disk=1524611, blob_last_accessed=1756926378.410127, blob_last_modified=1756926378.897125), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417675/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/79072181ebce12769f2f0a1971977d6691dde1841b53d7b54cf9d4f47f53b3dc'), size_on_disk=6478433, blob_last_accessed=1756926315.763399, blob_last_modified=1756926317.2643924), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417630/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/26fdbadf7c1252a8fd73ac85ea7430d942d2d11ddc43ea1b2590aaa008e08337'), size_on_disk=3015575, blob_last_accessed=1756926305.7104428, blob_last_modified=1756926306.7214384), CachedFileInfo(file_name='GSE222268_metadata.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE222268_metadata.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4459325dc2deeb9f00d803b89cd8fdc823ed383f'), size_on_disk=61, blob_last_accessed=1756926300.2714665, blob_last_modified=1756926300.3444662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417719/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cc0a365d1d79f0469571216394f0cb211b9096f2a567e0a347a18f0c496bac06'), size_on_disk=2076369, blob_last_accessed=1756926324.010363, blob_last_modified=1756926324.951359), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417849/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/23b6606ffcf116cf066b103bf1b6b4fb33c40e544e20e4565fa44c5bbcea4bdd'), size_on_disk=5689527, blob_last_accessed=1756926346.584265, blob_last_modified=1756926347.3892615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381034/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/112a8e705f7536fa06c630165d3c409f87a5784ad626b891cc2b3919e24bdd14'), size_on_disk=250813, blob_last_accessed=1756926379.795121, blob_last_modified=1756926380.1791193), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417710/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9837ecd3769eaaf407d939ff09d6ae74d832630b27353251942bd262cd70fc35'), size_on_disk=330457, blob_last_accessed=1756926323.098367, blob_last_modified=1756926323.797364), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417607/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/623d085888c300fba1d05a9018ec3d4c31912f2b91a872ef34feda3322889b70'), size_on_disk=6809356, blob_last_accessed=1756926300.611465, blob_last_modified=1756926301.75846), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417766/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/39da4f16b82cc0b5019d8526f2e3eed49de9760e319f8e839db621bf4c1ab021'), size_on_disk=6482957, blob_last_accessed=1756926331.2123318, blob_last_modified=1756926332.1133277), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417920/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/00a869703f03e3df3f0f51a7d93d52dd7eac5174ed2a1b752207460e71c7b41a'), size_on_disk=14880236, blob_last_accessed=1756926359.1242106, blob_last_modified=1756926360.409205), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417698/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d240b652ebca64784e037cc8ffbda424e2c92f368c5bb9c730930f48f29facc2'), size_on_disk=1855826, blob_last_accessed=1756926321.2993748, blob_last_modified=1756926322.0703714), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417672/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f0741c46f1460c30d4d9b88ca1f7c0ca77a040cf25a34d82fab4c777a921ac26'), size_on_disk=18268758, blob_last_accessed=1756926314.9764023, blob_last_modified=1756926316.5143957), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417864/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/15d259c52e80a2d2d9c20274ef940237c90001d32f2aecb80779335a708222ff'), size_on_disk=3706383, blob_last_accessed=1756926348.5722563, blob_last_modified=1756926350.0962496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3fe2828766efd936f02efae2da3e068ef9b28ea374e594e09850e3e8ff0d8164'), size_on_disk=4421540, blob_last_accessed=1756926375.7961383, blob_last_modified=1756926376.7581341), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417780/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4f1b7c3ded6441c8e906c3494ae63750d25303ee6f8d8e05120dc11ce8d79080'), size_on_disk=9025914, blob_last_accessed=1756926333.2923226, blob_last_modified=1756926334.387318), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417931/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7e70415ed0dfb1eb42e46789c905ccf2b593dc192d907d939d87acd59cd963e1'), size_on_disk=4991063, blob_last_accessed=1756926361.306201, blob_last_modified=1756926363.1161933), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417751/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9a5733130de69115a731511dac8bf577fc5609a7f9649106ae9a12ffae7011ba'), size_on_disk=1660869, blob_last_accessed=1756926329.2023404, blob_last_modified=1756926329.9043374), CachedFileInfo(file_name='GSE222268_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE222268_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e0f1d5b82fd97798d0c02d992c49c0e83d8dcf82e07e001284e78d9656355481'), size_on_disk=4411, blob_last_accessed=1756926300.1784668, blob_last_modified=1756926300.5344653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381013/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c831ca7cc9cfd2cc2ccd92a0168b2e14dedf2bde9e29780dfaf6d281b31e9462'), size_on_disk=4648281, blob_last_accessed=1756926377.4421313, blob_last_modified=1756926378.3451273), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417900/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/45d5a0bbf39e542f22aa4e36186673d1246533dc513f006312a26286496f6ead'), size_on_disk=11235417, blob_last_accessed=1756926355.535226, blob_last_modified=1756926356.6632211), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417758/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/54f8e8d84e5b8b9457aaa143829fcfd5a4715d0fb184478ce659cd6c6efe7be1'), size_on_disk=4176901, blob_last_accessed=1756926330.0203369, blob_last_modified=1756926331.1383321), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380983/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eec33f1158aa017b073fcb16d6400cd800eb79e8c0b78b5e2f831d7413e4a736'), size_on_disk=4478939, blob_last_accessed=1756926372.869151, blob_last_modified=1756926373.6721475), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381047/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dda6f6ca669a0e5c9d34b548305cbeb6b82f1b70086e06d3604e7e096fd7fb25'), size_on_disk=6799004, blob_last_accessed=1756926776.8777544, blob_last_modified=1756926778.4717455), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417682/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/83b3100cfd1dc6918855c4a711ff607b36f40971655733855a7df353b4d5fca5'), size_on_disk=14037100, blob_last_accessed=1756926317.1663928, blob_last_modified=1756926319.1013844), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417605/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f689b77eccd8d95d70fb616f4e2aa03a41201aec2a8c3c86849b7b145cb0f796'), size_on_disk=1787545, blob_last_accessed=1756926300.5184655, blob_last_modified=1756926301.74346), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417806/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2befcad73b2aa8170ceaf8c249ceba45e87037771e86c9bb4bd7addc5815bbec'), size_on_disk=5276603, blob_last_accessed=1756926337.9863024, blob_last_modified=1756926338.8112986), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417802/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ea68aebb727f77d846a145d43ec16fa2496e9c05d49a8791aa131b8e3e536452'), size_on_disk=4881005, blob_last_accessed=1756926337.2223055, blob_last_modified=1756926338.1273017), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417632/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/23c561fde2e6c2973ec1913373c343a5c49d6cc0dbb44ff21eb5aff87cd77d3a'), size_on_disk=18481828, blob_last_accessed=1756926306.2764404, blob_last_modified=1756926308.5744302), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417765/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bd2b5a1b1593db002ea9c159d4c40d1d8addddbf124207a7b32b0bb43e2e4d72'), size_on_disk=2696437, blob_last_accessed=1756926331.2103317, blob_last_modified=1756926332.046328), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f00bc431128183affc8964f9610f3e9486a043bcea5b31b3ebbcbd9b0070a448'), size_on_disk=6096918, blob_last_accessed=1756926377.3431315, blob_last_modified=1756926378.2571278), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417688/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f21ccaca0f6782b6ce2b70b50c9dc93487b6aaec03db6c30d9cb0c9fc01dda42'), size_on_disk=16023102, blob_last_accessed=1756926318.5463867, blob_last_modified=1756926320.0283804), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417647/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8c01c928abe00b5afa6a2292820695797391b3671cc110b9026f5fc5cb8564ae'), size_on_disk=2891716, blob_last_accessed=1756926310.1614234, blob_last_modified=1756926310.93942), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417613/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/695d3095619f3a313d25c506b9d0493f171896f1439bcf3bd1cd932d1f6fed5d'), size_on_disk=2812565, blob_last_accessed=1756926301.6434605, blob_last_modified=1756926302.4814568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417810/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b807197acb33acdd1b70eddedd59dc4d831e271d231d6c9bb6f1281fb4d5813f'), size_on_disk=9808564, blob_last_accessed=1756926338.8082986, blob_last_modified=1756926339.7202947), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417834/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a69c65a4d32399355863fa14d4a1e92ecff2c92498e122e8e77796de16d42948'), size_on_disk=8537233, blob_last_accessed=1756926343.3672788, blob_last_modified=1756926345.2462707), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417870/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ca1bc9825b47ee9ac59e53bc6ada3b1a9e8aba1b57c57c57947afae7926054b'), size_on_disk=10710319, blob_last_accessed=1756926349.7052515, blob_last_modified=1756926350.7552469), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417833/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/48792db399d7f0aab6765109e8c01abd61a2da93e5d0bdf7c325bcffe01fe113'), size_on_disk=8139822, blob_last_accessed=1756926343.3262792, blob_last_modified=1756926344.480274), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381015/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/345c83c5ab13d230542683014d3d6073ba5a348e3063fea325843981c6c6e7c1'), size_on_disk=3807314, blob_last_accessed=1756926377.67913, blob_last_modified=1756926378.4971266), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417767/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8d0afed33cd85298c709656da4b210e3fe87aa4d6822208d744200816d59b95b'), size_on_disk=3739401, blob_last_accessed=1756926331.2333317, blob_last_modified=1756926331.8623288), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417760/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0500e5835116a61477f2d98554d0dd97e8d932a6b564f709c40c4884f2b49052'), size_on_disk=2234621, blob_last_accessed=1756926330.2873356, blob_last_modified=1756926330.8273335), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417735/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/634551506800bc76183ce179c8c8f324cb6f0f577f6cf0b629b74ce4f1e995d5'), size_on_disk=19171195, blob_last_accessed=1756926326.0393543, blob_last_modified=1756926329.4873393), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417914/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d062dbf660e1ef240ff8ef99ff2d486d9caaabda14575e6365ce6f725fa91ccf'), size_on_disk=8599250, blob_last_accessed=1756926358.2422144, blob_last_modified=1756926360.3412054), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417797/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/12ee278356fa75b0ad555fe06376c78b71630324b7b89aa28abe960c187d256f'), size_on_disk=14391201, blob_last_accessed=1756926336.3743093, blob_last_modified=1756926338.322301), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417619/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0a4d4159941caa34a5e079821d49047d1434077926b2933c27d384e04ed2e5b5'), size_on_disk=9949802, blob_last_accessed=1756926302.5744565, blob_last_modified=1756926304.05045), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417761/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ba86b2041414034d38b95ccbf3c65957642c56b56914240999986ebc76b9e193'), size_on_disk=1635118, blob_last_accessed=1756926330.478335, blob_last_modified=1756926331.0933323), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381000/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/503f854145f3a220aa936072c3e66410d34914fef948992399a4addbfc977186'), size_on_disk=3904727, blob_last_accessed=1756926375.3401403, blob_last_modified=1756926376.5601351), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417603/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdfb1fd9b21341104e6423c684b0ae8a52ed1701eb3e232babb98340f953ea5b'), size_on_disk=4460026, blob_last_accessed=1756926300.366466, blob_last_modified=1756926301.2574623), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380986/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f23d3060df5cb96778fbdf10fff0155c931e1a1e8cc077c7f1582542d54827f5'), size_on_disk=5858046, blob_last_accessed=1756926373.285149, blob_last_modified=1756926374.5971434)}), refs=frozenset({'main'}), last_modified=1756926783.3167186)}), last_accessed=1756926861.9192877, last_modified=1756926783.3167186), CachedRepoInfo(repo_id='BrentLab/rossi_2021', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021'), size_on_disk=213725645, nb_files=47, revisions=frozenset({CachedRevisionInfo(commit_hash='6b29baae54286d5fbdd1c70f499c03319badad51', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51'), size_on_disk=213707016, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466110/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/d5283e08e898b4f59837f7ff2064f79c8a828994f868c637894f4e07df36daa5'), size_on_disk=13123795, blob_last_accessed=1757598690.8318212, blob_last_modified=1757104407.6787057), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466116/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/587a7b86dffe3d7ade9adeeb94029ea1de54e61fb2c2445dc527979b7f1c24ac'), size_on_disk=5596311, blob_last_accessed=1757598690.988821, blob_last_modified=1757104407.3507075), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/04bacc0f8c03ee000ba3b69320522d0b1be909686474b6e8a126173a0ceae8c4'), size_on_disk=15547870, blob_last_accessed=1757598690.8528214, blob_last_modified=1756944357.1972551), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466142/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/3dc044b5cc1b7bc8070d8de4b7b870524b296f26b4f43b22dce2dcbf161d4c74'), size_on_disk=1739520, blob_last_accessed=1757598691.2588208, blob_last_modified=1757104415.5896657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466112/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8f3441964a046d3c148b41f335948a085d26884e1a890e515711a2d1b48a40be'), size_on_disk=4883601, blob_last_accessed=1757598690.9128213, blob_last_modified=1757104408.5027015), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466125/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/0bba21490ea0a6297031b0ff3219fc0158d9bbcf15099906c86a770b1a1312fc'), size_on_disk=2624759, blob_last_accessed=1757598691.081821, blob_last_modified=1757104409.4426968), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466139/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/25c7d3ffbacca76fd998edffe0c762e220f448140019aa2d389facff5117ce48'), size_on_disk=176811, blob_last_accessed=1757598691.2478209, blob_last_modified=1757104412.6266806), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466108/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/56875f81a3cc7c1d40b3042b2c443058eb0f9a2b7e7ebc2e1704c098be612628'), size_on_disk=1765819, blob_last_accessed=1757598690.8238213, blob_last_modified=1757104407.1947083), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466117/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/49065d4cee74c20f34303405e34e284e29f19082b76fa883a38aeab24cadf674'), size_on_disk=1742958, blob_last_accessed=1757598690.995821, blob_last_modified=1757104411.8276846), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466150/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/80cd2d325e5d7a50c6ecbf79a052f45c54a93b9423e90735bf8989aada9bb9eb'), size_on_disk=3234303, blob_last_accessed=1757598691.3798206, blob_last_modified=1757104417.837654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466111/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/ba431fc9a05f40581e450429d467226a8e3c49195de355437f9044bb0fdfe524'), size_on_disk=5749460, blob_last_accessed=1757598690.9028213, blob_last_modified=1757104404.9707196), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466128/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/f631797d2bc13a580f570637d94a0b9ea37deb2fd7ef92e32c66fd9014260525'), size_on_disk=1059587, blob_last_accessed=1757598691.118821, blob_last_modified=1757104410.6996903), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466140/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/25f98a891820b5a91c11629ed5615d40ffe1fe4a6cba1b2c6642067d76b5be87'), size_on_disk=68174, blob_last_accessed=1757598691.2588208, blob_last_modified=1757104415.6456654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466141/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/e9efb807ce35fc3e43b1ff0d27b80646c7d137c587abd9d6247393e44d4f30a0'), size_on_disk=986450, blob_last_accessed=1757598691.2628207, blob_last_modified=1757104413.129678), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/09f6dce90ec0e64d8f18614091dd18ad2e86213a'), size_on_disk=4403, blob_last_accessed=1757598690.987821, blob_last_modified=1757104409.9196944), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466149/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/ae4d5237fde214a92d3267fbe11f6f1f88569cc7f3f7ccb40fed200871bf33b1'), size_on_disk=10933582, blob_last_accessed=1757598691.3518207, blob_last_modified=1757104415.1316679), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466114/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/2c07f671cca471d6ad3e10e071cd1b976a89a45ff32920913d8fc36dbeaa1678'), size_on_disk=10992797, blob_last_accessed=1757598690.9498212, blob_last_modified=1757104406.3287127), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466136/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/f5e7fd644d30ae0c8e2c72bc341155bfe5df2ce102b68b08ff4d243470026fcc'), size_on_disk=343033, blob_last_accessed=1757598691.1958208, blob_last_modified=1757104411.7116852), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466144/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/57b3d2c7917bfa29312cc51c921beff914858c61762e28a658eb29d8eaf3348b'), size_on_disk=918271, blob_last_accessed=1757598691.2758207, blob_last_modified=1757104413.554676), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466124/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/39ef32412cae257b724c3b797ea482eeffb5b29afef4246d997ea525a2124054'), size_on_disk=7162800, blob_last_accessed=1757598691.075821, blob_last_modified=1757104409.5966961), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466113/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/acec3b041a74ab45dbeb03e7f183c2b3d16479aadd64a81f80a74aa42d56368d'), size_on_disk=5866774, blob_last_accessed=1757598690.921821, blob_last_modified=1757104409.0226989), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466129/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/b290d4c9436756b444d0da1af1229b0a2a6e877fd968c5d01b73efa96e62cff8'), size_on_disk=6955953, blob_last_accessed=1757598691.196821, blob_last_modified=1757104410.78769), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466122/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/6362da9e07f02d4c9dc05f15f638ca1b1bf0de5989da4ee8c6f0768f2453f98f'), size_on_disk=1249990, blob_last_accessed=1757598691.0398211, blob_last_modified=1757104411.9016843), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1757598690.9228213, blob_last_modified=1757104403.6017265), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466118/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/192057e6004dd5cd303e8b1abf54cb72005e211c315665e28eec7b617a84d95a'), size_on_disk=728586, blob_last_accessed=1757598691.0068212, blob_last_modified=1757104407.0917087), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466130/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/73db821736e3e47c09da250e369fa06812e02e5abe7a4a3ceab5d1020422b66c'), size_on_disk=1920920, blob_last_accessed=1757598691.1328208, blob_last_modified=1757104410.9306893), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466107/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/2f924bc58d82a21b621b051addd1913c77369afaa738171ef0962548fc1a1021'), size_on_disk=5670198, blob_last_accessed=1757598690.8608212, blob_last_modified=1757104405.0417192), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466126/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/34292b90d99b5a5be542c7dd10b11336605330639d92e93b092e567a56fe7d93'), size_on_disk=2850164, blob_last_accessed=1757598691.086821, blob_last_modified=1757104410.0976934), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466143/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/54bd4af6ed8caf52a5810598d96c9b235266542586a692112cb247a209c2649f'), size_on_disk=10801408, blob_last_accessed=1757598691.2688208, blob_last_modified=1757104413.3726768), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466135/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/d44d381db0b8c0cb4960796e48d3d16e3d766aff87cbc686789a0c5799ba0a98'), size_on_disk=5204256, blob_last_accessed=1757598691.180821, blob_last_modified=1757104412.4376817), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466115/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/c08ce1194dd98cb028a6038944317e70ac1545e32b21dd323eafa57b16611e6e'), size_on_disk=3503431, blob_last_accessed=1757598690.9468212, blob_last_modified=1757104405.9427147), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466121/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/78d0f377f8a756b28f2f5296932368c9c6b764d2972dc93aa6dd354591d29860'), size_on_disk=7053621, blob_last_accessed=1757598691.0328212, blob_last_modified=1757104408.5097015), CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1757101035.6211069, blob_last_modified=1756944310.9395182), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466127/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/1565c5175e05c322f160529cb4bb74d5e8468458f150f228d6aadf2b670c3b4a'), size_on_disk=11670434, blob_last_accessed=1757598691.092821, blob_last_modified=1757104411.0776885), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466123/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/57b7ca5d419676590538573f295b99db05a952e20cb4c6b2a1618974611d5051'), size_on_disk=3672899, blob_last_accessed=1757598691.073821, blob_last_modified=1757104409.590696), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466134/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/0b23434181eb1fc864e858cfca82d2d8fed1524a8f7dbea3bf576f6f452cf08b'), size_on_disk=12398132, blob_last_accessed=1757598691.169821, blob_last_modified=1757104412.552681), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466133/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/b6d6913e14b52d5d51dab2f254815da89e03cebe75e80356b27cf18e31eb048f'), size_on_disk=5090722, blob_last_accessed=1757598691.169821, blob_last_modified=1757104412.180683), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466131/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/bdce0842b5568e406a799dbe36b4b9645a30fa58eb0a707f24ed3bb3900fbf67'), size_on_disk=11759994, blob_last_accessed=1757598691.157821, blob_last_modified=1757104411.1146884), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466132/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/cc41509e63473d565341ed36fd6881fecfd537e59c003f4779d4c4ec7b49cb54'), size_on_disk=1906184, blob_last_accessed=1757598691.161821, blob_last_modified=1757104413.4366765), CachedFileInfo(file_name='genome_map.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/51978dc2125afb40a41a29672f8ae750308d9a63'), size_on_disk=5311747, blob_last_accessed=1757598690.9468212, blob_last_modified=1757104406.477712), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466109/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/6588704c47423e671054df9cb2318c045138b0018f2eefff61ef91a25848eb58'), size_on_disk=5259413, blob_last_accessed=1757598690.8298213, blob_last_modified=1757104404.9987195), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466119/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/0157d9136c2c2bb5640866449adf4bab8c62b3d7f7fb4262981908c77986f89e'), size_on_disk=12516152, blob_last_accessed=1757598691.0068212, blob_last_modified=1757104412.3166823), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466120/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/4220a31e6a7be79341828d41da77ef77fb5a8026980d4445fec18c57b5229c1f'), size_on_disk=3644577, blob_last_accessed=1757598691.034821, blob_last_modified=1757104408.3467023)}), refs=frozenset(), last_modified=1757104417.837654), CachedRevisionInfo(commit_hash='6aac54b4b3fbb414561a8b6cc7acf87880c3c3c2', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6aac54b4b3fbb414561a8b6cc7acf87880c3c3c2'), size_on_disk=4665, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6aac54b4b3fbb414561a8b6cc7acf87880c3c3c2/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/14e1e02ad3804c0a4146a52509460e9cbb759d93'), size_on_disk=4665, blob_last_accessed=1758306843.4906857, blob_last_modified=1758156479.1516545)}), refs=frozenset(), last_modified=1758156479.1516545), CachedRevisionInfo(commit_hash='1e07e46fd0b5348243487ac4a573b570a2a3946b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1e07e46fd0b5348243487ac4a573b570a2a3946b'), size_on_disk=4683, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1e07e46fd0b5348243487ac4a573b570a2a3946b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/bb690d71edd44885609ed660d926f3cf2c4c473e'), size_on_disk=4683, blob_last_accessed=1758306349.7238934, blob_last_modified=1758156991.1235294)}), refs=frozenset({'main'}), last_modified=1758156991.1235294), CachedRevisionInfo(commit_hash='22ebbfcf62a7ed665fb8eceaea53b58b45a1709e', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/22ebbfcf62a7ed665fb8eceaea53b58b45a1709e'), size_on_disk=4602, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/22ebbfcf62a7ed665fb8eceaea53b58b45a1709e/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/e03b8d10ba59c549907a309ad8343133b7b902fc'), size_on_disk=4602, blob_last_accessed=1757613581.062093, blob_last_modified=1757613581.0610929)}), refs=frozenset(), last_modified=1757613581.0610929), CachedRevisionInfo(commit_hash='1acaa5629922414e8efe23a5d023bd44965824c8', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1acaa5629922414e8efe23a5d023bd44965824c8'), size_on_disk=4683, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1acaa5629922414e8efe23a5d023bd44965824c8/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/bb690d71edd44885609ed660d926f3cf2c4c473e'), size_on_disk=4683, blob_last_accessed=1758306349.7238934, blob_last_modified=1758156991.1235294)}), refs=frozenset(), last_modified=1758156991.1235294), CachedRevisionInfo(commit_hash='664d95cdd482d753bc139d26119061c2fa6eaa76', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/664d95cdd482d753bc139d26119061c2fa6eaa76'), size_on_disk=4679, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/664d95cdd482d753bc139d26119061c2fa6eaa76/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/ff6f75db42c0690f0e0285e56c1aa95bc5cf1300'), size_on_disk=4679, blob_last_accessed=1758157110.1705706, blob_last_modified=1758157110.1695707)}), refs=frozenset(), last_modified=1758157110.1695707), CachedRevisionInfo(commit_hash='3ce77eb2070d7bb43049ba26bb462fded0ff7863', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/3ce77eb2070d7bb43049ba26bb462fded0ff7863'), size_on_disk=15562566, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/3ce77eb2070d7bb43049ba26bb462fded0ff7863/genome_map/accession=SRR11466106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/04bacc0f8c03ee000ba3b69320522d0b1be909686474b6e8a126173a0ceae8c4'), size_on_disk=15547870, blob_last_accessed=1757598690.8528214, blob_last_modified=1756944357.1972551), CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/3ce77eb2070d7bb43049ba26bb462fded0ff7863/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1757101035.6211069, blob_last_modified=1756944310.9395182)}), refs=frozenset(), last_modified=1756944357.1972551)}), last_accessed=1758306843.4906857, last_modified=1758157110.1695707), CachedRepoInfo(repo_id='BrentLab/harbison_2004', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004'), size_on_disk=12305, nb_files=2, revisions=frozenset({CachedRevisionInfo(commit_hash='2916ad316cc0d8a1ce2e7f35d3707b831b203e87', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/2916ad316cc0d8a1ce2e7f35d3707b831b203e87'), size_on_disk=7160, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/2916ad316cc0d8a1ce2e7f35d3707b831b203e87/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/7e3ec66f5479346ca9d082545ff0b6a960fb60d5'), size_on_disk=7160, blob_last_accessed=1758155946.7689884, blob_last_modified=1758155946.7669883)}), refs=frozenset({'main'}), last_modified=1758155946.7669883), CachedRevisionInfo(commit_hash='073cdeb1b541c28b1a97df9dbc6be6af1d9c23b6', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/073cdeb1b541c28b1a97df9dbc6be6af1d9c23b6'), size_on_disk=5145, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/073cdeb1b541c28b1a97df9dbc6be6af1d9c23b6/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/f7a6cf3dd4477f08e508f38665350e8d11589963'), size_on_disk=5145, blob_last_accessed=1755876214.0380862, blob_last_modified=1755876188.1642818)}), refs=frozenset(), last_modified=1755876188.1642818)}), last_accessed=1758155946.7689884, last_modified=1758155946.7669883), CachedRepoInfo(repo_id='BrentLab/hughes_2006', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006'), size_on_disk=11943331, nb_files=8, revisions=frozenset({CachedRevisionInfo(commit_hash='cc20720c4fe7de9151a051aafbd41e6de2b4cd84', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84'), size_on_disk=11937630, files=frozenset({CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756856028.69735, blob_last_modified=1756856028.7793496), CachedFileInfo(file_name='overexpression.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/overexpression.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/9b8cc1feede780efe0e8537555ed1cbbf067e7fa9aa7a7edc74e5a343c64a443'), size_on_disk=6337781, blob_last_accessed=1756856028.536351, blob_last_modified=1756856029.230347), CachedFileInfo(file_name='metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/e27a67137876123c68c229705cfbb8e06ed3ee76a3a868b66d80d624f8d03671'), size_on_disk=7942, blob_last_accessed=1756856028.533351, blob_last_modified=1756856028.9083488), CachedFileInfo(file_name='checksum.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/checksum.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/973b686313e32c96b6e0ef1dbc84020857c073ef'), size_on_disk=159, blob_last_accessed=1756856028.5883508, blob_last_modified=1756856028.6513503), CachedFileInfo(file_name='parse_hughes_2006.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/scripts/parse_hughes_2006.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/0c2645bdc0ab7f6be38a428a0a92752e9d3f88d5'), size_on_disk=9255, blob_last_accessed=1756856028.5803509, blob_last_modified=1756856028.6393504), CachedFileInfo(file_name='knockout.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/knockout.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/5e000e2e9401011c9ecc81655c94d55b7f9469ed856216fa21cef2883a904c93'), size_on_disk=5571518, blob_last_accessed=1756856029.2493467, blob_last_modified=1756856029.2143471), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/842b551fb3d1ba1e6f1daa35157a0c7c616f7655'), size_on_disk=8514, blob_last_accessed=1756855276.4871445, blob_last_modified=1756855276.4861445)}), refs=frozenset({'main'}), last_modified=1756856029.230347), CachedRevisionInfo(commit_hash='51ae7addf429c955a97934a15b242ff8a2ccd653', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/51ae7addf429c955a97934a15b242ff8a2ccd653'), size_on_disk=5701, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/51ae7addf429c955a97934a15b242ff8a2ccd653/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/f799807a7bf81229b0d063e6c939339c1f5c90f4'), size_on_disk=5701, blob_last_accessed=1756855057.8437808, blob_last_modified=1756855057.8417807)}), refs=frozenset(), last_modified=1756855057.8417807)}), last_accessed=1756856029.2493467, last_modified=1756856029.230347), CachedRepoInfo(repo_id='BrentLab/hackett_2020', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020'), size_on_disk=407271569, nb_files=17, revisions=frozenset({CachedRevisionInfo(commit_hash='60b3ecf6a06e709f0397b327ece5c31016303b5c', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/60b3ecf6a06e709f0397b327ece5c31016303b5c'), size_on_disk=9543, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/60b3ecf6a06e709f0397b327ece5c31016303b5c/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/6f54100c2bb84a6bc059c17e0954dad0ea451666'), size_on_disk=9543, blob_last_accessed=1758400469.288775, blob_last_modified=1758155372.1328666)}), refs=frozenset({'main'}), last_modified=1758155372.1328666), CachedRevisionInfo(commit_hash='b9d39535e175295d9cba23489a49fafebbac5ca0', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0'), size_on_disk=404565563, files=frozenset({CachedFileInfo(file_name='hackett_2020.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/hackett_2020.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4f47951c65c799b5c74ee3782b49c16cbb038d50'), size_on_disk=55, blob_last_accessed=1757105553.3228161, blob_last_modified=1756843395.5719483), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756843395.5059488, blob_last_modified=1756843395.5679483), CachedFileInfo(file_name='parse_mcisaac_data.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/scripts/parse_mcisaac_data.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0'), size_on_disk=4691, blob_last_accessed=1756843395.7579472, blob_last_modified=1756843395.820947), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/03e579e00c632cdb393c0ffafd9d15b069390e5c'), size_on_disk=9066, blob_last_accessed=1757439136.0598524, blob_last_modified=1757105945.6476595), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1757541699.4460278, blob_last_modified=1756843405.225893)}), refs=frozenset(), last_modified=1757105945.6476595), CachedRevisionInfo(commit_hash='2c196866d4f057cb5cf0adf1fe35f2c712c61025', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025'), size_on_disk=404565376, files=frozenset({CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756843395.5059488, blob_last_modified=1756843395.5679483), CachedFileInfo(file_name='hackett_2020.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/hackett_2020.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4f47951c65c799b5c74ee3782b49c16cbb038d50'), size_on_disk=55, blob_last_accessed=1757105553.3228161, blob_last_modified=1756843395.5719483), CachedFileInfo(file_name='parse_mcisaac_data.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/scripts/parse_mcisaac_data.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0'), size_on_disk=4691, blob_last_accessed=1756843395.7579472, blob_last_modified=1756843395.820947), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1757541699.4460278, blob_last_modified=1756843405.225893), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c3e72ccb1b8deba4bbfd18abe6081de7ec3914d9'), size_on_disk=8879, blob_last_accessed=1757105552.290822, blob_last_modified=1757104206.2667632)}), refs=frozenset(), last_modified=1757104206.2667632), CachedRevisionInfo(commit_hash='1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43'), size_on_disk=2678425, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=20/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/97adc0bb2b436c325555d82bb7f0ffb0e250d73092fcbf03ac0c00fa2b762e51'), size_on_disk=351379, blob_last_accessed=1756145253.7895415, blob_last_modified=1756145254.6265287), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=10/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4a2eb1051cad96c5b39a014f19b092f042b03700cc1b1a40c2de9e7edd73edf9'), size_on_disk=352201, blob_last_accessed=1756145253.7915416, blob_last_modified=1756145254.6375287), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=0/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/409962e1914ac9e9d631e4ae73e04b167822d0915d8d8d4a87703d4c7281ffb9'), size_on_disk=209695, blob_last_accessed=1756145254.7305272, blob_last_modified=1756145254.608529), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=15/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/51dfb68ed2518f0dd8ab4d4070d4896b6eca912ecb7e4aa773d857e3096020b5'), size_on_disk=351273, blob_last_accessed=1756145217.972108, blob_last_modified=1756142799.7054222), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=90/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/ebd06af83cc386d9094ce6b8d795b155c76c47c96da1a458743348264f1638ae'), size_on_disk=349917, blob_last_accessed=1756145253.7845416, blob_last_modified=1756145254.7235274), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/45cb0687c3ea72e7b32ad17dd799bc38ad02f80b'), size_on_disk=16034, blob_last_accessed=1756138543.6766906, blob_last_modified=1756138543.6746907), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=5/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/ed331d9d2b27a0958b12368c7b5d2d3383b111d3d1c39ed0e77b78561dc78b6a'), size_on_disk=348626, blob_last_accessed=1756145253.7845416, blob_last_modified=1756145254.6395288), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=45/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/db8595cd20dd7440c890c46025cf0643da8ec6be3e15bc18a8759b3fecdc02af'), size_on_disk=349256, blob_last_accessed=1756145253.7795417, blob_last_modified=1756145254.6225288), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=30/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/a85bd6b418d9644d9adaa1269c27f97469a4aaee51af63cf1aa041f62cd8ba2c'), size_on_disk=350044, blob_last_accessed=1756145253.7915416, blob_last_modified=1756145254.6385286)}), refs=frozenset(), last_modified=1756145254.7235274), CachedRevisionInfo(commit_hash='7192b0e2275ba5aa3ffb3752b87f1c3595f2331a', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a'), size_on_disk=404565656, files=frozenset({CachedFileInfo(file_name='hackett_2020.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/hackett_2020.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4f47951c65c799b5c74ee3782b49c16cbb038d50'), size_on_disk=55, blob_last_accessed=1757105553.3228161, blob_last_modified=1756843395.5719483), CachedFileInfo(file_name='parse_mcisaac_data.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/scripts/parse_mcisaac_data.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0'), size_on_disk=4691, blob_last_accessed=1756843395.7579472, blob_last_modified=1756843395.820947), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756843395.5059488, blob_last_modified=1756843395.5679483), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/f3899cd97da03a3543db10d76c15ee0442bb136e'), size_on_disk=9159, blob_last_accessed=1758145252.5929751, blob_last_modified=1757541416.40237), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1757541699.4460278, blob_last_modified=1756843405.225893)}), refs=frozenset(), last_modified=1757541416.40237)}), last_accessed=1758400469.288775, last_modified=1758155372.1328666), CachedRepoInfo(repo_id='BrentLab/hu_2007_reimand_2010', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010'), size_on_disk=42682732, nb_files=4, revisions=frozenset({CachedRevisionInfo(commit_hash='31ccd35daf785420014a1346a7db064dd306d4f8', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8'), size_on_disk=42682732, files=frozenset({CachedFileInfo(file_name='hu_2007_reimand_2010.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/hu_2007_reimand_2010.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/eb9be5a1f01907a81eb2ab4fbbee36ff8c50186f'), size_on_disk=63, blob_last_accessed=1756844741.7997973, blob_last_modified=1756844741.8557973), CachedFileInfo(file_name='hu_2007_reimand_2010.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/hu_2007_reimand_2010.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/47a15f3e23f8cb50af3217d00c6439d29a7422b5190116e4453a5f78cb22540d'), size_on_disk=42677516, blob_last_accessed=1756844742.1787965, blob_last_modified=1756844742.1477966), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756844741.8097973, blob_last_modified=1756844741.8767972), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/886f4614dfd1ef33709d462908ae18608c13e8ba'), size_on_disk=2692, blob_last_accessed=1756844742.0937967, blob_last_modified=1756844742.1537964)}), refs=frozenset({'main'}), last_modified=1756844742.1537964)}), last_accessed=1756844742.1787965, last_modified=1756844742.1537964), CachedRepoInfo(repo_id='BrentLab/kemmeren_2014', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014'), size_on_disk=319226397, nb_files=3, revisions=frozenset({CachedRevisionInfo(commit_hash='a6c9a954a1def1774b68582f117ad1abc5864a07', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07'), size_on_disk=319226397, files=frozenset({CachedFileInfo(file_name='kemmeren_2014.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07/kemmeren_2014.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/3c463017444bd7690959c0d6fff0d0ebe2faa7b916dd9c44e305156f6c98b863'), size_on_disk=319218929, blob_last_accessed=1756832531.471449, blob_last_modified=1756832530.9644527), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756832529.5094638, blob_last_modified=1756832529.5434635), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/a29a64128c10ef9691ee773131972c61f720ba07'), size_on_disk=5007, blob_last_accessed=1756832529.5094638, blob_last_modified=1756832529.5404634)}), refs=frozenset({'main'}), last_modified=1756832530.9644527)}), last_accessed=1756832531.471449, last_modified=1756832530.9644527)}), warnings=[CorruptedCacheException(\"Snapshots dir doesn't exist in cached repo: /home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots\")])" + "HFCacheInfo(size_on_disk=5198144267, repos=frozenset({CachedRepoInfo(repo_id='BrentLab/barkai_compendium', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium'), size_on_disk=3579068970, nb_files=504, revisions=frozenset({CachedRevisionInfo(commit_hash='a987ef37c72fcd07b18828d320bc4305480daade', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade'), size_on_disk=3579068970, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417639/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/43c5fb0a4f049fa5895f8167928d047cc4954aa37db1fc8a9892fe9f99c401bf'), size_on_disk=782319, blob_last_accessed=1756926308.7694294, blob_last_modified=1756926309.6284256), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417694/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/058b507f75cb01bb5912e14e7bbb4e25a1de945e578323bce9f920f31a9ced82'), size_on_disk=11980395, blob_last_accessed=1756926320.09838, blob_last_modified=1756926321.5623736), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417643/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/66dfa3feea4410ba34f5d5d37c7b01e1fa2cdd4ff38f9dfb10d20d55e40e449f'), size_on_disk=1012185, blob_last_accessed=1756926309.326427, blob_last_modified=1756926310.0784237), CachedFileInfo(file_name='genome_map.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b8f0ed936cb43c4649126e9b7596cef0ab626a2d'), size_on_disk=142786, blob_last_accessed=1756926300.410466, blob_last_modified=1756926300.4954655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417701/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a0dd9587c7525a610a3794cf41f6db24a6ba22a12ed852dc27381a68db3e8fca'), size_on_disk=8781514, blob_last_accessed=1756926321.6273735, blob_last_modified=1756926323.1263669), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417706/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4f1ea5592a855f3bf5739b0caae1c69c7606276d125c3dfe8e98379d12bedf1b'), size_on_disk=1154142, blob_last_accessed=1756926322.9053679, blob_last_modified=1756926323.5173652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381054/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1a9f9c650973937d852d2de54c0d73336d1516184d29e203563fd9bf8d7fefa5'), size_on_disk=3346485, blob_last_accessed=1756926778.1077476, blob_last_modified=1756926778.8637433), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417619/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0a4d4159941caa34a5e079821d49047d1434077926b2933c27d384e04ed2e5b5'), size_on_disk=9949802, blob_last_accessed=1756926302.5744565, blob_last_modified=1756926304.05045), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380996/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/09e663ce2a60df5635f4e4ea77d6350e7752a9bb5e81fbdfd818869ac53ca805'), size_on_disk=13309208, blob_last_accessed=1756926375.0511415, blob_last_modified=1756926376.0221374), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380977/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/74237b16fd38090785d203d96bae72e1f5ce919d423f793cfb9d3b86623358e6'), size_on_disk=11114601, blob_last_accessed=1756926371.6651561, blob_last_modified=1756926373.12615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417906/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/34d3381dd45e579dcc298e91cc95174c4219cf548401b8f2392cf746fd508c5b'), size_on_disk=13795318, blob_last_accessed=1756926356.3642225, blob_last_modified=1756926357.640217), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417920/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/00a869703f03e3df3f0f51a7d93d52dd7eac5174ed2a1b752207460e71c7b41a'), size_on_disk=14880236, blob_last_accessed=1756926359.1242106, blob_last_modified=1756926360.409205), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381060/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1bb05aa71fe55db65fc3868b131854c1e6666dca8cc370874f517aa0756d21ac'), size_on_disk=9137992, blob_last_accessed=1756926778.9637427, blob_last_modified=1756926780.2307358), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417890/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3eca80a3220fd0294fdfca83740ba70a20af764d28833494948705c4d542ded3'), size_on_disk=9018889, blob_last_accessed=1756926353.7652338, blob_last_modified=1756926354.7252295), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380943/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/987771cb978dbc949eff5044071149a00223c6771efcc870850e3c8cbe0648c3'), size_on_disk=13397144, blob_last_accessed=1756926367.295175, blob_last_modified=1756926368.7601688), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417662/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/90008c75533e5a395bf92b2a4382cfb8d6c27af4921615cac22bc1965375efb5'), size_on_disk=12701661, blob_last_accessed=1756926313.0204108, blob_last_modified=1756926314.158406), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417752/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/efc9f1ecf4a7b06b23da46fb8c7d1879b67a9d6603ed753d09e2028396ec05c5'), size_on_disk=3420764, blob_last_accessed=1756926329.26534, blob_last_modified=1756926330.0613368), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417848/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eec4ffff5d145c0ee0850d5e68787ab90e6510cffa5803579db44e3a575822dc'), size_on_disk=13556826, blob_last_accessed=1756926346.1882668, blob_last_modified=1756926347.2852619), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380950/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f508a5da82832b920577592b853da2c2d76ebf0adca587e10b63507f46dc5066'), size_on_disk=3452513, blob_last_accessed=1756926368.7781687, blob_last_modified=1756926369.7081647), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417940/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/270559ca1db4605de241e8642461b4d7304d124e4fc624f7d8a8a59867301a4c'), size_on_disk=2328150, blob_last_accessed=1756926363.182193, blob_last_modified=1756926363.9021897), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417730/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f2554716083cfa1bb54c2098315b92e1b3537eb265da884c00ba7e7d4c050da'), size_on_disk=14980864, blob_last_accessed=1756926325.5143564, blob_last_modified=1756926326.803351), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417632/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/23c561fde2e6c2973ec1913373c343a5c49d6cc0dbb44ff21eb5aff87cd77d3a'), size_on_disk=18481828, blob_last_accessed=1756926306.2764404, blob_last_modified=1756926308.5744302), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417736/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/671a67d7275810840cafc9db32e43385c4ba61b007d702e7eb5cb244a0e4afa9'), size_on_disk=13855124, blob_last_accessed=1756926326.1693537, blob_last_modified=1756926327.261349), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417684/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eee25a9a91c54d0404db884532d1b85555f2e1de0c664abcbda4343f9d729842'), size_on_disk=10310520, blob_last_accessed=1756926317.7173905, blob_last_modified=1756926319.3473833), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417761/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ba86b2041414034d38b95ccbf3c65957642c56b56914240999986ebc76b9e193'), size_on_disk=1635118, blob_last_accessed=1756926330.478335, blob_last_modified=1756926331.0933323), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417813/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f7d5239d0d39e171aad1e84964f8cdc3c7a325fe8c38b6829e114cb3997e658'), size_on_disk=10238322, blob_last_accessed=1756926339.4142962, blob_last_modified=1756926340.8722897), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417823/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1887333f71d11fe7c43748662f58d9ba3fb12f3f6ca30a61d7e1c7a0c95f7064'), size_on_disk=9444476, blob_last_accessed=1756926341.6372864, blob_last_modified=1756926342.8442812), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417791/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8d2eb1bdd1a5cae023abb271e5858cf111879953cb0d6267f4ebf21a3d30c4ad'), size_on_disk=5232904, blob_last_accessed=1756926335.2033143, blob_last_modified=1756926336.417309), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417915/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9fbfc50c3e1f756169d25442880ba33e5cf237e3c83a0d674b64ace47e8d3322'), size_on_disk=13801295, blob_last_accessed=1756926358.318214, blob_last_modified=1756926359.4592092), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417614/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dac699f09872b0c58b742c692b341f0b00b7d83284cec5ef838c276a6f48908c'), size_on_disk=1759475, blob_last_accessed=1756926301.7274601, blob_last_modified=1756926302.4774568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417648/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fb706eb700e8fc003822b897accd3501360d737fd1a9ffcf26c3e9d9b25a5e35'), size_on_disk=1285054, blob_last_accessed=1756926310.3104227, blob_last_modified=1756926311.0174196), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381013/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c831ca7cc9cfd2cc2ccd92a0168b2e14dedf2bde9e29780dfaf6d281b31e9462'), size_on_disk=4648281, blob_last_accessed=1756926377.4421313, blob_last_modified=1756926378.3451273), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381040/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a7c622a8ae1f5083eef26ecbcc2444055a9265b674ae80002e5468c94c0eb070'), size_on_disk=9682671, blob_last_accessed=1756926774.782766, blob_last_modified=1756926776.7967548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417797/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/12ee278356fa75b0ad555fe06376c78b71630324b7b89aa28abe960c187d256f'), size_on_disk=14391201, blob_last_accessed=1756926336.3743093, blob_last_modified=1756926338.322301), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417665/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7c9b3d448e53fcac9334df092cc003f438cbc462e6785ac3d61202bd65a7125f'), size_on_disk=7481605, blob_last_accessed=1756926313.490409, blob_last_modified=1756926314.7744033), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417914/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d062dbf660e1ef240ff8ef99ff2d486d9caaabda14575e6365ce6f725fa91ccf'), size_on_disk=8599250, blob_last_accessed=1756926358.2422144, blob_last_modified=1756926360.3412054), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381018/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/878bf028aca9daeebe7ed0f8ee522580d512771ec4a5e35bfc62ac675176ad15'), size_on_disk=701951, blob_last_accessed=1756926378.2281277, blob_last_modified=1756926378.643126), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8168980e59c50149bfb9866a1e8855c235f2882b988df99331c9247f65e9813e'), size_on_disk=2289686, blob_last_accessed=1756926376.4721353, blob_last_modified=1756926377.2151322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417725/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4195b9ed6506437a735fd8061cd1b79211c99169c4319c4f72b801a501f2b0f9'), size_on_disk=2233032, blob_last_accessed=1756926324.958359, blob_last_modified=1756926325.594356), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417863/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4aabb9d1a4b105c7669deb0f13cd635ab7433ed1ae02f5785aad014d604f1504'), size_on_disk=6900487, blob_last_accessed=1756926348.4532568, blob_last_modified=1756926349.2742534), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417617/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9ad746ec09ed51ed497d87f3dd64b11916b70e6833ce66141bc8feb6ba73d1d3'), size_on_disk=23082279, blob_last_accessed=1756926302.1754582, blob_last_modified=1756926303.6944516), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417943/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0e4191a403f5070636bcb281bd80d39e11c70fb502fa2b18150032d58f2e0740'), size_on_disk=5396199, blob_last_accessed=1756926363.7271905, blob_last_modified=1756926364.542187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417790/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f88bed27521de8285bbb92cf95242820116e6fa2c23ab578e6608a6a25e0c04e'), size_on_disk=10836858, blob_last_accessed=1756926335.078315, blob_last_modified=1756926336.1843102), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417794/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f15be566c029c52cf46f6258e589529afb3a002f010710a54d04dd6151f8b684'), size_on_disk=10205023, blob_last_accessed=1756926335.9063113, blob_last_modified=1756926337.151306), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380952/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3811d6e6194d8b88c84fc0931cba844da23677e76e1e9a25e2890df968a7e529'), size_on_disk=1003172, blob_last_accessed=1756926368.8351684, blob_last_modified=1756926369.4641657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417847/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4dd39704bc9c1d60a8271f16206887171ee92abde023c53501c1560533a688c1'), size_on_disk=4755120, blob_last_accessed=1756926346.125267, blob_last_modified=1756926346.8372638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381023/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/22e3e0f4cd1f2c755ec1ac13b99d2d6c98d0158319478f2deca4e89aefc5e88e'), size_on_disk=3860195, blob_last_accessed=1756926378.5651264, blob_last_modified=1756926379.6101217), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417827/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8bfd1221f930e86ffd6150c82aae85f00a23fff33c672e40f47d671f5a2e4d17'), size_on_disk=7291599, blob_last_accessed=1756926342.156284, blob_last_modified=1756926343.1502798), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417886/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62fe87e7519370c4ac46e6d9d0abf314a8f92d9a5d6804ebddae0ed095641219'), size_on_disk=7968977, blob_last_accessed=1756926352.9472373, blob_last_modified=1756926353.7992337), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380934/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d264f2a91c87696f6a07f573f1d921c91e1d53e3afca59610ba4703b3683b617'), size_on_disk=4601537, blob_last_accessed=1756926366.1091802, blob_last_modified=1756926366.9191768), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417609/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e3d8451242fe9ecf9f82e2ac1a406144840a6182178228e6e1fa67e6a04a318a'), size_on_disk=3176651, blob_last_accessed=1756926300.651465, blob_last_modified=1756926302.0864584), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417849/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/23b6606ffcf116cf066b103bf1b6b4fb33c40e544e20e4565fa44c5bbcea4bdd'), size_on_disk=5689527, blob_last_accessed=1756926346.584265, blob_last_modified=1756926347.3892615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417808/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0251ae27bd961d6c962253c555963b42148092cdaa2616a78f6872cf1a4e5e4d'), size_on_disk=12900784, blob_last_accessed=1756926338.3973005, blob_last_modified=1756926339.439296), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417628/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/291f0e4d2d0fa1656de1583bd9e131fd4d2fc1934d0bb70a6774f7e7f90e62ae'), size_on_disk=15444530, blob_last_accessed=1756926305.1314452, blob_last_modified=1756926307.1334364), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417671/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bced5a56092fea7de5754bebc33caaab643f27236f8d9020e7bc6b30b9045d6e'), size_on_disk=5347180, blob_last_accessed=1756926314.7704034, blob_last_modified=1756926315.8293986), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381062/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/76151e17f242c5108010debea961b0b13f3821be2fcb77fd923a36cc0e672695'), size_on_disk=4880117, blob_last_accessed=1756926779.3357406, blob_last_modified=1756926780.5167341), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417843/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6569e23f0eb660e9f7111b9cdd69e7ae797ba265647ee44e4cbf2d8babdeca9e'), size_on_disk=2706587, blob_last_accessed=1756926345.3402703, blob_last_modified=1756926346.125267), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c103eef2a9834296fb1328b7012b5b9184c5a3f131817dfca257843e863d34b2'), size_on_disk=9829417, blob_last_accessed=1756926362.1531975, blob_last_modified=1756926363.3531923), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bbfeb8571ee8915d793698c2116cef9f2ea61573c66b4ae7066078271c6172d6'), size_on_disk=5132301, blob_last_accessed=1756926362.6441953, blob_last_modified=1756926363.7271905), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417756/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ede027f2c13a1e6669becf6194ace85af5800d5fe77db16ec47bfb3abaa735d3'), size_on_disk=8620421, blob_last_accessed=1756926329.968337, blob_last_modified=1756926331.0303326), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417700/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1240fbb2497074bb7ff98b1bc6399b8a8ad5d320246285abfb6738e27e2bbdeb'), size_on_disk=19312447, blob_last_accessed=1756926321.6763732, blob_last_modified=1756926322.8383682), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380951/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b9b68679dd2da94e4aa4852ae3de3ee5b823c1fb1483f4ece3eee5d947014785'), size_on_disk=1154119, blob_last_accessed=1756926368.8281684, blob_last_modified=1756926369.410166), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417687/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fa4f79173f5a5ed278c7ef80d9fb220aa9c615de97bae7963969e27ecf0e587c'), size_on_disk=13352339, blob_last_accessed=1756926318.3003879, blob_last_modified=1756926320.3913789), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3fe2828766efd936f02efae2da3e068ef9b28ea374e594e09850e3e8ff0d8164'), size_on_disk=4421540, blob_last_accessed=1756926375.7961383, blob_last_modified=1756926376.7581341), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417642/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/591f688d0d9f98350a1d636e6f93ed37563f3ea9765ca6b633490533b0cf32d4'), size_on_disk=11498542, blob_last_accessed=1756926309.2804272, blob_last_modified=1756926310.8714201), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417817/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ccf81aa26021a009a212ef8f8bee71c12f5c8d0a2b97ffdb2c4d86d13c8b8133'), size_on_disk=7521086, blob_last_accessed=1756926340.0802932, blob_last_modified=1756926341.3222878), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381047/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dda6f6ca669a0e5c9d34b548305cbeb6b82f1b70086e06d3604e7e096fd7fb25'), size_on_disk=6799004, blob_last_accessed=1756926776.8777544, blob_last_modified=1756926778.4717455), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417689/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e27d1d153e5b135a09ec5db80bb05ace721fd60ac90f0693ac34ca3160fdfbdd'), size_on_disk=5015020, blob_last_accessed=1756926319.2973835, blob_last_modified=1756926321.220375), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417909/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c5de46a74c16aee32dfb2be89a50dceaa5704c0c550076dcec483d1c21d983af'), size_on_disk=5969725, blob_last_accessed=1756926357.3362184, blob_last_modified=1756926358.5162132), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417634/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/529d984782684991b2eb7be2d345b5a4e8a6e08d3dbe30ad34495d8380464e6e'), size_on_disk=16306189, blob_last_accessed=1756926306.810438, blob_last_modified=1756926310.239423), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417668/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b426513aab4e071a24b7d27100a4da4da8c71c8e9b3e82163808b0ec6a42b8d0'), size_on_disk=6011682, blob_last_accessed=1756926314.5374043, blob_last_modified=1756926315.4354005), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417947/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/50bb9cc74904c9d3b6cc3ec8bc12c7ad1fb89204be6f89a863e703a94d224512'), size_on_disk=4023877, blob_last_accessed=1756926364.0161893, blob_last_modified=1756926364.9561853), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417895/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2daca9b311da1afa40f578c6df7dc4b0039dd4857663c98cd87c665a547b7144'), size_on_disk=11915424, blob_last_accessed=1756926354.7232296, blob_last_modified=1756926355.8262248), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380933/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/07c22e11c56ce5ecb6c22c910c3d3a0d9b54fd341bef0513b4bf42a9c53ec6a0'), size_on_disk=10138164, blob_last_accessed=1756926365.8761814, blob_last_modified=1756926366.7471776), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417789/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6bd6339b995a5730b6b65ec65f680b57195469ebee9f2e15de6e604b069d4b0e'), size_on_disk=9612750, blob_last_accessed=1756926334.6493168, blob_last_modified=1756926335.8183117), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4c942b91ac018330aeb4da512a3e47ae4531a434b6e9933cf3e2bb476a77f9ca'), size_on_disk=6312758, blob_last_accessed=1756926366.1041803, blob_last_modified=1756926367.0141764), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381043/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f6ded8e6496dcfdd499edc382d19da2071f25daebbb250b375b9a2706e9d8211'), size_on_disk=9139336, blob_last_accessed=1756926775.6057615, blob_last_modified=1756926777.1057532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417683/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6131abcd72bd4c6a8627685c4df8a295af50e824f52a5cde156adae7cd694bba'), size_on_disk=15545733, blob_last_accessed=1756926317.382392, blob_last_modified=1756926319.0483847), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417766/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/39da4f16b82cc0b5019d8526f2e3eed49de9760e319f8e839db621bf4c1ab021'), size_on_disk=6482957, blob_last_accessed=1756926331.2123318, blob_last_modified=1756926332.1133277), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417613/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/695d3095619f3a313d25c506b9d0493f171896f1439bcf3bd1cd932d1f6fed5d'), size_on_disk=2812565, blob_last_accessed=1756926301.6434605, blob_last_modified=1756926302.4814568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380936/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5a931bc1181001314c3822051c493ba7a8b87b5bb26b8866783612f2bcf49c62'), size_on_disk=3467608, blob_last_accessed=1756926366.19118, blob_last_modified=1756926367.2261755), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381015/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/345c83c5ab13d230542683014d3d6073ba5a348e3063fea325843981c6c6e7c1'), size_on_disk=3807314, blob_last_accessed=1756926377.67913, blob_last_modified=1756926378.4971266), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381036/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ff76dabe83d2948240dd846fcc2a969380b7bd9f8d2eff2c71c299f0b14134cd'), size_on_disk=2593393, blob_last_accessed=1756926379.8661208, blob_last_modified=1756926380.5611176), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417675/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/79072181ebce12769f2f0a1971977d6691dde1841b53d7b54cf9d4f47f53b3dc'), size_on_disk=6478433, blob_last_accessed=1756926315.763399, blob_last_modified=1756926317.2643924), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417806/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2befcad73b2aa8170ceaf8c249ceba45e87037771e86c9bb4bd7addc5815bbec'), size_on_disk=5276603, blob_last_accessed=1756926337.9863024, blob_last_modified=1756926338.8112986), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380979/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef743a2a6a8fe44b62963b981ef0429676e7854243ab07d450d9c3ab569c7e8e'), size_on_disk=3735979, blob_last_accessed=1756926372.2201538, blob_last_modified=1756926373.6791475), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417656/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/03f8707a323ef7563c8ec2d6352d24c6454d043c4bbbca417680df5c7db208f8'), size_on_disk=10029839, blob_last_accessed=1756926311.588417, blob_last_modified=1756926312.8104117), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380928/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/251ffcb8e06f2ac0db0791065248327a9d1eeef33a07f5c6b25946e04b6c46d8'), size_on_disk=2289693, blob_last_accessed=1756926365.037185, blob_last_modified=1756926366.1961799), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417674/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5970db11c3efe2bba4eb50d07480cb0b46ed74dfc3ddf12d611ecd61f1d5fb68'), size_on_disk=4619941, blob_last_accessed=1756926315.5114, blob_last_modified=1756926317.630391), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417667/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef18f0ec136ef4b0facd217b32604556a7c9f514d75e26d2be17eb8da9b74a14'), size_on_disk=7979245, blob_last_accessed=1756926314.2284057, blob_last_modified=1756926315.734399), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fb5958ec4e10f5c7e19ebc55b24f26012e6d8ccf'), size_on_disk=8004, blob_last_accessed=1756926300.2594666, blob_last_modified=1756926300.3214662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417931/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7e70415ed0dfb1eb42e46789c905ccf2b593dc192d907d939d87acd59cd963e1'), size_on_disk=4991063, blob_last_accessed=1756926361.306201, blob_last_modified=1756926363.1161933), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417825/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/efc4971b63b21d6aee96e17d9e9b2cc2e3fd34608cc3722e36032c594fc03e48'), size_on_disk=8550531, blob_last_accessed=1756926341.8732853, blob_last_modified=1756926345.0782714), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417907/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b24926bd36cf33ea9107043b9a47db8239ad6208745c56002513225e8cb80a6e'), size_on_disk=7594380, blob_last_accessed=1756926356.7332208, blob_last_modified=1756926357.882216), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381068/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/16e4412527449a3cb9d2da129c3309a71b2ec9b33a7a39cb9bb31d41ce5d2e04'), size_on_disk=14909067, blob_last_accessed=1756926780.5687337, blob_last_modified=1756926782.3187242), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417753/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a8969c75211310735ad33d5af57327f2babc86276adc7cefcda254cc9fc9baa5'), size_on_disk=6433095, blob_last_accessed=1756926329.577339, blob_last_modified=1756926330.4023352), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417750/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aedd94fe4b42547108a807242052397b6dfbfda0eb4e56a9f2e267b312251147'), size_on_disk=1531584, blob_last_accessed=1756926329.1973405, blob_last_modified=1756926329.9403372), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381020/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c85f953439e9f6918c7f7c8194bcbbebe0ef8a96070119813025a42bde8c08fe'), size_on_disk=897644, blob_last_accessed=1756926378.3451273, blob_last_modified=1756926378.7061257), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381045/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdc8f9e6447280a7fc713c2a8f2157eb9076a5c01a7eb8437176e9ee30d6d4ab'), size_on_disk=4939421, blob_last_accessed=1756926776.3437574, blob_last_modified=1756926777.2507522), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417844/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ae034bb207abc0c649fb60aae78e798d46647f53b4e0352c6510fb7c8339a234'), size_on_disk=5408445, blob_last_accessed=1756926345.5212696, blob_last_modified=1756926346.4342656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417830/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/21e2dbb49f07cb7c2d7277f5bf1a442e216a2991677c3c4ece5c51316795ea77'), size_on_disk=12760016, blob_last_accessed=1756926342.9432807, blob_last_modified=1756926344.1602755), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417928/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/97e74e01fd641bfef68b6206a3c62ab41b5c28d8ea8369ce4198df1b3387b266'), size_on_disk=2449472, blob_last_accessed=1756926360.9072027, blob_last_modified=1756926361.3542008), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417760/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0500e5835116a61477f2d98554d0dd97e8d932a6b564f709c40c4884f2b49052'), size_on_disk=2234621, blob_last_accessed=1756926330.2873356, blob_last_modified=1756926330.8273335), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417708/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b58038866c334daaf757a086f99c3a5c5962b8563769ee0a3205bcdcbb0144ec'), size_on_disk=250863, blob_last_accessed=1756926323.0003674, blob_last_modified=1756926323.366366), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417884/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ff7d3efa4a231f5130be299d8d0ff67fe4973856e4db18e8a6c53d47ba6b28a7'), size_on_disk=11684836, blob_last_accessed=1756926352.791238, blob_last_modified=1756926354.0932324), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417918/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9f1cf3832969364b6cee1c817d3583a554b765bdbc1ff3f5b29692b7ea98ee7e'), size_on_disk=6936136, blob_last_accessed=1756926358.5802128, blob_last_modified=1756926360.9132028), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417901/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/69432386b347f5b364c02d270b38891358fa2068465354d1ada84d5450ea5bf0'), size_on_disk=9484337, blob_last_accessed=1756926355.6292257, blob_last_modified=1756926357.0582194), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381070/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/32c8f4c4efdbe6e137add61b0e31c27ae6a9eb5e8b463e9e4410a27cc51a57d3'), size_on_disk=29864184, blob_last_accessed=1756926780.7487328, blob_last_modified=1756926783.3167186), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417670/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5e36b63ef1fbb55f821ed2c3d61f0d0bf86cf00846e86ff9dbe6f4d9597f9dc8'), size_on_disk=10881106, blob_last_accessed=1756926314.7704034, blob_last_modified=1756926315.980398), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380973/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/343eb57057b5af866386fb4c02d0c41b888e206f5473ed7b4041200cfb93dbd5'), size_on_disk=1511721, blob_last_accessed=1756926371.3611574, blob_last_modified=1756926372.4931526), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381017/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1d9b2dbb5079eca871182c2dce1d681b943f1362072c18e255a4bd6d1068b840'), size_on_disk=2436549, blob_last_accessed=1756926378.161128, blob_last_modified=1756926378.7931254), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417660/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c394d7b0cfca3f3782dca86380e1a55903111d32efde98173af8a0b726ef5e7d'), size_on_disk=6441490, blob_last_accessed=1756926312.4744132, blob_last_modified=1756926313.4024093), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417840/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7e32bef3c0f7f7520382325aba12f5cf9405c22ebce8554b1d877fe24d2da52b'), size_on_disk=9679209, blob_last_accessed=1756926345.0092719, blob_last_modified=1756926346.0542672), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417882/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ac2bd9710196abc4469d4d78ccbde6ddc67e9ef39ade7d50c32b0d951866e653'), size_on_disk=10076041, blob_last_accessed=1756926352.116241, blob_last_modified=1756926353.223236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417746/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/432442dbdd5578acedcbf6eddc14cfe0d460facc7b0dea857225fbd4e6a4242c'), size_on_disk=2726090, blob_last_accessed=1756926328.6353428, blob_last_modified=1756926329.1983404), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417625/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/96b04f28e74e25bd1502a4ea9aca8047bfa8462e61cced0d726af4a336e1a585'), size_on_disk=17384720, blob_last_accessed=1756926304.4014485, blob_last_modified=1756926306.2074406), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417720/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/961772a081bde1795af5caea9d7562e2b1f889981bd3c3f1c792ad9093a758d7'), size_on_disk=659095, blob_last_accessed=1756926324.1233625, blob_last_modified=1756926324.6243603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380947/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3d6062f97d18f4945c36590355a312df2a16206c26626392cfb44d9613aea49d'), size_on_disk=9201129, blob_last_accessed=1756926368.5321698, blob_last_modified=1756926369.383166), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381063/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1e4361a7d20f8e92e8efc238365e0ed06eb021243864321c93d4638fa21634c4'), size_on_disk=10916323, blob_last_accessed=1756926779.3497405, blob_last_modified=1756926780.685733), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417768/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8bb8e088eb5123049e418c868ab42d94fb48946f934a8313f4cf283e3bfa09ba'), size_on_disk=4124375, blob_last_accessed=1756926331.3153312, blob_last_modified=1756926332.2313273), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417845/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ca2b89837de406b10d72533a3e3d5457b3512b704f7cea395fb0e1b88828393e'), size_on_disk=4648586, blob_last_accessed=1756926345.6932688, blob_last_modified=1756926346.5862648), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417861/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef65c45e0e86b7d04e274c39408b332cc94879c0804f16acf7c537b14bb6498c'), size_on_disk=10436399, blob_last_accessed=1756926348.3832572, blob_last_modified=1756926349.6192517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417894/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/12f262a12783317d135ffc5e1d281a3d43b746a82ed8053c15de824b94bd5ef8'), size_on_disk=6449687, blob_last_accessed=1756926354.4772308, blob_last_modified=1756926355.4742265), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417677/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cfcdac65879c0c26771c1cd1c4a656d4db20e6adb2ed3ad833cf23cd35a9d848'), size_on_disk=13135194, blob_last_accessed=1756926315.9083984, blob_last_modified=1756926318.1283886), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380942/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a958d997c1aff07ec07304485ee818280bc7714dd9c485ea73ef7fda81f2fe63'), size_on_disk=11407010, blob_last_accessed=1756926367.109176, blob_last_modified=1756926368.6211693), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380925/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdf190d30101829153acfd62edd4d689ac65a7d737f0e80978c7d460819b5caa'), size_on_disk=9220100, blob_last_accessed=1756926364.6101868, blob_last_modified=1756926366.1331801), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417854/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d52c8b14e197b179a9b43fbc89bb3205d09d458161ee49ac15a62b7c1f7a39fe'), size_on_disk=6203801, blob_last_accessed=1756926347.311262, blob_last_modified=1756926348.387257), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417717/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/689842dffacf7efa4ba6cd9b4314d195e89f9105eb71675f27019a9e25be63db'), size_on_disk=102777, blob_last_accessed=1756926323.9603631, blob_last_modified=1756926324.5793605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380966/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f27a802d18bf78781f3d168370e0c0e241a130d04a5871d7416442be3ce3e40f'), size_on_disk=4241927, blob_last_accessed=1756926370.3881617, blob_last_modified=1756926371.272158), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380986/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f23d3060df5cb96778fbdf10fff0155c931e1a1e8cc077c7f1582542d54827f5'), size_on_disk=5858046, blob_last_accessed=1756926373.285149, blob_last_modified=1756926374.5971434), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417896/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/71e393229e9071bad31abe5dc3d41bd5d719a5dcb68077827861b4b4e9a49a85'), size_on_disk=6055777, blob_last_accessed=1756926354.7882292, blob_last_modified=1756926355.8442247), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381022/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/980ed07a51dfad581fc2f7bce44031c92445822ba7e994cbfc9b5508cdf97c85'), size_on_disk=2103246, blob_last_accessed=1756926378.5151265, blob_last_modified=1756926378.9441247), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417949/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6123bc7bd312e6e37fabadee561c7dacad32d26bada92c9785c783f94fc50a37'), size_on_disk=1320707, blob_last_accessed=1756926364.4151876, blob_last_modified=1756926365.0891848), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417740/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e91586589c967e7bcbd521989874b21c024da79365d0d9916ffc66b774043e01'), size_on_disk=11875677, blob_last_accessed=1756926326.8903506, blob_last_modified=1756926327.6843472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380963/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c0a30240a0a99347e114928a15a24268495fd37480fde6822f503438bdf3ed48'), size_on_disk=579777, blob_last_accessed=1756926370.2681623, blob_last_modified=1756926370.81716), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417912/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4451877e599fa65e1f00f67b5fcd41fe74719d8bcb19ddacac419fb347c77cf5'), size_on_disk=4943909, blob_last_accessed=1756926357.7152166, blob_last_modified=1756926358.6302128), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417757/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6c9c5dbd0513759ace8599b2542a1f99fe0c30feb318665ebdb55bfb111c8c67'), size_on_disk=5834465, blob_last_accessed=1756926330.011337, blob_last_modified=1756926330.7623336), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381011/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/839fea471c67e08e0b68ae6344432c76da559e67c505c3f6120b20db2f1f753e'), size_on_disk=4378212, blob_last_accessed=1756926377.2971318, blob_last_modified=1756926378.0891285), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381049/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c571bcc5f87a79c7cd38b9614ee83b0d5cc742094e17a7990c829394fca193be'), size_on_disk=3753119, blob_last_accessed=1756926777.145753, blob_last_modified=1756926778.2097468), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417828/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eb4987a8ef94e1691c74c1d828d7d9313da4691b577118a73524def2a90c6d0b'), size_on_disk=4827634, blob_last_accessed=1756926342.3472834, blob_last_modified=1756926343.2782793), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417604/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e6dd3abdf75462f1f5d16f19383b98a8a4c817dc3b13c7cea2b4bea611c40c62'), size_on_disk=961320, blob_last_accessed=1756926300.390466, blob_last_modified=1756926301.3534617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417922/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fba17c65fa210922e57c533f5146b4237677149a5022fb836c1cd52dc89855b2'), size_on_disk=10194446, blob_last_accessed=1756926359.5812085, blob_last_modified=1756926360.610204), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417631/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b4b8c985528935ec46c6c7e78482af1ed26238137fc63b2196c6631e2d3796ab'), size_on_disk=27651513, blob_last_accessed=1756926306.2814403, blob_last_modified=1756926309.1994276), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417692/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/82405dc0a21bd225cc6a9e813e62d4dc0522719abf1e677c954a6ec38763b306'), size_on_disk=4873514, blob_last_accessed=1756926319.6253822, blob_last_modified=1756926320.6443777), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417898/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ced2c80d4f9d2aa28260b885a20b1c90b41d8eb54ee92f418ed592b97b954a9e'), size_on_disk=6422129, blob_last_accessed=1756926355.1262279, blob_last_modified=1756926356.0962236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417864/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/15d259c52e80a2d2d9c20274ef940237c90001d32f2aecb80779335a708222ff'), size_on_disk=3706383, blob_last_accessed=1756926348.5722563, blob_last_modified=1756926350.0962496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417826/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3532f474125bbdc0445e46fe8be83801ca9c079afb1dedd7cd95e25994c50d17'), size_on_disk=11492099, blob_last_accessed=1756926341.9132853, blob_last_modified=1756926343.2502794), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380949/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/65090ef93158269c98bc2e747e505cea9d460f0959d0b2957e58543cd1944163'), size_on_disk=2110872, blob_last_accessed=1756926368.705169, blob_last_modified=1756926369.142167), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417818/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3b30217b127ff88f165762c5054c392d9489b9007ba7ca8b86f18b44f9988e4d'), size_on_disk=8388230, blob_last_accessed=1756926340.2372925, blob_last_modified=1756926341.5632868), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417620/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b973f1a83f111c4e114a40d3731ff24779cafcb7b71523fe8571cf2ed849b316'), size_on_disk=20780203, blob_last_accessed=1756926302.5494566, blob_last_modified=1756926306.7334383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380946/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62fe0084bf7a8f9f484e76366f203e1c68862a211d5d501c08e19b8f28678d6b'), size_on_disk=8090103, blob_last_accessed=1756926367.6871734, blob_last_modified=1756926368.7681687), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417698/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d240b652ebca64784e037cc8ffbda424e2c92f368c5bb9c730930f48f29facc2'), size_on_disk=1855826, blob_last_accessed=1756926321.2993748, blob_last_modified=1756926322.0703714), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380929/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aeddf7cf3aee11ee08c90d305e66fc682e675959c171ecfaa591bc1d9015ebbc'), size_on_disk=6656759, blob_last_accessed=1756926365.1011846, blob_last_modified=1756926366.0181806), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417807/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ebc732f5d06d745bb63af3c7f5966722972fa722ef9d0194d71cf6d9485fda83'), size_on_disk=14049230, blob_last_accessed=1756926338.268301, blob_last_modified=1756926339.5272956), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417637/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ca1a954c7b2aca56529daf9a89249ad4e6ec2373e29ac7c19d2af1533afc4263'), size_on_disk=2776397, blob_last_accessed=1756926307.8804333, blob_last_modified=1756926309.1794276), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417821/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c29db68a3430cf8cb01716d90a92105387777ed1361992eb9501271c35359cfb'), size_on_disk=6597050, blob_last_accessed=1756926341.1692884, blob_last_modified=1756926342.2702837), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417929/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/85b194132215311994e48908799e31357d1094663a232e8170a4edcfa5a11eff'), size_on_disk=9155231, blob_last_accessed=1756926360.9862025, blob_last_modified=1756926361.9231985), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380955/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/369e0f1de51ea197e928618e562613eeed2ed59ce5c1389ce07ff6dd96d391b4'), size_on_disk=3596907, blob_last_accessed=1756926369.4561658, blob_last_modified=1756926370.2661622), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417743/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d4eb93049648498d2fdc1f125574429a4dfdec65e626ad9fa16bdf0d0cf599d8'), size_on_disk=2280534, blob_last_accessed=1756926327.462348, blob_last_modified=1756926329.1343408), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417788/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fd6f44471f1b86568bc059f25e5ccec7bda2b85b9f8be536aeaf2305fe015f5d'), size_on_disk=9112879, blob_last_accessed=1756926334.546317, blob_last_modified=1756926335.971311), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380945/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/51efeee4439e81d920c4d9e860de321d90c72703600044e52215c774e1540650'), size_on_disk=6902367, blob_last_accessed=1756926367.356175, blob_last_modified=1756926368.6011696), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381059/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/59daa71f55c9c0da6f8d56fc2497491477312fbdbcb9e92d4951874f2ed72686'), size_on_disk=13124154, blob_last_accessed=1756926778.926743, blob_last_modified=1756926781.8157268), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380974/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ea9eb19e91fedadd9e46d5d7dc9169995065af5b6b8e823f1667117be9d737d0'), size_on_disk=8889742, blob_last_accessed=1756926371.5481567, blob_last_modified=1756926372.2801535), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417934/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5816750a355a277e532d46ffa5159f8a2ec1ce66686fb12f07ccab4b4f7b3c98'), size_on_disk=7324082, blob_last_accessed=1756926361.995198, blob_last_modified=1756926363.89619), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380985/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2ec986412ef2a9e6e59f4ea3dc278b27ab3975b5025d00bd1cd38fe1867b0fea'), size_on_disk=7208882, blob_last_accessed=1756926373.2251494, blob_last_modified=1756926373.9591463), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417876/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0713329434bfbd55f1998400d9d44c1d8c7fec6cb1b646c0fdb926f1046f9b47'), size_on_disk=11246388, blob_last_accessed=1756926350.971246, blob_last_modified=1756926352.0472412), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380980/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e36f58da696f0827c72fed8647c2a45a0499fdc5d1859386dbf025388f07e16d'), size_on_disk=2288305, blob_last_accessed=1756926372.371153, blob_last_modified=1756926373.2051497), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417869/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8dd08ab28007f90a664a6fbde23298ae2bb0ded917d5d75b1275c41e3eb5f7dd'), size_on_disk=16604892, blob_last_accessed=1756926349.6872516, blob_last_modified=1756926352.7202382), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417649/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6baf4bcd82d9fe55b8dcdc7602c0d39d20954675ef4c5484331ff25dac8ad3f4'), size_on_disk=2672976, blob_last_accessed=1756926310.8234205, blob_last_modified=1756926311.4704177), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417764/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8a30e54d96d5b83981e37a0d8f6cae9e6584819a76a200f2730d9de32e282d02'), size_on_disk=2641764, blob_last_accessed=1756926331.099332, blob_last_modified=1756926332.088328), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417652/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d38c028233981eb275c44995eafa73172388a9e0e704fe278b0cedbab49d429f'), size_on_disk=12947754, blob_last_accessed=1756926311.0344195, blob_last_modified=1756926312.1414146), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417855/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ac2218ccb9f80664ce377351ea1dc84bf3f70b36006a86cffdb761a40ddab791'), size_on_disk=1981158, blob_last_accessed=1756926347.3992615, blob_last_modified=1756926348.3062575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417719/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cc0a365d1d79f0469571216394f0cb211b9096f2a567e0a347a18f0c496bac06'), size_on_disk=2076369, blob_last_accessed=1756926324.010363, blob_last_modified=1756926324.951359), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417903/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3ddfaef9dc57d0f63b8613408aba3af803dabd5ee87a1584edd9391b2b41bec3'), size_on_disk=11218813, blob_last_accessed=1756926355.9432244, blob_last_modified=1756926357.2232187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417709/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/395b1dd171ab9759e06a22580672bef65aac2acd7d548e38098d207c80071d89'), size_on_disk=717131, blob_last_accessed=1756926322.9983675, blob_last_modified=1756926323.6373646), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417870/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ca1bc9825b47ee9ac59e53bc6ada3b1a9e8aba1b57c57c57947afae7926054b'), size_on_disk=10710319, blob_last_accessed=1756926349.7052515, blob_last_modified=1756926350.7552469), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417742/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1a87c492aab479dd0c013fbe80e006f18ba80de751da600b95babcb3f6e88dad'), size_on_disk=2054520, blob_last_accessed=1756926327.3293486, blob_last_modified=1756926328.4213438), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380990/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4e04122d2830db35f2b82b29efaca846fe10d7638c88c3e4d3cf78bf10cf245a'), size_on_disk=2093813, blob_last_accessed=1756926373.9261465, blob_last_modified=1756926375.1611412), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f00bc431128183affc8964f9610f3e9486a043bcea5b31b3ebbcbd9b0070a448'), size_on_disk=6096918, blob_last_accessed=1756926377.3431315, blob_last_modified=1756926378.2571278), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381025/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aea8c4d9f3783191d3e89f4566811f01089f3b0b76b736c5167bda54693b0bc4'), size_on_disk=339954, blob_last_accessed=1756926378.7781255, blob_last_modified=1756926379.125124), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380958/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ef3849004b6c72205ea1b298cf6a586f1114061ca94925229eda191d4666885'), size_on_disk=3288900, blob_last_accessed=1756926369.617165, blob_last_modified=1756926370.303162), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417899/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b4349f94cb0679468e9c28a9439c1a71c88d7288adc3bdad0ca31a2590f1d4e0'), size_on_disk=7910137, blob_last_accessed=1756926355.3832269, blob_last_modified=1756926356.3022227), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380931/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/be287270314fed3dded81e0c24a66d8bd991b219fd6a157f1aeaa1690262e563'), size_on_disk=4324179, blob_last_accessed=1756926365.1611843, blob_last_modified=1756926366.1241803), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417776/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/01c4a97f92cd5e2919759d52d083eb824408a2fd7ade9413a060c6b20d233963'), size_on_disk=2759569, blob_last_accessed=1756926332.8593245, blob_last_modified=1756926333.629321), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417618/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d2ce91297551bc97b4fca46007f80c8846e5f9e9c78d9c1d7ded02fc31b3ef38'), size_on_disk=9239111, blob_last_accessed=1756926302.3654573, blob_last_modified=1756926304.3714485), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417862/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cea0cfbd611f8c6482ec33f8ea32f304a481d1c1225d3dacea432dd359fc976b'), size_on_disk=5244392, blob_last_accessed=1756926348.381257, blob_last_modified=1756926349.6162517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381026/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e41f2d7a1b084a404e7f5e9e003b27f88665a983ec73859f7eb42cd8bfa2bdd3'), size_on_disk=2857045, blob_last_accessed=1756926378.898125, blob_last_modified=1756926379.7281213), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417872/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7d71ca12594ab85797fca8c1b88947cb0f3ece93596f1a4c5adc04c11c0abf68'), size_on_disk=8522283, blob_last_accessed=1756926350.1742494, blob_last_modified=1756926351.180245), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417941/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/30188c510d2d7041e1525e304edc8f25b569a1771cf00844cb27300a052441e8'), size_on_disk=7032333, blob_last_accessed=1756926363.2551925, blob_last_modified=1756926364.2891881), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417680/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aa9e972e7427b1db5556008756651933c8792891339607dee2ee231733072d1c'), size_on_disk=18443288, blob_last_accessed=1756926316.5853953, blob_last_modified=1756926321.4263742), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417738/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c3d82a351044aa8cd5d43979369088ccf2f563022d4f7aa3683ef20923202770'), size_on_disk=19772535, blob_last_accessed=1756926326.3913527, blob_last_modified=1756926328.6913426), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417654/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/67d658902a2b91d0d36345be06dc00c85af1b88ba40c37deaa091705d97b3ffc'), size_on_disk=6323092, blob_last_accessed=1756926311.166419, blob_last_modified=1756926314.4344046), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417946/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef43c84affe53bbdefcd70cb97a4a4d1e483824b8b6f91e3360a11094609f815'), size_on_disk=4853893, blob_last_accessed=1756926364.0141892, blob_last_modified=1756926365.0591848), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417641/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/355d8669b1d4c5576d8aa900582800fdf1819360f0db91d5e0299342b49884fb'), size_on_disk=6322689, blob_last_accessed=1756926309.3414268, blob_last_modified=1756926310.7974205), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417796/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/52d8262071c9c9de8cd8fb52be9e67acf125b8ce67033749fb92989f0d1f9345'), size_on_disk=10184069, blob_last_accessed=1756926336.2803097, blob_last_modified=1756926337.4333048), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417627/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4027ab6ecd21364ca365bbe7192797ed924c817e6edc841d750111b2e3f6f45a'), size_on_disk=21089243, blob_last_accessed=1756926305.0144458, blob_last_modified=1756926309.6264257), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417651/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b95b6be3a7a6a5b4af3bbf15c5a4a6b3893f3030ef6d1dcfe79e6c6554e3d3cb'), size_on_disk=13551797, blob_last_accessed=1756926310.9544199, blob_last_modified=1756926314.6814036), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417630/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/26fdbadf7c1252a8fd73ac85ea7430d942d2d11ddc43ea1b2590aaa008e08337'), size_on_disk=3015575, blob_last_accessed=1756926305.7104428, blob_last_modified=1756926306.7214384), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417802/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ea68aebb727f77d846a145d43ec16fa2496e9c05d49a8791aa131b8e3e536452'), size_on_disk=4881005, blob_last_accessed=1756926337.2223055, blob_last_modified=1756926338.1273017), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381028/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8d40caa48e7b9d32d2a0548732ef91c01ede9d0f4f6d74f045817e818c52c808'), size_on_disk=18141950, blob_last_accessed=1756926378.9691246, blob_last_modified=1756926778.2687466), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417858/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2d7539fcb6297592fe3755ccc88e122441925fbaf9f5600d4daa88639630fd8b'), size_on_disk=1021831, blob_last_accessed=1756926347.8042595, blob_last_modified=1756926348.5072565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417815/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ee8a37cd03dfe154008718f2a0f7fd95b3fdf468f99b1fc73ca515433fa45930'), size_on_disk=6563669, blob_last_accessed=1756926339.6062953, blob_last_modified=1756926341.8062856), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381061/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d44d57e9c03bb6e6e750809eea88657c2960ed251b8acaa1352a07f4cb346a82'), size_on_disk=3789431, blob_last_accessed=1756926779.0527422, blob_last_modified=1756926779.8977375), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380997/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/add73ab9723ef88a47192fa169b89aaf17c16a0841fc9e53c1c5d73cfdc2ad50'), size_on_disk=15536494, blob_last_accessed=1756926375.1291413, blob_last_modified=1756926377.6141305), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380932/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f214422d02adb2b0a1037eb424d8574123cf27d8a5cd1418feb5733b0249f60c'), size_on_disk=6251591, blob_last_accessed=1756926365.6181824, blob_last_modified=1756926366.5551784), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380971/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0147090dd5a74f558b67151503ff635423a8af661b56d1faf06bb50a59adfb3a'), size_on_disk=1276600, blob_last_accessed=1756926370.9411592, blob_last_modified=1756926371.462157), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417836/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4fb964c54f4a37387c7844902d6171011a9a81efff244f365efcb156f630158f'), size_on_disk=3312893, blob_last_accessed=1756926344.2532752, blob_last_modified=1756926345.0702715), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417846/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a3dc440aac6a69d75ec73242a30e59d8183023a2677f1576b5e73d037d6e6654'), size_on_disk=2680468, blob_last_accessed=1756926346.0562673, blob_last_modified=1756926346.7392642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381044/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/89a79f2c5f6258380ff4776af49db6119ac0c04342686705cfde94feebd7c9ca'), size_on_disk=3576881, blob_last_accessed=1756926776.065759, blob_last_modified=1756926778.8987432), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381024/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5182750a0c8af8253cc98ecad8715519463d5c18adbe067488dfef91198c0d80'), size_on_disk=603678, blob_last_accessed=1756926378.7151258, blob_last_modified=1756926379.128124), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417638/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c8cface311cd55cd1542ed94d609da72cd2b3087422c28ad26c60e1d81a3e6bd'), size_on_disk=993699, blob_last_accessed=1756926308.4724307, blob_last_modified=1756926309.2634273), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417663/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cc7010c3ed70c3083d49d88c0f95dbe8f6eff873d4b5848eeae06a22df71a479'), size_on_disk=8938116, blob_last_accessed=1756926313.18441, blob_last_modified=1756926314.5674043), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380962/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/72be01aa42db736ffac8236c23c16edea00bdf5da88c3114fc94d9eb3daa4ee9'), size_on_disk=1460258, blob_last_accessed=1756926370.2361624, blob_last_modified=1756926370.6811604), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417814/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9f70b1469a6d0ad8758aa7a44e754e07ccc401906f8af403a72075d763919535'), size_on_disk=7618977, blob_last_accessed=1756926339.5142956, blob_last_modified=1756926341.0872889), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417676/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/53c35af59283469a22148b2841cb8574faf80d59162a33d269560283c9228af2'), size_on_disk=12006227, blob_last_accessed=1756926315.8083987, blob_last_modified=1756926316.8563943), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417666/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/38008595447ab722c1889d0e19babf74299822ac7733504a4f09e1af1e4ad1ac'), size_on_disk=7712422, blob_last_accessed=1756926313.945407, blob_last_modified=1756926315.073402), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417856/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3dc4e868f31759c9ac3d43f40097f5ed2f0efb6307e3bc1ccbed70c50a16ee4e'), size_on_disk=6126482, blob_last_accessed=1756926347.3832614, blob_last_modified=1756926348.3162575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417718/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3ba23a0e7c5b3a455e95fbf6a22b51687d5330c19207241bdfd189e3776f5ba3'), size_on_disk=3304954, blob_last_accessed=1756926324.0233629, blob_last_modified=1756926324.986359), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417942/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f3a5b1eb0f954ddf66a8dfa1a38465726d2251730d3898865535269a43c7fa4a'), size_on_disk=6911573, blob_last_accessed=1756926363.415192, blob_last_modified=1756926364.3521879), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417695/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d6ae8d39c2949235dd4d82e7a0e56d9f20c20ba5f1b7a445fbba1f0df29c88b7'), size_on_disk=7710772, blob_last_accessed=1756926320.4913783, blob_last_modified=1756926321.959372), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417678/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/825d9bcd10c008b68cc84e92f20608c0d52770ac028bbc4a9219f1cd210ab8c4'), size_on_disk=29320273, blob_last_accessed=1756926316.0503976, blob_last_modified=1756926318.3993874), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380972/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/91a8106ca7ac2babcabd1549072b2b730909e1386f827e732428bff2bb5251cb'), size_on_disk=6712599, blob_last_accessed=1756926371.1721582, blob_last_modified=1756926372.0711544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417888/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/961a35161b04a0e892f46a0a90b6d4776d7fe07ef82fc7016c95b1a885c4274f'), size_on_disk=9651505, blob_last_accessed=1756926353.298236, blob_last_modified=1756926354.65023), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417921/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/05fb4ff0001b88b711eee9f29ae30642d940667f7166121c9782f464057880f5'), size_on_disk=7891276, blob_last_accessed=1756926359.3782094, blob_last_modified=1756926361.2442014), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417799/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cba2fb579c7706a7ff38fad3c7c649000560c1052e681adea986c5cc02c02cfb'), size_on_disk=5688906, blob_last_accessed=1756926336.7133079, blob_last_modified=1756926337.7073035), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417867/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f4783b00d4674df92d84f9a304fb9d143c7b9e4754e3efcb9c072a49a9b9298b'), size_on_disk=8536709, blob_last_accessed=1756926349.3882527, blob_last_modified=1756926350.2882488), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417887/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/58707813ec63af2eb1a72b0c8317b4701b4777df8f828f2e08a0cbb1ccfa534f'), size_on_disk=9080847, blob_last_accessed=1756926353.0712368, blob_last_modified=1756926354.412231), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bd149efcdaa899981b965dc424d16fb66668a7666a059c6bc5043572331394c2'), size_on_disk=4513644, blob_last_accessed=1756926377.1751323, blob_last_modified=1756926378.1161282), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417820/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9d36f1c17151e3203186142abff734c0182377d5287b3f27d00917608501abb3'), size_on_disk=8116588, blob_last_accessed=1756926340.9562893, blob_last_modified=1756926342.0902843), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381033/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5fff567b96a4649d787e93ef8cdca2d8ce977b7f2eb25d6f78e50bc30c798bce'), size_on_disk=1712156, blob_last_accessed=1756926379.7391212, blob_last_modified=1756926380.3661187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417710/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9837ecd3769eaaf407d939ff09d6ae74d832630b27353251942bd262cd70fc35'), size_on_disk=330457, blob_last_accessed=1756926323.098367, blob_last_modified=1756926323.797364), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417767/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8d0afed33cd85298c709656da4b210e3fe87aa4d6822208d744200816d59b95b'), size_on_disk=3739401, blob_last_accessed=1756926331.2333317, blob_last_modified=1756926331.8623288), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380964/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3a63df07de544662fb04d5388f34b0c8068efbc19f464b673d8a860a57b5b422'), size_on_disk=956844, blob_last_accessed=1756926370.333162, blob_last_modified=1756926370.8741596), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417801/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4bbb1b3faac1a4c15eaac8c81260f4f18493d2a082915cbe4c480ce76a94140e'), size_on_disk=4600965, blob_last_accessed=1756926337.090306, blob_last_modified=1756926337.9153025), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417711/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e30dbc09cd2dfb4722d60d0833123542e9c5096f70b97867cd15d466aa7b927a'), size_on_disk=96859, blob_last_accessed=1756926323.1863666, blob_last_modified=1756926323.7823641), CachedFileInfo(file_name='GSE222268_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE222268_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e0f1d5b82fd97798d0c02d992c49c0e83d8dcf82e07e001284e78d9656355481'), size_on_disk=4411, blob_last_accessed=1756926300.1784668, blob_last_modified=1756926300.5344653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417737/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2e1d5b2306dd24257d4ba261baa42b324c9f4f8ca3a1d30e0f920c4f4a5f99ba'), size_on_disk=1342511, blob_last_accessed=1756926326.2713532, blob_last_modified=1756926326.9043505), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380927/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/38e67ea2fef57610ff63c0c298a6db65ae1c0a159f2b1e61940f2e65306b8fa9'), size_on_disk=7297320, blob_last_accessed=1756926364.969185, blob_last_modified=1756926365.7781818), CachedFileInfo(file_name='GSE178430_metadata.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE178430_metadata.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9673758f13ec096ec4a89642bbe34d60975f5f05'), size_on_disk=61, blob_last_accessed=1756926300.2374666, blob_last_modified=1756926300.2994664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417702/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/41123195fa339a4ffabf6d686a284c343d0519089a18b828818c92ec43a26eae'), size_on_disk=6213776, blob_last_accessed=1756926322.0263717, blob_last_modified=1756926323.0323672), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381032/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/335a75b123ae38273f1b1a10f16e9547eec356f68a1d3078926f011d3332e2b5'), size_on_disk=656385, blob_last_accessed=1756926379.6781216, blob_last_modified=1756926775.4077625), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417771/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8fe5513ae86b28ecca3d4c70bdb73097f578dd31b4d08a072960b61550f582fa'), size_on_disk=3970195, blob_last_accessed=1756926331.9313285, blob_last_modified=1756926332.8643246), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417716/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cd98cf97346b62e4f1efcace598c9c273d9ad9b8d8692e35f50b11d46f7ed8a6'), size_on_disk=5100666, blob_last_accessed=1756926323.8513637, blob_last_modified=1756926324.8793592), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417833/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/48792db399d7f0aab6765109e8c01abd61a2da93e5d0bdf7c325bcffe01fe113'), size_on_disk=8139822, blob_last_accessed=1756926343.3262792, blob_last_modified=1756926344.480274), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417786/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdd4770c9b3e18e86f9a46ff924e3a562e37b632d6f0e8ad11627956a3416e09'), size_on_disk=11354740, blob_last_accessed=1756926334.3243182, blob_last_modified=1756926335.4083135), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417900/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/45d5a0bbf39e542f22aa4e36186673d1246533dc513f006312a26286496f6ead'), size_on_disk=11235417, blob_last_accessed=1756926355.535226, blob_last_modified=1756926356.6632211), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4331f53e52f628f99a9695fa70339b08eb69a011cc8a3cbdb08dfa8e6c5d3ddd'), size_on_disk=10779086, blob_last_accessed=1756926376.8381338, blob_last_modified=1756926377.9141293), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417686/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6a42f24b250baf0d88696bc123f01494a400b3f03f2ff2a1fc828f9295721a16'), size_on_disk=12070748, blob_last_accessed=1756926318.2063882, blob_last_modified=1756926319.627382), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380926/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fd708dfb8084d767fde32c52acb58e785083ce7becbd2e160277f31460719346'), size_on_disk=6579161, blob_last_accessed=1756926364.8451858, blob_last_modified=1756926365.5441828), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380981/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/198a0b08846a11e72de25e0e9c44f495ef88c9260de2e036d240715004c04953'), size_on_disk=8535317, blob_last_accessed=1756926372.5771523, blob_last_modified=1756926373.8291469), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417602/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/26ae91a914e9ad095615c88ac3c62c99e28ae2687ccd66007f13ac8fbbffa3fe'), size_on_disk=3717181, blob_last_accessed=1756926448.0367467, blob_last_modified=1756926301.5714607), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417889/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/295f9efb0d78e3a116ce4022a7830297570446e2017a3fcd3b3d423c0c20664b'), size_on_disk=9617979, blob_last_accessed=1756926353.3102357, blob_last_modified=1756926354.7322295), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ad0e80a28e4cd2027893adee5afceab851576ed2637c1a8e94d0af2adae6ace1'), size_on_disk=5088900, blob_last_accessed=1756926375.41114, blob_last_modified=1756926376.3821359), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417804/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/16ee23ae4dbe8a4d968a614f5e6de65028b6edce012231a50eb776d65f949d34'), size_on_disk=2107502, blob_last_accessed=1756926337.7883031, blob_last_modified=1756926338.5083), CachedFileInfo(file_name='GSE209631_metadata.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE209631_metadata.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fe31b19357a717d3d51d1d201620d37fca79b1c8'), size_on_disk=61, blob_last_accessed=1756926300.2214668, blob_last_modified=1756926300.2814665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417735/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/634551506800bc76183ce179c8c8f324cb6f0f577f6cf0b629b74ce4f1e995d5'), size_on_disk=19171195, blob_last_accessed=1756926326.0393543, blob_last_modified=1756926329.4873393), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417793/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8a51bd4d953e697022a85f9b527afd45126988ba4bbb796d71c9e5f6c8fffc44'), size_on_disk=10797388, blob_last_accessed=1756926335.4883132, blob_last_modified=1756926336.7013078), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417650/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c4d103888f768ff3cb0c68f4b747cb8d8aad8058b75174160cfc42b2504407f1'), size_on_disk=1337049, blob_last_accessed=1756926310.89842, blob_last_modified=1756926311.4924176), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417865/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/348bfc1685f79e9ec7f559e96b61b8c43628d611378b6c4ead8bffea44415f1d'), size_on_disk=9233047, blob_last_accessed=1756926348.8982549, blob_last_modified=1756926349.9182506), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417868/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/52e3f224a0147264d4ce477d26f4e11ff0745c328eb3e2ac44209dba1c8c55eb'), size_on_disk=5039768, blob_last_accessed=1756926349.6852515, blob_last_modified=1756926350.6082475), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381046/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a96c1819b1010c40fc4d92418e407cc5b5f48f9e24883d4b4cdf3e02d23b688a'), size_on_disk=3071378, blob_last_accessed=1756926776.6277559, blob_last_modified=1756926777.855749), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381037/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5cef560daffa3a5b1e841a312937eca595ced0bbdb3e6c641b13359a0340fea1'), size_on_disk=2090047, blob_last_accessed=1756926379.8821206, blob_last_modified=1756926380.4171183), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417732/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f99cdb10c12a75e1d020125d68b5c5302263eaef050b5c6b0032e27a0ac9a23c'), size_on_disk=685742, blob_last_accessed=1756926325.7463555, blob_last_modified=1756926326.326353), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380987/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2eb045d9f88b952eed8f8354e77c682bc3003bd2e9eacb0eb0c2df5ca44a34cc'), size_on_disk=4463796, blob_last_accessed=1756926373.559148, blob_last_modified=1756926374.4241443), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381065/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/99cc73bc4c0908da04c01c7c59eff995a478ffc023c31fb970c7bb9789b2d70b'), size_on_disk=14673323, blob_last_accessed=1756926780.0087368, blob_last_modified=1756926781.5847282), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417640/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/411d9146359847b5805450f67cbde4f1cbd51e3c2fae4bbcd6295db7426cb2ca'), size_on_disk=7000528, blob_last_accessed=1756926308.9124289, blob_last_modified=1756926310.0344238), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/64a119c8028083521d3872e2daca1545f2f8753eb8acfc751bf0c6d25803a3f8'), size_on_disk=2003211, blob_last_accessed=1756926376.794134, blob_last_modified=1756926377.2761319), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417696/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2e1bce702520ca07d9037bd4de4d21bb30ef6b7fdf7de9d75ea232acc036a72e'), size_on_disk=31303382, blob_last_accessed=1756926320.7203774, blob_last_modified=1756926777.0657532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417810/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b807197acb33acdd1b70eddedd59dc4d831e271d231d6c9bb6f1281fb4d5813f'), size_on_disk=9808564, blob_last_accessed=1756926338.8082986, blob_last_modified=1756926339.7202947), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417853/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5995033a04f017892b30c025a1527f9b6420bf1e7636c466cdd8d1462cf228c8'), size_on_disk=2186899, blob_last_accessed=1756926346.9012635, blob_last_modified=1756926347.8172596), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417893/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f3b9f2c711cb806350320b93dba528efd6433d0ca10c39e5e2dd6aa89a408bd'), size_on_disk=9896637, blob_last_accessed=1756926354.2372317, blob_last_modified=1756926355.535226), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417800/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8e563a7d64bf06e7fe26c2817271f626273eed4c7dd19db0b9fd8e14c25f73b3'), size_on_disk=9904697, blob_last_accessed=1756926336.7953074, blob_last_modified=1756926338.958298), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417713/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c5d75f4a32cadceee844da0c5ce703483f575fb927990a1615ee37205137d986'), size_on_disk=524444, blob_last_accessed=1756926323.533365, blob_last_modified=1756926324.045363), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381048/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/53cd017f084781da16e69ae01b406ac0d8824a4d991e39b22c428e664a3808f2'), size_on_disk=2181901, blob_last_accessed=1756926776.9907537, blob_last_modified=1756926777.7067497), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380976/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d27d3efdccd104ca208d69fe9d94fe9011cb6212b34739679b1c8f550c5e9df2'), size_on_disk=12889551, blob_last_accessed=1756926371.6141565, blob_last_modified=1756926372.8021512), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417774/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ad00f794fb29d2a609375d63b09170f396f055ae392cd05a31d5f8fb5ede7a33'), size_on_disk=2914273, blob_last_accessed=1756926332.1763275, blob_last_modified=1756926333.217323), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380983/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eec33f1158aa017b073fcb16d6400cd800eb79e8c0b78b5e2f831d7413e4a736'), size_on_disk=4478939, blob_last_accessed=1756926372.869151, blob_last_modified=1756926373.6721475), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417834/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a69c65a4d32399355863fa14d4a1e92ecff2c92498e122e8e77796de16d42948'), size_on_disk=8537233, blob_last_accessed=1756926343.3672788, blob_last_modified=1756926345.2462707), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417926/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/303d44a38fbb646cf211ce1bd03fe2d24bb004f385c63d05ab8e707c5b283f0c'), size_on_disk=10766909, blob_last_accessed=1756926360.6072042, blob_last_modified=1756926361.6751995), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417734/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/10d4c6d9acc5b56370fc32062262ae3871008e20e960c6b352ac27a4e229c905'), size_on_disk=1495194, blob_last_accessed=1756926325.9453547, blob_last_modified=1756926326.745351), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417910/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/694d86a1e469836a53e1ce5dba0d64b77b8f68590a2034e2a1ef5310457dc7fb'), size_on_disk=4494010, blob_last_accessed=1756926357.3112185, blob_last_modified=1756926358.3942137), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417722/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f29055ed874a3a40735526808a6739884efecd8d7c5e7e9eb1fcaa8e495ade3'), size_on_disk=345818, blob_last_accessed=1756926324.5753605, blob_last_modified=1756926325.2483575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417945/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6689ba375c993d00d1f0e716d3bc39604d68df654a67b2518c56f4d54c7f4ca6'), size_on_disk=11289854, blob_last_accessed=1756926364.0191894, blob_last_modified=1756926364.9011855), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417839/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a04fc093d2a4fe873de135165bd62a38a1cb9b29860881ed973022ff128dc6d8'), size_on_disk=6789775, blob_last_accessed=1756926344.5502737, blob_last_modified=1756926346.4242656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417944/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/01e0e606e4e8b2203e6497ffc8c231bce8672a43d83098ba838c8edfdb052c27'), size_on_disk=4563261, blob_last_accessed=1756926363.89319, blob_last_modified=1756926364.6951864), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417681/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6e4ea424e9eab4dc3ea304621992e1db158565a8ce30374980b1084a653a0132'), size_on_disk=7689435, blob_last_accessed=1756926316.934394, blob_last_modified=1756926318.231388), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417779/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/918376b5cb0b63e3e531ac990854e5a7b461e7251f5ed562c5a92cbf6a5a0141'), size_on_disk=9459105, blob_last_accessed=1756926333.1593232, blob_last_modified=1756926334.2033186), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417917/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2118491b5ba6b4af1c0d394eec3139b0258cf3a4e42b4d1a02ab177247852081'), size_on_disk=14019020, blob_last_accessed=1756926358.4622135, blob_last_modified=1756926359.8632073), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381064/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8569620c4d2efb8dd163aa69d8edf8f855b96eba637e754a126a88e6f0c73192'), size_on_disk=20598404, blob_last_accessed=1756926779.800738, blob_last_modified=1756926781.1587305), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417871/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e44cb883893b5e30baa752dfbd773b31795f2072a7ec4f473371f435989cad42'), size_on_disk=6264896, blob_last_accessed=1756926349.9852502, blob_last_modified=1756926350.937246), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/059cc8a8b1f7918ab1b6499d3d9843100b3b37fe6dcd57dfe9ae512c6030cad6'), size_on_disk=2109635, blob_last_accessed=1756926376.7021344, blob_last_modified=1756926377.469131), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417930/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62f17648a07c7675d6a101e3d12efeae247c742e37eaacf96f95e03dceedefde'), size_on_disk=20574235, blob_last_accessed=1756926361.2242014, blob_last_modified=1756926362.5701957), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417731/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/39ec5969bc8f3acceb3aa30b72f0c0101785a36da340f3983ad00d8cc80160a4'), size_on_disk=1557745, blob_last_accessed=1756926325.658356, blob_last_modified=1756926326.2043536), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381055/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0aad0d3724742845da7f0afae0c18ab5c992bec76c02e0ffda057f00d0894d35'), size_on_disk=3355008, blob_last_accessed=1756926778.3377461, blob_last_modified=1756926779.263741), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381058/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9dc97b5892fcc8af593c03fad2040396b9b10626a655319ad40b38acd75e1d7b'), size_on_disk=11534270, blob_last_accessed=1756926778.543745, blob_last_modified=1756926779.7317386), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417880/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b08db0a2399fc4e5b6f59c19b7af05b1294d0fac10b2c336b627234fa828f1be'), size_on_disk=9818886, blob_last_accessed=1756926351.5782433, blob_last_modified=1756926353.246236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380982/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d1e44b0b82ea22fb6c513ce02b49facf5f38b64b2baa91b24942fc6858125e37'), size_on_disk=6977161, blob_last_accessed=1756926372.8241513, blob_last_modified=1756926373.4951482), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417704/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/477a83e4a5eace957a036c5ec65ef4539442c078058c1a44a877763cb850c86d'), size_on_disk=459034, blob_last_accessed=1756926322.1393712, blob_last_modified=1756926322.8993678), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417724/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e5beee12039dfe6b4dae180a31ac0897dea725754554db43e2df9f99da94db58'), size_on_disk=4535402, blob_last_accessed=1756926324.69836, blob_last_modified=1756926325.9703546), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381027/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ba9aba2cc5b9bd9e83599ccba5920f028ac9c6936436dac50f45c606f3abd841'), size_on_disk=6537548, blob_last_accessed=1756926379.0151243, blob_last_modified=1756926775.5467618), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417688/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f21ccaca0f6782b6ce2b70b50c9dc93487b6aaec03db6c30d9cb0c9fc01dda42'), size_on_disk=16023102, blob_last_accessed=1756926318.5463867, blob_last_modified=1756926320.0283804), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417608/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d225f561536fca4d073d2418a32efc3df68e9666a3403c7d8b1ee0a520861322'), size_on_disk=873305, blob_last_accessed=1756926300.603465, blob_last_modified=1756926301.521461), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417897/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/309aab637d037638082524649ef7b0828885a98e786c64af01072d68968b7d66'), size_on_disk=7857749, blob_last_accessed=1756926354.8022292, blob_last_modified=1756926355.9902241), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381051/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ba017d941936d755013757711f5de4661acd187ef79524662d85485ba6588f34'), size_on_disk=8199472, blob_last_accessed=1756926777.322752, blob_last_modified=1756926778.4757454), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417824/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/65d997db1339fc5a8380a61fcf1ab1a6d28fb9142fc7d78c89236dc218b91ea3'), size_on_disk=8218856, blob_last_accessed=1756926341.7612858, blob_last_modified=1756926343.0512803), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417659/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/20f21bb1ba86fdbee1918776793b1ff2275be49933c72eaba90b8be8582f7692'), size_on_disk=15311232, blob_last_accessed=1756926312.2364142, blob_last_modified=1756926313.8474073), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380937/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e31f54581c4a4a47033065033f3f9266913edad654543dd07b20d0ffe54847dd'), size_on_disk=2691574, blob_last_accessed=1756926366.2071798, blob_last_modified=1756926367.1951756), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417644/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0c788a7ba70689a796af823df5cc14504dfdc854617952459367364c4498cdeb'), size_on_disk=949200, blob_last_accessed=1756926309.758425, blob_last_modified=1756926310.6444213), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417745/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ab84f2e8e76311cfe45a4b525b239186e835cab37f1e5222fdc35c286064e5e'), size_on_disk=1270105, blob_last_accessed=1756926328.596343, blob_last_modified=1756926329.1233408), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380989/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3c8b646955209386a1d97882163b7c9816463d2dea68fbed2cc960817ffeb332'), size_on_disk=12022144, blob_last_accessed=1756926373.7761471, blob_last_modified=1756926375.1511412), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/43f12fb4826be4c7a3dd64800fc72cbcab2a9ce5bed7f7b5b9aee974deb7c06d'), size_on_disk=1116877, blob_last_accessed=1756926376.094137, blob_last_modified=1756926376.6491346), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417913/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/35c1a160b767f9f5870216ed33a1dc24804690772a5baee58f623c6dc0632127'), size_on_disk=9135350, blob_last_accessed=1756926357.9462156, blob_last_modified=1756926359.3142097), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380944/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7331bfc92dc902a721083ac4ea027481c90ce204d7dca7c493fec8f284db0155'), size_on_disk=8432479, blob_last_accessed=1756926367.299175, blob_last_modified=1756926368.6141694), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417690/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/283af9b17d776b75f6ff6ad1f426d6d0e0e5aa1f2089a20910edcb6763191b69'), size_on_disk=12265448, blob_last_accessed=1756926319.171384, blob_last_modified=1756926321.4153743), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417739/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1c9e3fcff1dc467673efe70177cb4d678d7eff7b5b2bb3ded247d7a458b466dc'), size_on_disk=21974051, blob_last_accessed=1756926326.8623507, blob_last_modified=1756926328.7393425), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417727/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/414c702fdf3dd97c536f3237d2c38e0ef5ad8d08bb7c991ee252e16d57b201b6'), size_on_disk=2636905, blob_last_accessed=1756926325.0683584, blob_last_modified=1756926325.8093553), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380960/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/60771297311bf96c3213c2a96321062a06067c91d2bba2fce69a7a6d7a0c980e'), size_on_disk=560607, blob_last_accessed=1756926369.7811644, blob_last_modified=1756926370.3121622), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417612/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ee40f7af39e729ee79ef18d6777bbd632b7cc7811041a32d54f4a02b42638464'), size_on_disk=3509045, blob_last_accessed=1756926301.5874608, blob_last_modified=1756926302.6274562), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417881/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bf380975f31e362e8964000688bff364809bbe2e8e3efa9ceaa71253ca6beefb'), size_on_disk=8717095, blob_last_accessed=1756926351.848242, blob_last_modified=1756926352.9732373), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380988/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/27b6e71986cf2b7da20b64dbda0ba47e08753f7184ddd57ed288f11df88d21e1'), size_on_disk=16063760, blob_last_accessed=1756926373.7421472, blob_last_modified=1756926374.932142), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381030/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e5baa8386faa327b26d27d6c8e6b265dddac1d55565e81d724a1980b93d871a3'), size_on_disk=1867415, blob_last_accessed=1756926379.2021236, blob_last_modified=1756926379.6681216), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417714/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d6abeb724a8c8b23217f3465284564da5baffee59ee4e3af582e0a3705ae32c6'), size_on_disk=262443, blob_last_accessed=1756926323.756364, blob_last_modified=1756926324.256362), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417744/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/faf407f0b251cf5f58c8ac7becee4b995327fd9a7d6599c3045fde8a78e1e811'), size_on_disk=2548979, blob_last_accessed=1756926327.7843466, blob_last_modified=1756926328.7443423), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380968/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/549ef4e39a437adb99efecdc9b05fee3ca4b31c878b99f7a4cf1398f405ca049'), size_on_disk=3575926, blob_last_accessed=1756926370.7441602, blob_last_modified=1756926371.5941565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417832/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/268a3b7a91ccc3f3909042a711454c336b1887705de99442bda022bcbc26a01e'), size_on_disk=7655304, blob_last_accessed=1756926343.2332795, blob_last_modified=1756926344.3042748), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380948/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6ac219aa7f5be77c21bde60c80ccad59edf825e2a009d6a4b7fc8bd4d8be83d4'), size_on_disk=6046323, blob_last_accessed=1756926368.6691692, blob_last_modified=1756926369.5471654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380941/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9a23f03fee8d60d752bd0bc1e59e9205abea4f3a78e3272b9a03447f076383d5'), size_on_disk=3514837, blob_last_accessed=1756926366.9851766, blob_last_modified=1756926368.7591689), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417851/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2d8a571fae50598143744a1f13d40cec2ea8c2198e87aae17f96d7c8a00460d0'), size_on_disk=11051527, blob_last_accessed=1756926346.6472647, blob_last_modified=1756926347.6582603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417783/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9603c95097c451e616c54d52d3474b866453409905ea16365d019e77c6ca31b3'), size_on_disk=4176778, blob_last_accessed=1756926333.6983209, blob_last_modified=1756926334.4773176), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417835/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6245fc8d220aad556115d4b831f72ae6a9beac48ed6e2aba811284aa0ccab4f7'), size_on_disk=7888215, blob_last_accessed=1756926343.795277, blob_last_modified=1756926344.942272), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380970/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bdec4a51127eaf09abb5d25d7f3a732afc3b561105fd18ba1df44d04e6dd90a2'), size_on_disk=3545790, blob_last_accessed=1756926370.9301593, blob_last_modified=1756926371.5451567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417811/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/da92b1c1579a2295da085050305367bd02a47fd52a74d1ec2a9f227b5e808b11'), size_on_disk=6127159, blob_last_accessed=1756926338.8782983, blob_last_modified=1756926340.1172931), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380992/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dbc6b1de202e7fb25701c02607facc3e8c332550907ca0b70ba177533a311d6d'), size_on_disk=2551903, blob_last_accessed=1756926374.2281451, blob_last_modified=1756926375.0341415), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417773/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d7980069a740f581dbcfd9416372452203d636d8ad4c9352c3b2d6483cd27379'), size_on_disk=5178664, blob_last_accessed=1756926332.1683276, blob_last_modified=1756926333.411322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380969/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/10410488c74f35a493cef2ffc4a64b28b336be1fb32a2b6dac692e0d7fbd4271'), size_on_disk=1973405, blob_last_accessed=1756926371.041159, blob_last_modified=1756926372.1191542), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381053/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6ca1dc55841bbb6d8d8a83d1a87d00570c1eea7cf1963db8cb04ddd427711fb6'), size_on_disk=10277654, blob_last_accessed=1756926777.9197485, blob_last_modified=1756926779.9187374), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380965/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6b384b53be751cec9056e74c2c8ce9fe2e532cfbf6a38e43bb09ce74ec817f3a'), size_on_disk=12974487, blob_last_accessed=1756926370.3761618, blob_last_modified=1756926371.4361572), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417838/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0dcd4dc268902c8e0845a1ee81938a81a00412014d5b198a41a523cc3450dfc4'), size_on_disk=10178872, blob_last_accessed=1756926344.5452738, blob_last_modified=1756926345.3902702), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381042/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/80a0c18e8d146043e9c95c6a86552d28e28267a489b3e5c6f2a11319cf47b877'), size_on_disk=4477936, blob_last_accessed=1756926775.660761, blob_last_modified=1756926776.9177542), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417765/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bd2b5a1b1593db002ea9c159d4c40d1d8addddbf124207a7b32b0bb43e2e4d72'), size_on_disk=2696437, blob_last_accessed=1756926331.2103317, blob_last_modified=1756926332.046328), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417647/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8c01c928abe00b5afa6a2292820695797391b3671cc110b9026f5fc5cb8564ae'), size_on_disk=2891716, blob_last_accessed=1756926310.1614234, blob_last_modified=1756926310.93942), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417747/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7011e7ff4b64ca9c57bdab93cfcfc52938e7b0574b48417feb4f6185f6c118b9'), size_on_disk=4791771, blob_last_accessed=1756926328.7593424, blob_last_modified=1756926329.9543371), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381069/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aa8f6186814bf973f8b4448a0c4b692c31594e03905c75d5691b982d6ccde97d'), size_on_disk=13705106, blob_last_accessed=1756926780.5807338, blob_last_modified=1756926782.319724), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380940/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4f59a7f9287c427bed27910f08a041e460eff2490c21e2fbdaa5cc9348f5921e'), size_on_disk=6062382, blob_last_accessed=1756926366.8141773, blob_last_modified=1756926368.3051708), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417809/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/486100d34df42ce97476d9753f8d57edb5f53596eaeca2d0cf81f5f95a4f4e12'), size_on_disk=7376702, blob_last_accessed=1756926338.5872996, blob_last_modified=1756926339.9172938), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417891/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cabef83ee373e37d528b127d813f1535faede4b98c5524d83e9906df83558b07'), size_on_disk=12596609, blob_last_accessed=1756926353.9052331, blob_last_modified=1756926355.2992272), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417904/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cf9194917da2f4c6c3f0411256b2604e8c14959373d7431047de2e23cffbb9bf'), size_on_disk=7269877, blob_last_accessed=1756926356.062224, blob_last_modified=1756926358.1762147), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417908/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/128e54326b088977000f8caaba7d09e324281ed7fe0e3e5f2779783f0a82f293'), size_on_disk=3614805, blob_last_accessed=1756926357.1272192, blob_last_modified=1756926358.2542143), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381034/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/112a8e705f7536fa06c630165d3c409f87a5784ad626b891cc2b3919e24bdd14'), size_on_disk=250813, blob_last_accessed=1756926379.795121, blob_last_modified=1756926380.1791193), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417866/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b0424cd7567723443472cb266e090a30e2b64ee6b09bb1b94d8dcfdb9f0b0c43'), size_on_disk=9685580, blob_last_accessed=1756926349.109254, blob_last_modified=1756926350.493248), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417860/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/72e751d246e9b938ce9ab4380e2a1114ecd3c36d5829e3298f391c1edfefae24'), size_on_disk=3996315, blob_last_accessed=1756926347.9312592, blob_last_modified=1756926348.819255), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417729/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8383a0e86c2bfe15c46045980d6768eb4e7e3c4c157ff1ae7ce20144e245ca80'), size_on_disk=3058876, blob_last_accessed=1756926325.3113573, blob_last_modified=1756926326.103354), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417805/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/661f37ed294c22f6c3b02d5c1ea25794f5e33090c246185e891ee82153e2e5f2'), size_on_disk=3071166, blob_last_accessed=1756926337.8643029, blob_last_modified=1756926339.3292964), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417877/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b170370a220db5677b9c6193707d32d1d14b9082ca574b9562c4ac8ea4c72c26'), size_on_disk=10449263, blob_last_accessed=1756926351.0392456, blob_last_modified=1756926352.32624), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417924/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f2053fcaa7f21b1703e3491e167b457c29759b2fe9535f0cb2bfa257a1b86cea'), size_on_disk=6988370, blob_last_accessed=1756926359.940207, blob_last_modified=1756926360.8152032), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417905/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b9d291469b7a414bab157f45bfd3a1f7ffaaf931de25fec9eb18012a18b70939'), size_on_disk=4418829, blob_last_accessed=1756926356.1612234, blob_last_modified=1756926357.2652185), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380998/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6808b8e1fa74b807321826b2c54182a3b6e257f0a223be0a6a94caf9463959e9'), size_on_disk=16847111, blob_last_accessed=1756926375.2231407, blob_last_modified=1756926376.572135), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417932/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f040d4f594fe69143a7c3f04f0498be2a30fb9b5e78d42ec464fee65efcc42dd'), size_on_disk=9231914, blob_last_accessed=1756926361.4262006, blob_last_modified=1756926362.5771956), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417636/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4a3b2c7076107dcc47a87179cecab9e2051350c94e951a7aadc2ff0cf6fdbaca'), size_on_disk=43479381, blob_last_accessed=1756926307.6494343, blob_last_modified=1756926311.4484177), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381067/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/da9c5ab3c0f970f9c2af4c0f5f0e9a95d47769e8096ff6b9d76092becbc8517e'), size_on_disk=9837433, blob_last_accessed=1756926780.2977352, blob_last_modified=1756926781.5777283), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417754/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0d840353f1148a08928dfb3177a0abfe635c406fc77d6d9958743585ade6b872'), size_on_disk=1101891, blob_last_accessed=1756926329.758338, blob_last_modified=1756926330.215336), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381039/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5f95b6de8d0d73589c612313eaed6ce1e6adf734e795719f23df6f54cbbedc69'), size_on_disk=1656495, blob_last_accessed=1756926379.8751206, blob_last_modified=1756926380.283119), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380984/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a15ab5e5a0ead07dc2a530a1e4abb714027a95ce090c7a0eda5ec2bb714d903f'), size_on_disk=8070704, blob_last_accessed=1756926373.2251494, blob_last_modified=1756926374.1601455), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417624/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/28c1de2a4bd6b421cd05c3bb211ffde83e4b830127f106ec7be42ad288e26ef6'), size_on_disk=16758887, blob_last_accessed=1756926303.7884512, blob_last_modified=1756926305.6184433), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417703/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e82b21d919b84770e2d141eaea09aff82ef57c3ff3dbd15c588c1b8cff212c9e'), size_on_disk=1462841, blob_last_accessed=1756926322.0533717, blob_last_modified=1756926322.898368), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380995/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e0cae334f1cb71be0be1e8ceb5fc6ad23d22e4db20cb64b1314c3d080f2bb5f7'), size_on_disk=6315860, blob_last_accessed=1756926374.931142, blob_last_modified=1756926375.7271385), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417787/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/17f33e06b6bd77c0bbd3141b53fc2d9550b0c54e99b18fe79ddad931e3a283a9'), size_on_disk=12355842, blob_last_accessed=1756926334.5003173, blob_last_modified=1756926336.2953095), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417672/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f0741c46f1460c30d4d9b88ca1f7c0ca77a040cf25a34d82fab4c777a921ac26'), size_on_disk=18268758, blob_last_accessed=1756926314.9764023, blob_last_modified=1756926316.5143957), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417936/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8ba89583b8c822b64968df9eca8273d12270429f0f820d6027f13c66504c383d'), size_on_disk=4552381, blob_last_accessed=1756926362.205197, blob_last_modified=1756926363.171193), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380956/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/15047f69259f2cbc2cb08949d07ac6fde505bade6a0e3bb3912b183a48a4b288'), size_on_disk=2282680, blob_last_accessed=1756926369.4771657, blob_last_modified=1756926370.1431627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/225bf60f7c9796682210dc48d07a30d3dabab8ebc13abb7a84834279fa131020'), size_on_disk=5307353, blob_last_accessed=1756926376.2401364, blob_last_modified=1756926377.3751316), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417645/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/761eba472193b23cbf7735946e08aa1c4db5b1abd5125f1aad22ddabbf872f04'), size_on_disk=2080044, blob_last_accessed=1756926309.765425, blob_last_modified=1756926311.5084174), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417712/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/59cbfa83b9abe13f60d6a80b35afe857e3ffd761366133f60505312a2b5a72e2'), size_on_disk=641243, blob_last_accessed=1756926323.2163665, blob_last_modified=1756926323.7173643), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417923/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a32971eb62fe5c4c08298b1bdd97a916d8ef8f5898aee42aaf3fbaa8069e92d1'), size_on_disk=10004280, blob_last_accessed=1756926359.919207, blob_last_modified=1756926362.0851977), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417693/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/98a5478f5bd4feb033ac69510f2bf785c87777377b7b8b6dce24b8f89dbd6a03'), size_on_disk=18580692, blob_last_accessed=1756926319.7013817, blob_last_modified=1756926321.9783719), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417812/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9e8445f413d26051897f0fe3e7566b116a9a55062b78201990de334f17aecb31'), size_on_disk=19905089, blob_last_accessed=1756926339.0272977, blob_last_modified=1756926340.6232908), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417755/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f4ffef985aed69a80417b5179ef9923bd73378a93a97f2dff128b1f7716b4599'), size_on_disk=8437424, blob_last_accessed=1756926329.777338, blob_last_modified=1756926331.2423315), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417772/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a21b204a2da7654dd5bf4e7965a3c1e19159c16052232030787175a830ff0233'), size_on_disk=8204405, blob_last_accessed=1756926332.1323278, blob_last_modified=1756926333.453322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381052/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/652334151cb539b888d9cfcb60d9711d80bd74ec65cfa3799de10b2970ab9c09'), size_on_disk=5788579, blob_last_accessed=1756926777.7767494, blob_last_modified=1756926779.283741), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380999/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/acb2ee4711dab5800afb7ccbea72c8c80268b0a848723e7bd8ecf197decb982d'), size_on_disk=2605563, blob_last_accessed=1756926375.2281408, blob_last_modified=1756926376.1321368), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417751/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9a5733130de69115a731511dac8bf577fc5609a7f9649106ae9a12ffae7011ba'), size_on_disk=1660869, blob_last_accessed=1756926329.2023404, blob_last_modified=1756926329.9043374), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380959/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4693c1876e880fee1ec277fd5ae9127aafa9f40d41a3d5ed482dfcfa8cef55f1'), size_on_disk=873902, blob_last_accessed=1756926369.7061646, blob_last_modified=1756926370.1731627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417749/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8460c24102627d611c8918a1620078ee1bdf9929c44cdca863cd0dbb23f9cf1f'), size_on_disk=4938551, blob_last_accessed=1756926328.814342, blob_last_modified=1756926329.7143383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380957/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5a2362d35ac90fb1926051e6313886a649074b0022b128741a22a5ee24bb095d'), size_on_disk=5388789, blob_last_accessed=1756926369.5311654, blob_last_modified=1756926370.8571596), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417874/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6674c316eab3b03bbbe3502565efff7650445f5e1453933336d74938e42a14bf'), size_on_disk=6201897, blob_last_accessed=1756926350.5772476, blob_last_modified=1756926351.5142436), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417792/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/196cd0cf279f6bc85ca0dbeb1f089721f63eb167644d999b45d8cfe9a1f52acf'), size_on_disk=11982413, blob_last_accessed=1756926335.4593132, blob_last_modified=1756926336.651308), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417778/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/54e8ea03e214967eb4a1c7b50a086c4d7083d2c9c95dd0419e545fb0e8f18422'), size_on_disk=9345257, blob_last_accessed=1756926333.1513233, blob_last_modified=1756926334.0903192), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417933/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4fe347d88754646d39d93dd8a78e857d04d1903f7163a21495038b087d5f6b8a'), size_on_disk=12389872, blob_last_accessed=1756926361.7481992, blob_last_modified=1756926362.9061942), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417726/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/739e474fc54ffee0c7e6300d3abb1f83d33d48c78d137ed848f837fa186b9308'), size_on_disk=2430088, blob_last_accessed=1756926325.0663583, blob_last_modified=1756926325.6803558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380978/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a1d811dc3a4bfd44683ddf8bd0401a71d4941497c5cac8d8040a597fc5d26587'), size_on_disk=5902739, blob_last_accessed=1756926372.160154, blob_last_modified=1756926373.09715), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417878/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d3d105cc45280a5ea88c2bba754c0c5849d0cec7112a0926e9ed0c946e7f5cfd'), size_on_disk=8838404, blob_last_accessed=1756926351.2492447, blob_last_modified=1756926352.7352383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380993/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d568d29486ad3ec6f67f87b78cacb8b7102c591f979ec2bdb17ff75bde789670'), size_on_disk=3031778, blob_last_accessed=1756926374.496144, blob_last_modified=1756926375.2661407), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417892/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7ac6b85018422c74c894467b7b53b1c2dc7c5298fc31654bdea5ca70493e0a87'), size_on_disk=8194725, blob_last_accessed=1756926354.176232, blob_last_modified=1756926354.9712286), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417798/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0ac62fa7573f4b3c756fd9c280be4b97ec0c7c756e8c76307d4fab56d29e1c08'), size_on_disk=10509245, blob_last_accessed=1756926336.5193086, blob_last_modified=1756926337.800303), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417873/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d282d59c3c191bd7b7fa7251108224d0ac3929bc98f96a2c43f283f8a5cd046a'), size_on_disk=7609540, blob_last_accessed=1756926350.3852484, blob_last_modified=1756926351.373244), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417770/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0e7437c90c616b78ccc0963e0388385028b70d66fbe73e0ceb4e30e2d0239816'), size_on_disk=8252889, blob_last_accessed=1756926331.6563299, blob_last_modified=1756926333.0863235), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417603/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdfb1fd9b21341104e6423c684b0ae8a52ed1701eb3e232babb98340f953ea5b'), size_on_disk=4460026, blob_last_accessed=1756926300.366466, blob_last_modified=1756926301.2574623), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417795/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b995a48f28ae3baa145438a39c9fed73fa8689eb31e07ff02a3bc6bc827eb76e'), size_on_disk=8032882, blob_last_accessed=1756926336.1053104, blob_last_modified=1756926336.9993064), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381014/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5c5106dfb24bcfb1436aeb87b8218df873417ca2809aed71ca58e9b48ffacd29'), size_on_disk=1425306, blob_last_accessed=1756926377.5811305, blob_last_modified=1756926378.2571278), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417837/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/04d099809ca78bd87dcb9921398681ac64e827439049a37e97a71ba0828debfb'), size_on_disk=8348990, blob_last_accessed=1756926344.4012744, blob_last_modified=1756926345.6152692), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417785/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/209799965b03585e1143e881d87c5cea80d37e06637d2fc3f7e1ad2eaadfd3bc'), size_on_disk=11557515, blob_last_accessed=1756926334.1813188, blob_last_modified=1756926335.1353147), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417669/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8eaf08a45dbd72787c5250403f3d3079db9902e77527c7ca438abc740fdfb753'), size_on_disk=7376929, blob_last_accessed=1756926314.6724036, blob_last_modified=1756926316.0383978), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417733/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/542261ed8bf189205b611485f1a878f28cae5c90e452ba9cad687eacfdfc2814'), size_on_disk=2645461, blob_last_accessed=1756926325.8883548, blob_last_modified=1756926327.3873484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417819/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a1ba6808d672dbc9c58c26364c5cd9ed0b8a3041e6714844846648d3cd1aa050'), size_on_disk=3244517, blob_last_accessed=1756926340.7022905, blob_last_modified=1756926341.6952863), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417857/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/64f710e0b8a62bb6eddf3508f133b6b82af8bd21dd07f2319af6d07981085b1e'), size_on_disk=11109477, blob_last_accessed=1756926347.471261, blob_last_modified=1756926349.6202517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417841/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cb829be078dd8b533a90c2c548dfb2ce0455026be54c36572337e0a1ddeeb2cf'), size_on_disk=5791736, blob_last_accessed=1756926345.138271, blob_last_modified=1756926345.9902675), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417925/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8cdb783613764a35fd3a73db89463db6876951d443f5651a9a46877a53917f3f'), size_on_disk=912848, blob_last_accessed=1756926360.409205, blob_last_modified=1756926361.1532018), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417916/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2abad8555b89fbb5009e8f2a95e553a5ce579d1841ae4e71eec7b137f3282ef1'), size_on_disk=9236466, blob_last_accessed=1756926358.371214, blob_last_modified=1756926359.0582108), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417850/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c08fca103aa3dd1944dcf5c73b67397263842dc687b4101c8ab2c1a63d50205a'), size_on_disk=1182282, blob_last_accessed=1756926346.5122652, blob_last_modified=1756926347.3082619), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417635/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d2d1c241467bd5c55cf059f793b4236c99d48e59bec853d1052ff6c644e50d1b'), size_on_disk=3044373, blob_last_accessed=1756926307.2014363, blob_last_modified=1756926308.391431), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417606/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f76c81d98337c9e127bf5516af0dd135458fd622c01f2b5c9fd85ce6edfa4c7f'), size_on_disk=9502783, blob_last_accessed=1756926300.5794652, blob_last_modified=1756926301.6394606), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417653/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/59233342d3c3b71fce95d5295cf7c1e5ef3f4254dbc120229cb83b6c8115c759'), size_on_disk=12208357, blob_last_accessed=1756926311.136419, blob_last_modified=1756926312.3904135), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417629/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/29c9c86be94792d797005b106ab00d066634bea44fbf17e81738a00cbffa264b'), size_on_disk=22750780, blob_last_accessed=1756926305.5594435, blob_last_modified=1756926307.5804346), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381066/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ae017c72cf3b91ea3fdd40f63a126f640e7049340baca502ff3228c6ee40a7b4'), size_on_disk=13139272, blob_last_accessed=1756926780.000737, blob_last_modified=1756926782.6217225), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417902/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2d72fc510cd348af7611ca170b7804b228b74bd38e7ff5e437375c1cc926fa95'), size_on_disk=8952511, blob_last_accessed=1756926355.9322243, blob_last_modified=1756926357.2422187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380991/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dd86db1779581d5853d4d6a0fa2aeacdb00f92c832683ad788743f17db625db4'), size_on_disk=2640507, blob_last_accessed=1756926374.030146, blob_last_modified=1756926374.8661423), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381035/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/410edf5a6097a77bcec6880ec2618b4aec4bc10c44607163fe2b27aca0a05117'), size_on_disk=1451911, blob_last_accessed=1756926379.8691208, blob_last_modified=1756926380.5191178), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417657/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62398c518588ad11a939f95736cf0ed01a39890567cd4a8f05e9c9ba1155f9c1'), size_on_disk=10144285, blob_last_accessed=1756926311.5634172, blob_last_modified=1756926313.1174104), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417762/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c8b34d14be6dd44af03432d758f705cf81e86a46831c6deb4b0944248709630a'), size_on_disk=3024339, blob_last_accessed=1756926330.943333, blob_last_modified=1756926331.59933), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417633/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0b38824aeb2fac57eca256e3652539deb4bc221eeb5afc3701a997885f9ad9d0'), size_on_disk=19311587, blob_last_accessed=1756926306.8324378, blob_last_modified=1756926308.847429), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381041/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a53caa1961d9245d82db10989b8a8d601760ccf26f67666d5558937830c51e31'), size_on_disk=6621865, blob_last_accessed=1756926774.784766, blob_last_modified=1756926775.9747593), CachedFileInfo(file_name='GSE209631_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE209631_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/be49eef90682fbd55a5e60ada8b4c1c554e1b5a2ec1209996269db020d3bb074'), size_on_disk=4258, blob_last_accessed=1756926300.173467, blob_last_modified=1756926300.594465), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417697/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/198a3cd31740f347f0308db2914df4fc0fc303b4a9f040fbaf79fb5ca0170e7f'), size_on_disk=5521438, blob_last_accessed=1756926321.4153743, blob_last_modified=1756926322.4743698), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417611/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c82d882f6538d65316dd01e26a82af13e41717952c79e20dae6c3f1d9724ab60'), size_on_disk=3720891, blob_last_accessed=1756926301.4614613, blob_last_modified=1756926302.2704577), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417605/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f689b77eccd8d95d70fb616f4e2aa03a41201aec2a8c3c86849b7b145cb0f796'), size_on_disk=1787545, blob_last_accessed=1756926300.5184655, blob_last_modified=1756926301.74346), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417673/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/535d074f70f4553e24f3171fe5cfbd1821635ce7ca6ef93f8665876de620d6c9'), size_on_disk=6559693, blob_last_accessed=1756926315.1844015, blob_last_modified=1756926317.0733933), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381031/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b4705ac4577c8e7b116d1a1b677967f69412b0e4716e0de7fa7ee97bc9be4d74'), size_on_disk=4551217, blob_last_accessed=1756926379.2931232, blob_last_modified=1756926776.2707577), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417780/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4f1b7c3ded6441c8e906c3494ae63750d25303ee6f8d8e05120dc11ce8d79080'), size_on_disk=9025914, blob_last_accessed=1756926333.2923226, blob_last_modified=1756926334.387318), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417911/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3ca8b1b14564fbc90b44995a61a34d5a67482eb5fd867ce4f9f2196de2b73539'), size_on_disk=3051177, blob_last_accessed=1756926357.404218, blob_last_modified=1756926358.3022141), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417883/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8e23cc97e00adfd244a989f83702f4ce7a998ba7998dd95908a1b4ec256bc054'), size_on_disk=10709227, blob_last_accessed=1756926352.4542394, blob_last_modified=1756926353.675234), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417664/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/91fc5dce8497d1fc5466fb5ac31e44a20b295d8f97825500f256f6830ea2f983'), size_on_disk=8203399, blob_last_accessed=1756926313.1894102, blob_last_modified=1756926314.6624038), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381021/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3e80794742d10b6d120e70c4e4f1d7f77123ddf8aa18499f686b4abc8eadd325'), size_on_disk=1524611, blob_last_accessed=1756926378.410127, blob_last_modified=1756926378.897125), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417621/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/354c654ace9bc123bcd9009c56af518b98f1a547a7ccd79f348d0e533607a41e'), size_on_disk=20457307, blob_last_accessed=1756926302.5744565, blob_last_modified=1756926307.7874336), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380975/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ea018f4882a60f5fb07a4bbae51bd494e44e6df9b215d9da5e405687a567dcbf'), size_on_disk=8369150, blob_last_accessed=1756926371.6051564, blob_last_modified=1756926372.7471516), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380939/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dc6382d9dba7855504a6c7fa2a4adc44f564e25ff5c54eedd082c3cbe0a520a0'), size_on_disk=9235430, blob_last_accessed=1756926366.630178, blob_last_modified=1756926367.5861738), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417781/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9dd7989f90f8762246868ccd7d11c9ce876b2b33099efc3a4c5bf1bc7cbcca80'), size_on_disk=6923944, blob_last_accessed=1756926333.4783218, blob_last_modified=1756926335.3743136), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417784/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fd3dd53cf34f48c235e4f771378f42a5605446bbc62d108a5637a042513fd75d'), size_on_disk=4826642, blob_last_accessed=1756926333.7233207, blob_last_modified=1756926334.576317), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417919/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f5504428a85399cb212c74235abf9cec068dba03136a87ea79eae6d63a3cf430'), size_on_disk=7011211, blob_last_accessed=1756926358.6892123, blob_last_modified=1756926359.8312075), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417927/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8e639025209cff8d34b7e35a5fa371282bb725173225097a39f8f2101a49deed'), size_on_disk=7241057, blob_last_accessed=1756926360.6762037, blob_last_modified=1756926362.1171975), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381000/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/503f854145f3a220aa936072c3e66410d34914fef948992399a4addbfc977186'), size_on_disk=3904727, blob_last_accessed=1756926375.3401403, blob_last_modified=1756926376.5601351), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417748/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/db4c67c001a1f688f70918f0e25d3ddd996392b33b9f1144a14551bc0026d036'), size_on_disk=3246481, blob_last_accessed=1756926328.8123422, blob_last_modified=1756926329.6943383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381056/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/382194ca9758ddc18f059a649b13c5a21ead3a7f1fa5e012065315047a037a58'), size_on_disk=2351268, blob_last_accessed=1756926778.362746, blob_last_modified=1756926778.9827425), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381057/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4e6c617d22a655d32847df8b5334f4fb7236a15b8bf6e30aaabcbb0a4c55782f'), size_on_disk=17494867, blob_last_accessed=1756926778.5417452, blob_last_modified=1756926780.4997342), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417885/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6d5faeda491268a8ec88393883d743aac6cd14d4837698164ca32ca1052da36f'), size_on_disk=7668243, blob_last_accessed=1756926352.806238, blob_last_modified=1756926354.1422322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417661/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c6effb515a20066646b7863cb86e9dc012e14187e432ba7458cd6af18662bfd6'), size_on_disk=22122748, blob_last_accessed=1756926312.9054115, blob_last_modified=1756926315.6813993), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417699/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0c64ecbe6b245299fe04c04239d792fbae0f1b288562fe0c7912a24de56f0ad5'), size_on_disk=5707915, blob_last_accessed=1756926321.6203735, blob_last_modified=1756926322.8483682), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417816/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/86256a1b98c6d44750fd21ac9ea099262f1f0ec03d182b65741d669fb80f289e'), size_on_disk=10618044, blob_last_accessed=1756926339.7852945, blob_last_modified=1756926341.8372855), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417679/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bc48687cfd5c03c1a71787981180295dd3a27cf4d2c46bd26bb68cdf8b35925a'), size_on_disk=17346576, blob_last_accessed=1756926316.1203973, blob_last_modified=1756926317.84439), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417685/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/32edfbf9226b7ef1060ebe3e6986a1803d60408ccda969528cf4a59c5785c722'), size_on_disk=16967633, blob_last_accessed=1756926317.9163895, blob_last_modified=1756926319.5263824), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417852/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/43431a444b2761ed9b6fa4c7e6edfc8da31b64ff933408c517bc771e9f3e94dc'), size_on_disk=6967416, blob_last_accessed=1756926346.8272638, blob_last_modified=1756926347.8462594), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756926300.2174668, blob_last_modified=1756926300.2804666), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417842/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c763016dcbf1b53df840e3ea3c1b45234e2d9e797114000efaf692e2c394d487'), size_on_disk=9967694, blob_last_accessed=1756926345.1472712, blob_last_modified=1756926347.1812623), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417610/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a93c708ade83c1c4521589a51835a1dcc98972ab900e0eefdb8ce34e64394bf4'), size_on_disk=2566580, blob_last_accessed=1756926301.328462, blob_last_modified=1756926302.4814568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417831/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/53d810292e5a513a8ab543fe18783f8d493cedbadc7c97400dc103a4675954df'), size_on_disk=6767934, blob_last_accessed=1756926343.12528, blob_last_modified=1756926344.4612741), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417723/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f397cef2d9c491fb034826727c63b3c224f6ed791c9b4dcf884c63c55c9961c'), size_on_disk=2800893, blob_last_accessed=1756926324.70836, blob_last_modified=1756926325.4453568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417769/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e640018be0b2de04e0ed77c7586c413990876e91348531f43511c20da5fe279a'), size_on_disk=11568261, blob_last_accessed=1756926331.59533, blob_last_modified=1756926333.0913236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417705/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/92b4f5af6793c98b854bcca83c86ac0cb41ec4e40ec23d3ba9c4ff287852c63c'), size_on_disk=332184, blob_last_accessed=1756926322.5923693, blob_last_modified=1756926323.1503668), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417691/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/48de5a2968610e5ab855522d27f535d40e1973c1835ec32fcfa5bcd91a0795dc'), size_on_disk=14481663, blob_last_accessed=1756926319.447383, blob_last_modified=1756926321.2093751), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417948/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d43ea802a452aa9d57004381e1688222e61bd313b107077c1c663555547d1d44'), size_on_disk=1748124, blob_last_accessed=1756926364.3661878, blob_last_modified=1756926365.038185), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417646/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2b3d78fc052de8647b1ba6e83245b5f375b4e6c10952feff795855160458a1d5'), size_on_disk=396843, blob_last_accessed=1756926310.1544235, blob_last_modified=1756926311.0694194), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381050/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f3c3bac37a1d4ec633edf53ba305c970eb3de412fd54dfbaf0cdf89f39574334'), size_on_disk=1518412, blob_last_accessed=1756926777.1687527, blob_last_modified=1756926778.028748), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417728/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2be09708238c50a31b4e9b334c6202c26eaa6a950ebf3de4287651999d45097e'), size_on_disk=3385901, blob_last_accessed=1756926325.0603585, blob_last_modified=1756926325.869355), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4c4a265f9e3e8d373550ea58ca1cde6094c823f090336974fde3e7cee117fd99'), size_on_disk=1770007, blob_last_accessed=1756926376.6771345, blob_last_modified=1756926377.1011326), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417875/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8dc3634dae23cad7f562d15903341a67d3048929bd2198e53f71529b00a3508a'), size_on_disk=11490812, blob_last_accessed=1756926350.6762471, blob_last_modified=1756926351.7832425), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417775/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2ca9ba5e1394191206945f41168d555519fe3963d4c39f251c1ead57f2a323f4'), size_on_disk=1059891, blob_last_accessed=1756926332.3723266, blob_last_modified=1756926332.773325), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417803/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1fd8b542fb1b0e18bc5346c50ad85da440ff6190a987467ec6876ed3ec51a4e8'), size_on_disk=4928668, blob_last_accessed=1756926337.5433042, blob_last_modified=1756926338.7182992), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417741/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/734d5947cb26e877b6ae9e9adaf84575974a4fe2fb464ba11c4320906f17a42a'), size_on_disk=4566889, blob_last_accessed=1756926326.96635, blob_last_modified=1756926328.5653431), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381038/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/415f62b8333a02df8a87ba85e1d0de30a6cc9fdb07f7c8dc02d30f81ed4c14ef'), size_on_disk=2424198, blob_last_accessed=1756926379.8751206, blob_last_modified=1756926380.4921181), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380967/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3abcaaa4db306d580dc27882172ced53d591b8aa69a1805bd2a9eea0448ff941'), size_on_disk=1245882, blob_last_accessed=1756926370.6731606, blob_last_modified=1756926371.1021585), CachedFileInfo(file_name='GSE178430_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE178430_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/be2c4af55a502d7fa22abb28afe6db347950327bde1bc7e97dbb68fdb0c59f3d'), size_on_disk=9398, blob_last_accessed=1756926861.9192877, blob_last_modified=1756926300.5344653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417682/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/83b3100cfd1dc6918855c4a711ff607b36f40971655733855a7df353b4d5fca5'), size_on_disk=14037100, blob_last_accessed=1756926317.1663928, blob_last_modified=1756926319.1013844), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417822/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62cbbb0172158a2c0776000136424032a794757ca9e9f971e5f445deb20e8579'), size_on_disk=6040431, blob_last_accessed=1756926341.3882875, blob_last_modified=1756926342.8302813), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417626/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1981a2a1e321acc5c35bbeb9b61a12636c27f28d6e216de20093afdbee9f8689'), size_on_disk=12046611, blob_last_accessed=1756926304.4464483, blob_last_modified=1756926306.2084405), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380961/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fc4356aaa2a30217145b4aecc4f3fdd2575fcd696a2603fea3d8112998f770c7'), size_on_disk=798599, blob_last_accessed=1756926370.1791627, blob_last_modified=1756926370.589161), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380953/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5b3410430d33f3123b935982be90037f2d0a8e657f6b0b70a7042846d5ddd1b0'), size_on_disk=1192961, blob_last_accessed=1756926368.8401685, blob_last_modified=1756926369.627165), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381071/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cf15be48b47b6c369c6a4290b783b9b22b01d6cb89d04c738c275241548b811b'), size_on_disk=2543373, blob_last_accessed=1756926781.2297301, blob_last_modified=1756926782.309724), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417623/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/562a43ff381a2ca177fb1cc3dbe47fc2cf7cade495bad511ca7ed75aefca7a98'), size_on_disk=19049552, blob_last_accessed=1756926302.696456, blob_last_modified=1756926305.4934437), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417715/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ac5effc2ec07a8fb49bac4d77b7d4129a18ae2180807874ea02d1c252a3b68bd'), size_on_disk=520286, blob_last_accessed=1756926323.8693635, blob_last_modified=1756926324.477361), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417859/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e4c5915df6cb5bb450e1948d8c43bbe031d09ec345688ab51f2d8ea60f3be09f'), size_on_disk=12709203, blob_last_accessed=1756926347.8872592, blob_last_modified=1756926348.9972544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417937/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e432985b09ba72eab54fc008b87b20a718dbae96d920a61b47de7e16d214227c'), size_on_disk=6445266, blob_last_accessed=1756926362.6341953, blob_last_modified=1756926363.6641908), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417763/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/91d8a58ead35fc52f7c2649ce9509a92236a20ca8c26f5f2ef4ba1a38e64afb1'), size_on_disk=230495, blob_last_accessed=1756926330.8943331, blob_last_modified=1756926331.4653306), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380954/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/338886924ec42501a99a4b842e18fdbed28200d52daaf43e00ee601a147e0188'), size_on_disk=4903852, blob_last_accessed=1756926369.2841666, blob_last_modified=1756926370.101163), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417707/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/89192f5007f730a90c823219ea107c799935f5e824963090abad813603160fa8'), size_on_disk=2615872, blob_last_accessed=1756926322.9183679, blob_last_modified=1756926323.9473634), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417622/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/28eed3b67a6206b453d8adb775edf2bea32cfd54ba5b37d51787f67280e0317f'), size_on_disk=20411775, blob_last_accessed=1756926302.674456, blob_last_modified=1756926304.8564465), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417758/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/54f8e8d84e5b8b9457aaa143829fcfd5a4715d0fb184478ce659cd6c6efe7be1'), size_on_disk=4176901, blob_last_accessed=1756926330.0203369, blob_last_modified=1756926331.1383321), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417939/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a0fc7804b026f95809f33087deb75f00beeb9635475b62864d3e58e948361bfa'), size_on_disk=9099632, blob_last_accessed=1756926362.9701939, blob_last_modified=1756926363.9411898), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417658/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b6cf4c024743be14fcd5730f0ddbb960dbcd83d07adf8668639f9a1cdf195368'), size_on_disk=8754108, blob_last_accessed=1756926311.5764172, blob_last_modified=1756926313.1134105), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417829/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/63a9af789279180a07d8e8afe07bb0e9b8aa32c4340e01347a6f7ee809198308'), size_on_disk=5677879, blob_last_accessed=1756926342.904281, blob_last_modified=1756926343.7332773), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417721/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5457a24eab864fb369ae0e6fbdb302876e2a6c65ae83444f18bc01ad08ecfc0c'), size_on_disk=2073045, blob_last_accessed=1756926324.3243616, blob_last_modified=1756926324.9983587), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417759/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b2dad4cf4b03c846adf9d5229aeab340e58034c751bc68b37bb0c8402a7c071b'), size_on_disk=1372312, blob_last_accessed=1756926330.1243365, blob_last_modified=1756926331.1623318), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417782/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d6624acaa7c4ec21ae02d6d1322b8669a148308095e714b41bff868483fe2fa9'), size_on_disk=11742567, blob_last_accessed=1756926333.5273216, blob_last_modified=1756926334.824316), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381029/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6431cec7b7404a0773d8715761e2b161f01ebd8471950e71e17dad8976d83f3c'), size_on_disk=7229691, blob_last_accessed=1756926379.0071244, blob_last_modified=1756926776.565756), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417879/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6714b3f5155dd540962a7541625f45e14e6a93b4c01e913790b70818e50c821b'), size_on_disk=8601872, blob_last_accessed=1756926351.4612439, blob_last_modified=1756926352.8182378), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380994/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7c5f237d9c32233a005e39386360f02d9e1932f9ac267ac3458f96aaf59e01ef'), size_on_disk=2496053, blob_last_accessed=1756926374.8051426, blob_last_modified=1756926375.3471403), CachedFileInfo(file_name='GSE222268_metadata.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE222268_metadata.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4459325dc2deeb9f00d803b89cd8fdc823ed383f'), size_on_disk=61, blob_last_accessed=1756926300.2714665, blob_last_modified=1756926300.3444662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417607/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/623d085888c300fba1d05a9018ec3d4c31912f2b91a872ef34feda3322889b70'), size_on_disk=6809356, blob_last_accessed=1756926300.611465, blob_last_modified=1756926301.75846), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417615/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3b8b3c4c686238c7c642151bbdcb2ba7796de56c1922e558a1e7f08507dc5abb'), size_on_disk=2866472, blob_last_accessed=1756926301.8314598, blob_last_modified=1756926302.5744565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381016/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0e597280417f8f8b5b476bc9d66033afc9412759f5e2f9bd9f3367932cc6f03f'), size_on_disk=820782, blob_last_accessed=1756926377.9791288, blob_last_modified=1756926378.4041271), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417777/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bd953fd8a3ee2a5c90382c1c9adc8091fbca801aa87f07a409b6dd5a9704421f'), size_on_disk=1267333, blob_last_accessed=1756926332.9623241, blob_last_modified=1756926333.6353211), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380930/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/734e9bc8119c7a507e9cf84f9926b1f9d06ccc8b0062a9b4dc36631f6f1bc6d1'), size_on_disk=4604638, blob_last_accessed=1756926365.1301844, blob_last_modified=1756926365.9251812), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417616/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0b0f3c9481fd0e254abd544ba803de0c9da9b3a686451975f981d02fc481148d'), size_on_disk=15058562, blob_last_accessed=1756926301.8254597, blob_last_modified=1756926305.0234458), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417655/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/25d98ba17be9b354b0ec7a0e513a6ff9d4593dd1cadf269f28bcea1ca9c71565'), size_on_disk=5461224, blob_last_accessed=1756926311.5504172, blob_last_modified=1756926312.9474113), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381019/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3023e878b223b00b77d3411a6d20195b85aa7e61a8445fe725ff694c77816053'), size_on_disk=1022843, blob_last_accessed=1756926378.3191273, blob_last_modified=1756926378.864125), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fc6783c3340c869bc3838a9a9d5abb8bca67361feeef12c42b6cf478d73bfd66'), size_on_disk=9020794, blob_last_accessed=1756926366.2571797, blob_last_modified=1756926367.2561753)}), refs=frozenset({'main'}), last_modified=1756926783.3167186)}), last_accessed=1756926861.9192877, last_modified=1756926783.3167186), CachedRepoInfo(repo_id='BrentLab/mahendrawada_2025', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025'), size_on_disk=94317969, nb_files=8, revisions=frozenset({CachedRevisionInfo(commit_hash='3b912489743a1797199beb023a49b14f7e3ba19d', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/3b912489743a1797199beb023a49b14f7e3ba19d'), size_on_disk=1957331, files=frozenset({CachedFileInfo(file_name='chec_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/3b912489743a1797199beb023a49b14f7e3ba19d/chec_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/dd3e967d8c54c6056dc9fd6bdb921c3eed9d10df3a449273bd08faeb1b936765'), size_on_disk=1220601, blob_last_accessed=1759345153.538238, blob_last_modified=1758569549.9902885), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/3b912489743a1797199beb023a49b14f7e3ba19d/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/c8cbc56ef9fad47193120cce2affd35374b8373d'), size_on_disk=18689, blob_last_accessed=1759345153.4962385, blob_last_modified=1758569549.1302917), CachedFileInfo(file_name='features_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/3b912489743a1797199beb023a49b14f7e3ba19d/features_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/618ea89d880c8a1b4c6d05cc3c13a70cbb05784d10bf2b6c4fc954705203096c'), size_on_disk=431436, blob_last_accessed=1759345156.897212, blob_last_modified=1758652945.6395862), CachedFileInfo(file_name='rnaseq_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/3b912489743a1797199beb023a49b14f7e3ba19d/rnaseq_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/4411789f50aab449658c100f2758ceb410022a0a30f956fd5fe41152915580f9'), size_on_disk=286605, blob_last_accessed=1759345153.5952375, blob_last_modified=1758569588.8621461)}), refs=frozenset(), last_modified=1758652945.6395862), CachedRevisionInfo(commit_hash='8bea431be57e21633be4174df291540b1fae212a', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/8bea431be57e21633be4174df291540b1fae212a'), size_on_disk=18603, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/8bea431be57e21633be4174df291540b1fae212a/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/6a2a5a6a8006e386c0e47a2a98151442046d3d41'), size_on_disk=18603, blob_last_accessed=1758568089.249362, blob_last_modified=1758568089.246362)}), refs=frozenset(), last_modified=1758568089.246362), CachedRevisionInfo(commit_hash='874a5dfe4052a1e71b6544a2e5b2c99ad4286c04', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/874a5dfe4052a1e71b6544a2e5b2c99ad4286c04'), size_on_disk=18603, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/874a5dfe4052a1e71b6544a2e5b2c99ad4286c04/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/685c0e56e0c68e20e3c2af8e38267095d686c03c'), size_on_disk=18603, blob_last_accessed=1758569041.2889845, blob_last_modified=1758569041.2879846)}), refs=frozenset(), last_modified=1758569041.2879846), CachedRevisionInfo(commit_hash='af5ac9dc922b7fbd14f460c2fe94b727db1e1245', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/af5ac9dc922b7fbd14f460c2fe94b727db1e1245'), size_on_disk=92323432, files=frozenset({CachedFileInfo(file_name='reprocess_diffcontrol_5prime.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/af5ac9dc922b7fbd14f460c2fe94b727db1e1245/reprocess_diffcontrol_5prime.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/eba528776f8fe8da63c39ee0660527bc35f989ac6a976daf09270860cad6d735'), size_on_disk=92302997, blob_last_accessed=1763665270.5143101, blob_last_modified=1763578870.280984), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/af5ac9dc922b7fbd14f460c2fe94b727db1e1245/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/8f0cf5e3dd2bb5bdc2f8fde970e31a2404481fec'), size_on_disk=20435, blob_last_accessed=1764298314.9501038, blob_last_modified=1763578711.307058)}), refs=frozenset({'main'}), last_modified=1763578870.280984)}), last_accessed=1764298314.9501038, last_modified=1763578870.280984), CachedRepoInfo(repo_id='BrentLab/hughes_2006', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006'), size_on_disk=11969274, nb_files=11, revisions=frozenset({CachedRevisionInfo(commit_hash='51ae7addf429c955a97934a15b242ff8a2ccd653', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/51ae7addf429c955a97934a15b242ff8a2ccd653'), size_on_disk=5701, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/51ae7addf429c955a97934a15b242ff8a2ccd653/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/f799807a7bf81229b0d063e6c939339c1f5c90f4'), size_on_disk=5701, blob_last_accessed=1756855057.8437808, blob_last_modified=1756855057.8417807)}), refs=frozenset(), last_modified=1756855057.8417807), CachedRevisionInfo(commit_hash='0de73d0932e423cfbbfbc1b21d029c96490dc200', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/0de73d0932e423cfbbfbc1b21d029c96490dc200'), size_on_disk=8791, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/0de73d0932e423cfbbfbc1b21d029c96490dc200/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/76efc047ec3c08e362cfcaac776b7e05c02f8a6b'), size_on_disk=8791, blob_last_accessed=1764712744.7980804, blob_last_modified=1764712744.7960804)}), refs=frozenset(), last_modified=1764712744.7960804), CachedRevisionInfo(commit_hash='1009546961df1cf9743a03383826d8a5e010bb38', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/1009546961df1cf9743a03383826d8a5e010bb38'), size_on_disk=17152, files=frozenset({CachedFileInfo(file_name='metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/1009546961df1cf9743a03383826d8a5e010bb38/metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/54173aa9fff98e5b077e2b24e139ad600a6d7cea74a47b0305cb0fbdf1c14d9b'), size_on_disk=8362, blob_last_accessed=1764713263.750364, blob_last_modified=1764713263.741364), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/1009546961df1cf9743a03383826d8a5e010bb38/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/572115c6e031637a3ac876795a6f91d3653bf5e2'), size_on_disk=8790, blob_last_accessed=1764713262.7733643, blob_last_modified=1764713223.5823674)}), refs=frozenset({'main'}), last_modified=1764713263.741364), CachedRevisionInfo(commit_hash='cc20720c4fe7de9151a051aafbd41e6de2b4cd84', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84'), size_on_disk=11937630, files=frozenset({CachedFileInfo(file_name='knockout.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/knockout.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/5e000e2e9401011c9ecc81655c94d55b7f9469ed856216fa21cef2883a904c93'), size_on_disk=5571518, blob_last_accessed=1756856029.2493467, blob_last_modified=1756856029.2143471), CachedFileInfo(file_name='overexpression.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/overexpression.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/9b8cc1feede780efe0e8537555ed1cbbf067e7fa9aa7a7edc74e5a343c64a443'), size_on_disk=6337781, blob_last_accessed=1756856028.536351, blob_last_modified=1756856029.230347), CachedFileInfo(file_name='metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/e27a67137876123c68c229705cfbb8e06ed3ee76a3a868b66d80d624f8d03671'), size_on_disk=7942, blob_last_accessed=1756856028.533351, blob_last_modified=1756856028.9083488), CachedFileInfo(file_name='checksum.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/checksum.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/973b686313e32c96b6e0ef1dbc84020857c073ef'), size_on_disk=159, blob_last_accessed=1756856028.5883508, blob_last_modified=1756856028.6513503), CachedFileInfo(file_name='parse_hughes_2006.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/scripts/parse_hughes_2006.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/0c2645bdc0ab7f6be38a428a0a92752e9d3f88d5'), size_on_disk=9255, blob_last_accessed=1756856028.5803509, blob_last_modified=1756856028.6393504), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/842b551fb3d1ba1e6f1daa35157a0c7c616f7655'), size_on_disk=8514, blob_last_accessed=1756855276.4871445, blob_last_modified=1756855276.4861445), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756856028.69735, blob_last_modified=1756856028.7793496)}), refs=frozenset(), last_modified=1756856029.230347)}), last_accessed=1764713263.750364, last_modified=1764713263.741364), CachedRepoInfo(repo_id='BrentLab/callingcards', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards'), size_on_disk=49606009, nb_files=137, revisions=frozenset({CachedRevisionInfo(commit_hash='9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0'), size_on_disk=49588983, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6140/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/66c2da17957d7e6a8b159d3573879ac779d0094e35150b51e2643f7ddee36e1f'), size_on_disk=332958, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4236548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=MAG001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/30b63d014686a103b5e7fc6571e902fa9c08630a96557cc12ec9dfcf60871306'), size_on_disk=347333, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.559651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR007B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6f0620b102e7cbfadced7d5530545a185036b53118d3e2b7f074c56403d80f4b'), size_on_disk=263185, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7616518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7295/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/19bd07f913e0af3460e6b4b5091971ceed8f4aa510cf18b82ec36dabb67c68b8'), size_on_disk=272619, blob_last_accessed=1763591393.1151721, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6863/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec534c91cae4413d30477014131d39b51c9c236dfbf150874bd21ec3de821613'), size_on_disk=389025, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648269.6376605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7118/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/be8bd409b83526d5671501de6ef2980177955fdcc3a13d5e153d2593493b2a1e'), size_on_disk=346308, blob_last_accessed=1763591392.9281735, blob_last_modified=1758648270.1336627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7297/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4bd4088cbfcccdf96def916bb8fd9d673327d3e57c2a4a3e57299743ce4aca7'), size_on_disk=261344, blob_last_accessed=1763591393.588169, blob_last_modified=1758648270.6516652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5301_5088/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/65ef775934339b80eda59a141cf791a74e1c6f12513d8143307cebe2f11c3738'), size_on_disk=410106, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.9516528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5617/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bc01241e8363373c995a0fa7439aa1e432a60eb8a7805124fff302311f638361'), size_on_disk=351950, blob_last_accessed=1763591393.6541686, blob_last_modified=1758648268.1026535), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7367/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a7778bb24506047963575fa96126157895c7f329996c729c3e7d6070595955c3'), size_on_disk=324361, blob_last_accessed=1763591392.1631787, blob_last_modified=1758648270.628665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4d098c33564f99ec6aed77d585d1f6e871806f441f099790eef4d6478e322af'), size_on_disk=264459, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7486472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6073/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d68ca6411ba47eb63adad81e88fa1341a0ee269897268957ae936dcc9ba7788'), size_on_disk=450031, blob_last_accessed=1763591393.1341722, blob_last_modified=1758648268.3886547), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6061/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73e03845d0fbff1a5931e4f792e2c1965ade02fc622ffc08e05c267ac6cd3231'), size_on_disk=394930, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.447655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7211/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73d7747698fa1168c89f12c105bbe900d5969d27a82ae2302d0720d761fbfdab'), size_on_disk=298482, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.1276627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6770/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/082f14f7b0d5fc47e088efc40ae85a8ec41f99437c66da96ca69af210bce61cc'), size_on_disk=318118, blob_last_accessed=1763591392.8081744, blob_last_modified=1758648269.3776593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/62e9a627f20607bc4887ebc9277131f974ec80ff39ac386775f4b8f2f5cb9d1d'), size_on_disk=346782, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7476518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/11485fd7bb94d0093ce2508d541fea3ce64f934197d8a5f7bb26ecbdb59f8dbb'), size_on_disk=124642, blob_last_accessed=1763591392.3321776, blob_last_modified=1758648267.0666487), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5961/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/49cac5c2df1adf790a9ac0209db6c0945de50b8191d403e8b1ad386ee2dcb35e'), size_on_disk=321368, blob_last_accessed=1763591392.560176, blob_last_modified=1758648268.215654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6532/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3041f19cf0dcb3e0bd9b1178994351a22f3297ccd43c5e291df8bfe0ad8d5d02'), size_on_disk=516437, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.8236568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6506/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/986d9d3b4ed064d66ebaa0f2502b933f1650a20ac4bbbba804e2cde03b9454c0'), size_on_disk=378407, blob_last_accessed=1763591391.97318, blob_last_modified=1758648268.7656565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5614/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1aa702ff57d34ebed961043e0efae064bfdb62f589cbb515311ae8ea37ba8c76'), size_on_disk=429423, blob_last_accessed=1763591393.4671698, blob_last_modified=1758648268.0406532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6764/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec87eef9e2e8cdf3c3a10ead28dcf1bfa76e94e12a42422a46c8559fb18207c2'), size_on_disk=75684, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.4036593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02313d4d85859a7353e56d89b6e14bcec5160849b5c0089ccbecaa1cda5012cf'), size_on_disk=301213, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4356549), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6635/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6c5b7f1fda3a7f5679870f438d13a45558f7f8da0f479c5144d7751bc8f7fbbc'), size_on_disk=420658, blob_last_accessed=1763591391.95018, blob_last_modified=1758648269.3956594), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ef036874e53ee98dfe1b5d84c42749fc40da9279cd573174321ac83201c1651'), size_on_disk=349762, blob_last_accessed=1763591393.0791724, blob_last_modified=1758648267.4966507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=KB001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38c939d54d3ed77a09650d2ccf00a6ed0e20390b8be722f1c3ed7881a5904a60'), size_on_disk=351677, blob_last_accessed=1763591392.9151735, blob_last_modified=1758648267.5566509), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e5b3174998823af40d055f46ca06e7bb44601cbe97b133062cd3e0491dd30ac2'), size_on_disk=323308, blob_last_accessed=1763591393.153172, blob_last_modified=1758648267.2686496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fc9311b65abb851fbbfd8bb0c0765af59069659f5a715aee546a8644b0af4ec2'), size_on_disk=210948, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648267.6766515), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7283/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0dc91bfacca82c5ed23de7757d59f9f808d541d96dd79aab56f0c4252e7aabf3'), size_on_disk=265146, blob_last_accessed=1763591392.7731745, blob_last_modified=1758648270.401664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6374/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e410602f56bc94d9831a2bc8bdb865ee4ebe9862a65b603165b655fbcc4b9914'), size_on_disk=441531, blob_last_accessed=1763591393.1891718, blob_last_modified=1758648268.5356555), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/33d7303ed077f489ee28021daca0bbc859feb1cfd383dfc9a856ea13994801a9'), size_on_disk=331877, blob_last_accessed=1763591393.292171, blob_last_modified=1758648268.2226539), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6545/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/260c4a280f52024cb12d44559a6189641ee02efbaf20168435196efbfc69d741'), size_on_disk=404812, blob_last_accessed=1763591392.3761773, blob_last_modified=1758648268.8256567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR006B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fcc5e886ec7c6363a5ab51b03d3da189ac3e0cde0a0f79d4f0c46043a6b61838'), size_on_disk=253857, blob_last_accessed=1763591392.0501795, blob_last_modified=1758648267.785652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS011/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ea7cb0559b342dd0ec278e7d5846a5ba0c0d867ea04d545f72e0d1c9472a0cc'), size_on_disk=363979, blob_last_accessed=1763591392.9791732, blob_last_modified=1758648267.0116484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6872/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02fabe6e32f52fe6fca923beb0a6cc2898d991304e23a3e75f3ae8a6bcf361c6'), size_on_disk=274362, blob_last_accessed=1763591393.6231687, blob_last_modified=1758648269.6966608), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a76f0849e518faedfe50e6f0c76908a3f6beed34fd0b607603db1859493595d0'), size_on_disk=292312, blob_last_accessed=1763587658.6168654, blob_last_modified=1758648266.7676473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5399/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b65d0aa7ddc98940f91f2ae5f07e1d3f81dda688792552b91fcc39fb4f9ededa'), size_on_disk=256729, blob_last_accessed=1763591392.5471761, blob_last_modified=1758648267.972653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6437/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/556084dbbc174db5450872d6f5e66cc72091380ea32006977dd176e23c105a7f'), size_on_disk=389840, blob_last_accessed=1763591392.714175, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6567/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0e9713a94594dfcbc80134af52449e12ef207ca9149f5a96c43b6cb457b2431c'), size_on_disk=478809, blob_last_accessed=1763591392.6241755, blob_last_modified=1758648268.893657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7370/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ff4dce26afb34b291220c17625be4a28078f9721a8b000ea177c4add3bf1a039'), size_on_disk=241010, blob_last_accessed=1763591393.7701678, blob_last_modified=1758648270.6726654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5a70fa6f22aeda3246d17646cc4ec7c5a46e1993f9981b19d9fd5a8cc99bf11f'), size_on_disk=308858, blob_last_accessed=1763591391.9411802, blob_last_modified=1758648267.0186484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6177/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b91ffef1dfcdf7952df46956d76a4b364aa0d8342453636cf09e6a343607184f'), size_on_disk=387618, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.424655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6959/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c0cb6c2ec2bda19b0c1da5d5fc2229d0b6d6987d75646fa230c0c14d591cf1f1'), size_on_disk=537588, blob_last_accessed=1763591392.851174, blob_last_modified=1758648269.8686616), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6390/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f324cdf73148c8b0fa23456bc4f51cff3032d08ed42e44dd9fcc4815a6d969c'), size_on_disk=560601, blob_last_accessed=1763591392.7391748, blob_last_modified=1758648268.6206558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7151/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9969e4607d6bf42424f5d2ce8f0d16e3d2d89d069cae6d75997e99c88a68dc77'), size_on_disk=275572, blob_last_accessed=1763591393.7231681, blob_last_modified=1758648270.1476629), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4e8332a5db6260f6357f940e62e3e7b622785f386e81cdb9c75b538d318f4a6'), size_on_disk=158978, blob_last_accessed=1763591393.5471692, blob_last_modified=1758648266.7626472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6421/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1e3bacd3bcf1fcb46cfc5f9ab66c2af57e190f3d006031e7ef37864fbd36aaca'), size_on_disk=495452, blob_last_accessed=1763591392.2161784, blob_last_modified=1758648268.6366558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6708/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/35520976736159652e3be8066e77b1371522f63a8b3564d9b0d9f5b1598a144d'), size_on_disk=253955, blob_last_accessed=1763591393.6881683, blob_last_modified=1758648269.1996584), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6551/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c3729692991cdcafe9baa3d2805b3866f72fe9bc89ba22c6d2cdae4d5b5bbf5f'), size_on_disk=545477, blob_last_accessed=1763591393.0611725, blob_last_modified=1758648268.8376567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1fa09a5a6e6e69198501d994050adfcad7ff39a3cc2fe94e7ea177414e1bc968'), size_on_disk=303532, blob_last_accessed=1763591393.7091682, blob_last_modified=1758648267.2776496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ac8c590c19e7b95b9aac571b1394976039f25fdd66e62a68ecb99925433f8e54'), size_on_disk=355706, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0226483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d3e36418bd46acf9537a8f864b1a1361a062d72fec2b093789b88ed655ec117'), size_on_disk=377393, blob_last_accessed=1763591393.0451727, blob_last_modified=1758648267.0136483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dcb33135a9d854d17a6b80348f6bee8becea9ca3808583a034f89c5e4f460c54'), size_on_disk=218643, blob_last_accessed=1763591393.4831698, blob_last_modified=1758648267.9636528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/00e5602b87ac84670a98c081e6520ca744038f4e9b3a72a78cb61f5926be56b1'), size_on_disk=251087, blob_last_accessed=1763591392.5781758, blob_last_modified=1758648267.8286521), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e91fd764a83947c14f35d7a2d8c0d156c0a61a32a1da96273718ef4be720787'), size_on_disk=361855, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.7196517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6527/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/10081f44b1c2f5ad32489da51fc9ac5b5289b54688a7e78725db247a644b7e4d'), size_on_disk=494640, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.882657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6861/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6955b1a1746fe41162ea1d7a098482bbfdb3d0e411c809b52d698e285627ce03'), size_on_disk=385103, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6146603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6689/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/90ea249116dc6a9be86974e1ec0acf79558dfd56a5666be2e05d2107b8c4ee60'), size_on_disk=366787, blob_last_accessed=1763591393.6661685, blob_last_modified=1758648269.1406581), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6021/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eea0d101dd58c9619b41a94009fc0dff8156d2d36d7f2b88bb653e96123a032e'), size_on_disk=439545, blob_last_accessed=1763591393.5531693, blob_last_modified=1758648268.258654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6920/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/45122fcfb3abc3a43631023712c224dc6e00dba566fb2e15deb4ab4501ad2538'), size_on_disk=446998, blob_last_accessed=1763591392.8881738, blob_last_modified=1758648269.6416605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6448/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/182dc134914a5ddb02992ab6a4f1c12d2450aa331820bf7bd95693dc999bcfae'), size_on_disk=390210, blob_last_accessed=1763591392.9591732, blob_last_modified=1758648268.672656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6954/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/aeed3c55d81a4eb0c568486e476211c599ce2bf37f9dff22c7dfca23f5d80495'), size_on_disk=476054, blob_last_accessed=1763591393.011173, blob_last_modified=1758648269.943662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6853/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ed47ecb2aa870fdbf12920fd4486933262c0a24318c9c8c5531e359a3c416c90'), size_on_disk=515114, blob_last_accessed=1763591393.2661712, blob_last_modified=1758648269.4276595), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1c8a8cb23b1ec073827d6f5343a3bf03a9d7a04cf2ae3fd3ea4cc5ac37673b38'), size_on_disk=342987, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.6226559), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6572/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3488eaac5e4b9b3418b909138e6b50e3f9ebaf3e7345a4a10b6ced18b66152e4'), size_on_disk=440579, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9506574), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6055/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/31bb70b4c90654ecb276664f5c914679322b24985ec66d5b396a99f253ebfa17'), size_on_disk=377015, blob_last_accessed=1763591393.5701692, blob_last_modified=1758648268.3076544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6513/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c8f9c31704c2f8732b4668e1e6410034b8adf2ada39ff55935aa0199e920d055'), size_on_disk=507367, blob_last_accessed=1763591392.1451788, blob_last_modified=1758648268.8406568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6965/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9ae4546d58672f0632757c94b595af8b7c40da215611783fdc1a65000075ec98'), size_on_disk=509677, blob_last_accessed=1763591392.2731779, blob_last_modified=1758648269.933662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6354/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/80b129a46defc7b06e472a35935bfaf410bc5e456475b2885feb2c8a4934a81b'), size_on_disk=173225, blob_last_accessed=1763591393.4981697, blob_last_modified=1758648268.4986553), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bd2017e75947ead2c06f9d1936347f7fdcfac7fbff620e0b39077208a02c3bd9'), size_on_disk=303094, blob_last_accessed=1763591393.1271722, blob_last_modified=1758648269.9296618), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6731/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/23ecd71c4e3c1c547295bb756d4654eb413ff288d10118ab286cd21ccd95b666'), size_on_disk=346187, blob_last_accessed=1763591392.1871786, blob_last_modified=1758648269.1906583), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ac08e026f6d48c034f13a49f3531c90b6c070907'), size_on_disk=9519, blob_last_accessed=1763588875.9720669, blob_last_modified=1763588875.9700668), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/530b5650d91dae3bf169f967af8e800611e3e01daeb1811d6fd21c2612ac4ad9'), size_on_disk=315170, blob_last_accessed=1763591393.6121688, blob_last_modified=1758648266.7956474), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5690/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4831e3e037d421a14efbe1da8c369015ec7d01df351bb27f0ef0400c8b94c693'), size_on_disk=311754, blob_last_accessed=1763591392.9021738, blob_last_modified=1758648268.211654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d4c0cbbf6075a8b5732e3cef2ba154d95d21aa0efc7e49d947cceebd8742b30'), size_on_disk=269007, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4106548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7258/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7b1860ba3be7dbf053203fa5690da2002f7841186940231acea6064e70f0bdc8'), size_on_disk=171331, blob_last_accessed=1763591392.6171756, blob_last_modified=1758648270.3746638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6651/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7644af4cc956ea08809ab6c80c65095e3fdbd36e056040c0eaded3773d546359'), size_on_disk=559951, blob_last_accessed=1763591393.6361687, blob_last_modified=1758648269.1856585), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7155/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/521fc1bea6963640645c21cbdb8d1de07793f11213c23ac71d249f3252b32f54'), size_on_disk=304554, blob_last_accessed=1763591393.5841691, blob_last_modified=1758648270.1366627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6924/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5e9b3a94d73c1720e6b05ea55000054905a8672edea7f950736ce3fe191f8faf'), size_on_disk=412939, blob_last_accessed=1763591393.748168, blob_last_modified=1758648269.7066607), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6923/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/454ca6a52d4b305eddc4b6967ef9604be4861269fdcfff419883daed9b2fe48b'), size_on_disk=337423, blob_last_accessed=1763591393.5031695, blob_last_modified=1758648269.6476605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5801/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/12e91a415cc2a149af4c34c3c8b032821ff301e2828dfb9f4636bd6b2913ea3d'), size_on_disk=360485, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.1636536), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7063/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9427d8f8c54e3c0cc9d720958ef15d3859f00cdfb678559e0268387a36a8ef37'), size_on_disk=404051, blob_last_accessed=1763591392.5301762, blob_last_modified=1758648269.8976617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS006b/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7eedae811a5b3dd4389e7e814d35b022bfaf01fc4d2cfb47be6e6786d6d58c64'), size_on_disk=315912, blob_last_accessed=1763591393.2571712, blob_last_modified=1758648266.7546473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bbe701ffd546a2f4359f821262f7b44232bdc328d9d4719bfdaa8f2aedc6d2e9'), size_on_disk=346079, blob_last_accessed=1763591393.0861723, blob_last_modified=1758648267.589651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5986/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f1179c75ac6e91de03b67a73882f3b9c07c8e435d938d3059de489403adde7a'), size_on_disk=328749, blob_last_accessed=1763591392.691175, blob_last_modified=1758648268.228654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7331/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f159a9811b8ac1b5e252b52427979a3bddce2e7131b13f84046b31e47f7fa399'), size_on_disk=242821, blob_last_accessed=1763591393.2501714, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eb61968b5ec5eadd9087e42ead29333f91195dd5b3f9c5a616b41e94ca23c494'), size_on_disk=330245, blob_last_accessed=1763591393.6761684, blob_last_modified=1758648269.710661), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6808/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c71f5371e8c803cfc7fd507d0b3c11f7548db5d7a0fa42c68977169369ea036e'), size_on_disk=422880, blob_last_accessed=1763591392.9491735, blob_last_modified=1758648269.3916593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ab338795b8bbf334650386a123f6e0df41363cb5dcbb778d485ce58804e96b29'), size_on_disk=375506, blob_last_accessed=1763591392.9711733, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7237/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/110a743162ace7792a44dfe48d7afd40bb67c45c722038c981548477ebc5d8fe'), size_on_disk=313207, blob_last_accessed=1763591393.143172, blob_last_modified=1758648270.1426628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7137/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/703fe9f8552adcbdfb5e640509cb62df2fa734b43fe1e98fc1fd342415f823d7'), size_on_disk=515398, blob_last_accessed=1763589053.7698598, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f427ea0f4ded3b6ff841153d8ad87d72a26a964c189246fb6fd5c439bd8b0c1e'), size_on_disk=353623, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.812652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7115/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/205443fd87ce5c793dab86eb75c21dab7dc2ae2edac41d7e6314cfe491bdb01d'), size_on_disk=321850, blob_last_accessed=1763591393.2331715, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6855/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48e48378d24404e926bc1c44b10af60d4b12f82124ff2f80bc0b69b4a0f98740'), size_on_disk=609248, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6556606), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6798/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/899cde55032cd593be0013e1f6ff33fe9a12f4616477b1410d86fae1b2008a09'), size_on_disk=342376, blob_last_accessed=1763591392.7951744, blob_last_modified=1758648269.4006593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6677/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4ee6e1d3e2b9a9bbcc8851048d2b16f4aea6a1e2f752afb9e0bf75a56adf7042'), size_on_disk=322147, blob_last_accessed=1763591392.393177, blob_last_modified=1758648269.0826578), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7243/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cb59e645fff2683db692b4d2f514602ba210271b632cd113853febe941c6485f'), size_on_disk=281696, blob_last_accessed=1763591392.701175, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7269/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5bae329d2318610f627fc562a5163253c7d61e0ef83c53808b220133b2299264'), size_on_disk=203263, blob_last_accessed=1763591392.541176, blob_last_modified=1758648270.4096642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e20f616f6e340b76f6446d2cdffb6cc9feb81297bd0307733f835acf4142cfd'), size_on_disk=269523, blob_last_accessed=1763591392.3461773, blob_last_modified=1758648267.2696495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6443/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4deb4634e8b4bf141a2410e94b2d99e2b31a0e8f037de9a1c6e14696025bffae'), size_on_disk=494775, blob_last_accessed=1763591392.991173, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6640/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48339729112d22aa5aabf411674bfb90a567ec4c7a07ea7930738645a96823ef'), size_on_disk=329042, blob_last_accessed=1763591392.6391754, blob_last_modified=1758648269.090658), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6573/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e435989051765cf2955d8cd733904e8b60e5f3121e1c2fdc987f4e6f71fe3970'), size_on_disk=265022, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9586573), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/d1cd38451bc41ef14dbc1566c4704b004b2c0661f667210c81dba09bec90540f'), size_on_disk=244487, blob_last_accessed=1763591393.6301687, blob_last_modified=1758648267.3246498), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/74cbce50c258bdddb7e42c8fd5b0b2a9da74fe3cf89a9ab5a53ee4fde7c1dee6'), size_on_disk=186142, blob_last_accessed=1763591392.842174, blob_last_modified=1758648266.9966483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7351/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cc708adf186f28db09dfdd4ac43865a90482b2192306949eab01492598cf23e7'), size_on_disk=198070, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.610665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7185/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/14f67668aca8d8f8aa2b8af98835da683a7fc3f22aa09cdcd7f943a37632391e'), size_on_disk=201650, blob_last_accessed=1763591393.2191715, blob_last_modified=1758648270.1076627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7256/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1af21cbdd7f6f6effff15a1b061a48f85132d582f08c8fd42e04ea58bde7ec00'), size_on_disk=206710, blob_last_accessed=1763591392.586176, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5610/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a86512ae1000a994c86e9aebaf30cf9ce1a59160155f3dc1afa6cbde0a1fc47e'), size_on_disk=307382, blob_last_accessed=1763591393.5291693, blob_last_modified=1758648267.994653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6739/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/58b5ed63f2197f406dd3df6a569975d776722193007912718979eaed4d4e5f9c'), size_on_disk=399160, blob_last_accessed=1763591392.6551754, blob_last_modified=1758648269.331659), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/128015caa722289251a45aa3d56cabac6b5946eb4e1be16eb950555fb24ac413'), size_on_disk=172328, blob_last_accessed=1763591393.735168, blob_last_modified=1758648267.2826495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9f7663ccf72b0681ae5866c13500fbde98f35877fd677ac0d49c17447d17d6ce'), size_on_disk=332767, blob_last_accessed=1763591392.1731787, blob_last_modified=1758648266.744647), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5654/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38961219eb66dec31505ebcc5088f16684be9891b96fffa8c9a13cd3f13254d0'), size_on_disk=447782, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.1876538), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6738/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e6d612d16538a06c985f1134f3fdfd367944b2a85cedfb8b0ddad1679004c940'), size_on_disk=333560, blob_last_accessed=1763591392.8651738, blob_last_modified=1758648269.3676593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6b92129c2fb99028b6cab9509cf1a4c40b142109cde34c9a130d375001dcd211'), size_on_disk=267273, blob_last_accessed=1763591392.7811744, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6454/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c35b60861d2fc7a3f4256869e14e629aed07cd2ccce76608578ceb8c6ec423e1'), size_on_disk=498326, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.7226562), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d50648ed5c9f593f830aff661d13b3d34bd14f0bf93b16169df4765c9f47fc8'), size_on_disk=295924, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7426472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7266/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9aea2d6f5606d24e1999d450379b1f8c8b9d80eded9e784fb729faa516a27026'), size_on_disk=324480, blob_last_accessed=1763591392.0401795, blob_last_modified=1758648270.379664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=MAG002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f7f35257d4c1f7fa5812ea31c1de791e08ab5e3a4e7ca42b2bb12b2c64f7ed7'), size_on_disk=490174, blob_last_accessed=1763591392.829174, blob_last_modified=1758648267.583651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dc74862f02ff12b8c906046f1f03953d33f5516091d58397b0f9a0fc05bb4b5c'), size_on_disk=315435, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.5006506), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7276/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4e0d576cc4a6a85be64f2198e5001a9f5b02b506072df6f695cfdfc719cb041'), size_on_disk=204036, blob_last_accessed=1763591392.987173, blob_last_modified=1758648270.4646642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=composite/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1f8adf8fb4992283714df8587126211d54e6a0580b8be88c1d5e911196aed783'), size_on_disk=4814573, blob_last_accessed=1763591392.0561793, blob_last_modified=1758648268.0256531), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7cb5cca74f1bda2f8f1c8ef962e54d34228361eaa9dd8b21e0d1dd7092ff8164'), size_on_disk=229703, blob_last_accessed=1763591392.555176, blob_last_modified=1758648267.0126483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6657/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/95ad2225152f51e2e31c030ff6708aeebccee5e548035d6f43c9cc3554bdfe5e'), size_on_disk=507730, blob_last_accessed=1763591393.163172, blob_last_modified=1758648269.1676583), CachedFileInfo(file_name='annotated_features_meta.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features_meta.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/2be4b693d333698f7ada7ef40ffaf927c0160d7975ebcf6e215cbc4ba1be1604'), size_on_disk=11813, blob_last_accessed=1763587594.724117, blob_last_modified=1758648264.795638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4b2ff192aa4808cb50b1c1b1667060f98505dd63c1aa58c7d8354e41194957e4'), size_on_disk=289767, blob_last_accessed=1763591393.4871697, blob_last_modified=1758648266.7706473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ddd58117dbedfe7e9dc3fd946187e7bd5d88fe8b0f38999c6e4b5660938bb787'), size_on_disk=359739, blob_last_accessed=1763591393.5981688, blob_last_modified=1758648267.2726495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6983/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4ee29f84ab279c520521a6c5a5eed9e5e5581b2e39378bdd898eec67d13654f'), size_on_disk=410123, blob_last_accessed=1763591392.6751752, blob_last_modified=1758648269.8636615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/646c843611bf88c7f5dcd5b1a664d724c3b24b6434cc52d9a54de62ccaf6c542'), size_on_disk=255949, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.4506505), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fa1a44bd33df22ec6b5a8baf3be17e51a11522b5fdd545a38e6e163cc5ae8207'), size_on_disk=241911, blob_last_accessed=1763591392.572176, blob_last_modified=1758648267.5226507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/71a33d11c3ff714ce0730145c740eec4fe88812c6136b8d10b86fa8d4054991f'), size_on_disk=186500, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7286518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7332/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f7f62ee008fa2b5cc688cbd80c888f1bf9da538caa245b63581520d485338a99'), size_on_disk=309595, blob_last_accessed=1763591393.1021724, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c9b19726dc45896e4f86a5acc52d07cc422f895bfaaaddb332aaceedff36560c'), size_on_disk=178793, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0266485), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0230df4f1d3748337e5315bb1d22a0373fdab95f9bc900041b0e34966d868718'), size_on_disk=311116, blob_last_accessed=1763591392.9241736, blob_last_modified=1758648267.2506495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7240/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4460d188b4cd3b362f33acf0f0b5312a267b37f8fb2dab4b2577c6c3bbb1231'), size_on_disk=307039, blob_last_accessed=1763591392.6101756, blob_last_modified=1758648270.403664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e871cf06f596bfe74896b62aa28d6b1e6f2f0b8408630fb146c7e29cfd88fbf0'), size_on_disk=232721, blob_last_accessed=1763591392.7871745, blob_last_modified=1758648267.991653)}), refs=frozenset(), last_modified=1763588875.9700668), CachedRevisionInfo(commit_hash='27becd0ab489579a50c38ba896dd03680c5fd2da', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da'), size_on_disk=49585603, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ab338795b8bbf334650386a123f6e0df41363cb5dcbb778d485ce58804e96b29'), size_on_disk=375506, blob_last_accessed=1763591392.9711733, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6965/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9ae4546d58672f0632757c94b595af8b7c40da215611783fdc1a65000075ec98'), size_on_disk=509677, blob_last_accessed=1763591392.2731779, blob_last_modified=1758648269.933662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e871cf06f596bfe74896b62aa28d6b1e6f2f0b8408630fb146c7e29cfd88fbf0'), size_on_disk=232721, blob_last_accessed=1763591392.7871745, blob_last_modified=1758648267.991653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7331/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f159a9811b8ac1b5e252b52427979a3bddce2e7131b13f84046b31e47f7fa399'), size_on_disk=242821, blob_last_accessed=1763591393.2501714, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6532/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3041f19cf0dcb3e0bd9b1178994351a22f3297ccd43c5e291df8bfe0ad8d5d02'), size_on_disk=516437, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.8236568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6177/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b91ffef1dfcdf7952df46956d76a4b364aa0d8342453636cf09e6a343607184f'), size_on_disk=387618, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.424655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6390/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f324cdf73148c8b0fa23456bc4f51cff3032d08ed42e44dd9fcc4815a6d969c'), size_on_disk=560601, blob_last_accessed=1763591392.7391748, blob_last_modified=1758648268.6206558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6954/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/aeed3c55d81a4eb0c568486e476211c599ce2bf37f9dff22c7dfca23f5d80495'), size_on_disk=476054, blob_last_accessed=1763591393.011173, blob_last_modified=1758648269.943662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ef036874e53ee98dfe1b5d84c42749fc40da9279cd573174321ac83201c1651'), size_on_disk=349762, blob_last_accessed=1763591393.0791724, blob_last_modified=1758648267.4966507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7258/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7b1860ba3be7dbf053203fa5690da2002f7841186940231acea6064e70f0bdc8'), size_on_disk=171331, blob_last_accessed=1763591392.6171756, blob_last_modified=1758648270.3746638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS011/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ea7cb0559b342dd0ec278e7d5846a5ba0c0d867ea04d545f72e0d1c9472a0cc'), size_on_disk=363979, blob_last_accessed=1763591392.9791732, blob_last_modified=1758648267.0116484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6513/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c8f9c31704c2f8732b4668e1e6410034b8adf2ada39ff55935aa0199e920d055'), size_on_disk=507367, blob_last_accessed=1763591392.1451788, blob_last_modified=1758648268.8406568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6506/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/986d9d3b4ed064d66ebaa0f2502b933f1650a20ac4bbbba804e2cde03b9454c0'), size_on_disk=378407, blob_last_accessed=1763591391.97318, blob_last_modified=1758648268.7656565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6573/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e435989051765cf2955d8cd733904e8b60e5f3121e1c2fdc987f4e6f71fe3970'), size_on_disk=265022, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9586573), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6454/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c35b60861d2fc7a3f4256869e14e629aed07cd2ccce76608578ceb8c6ec423e1'), size_on_disk=498326, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.7226562), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6443/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4deb4634e8b4bf141a2410e94b2d99e2b31a0e8f037de9a1c6e14696025bffae'), size_on_disk=494775, blob_last_accessed=1763591392.991173, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7240/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4460d188b4cd3b362f33acf0f0b5312a267b37f8fb2dab4b2577c6c3bbb1231'), size_on_disk=307039, blob_last_accessed=1763591392.6101756, blob_last_modified=1758648270.403664), CachedFileInfo(file_name='annotated_features_meta.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features_meta.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/2be4b693d333698f7ada7ef40ffaf927c0160d7975ebcf6e215cbc4ba1be1604'), size_on_disk=11813, blob_last_accessed=1763587594.724117, blob_last_modified=1758648264.795638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6421/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1e3bacd3bcf1fcb46cfc5f9ab66c2af57e190f3d006031e7ef37864fbd36aaca'), size_on_disk=495452, blob_last_accessed=1763591392.2161784, blob_last_modified=1758648268.6366558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7118/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/be8bd409b83526d5671501de6ef2980177955fdcc3a13d5e153d2593493b2a1e'), size_on_disk=346308, blob_last_accessed=1763591392.9281735, blob_last_modified=1758648270.1336627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4d098c33564f99ec6aed77d585d1f6e871806f441f099790eef4d6478e322af'), size_on_disk=264459, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7486472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6055/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/31bb70b4c90654ecb276664f5c914679322b24985ec66d5b396a99f253ebfa17'), size_on_disk=377015, blob_last_accessed=1763591393.5701692, blob_last_modified=1758648268.3076544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5690/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4831e3e037d421a14efbe1da8c369015ec7d01df351bb27f0ef0400c8b94c693'), size_on_disk=311754, blob_last_accessed=1763591392.9021738, blob_last_modified=1758648268.211654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6731/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/23ecd71c4e3c1c547295bb756d4654eb413ff288d10118ab286cd21ccd95b666'), size_on_disk=346187, blob_last_accessed=1763591392.1871786, blob_last_modified=1758648269.1906583), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7211/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73d7747698fa1168c89f12c105bbe900d5969d27a82ae2302d0720d761fbfdab'), size_on_disk=298482, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.1276627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6708/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/35520976736159652e3be8066e77b1371522f63a8b3564d9b0d9f5b1598a144d'), size_on_disk=253955, blob_last_accessed=1763591393.6881683, blob_last_modified=1758648269.1996584), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6b92129c2fb99028b6cab9509cf1a4c40b142109cde34c9a130d375001dcd211'), size_on_disk=267273, blob_last_accessed=1763591392.7811744, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6551/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c3729692991cdcafe9baa3d2805b3866f72fe9bc89ba22c6d2cdae4d5b5bbf5f'), size_on_disk=545477, blob_last_accessed=1763591393.0611725, blob_last_modified=1758648268.8376567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5617/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bc01241e8363373c995a0fa7439aa1e432a60eb8a7805124fff302311f638361'), size_on_disk=351950, blob_last_accessed=1763591393.6541686, blob_last_modified=1758648268.1026535), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/33d7303ed077f489ee28021daca0bbc859feb1cfd383dfc9a856ea13994801a9'), size_on_disk=331877, blob_last_accessed=1763591393.292171, blob_last_modified=1758648268.2226539), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6657/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/95ad2225152f51e2e31c030ff6708aeebccee5e548035d6f43c9cc3554bdfe5e'), size_on_disk=507730, blob_last_accessed=1763591393.163172, blob_last_modified=1758648269.1676583), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dc74862f02ff12b8c906046f1f03953d33f5516091d58397b0f9a0fc05bb4b5c'), size_on_disk=315435, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.5006506), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/00e5602b87ac84670a98c081e6520ca744038f4e9b3a72a78cb61f5926be56b1'), size_on_disk=251087, blob_last_accessed=1763591392.5781758, blob_last_modified=1758648267.8286521), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e91fd764a83947c14f35d7a2d8c0d156c0a61a32a1da96273718ef4be720787'), size_on_disk=361855, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.7196517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7063/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9427d8f8c54e3c0cc9d720958ef15d3859f00cdfb678559e0268387a36a8ef37'), size_on_disk=404051, blob_last_accessed=1763591392.5301762, blob_last_modified=1758648269.8976617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/11485fd7bb94d0093ce2508d541fea3ce64f934197d8a5f7bb26ecbdb59f8dbb'), size_on_disk=124642, blob_last_accessed=1763591392.3321776, blob_last_modified=1758648267.0666487), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c9b19726dc45896e4f86a5acc52d07cc422f895bfaaaddb332aaceedff36560c'), size_on_disk=178793, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0266485), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9f7663ccf72b0681ae5866c13500fbde98f35877fd677ac0d49c17447d17d6ce'), size_on_disk=332767, blob_last_accessed=1763591392.1731787, blob_last_modified=1758648266.744647), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6923/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/454ca6a52d4b305eddc4b6967ef9604be4861269fdcfff419883daed9b2fe48b'), size_on_disk=337423, blob_last_accessed=1763591393.5031695, blob_last_modified=1758648269.6476605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02313d4d85859a7353e56d89b6e14bcec5160849b5c0089ccbecaa1cda5012cf'), size_on_disk=301213, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4356549), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6527/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/10081f44b1c2f5ad32489da51fc9ac5b5289b54688a7e78725db247a644b7e4d'), size_on_disk=494640, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.882657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7269/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5bae329d2318610f627fc562a5163253c7d61e0ef83c53808b220133b2299264'), size_on_disk=203263, blob_last_accessed=1763591392.541176, blob_last_modified=1758648270.4096642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1c8a8cb23b1ec073827d6f5343a3bf03a9d7a04cf2ae3fd3ea4cc5ac37673b38'), size_on_disk=342987, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.6226559), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6738/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e6d612d16538a06c985f1134f3fdfd367944b2a85cedfb8b0ddad1679004c940'), size_on_disk=333560, blob_last_accessed=1763591392.8651738, blob_last_modified=1758648269.3676593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7370/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ff4dce26afb34b291220c17625be4a28078f9721a8b000ea177c4add3bf1a039'), size_on_disk=241010, blob_last_accessed=1763591393.7701678, blob_last_modified=1758648270.6726654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d3e36418bd46acf9537a8f864b1a1361a062d72fec2b093789b88ed655ec117'), size_on_disk=377393, blob_last_accessed=1763591393.0451727, blob_last_modified=1758648267.0136483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dcb33135a9d854d17a6b80348f6bee8becea9ca3808583a034f89c5e4f460c54'), size_on_disk=218643, blob_last_accessed=1763591393.4831698, blob_last_modified=1758648267.9636528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ddd58117dbedfe7e9dc3fd946187e7bd5d88fe8b0f38999c6e4b5660938bb787'), size_on_disk=359739, blob_last_accessed=1763591393.5981688, blob_last_modified=1758648267.2726495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6448/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/182dc134914a5ddb02992ab6a4f1c12d2450aa331820bf7bd95693dc999bcfae'), size_on_disk=390210, blob_last_accessed=1763591392.9591732, blob_last_modified=1758648268.672656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6739/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/58b5ed63f2197f406dd3df6a569975d776722193007912718979eaed4d4e5f9c'), size_on_disk=399160, blob_last_accessed=1763591392.6551754, blob_last_modified=1758648269.331659), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=KB001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38c939d54d3ed77a09650d2ccf00a6ed0e20390b8be722f1c3ed7881a5904a60'), size_on_disk=351677, blob_last_accessed=1763591392.9151735, blob_last_modified=1758648267.5566509), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5801/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/12e91a415cc2a149af4c34c3c8b032821ff301e2828dfb9f4636bd6b2913ea3d'), size_on_disk=360485, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.1636536), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR006B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fcc5e886ec7c6363a5ab51b03d3da189ac3e0cde0a0f79d4f0c46043a6b61838'), size_on_disk=253857, blob_last_accessed=1763591392.0501795, blob_last_modified=1758648267.785652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6677/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4ee6e1d3e2b9a9bbcc8851048d2b16f4aea6a1e2f752afb9e0bf75a56adf7042'), size_on_disk=322147, blob_last_accessed=1763591392.393177, blob_last_modified=1758648269.0826578), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6140/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/66c2da17957d7e6a8b159d3573879ac779d0094e35150b51e2643f7ddee36e1f'), size_on_disk=332958, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4236548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7237/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/110a743162ace7792a44dfe48d7afd40bb67c45c722038c981548477ebc5d8fe'), size_on_disk=313207, blob_last_accessed=1763591393.143172, blob_last_modified=1758648270.1426628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6798/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/899cde55032cd593be0013e1f6ff33fe9a12f4616477b1410d86fae1b2008a09'), size_on_disk=342376, blob_last_accessed=1763591392.7951744, blob_last_modified=1758648269.4006593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6861/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6955b1a1746fe41162ea1d7a098482bbfdb3d0e411c809b52d698e285627ce03'), size_on_disk=385103, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6146603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6920/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/45122fcfb3abc3a43631023712c224dc6e00dba566fb2e15deb4ab4501ad2538'), size_on_disk=446998, blob_last_accessed=1763591392.8881738, blob_last_modified=1758648269.6416605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/74cbce50c258bdddb7e42c8fd5b0b2a9da74fe3cf89a9ab5a53ee4fde7c1dee6'), size_on_disk=186142, blob_last_accessed=1763591392.842174, blob_last_modified=1758648266.9966483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/128015caa722289251a45aa3d56cabac6b5946eb4e1be16eb950555fb24ac413'), size_on_disk=172328, blob_last_accessed=1763591393.735168, blob_last_modified=1758648267.2826495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0230df4f1d3748337e5315bb1d22a0373fdab95f9bc900041b0e34966d868718'), size_on_disk=311116, blob_last_accessed=1763591392.9241736, blob_last_modified=1758648267.2506495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6635/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6c5b7f1fda3a7f5679870f438d13a45558f7f8da0f479c5144d7751bc8f7fbbc'), size_on_disk=420658, blob_last_accessed=1763591391.95018, blob_last_modified=1758648269.3956594), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6073/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d68ca6411ba47eb63adad81e88fa1341a0ee269897268957ae936dcc9ba7788'), size_on_disk=450031, blob_last_accessed=1763591393.1341722, blob_last_modified=1758648268.3886547), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7332/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f7f62ee008fa2b5cc688cbd80c888f1bf9da538caa245b63581520d485338a99'), size_on_disk=309595, blob_last_accessed=1763591393.1021724, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS006b/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7eedae811a5b3dd4389e7e814d35b022bfaf01fc4d2cfb47be6e6786d6d58c64'), size_on_disk=315912, blob_last_accessed=1763591393.2571712, blob_last_modified=1758648266.7546473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6651/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7644af4cc956ea08809ab6c80c65095e3fdbd36e056040c0eaded3773d546359'), size_on_disk=559951, blob_last_accessed=1763591393.6361687, blob_last_modified=1758648269.1856585), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4e8332a5db6260f6357f940e62e3e7b622785f386e81cdb9c75b538d318f4a6'), size_on_disk=158978, blob_last_accessed=1763591393.5471692, blob_last_modified=1758648266.7626472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6764/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec87eef9e2e8cdf3c3a10ead28dcf1bfa76e94e12a42422a46c8559fb18207c2'), size_on_disk=75684, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.4036593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7295/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/19bd07f913e0af3460e6b4b5091971ceed8f4aa510cf18b82ec36dabb67c68b8'), size_on_disk=272619, blob_last_accessed=1763591393.1151721, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6872/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02fabe6e32f52fe6fca923beb0a6cc2898d991304e23a3e75f3ae8a6bcf361c6'), size_on_disk=274362, blob_last_accessed=1763591393.6231687, blob_last_modified=1758648269.6966608), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=MAG001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/30b63d014686a103b5e7fc6571e902fa9c08630a96557cc12ec9dfcf60871306'), size_on_disk=347333, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.559651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d50648ed5c9f593f830aff661d13b3d34bd14f0bf93b16169df4765c9f47fc8'), size_on_disk=295924, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7426472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6863/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec534c91cae4413d30477014131d39b51c9c236dfbf150874bd21ec3de821613'), size_on_disk=389025, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648269.6376605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR007B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6f0620b102e7cbfadced7d5530545a185036b53118d3e2b7f074c56403d80f4b'), size_on_disk=263185, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7616518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bd2017e75947ead2c06f9d1936347f7fdcfac7fbff620e0b39077208a02c3bd9'), size_on_disk=303094, blob_last_accessed=1763591393.1271722, blob_last_modified=1758648269.9296618), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/530b5650d91dae3bf169f967af8e800611e3e01daeb1811d6fd21c2612ac4ad9'), size_on_disk=315170, blob_last_accessed=1763591393.6121688, blob_last_modified=1758648266.7956474), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bbe701ffd546a2f4359f821262f7b44232bdc328d9d4719bfdaa8f2aedc6d2e9'), size_on_disk=346079, blob_last_accessed=1763591393.0861723, blob_last_modified=1758648267.589651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6640/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48339729112d22aa5aabf411674bfb90a567ec4c7a07ea7930738645a96823ef'), size_on_disk=329042, blob_last_accessed=1763591392.6391754, blob_last_modified=1758648269.090658), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5654/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38961219eb66dec31505ebcc5088f16684be9891b96fffa8c9a13cd3f13254d0'), size_on_disk=447782, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.1876538), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=composite/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1f8adf8fb4992283714df8587126211d54e6a0580b8be88c1d5e911196aed783'), size_on_disk=4814573, blob_last_accessed=1763591392.0561793, blob_last_modified=1758648268.0256531), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/71a33d11c3ff714ce0730145c740eec4fe88812c6136b8d10b86fa8d4054991f'), size_on_disk=186500, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7286518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7243/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cb59e645fff2683db692b4d2f514602ba210271b632cd113853febe941c6485f'), size_on_disk=281696, blob_last_accessed=1763591392.701175, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7185/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/14f67668aca8d8f8aa2b8af98835da683a7fc3f22aa09cdcd7f943a37632391e'), size_on_disk=201650, blob_last_accessed=1763591393.2191715, blob_last_modified=1758648270.1076627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7276/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4e0d576cc4a6a85be64f2198e5001a9f5b02b506072df6f695cfdfc719cb041'), size_on_disk=204036, blob_last_accessed=1763591392.987173, blob_last_modified=1758648270.4646642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7151/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9969e4607d6bf42424f5d2ce8f0d16e3d2d89d069cae6d75997e99c88a68dc77'), size_on_disk=275572, blob_last_accessed=1763591393.7231681, blob_last_modified=1758648270.1476629), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7266/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9aea2d6f5606d24e1999d450379b1f8c8b9d80eded9e784fb729faa516a27026'), size_on_disk=324480, blob_last_accessed=1763591392.0401795, blob_last_modified=1758648270.379664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5399/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b65d0aa7ddc98940f91f2ae5f07e1d3f81dda688792552b91fcc39fb4f9ededa'), size_on_disk=256729, blob_last_accessed=1763591392.5471761, blob_last_modified=1758648267.972653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6354/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/80b129a46defc7b06e472a35935bfaf410bc5e456475b2885feb2c8a4934a81b'), size_on_disk=173225, blob_last_accessed=1763591393.4981697, blob_last_modified=1758648268.4986553), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/d1cd38451bc41ef14dbc1566c4704b004b2c0661f667210c81dba09bec90540f'), size_on_disk=244487, blob_last_accessed=1763591393.6301687, blob_last_modified=1758648267.3246498), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5961/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/49cac5c2df1adf790a9ac0209db6c0945de50b8191d403e8b1ad386ee2dcb35e'), size_on_disk=321368, blob_last_accessed=1763591392.560176, blob_last_modified=1758648268.215654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5986/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f1179c75ac6e91de03b67a73882f3b9c07c8e435d938d3059de489403adde7a'), size_on_disk=328749, blob_last_accessed=1763591392.691175, blob_last_modified=1758648268.228654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7297/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4bd4088cbfcccdf96def916bb8fd9d673327d3e57c2a4a3e57299743ce4aca7'), size_on_disk=261344, blob_last_accessed=1763591393.588169, blob_last_modified=1758648270.6516652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/62e9a627f20607bc4887ebc9277131f974ec80ff39ac386775f4b8f2f5cb9d1d'), size_on_disk=346782, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7476518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7256/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1af21cbdd7f6f6effff15a1b061a48f85132d582f08c8fd42e04ea58bde7ec00'), size_on_disk=206710, blob_last_accessed=1763591392.586176, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7115/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/205443fd87ce5c793dab86eb75c21dab7dc2ae2edac41d7e6314cfe491bdb01d'), size_on_disk=321850, blob_last_accessed=1763591393.2331715, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7cb5cca74f1bda2f8f1c8ef962e54d34228361eaa9dd8b21e0d1dd7092ff8164'), size_on_disk=229703, blob_last_accessed=1763591392.555176, blob_last_modified=1758648267.0126483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5610/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a86512ae1000a994c86e9aebaf30cf9ce1a59160155f3dc1afa6cbde0a1fc47e'), size_on_disk=307382, blob_last_accessed=1763591393.5291693, blob_last_modified=1758648267.994653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1fa09a5a6e6e69198501d994050adfcad7ff39a3cc2fe94e7ea177414e1bc968'), size_on_disk=303532, blob_last_accessed=1763591393.7091682, blob_last_modified=1758648267.2776496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5614/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1aa702ff57d34ebed961043e0efae064bfdb62f589cbb515311ae8ea37ba8c76'), size_on_disk=429423, blob_last_accessed=1763591393.4671698, blob_last_modified=1758648268.0406532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=MAG002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f7f35257d4c1f7fa5812ea31c1de791e08ab5e3a4e7ca42b2bb12b2c64f7ed7'), size_on_disk=490174, blob_last_accessed=1763591392.829174, blob_last_modified=1758648267.583651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fa1a44bd33df22ec6b5a8baf3be17e51a11522b5fdd545a38e6e163cc5ae8207'), size_on_disk=241911, blob_last_accessed=1763591392.572176, blob_last_modified=1758648267.5226507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7137/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/703fe9f8552adcbdfb5e640509cb62df2fa734b43fe1e98fc1fd342415f823d7'), size_on_disk=515398, blob_last_accessed=1763589053.7698598, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6924/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5e9b3a94d73c1720e6b05ea55000054905a8672edea7f950736ce3fe191f8faf'), size_on_disk=412939, blob_last_accessed=1763591393.748168, blob_last_modified=1758648269.7066607), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6061/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73e03845d0fbff1a5931e4f792e2c1965ade02fc622ffc08e05c267ac6cd3231'), size_on_disk=394930, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.447655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6689/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/90ea249116dc6a9be86974e1ec0acf79558dfd56a5666be2e05d2107b8c4ee60'), size_on_disk=366787, blob_last_accessed=1763591393.6661685, blob_last_modified=1758648269.1406581), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6021/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eea0d101dd58c9619b41a94009fc0dff8156d2d36d7f2b88bb653e96123a032e'), size_on_disk=439545, blob_last_accessed=1763591393.5531693, blob_last_modified=1758648268.258654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6545/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/260c4a280f52024cb12d44559a6189641ee02efbaf20168435196efbfc69d741'), size_on_disk=404812, blob_last_accessed=1763591392.3761773, blob_last_modified=1758648268.8256567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e5b3174998823af40d055f46ca06e7bb44601cbe97b133062cd3e0491dd30ac2'), size_on_disk=323308, blob_last_accessed=1763591393.153172, blob_last_modified=1758648267.2686496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6567/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0e9713a94594dfcbc80134af52449e12ef207ca9149f5a96c43b6cb457b2431c'), size_on_disk=478809, blob_last_accessed=1763591392.6241755, blob_last_modified=1758648268.893657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ac8c590c19e7b95b9aac571b1394976039f25fdd66e62a68ecb99925433f8e54'), size_on_disk=355706, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0226483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f427ea0f4ded3b6ff841153d8ad87d72a26a964c189246fb6fd5c439bd8b0c1e'), size_on_disk=353623, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.812652), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a88564c359f73b5ac3f70a4635f33a1ec57f527f'), size_on_disk=6139, blob_last_accessed=1763587733.4135532, blob_last_modified=1758648264.1986353), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6853/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ed47ecb2aa870fdbf12920fd4486933262c0a24318c9c8c5531e359a3c416c90'), size_on_disk=515114, blob_last_accessed=1763591393.2661712, blob_last_modified=1758648269.4276595), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d4c0cbbf6075a8b5732e3cef2ba154d95d21aa0efc7e49d947cceebd8742b30'), size_on_disk=269007, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4106548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6437/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/556084dbbc174db5450872d6f5e66cc72091380ea32006977dd176e23c105a7f'), size_on_disk=389840, blob_last_accessed=1763591392.714175, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7155/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/521fc1bea6963640645c21cbdb8d1de07793f11213c23ac71d249f3252b32f54'), size_on_disk=304554, blob_last_accessed=1763591393.5841691, blob_last_modified=1758648270.1366627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4b2ff192aa4808cb50b1c1b1667060f98505dd63c1aa58c7d8354e41194957e4'), size_on_disk=289767, blob_last_accessed=1763591393.4871697, blob_last_modified=1758648266.7706473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fc9311b65abb851fbbfd8bb0c0765af59069659f5a715aee546a8644b0af4ec2'), size_on_disk=210948, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648267.6766515), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a76f0849e518faedfe50e6f0c76908a3f6beed34fd0b607603db1859493595d0'), size_on_disk=292312, blob_last_accessed=1763587658.6168654, blob_last_modified=1758648266.7676473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7351/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cc708adf186f28db09dfdd4ac43865a90482b2192306949eab01492598cf23e7'), size_on_disk=198070, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.610665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6770/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/082f14f7b0d5fc47e088efc40ae85a8ec41f99437c66da96ca69af210bce61cc'), size_on_disk=318118, blob_last_accessed=1763591392.8081744, blob_last_modified=1758648269.3776593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6983/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4ee29f84ab279c520521a6c5a5eed9e5e5581b2e39378bdd898eec67d13654f'), size_on_disk=410123, blob_last_accessed=1763591392.6751752, blob_last_modified=1758648269.8636615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6959/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c0cb6c2ec2bda19b0c1da5d5fc2229d0b6d6987d75646fa230c0c14d591cf1f1'), size_on_disk=537588, blob_last_accessed=1763591392.851174, blob_last_modified=1758648269.8686616), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6808/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c71f5371e8c803cfc7fd507d0b3c11f7548db5d7a0fa42c68977169369ea036e'), size_on_disk=422880, blob_last_accessed=1763591392.9491735, blob_last_modified=1758648269.3916593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/646c843611bf88c7f5dcd5b1a664d724c3b24b6434cc52d9a54de62ccaf6c542'), size_on_disk=255949, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.4506505), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5301_5088/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/65ef775934339b80eda59a141cf791a74e1c6f12513d8143307cebe2f11c3738'), size_on_disk=410106, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.9516528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e20f616f6e340b76f6446d2cdffb6cc9feb81297bd0307733f835acf4142cfd'), size_on_disk=269523, blob_last_accessed=1763591392.3461773, blob_last_modified=1758648267.2696495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6572/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3488eaac5e4b9b3418b909138e6b50e3f9ebaf3e7345a4a10b6ced18b66152e4'), size_on_disk=440579, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9506574), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eb61968b5ec5eadd9087e42ead29333f91195dd5b3f9c5a616b41e94ca23c494'), size_on_disk=330245, blob_last_accessed=1763591393.6761684, blob_last_modified=1758648269.710661), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6374/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e410602f56bc94d9831a2bc8bdb865ee4ebe9862a65b603165b655fbcc4b9914'), size_on_disk=441531, blob_last_accessed=1763591393.1891718, blob_last_modified=1758648268.5356555), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6855/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48e48378d24404e926bc1c44b10af60d4b12f82124ff2f80bc0b69b4a0f98740'), size_on_disk=609248, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6556606), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7367/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a7778bb24506047963575fa96126157895c7f329996c729c3e7d6070595955c3'), size_on_disk=324361, blob_last_accessed=1763591392.1631787, blob_last_modified=1758648270.628665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7283/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0dc91bfacca82c5ed23de7757d59f9f808d541d96dd79aab56f0c4252e7aabf3'), size_on_disk=265146, blob_last_accessed=1763591392.7731745, blob_last_modified=1758648270.401664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5a70fa6f22aeda3246d17646cc4ec7c5a46e1993f9981b19d9fd5a8cc99bf11f'), size_on_disk=308858, blob_last_accessed=1763591391.9411802, blob_last_modified=1758648267.0186484)}), refs=frozenset(), last_modified=1758648270.6726654), CachedRevisionInfo(commit_hash='6864b2582ce35f92a8dde59851786319c2c494bc', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc'), size_on_disk=49590351, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6443/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4deb4634e8b4bf141a2410e94b2d99e2b31a0e8f037de9a1c6e14696025bffae'), size_on_disk=494775, blob_last_accessed=1763591392.991173, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6863/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec534c91cae4413d30477014131d39b51c9c236dfbf150874bd21ec3de821613'), size_on_disk=389025, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648269.6376605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7295/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/19bd07f913e0af3460e6b4b5091971ceed8f4aa510cf18b82ec36dabb67c68b8'), size_on_disk=272619, blob_last_accessed=1763591393.1151721, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6513/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c8f9c31704c2f8732b4668e1e6410034b8adf2ada39ff55935aa0199e920d055'), size_on_disk=507367, blob_last_accessed=1763591392.1451788, blob_last_modified=1758648268.8406568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7269/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5bae329d2318610f627fc562a5163253c7d61e0ef83c53808b220133b2299264'), size_on_disk=203263, blob_last_accessed=1763591392.541176, blob_last_modified=1758648270.4096642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6965/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9ae4546d58672f0632757c94b595af8b7c40da215611783fdc1a65000075ec98'), size_on_disk=509677, blob_last_accessed=1763591392.2731779, blob_last_modified=1758648269.933662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/71a33d11c3ff714ce0730145c740eec4fe88812c6136b8d10b86fa8d4054991f'), size_on_disk=186500, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7286518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6640/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48339729112d22aa5aabf411674bfb90a567ec4c7a07ea7930738645a96823ef'), size_on_disk=329042, blob_last_accessed=1763591392.6391754, blob_last_modified=1758648269.090658), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7211/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73d7747698fa1168c89f12c105bbe900d5969d27a82ae2302d0720d761fbfdab'), size_on_disk=298482, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.1276627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7237/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/110a743162ace7792a44dfe48d7afd40bb67c45c722038c981548477ebc5d8fe'), size_on_disk=313207, blob_last_accessed=1763591393.143172, blob_last_modified=1758648270.1426628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1c8a8cb23b1ec073827d6f5343a3bf03a9d7a04cf2ae3fd3ea4cc5ac37673b38'), size_on_disk=342987, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.6226559), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9f7663ccf72b0681ae5866c13500fbde98f35877fd677ac0d49c17447d17d6ce'), size_on_disk=332767, blob_last_accessed=1763591392.1731787, blob_last_modified=1758648266.744647), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6545/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/260c4a280f52024cb12d44559a6189641ee02efbaf20168435196efbfc69d741'), size_on_disk=404812, blob_last_accessed=1763591392.3761773, blob_last_modified=1758648268.8256567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6437/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/556084dbbc174db5450872d6f5e66cc72091380ea32006977dd176e23c105a7f'), size_on_disk=389840, blob_last_accessed=1763591392.714175, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6923/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/454ca6a52d4b305eddc4b6967ef9604be4861269fdcfff419883daed9b2fe48b'), size_on_disk=337423, blob_last_accessed=1763591393.5031695, blob_last_modified=1758648269.6476605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5986/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f1179c75ac6e91de03b67a73882f3b9c07c8e435d938d3059de489403adde7a'), size_on_disk=328749, blob_last_accessed=1763591392.691175, blob_last_modified=1758648268.228654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6506/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/986d9d3b4ed064d66ebaa0f2502b933f1650a20ac4bbbba804e2cde03b9454c0'), size_on_disk=378407, blob_last_accessed=1763591391.97318, blob_last_modified=1758648268.7656565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6983/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4ee29f84ab279c520521a6c5a5eed9e5e5581b2e39378bdd898eec67d13654f'), size_on_disk=410123, blob_last_accessed=1763591392.6751752, blob_last_modified=1758648269.8636615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6532/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3041f19cf0dcb3e0bd9b1178994351a22f3297ccd43c5e291df8bfe0ad8d5d02'), size_on_disk=516437, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.8236568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6738/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e6d612d16538a06c985f1134f3fdfd367944b2a85cedfb8b0ddad1679004c940'), size_on_disk=333560, blob_last_accessed=1763591392.8651738, blob_last_modified=1758648269.3676593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS006b/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7eedae811a5b3dd4389e7e814d35b022bfaf01fc4d2cfb47be6e6786d6d58c64'), size_on_disk=315912, blob_last_accessed=1763591393.2571712, blob_last_modified=1758648266.7546473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/128015caa722289251a45aa3d56cabac6b5946eb4e1be16eb950555fb24ac413'), size_on_disk=172328, blob_last_accessed=1763591393.735168, blob_last_modified=1758648267.2826495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0230df4f1d3748337e5315bb1d22a0373fdab95f9bc900041b0e34966d868718'), size_on_disk=311116, blob_last_accessed=1763591392.9241736, blob_last_modified=1758648267.2506495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5a70fa6f22aeda3246d17646cc4ec7c5a46e1993f9981b19d9fd5a8cc99bf11f'), size_on_disk=308858, blob_last_accessed=1763591391.9411802, blob_last_modified=1758648267.0186484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7240/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4460d188b4cd3b362f33acf0f0b5312a267b37f8fb2dab4b2577c6c3bbb1231'), size_on_disk=307039, blob_last_accessed=1763591392.6101756, blob_last_modified=1758648270.403664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6374/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e410602f56bc94d9831a2bc8bdb865ee4ebe9862a65b603165b655fbcc4b9914'), size_on_disk=441531, blob_last_accessed=1763591393.1891718, blob_last_modified=1758648268.5356555), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6770/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/082f14f7b0d5fc47e088efc40ae85a8ec41f99437c66da96ca69af210bce61cc'), size_on_disk=318118, blob_last_accessed=1763591392.8081744, blob_last_modified=1758648269.3776593), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fc266bb938fa146c46d1a7bd5446d5d27b3bf202'), size_on_disk=10887, blob_last_accessed=1763649507.5438576, blob_last_modified=1763649507.5418575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/00e5602b87ac84670a98c081e6520ca744038f4e9b3a72a78cb61f5926be56b1'), size_on_disk=251087, blob_last_accessed=1763591392.5781758, blob_last_modified=1758648267.8286521), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6021/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eea0d101dd58c9619b41a94009fc0dff8156d2d36d7f2b88bb653e96123a032e'), size_on_disk=439545, blob_last_accessed=1763591393.5531693, blob_last_modified=1758648268.258654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6551/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c3729692991cdcafe9baa3d2805b3866f72fe9bc89ba22c6d2cdae4d5b5bbf5f'), size_on_disk=545477, blob_last_accessed=1763591393.0611725, blob_last_modified=1758648268.8376567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5614/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1aa702ff57d34ebed961043e0efae064bfdb62f589cbb515311ae8ea37ba8c76'), size_on_disk=429423, blob_last_accessed=1763591393.4671698, blob_last_modified=1758648268.0406532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02313d4d85859a7353e56d89b6e14bcec5160849b5c0089ccbecaa1cda5012cf'), size_on_disk=301213, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4356549), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6572/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3488eaac5e4b9b3418b909138e6b50e3f9ebaf3e7345a4a10b6ced18b66152e4'), size_on_disk=440579, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9506574), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ddd58117dbedfe7e9dc3fd946187e7bd5d88fe8b0f38999c6e4b5660938bb787'), size_on_disk=359739, blob_last_accessed=1763591393.5981688, blob_last_modified=1758648267.2726495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6073/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d68ca6411ba47eb63adad81e88fa1341a0ee269897268957ae936dcc9ba7788'), size_on_disk=450031, blob_last_accessed=1763591393.1341722, blob_last_modified=1758648268.3886547), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6421/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1e3bacd3bcf1fcb46cfc5f9ab66c2af57e190f3d006031e7ef37864fbd36aaca'), size_on_disk=495452, blob_last_accessed=1763591392.2161784, blob_last_modified=1758648268.6366558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/530b5650d91dae3bf169f967af8e800611e3e01daeb1811d6fd21c2612ac4ad9'), size_on_disk=315170, blob_last_accessed=1763591393.6121688, blob_last_modified=1758648266.7956474), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bd2017e75947ead2c06f9d1936347f7fdcfac7fbff620e0b39077208a02c3bd9'), size_on_disk=303094, blob_last_accessed=1763591393.1271722, blob_last_modified=1758648269.9296618), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c9b19726dc45896e4f86a5acc52d07cc422f895bfaaaddb332aaceedff36560c'), size_on_disk=178793, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0266485), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7063/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9427d8f8c54e3c0cc9d720958ef15d3859f00cdfb678559e0268387a36a8ef37'), size_on_disk=404051, blob_last_accessed=1763591392.5301762, blob_last_modified=1758648269.8976617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6635/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6c5b7f1fda3a7f5679870f438d13a45558f7f8da0f479c5144d7751bc8f7fbbc'), size_on_disk=420658, blob_last_accessed=1763591391.95018, blob_last_modified=1758648269.3956594), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ef036874e53ee98dfe1b5d84c42749fc40da9279cd573174321ac83201c1651'), size_on_disk=349762, blob_last_accessed=1763591393.0791724, blob_last_modified=1758648267.4966507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4e8332a5db6260f6357f940e62e3e7b622785f386e81cdb9c75b538d318f4a6'), size_on_disk=158978, blob_last_accessed=1763591393.5471692, blob_last_modified=1758648266.7626472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6177/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b91ffef1dfcdf7952df46956d76a4b364aa0d8342453636cf09e6a343607184f'), size_on_disk=387618, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.424655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eb61968b5ec5eadd9087e42ead29333f91195dd5b3f9c5a616b41e94ca23c494'), size_on_disk=330245, blob_last_accessed=1763591393.6761684, blob_last_modified=1758648269.710661), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6689/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/90ea249116dc6a9be86974e1ec0acf79558dfd56a5666be2e05d2107b8c4ee60'), size_on_disk=366787, blob_last_accessed=1763591393.6661685, blob_last_modified=1758648269.1406581), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7185/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/14f67668aca8d8f8aa2b8af98835da683a7fc3f22aa09cdcd7f943a37632391e'), size_on_disk=201650, blob_last_accessed=1763591393.2191715, blob_last_modified=1758648270.1076627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7370/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ff4dce26afb34b291220c17625be4a28078f9721a8b000ea177c4add3bf1a039'), size_on_disk=241010, blob_last_accessed=1763591393.7701678, blob_last_modified=1758648270.6726654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5399/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b65d0aa7ddc98940f91f2ae5f07e1d3f81dda688792552b91fcc39fb4f9ededa'), size_on_disk=256729, blob_last_accessed=1763591392.5471761, blob_last_modified=1758648267.972653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6567/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0e9713a94594dfcbc80134af52449e12ef207ca9149f5a96c43b6cb457b2431c'), size_on_disk=478809, blob_last_accessed=1763591392.6241755, blob_last_modified=1758648268.893657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6390/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f324cdf73148c8b0fa23456bc4f51cff3032d08ed42e44dd9fcc4815a6d969c'), size_on_disk=560601, blob_last_accessed=1763591392.7391748, blob_last_modified=1758648268.6206558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6808/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c71f5371e8c803cfc7fd507d0b3c11f7548db5d7a0fa42c68977169369ea036e'), size_on_disk=422880, blob_last_accessed=1763591392.9491735, blob_last_modified=1758648269.3916593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fc9311b65abb851fbbfd8bb0c0765af59069659f5a715aee546a8644b0af4ec2'), size_on_disk=210948, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648267.6766515), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7367/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a7778bb24506047963575fa96126157895c7f329996c729c3e7d6070595955c3'), size_on_disk=324361, blob_last_accessed=1763591392.1631787, blob_last_modified=1758648270.628665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6448/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/182dc134914a5ddb02992ab6a4f1c12d2450aa331820bf7bd95693dc999bcfae'), size_on_disk=390210, blob_last_accessed=1763591392.9591732, blob_last_modified=1758648268.672656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5654/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38961219eb66dec31505ebcc5088f16684be9891b96fffa8c9a13cd3f13254d0'), size_on_disk=447782, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.1876538), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6055/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/31bb70b4c90654ecb276664f5c914679322b24985ec66d5b396a99f253ebfa17'), size_on_disk=377015, blob_last_accessed=1763591393.5701692, blob_last_modified=1758648268.3076544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/74cbce50c258bdddb7e42c8fd5b0b2a9da74fe3cf89a9ab5a53ee4fde7c1dee6'), size_on_disk=186142, blob_last_accessed=1763591392.842174, blob_last_modified=1758648266.9966483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e91fd764a83947c14f35d7a2d8c0d156c0a61a32a1da96273718ef4be720787'), size_on_disk=361855, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.7196517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7cb5cca74f1bda2f8f1c8ef962e54d34228361eaa9dd8b21e0d1dd7092ff8164'), size_on_disk=229703, blob_last_accessed=1763591392.555176, blob_last_modified=1758648267.0126483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ac8c590c19e7b95b9aac571b1394976039f25fdd66e62a68ecb99925433f8e54'), size_on_disk=355706, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0226483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7332/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f7f62ee008fa2b5cc688cbd80c888f1bf9da538caa245b63581520d485338a99'), size_on_disk=309595, blob_last_accessed=1763591393.1021724, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7151/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9969e4607d6bf42424f5d2ce8f0d16e3d2d89d069cae6d75997e99c88a68dc77'), size_on_disk=275572, blob_last_accessed=1763591393.7231681, blob_last_modified=1758648270.1476629), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4d098c33564f99ec6aed77d585d1f6e871806f441f099790eef4d6478e322af'), size_on_disk=264459, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7486472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d3e36418bd46acf9537a8f864b1a1361a062d72fec2b093789b88ed655ec117'), size_on_disk=377393, blob_last_accessed=1763591393.0451727, blob_last_modified=1758648267.0136483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dcb33135a9d854d17a6b80348f6bee8becea9ca3808583a034f89c5e4f460c54'), size_on_disk=218643, blob_last_accessed=1763591393.4831698, blob_last_modified=1758648267.9636528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6140/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/66c2da17957d7e6a8b159d3573879ac779d0094e35150b51e2643f7ddee36e1f'), size_on_disk=332958, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4236548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6573/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e435989051765cf2955d8cd733904e8b60e5f3121e1c2fdc987f4e6f71fe3970'), size_on_disk=265022, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9586573), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6354/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/80b129a46defc7b06e472a35935bfaf410bc5e456475b2885feb2c8a4934a81b'), size_on_disk=173225, blob_last_accessed=1763591393.4981697, blob_last_modified=1758648268.4986553), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f427ea0f4ded3b6ff841153d8ad87d72a26a964c189246fb6fd5c439bd8b0c1e'), size_on_disk=353623, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.812652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6954/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/aeed3c55d81a4eb0c568486e476211c599ce2bf37f9dff22c7dfca23f5d80495'), size_on_disk=476054, blob_last_accessed=1763591393.011173, blob_last_modified=1758648269.943662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6739/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/58b5ed63f2197f406dd3df6a569975d776722193007912718979eaed4d4e5f9c'), size_on_disk=399160, blob_last_accessed=1763591392.6551754, blob_last_modified=1758648269.331659), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6924/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5e9b3a94d73c1720e6b05ea55000054905a8672edea7f950736ce3fe191f8faf'), size_on_disk=412939, blob_last_accessed=1763591393.748168, blob_last_modified=1758648269.7066607), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=composite/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1f8adf8fb4992283714df8587126211d54e6a0580b8be88c1d5e911196aed783'), size_on_disk=4814573, blob_last_accessed=1763591392.0561793, blob_last_modified=1758648268.0256531), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR007B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6f0620b102e7cbfadced7d5530545a185036b53118d3e2b7f074c56403d80f4b'), size_on_disk=263185, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7616518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6764/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec87eef9e2e8cdf3c3a10ead28dcf1bfa76e94e12a42422a46c8559fb18207c2'), size_on_disk=75684, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.4036593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4b2ff192aa4808cb50b1c1b1667060f98505dd63c1aa58c7d8354e41194957e4'), size_on_disk=289767, blob_last_accessed=1763591393.4871697, blob_last_modified=1758648266.7706473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d50648ed5c9f593f830aff661d13b3d34bd14f0bf93b16169df4765c9f47fc8'), size_on_disk=295924, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7426472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7256/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1af21cbdd7f6f6effff15a1b061a48f85132d582f08c8fd42e04ea58bde7ec00'), size_on_disk=206710, blob_last_accessed=1763591392.586176, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5801/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/12e91a415cc2a149af4c34c3c8b032821ff301e2828dfb9f4636bd6b2913ea3d'), size_on_disk=360485, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.1636536), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=MAG002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f7f35257d4c1f7fa5812ea31c1de791e08ab5e3a4e7ca42b2bb12b2c64f7ed7'), size_on_disk=490174, blob_last_accessed=1763591392.829174, blob_last_modified=1758648267.583651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fa1a44bd33df22ec6b5a8baf3be17e51a11522b5fdd545a38e6e163cc5ae8207'), size_on_disk=241911, blob_last_accessed=1763591392.572176, blob_last_modified=1758648267.5226507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dc74862f02ff12b8c906046f1f03953d33f5516091d58397b0f9a0fc05bb4b5c'), size_on_disk=315435, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.5006506), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6731/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/23ecd71c4e3c1c547295bb756d4654eb413ff288d10118ab286cd21ccd95b666'), size_on_disk=346187, blob_last_accessed=1763591392.1871786, blob_last_modified=1758648269.1906583), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6920/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/45122fcfb3abc3a43631023712c224dc6e00dba566fb2e15deb4ab4501ad2538'), size_on_disk=446998, blob_last_accessed=1763591392.8881738, blob_last_modified=1758648269.6416605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6657/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/95ad2225152f51e2e31c030ff6708aeebccee5e548035d6f43c9cc3554bdfe5e'), size_on_disk=507730, blob_last_accessed=1763591393.163172, blob_last_modified=1758648269.1676583), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR006B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fcc5e886ec7c6363a5ab51b03d3da189ac3e0cde0a0f79d4f0c46043a6b61838'), size_on_disk=253857, blob_last_accessed=1763591392.0501795, blob_last_modified=1758648267.785652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7283/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0dc91bfacca82c5ed23de7757d59f9f808d541d96dd79aab56f0c4252e7aabf3'), size_on_disk=265146, blob_last_accessed=1763591392.7731745, blob_last_modified=1758648270.401664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e871cf06f596bfe74896b62aa28d6b1e6f2f0b8408630fb146c7e29cfd88fbf0'), size_on_disk=232721, blob_last_accessed=1763591392.7871745, blob_last_modified=1758648267.991653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS011/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ea7cb0559b342dd0ec278e7d5846a5ba0c0d867ea04d545f72e0d1c9472a0cc'), size_on_disk=363979, blob_last_accessed=1763591392.9791732, blob_last_modified=1758648267.0116484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5301_5088/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/65ef775934339b80eda59a141cf791a74e1c6f12513d8143307cebe2f11c3738'), size_on_disk=410106, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.9516528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7118/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/be8bd409b83526d5671501de6ef2980177955fdcc3a13d5e153d2593493b2a1e'), size_on_disk=346308, blob_last_accessed=1763591392.9281735, blob_last_modified=1758648270.1336627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e20f616f6e340b76f6446d2cdffb6cc9feb81297bd0307733f835acf4142cfd'), size_on_disk=269523, blob_last_accessed=1763591392.3461773, blob_last_modified=1758648267.2696495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7266/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9aea2d6f5606d24e1999d450379b1f8c8b9d80eded9e784fb729faa516a27026'), size_on_disk=324480, blob_last_accessed=1763591392.0401795, blob_last_modified=1758648270.379664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6527/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/10081f44b1c2f5ad32489da51fc9ac5b5289b54688a7e78725db247a644b7e4d'), size_on_disk=494640, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.882657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7297/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4bd4088cbfcccdf96def916bb8fd9d673327d3e57c2a4a3e57299743ce4aca7'), size_on_disk=261344, blob_last_accessed=1763591393.588169, blob_last_modified=1758648270.6516652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/d1cd38451bc41ef14dbc1566c4704b004b2c0661f667210c81dba09bec90540f'), size_on_disk=244487, blob_last_accessed=1763591393.6301687, blob_last_modified=1758648267.3246498), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5610/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a86512ae1000a994c86e9aebaf30cf9ce1a59160155f3dc1afa6cbde0a1fc47e'), size_on_disk=307382, blob_last_accessed=1763591393.5291693, blob_last_modified=1758648267.994653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6061/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73e03845d0fbff1a5931e4f792e2c1965ade02fc622ffc08e05c267ac6cd3231'), size_on_disk=394930, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.447655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5617/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bc01241e8363373c995a0fa7439aa1e432a60eb8a7805124fff302311f638361'), size_on_disk=351950, blob_last_accessed=1763591393.6541686, blob_last_modified=1758648268.1026535), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6651/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7644af4cc956ea08809ab6c80c65095e3fdbd36e056040c0eaded3773d546359'), size_on_disk=559951, blob_last_accessed=1763591393.6361687, blob_last_modified=1758648269.1856585), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/11485fd7bb94d0093ce2508d541fea3ce64f934197d8a5f7bb26ecbdb59f8dbb'), size_on_disk=124642, blob_last_accessed=1763591392.3321776, blob_last_modified=1758648267.0666487), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7115/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/205443fd87ce5c793dab86eb75c21dab7dc2ae2edac41d7e6314cfe491bdb01d'), size_on_disk=321850, blob_last_accessed=1763591393.2331715, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6872/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02fabe6e32f52fe6fca923beb0a6cc2898d991304e23a3e75f3ae8a6bcf361c6'), size_on_disk=274362, blob_last_accessed=1763591393.6231687, blob_last_modified=1758648269.6966608), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7243/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cb59e645fff2683db692b4d2f514602ba210271b632cd113853febe941c6485f'), size_on_disk=281696, blob_last_accessed=1763591392.701175, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/62e9a627f20607bc4887ebc9277131f974ec80ff39ac386775f4b8f2f5cb9d1d'), size_on_disk=346782, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7476518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6855/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48e48378d24404e926bc1c44b10af60d4b12f82124ff2f80bc0b69b4a0f98740'), size_on_disk=609248, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6556606), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e5b3174998823af40d055f46ca06e7bb44601cbe97b133062cd3e0491dd30ac2'), size_on_disk=323308, blob_last_accessed=1763591393.153172, blob_last_modified=1758648267.2686496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=KB001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38c939d54d3ed77a09650d2ccf00a6ed0e20390b8be722f1c3ed7881a5904a60'), size_on_disk=351677, blob_last_accessed=1763591392.9151735, blob_last_modified=1758648267.5566509), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5961/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/49cac5c2df1adf790a9ac0209db6c0945de50b8191d403e8b1ad386ee2dcb35e'), size_on_disk=321368, blob_last_accessed=1763591392.560176, blob_last_modified=1758648268.215654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7258/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7b1860ba3be7dbf053203fa5690da2002f7841186940231acea6064e70f0bdc8'), size_on_disk=171331, blob_last_accessed=1763591392.6171756, blob_last_modified=1758648270.3746638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7331/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f159a9811b8ac1b5e252b52427979a3bddce2e7131b13f84046b31e47f7fa399'), size_on_disk=242821, blob_last_accessed=1763591393.2501714, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a76f0849e518faedfe50e6f0c76908a3f6beed34fd0b607603db1859493595d0'), size_on_disk=292312, blob_last_accessed=1763587658.6168654, blob_last_modified=1758648266.7676473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7137/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/703fe9f8552adcbdfb5e640509cb62df2fa734b43fe1e98fc1fd342415f823d7'), size_on_disk=515398, blob_last_accessed=1763589053.7698598, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bbe701ffd546a2f4359f821262f7b44232bdc328d9d4719bfdaa8f2aedc6d2e9'), size_on_disk=346079, blob_last_accessed=1763591393.0861723, blob_last_modified=1758648267.589651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7276/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4e0d576cc4a6a85be64f2198e5001a9f5b02b506072df6f695cfdfc719cb041'), size_on_disk=204036, blob_last_accessed=1763591392.987173, blob_last_modified=1758648270.4646642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1fa09a5a6e6e69198501d994050adfcad7ff39a3cc2fe94e7ea177414e1bc968'), size_on_disk=303532, blob_last_accessed=1763591393.7091682, blob_last_modified=1758648267.2776496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6708/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/35520976736159652e3be8066e77b1371522f63a8b3564d9b0d9f5b1598a144d'), size_on_disk=253955, blob_last_accessed=1763591393.6881683, blob_last_modified=1758648269.1996584), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ab338795b8bbf334650386a123f6e0df41363cb5dcbb778d485ce58804e96b29'), size_on_disk=375506, blob_last_accessed=1763591392.9711733, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6959/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c0cb6c2ec2bda19b0c1da5d5fc2229d0b6d6987d75646fa230c0c14d591cf1f1'), size_on_disk=537588, blob_last_accessed=1763591392.851174, blob_last_modified=1758648269.8686616), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/33d7303ed077f489ee28021daca0bbc859feb1cfd383dfc9a856ea13994801a9'), size_on_disk=331877, blob_last_accessed=1763591393.292171, blob_last_modified=1758648268.2226539), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7155/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/521fc1bea6963640645c21cbdb8d1de07793f11213c23ac71d249f3252b32f54'), size_on_disk=304554, blob_last_accessed=1763591393.5841691, blob_last_modified=1758648270.1366627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6853/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ed47ecb2aa870fdbf12920fd4486933262c0a24318c9c8c5531e359a3c416c90'), size_on_disk=515114, blob_last_accessed=1763591393.2661712, blob_last_modified=1758648269.4276595), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6454/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c35b60861d2fc7a3f4256869e14e629aed07cd2ccce76608578ceb8c6ec423e1'), size_on_disk=498326, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.7226562), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6798/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/899cde55032cd593be0013e1f6ff33fe9a12f4616477b1410d86fae1b2008a09'), size_on_disk=342376, blob_last_accessed=1763591392.7951744, blob_last_modified=1758648269.4006593), CachedFileInfo(file_name='annotated_features_meta.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features_meta.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/2be4b693d333698f7ada7ef40ffaf927c0160d7975ebcf6e215cbc4ba1be1604'), size_on_disk=11813, blob_last_accessed=1763587594.724117, blob_last_modified=1758648264.795638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=MAG001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/30b63d014686a103b5e7fc6571e902fa9c08630a96557cc12ec9dfcf60871306'), size_on_disk=347333, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.559651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6677/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4ee6e1d3e2b9a9bbcc8851048d2b16f4aea6a1e2f752afb9e0bf75a56adf7042'), size_on_disk=322147, blob_last_accessed=1763591392.393177, blob_last_modified=1758648269.0826578), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5690/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4831e3e037d421a14efbe1da8c369015ec7d01df351bb27f0ef0400c8b94c693'), size_on_disk=311754, blob_last_accessed=1763591392.9021738, blob_last_modified=1758648268.211654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d4c0cbbf6075a8b5732e3cef2ba154d95d21aa0efc7e49d947cceebd8742b30'), size_on_disk=269007, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4106548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/646c843611bf88c7f5dcd5b1a664d724c3b24b6434cc52d9a54de62ccaf6c542'), size_on_disk=255949, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.4506505), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6861/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6955b1a1746fe41162ea1d7a098482bbfdb3d0e411c809b52d698e285627ce03'), size_on_disk=385103, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6146603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7351/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cc708adf186f28db09dfdd4ac43865a90482b2192306949eab01492598cf23e7'), size_on_disk=198070, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.610665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6b92129c2fb99028b6cab9509cf1a4c40b142109cde34c9a130d375001dcd211'), size_on_disk=267273, blob_last_accessed=1763591392.7811744, blob_last_modified=1758648269.9106617)}), refs=frozenset({'main'}), last_modified=1763649507.5418575)}), last_accessed=1763649507.5438576, last_modified=1763649507.5418575), CachedRepoInfo(repo_id='BrentLab/kemmeren_2014', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014'), size_on_disk=319245924, nb_files=5, revisions=frozenset({CachedRevisionInfo(commit_hash='b76f0dfe8477b300000faecf16b7d315fbf59344', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/b76f0dfe8477b300000faecf16b7d315fbf59344'), size_on_disk=319228170, files=frozenset({CachedFileInfo(file_name='kemmeren_2014.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/b76f0dfe8477b300000faecf16b7d315fbf59344/kemmeren_2014.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/3c463017444bd7690959c0d6fff0d0ebe2faa7b916dd9c44e305156f6c98b863'), size_on_disk=319218929, blob_last_accessed=1760552530.221978, blob_last_modified=1756832530.9644527), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/b76f0dfe8477b300000faecf16b7d315fbf59344/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/199dce9d01d3884716dd3a785a85751cb9b1de3e'), size_on_disk=9241, blob_last_accessed=1760552530.0869784, blob_last_modified=1758654056.5908144)}), refs=frozenset(), last_modified=1758654056.5908144), CachedRevisionInfo(commit_hash='95a5f915ad49dfe4af75632861d9da69de2b525f', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/95a5f915ad49dfe4af75632861d9da69de2b525f'), size_on_disk=10286, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/95a5f915ad49dfe4af75632861d9da69de2b525f/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/05a15ab9764763e5d1957023162e13b0068a5697'), size_on_disk=10286, blob_last_accessed=1765901283.9662895, blob_last_modified=1765415815.7076464)}), refs=frozenset({'main'}), last_modified=1765415815.7076464), CachedRevisionInfo(commit_hash='a6c9a954a1def1774b68582f117ad1abc5864a07', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07'), size_on_disk=319226397, files=frozenset({CachedFileInfo(file_name='kemmeren_2014.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07/kemmeren_2014.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/3c463017444bd7690959c0d6fff0d0ebe2faa7b916dd9c44e305156f6c98b863'), size_on_disk=319218929, blob_last_accessed=1760552530.221978, blob_last_modified=1756832530.9644527), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/a29a64128c10ef9691ee773131972c61f720ba07'), size_on_disk=5007, blob_last_accessed=1756832529.5094638, blob_last_modified=1756832529.5404634), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756832529.5094638, blob_last_modified=1756832529.5434635)}), refs=frozenset(), last_modified=1756832530.9644527)}), last_accessed=1765901283.9662895, last_modified=1765415815.7076464), CachedRepoInfo(repo_id='BrentLab/hu_2007_reimand_2010', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010'), size_on_disk=42682732, nb_files=4, revisions=frozenset({CachedRevisionInfo(commit_hash='31ccd35daf785420014a1346a7db064dd306d4f8', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8'), size_on_disk=42682732, files=frozenset({CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756844741.8097973, blob_last_modified=1756844741.8767972), CachedFileInfo(file_name='hu_2007_reimand_2010.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/hu_2007_reimand_2010.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/eb9be5a1f01907a81eb2ab4fbbee36ff8c50186f'), size_on_disk=63, blob_last_accessed=1756844741.7997973, blob_last_modified=1756844741.8557973), CachedFileInfo(file_name='hu_2007_reimand_2010.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/hu_2007_reimand_2010.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/47a15f3e23f8cb50af3217d00c6439d29a7422b5190116e4453a5f78cb22540d'), size_on_disk=42677516, blob_last_accessed=1756844742.1787965, blob_last_modified=1756844742.1477966), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/886f4614dfd1ef33709d462908ae18608c13e8ba'), size_on_disk=2692, blob_last_accessed=1756844742.0937967, blob_last_modified=1756844742.1537964)}), refs=frozenset({'main'}), last_modified=1756844742.1537964)}), last_accessed=1756844742.1787965, last_modified=1756844742.1537964), CachedRepoInfo(repo_id='BrentLab/rossi_2021', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021'), size_on_disk=213734041, nb_files=48, revisions=frozenset({CachedRevisionInfo(commit_hash='dcf529428b66f5efc8c44ea06fb4733c41952e03', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/dcf529428b66f5efc8c44ea06fb4733c41952e03'), size_on_disk=15570962, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/dcf529428b66f5efc8c44ea06fb4733c41952e03/genome_map/accession=SRR11466106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/04bacc0f8c03ee000ba3b69320522d0b1be909686474b6e8a126173a0ceae8c4'), size_on_disk=15547870, blob_last_accessed=1762805142.7999256, blob_last_modified=1756944357.1972551), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/dcf529428b66f5efc8c44ea06fb4733c41952e03/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/192e51c15075f65f15da70558261e4c95bb1392a'), size_on_disk=8396, blob_last_accessed=1762805108.5211468, blob_last_modified=1762805108.5201466), CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/dcf529428b66f5efc8c44ea06fb4733c41952e03/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1762805126.6580298, blob_last_modified=1756944310.9395182)}), refs=frozenset({'main'}), last_modified=1762805108.5201466), CachedRevisionInfo(commit_hash='6b29baae54286d5fbdd1c70f499c03319badad51', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51'), size_on_disk=213707016, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466115/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/c08ce1194dd98cb028a6038944317e70ac1545e32b21dd323eafa57b16611e6e'), size_on_disk=3503431, blob_last_accessed=1757598690.9468212, blob_last_modified=1757104405.9427147), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466143/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/54bd4af6ed8caf52a5810598d96c9b235266542586a692112cb247a209c2649f'), size_on_disk=10801408, blob_last_accessed=1757598691.2688208, blob_last_modified=1757104413.3726768), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466107/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/2f924bc58d82a21b621b051addd1913c77369afaa738171ef0962548fc1a1021'), size_on_disk=5670198, blob_last_accessed=1757598690.8608212, blob_last_modified=1757104405.0417192), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466124/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/39ef32412cae257b724c3b797ea482eeffb5b29afef4246d997ea525a2124054'), size_on_disk=7162800, blob_last_accessed=1757598691.075821, blob_last_modified=1757104409.5966961), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466108/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/56875f81a3cc7c1d40b3042b2c443058eb0f9a2b7e7ebc2e1704c098be612628'), size_on_disk=1765819, blob_last_accessed=1757598690.8238213, blob_last_modified=1757104407.1947083), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466127/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/1565c5175e05c322f160529cb4bb74d5e8468458f150f228d6aadf2b670c3b4a'), size_on_disk=11670434, blob_last_accessed=1757598691.092821, blob_last_modified=1757104411.0776885), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466125/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/0bba21490ea0a6297031b0ff3219fc0158d9bbcf15099906c86a770b1a1312fc'), size_on_disk=2624759, blob_last_accessed=1757598691.081821, blob_last_modified=1757104409.4426968), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466129/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/b290d4c9436756b444d0da1af1229b0a2a6e877fd968c5d01b73efa96e62cff8'), size_on_disk=6955953, blob_last_accessed=1757598691.196821, blob_last_modified=1757104410.78769), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466126/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/34292b90d99b5a5be542c7dd10b11336605330639d92e93b092e567a56fe7d93'), size_on_disk=2850164, blob_last_accessed=1757598691.086821, blob_last_modified=1757104410.0976934), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466144/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/57b3d2c7917bfa29312cc51c921beff914858c61762e28a658eb29d8eaf3348b'), size_on_disk=918271, blob_last_accessed=1757598691.2758207, blob_last_modified=1757104413.554676), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466112/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8f3441964a046d3c148b41f335948a085d26884e1a890e515711a2d1b48a40be'), size_on_disk=4883601, blob_last_accessed=1757598690.9128213, blob_last_modified=1757104408.5027015), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466142/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/3dc044b5cc1b7bc8070d8de4b7b870524b296f26b4f43b22dce2dcbf161d4c74'), size_on_disk=1739520, blob_last_accessed=1757598691.2588208, blob_last_modified=1757104415.5896657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466149/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/ae4d5237fde214a92d3267fbe11f6f1f88569cc7f3f7ccb40fed200871bf33b1'), size_on_disk=10933582, blob_last_accessed=1757598691.3518207, blob_last_modified=1757104415.1316679), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466109/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/6588704c47423e671054df9cb2318c045138b0018f2eefff61ef91a25848eb58'), size_on_disk=5259413, blob_last_accessed=1757598690.8298213, blob_last_modified=1757104404.9987195), CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1762805126.6580298, blob_last_modified=1756944310.9395182), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466141/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/e9efb807ce35fc3e43b1ff0d27b80646c7d137c587abd9d6247393e44d4f30a0'), size_on_disk=986450, blob_last_accessed=1757598691.2628207, blob_last_modified=1757104413.129678), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/04bacc0f8c03ee000ba3b69320522d0b1be909686474b6e8a126173a0ceae8c4'), size_on_disk=15547870, blob_last_accessed=1762805142.7999256, blob_last_modified=1756944357.1972551), CachedFileInfo(file_name='genome_map.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/51978dc2125afb40a41a29672f8ae750308d9a63'), size_on_disk=5311747, blob_last_accessed=1757598690.9468212, blob_last_modified=1757104406.477712), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466118/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/192057e6004dd5cd303e8b1abf54cb72005e211c315665e28eec7b617a84d95a'), size_on_disk=728586, blob_last_accessed=1757598691.0068212, blob_last_modified=1757104407.0917087), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466122/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/6362da9e07f02d4c9dc05f15f638ca1b1bf0de5989da4ee8c6f0768f2453f98f'), size_on_disk=1249990, blob_last_accessed=1757598691.0398211, blob_last_modified=1757104411.9016843), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466150/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/80cd2d325e5d7a50c6ecbf79a052f45c54a93b9423e90735bf8989aada9bb9eb'), size_on_disk=3234303, blob_last_accessed=1757598691.3798206, blob_last_modified=1757104417.837654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466139/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/25c7d3ffbacca76fd998edffe0c762e220f448140019aa2d389facff5117ce48'), size_on_disk=176811, blob_last_accessed=1757598691.2478209, blob_last_modified=1757104412.6266806), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466110/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/d5283e08e898b4f59837f7ff2064f79c8a828994f868c637894f4e07df36daa5'), size_on_disk=13123795, blob_last_accessed=1757598690.8318212, blob_last_modified=1757104407.6787057), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466132/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/cc41509e63473d565341ed36fd6881fecfd537e59c003f4779d4c4ec7b49cb54'), size_on_disk=1906184, blob_last_accessed=1757598691.161821, blob_last_modified=1757104413.4366765), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466131/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/bdce0842b5568e406a799dbe36b4b9645a30fa58eb0a707f24ed3bb3900fbf67'), size_on_disk=11759994, blob_last_accessed=1757598691.157821, blob_last_modified=1757104411.1146884), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466111/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/ba431fc9a05f40581e450429d467226a8e3c49195de355437f9044bb0fdfe524'), size_on_disk=5749460, blob_last_accessed=1757598690.9028213, blob_last_modified=1757104404.9707196), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466114/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/2c07f671cca471d6ad3e10e071cd1b976a89a45ff32920913d8fc36dbeaa1678'), size_on_disk=10992797, blob_last_accessed=1757598690.9498212, blob_last_modified=1757104406.3287127), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466123/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/57b7ca5d419676590538573f295b99db05a952e20cb4c6b2a1618974611d5051'), size_on_disk=3672899, blob_last_accessed=1757598691.073821, blob_last_modified=1757104409.590696), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466135/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/d44d381db0b8c0cb4960796e48d3d16e3d766aff87cbc686789a0c5799ba0a98'), size_on_disk=5204256, blob_last_accessed=1757598691.180821, blob_last_modified=1757104412.4376817), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466113/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/acec3b041a74ab45dbeb03e7f183c2b3d16479aadd64a81f80a74aa42d56368d'), size_on_disk=5866774, blob_last_accessed=1757598690.921821, blob_last_modified=1757104409.0226989), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466134/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/0b23434181eb1fc864e858cfca82d2d8fed1524a8f7dbea3bf576f6f452cf08b'), size_on_disk=12398132, blob_last_accessed=1757598691.169821, blob_last_modified=1757104412.552681), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466140/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/25f98a891820b5a91c11629ed5615d40ffe1fe4a6cba1b2c6642067d76b5be87'), size_on_disk=68174, blob_last_accessed=1757598691.2588208, blob_last_modified=1757104415.6456654), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/09f6dce90ec0e64d8f18614091dd18ad2e86213a'), size_on_disk=4403, blob_last_accessed=1757598690.987821, blob_last_modified=1757104409.9196944), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466136/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/f5e7fd644d30ae0c8e2c72bc341155bfe5df2ce102b68b08ff4d243470026fcc'), size_on_disk=343033, blob_last_accessed=1757598691.1958208, blob_last_modified=1757104411.7116852), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466119/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/0157d9136c2c2bb5640866449adf4bab8c62b3d7f7fb4262981908c77986f89e'), size_on_disk=12516152, blob_last_accessed=1757598691.0068212, blob_last_modified=1757104412.3166823), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466120/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/4220a31e6a7be79341828d41da77ef77fb5a8026980d4445fec18c57b5229c1f'), size_on_disk=3644577, blob_last_accessed=1757598691.034821, blob_last_modified=1757104408.3467023), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466121/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/78d0f377f8a756b28f2f5296932368c9c6b764d2972dc93aa6dd354591d29860'), size_on_disk=7053621, blob_last_accessed=1757598691.0328212, blob_last_modified=1757104408.5097015), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1757598690.9228213, blob_last_modified=1757104403.6017265), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466116/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/587a7b86dffe3d7ade9adeeb94029ea1de54e61fb2c2445dc527979b7f1c24ac'), size_on_disk=5596311, blob_last_accessed=1757598690.988821, blob_last_modified=1757104407.3507075), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466130/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/73db821736e3e47c09da250e369fa06812e02e5abe7a4a3ceab5d1020422b66c'), size_on_disk=1920920, blob_last_accessed=1757598691.1328208, blob_last_modified=1757104410.9306893), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466117/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/49065d4cee74c20f34303405e34e284e29f19082b76fa883a38aeab24cadf674'), size_on_disk=1742958, blob_last_accessed=1757598690.995821, blob_last_modified=1757104411.8276846), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466128/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/f631797d2bc13a580f570637d94a0b9ea37deb2fd7ef92e32c66fd9014260525'), size_on_disk=1059587, blob_last_accessed=1757598691.118821, blob_last_modified=1757104410.6996903), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466133/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/b6d6913e14b52d5d51dab2f254815da89e03cebe75e80356b27cf18e31eb048f'), size_on_disk=5090722, blob_last_accessed=1757598691.169821, blob_last_modified=1757104412.180683)}), refs=frozenset(), last_modified=1757104417.837654), CachedRevisionInfo(commit_hash='6aac54b4b3fbb414561a8b6cc7acf87880c3c3c2', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6aac54b4b3fbb414561a8b6cc7acf87880c3c3c2'), size_on_disk=4665, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6aac54b4b3fbb414561a8b6cc7acf87880c3c3c2/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/14e1e02ad3804c0a4146a52509460e9cbb759d93'), size_on_disk=4665, blob_last_accessed=1758306843.4906857, blob_last_modified=1758156479.1516545)}), refs=frozenset(), last_modified=1758156479.1516545), CachedRevisionInfo(commit_hash='824bf517ff0722a085c86ac7924df0ab1278c8bf', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/824bf517ff0722a085c86ac7924df0ab1278c8bf'), size_on_disk=14696, files=frozenset({CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/824bf517ff0722a085c86ac7924df0ab1278c8bf/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1762805126.6580298, blob_last_modified=1756944310.9395182)}), refs=frozenset(), last_modified=1756944310.9395182), CachedRevisionInfo(commit_hash='3ce77eb2070d7bb43049ba26bb462fded0ff7863', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/3ce77eb2070d7bb43049ba26bb462fded0ff7863'), size_on_disk=15562566, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/3ce77eb2070d7bb43049ba26bb462fded0ff7863/genome_map/accession=SRR11466106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/04bacc0f8c03ee000ba3b69320522d0b1be909686474b6e8a126173a0ceae8c4'), size_on_disk=15547870, blob_last_accessed=1762805142.7999256, blob_last_modified=1756944357.1972551), CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/3ce77eb2070d7bb43049ba26bb462fded0ff7863/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1762805126.6580298, blob_last_modified=1756944310.9395182)}), refs=frozenset(), last_modified=1756944357.1972551), CachedRevisionInfo(commit_hash='1acaa5629922414e8efe23a5d023bd44965824c8', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1acaa5629922414e8efe23a5d023bd44965824c8'), size_on_disk=4683, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1acaa5629922414e8efe23a5d023bd44965824c8/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/bb690d71edd44885609ed660d926f3cf2c4c473e'), size_on_disk=4683, blob_last_accessed=1758306349.7238934, blob_last_modified=1758156991.1235294)}), refs=frozenset(), last_modified=1758156991.1235294), CachedRevisionInfo(commit_hash='22ebbfcf62a7ed665fb8eceaea53b58b45a1709e', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/22ebbfcf62a7ed665fb8eceaea53b58b45a1709e'), size_on_disk=4602, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/22ebbfcf62a7ed665fb8eceaea53b58b45a1709e/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/e03b8d10ba59c549907a309ad8343133b7b902fc'), size_on_disk=4602, blob_last_accessed=1757613581.062093, blob_last_modified=1757613581.0610929)}), refs=frozenset(), last_modified=1757613581.0610929), CachedRevisionInfo(commit_hash='1e07e46fd0b5348243487ac4a573b570a2a3946b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1e07e46fd0b5348243487ac4a573b570a2a3946b'), size_on_disk=4683, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1e07e46fd0b5348243487ac4a573b570a2a3946b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/bb690d71edd44885609ed660d926f3cf2c4c473e'), size_on_disk=4683, blob_last_accessed=1758306349.7238934, blob_last_modified=1758156991.1235294)}), refs=frozenset(), last_modified=1758156991.1235294), CachedRevisionInfo(commit_hash='664d95cdd482d753bc139d26119061c2fa6eaa76', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/664d95cdd482d753bc139d26119061c2fa6eaa76'), size_on_disk=4679, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/664d95cdd482d753bc139d26119061c2fa6eaa76/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/ff6f75db42c0690f0e0285e56c1aa95bc5cf1300'), size_on_disk=4679, blob_last_accessed=1758157110.1705706, blob_last_modified=1758157110.1695707)}), refs=frozenset(), last_modified=1758157110.1695707)}), last_accessed=1762805142.7999256, last_modified=1762805108.5201466), CachedRepoInfo(repo_id='BrentLab/harbison_2004', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004'), size_on_disk=70377892, nb_files=10, revisions=frozenset({CachedRevisionInfo(commit_hash='2916ad316cc0d8a1ce2e7f35d3707b831b203e87', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/2916ad316cc0d8a1ce2e7f35d3707b831b203e87'), size_on_disk=7160, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/2916ad316cc0d8a1ce2e7f35d3707b831b203e87/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/7e3ec66f5479346ca9d082545ff0b6a960fb60d5'), size_on_disk=7160, blob_last_accessed=1758649475.233073, blob_last_modified=1758155946.7669883)}), refs=frozenset(), last_modified=1758155946.7669883), CachedRevisionInfo(commit_hash='073cdeb1b541c28b1a97df9dbc6be6af1d9c23b6', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/073cdeb1b541c28b1a97df9dbc6be6af1d9c23b6'), size_on_disk=5145, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/073cdeb1b541c28b1a97df9dbc6be6af1d9c23b6/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/f7a6cf3dd4477f08e508f38665350e8d11589963'), size_on_disk=5145, blob_last_accessed=1755876214.0380862, blob_last_modified=1755876188.1642818)}), refs=frozenset(), last_modified=1755876188.1642818), CachedRevisionInfo(commit_hash='a0f6f1b23505c4e6a471e65a43a33ff5cced0733', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a0f6f1b23505c4e6a471e65a43a33ff5cced0733'), size_on_disk=25423545, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a0f6f1b23505c4e6a471e65a43a33ff5cced0733/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b5fbd9e98fd8ddadeeb5631e3b6f5055e917c98d'), size_on_disk=7679, blob_last_accessed=1759345152.2162483, blob_last_modified=1758649637.161202), CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a0f6f1b23505c4e6a471e65a43a33ff5cced0733/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/91904b3e84754bd6011cd4b385bbc99d69a899e5c7562f039a121a744ba95a0a'), size_on_disk=25415866, blob_last_accessed=1763588775.480192, blob_last_modified=1758649664.1292322)}), refs=frozenset(), last_modified=1758649664.1292322), CachedRevisionInfo(commit_hash='5eeb06e389a648a36087e20eabad1f961e22dc5a', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/5eeb06e389a648a36087e20eabad1f961e22dc5a'), size_on_disk=44894945, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/5eeb06e389a648a36087e20eabad1f961e22dc5a/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/aa43ea37112a84a60d21ce254c1cc1e433def6dc'), size_on_disk=8550, blob_last_accessed=1765406568.606891, blob_last_modified=1765292615.6690626), CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/5eeb06e389a648a36087e20eabad1f961e22dc5a/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b89f865a471fbf3a8054871f8fe79507f6f4be5c2291dcd19030ec8fd4a5325c'), size_on_disk=44886395, blob_last_accessed=1765901285.6763043, blob_last_modified=1765314558.8410568)}), refs=frozenset(), last_modified=1765314558.8410568), CachedRevisionInfo(commit_hash='4e54b7fafd79829bcd179b508efd11a3dc7182cc', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/4e54b7fafd79829bcd179b508efd11a3dc7182cc'), size_on_disk=44900498, files=frozenset({CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/4e54b7fafd79829bcd179b508efd11a3dc7182cc/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b89f865a471fbf3a8054871f8fe79507f6f4be5c2291dcd19030ec8fd4a5325c'), size_on_disk=44886395, blob_last_accessed=1765901285.6763043, blob_last_modified=1765314558.8410568), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/4e54b7fafd79829bcd179b508efd11a3dc7182cc/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/ef196364c79e1a0ec01b3ab6db8dae798f0f8fd5'), size_on_disk=14103, blob_last_accessed=1765911961.5656407, blob_last_modified=1765819563.7734888)}), refs=frozenset(), last_modified=1765819563.7734888), CachedRevisionInfo(commit_hash='95dace55563820030e6bcac2d4b2a9a386a2369b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/95dace55563820030e6bcac2d4b2a9a386a2369b'), size_on_disk=44900425, files=frozenset({CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/95dace55563820030e6bcac2d4b2a9a386a2369b/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b89f865a471fbf3a8054871f8fe79507f6f4be5c2291dcd19030ec8fd4a5325c'), size_on_disk=44886395, blob_last_accessed=1765901285.6763043, blob_last_modified=1765314558.8410568), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/95dace55563820030e6bcac2d4b2a9a386a2369b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/1cfc9a691644fd13ba4e3c4d141caf8bc8dadc92'), size_on_disk=14030, blob_last_accessed=1765809787.9431984, blob_last_modified=1765415607.8811436)}), refs=frozenset(), last_modified=1765415607.8811436), CachedRevisionInfo(commit_hash='a33c34b373e379dfa9bd4922d281790180bb1217', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a33c34b373e379dfa9bd4922d281790180bb1217'), size_on_disk=44899466, files=frozenset({CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a33c34b373e379dfa9bd4922d281790180bb1217/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b89f865a471fbf3a8054871f8fe79507f6f4be5c2291dcd19030ec8fd4a5325c'), size_on_disk=44886395, blob_last_accessed=1765901285.6763043, blob_last_modified=1765314558.8410568), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a33c34b373e379dfa9bd4922d281790180bb1217/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/3cb3997d01aa1c91d0719f954e1cf207976c8a7d'), size_on_disk=13071, blob_last_accessed=1765916907.5030358, blob_last_modified=1765916907.339037)}), refs=frozenset({'main'}), last_modified=1765916907.339037), CachedRevisionInfo(commit_hash='3ff5f32dfeef3c0b2b7d6cc860b259b0ad095829', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/3ff5f32dfeef3c0b2b7d6cc860b259b0ad095829'), size_on_disk=25421759, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/3ff5f32dfeef3c0b2b7d6cc860b259b0ad095829/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/342343ab9f71e7ce0394c540bed737de39f2cf08'), size_on_disk=5893, blob_last_accessed=1764345374.74204, blob_last_modified=1763587990.060371), CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/3ff5f32dfeef3c0b2b7d6cc860b259b0ad095829/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/91904b3e84754bd6011cd4b385bbc99d69a899e5c7562f039a121a744ba95a0a'), size_on_disk=25415866, blob_last_accessed=1763588775.480192, blob_last_modified=1758649664.1292322)}), refs=frozenset(), last_modified=1763587990.060371)}), last_accessed=1765916907.5030358, last_modified=1765916907.339037), CachedRepoInfo(repo_id='BrentLab/yeast_genome_resources', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources'), size_on_disk=114475, nb_files=7, revisions=frozenset({CachedRevisionInfo(commit_hash='6607f7be76546563db0a68a9365c0a858bb5f3a1', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/6607f7be76546563db0a68a9365c0a858bb5f3a1'), size_on_disk=4495, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/6607f7be76546563db0a68a9365c0a858bb5f3a1/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/a31c4647c502767c22b5fe725ec8de58686a02d4'), size_on_disk=4495, blob_last_accessed=1755813454.8008156, blob_last_modified=1755813454.7998157)}), refs=frozenset(), last_modified=1755813454.7998157), CachedRevisionInfo(commit_hash='7441b9a8c858332e730e7ddb9d255460ae698626', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/7441b9a8c858332e730e7ddb9d255460ae698626'), size_on_disk=87714, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/7441b9a8c858332e730e7ddb9d255460ae698626/features/chr=chrII/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/d64638adb2290412a962abffe0fbd0546ff139b29064bbf313b0a768449454a5'), size_on_disk=82276, blob_last_accessed=1755812630.656903, blob_last_modified=1755812631.0999012), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/7441b9a8c858332e730e7ddb9d255460ae698626/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/e63c2087b2e56dc15e8ac6cf5693c2391127b732'), size_on_disk=5438, blob_last_accessed=1755816785.70087, blob_last_modified=1755816785.6988702)}), refs=frozenset(), last_modified=1755816785.6988702), CachedRevisionInfo(commit_hash='25b47a191e40728efc2437d906e84317f922b36b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/25b47a191e40728efc2437d906e84317f922b36b'), size_on_disk=5426, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/25b47a191e40728efc2437d906e84317f922b36b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/9cdf7b342b249f25f2c2cbe46a2e0a26ded4f692'), size_on_disk=5426, blob_last_accessed=1755815405.3308983, blob_last_modified=1755815405.3298984)}), refs=frozenset(), last_modified=1755815405.3298984), CachedRevisionInfo(commit_hash='aae6e5ea8139e23940d11b4d7e77684c78bb7f6b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/aae6e5ea8139e23940d11b4d7e77684c78bb7f6b'), size_on_disk=4585, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/aae6e5ea8139e23940d11b4d7e77684c78bb7f6b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/4320f8f18620d988a5fc32872a671ace406fd0a2'), size_on_disk=4585, blob_last_accessed=1755814342.1923234, blob_last_modified=1755814342.1913235)}), refs=frozenset(), last_modified=1755814342.1913235), CachedRevisionInfo(commit_hash='a5ecb243ba27ca4b4d6ad1b60cc160c033ca2be6', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/a5ecb243ba27ca4b4d6ad1b60cc160c033ca2be6'), size_on_disk=82276, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/a5ecb243ba27ca4b4d6ad1b60cc160c033ca2be6/features/chr=chrII/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/d64638adb2290412a962abffe0fbd0546ff139b29064bbf313b0a768449454a5'), size_on_disk=82276, blob_last_accessed=1755812630.656903, blob_last_modified=1755812631.0999012)}), refs=frozenset(), last_modified=1755812631.0999012), CachedRevisionInfo(commit_hash='42beb28478e27c5d4c5bb2308b12100e6489ef07', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/42beb28478e27c5d4c5bb2308b12100e6489ef07'), size_on_disk=6125, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/42beb28478e27c5d4c5bb2308b12100e6489ef07/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/ab6cc475062a2767a41b2aaa006faf4d9d2d60d6'), size_on_disk=6125, blob_last_accessed=1759787821.8450553, blob_last_modified=1758155946.5549896)}), refs=frozenset({'main'}), last_modified=1758155946.5549896), CachedRevisionInfo(commit_hash='15fdb72f8c31ae58f3e3ce7c90be279383743a2f', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/15fdb72f8c31ae58f3e3ce7c90be279383743a2f'), size_on_disk=88406, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/15fdb72f8c31ae58f3e3ce7c90be279383743a2f/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/645da8028fd6abe86ee305f76eab78cdf29be364'), size_on_disk=6130, blob_last_accessed=1755819093.2316637, blob_last_modified=1755819093.2306638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/15fdb72f8c31ae58f3e3ce7c90be279383743a2f/features/chr=chrII/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/d64638adb2290412a962abffe0fbd0546ff139b29064bbf313b0a768449454a5'), size_on_disk=82276, blob_last_accessed=1755812630.656903, blob_last_modified=1755812631.0999012)}), refs=frozenset(), last_modified=1755819093.2306638)}), last_accessed=1759787821.8450553, last_modified=1758155946.5549896), CachedRepoInfo(repo_id='BrentLab/hackett_2020', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020'), size_on_disk=817026981, nb_files=22, revisions=frozenset({CachedRevisionInfo(commit_hash='4fef197cd055065207d66ea42aec9746b23f38c8', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/4fef197cd055065207d66ea42aec9746b23f38c8'), size_on_disk=9431, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/4fef197cd055065207d66ea42aec9746b23f38c8/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/0a3330d889673abc635b58a78c221ba5b3e36200'), size_on_disk=9431, blob_last_accessed=1765809790.786152, blob_last_modified=1765650535.7166286)}), refs=frozenset({'main'}), last_modified=1765650535.7166286), CachedRevisionInfo(commit_hash='2c196866d4f057cb5cf0adf1fe35f2c712c61025', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025'), size_on_disk=404565376, files=frozenset({CachedFileInfo(file_name='hackett_2020.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/hackett_2020.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4f47951c65c799b5c74ee3782b49c16cbb038d50'), size_on_disk=55, blob_last_accessed=1757105553.3228161, blob_last_modified=1756843395.5719483), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c3e72ccb1b8deba4bbfd18abe6081de7ec3914d9'), size_on_disk=8879, blob_last_accessed=1757105552.290822, blob_last_modified=1757104206.2667632), CachedFileInfo(file_name='parse_mcisaac_data.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/scripts/parse_mcisaac_data.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0'), size_on_disk=4691, blob_last_accessed=1756843395.7579472, blob_last_modified=1756843395.820947), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1760552530.4189773, blob_last_modified=1756843405.225893), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756843395.5059488, blob_last_modified=1756843395.5679483)}), refs=frozenset(), last_modified=1757104206.2667632), CachedRevisionInfo(commit_hash='1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43'), size_on_disk=2678425, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=45/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/db8595cd20dd7440c890c46025cf0643da8ec6be3e15bc18a8759b3fecdc02af'), size_on_disk=349256, blob_last_accessed=1756145253.7795417, blob_last_modified=1756145254.6225288), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=5/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/ed331d9d2b27a0958b12368c7b5d2d3383b111d3d1c39ed0e77b78561dc78b6a'), size_on_disk=348626, blob_last_accessed=1756145253.7845416, blob_last_modified=1756145254.6395288), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=90/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/ebd06af83cc386d9094ce6b8d795b155c76c47c96da1a458743348264f1638ae'), size_on_disk=349917, blob_last_accessed=1756145253.7845416, blob_last_modified=1756145254.7235274), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=30/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/a85bd6b418d9644d9adaa1269c27f97469a4aaee51af63cf1aa041f62cd8ba2c'), size_on_disk=350044, blob_last_accessed=1756145253.7915416, blob_last_modified=1756145254.6385286), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/45cb0687c3ea72e7b32ad17dd799bc38ad02f80b'), size_on_disk=16034, blob_last_accessed=1756138543.6766906, blob_last_modified=1756138543.6746907), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=10/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4a2eb1051cad96c5b39a014f19b092f042b03700cc1b1a40c2de9e7edd73edf9'), size_on_disk=352201, blob_last_accessed=1756145253.7915416, blob_last_modified=1756145254.6375287), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=15/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/51dfb68ed2518f0dd8ab4d4070d4896b6eca912ecb7e4aa773d857e3096020b5'), size_on_disk=351273, blob_last_accessed=1756145217.972108, blob_last_modified=1756142799.7054222), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=20/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/97adc0bb2b436c325555d82bb7f0ffb0e250d73092fcbf03ac0c00fa2b762e51'), size_on_disk=351379, blob_last_accessed=1756145253.7895415, blob_last_modified=1756145254.6265287), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=0/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/409962e1914ac9e9d631e4ae73e04b167822d0915d8d8d4a87703d4c7281ffb9'), size_on_disk=209695, blob_last_accessed=1756145254.7305272, blob_last_modified=1756145254.608529)}), refs=frozenset(), last_modified=1756145254.7235274), CachedRevisionInfo(commit_hash='60b3ecf6a06e709f0397b327ece5c31016303b5c', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/60b3ecf6a06e709f0397b327ece5c31016303b5c'), size_on_disk=404558833, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/60b3ecf6a06e709f0397b327ece5c31016303b5c/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/6f54100c2bb84a6bc059c17e0954dad0ea451666'), size_on_disk=9543, blob_last_accessed=1760552529.9219792, blob_last_modified=1758155372.1328666), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/60b3ecf6a06e709f0397b327ece5c31016303b5c/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1760552530.4189773, blob_last_modified=1756843405.225893)}), refs=frozenset(), last_modified=1758155372.1328666), CachedRevisionInfo(commit_hash='ca2500351e8aa9fc5b996516cd25a60e8298734d', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/ca2500351e8aa9fc5b996516cd25a60e8298734d'), size_on_disk=6294, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/ca2500351e8aa9fc5b996516cd25a60e8298734d/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/ebb31f16ba92f3c0a9eec2aaf4897b3a7662cfad'), size_on_disk=6294, blob_last_accessed=1764169619.6288424, blob_last_modified=1764169578.8420532)}), refs=frozenset(), last_modified=1764169578.8420532), CachedRevisionInfo(commit_hash='6bea8e8497a12fec83927a941ba912a0ec781822', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/6bea8e8497a12fec83927a941ba912a0ec781822'), size_on_disk=8027, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/6bea8e8497a12fec83927a941ba912a0ec781822/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/159b3e518310d0bf928d14a691e41a77d5540aaa'), size_on_disk=8027, blob_last_accessed=1765299157.242701, blob_last_modified=1765299156.9627051)}), refs=frozenset(), last_modified=1765299156.9627051), CachedRevisionInfo(commit_hash='5b09fb31bac250b0bfc099c1bdb32bc2673e754b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/5b09fb31bac250b0bfc099c1bdb32bc2673e754b'), size_on_disk=409731660, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/5b09fb31bac250b0bfc099c1bdb32bc2673e754b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/dacac6a8023596c4c46dce14e80f861d4e4a7401'), size_on_disk=9583, blob_last_accessed=1765309535.4347303, blob_last_modified=1765308942.1208022), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/5b09fb31bac250b0bfc099c1bdb32bc2673e754b/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/10f544e6c4e4da0b411f1084cbe7f9188a842bc6578b699bdf02f3507aff6592'), size_on_disk=409722077, blob_last_accessed=1765311990.5457778, blob_last_modified=1765311990.540778)}), refs=frozenset(), last_modified=1765311990.540778), CachedRevisionInfo(commit_hash='7192b0e2275ba5aa3ffb3752b87f1c3595f2331a', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a'), size_on_disk=404565656, files=frozenset({CachedFileInfo(file_name='parse_mcisaac_data.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/scripts/parse_mcisaac_data.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0'), size_on_disk=4691, blob_last_accessed=1756843395.7579472, blob_last_modified=1756843395.820947), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/f3899cd97da03a3543db10d76c15ee0442bb136e'), size_on_disk=9159, blob_last_accessed=1758145252.5929751, blob_last_modified=1757541416.40237), CachedFileInfo(file_name='hackett_2020.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/hackett_2020.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4f47951c65c799b5c74ee3782b49c16cbb038d50'), size_on_disk=55, blob_last_accessed=1757105553.3228161, blob_last_modified=1756843395.5719483), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756843395.5059488, blob_last_modified=1756843395.5679483), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1760552530.4189773, blob_last_modified=1756843405.225893)}), refs=frozenset(), last_modified=1757541416.40237), CachedRevisionInfo(commit_hash='b9d39535e175295d9cba23489a49fafebbac5ca0', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0'), size_on_disk=404565563, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/03e579e00c632cdb393c0ffafd9d15b069390e5c'), size_on_disk=9066, blob_last_accessed=1757439136.0598524, blob_last_modified=1757105945.6476595), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756843395.5059488, blob_last_modified=1756843395.5679483), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1760552530.4189773, blob_last_modified=1756843405.225893), CachedFileInfo(file_name='hackett_2020.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/hackett_2020.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4f47951c65c799b5c74ee3782b49c16cbb038d50'), size_on_disk=55, blob_last_accessed=1757105553.3228161, blob_last_modified=1756843395.5719483), CachedFileInfo(file_name='parse_mcisaac_data.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/scripts/parse_mcisaac_data.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0'), size_on_disk=4691, blob_last_accessed=1756843395.7579472, blob_last_modified=1756843395.820947)}), refs=frozenset(), last_modified=1757105945.6476595)}), last_accessed=1765809790.786152, last_modified=1765650535.7166286)}), warnings=[])" ] }, - "execution_count": 76, + "execution_count": 2, "metadata": {}, "output_type": "execute_result" } @@ -122,7 +130,7 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -176,7 +184,7 @@ }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -185,16 +193,16 @@ "text": [ "HuggingFace Cache Status (Case 2):\n", "========================================\n", - "✓ Repository BrentLab/hackett_2020 found in cache\n", - " Size: 407.3M\n", - " Revisions: 5\n", - " Files: 17\n", - " Latest revision: 60b3ecf6\n", - " Last accessed: 1758155372.1328666\n", + "✓ Repository BrentLab/mahendrawada_2025 found in cache\n", + " Size: 94.3M\n", + " Revisions: 4\n", + " Files: 8\n", + " Latest revision: af5ac9dc\n", + " Last accessed: 1763578870.280984\n", "\n", " → Case 2 would succeed: Load from local cache\n", "\n", - "Cache efficiency: Using local files avoids re-downloading 407.3M\n" + "Cache efficiency: Using local files avoids re-downloading 94.3M\n" ] } ], @@ -251,7 +259,7 @@ }, { "cell_type": "code", - "execution_count": 79, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -260,20 +268,20 @@ "text": [ "Current HuggingFace Cache Overview:\n", "========================================\n", - "Total cache size: 4.6G\n", - "Number of repositories: 9\n", + "Total cache size: 5.2G\n", + "Number of repositories: 10\n", "\n", "Largest repositories (top 5):\n", " • BrentLab/barkai_compendium: 3.6G (1 revisions)\n", - " • BrentLab/hackett_2020: 407.3M (5 revisions)\n", - " • BrentLab/kemmeren_2014: 319.2M (1 revisions)\n", - " • BrentLab/rossi_2021: 213.7M (7 revisions)\n", - " • BrentLab/hu_2007_reimand_2010: 42.7M (1 revisions)\n", - " ... and 4 more repositories\n", + " • BrentLab/hackett_2020: 817.0M (9 revisions)\n", + " • BrentLab/kemmeren_2014: 319.2M (3 revisions)\n", + " • BrentLab/rossi_2021: 213.7M (9 revisions)\n", + " • BrentLab/mahendrawada_2025: 94.3M (4 revisions)\n", + " ... and 5 more repositories\n", "\n", - "Total revisions across all repos: 29\n", - "Revisions older than 30 days: 0\n", - "Recent revisions (≤30 days): 29\n" + "Total revisions across all repos: 49\n", + "Revisions older than 30 days: 34\n", + "Recent revisions (≤30 days): 15\n" ] } ], @@ -348,7 +356,7 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -546,7 +554,7 @@ }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -555,22 +563,22 @@ "text": [ "Current HuggingFace Cache Status:\n", "===================================\n", - "Total size: 4.6G\n", - "Number of repositories: 9\n", + "Total size: 5.2G\n", + "Number of repositories: 10\n", "\n", "Repository breakdown:\n", - " • BrentLab/mahendrawada_2025: 2.0M (3 revisions)\n", - " • BrentLab/yeast_genome_resources: 114.5K (7 revisions)\n", " • BrentLab/barkai_compendium: 3.6G (1 revisions)\n", - " • BrentLab/rossi_2021: 213.7M (7 revisions)\n", - " • BrentLab/harbison_2004: 12.3K (2 revisions)\n", - " ... and 4 more repositories\n", + " • BrentLab/mahendrawada_2025: 94.3M (4 revisions)\n", + " • BrentLab/hughes_2006: 12.0M (4 revisions)\n", + " • BrentLab/callingcards: 49.6M (3 revisions)\n", + " • BrentLab/kemmeren_2014: 319.2M (3 revisions)\n", + " ... and 5 more repositories\n", "\n", - "Target repository (BrentLab/hackett_2020) cache info:\n", - " Size: 407.3M\n", - " Revisions: 5\n", - " Latest revision: 60b3ecf6\n", - " Last modified: 1758155372.1328666\n" + "Target repository (BrentLab/mahendrawada_2025) cache info:\n", + " Size: 94.3M\n", + " Revisions: 4\n", + " Latest revision: af5ac9dc\n", + " Last modified: 1763578870.280984\n" ] } ], @@ -621,7 +629,7 @@ }, { "cell_type": "code", - "execution_count": 82, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -636,8 +644,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "INFO:__main__:No old revisions found to delete\n", - "INFO:__main__:Found 0 old revisions. Will free 0.0\n", + "INFO:__main__:Found 34 old revisions. Will free 4.6G\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" ] }, @@ -647,9 +654,20 @@ "text": [ "\n", "Cleanup strategy created:\n", - "Expected space freed: 0.0\n", - "Items to delete: 0\n", - "No old files found for cleanup.\n" + "Expected space freed: 4.6G\n", + "Items to delete: 59\n", + "\n", + "Breakdown of items to delete:\n", + " • Blob files: 39\n", + " • Reference files: 0\n", + " • Repository directories: 4\n", + " • Snapshot directories: 16\n", + "\n", + "Sample blob files to delete:\n", + " • /home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0\n", + " • /home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/51dfb68ed2518f0dd8ab4d4070d4896b6eca912ecb7e4aa773d857e3096020b5\n", + " • /home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/f799807a7bf81229b0d063e6c939339c1f5c90f4\n", + " ... and 36 more blob files\n" ] } ], @@ -698,7 +716,7 @@ }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -713,7 +731,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "INFO:__main__:Selected 14 revisions for deletion. Will free 4.0G\n", + "INFO:__main__:Selected 16 revisions for deletion. Will free 3.6G\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" ] }, @@ -723,8 +741,8 @@ "text": [ "\n", "Size-based cleanup strategy:\n", - "Expected space freed: 4.0G\n", - "Items to delete: 30\n", + "Expected space freed: 3.6G\n", + "Items to delete: 43\n", "\n", "Comparing cleanup strategies for 1GB:\n" ] @@ -733,22 +751,9 @@ "name": "stderr", "output_type": "stream", "text": [ - "INFO:__main__:Selected 14 revisions for deletion. Will free 4.0G\n", - "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " • oldest_first : 4.0G (30 items)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:__main__:Selected 1 revisions for deletion. Will free 3.6G\n", + "INFO:__main__:Selected 16 revisions for deletion. Will free 3.6G\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n", + "INFO:__main__:Selected 3 revisions for deletion. Will free 4.0G\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" ] }, @@ -756,14 +761,15 @@ "name": "stdout", "output_type": "stream", "text": [ - " • largest_first : 3.6G (1 items)\n" + " • oldest_first : 3.6G (43 items)\n", + " • largest_first : 4.0G (6 items)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "INFO:__main__:Selected 14 revisions for deletion. Will free 4.0G\n", + "INFO:__main__:Selected 16 revisions for deletion. Will free 3.6G\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" ] }, @@ -771,7 +777,7 @@ "name": "stdout", "output_type": "stream", "text": [ - " • least_used : 4.0G (30 items)\n" + " • least_used : 3.6G (43 items)\n" ] } ], @@ -822,7 +828,7 @@ }, { "cell_type": "code", - "execution_count": 84, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -837,7 +843,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "INFO:__main__:Found 14 unused revisions. Will free 216.4M\n", + "INFO:__main__:Found 31 unused revisions. Will free 642.9M\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" ] }, @@ -847,38 +853,38 @@ "text": [ "\n", "Revision cleanup strategy:\n", - "Expected space freed: 216.4M\n", - "Items to delete: 75\n", + "Expected space freed: 642.9M\n", + "Items to delete: 118\n", "\n", "Breakdown of cleanup:\n", - " • Blob files: 61\n", + " • Blob files: 87\n", " • Reference files: 0\n", " • Repository directories: 0\n", - " • Snapshot directories: 14\n", + " • Snapshot directories: 31\n", "\n", "Per-repository revision analysis:\n", "\n", - " • BrentLab/mahendrawada_2025:\n", - " Total revisions: 3\n", - " Would keep: 2\n", - " Would delete: 1\n", - " Keep: 8bea431b (modified: 1758155849.9076133)\n", - " Keep: d602c165 (modified: 1757439229.7949307)\n", - " Delete: 66e0cf1f (modified: 1756858618.1383443)\n", - "\n", - " • BrentLab/yeast_genome_resources:\n", - " Total revisions: 7\n", - " Would keep: 2\n", - " Would delete: 5\n", - " Keep: 42beb284 (modified: 1758155946.5549896)\n", - " Keep: 15fdb72f (modified: 1755819093.2306638)\n", - " Delete: 7441b9a8 (modified: 1755816785.6988702)\n", - "\n", " • BrentLab/barkai_compendium:\n", " Total revisions: 1\n", " Would keep: 1\n", " Would delete: 0\n", - " Keep: a987ef37 (modified: 1756926783.3167186)\n" + " Keep: a987ef37 (modified: 1756926783.3167186)\n", + "\n", + " • BrentLab/mahendrawada_2025:\n", + " Total revisions: 4\n", + " Would keep: 2\n", + " Would delete: 2\n", + " Keep: af5ac9dc (modified: 1763578870.280984)\n", + " Keep: 3b912489 (modified: 1758652945.6395862)\n", + " Delete: 874a5dfe (modified: 1758569041.2879846)\n", + "\n", + " • BrentLab/hughes_2006:\n", + " Total revisions: 4\n", + " Would keep: 2\n", + " Would delete: 2\n", + " Keep: 10095469 (modified: 1764713263.741364)\n", + " Keep: 0de73d09 (modified: 1764712744.7960804)\n", + " Delete: cc20720c (modified: 1756856029.230347)\n" ] } ], @@ -935,7 +941,7 @@ }, { "cell_type": "code", - "execution_count": 85, + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -957,12 +963,11 @@ "name": "stderr", "output_type": "stream", "text": [ - "INFO:__main__:No old revisions found to delete\n", - "INFO:__main__:Found 0 old revisions. Will free 0.0\n", + "INFO:__main__:Found 34 old revisions. Will free 4.6G\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n", - "INFO:__main__:Found 14 unused revisions. Will free 216.4M\n", + "INFO:__main__:Found 31 unused revisions. Will free 642.9M\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n", - "INFO:__main__:Automated cleanup complete. Total freed: 206.4MB\n" + "INFO:__main__:Automated cleanup complete. Total freed: 4.9GB\n" ] }, { @@ -971,11 +976,11 @@ "text": [ "\n", "Automated cleanup executed 2 strategies:\n", - " 1. Strategy freed: 0.0\n", - " 2. Strategy freed: 216.4M\n", + " 1. Strategy freed: 4.6G\n", + " 2. Strategy freed: 642.9M\n", "\n", - "Total space that would be freed: 206.4MB\n", - "Cache size after cleanup: 4.1GB\n" + "Total space that would be freed: 4.9GB\n", + "Cache size after cleanup: 0B\n" ] } ], @@ -1024,7 +1029,7 @@ }, { "cell_type": "code", - "execution_count": 86, + "execution_count": 12, "metadata": {}, "outputs": [ { @@ -1037,9 +1042,9 @@ "Demonstrating cache cleanup performance...\n", "\n", "1. Cache scanning performance:\n", - " Time to scan cache: 0.111 seconds\n", - " Repositories found: 9\n", - " Total cache size: 4.6G\n", + " Time to scan cache: 0.095 seconds\n", + " Repositories found: 10\n", + " Total cache size: 5.2G\n", "\n", "2. Cleanup strategy creation performance:\n" ] @@ -1048,8 +1053,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "INFO:__main__:No old revisions found to delete\n", - "INFO:__main__:Found 0 old revisions. Will free 0.0\n", + "INFO:__main__:Found 34 old revisions. Will free 4.6G\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" ] }, @@ -1057,29 +1061,16 @@ "name": "stdout", "output_type": "stream", "text": [ - " Age cleanup strategy: 0.109 seconds\n" + " Age cleanup strategy: 0.099 seconds\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "INFO:__main__:Selected 14 revisions for deletion. Will free 4.0G\n", - "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " Size cleanup strategy: 0.105 seconds\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:__main__:Found 14 unused revisions. Will free 216.4M\n", + "INFO:__main__:Selected 16 revisions for deletion. Will free 3.6G\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n", + "INFO:__main__:Found 31 unused revisions. Will free 642.9M\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" ] }, @@ -1087,10 +1078,11 @@ "name": "stdout", "output_type": "stream", "text": [ - " Revision cleanup strategy: 0.111 seconds\n", + " Size cleanup strategy: 0.097 seconds\n", + " Revision cleanup strategy: 0.096 seconds\n", "\n", "Performance insights:\n", - "• Cache scanning is fast: 0.111s for 9 repos\n", + "• Cache scanning is fast: 0.095s for 10 repos\n", "• Cleanup strategy creation is efficient\n", "• Dry runs allow safe preview of cleanup operations\n", "• Multiple strategies can be compared quickly\n" @@ -1148,7 +1140,7 @@ }, { "cell_type": "code", - "execution_count": 87, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -1219,7 +1211,7 @@ }, { "cell_type": "code", - "execution_count": 88, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -1258,10 +1250,10 @@ " cache_mgr.clean_cache_by_size('10GB', strategy='oldest_first')\n", "\n", "Current Session State:\n", - "Repository: BrentLab/hackett_2020\n", + "Repository: BrentLab/mahendrawada_2025\n", "DuckDB tables: 0\n", - "HF cache size: 4.6G\n", - "Cache repositories: 9\n" + "HF cache size: 5.2G\n", + "Cache repositories: 10\n" ] } ], @@ -1318,7 +1310,7 @@ }, { "cell_type": "code", - "execution_count": 89, + "execution_count": 15, "metadata": {}, "outputs": [ { @@ -1334,7 +1326,7 @@ " • Check repository access permissions\n", "\n", "2. Cache Space and Performance Issues:\n", - " Current cache size: 4.6G\n", + " Current cache size: 5.2G\n", " • Use auto_clean_cache() for automated management\n", " • Monitor cache growth with scan_cache_dir()\n", " • Set appropriate size limits for your system\n", @@ -1351,7 +1343,7 @@ "\n", "Cache Health Check:\n", "✓ DuckDB connection: DuckDB OK\n", - "✓ Cache access: 9 repositories found\n" + "✓ Cache access: 10 repositories found\n" ] }, { @@ -1370,7 +1362,7 @@ "✓ Cache cleanup methods: Working\n", "\n", "Current Status:\n", - "Repository: BrentLab/hackett_2020\n", + "Repository: BrentLab/mahendrawada_2025\n", "Logger configured: True\n", "Cache management ready: ✓\n" ] diff --git a/docs/tutorials/correlation_tutorial.ipynb b/docs/tutorials/correlation_tutorial.ipynb deleted file mode 100644 index 5c3d5d4..0000000 --- a/docs/tutorials/correlation_tutorial.ipynb +++ /dev/null @@ -1,3420 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "fcdcb13a", - "metadata": {}, - "source": [ - "# Hackett 2020 vs Kemmeren Correlation Analysis Tutorial\n", - "\n", - "This tutorial demonstrates how to conduct comprehensive correlation analysis between Hackett 2020 (mcisaac) and Kemmeren datasets using tfbpapi, focusing on regulator-target relationships and temporal dynamics.\n", - "\n", - "## Overview\n", - "\n", - "Correlation analysis evalute how well the effects of the same regulators correlate between these two experimental approaches, and whether timing affects the correlation strength.\n", - "\n", - "### Datasets Used\n", - "- **Perturbation data**: McIsaac (ZEV system), Kemmeren (overexpression)\n", - "\n", - "### Analysis Strategy\n", - "1. Load and filter Hackett 2020 data (time=15, mechanism=\"zev\", restriction=\"p\") and Kemmeren 2014 data\n", - "2. Identify shared regulators between Hackett and Kemmeren\n", - "3. For each shared regulator, calculate Spearman correlations:\n", - " - Hackett effect vs Kemmeren effect\n", - " - Hackett effect vs Kemmeren p-value\n", - "4. Generate scatter plots for specific individual regulator\n", - "5. Create distribution plots (boxplots) across all shared regulators\n", - "6. Analyze temporal dynamics by comparing different timepoints in Hackett data" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "30abaca0", - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chase/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", - " from .autonotebook import tqdm as notebook_tqdm\n" - ] - } - ], - "source": [ - "import pandas as pd\n", - "import numpy as np\n", - "from tfbpapi.HfQueryAPI import HfQueryAPI\n", - "from tfbpapi.datainfo.datacard import DataCard\n", - "from tfbpapi.errors import DataCardError, HfDataFetchError\n", - "from scipy.stats import spearmanr\n", - "import matplotlib.pyplot as plt\n", - "import seaborn as sns\n", - "\n", - "# Configure plotting\n", - "plt.style.use('default')\n", - "sns.set_palette(\"husl\")" - ] - }, - { - "cell_type": "markdown", - "id": "12941f4d", - "metadata": {}, - "source": [ - "## Dataset Exploration with DataCard\n", - "\n", - "Before loading data, let's explore dataset structure and metadata using the DataCard interface." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "c9b9afdf", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Exploring dataset structures...\n", - "\n", - "==================================================\n", - "Exploring BrentLab/hackett_2020\n", - "Dataset types in configs: {'annotated_features'}\n", - "Configurations: 1\n", - "\n", - "Config: hackett_2020\n", - " Type: annotated_features\n", - " Features: 17\n", - " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", - " Target fields: ['target_locus_tag', 'target_symbol']\n", - " Metadata fields: ['regulator_locus_tag', 'regulator_symbol', 'time', 'mechanism', 'restriction', 'date', 'strain']\n", - "\n", - "==================================================\n", - "Exploring BrentLab/kemmeren_2014\n", - "Dataset types in configs: {'annotated_features'}\n", - "Configurations: 1\n", - "\n", - "Config: kemmeren_2014\n", - " Type: annotated_features\n", - " Features: 11\n", - " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", - " Target fields: ['target_locus_tag', 'target_symbol']\n", - " Metadata fields: ['regulator_locus_tag', 'regulator_symbol']\n", - "\n", - "==================================================\n", - "Successfully explored 2 datasets\n" - ] - } - ], - "source": [ - "# Explore dataset structure using DataCard\n", - "print(\"Exploring dataset structures...\")\n", - "\n", - "datasets_to_explore = [\n", - " \"BrentLab/hackett_2020\",\n", - " \"BrentLab/kemmeren_2014\"\n", - "]\n", - "\n", - "dataset_info = {}\n", - "\n", - "for repo_id in datasets_to_explore:\n", - " try:\n", - " print(f\"\\n{'='*50}\")\n", - " print(f\"Exploring {repo_id}\")\n", - "\n", - " # Create DataCard instance\n", - " datacard = DataCard(repo_id)\n", - " card = datacard.dataset_card\n", - "\n", - " # DatasetCard doesn't have dataset_type directly - it's on individual configs\n", - " dataset_types = [config.dataset_type.value for config in card.configs]\n", - " print(f\"Dataset types in configs: {set(dataset_types)}\")\n", - " print(f\"Configurations: {len(card.configs)}\")\n", - "\n", - " # Store dataset info for later use\n", - " dataset_info[repo_id] = {\n", - " 'datacard': datacard,\n", - " 'card': card,\n", - " 'configs': {config.config_name: config for config in card.configs}\n", - " }\n", - "\n", - " # Display configuration details\n", - " for config in card.configs:\n", - " print(f\"\\nConfig: {config.config_name}\")\n", - " print(f\" Type: {config.dataset_type.value}\")\n", - " print(f\" Features: {len(config.dataset_info.features) if config.dataset_info.features else 0}\")\n", - "\n", - " if config.dataset_info.features:\n", - " # Show regulator and target fields if available\n", - " feature_names = [f.name for f in config.dataset_info.features]\n", - " regulator_fields = [f for f in feature_names if 'regulator' in f.lower()]\n", - " target_fields = [f for f in feature_names if 'target' in f.lower()]\n", - "\n", - " if regulator_fields:\n", - " print(f\" Regulator fields: {regulator_fields}\")\n", - " if target_fields:\n", - " print(f\" Target fields: {target_fields}\")\n", - "\n", - " if config.metadata_fields:\n", - " print(f\" Metadata fields: {config.metadata_fields}\")\n", - "\n", - " except (DataCardError, HfDataFetchError) as e:\n", - " print(f\"Error exploring {repo_id}: {e}\")\n", - " continue\n", - " except Exception as e:\n", - " print(f\"Unexpected error exploring {repo_id}: {e}\")\n", - " continue\n", - "\n", - "print(f\"\\n{'='*50}\")\n", - "print(f\"Successfully explored {len(dataset_info)} datasets\")" - ] - }, - { - "cell_type": "markdown", - "id": "cf0265f0", - "metadata": {}, - "source": [ - "## Initialize dataset connections and load data" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "a6b0424f", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Initializing HfQueryAPI connections...\n", - "All API connections initialized\n" - ] - } - ], - "source": [ - "# Initialize dataset connections and load data\n", - "print(\"Initializing HfQueryAPI connections...\")\n", - "\n", - "hackett_2020 = HfQueryAPI(repo_id=\"BrentLab/hackett_2020\")\n", - "kemmeren_2014 = HfQueryAPI(repo_id=\"BrentLab/kemmeren_2014\")\n", - "\n", - "print(\"All API connections initialized\")" - ] - }, - { - "cell_type": "markdown", - "id": "6e1b7269", - "metadata": {}, - "source": [ - "## Get metadata from each dataset to find common regulators" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "baafb468", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Getting metadata from datasets...\n", - "All metadata loaded\n", - "Found 185 common regulators across all datasets\n" - ] - } - ], - "source": [ - "# Get metadata from each dataset to find common regulators\n", - "print(\"Getting metadata from datasets...\")\n", - "\n", - "kemmeren_meta = kemmeren_2014.get_metadata(\"kemmeren_2014\") \n", - "hackett_2020_meta = hackett_2020.get_metadata(\"hackett_2020\")\n", - "\n", - "print(\"All metadata loaded\")\n", - "\n", - "# Get the intersection of common regulators\n", - "common_regulators = (set(hackett_2020_meta.regulator_symbol.unique())\n", - " & set(kemmeren_meta.regulator_symbol.unique()))\n", - "\n", - "print(f\"Found {len(common_regulators)} common regulators across all datasets\")\n", - "\n", - "# Create proper SQL IN clause\n", - "if common_regulators:\n", - " regulator_clause = \"(\" + \", \".join(f\"'{reg}'\" for reg in common_regulators) + \")\"" - ] - }, - { - "cell_type": "markdown", - "id": "2d89d745", - "metadata": {}, - "source": [ - "## Filter the data" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "c888c477", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Applying dataset-specific filters...\n", - "All filters applied\n" - ] - } - ], - "source": [ - "# Apply dataset-specific filters\n", - "print(\"Applying dataset-specific filters...\")\n", - "\n", - "hackett_2020.set_sql_filter(\n", - " \"hackett_2020\",\n", - " f\"\"\"\n", - " time = 15 \n", - " AND mechanism = 'ZEV' \n", - " AND restriction = 'P' \n", - " AND regulator_symbol IN {regulator_clause}\n", - " \"\"\")\n", - "\n", - "print(\"All filters applied\")" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "050e3f4c", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Loading both perturbation datasets...\n", - "Both datasets loaded\n", - "hackett: 145 regulators\n", - "kemmeren: 1487 regulators\n", - "Final intersection: 145 regulators\n", - "Common regulators: ['ACA1', 'ADA2', 'AFT2', 'ARO80', 'ARR1', 'ASH1', 'AZF1', 'BDF2', 'BMH1', 'CAD1']...\n" - ] - } - ], - "source": [ - "# Load both perturbation datasets to find common regulators\n", - "print(\"Loading both perturbation datasets...\")\n", - "\n", - "# Load both datasets\n", - "hackett_data = hackett_2020.query(\"SELECT * FROM hackett_2020\", \"hackett_2020\")\n", - "kemmeren_data = kemmeren_2014.query(\"SELECT * FROM kemmeren_2014\", \"kemmeren_2014\")\n", - "\n", - "print(\"Both datasets loaded\")\n", - "\n", - "# Check what regulators we actually got from each dataset\n", - "actual_regulators = {\n", - " 'hackett': set(hackett_data.regulator_symbol.unique()),\n", - " 'kemmeren': set(kemmeren_data.regulator_symbol.unique()),\n", - "}\n", - "\n", - "for name, regulators in actual_regulators.items():\n", - " print(f\"{name}: {len(regulators)} regulators\")\n", - "\n", - "# Find the intersection - regulators present in BOTH datasets\n", - "final_common = set.intersection(*actual_regulators.values())\n", - "print(f\"Final intersection: {len(final_common)} regulators\")\n", - "\n", - "if final_common:\n", - " final_common_clause = \"(\" + \", \".join(f\"'{reg}'\" for reg in final_common) + \")\"\n", - " print(f\"Common regulators: {sorted(list(final_common))[:10]}...\") # Show first 10\n", - "else:\n", - " print(\"WARNING: No common regulators found!\")\n", - " final_common_clause = \"()\"\n" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "06293141", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Finding common targets...\n", - "Found 6063 common targets\n", - "Loading final datasets with complete filtering...\n", - "Final datasets loaded with complete filtering\n", - "McIsaac: 891,408 rows, 145 regulators\n", - "Kemmeren: 880,730 rows, 145 regulators\n" - ] - } - ], - "source": [ - "# Find common targets across all datasets\n", - "print(\"Finding common targets...\")\n", - "\n", - "common_targets = (set(hackett_data.target_symbol.unique())\n", - " & set(kemmeren_data.target_symbol.unique()))\n", - "\n", - "print(f\"Found {len(common_targets)} common targets\")\n", - "\n", - "if common_targets:\n", - " target_clause = \"(\" + \", \".join(f\"'{tgt}'\" for tgt in common_targets) + \")\"\n", - "\n", - "# Load final datasets with both regulator and target filtering \n", - "print(\"Loading final datasets with complete filtering...\")\n", - "\n", - "\n", - "# Load final datasets with both regulator and target filtering \n", - "hackett_final = hackett_2020.query(f\"\"\"\n", - " SELECT * FROM hackett_2020\n", - " WHERE regulator_symbol IN {final_common_clause} \n", - " AND target_symbol IN {target_clause}\n", - "\"\"\", \"hackett_2020\")\n", - "\n", - "kemmeren_final = kemmeren_2014.query(f\"\"\"\n", - " SELECT * FROM kemmeren_2014\n", - " WHERE regulator_symbol IN {final_common_clause} \n", - " AND target_symbol IN {target_clause}\n", - "\"\"\", \"kemmeren_2014\")\n", - "\n", - "print(\"Final datasets loaded with complete filtering\")\n", - "\n", - "# Print final dataset sizes\n", - "datasets_info = [\n", - " ('McIsaac', hackett_final), ('Kemmeren', kemmeren_final)\n", - "]\n", - "\n", - "for name, data in datasets_info:\n", - " if len(data) > 0:\n", - " regulators = data['regulator_symbol'].nunique() if 'regulator_symbol' in data.columns else 0\n", - " print(f\"{name}: {len(data):,} rows, {regulators} regulators\")\n", - " else:\n", - " print(f\"{name}: No data loaded\")" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "549e73ae", - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "regulator_locus_tag", - "rawType": "object", - "type": "string" - }, - { - "name": "regulator_symbol", - "rawType": "object", - "type": "string" - }, - { - "name": "reporterId", - "rawType": "object", - "type": "string" - }, - { - "name": "target_locus_tag", - "rawType": "object", - "type": "string" - }, - { - "name": "target_symbol", - "rawType": "object", - "type": "string" - }, - { - "name": "M", - "rawType": "float64", - "type": "float" - }, - { - "name": "Madj", - "rawType": "float64", - "type": "float" - }, - { - "name": "A", - "rawType": "float64", - "type": "float" - }, - { - "name": "pval", - "rawType": "float64", - "type": "float" - }, - { - "name": "variable_in_wt", - "rawType": "bool", - "type": "boolean" - }, - { - "name": "multiple_probes", - "rawType": "bool", - "type": "boolean" - } - ], - "ref": "31ae3af5-8283-4e49-9dc6-2dbd59140d0f", - "rows": [ - [ - "0", - "YBL008W", - "HIR1", - "SCAB000002", - "Q0017", - "Q0017", - "-0.039025753", - "-0.0397208098656576", - "5.666642", - "0.591335", - "False", - "False" - ], - [ - "1", - "YBL008W", - "HIR1", - "SCAB000004", - "Q0045", - "COX1", - "-0.10270845", - "-0.104404483712687", - "7.1872175", - "0.278744", - "False", - "False" - ], - [ - "2", - "YBL008W", - "HIR1", - "SCAB000005", - "Q0050", - "AI1", - "-0.09215408", - "-0.101312394230742", - "7.9818127", - "0.789989", - "True", - "False" - ], - [ - "3", - "YBL008W", - "HIR1", - "SCAB000006", - "Q0055", - "AI2", - "-0.11363403", - "-0.123208021645201", - "9.3025734", - "0.695235", - "True", - "False" - ], - [ - "4", - "YBL008W", - "HIR1", - "SCAB000007", - "Q0060", - "AI3", - "-0.0099832871", - "-0.0186756321118991", - "8.3151407", - "0.975177", - "False", - "False" - ], - [ - "5", - "YBL008W", - "HIR1", - "SCAB000008", - "Q0065", - "AI4", - "-0.16157419", - "-0.173001391865694", - "11.104408", - "0.508279", - "True", - "False" - ], - [ - "6", - "YBL008W", - "HIR1", - "SCAB000009", - "Q0070", - "AI5_ALPHA", - "-0.29374796", - "-0.306969229210268", - "10.721268", - "0.127481", - "True", - "False" - ], - [ - "7", - "YBL008W", - "HIR1", - "SCAB000010", - "Q0075", - "AI5_BETA", - "-0.048776282", - "-0.0512431882361536", - "7.3602157", - "0.788852", - "True", - "False" - ], - [ - "8", - "YBL008W", - "HIR1", - "SCAB000011", - "Q0080", - "ATP8", - "-0.18210337", - "-0.187242319447809", - "6.7582501", - "0.227365", - "True", - "False" - ], - [ - "9", - "YBL008W", - "HIR1", - "SCAB000012", - "Q0085", - "ATP6", - "-0.0065067789", - "-0.00701312899803988", - "6.4296849", - "0.9677", - "False", - "False" - ], - [ - "10", - "YBL008W", - "HIR1", - "SCAB000014", - "Q0105", - "COB", - "-0.023712626", - "-0.0241945156649505", - "5.6905146", - "0.819055", - "False", - "False" - ], - [ - "11", - "YBL008W", - "HIR1", - "SCAB000015", - "Q0110", - "BI2", - "-0.10026133", - "-0.102682551857025", - "6.3605368", - "0.538407", - "False", - "False" - ], - [ - "12", - "YBL008W", - "HIR1", - "SCAB000016", - "Q0115", - "BI3", - "-0.053808146", - "-0.0569359410492525", - "6.6718358", - "0.725973", - "False", - "False" - ], - [ - "13", - "YBL008W", - "HIR1", - "SCAB000017", - "Q0120", - "BI4", - "-0.020458971", - "-0.0236853183042711", - "6.9095507", - "0.927896", - "False", - "False" - ], - [ - "14", - "YBL008W", - "HIR1", - "SCAB000018", - "Q0130", - "OLI1", - "-0.043537368", - "-0.0566553964261391", - "12.556363", - "0.890456", - "True", - "False" - ], - [ - "15", - "YBL008W", - "HIR1", - "SCAB000019", - "Q0140", - "VAR1", - "-0.16343272", - "-0.163938440697184", - "6.1672218", - "0.188679", - "True", - "False" - ], - [ - "16", - "YBL008W", - "HIR1", - "SCAB000023", - "Q0160", - "SCEI", - "-0.11115273", - "-0.112545724325415", - "6.40361", - "0.233242", - "False", - "False" - ], - [ - "17", - "YBL008W", - "HIR1", - "SCAB000024", - "Q0182", - "Q0182", - "-0.074097777", - "-0.07502283029592", - "5.8703247", - "0.373117", - "False", - "False" - ], - [ - "18", - "YBL008W", - "HIR1", - "SCAB000025", - "Q0250", - "COX2", - "-0.093127871", - "-0.0967308066694721", - "7.4043715", - "0.275569", - "False", - "False" - ], - [ - "19", - "YBL008W", - "HIR1", - "SCAB000026", - "Q0255", - "Q0255", - "-0.031337077", - "-0.0317708668814822", - "5.6274759", - "0.677195", - "False", - "False" - ], - [ - "20", - "YBL008W", - "HIR1", - "SCAB000027", - "Q0275", - "COX3", - "-0.018126893", - "-0.0174743777233046", - "6.0614997", - "0.861477", - "False", - "False" - ], - [ - "21", - "YBL008W", - "HIR1", - "SCAB000028", - "Q0297", - "Q0297", - "-0.0015863937", - "-0.00165756742970601", - "6.2053847", - "0.99118", - "False", - "False" - ], - [ - "22", - "YBL008W", - "HIR1", - "SCAB000029", - "YAL001C", - "TFC3", - "-0.060650037", - "-0.0567581406799775", - "9.7950907", - "0.415566", - "False", - "False" - ], - [ - "23", - "YBL008W", - "HIR1", - "SCAB000030", - "YAL002W", - "VPS8", - "-0.0074215245", - "-0.00566180001033513", - "8.3969858", - "0.930872", - "False", - "False" - ], - [ - "24", - "YBL008W", - "HIR1", - "SCAB000031", - "YAL003W", - "EFB1", - "-0.029623397", - "-0.03985574724458", - "14.921067", - "0.84173", - "False", - "False" - ], - [ - "25", - "YBL008W", - "HIR1", - "SCAB000032", - "YAL004W", - "YAL004W", - "0.059369123", - "0.0561266464793721", - "6.6928234", - "0.54419", - "False", - "False" - ], - [ - "26", - "YBL008W", - "HIR1", - "SCAB000033", - "YAL005C", - "SSA1", - "0.042630331", - "0.0487909166720867", - "15.675554", - "0.862133", - "False", - "False" - ], - [ - "27", - "YBL008W", - "HIR1", - "SCAB000034", - "YAL007C", - "ERP2", - "-0.060302507", - "-0.0583166518417961", - "11.623729", - "0.346134", - "False", - "False" - ], - [ - "28", - "YBL008W", - "HIR1", - "SCAB000035", - "YAL008W", - "FUN14", - "-0.089632662", - "-0.0758621939217628", - "9.7471534", - "0.251776", - "False", - "False" - ], - [ - "29", - "YBL008W", - "HIR1", - "SCAB000036", - "YAL009W", - "SPO7", - "0.061596497", - "0.059807970651245", - "9.5949268", - "0.337943", - "False", - "False" - ], - [ - "30", - "YBL008W", - "HIR1", - "SCAB000037", - "YAL010C", - "MDM10", - "0.033553389", - "0.0347720388006548", - "7.8775572", - "0.707184", - "False", - "False" - ], - [ - "31", - "YBL008W", - "HIR1", - "SCAB000038", - "YAL011W", - "SWC3", - "-0.053778196", - "-0.0547258310698601", - "6.1043641", - "0.478414", - "False", - "False" - ], - [ - "32", - "YBL008W", - "HIR1", - "SCAB000039", - "YAL012W", - "CYS3", - "-0.067001533", - "-0.0704603092262692", - "12.296266", - "0.589873", - "False", - "False" - ], - [ - "33", - "YBL008W", - "HIR1", - "SCAB000040", - "YAL013W", - "DEP1", - "0.045037505", - "0.0422594728589327", - "8.8906158", - "0.705922", - "False", - "False" - ], - [ - "34", - "YBL008W", - "HIR1", - "SCAB000041", - "YAL014C", - "SYN8", - "-0.0028853882", - "-0.003421632139455", - "10.140393", - "0.984162", - "False", - "False" - ], - [ - "35", - "YBL008W", - "HIR1", - "SCAB000042", - "YAL015C", - "NTG1", - "0.17305066", - "0.178185020644813", - "9.5554939", - "0.010956", - "False", - "False" - ], - [ - "36", - "YBL008W", - "HIR1", - "SCAB000043", - "YAL016W", - "TPD3", - "-0.084858514", - "-0.0803126247904039", - "11.337088", - "0.270546", - "False", - "False" - ], - [ - "37", - "YBL008W", - "HIR1", - "SCAB000044", - "YAL017W", - "PSK1", - "-0.081218414", - "-0.0672602695566325", - "9.9839762", - "0.450345", - "False", - "False" - ], - [ - "38", - "YBL008W", - "HIR1", - "SCAB000045", - "YAL018C", - "LDS1", - "-0.0015791723", - "-0.00059224976381921", - "5.9934205", - "0.990055", - "False", - "False" - ], - [ - "39", - "YBL008W", - "HIR1", - "SCAB000046", - "YAL019W", - "FUN30", - "0.1520406", - "0.148431864200967", - "10.781851", - "0.0493148", - "False", - "False" - ], - [ - "40", - "YBL008W", - "HIR1", - "SCAB000047", - "YAL020C", - "ATS1", - "-0.020495554", - "-0.0238614082255441", - "9.3828017", - "0.836546", - "False", - "False" - ], - [ - "41", - "YBL008W", - "HIR1", - "SCAB000048", - "YAL021C", - "CCR4", - "-0.13337841", - "-0.13693929825126", - "9.4790413", - "0.171857", - "False", - "False" - ], - [ - "42", - "YBL008W", - "HIR1", - "SCAB000049", - "YAL022C", - "FUN26", - "-0.097163876", - "-0.0973296431760643", - "9.893272", - "0.226213", - "False", - "False" - ], - [ - "43", - "YBL008W", - "HIR1", - "SCAB000050", - "YAL023C", - "PMT2", - "-0.027986767", - "-0.0333328913664413", - "9.0967819", - "0.821875", - "False", - "False" - ], - [ - "44", - "YBL008W", - "HIR1", - "SCAB000051", - "YAL024C", - "LTE1", - "-0.13226007", - "-0.135436445273697", - "8.4398933", - "0.045112", - "False", - "False" - ], - [ - "45", - "YBL008W", - "HIR1", - "SCAB000052", - "YAL025C", - "MAK16", - "0.17071987", - "0.159309256880021", - "11.301871", - "0.0211462", - "False", - "False" - ], - [ - "46", - "YBL008W", - "HIR1", - "SCAB000053", - "YAL026C", - "DRS2", - "-0.1904308", - "-0.190251792611949", - "10.91405", - "0.00111783", - "False", - "False" - ], - [ - "47", - "YBL008W", - "HIR1", - "SCAB000054", - "YAL027W", - "SAW1", - "-0.18881836", - "-0.192964020762811", - "8.2938543", - "7.6856e-06", - "False", - "False" - ], - [ - "48", - "YBL008W", - "HIR1", - "SCAB000055", - "YAL028W", - "FRT2", - "-0.053743754", - "-0.0418562759081939", - "7.9800474", - "0.599747", - "False", - "False" - ], - [ - "49", - "YBL008W", - "HIR1", - "SCAB000056", - "YAL029C", - "MYO4", - "-0.025923023", - "-0.0246822755213326", - "10.036795", - "0.842811", - "False", - "False" - ] - ], - "shape": { - "columns": 11, - "rows": 880730 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
regulator_locus_tagregulator_symbolreporterIdtarget_locus_tagtarget_symbolMMadjApvalvariable_in_wtmultiple_probes
0YBL008WHIR1SCAB000002Q0017Q0017-0.039026-0.0397215.6666420.591335FalseFalse
1YBL008WHIR1SCAB000004Q0045COX1-0.102708-0.1044047.1872180.278744FalseFalse
2YBL008WHIR1SCAB000005Q0050AI1-0.092154-0.1013127.9818130.789989TrueFalse
3YBL008WHIR1SCAB000006Q0055AI2-0.113634-0.1232089.3025730.695235TrueFalse
4YBL008WHIR1SCAB000007Q0060AI3-0.009983-0.0186768.3151410.975177FalseFalse
....................................
880725YPL202CAFT2SCAB007301YML099W-AYML099W-A-0.141110-0.1164658.0925130.432310FalseFalse
880726YPL202CAFT2SCAB007304YNL067W-AYNL067W-A0.015283-0.0008496.4725350.969828FalseFalse
880727YPL202CAFT2SCAB007305YNL162W-AYNL162W-A0.1181870.0618369.3640320.696226FalseFalse
880728YPL202CAFT2SCAB007306YNR001W-AYNR001W-A0.0279180.0204606.3517640.944834FalseFalse
880729YPL202CAFT2SCAB007307YNR034W-AEGO4-0.1281200.1119417.6171170.846665FalseFalse
\n", - "

880730 rows × 11 columns

\n", - "
" - ], - "text/plain": [ - " regulator_locus_tag regulator_symbol reporterId target_locus_tag \\\n", - "0 YBL008W HIR1 SCAB000002 Q0017 \n", - "1 YBL008W HIR1 SCAB000004 Q0045 \n", - "2 YBL008W HIR1 SCAB000005 Q0050 \n", - "3 YBL008W HIR1 SCAB000006 Q0055 \n", - "4 YBL008W HIR1 SCAB000007 Q0060 \n", - "... ... ... ... ... \n", - "880725 YPL202C AFT2 SCAB007301 YML099W-A \n", - "880726 YPL202C AFT2 SCAB007304 YNL067W-A \n", - "880727 YPL202C AFT2 SCAB007305 YNL162W-A \n", - "880728 YPL202C AFT2 SCAB007306 YNR001W-A \n", - "880729 YPL202C AFT2 SCAB007307 YNR034W-A \n", - "\n", - " target_symbol M Madj A pval variable_in_wt \\\n", - "0 Q0017 -0.039026 -0.039721 5.666642 0.591335 False \n", - "1 COX1 -0.102708 -0.104404 7.187218 0.278744 False \n", - "2 AI1 -0.092154 -0.101312 7.981813 0.789989 True \n", - "3 AI2 -0.113634 -0.123208 9.302573 0.695235 True \n", - "4 AI3 -0.009983 -0.018676 8.315141 0.975177 False \n", - "... ... ... ... ... ... ... \n", - "880725 YML099W-A -0.141110 -0.116465 8.092513 0.432310 False \n", - "880726 YNL067W-A 0.015283 -0.000849 6.472535 0.969828 False \n", - "880727 YNL162W-A 0.118187 0.061836 9.364032 0.696226 False \n", - "880728 YNR001W-A 0.027918 0.020460 6.351764 0.944834 False \n", - "880729 EGO4 -0.128120 0.111941 7.617117 0.846665 False \n", - "\n", - " multiple_probes \n", - "0 False \n", - "1 False \n", - "2 False \n", - "3 False \n", - "4 False \n", - "... ... \n", - "880725 False \n", - "880726 False \n", - "880727 False \n", - "880728 False \n", - "880729 False \n", - "\n", - "[880730 rows x 11 columns]" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "kemmeren_final" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "d48a21fc", - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "regulator_locus_tag", - "rawType": "object", - "type": "string" - }, - { - "name": "regulator_symbol", - "rawType": "object", - "type": "string" - }, - { - "name": "target_locus_tag", - "rawType": "object", - "type": "string" - }, - { - "name": "target_symbol", - "rawType": "object", - "type": "string" - }, - { - "name": "time", - "rawType": "float64", - "type": "float" - }, - { - "name": "mechanism", - "rawType": "object", - "type": "string" - }, - { - "name": "restriction", - "rawType": "object", - "type": "string" - }, - { - "name": "date", - "rawType": "object", - "type": "string" - }, - { - "name": "strain", - "rawType": "object", - "type": "string" - }, - { - "name": "green_median", - "rawType": "float64", - "type": "float" - }, - { - "name": "red_median", - "rawType": "float64", - "type": "float" - }, - { - "name": "log2_ratio", - "rawType": "float64", - "type": "float" - }, - { - "name": "log2_cleaned_ratio", - "rawType": "float64", - "type": "float" - }, - { - "name": "log2_noise_model", - "rawType": "float64", - "type": "float" - }, - { - "name": "log2_cleaned_ratio_zth2d", - "rawType": "float64", - "type": "float" - }, - { - "name": "log2_selected_timecourses", - "rawType": "float64", - "type": "float" - }, - { - "name": "log2_shrunken_timecourses", - "rawType": "float64", - "type": "float" - } - ], - "ref": "58a78ce1-cf9e-4895-aeda-7a6835616ff2", - "rows": [ - [ - "0", - "YER045C", - "ACA1", - "YNL055C", - "POR1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "91090.295", - "124164.75", - "0.406527130762", - "0.0432783723328", - "0.167308051886", - "0.0", - "0.0", - "0.0" - ], - [ - "1", - "YER045C", - "ACA1", - "YIL114C", - "POR2", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "929.4918", - "662.37395", - "-0.0842615914349", - "-0.0977695029636", - "0.129415342895", - "-0.0", - "0.0", - "0.0" - ], - [ - "2", - "YER045C", - "ACA1", - "YPL188W", - "POS5", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "591.31345", - "734.7885", - "-0.0758081091353", - "-0.0508571587889", - "0.107883665412", - "-0.0", - "0.0", - "0.0" - ], - [ - "3", - "YER045C", - "ACA1", - "YIL160C", - "POT1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "205.71685", - "121.719", - "0.5847627015", - "0.1705942734", - "0.215645578247", - "0.1705942734", - "0.170594273399", - "0.00462968359641295" - ], - [ - "4", - "YER045C", - "ACA1", - "YGL205W", - "POX1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "119.5065", - "87.946405", - "0.179056555936", - "0.0200286797035", - "0.198205486203", - "0.0", - "0.0", - "0.0" - ], - [ - "5", - "YER045C", - "ACA1", - "YMR267W", - "PPA2", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "1275.302", - "1416.7", - "0.180721523462", - "0.0987729763456", - "0.104456428231", - "0.0", - "0.0", - "0.0" - ], - [ - "6", - "YER045C", - "ACA1", - "YHR075C", - "PPE1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "1326.4295", - "1359.741", - "0.390555253563", - "0.266483987284", - "0.131385515049", - "0.0", - "0.0", - "0.0" - ], - [ - "7", - "YER045C", - "ACA1", - "YNR032W", - "PPG1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "1099.959", - "1152.4355", - "-0.214915545431", - "-0.204062063627", - "0.117168825607", - "-0.0", - "0.0", - "0.0" - ], - [ - "8", - "YER045C", - "ACA1", - "YDL134C", - "PPH21", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "7493.293", - "7856.0695", - "-0.0775299490434", - "-0.133124758634", - "0.0927194595661", - "-0.0", - "0.0", - "0.0" - ], - [ - "9", - "YER045C", - "ACA1", - "YDL188C", - "PPH22", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "6011.497", - "4252.663", - "0.0215924976055", - "-0.0847941323527", - "0.126466576504", - "-0.0", - "0.0", - "0.0" - ], - [ - "10", - "YER045C", - "ACA1", - "YDR075W", - "PPH3", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "1084.69", - "953.6436", - "-0.143874021717", - "-0.0282806734827", - "0.115360567164", - "-0.0", - "0.0", - "0.0" - ], - [ - "11", - "YER045C", - "ACA1", - "YDR435C", - "PPM1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "1241.2985", - "1179.9235", - "0.125080147412", - "0.0906497470663", - "0.119276410483", - "0.0", - "0.0", - "0.0" - ], - [ - "12", - "YER045C", - "ACA1", - "YOL141W", - "PPM2", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "6.4157995", - "2.552427", - "-1.48508070136", - "-1.37626086393", - "0.520525479607", - "-0.0", - "0.0", - "0.0" - ], - [ - "13", - "YER045C", - "ACA1", - "YDR452W", - "PPN1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "4925.801", - "5224.871", - "-0.0386115679586", - "-0.121743225934", - "0.105781342704", - "-0.0", - "0.0", - "0.0" - ], - [ - "14", - "YER045C", - "ACA1", - "YPL179W", - "PPQ1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "7831.3035", - "5207.4895", - "-0.235458038146", - "-0.246050278661", - "0.134560096602", - "-0.0", - "0.0", - "0.0" - ], - [ - "15", - "YER045C", - "ACA1", - "YLR014C", - "PPR1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "525.5364", - "558.23195", - "-0.0720257307211", - "0.0530460147321", - "0.120093662406", - "0.0", - "0.0", - "0.0" - ], - [ - "16", - "YER045C", - "ACA1", - "YBR276C", - "PPS1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "2419.3925", - "2788.6315", - "0.0122846283333", - "0.0492935299569", - "0.0963579749912", - "0.0", - "0.0", - "0.0" - ], - [ - "17", - "YER045C", - "ACA1", - "YGR123C", - "PPT1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "1271.969", - "756.6555", - "-0.225361085921", - "0.0143144232829", - "0.143078282691", - "0.0", - "0.0", - "0.0" - ], - [ - "18", - "YER045C", - "ACA1", - "YPL148C", - "PPT2", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "219.84405", - "182.46345", - "0.0613811644696", - "0.0627513730773", - "0.156174422974", - "0.0627513730773", - "0.0627513730772", - "0.0" - ], - [ - "19", - "YER045C", - "ACA1", - "YHR201C", - "PPX1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "3845.679", - "3410.8325", - "-0.154114687167", - "-0.117511741551", - "0.0967597414614", - "-0.0", - "0.0", - "0.0" - ], - [ - "20", - "YER045C", - "ACA1", - "YML016C", - "PPZ1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "1342.195", - "1373.8975", - "-0.0362655421877", - "-0.0738714297714", - "0.101152319324", - "-0.0", - "0.0", - "0.0" - ], - [ - "21", - "YER045C", - "ACA1", - "YDR436W", - "PPZ2", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "1525.9705", - "1543.802", - "0.218383737172", - "0.0563081385542", - "0.113157259395", - "0.0", - "0.0", - "0.0" - ], - [ - "22", - "YER045C", - "ACA1", - "YEL060C", - "PRB1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "5911.781", - "5569.027", - "-0.455707646615", - "-0.571860070431", - "0.207328157539", - "-0.0", - "0.0", - "0.0" - ], - [ - "23", - "YER045C", - "ACA1", - "YMR297W", - "PRC1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "36126.48", - "39761.95", - "0.410772124663", - "0.131407081485", - "0.141859292096", - "0.131407081485", - "0.131407081485", - "0.01127754540884046" - ], - [ - "24", - "YER045C", - "ACA1", - "YCL057W", - "PRD1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "5299.106", - "5666.927", - "0.217251799868", - "0.0958561117769", - "0.118813194578", - "0.0", - "0.0", - "0.0" - ], - [ - "25", - "YER045C", - "ACA1", - "YER012W", - "PRE1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "3579.9625", - "3507.407", - "0.257330221212", - "0.212146176027", - "0.117287337021", - "0.0", - "0.0", - "0.0" - ], - [ - "26", - "YER045C", - "ACA1", - "YOR362C", - "PRE10", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "7441.532", - "9475.323", - "0.122188272669", - "0.0718660920117", - "0.103600609006", - "0.0", - "0.0", - "0.0" - ], - [ - "27", - "YER045C", - "ACA1", - "YPR103W", - "PRE2", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "7716.9675", - "10321.0485", - "0.0640417232106", - "0.0126514481209", - "0.0994120132179", - "0.0", - "0.0", - "0.0" - ], - [ - "28", - "YER045C", - "ACA1", - "YJL001W", - "PRE3", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "8069.2035", - "8403.125", - "-0.0893897513213", - "-0.138440536033", - "0.097284953069", - "-0.0", - "0.0", - "0.0" - ], - [ - "29", - "YER045C", - "ACA1", - "YFR050C", - "PRE4", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "6965.228", - "8014.186", - "0.130369797315", - "0.0550630647991", - "0.101602878222", - "0.0", - "0.0", - "0.0" - ], - [ - "30", - "YER045C", - "ACA1", - "YMR314W", - "PRE5", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "9141.1395", - "10320.41", - "0.137077950724", - "0.122101501008", - "0.0963541759471", - "0.0", - "0.0", - "0.0" - ], - [ - "31", - "YER045C", - "ACA1", - "YOL038W", - "PRE6", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "8436.736", - "10381.487", - "0.107476341446", - "0.0669936382585", - "0.105301291198", - "0.0", - "0.0", - "0.0" - ], - [ - "32", - "YER045C", - "ACA1", - "YBL041W", - "PRE7", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "2173.121", - "1985.315", - "-0.0822820646082", - "-0.0939006623829", - "0.105080316319", - "-0.0", - "0.0", - "0.0" - ], - [ - "33", - "YER045C", - "ACA1", - "YML092C", - "PRE8", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "7177.268", - "6808.069", - "-0.257981612196", - "-0.309732696759", - "0.0980797505299", - "-0.0", - "0.0", - "0.0" - ], - [ - "34", - "YER045C", - "ACA1", - "YGR135W", - "PRE9", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "12547.15", - "15473.24", - "0.0691947071468", - "0.0589345565803", - "0.10341285939", - "0.0", - "0.0", - "0.0" - ], - [ - "35", - "YER045C", - "ACA1", - "YIR008C", - "PRI1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "1434.682", - "1462.7145", - "-0.131154719562", - "-0.124372895682", - "0.0957816864817", - "-0.0", - "0.0", - "0.0" - ], - [ - "36", - "YER045C", - "ACA1", - "YKL045W", - "PRI2", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "2744.2565", - "2751.2355", - "-0.0877778595728", - "0.0103854720443", - "0.109576289525", - "0.0103854720443", - "0.0103854720443", - "0.0" - ], - [ - "37", - "YER045C", - "ACA1", - "YIL095W", - "PRK1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "175.32645", - "213.4852", - "-0.306520099792", - "-0.359214715414", - "0.147309738357", - "-0.0", - "0.0", - "0.0" - ], - [ - "38", - "YER045C", - "ACA1", - "YNL279W", - "PRM1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "151.35995", - "60.88811", - "-0.163721814997", - "-0.155100365723", - "0.184526335745", - "-0.0", - "0.0", - "0.0" - ], - [ - "39", - "YER045C", - "ACA1", - "YJL108C", - "PRM10", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "434.85715", - "487.97225", - "0.197827906609", - "0.270562592171", - "0.155702667029", - "0.270562592171", - "0.270562592171", - "0.14228978173358325" - ], - [ - "40", - "YER045C", - "ACA1", - "YMR278W", - "PRM15", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "831.627", - "1182.736", - "-0.0430126710172", - "-0.14258375941", - "0.134093156636", - "-0.0", - "0.0", - "0.0" - ], - [ - "41", - "YER045C", - "ACA1", - "YIL037C", - "PRM2", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "211.582", - "225.26115", - "0.0187691123028", - "-0.028108956191", - "0.127184486471", - "-0.0", - "0.0", - "0.0" - ], - [ - "42", - "YER045C", - "ACA1", - "YPL192C", - "PRM3", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "10.9251915", - "5.660958", - "-0.591203647306", - "-0.509557179708", - "0.446843040608", - "-0.0", - "0.0", - "0.0" - ], - [ - "43", - "YER045C", - "ACA1", - "YPL156C", - "PRM4", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "817.6399", - "1094.5992", - "-0.114741081918", - "-0.0275536173095", - "0.141289574881", - "-0.0275536173095", - "-0.0275536173095", - "-0.0" - ], - [ - "44", - "YER045C", - "ACA1", - "YIL117C", - "PRM5", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "2402.5815", - "3609.9235", - "0.0528869881727", - "0.0523652440494", - "0.123605948584", - "0.0", - "0.0", - "0.0" - ], - [ - "45", - "YER045C", - "ACA1", - "YML047C", - "PRM6", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "10.5794195", - "2.8140655", - "-0.694608414725", - "-0.553824321969", - "0.643974825346", - "-0.0", - "0.0", - "0.0" - ], - [ - "46", - "YER045C", - "ACA1", - "YDL039C", - "PRM7", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "863.883", - "698.3094", - "-0.117281767333", - "0.0781061133085", - "0.177734867942", - "0.0781061133085", - "0.0781061133085", - "0.0" - ], - [ - "47", - "YER045C", - "ACA1", - "YGL053W", - "PRM8", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "823.95815", - "960.38885", - "0.389527968106", - "0.29532151425", - "0.127558963187", - "0.29532151425", - "0.29532151425", - "0.23594077664534263" - ], - [ - "48", - "YER045C", - "ACA1", - "YAR031W", - "PRM9", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "13.66105", - "16.78304", - "0.197818562658", - "0.201583564502", - "0.397577394418", - "0.0", - "0.0", - "0.0" - ], - [ - "49", - "YER045C", - "ACA1", - "YDR300C", - "PRO1", - "15.0", - "ZEV", - "P", - "20160229", - "SMY2049", - "803.2018", - "742.33955", - "-0.0789609053406", - "0.0695793804634", - "0.113501268363", - "0.0695793804634", - "0.0695793804634", - "0.0" - ] - ], - "shape": { - "columns": 17, - "rows": 891408 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
regulator_locus_tagregulator_symboltarget_locus_tagtarget_symboltimemechanismrestrictiondatestraingreen_medianred_medianlog2_ratiolog2_cleaned_ratiolog2_noise_modellog2_cleaned_ratio_zth2dlog2_selected_timecourseslog2_shrunken_timecourses
0YER045CACA1YNL055CPOR115.0ZEVP20160229SMY204991090.29500124164.7500000.4065270.0432780.1673080.0000000.0000000.00000
1YER045CACA1YIL114CPOR215.0ZEVP20160229SMY2049929.49180662.373950-0.084262-0.0977700.129415-0.0000000.0000000.00000
2YER045CACA1YPL188WPOS515.0ZEVP20160229SMY2049591.31345734.788500-0.075808-0.0508570.107884-0.0000000.0000000.00000
3YER045CACA1YIL160CPOT115.0ZEVP20160229SMY2049205.71685121.7190000.5847630.1705940.2156460.1705940.1705940.00463
4YER045CACA1YGL205WPOX115.0ZEVP20160229SMY2049119.5065087.9464050.1790570.0200290.1982050.0000000.0000000.00000
......................................................
891403YFL052WZNF1YGL087CMMS215.0ZEVP20161117SMY2245923.69900662.4565000.028204-0.0045660.117553-0.0000000.0000000.00000
891404YFL052WZNF1YKL175WZRT315.0ZEVP20161117SMY22455181.167503997.746500-0.136347-0.0541080.096181-0.0000000.0000000.00000
891405YFL052WZNF1YBR046CZTA115.0ZEVP20161117SMY2245466.54105581.0441000.3423840.0141900.1395310.0000000.0000000.00000
891406YFL052WZNF1YGR285CZUO115.0ZEVP20161117SMY224516498.7350017306.235000-0.143440-0.0572460.090285-0.0000000.0000000.00000
891407YFL052WZNF1YNL241CZWF115.0ZEVP20161117SMY22454058.685004520.9095000.013953-0.0964790.159377-0.0000000.0000000.00000
\n", - "

891408 rows × 17 columns

\n", - "
" - ], - "text/plain": [ - " regulator_locus_tag regulator_symbol target_locus_tag target_symbol \\\n", - "0 YER045C ACA1 YNL055C POR1 \n", - "1 YER045C ACA1 YIL114C POR2 \n", - "2 YER045C ACA1 YPL188W POS5 \n", - "3 YER045C ACA1 YIL160C POT1 \n", - "4 YER045C ACA1 YGL205W POX1 \n", - "... ... ... ... ... \n", - "891403 YFL052W ZNF1 YGL087C MMS2 \n", - "891404 YFL052W ZNF1 YKL175W ZRT3 \n", - "891405 YFL052W ZNF1 YBR046C ZTA1 \n", - "891406 YFL052W ZNF1 YGR285C ZUO1 \n", - "891407 YFL052W ZNF1 YNL241C ZWF1 \n", - "\n", - " time mechanism restriction date strain green_median \\\n", - "0 15.0 ZEV P 20160229 SMY2049 91090.29500 \n", - "1 15.0 ZEV P 20160229 SMY2049 929.49180 \n", - "2 15.0 ZEV P 20160229 SMY2049 591.31345 \n", - "3 15.0 ZEV P 20160229 SMY2049 205.71685 \n", - "4 15.0 ZEV P 20160229 SMY2049 119.50650 \n", - "... ... ... ... ... ... ... \n", - "891403 15.0 ZEV P 20161117 SMY2245 923.69900 \n", - "891404 15.0 ZEV P 20161117 SMY2245 5181.16750 \n", - "891405 15.0 ZEV P 20161117 SMY2245 466.54105 \n", - "891406 15.0 ZEV P 20161117 SMY2245 16498.73500 \n", - "891407 15.0 ZEV P 20161117 SMY2245 4058.68500 \n", - "\n", - " red_median log2_ratio log2_cleaned_ratio log2_noise_model \\\n", - "0 124164.750000 0.406527 0.043278 0.167308 \n", - "1 662.373950 -0.084262 -0.097770 0.129415 \n", - "2 734.788500 -0.075808 -0.050857 0.107884 \n", - "3 121.719000 0.584763 0.170594 0.215646 \n", - "4 87.946405 0.179057 0.020029 0.198205 \n", - "... ... ... ... ... \n", - "891403 662.456500 0.028204 -0.004566 0.117553 \n", - "891404 3997.746500 -0.136347 -0.054108 0.096181 \n", - "891405 581.044100 0.342384 0.014190 0.139531 \n", - "891406 17306.235000 -0.143440 -0.057246 0.090285 \n", - "891407 4520.909500 0.013953 -0.096479 0.159377 \n", - "\n", - " log2_cleaned_ratio_zth2d log2_selected_timecourses \\\n", - "0 0.000000 0.000000 \n", - "1 -0.000000 0.000000 \n", - "2 -0.000000 0.000000 \n", - "3 0.170594 0.170594 \n", - "4 0.000000 0.000000 \n", - "... ... ... \n", - "891403 -0.000000 0.000000 \n", - "891404 -0.000000 0.000000 \n", - "891405 0.000000 0.000000 \n", - "891406 -0.000000 0.000000 \n", - "891407 -0.000000 0.000000 \n", - "\n", - " log2_shrunken_timecourses \n", - "0 0.00000 \n", - "1 0.00000 \n", - "2 0.00000 \n", - "3 0.00463 \n", - "4 0.00000 \n", - "... ... \n", - "891403 0.00000 \n", - "891404 0.00000 \n", - "891405 0.00000 \n", - "891406 0.00000 \n", - "891407 0.00000 \n", - "\n", - "[891408 rows x 17 columns]" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "hackett_final" - ] - }, - { - "cell_type": "markdown", - "id": "81a946c6", - "metadata": {}, - "source": [ - "## Individual regulator correlation analysis and scatter plots\n", - "\n", - "Now we'll analyze correlations for each regulator individually, focusing on the relationship between Hackett effects and Kemmeren effects/p-values.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "75cc1bb9", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'regulator_symbol': 'ACA1', 'n_targets': 6075, 'eff_eff_corr': np.float64(0.01608402414610723), 'eff_eff_pval': np.float64(0.21004245167836072), 'eff_pval_corr': np.float64(-0.017860635301765383), 'eff_pval_pval': np.float64(0.1639456246498038)}\n", - "Correlation analysis function finished!\n" - ] - } - ], - "source": [ - "import numpy as np\n", - "from scipy.stats import spearmanr\n", - "\n", - "# Set up plotting parameters\n", - "plt.rcParams['figure.figsize'] = (12, 5)\n", - "plt.rcParams['font.size'] = 10\n", - "\n", - "# Example regulator symbol\n", - "chosen_regulator_symbol = 'ACA1'\n", - " \n", - "# Filter data for the specific regulator\n", - "hackett_reg = hackett_final[hackett_final['regulator_symbol'] == chosen_regulator_symbol].copy()\n", - "kemmeren_reg = kemmeren_final[kemmeren_final['regulator_symbol'] == chosen_regulator_symbol].copy()\n", - " \n", - "# Check if data was found\n", - "if len(hackett_reg) == 0 or len(kemmeren_reg) == 0:\n", - " print(f\"No data found for regulator {chosen_regulator_symbol}\")\n", - " \n", - "# Merge datasets on target_symbol\n", - "merged = pd.merge(\n", - " hackett_reg[['target_symbol', 'log2_shrunken_timecourses']].rename(columns={'log2_shrunken_timecourses': 'effect_hackett'}),\n", - " kemmeren_reg[['target_symbol', 'Madj', 'pval']].rename(columns={'Madj': 'effect_kemmeren', 'pval': 'pvalue_kemmeren'}),\n", - " on='target_symbol',\n", - " how='inner'\n", - ")\n", - " \n", - "if len(merged) < 3: # Need at least 3 points for correlation\n", - " print(f\"Insufficient data points ({len(merged)}) for regulator {chosen_regulator_symbol}\")\n", - "\n", - " \n", - "# Calculate correlations\n", - "# Remove any NaN values\n", - "clean_data = merged.dropna(subset=['effect_hackett', 'effect_kemmeren', 'pvalue_kemmeren'])\n", - " \n", - "if len(clean_data) < 3:\n", - " print(f\"Insufficient clean data points ({len(clean_data)}) for regulator {chosen_regulator_symbol}\")\n", - " \n", - "# Spearman correlations\n", - "eff_eff_corr, eff_eff_pval = spearmanr(clean_data['effect_hackett'], clean_data['effect_kemmeren'])\n", - "eff_pval_corr, eff_pval_pval = spearmanr(clean_data['effect_hackett'], clean_data['pvalue_kemmeren'])\n", - " \n", - "correlation_results = {\n", - " 'regulator_symbol': chosen_regulator_symbol,\n", - " 'n_targets': len(clean_data),\n", - " 'eff_eff_corr': eff_eff_corr,\n", - " 'eff_eff_pval': eff_eff_pval,\n", - " 'eff_pval_corr': eff_pval_corr,\n", - " 'eff_pval_pval': eff_pval_pval\n", - "}\n", - " \n", - "print(correlation_results)\n", - "print(\"Correlation analysis function finished!\")\n" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "b9324fd2", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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KEJDQ4Wnp6do3ry5WLt2rdi5c6fNA9/c3Fzx7rvvigYNGpjaN3XqVHH37l2rxwshxPz580WDBg2Eu7u71YFrXFyc6Nu3rwgMDBQeHh6iYcOGYvr06Rb9FaLsBr56vV5ERESUOBgVQoht27YJAKJnz55m2w0Gg1i/fr14/PHHRXh4uPDw8BDVqlUTTZs2FS+++KI4ceKEEEKIrVu3lthuI+MslLVr15q2RUZGmmagWPuKjY0t8bxEREREReHY1RzHrhy7EpF9FCGEKHkRBSIieuCBB7Bv3z6kpqaa7vJLREREROSMOHYlInJtGkc3gIjI2SQlJVlsW7NmDfbu3YsePXpw0EtEREREToNjVyKiyokzbYmICqlevTpatWqFe++9F1qtFkeOHMGuXbvg5+eHvXv3olmzZo5uIhERERERAI5diYgqKxZtiYgK+ec//4nvv/8ely5dQkZGBkJCQtCtWzdMnz4dMTExjm4eEREREZEJx65ERJUTi7ZEREREREREREREToRr2hIRERERERERERE5ERZtiYiIiIiIiIiIiJwIi7ZEVGX997//RevWreHn5wdFUfDSSy/ZtI/sk5eXh5kzZ6JBgwbw9PSEoijYuHFjifuIiIiIqhKOTSunCxcuQFEUjBkzxtFNISIXw6ItEbk840CouK969eqZPWbfvn0YPnw40tLS8Nxzz2HGjBno06dPifvKy65du6AoCmbOnFmuz1Naq1atKjHrwgPSBQsWYNasWQgPD8e0adMwY8YM000xittXXmbOnAlFUbBr165yfR4iIiKqmjg2rTjWxqbe3t6IiYnBP/7xDyQnJzu6iURE0twc3QAiorJSv359jBgxwuq+wMBAs+83b94MIQS++OIL3HfffTbvI9VDDz2EBx54wOq+li1bmn3/ww8/wNfXF9u2bYOHh4fN+4iIiIhcGcemFafg2PTmzZvYunUr/v3vf+Pbb7/Fn3/+ierVqzu4hURE9mPRlogqjXvuucfm2QBXr14FAISHh9u1j1Q9evTA66+/btOxV69eRfXq1a0WZYvbR0REROTKODatOIXHpnl5eejduzd27tyJJUuWOP2MYSIia7g8AhFVKcaPesXGxgIAoqKiTB+lMn68ytq+CxcumM6RkJCAp59+GnXr1oWnpyfCwsIwZswYXLx40epzxsfHY8KECYiKioKnpydq1qyJrl27YtWqVQDUj+t369YNADBr1iyzj3cVfN7CVq9eDUVRMHv2bKv7Dx06BEVRMHz4cNO2s2fPYuzYsaa2BAcHo0WLFnjppZcghLA1RpsYlyFISEjAxYsXzT4OWNy+guLi4vDoo4+iRo0a8PT0RIMGDfDWW28hMzPT6nPGxcXh8ccfR2hoKDw9PVGnTh0MHDgQv/76KwCga9eumDVrFgCgW7duRT4vERERUUXg2LR8xqbu7u6YOHEiAODAgQPFHjt+/HgoioK4uDir+xcuXAhFUfCf//zHtG3lypXo378/6tWrBy8vLwQHB5uKxLaqV69ekWPQrl27QlEUi+1CCKxcuRL3338//P39Ua1aNbRt2xYrV660+XmJyHVwpi0RVSn16tXDjBkzsHHjRhw9ehRTpkwxfTytZcuWRe4z/vn777+jd+/eyMjIwCOPPIIGDRrgwoULWLt2LX788Ufs27cP0dHRpuf79ddf0a9fP9y9exe9e/fGE088gTt37uDw4cNYvHgxxowZg65du+LChQv4/PPP0aVLF3Tt2tX0+MIfnSto4MCBeO6557B27Vq8/fbbFvtXr14NABg5ciQAdZZG+/btkZGRgX79+mHYsGHIyMjA2bNnsWzZMnzwwQdwcyu7fxaM/Vi0aBEAmG6YERgYaFpCwdo+o48//hiTJk1CYGAgHn30UdSsWRMHDx7Eu+++i507d2Lnzp1mM3QXL16Ml19+Gd7e3hgwYADq1q2LxMRE/Prrr1i/fj0eeOAB03q7u3fvxujRo00D5eJyJiIiIiovHJuW/9jUWvGzoJEjR2LlypVYs2YNOnfubLXdnp6eGDJkiGnbpEmT0KJFC/To0QMhISFITEzExo0b0aNHD3z77bfo379/qdtdmBACw4cPx3//+180aNAATz31FDw8PLBt2zaMHz8eJ06cwAcffFDmz0tEDiSIiFxcQkKCACDq168vZsyYYfXrxx9/NHvM6NGjBQCRkJBgcb6i9uXm5op69eoJPz8/cejQIbN9e/bsEVqtVjzyyCOmbdnZ2aJ27dpCo9FYPL8QQly+fNn09507dwoAYsaMGXb1fcSIEQKA+P33382263Q6ERoaKmrVqiV0Op0QQogPP/xQABCLFi2yOM+tW7dser7Y2FgBQDz00ENFZn3y5Emzx0RGRorIyEir5ytq399//y3c3NxEixYtRHJystm+uXPnCgDigw8+MG07cuSI0Gg0Ijw83OJ1MxgMIjEx0fT9jBkzBACxc+dOm/pMREREZA+OTSt+bDp37lyz7Xl5eaJ79+4CgJg1a1ax5zAYDKJu3boiKChIZGdnm+3766+/BAAxePBgs+3x8fEW57l69aoIDw8XDRo0MNtuvB5Gjx5ttr24MXKXLl1E4XLNJ598IgCIsWPHitzcXNP2nJwc8eijjwoA4uDBg8X2lYhcC2faElGlcf78edNH3wubMmVKqe+y+8MPP+DChQuYPXs2WrVqZbbvgQceQP/+/bFx40akpaXB398fmzZtQmJiIkaNGmX1uSMiIkrVHkCdGbBmzRqsWbMG7du3N23/+eefcf36dbz00kvQarVmj/H29rY4T3BwsF3Pu2PHDuzYscPqvpYtWyImJsau8xW2YsUK6HQ6LFmyxOLGEa+++ioWLlyI//73v5g6darpeIPBgHfeecfiY2aKonANOCIiIqpwHJtW3Nh0+/btyM7OBgDcunULW7duxdmzZxEVFYUXXnih2Mcal2yYO3cuNm/ejIEDB5r2GWcHF76hXFRUlMV5wsLCMGjQICxZsgQXL15EZGSkXX0oydKlS+Hj44OPPvoI7u7upu0eHh5499138f333+O///0v2rRpU6bPS0SOw6ItEVUavXv3xk8//VRu59+/fz8A4PTp01ZvZnDt2jUYDAacOXMGbdu2xR9//AEA6NWrV7m16aGHHkJYWBi++uorLFy40PQRsjVr1gDI//gZADz66KN44403MGnSJOzYsQN9+vRBly5dzD4yZ6u5c+fafCMyGcast27darU47O7ujlOnTpm+r4isiYiIiOzBsWnFjU0LTijw9PREvXr18I9//ANvvPGGqQBsLaOXXnoJgYGBGDlyJObOnYvVq1ebirYGgwFffvklqlevjocfftjscfHx8Zg7dy5++eUXJCYmIicnx2z/1atXy7Rom5mZib/++gvh4eF47733LPbn5eUBgNn4mIhcH4u2REQ2un37NgBg7dq1xR6XkZEBAEhNTQUA1K5du9zapNVq8dRTT2HBggXYunUr+vXrh/T0dGzcuBH33nsvWrdubTq2Xr162L9/P2bOnIktW7bgm2++AQDExMRg9uzZZut0OZox63fffdem41NTU6EoCsLCwsqzWUREREROg2PTfLZMKLA263nMmDEIDAxE48aN0aZNG2zZsgV37txBUFAQdu3ahStXruD55583m9l67tw5tG/fHmlpaejWrRseffRR+Pv7Q6PRYNeuXdi9e7dFEbe07ty5AyEEEhMTi5y9DeS/1kRUOWgc3QAiIlfh7+8PAPj+++8hhCjyq0uXLgDyb9SQmJhYru0yzlgwzmDYsGEDMjMzzWYyGDVt2hTr16/H7du3sW/fPrz99tu4du0ahg0bhr1795ZrO+1hzDotLa3YrI0CAwMhhEBSUpKjmkxERERUoTg2tY+1bAouqzVy5Ejk5uaaiseFb5xm9O9//xt37tzBqlWrsG3bNixatAizZ8/GzJkz7VoiTKPRQKfTWd1nLLAbGV/rNm3aFPta79y50+bnJyLnx6ItEZGNOnToAADYt2+fTccb1/H6+eefSzzWuLaXXq+3u10tWrRAs2bNsGnTJty9exdr1qwxrc1VFHd3d3Ts2BGzZs3Chx9+CCEEfvjhB7ufu7wYszZ+7K8kFZU1ERERkbPg2LRsPfnkk3Bzc8OaNWuQlZWFb7/9Fvfccw86duxodtz58+cBAP379zfbLoSwq9AcFBSEGzduWBRuMzIycPbsWbNtfn5+aNy4MU6ePImUlBQ7ekVEroxFWyIiG/Xv3x9169bFwoULERcXZ7E/Ly8Pv/76q+n7xx57DBEREVizZg22bt1qcXzBWQ7GtbYuX74s1baRI0ciKysLH374IX755Rd06dIFderUMTvmzz//RFpamsVjr1+/DgDw8vKSeu7y8Pzzz8PNzQ2TJ0/GpUuXLPanpKTg8OHDpu+fffZZaLVavPXWW7h48aLZsUIIXL161fR9abMmIiIicgYcm5atmjVrolevXti7dy8WLVqEtLQ0ixuQATCtVVswWwCYN28ejh8/bvPztWvXDnl5eWbLWwgh8MYbb1hd5uDFF19EZmYmnnnmGav7ExIScOHCBZufn4icH9e0JaJK49y5c1ZvMGD0+uuvl2rw5+npifXr16Nv377o0qULunfvjmbNmkFRFFy8eBF79uxB9erVTTcA8PT0xDfffIM+ffqgb9++6NOnD1q0aIG0tDQcOXIEmZmZpsJjTEwMwsPD8dVXX8HT0xMRERFQFAWTJ09GQEBAiW176qmn8Prrr2PWrFkwGAxWP362evVqrFixAp07d0b9+vXh7++PEydOYMuWLQgODsbYsWNtzqLgHXoLq1WrFp599lmbz2VN06ZNsWzZMjz33HNo1KgRHn74YdSvXx93795FfHw8du/ejTFjxmD58uUAgGbNmmHRokV48cUX0aRJEzz++OOIjIzEtWvXEBcXh379+mHRokUAgG7dukFRFLz55pv4+++/ERAQgMDAwBLvLExERERkD45NK25sWlZGjhyJLVu2YMaMGQBgtWj77LPPIjY2FoMGDcLQoUNRvXp17N+/H4cOHUK/fv2wefNmm57rhRdeQGxsLJ5++mls27YNISEh2LNnD1JSUtCiRQscPXrU7PiJEydi//79+Pzzz7F371706NED4eHhuH79Ok6dOoXff/8dX375pdmSD0Tk4gQRkYtLSEgQAEr8unPnjukxo0ePFgBEQkKCxfmK2yeEEFeuXBFTpkwRDRo0EJ6ensLf3180btxYPP3002LHjh0Wx587d06MHz9eRERECHd3d1GzZk3RtWtX8cUXX5gdt3//ftGlSxfh5+dnanNRbbCmR48eAoDw8vISqampFvv3798vJk6cKJo2bSoCAwOFt7e3aNCggXjhhRfExYsXbXqO2NjYEnNu0aKF2WMiIyNFZGSk1fMVt08IIf744w/xxBNPiPDwcOHu7i5q1KghWrduLV5//XVx8uRJi+N37twpHnnkEREcHCw8PDxERESEGDRokNi7d6/ZcatWrRLNmjUTnp6eAkCxbSAiIiKyB8emqoocm86dO9fmdhUnMzNT+Pv7CwCiU6dORR63c+dOcf/99ws/Pz8RGBgoHn74YfHnn3+KGTNmCABi586dpmON18Po0aMtzvPLL7+IDh06CE9PT1G9enUxcuRIcf36ddGlSxdRVLnm66+/Fj169BBBQUHC3d1d1K5dW3Tt2lUsWLBA3Lx5s7QREJETUYQocCcXIiIiIiIiIiIiInIormlLRERERERERERE5ERYtCUiIiIiIiIiIiJyIizaEhERERERERERETkRFm2JiIiIiIiIiIiInAiLtkREREREREREREROhEVbIiIiIiIiIiIiIifi5ugGOJLBYMDVq1fh5+cHRVEc3RwiIiIikiCEwN27dxEeHg6NpnLOSeC4lYiIiKhysHXsWqWLtlevXkWdOnUc3QwiIiIiKgOXL19GRESEo5tRLjhuJSIiIqpcShq7VumirZ+fHwA1JH9//xKPNxgMSEpKQlhYWKWd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WvbWr19vWgt28ODBAIChQ4eiYcOGePvtt0v8tNacOXOQkZGBP//8E3Xr1gUAtG/fHj179sSqVaswYcIE07EJCQmoV68ekpOTi33PLF26FGFhYfjll19My4tMnDgRMTExWLVqFV5++eXSdrtc8We5PGYnj9nJY3bymJ1tONPWwc6fP48mTZpYDHoBoGbNmmbfK4qCF154AWvXrkWjRo3g5eWFNm3aIC4uzuKxiYmJGDduHEJDQ+Hp6YkmTZpg5cqVZsfk5ubi7bffRps2bRAQEAAfHx88+OCD2Llzp9lxFy5cgKIo+OCDD7Bo0SLUr18fnp6eOHHiBGbOnAlFUXDmzBmMGDECAQEBCAkJwfTp0yGEwOXLl9G/f38EBASgfv36Fje0kGnDJ598YmpDu3btcODAAZvz/uWXX9C4cWN4eHggICAAY8aMQUpKis2PLwvr16+HVqs1G4h6eXlh/Pjx2LdvX4kzW9evX4927dqZCraAerfGhx56CN98843ZsZGRkTZ95Gv58uXQ6/WYPXs2AHU2bxW+RyEREVGZ++qrr3D16lXk5OTgypUr+Oqrr1C/wJpproJj16o3di3K+vXrERoaioEFPkoaEhKCIUOGYPPmzWY30rNmw4YNeOSRR0wFWwDo0aMHGjZsaDGmrVevnk1tSktLQ1BQkNl60G5ubqhRowa8vb1tOgcREZGzYDnbwSIjI7Fv3z4cP34cTZs2LfH43bt34+uvv8aLL74IT09PLFu2DH369MEff/xhevz169fRsWNH00A5JCQEP/74I8aPH4+0tDS89NJLANRBzaeffoonn3wSzzzzDO7evYvPPvsMvXv3xh9//IGWLVuaPXdsbCyys7MxYcIEeHp6Ijg42LRv2LBhaNy4MebNm4fNmzfjnXfeQXBwMFasWIHu3btj3rx5WL16NV555RW0b98enTt3lmrDl19+ibt372LixIlQFAXz58/HwIEDER8fD3d392Kz++WXX9CrVy/cd999eP7553H69Gl8/PHHSEhIwK5du4otbubk5ODu3bslvj4AUKNGjWL3Hz58GA0bNrS4Q2D79u0BAEeOHEGdOnWsPtZgMODYsWMYN26cxb727dvj559/xt27d+Hn52dTW422b9+OmJgYbNmyBa+88goSExMRFBSESZMmYdasWbyJHhEREQHg2LUqjl2LcvjwYbRu3dpi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HVnD66J0OP6Sx+zk8D0rj9nJY3bymJ08ZmcbFm3JZMeOHRg3bhwaNmyIatWqITo6Gk8//TSSkpJsevzp06fx8ssv47777oOXlxcURcGFCxeKPP5///sfWrduDS8vL9StWxczZsyATqezOC4lJQUTJkxASEgIfHx80K1bNxw6dEi2m8X67bff8MADD6BatWqoVasWXnzxRaSnp5sdk56ejhkzZqBPnz4IDg6GoihYtWpVubSHiIiIiKxLSkrC66+/jm7dusHPzw+KomDXrl12n+frr79Gp06d4OPjg8DAQNx333345ZdfTPuzsrIwfvx4NG3aFAEBAfD19UWLFi2wePFi5OXlWZxv27ZtpvFkUFAQBg8eXOyYuDQ4diUiIqq8WLQlk9deew27du3CgAED8OGHH+KJJ57AN998g1atWuHatWslPn7fvn348MMPcffu3RLXJvnxxx/x+OOPIzAwEEuWLMHjjz+Od955B5MnTzY7zmAwoF+/fvjyyy/xwgsvYP78+bhx4wa6du2Ks2fPlqq/hR05cgQPPfQQMjMzsXDhQjz99NP45JNPMGTIELPjkpOTMXv2bJw8eRItWrQo0zYQERERkW1Onz6N9957D4mJiWjWrJnUOWbOnIknn3wSderUwcKFC/HOO++gefPmSExMNB2TlZWFv//+Gw8//DDmzp2LDz74AC1atMDLL7+M0aNHm53vhx9+QJ8+fZCTk4N58+Zh6tSp2L17Nx544AHcvHmzVP0tjGNXIiKiyo03IrODRqNBSEgINJrKWeteuHAhHnjgAbP+9enTB126dMHSpUvxzjvvFPv4xx57DCkpKfDz88MHH3yAI0eOmO13c8u/3KZNm4bmzZvj559/Nm339/fHnDlzMGXKFMTExAAA1q9fj99++w3r1q3D4MGDAQBDhw5Fw4YNMWPGDHz55Zdl0XUAwJtvvomgoCDs2rUL/v7+AIB69erhmWeewc8//4xevXoBAMLCwpCUlIRatWrh4MGDaNeuXZm1oSgFsyPbVfb3bHlidvKYnTxmJ4/ZkTWV/bpo06YNbt26heDgYKxfv96iWFmS/fv3Y/bs2ViwYAFefvlls31CCOTk5AAAgoODsX//frP9zz77LAICArB06VIsXLgQtWrVAqBOgoiOjsbevXvh4eEBAHj00UfRunVrzJs3DwsWLJDtrgWOXSufyv6e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+/jss8+Qlpbm9Tbedtqmp6fj2LFjTXPsbDdJfX09CgsLkZubC4fDwV0/PszJMAwUFhZi4sSJcDgcYTEnt259nd55B+Yrr2DTtGnI/fRTOK65BrjsstCeUw/vtHV/zUZHR4fFnDx1907bwsJC5OTktLiwUSjPqadep4aGhhY/K8JhTj2509b9s8LdSinU59TR2P05J/e5q3vdhcOcvB1vb041NTW2zl1FRdsVK1bg2WefxRdffIE+ffr4eveg4esJvmVZqK2tRWxsrE/bmYnZdQWzk2N2csxOjtnJMTu53p5dV4u2H3/8MS677DKMGjUKa9as8dqy4M4778T//u//4rvf/S7eeust8TnwokWL8M4772DDhg22d1kAsjn29nXRFcxOziouRm1JCWJTU6FlZgZ6OCGF606O2ckxOzlmJ9fbs+vWnrY33HADDMPAGWecgSuuuAJpaWkt/jcLUJXjX/ziF5KHD2pRUVGBHkLIYnZyzE6O2ckxOzlmJ8fs5Jid3IUXXoi///3vuOyyy3DRRRfho48+aircWpaF66+/Hi+//DKuuOIK/PGPf0RkZKTPz2FZFhYvXoy33noL69at86lg2xVcF3LMTigjA1FDhwKtfj8ke7ju5JidHLOTY3ZyzK5zuuRO27Ztw7Jly1BSUoJnnnkGd911F+644442b+HGMAwUFBS02e5MnWN2csxOjtnJMTs5ZifH7OSYXddNnz4dq1evxu7duzF9+nQcP34cjY2NmDt3Ll5++WX85Cc/wRtvvCEq2ALAzTffjD/84Q/405/+hP79++PIkSM4cuRIi/5p/sZ1Icfs5JidHLOTY3ZyzE6O2ckxO3tEO21/+tOfoqKiAitWrMDkyZNbXLSBiIiIiCgUTZ06Fe+99x5mzpyJadOmISUlBR9//DGWLFmCX/7yl1167BdeeAEAMG3atBbH8/PzMX/+/C49NhERERGFH1HRdvPmzXjkkUdw/fXX+3s8REREREQBc9555+GDDz7ApZdeiq+++grLli3zy1+QCS4jQURERES9mKho21M9uIiIiIiIult2dnabY5GRkYiKisIrr7yCV155pcXnNE3Dli1bemp4RERERNQLaZbgv/3ffPNN3HHHHfj000+Rnp7eHePqEb5ehdeyLBiGAYfD0SuvbtcVzE6O2ckxOzlmJ8fs5JidXG/PztdzutamTZvm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svDQQw+hoaEh0EMLCY899hjOOecc9OnTBwkJCYEeTlB77rnncPrppyMmJgaTJ0/Gv//970APKSRs2LABP/jBDzB48GBomoa333470EMKCUuXLsXEiRPRv39/JCcnY9asWdi5c2eghxUSXnjhBWRnZyMuLg5xcXE4++yz8f777wd6WCHpl7/8JTRNw6233hrooQS9hx9+GJqmtXgbNWpUoIdFQYbnrV3D81bf8NzVdzxvleO5qxzPXf2D562+4bmrb1i0FdqxYwdM08SKFSvw1Vdf4cknn8Rvf/tb3HvvvYEeWkhoaGjA3LlzcdNNNwV6KEHtjTfewG233YaHHnoIhYWFGDduHC655BIcPXo00EMLejU1NRg3bhyee+65QA8lpKxfvx4333wzNm7ciDVr1qCxsREXX3wxampqAj20oJeWloZf/vKX+PLLL1FQUIDp06fj8ssvx1dffRXooYWUTZs2YcWKFcjOzg70UELGmWeeiZKSkqa3zz77LNBDoiDD89au4XmrfTx3leF5qxzPXeV47tp1PG+V4bmrDyzym2XLllkZGRmBHkZIyc/Pt+Lj4wM9jKA1adIk6+abb2762DAMa/DgwdbSpUsDOKrQA8B66623Aj2MkHT06FELgLV+/fpADyUkDRgwwPr9738f6GGEjKqqKuuMM86w1qxZY02dOtW65ZZbAj2koPfQQw9Z48aNC/QwKATxvNV3PG/tHM9du47nrV3Dc9eu4bmrfTxvleG5q2+409aPKioqkJiYGOhhUJhoaGjAl19+iYsuuqjpmK7ruOiii/Cvf/0rgCOj3qSiogIA+L3NR4Zh4PXXX0dNTQ3OPvvsQA8nZNx8882YOXNmi+971Lndu3dj8ODByMzMxH/9139h//79gR4ShQCet5K/8dyVggHPXWV47uo7nrfK8dzVvohADyBc7NmzB8888wyWL18e6KFQmCgvL4dhGEhJSWlxPCUlBTt27AjQqKg3MU0Tt956K84991yMHTs20MMJCf/5z39w9tlno66uDv369cNbb72FMWPGBHpYIeH1119HYWEhNm3aFOihhJTJkydj5cqVGDlyJEpKSvDII4/gvPPOw7Zt29C/f/9AD4+CFM9bqTvw3JUCjeeuvuO5qwzPW+V47uob7rRt5e67727TFLn1W+uTjkOHDmHGjBmYO3curr/++gCNPPAk2RFR8Lr55puxbds2vP7664EeSsgYOXIkNm/ejC+++AI33XQT5s2bh6+//jrQwwp6Bw4cwC233II//vGPiImJCfRwQsqll16KuXPnIjs7G5dccgnee+89nDx5En/+858DPTTqATxvleN5K1H44bmr73ju6juet3YNz119w522rdx+++2YP39+h7fJzMxs+vfhw4dxwQUX4JxzzsGLL77YzaMLbr5mRx0bOHAgHA4HSktLWxwvLS3FoEGDAjQq6i0WLVqEd999Fxs2bEBaWlqghxMyoqKiMHz4cADAhAkTsGnTJjz99NNYsWJFgEcW3L788kscPXoUubm5TccMw8CGDRvw7LPPor6+Hg6HI4AjDB0JCQkYMWIE9uzZE+ihUA/geascz1v9j+euFEg8d5XhuavveN7qXzx37RiLtq0kJSUhKSnJ1m0PHTqECy64ABMmTEB+fj50vXdvXPYlO+pcVFQUJkyYgI8//hizZs0CoP7k5+OPP8aiRYsCOzgKW5ZlYfHixXjrrbewbt06ZGRkBHpIIc00TdTX1wd6GEHvwgsvxH/+858WxxYsWIBRo0bhrrvu4omvD6qrq7F3715ce+21gR4K9QCet8rxvNX/eO5KgcBzV//iuWvneN7qXzx37RiLtkKHDh3CtGnTMGzYMCxfvhxlZWVNn+P/JHdu//79OH78OPbv3w/DMLB582YAwPDhw9GvX7/ADi6I3HbbbZg3bx7y8vIwadIkPPXUU6ipqcGCBQsCPbSgV11d3eJ/65xOJzZv3ozExEQMHTo0gCMLbjfffDP+9Kc/4Z133kH//v1x5MgRAEB8fDxiY2MDPLrgds899+DSSy/F0KFDUVVVhT/96U9Yt24d/vGPfwR6aEGvf//+bXrP9e3bF6eddhp70nXijjvuwA9+8AMMGzYMhw8fxkMPPQSHw4Grr7460EOjIMLz1q7heat9PHeV4XmrHM9d5XjuKsPz1q7huauPLBLJz8+3AHh9o87NmzfPa3Zr164N9NCCzjPPPGMNHTrUioqKsiZNmmRt3Lgx0EMKCWvXrvW6xubNmxfooQW19r6v5efnB3poQW/hwoXWsGHDrKioKCspKcm68MILrQ8//DDQwwpZU6dOtW655ZZADyPoXXnllVZqaqoVFRVlDRkyxLryyiutPXv2BHpYFGR43to1PG/1Dc9dfcfzVjmeu8rx3NV/eN5qH89dfaNZlmV1TzmYiIiIiIiIiIiIiHzVu5tZEREREREREREREQUZFm2JiIiIiIiIiIiIggiLtkRERERERERERERBhEVbIiIiIiIiIiIioiDCoi0RERERERERERFREGHRloiIiIiIiIiIiCiIsGhLREREREREREREFERYtCUiIiIiIiIiIiIKIizaEhH5YNq0aRg7dmygh9Elr776KkaNGoXIyEgkJCQ0HX/iiSeQmZkJh8OB8ePHB2x8vlq3bh00TcO6desCPRQiIiKioMJz1+DDc1cisotFWyIKKStXroSmaSgoKPD6+VA6Mf3666/x8MMP45tvvmnzueeffx4rV660/ViaprX7duONNzbdbseOHZg/fz6ysrLwu9/9Di+++CIA4MMPP8SSJUtw7rnnIj8/H48//nhXp9fGe++9h4cfftj27U3TxCuvvILJkycjMTER/fv3x4gRI3Dddddh48aNfh8fERERkb/x3NU7nrsSEXUuItADICLqrb7++ms88sgjmDZtGk4//fQWn3v++ecxcOBAzJ8/3/bjffe738V1113X5viIESOa/r1u3TqYpomnn34aw4cPbzr+ySefQNd1vPTSS4iKivJ5Lna89957eO6552yf/P785z/Hc889h8svvxz/9V//hYiICOzcuRPvv/8+MjMzMWXKFADA+eefj9ra2m4bNxERERHx3LUzPHclIn9j0ZaIKEyMGDEC11xzTYe3OXr0KAC0+NMy9/HY2NigOXksLS3F888/j+uvv75pR4XbU089hbKysqaPdV1HTExMTw+RiIiIiLqA565ERB1jewQiCnv5+fmYPn06kpOTER0djTFjxuCFF17wetv3338fU6dORf/+/REXF4eJEyfiT3/6U4eP/+GHH6JPnz64+uqr4XK5AKg/5briiiuQmJiImJgY5OXl4W9/+1vTfVauXIm5c+cCAC74/+3dfUzUdRwH8PdxQtKPwyg978KSx012wZgYLWQcHeI1H0ibGnMNq+HDvJS2+EMtdtV0za0FyhgSjlNzY8uITZnGWF63dC5rY8xILdnpsjDPp4tCUrlPfzh+4/ydPGl02vu13Xb3fbrf5/MPH773+9698IJ6HOzrr79GQkICOjs74fF41Pb8/Px7zkNCQgKcTicAYMqUKdDpdHjvvfeg0+ngcrnw119/qe83+Hjb3r17kZWV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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# create scatter plots\n", - "fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6))\n", - "\n", - "# scatter plot1: Hackett effect vs Kemmeren effect\n", - "scatter1 = ax1.scatter(clean_data['effect_hackett'], clean_data['effect_kemmeren'], \n", - " alpha=0.6, s=10, c='blue')\n", - "ax1.set_xlabel('Hackett Effect Size', fontsize=12)\n", - "ax1.set_ylabel('Kemmeren Effect Size', fontsize=12)\n", - "ax1.set_title(f'Regulator: {chosen_regulator_symbol}\\nEffect vs Effect', fontsize=14)\n", - "# Add correlation coefficient text\n", - "ax1.text(0.05, 0.95, f'Spearman ρ = {eff_eff_corr:.3f}\\np = {eff_eff_pval:.3e}', \n", - " transform=ax1.transAxes, fontsize=12, verticalalignment='top',\n", - " bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))\n", - "ax1.grid(True, linestyle='--', alpha=0.7)\n", - "\n", - "# scatter plot2: Hackett effect vs Kemmeren pvalue\n", - "# Set a minimum p-value to ensure that -log10(p) does not exceed the range of the plot\n", - "min_pvalue = 10**(-5) \n", - "\n", - "# copy pvalue column and replace 0 values\n", - "pvalue_for_plot = clean_data['pvalue_kemmeren'].copy()\n", - "pvalue_for_plot[pvalue_for_plot == 0] = min_pvalue\n", - "\n", - "# calculate log_pvalue\n", - "log_pvalue = -np.log10(pvalue_for_plot)\n", - "\n", - "scatter2 = ax2.scatter(clean_data['effect_hackett'], log_pvalue, \n", - " alpha=0.6, s=10, c='red')\n", - "ax2.set_xlabel('Hackett Effect Size', fontsize=12)\n", - "ax2.set_ylabel('-log10(Kemmeren p-value)', fontsize=12)\n", - "ax2.set_title(f'Regulator: {chosen_regulator_symbol}\\nEffect vs P-value', fontsize=14)\n", - "# Add correlation coefficient text\n", - "ax2.text(0.05, 0.95, f'Spearman ρ = {eff_pval_corr:.3f}\\np = {eff_pval_pval:.3e}', \n", - " transform=ax2.transAxes, fontsize=12, verticalalignment='top',\n", - " bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))\n", - "ax2.grid(True, linestyle='--', alpha=0.7)\n", - "\n", - "plt.tight_layout()\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "id": "da5d4268", - "metadata": {}, - "source": [ - "## Make distributions over the shared regulators" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "7db5d124", - "metadata": {}, - "outputs": [], - "source": [ - "from typing import Dict, Any, Optional\n", - "\n", - "# Functional encapsulation\n", - "# Calculate the correlation between the two datasets for a given regulator\n", - "def calculate_regulator_correlation(\n", - " chosen_regulator_symbol: str, \n", - " hackett_final: pd.DataFrame, \n", - " kemmeren_final: pd.DataFrame\n", - ") -> Optional[Dict[str, Any]]:\n", - " \"\"\"\n", - " Parameters:\n", - " chosen_regulator_symbol (str): The regulator symbol to be analyzed\n", - " hackett_final (DataFrame): Filtered Hackett dataset\n", - " kemmeren_final (DataFrame): Kemmeren dataset\n", - "\n", - " Returns:\n", - " dict: Dictionary containing correlation coefficients and other \n", - " statistical information, returns None if insufficient data\n", - " \"\"\"\n", - " # Filter data for the specific regulator\n", - " hackett_reg = hackett_final[hackett_final['regulator_symbol'] == chosen_regulator_symbol].copy()\n", - " kemmeren_reg = kemmeren_final[kemmeren_final['regulator_symbol'] == chosen_regulator_symbol].copy()\n", - " \n", - " # Check if data was found\n", - " if len(hackett_reg) == 0 or len(kemmeren_reg) == 0:\n", - " print(f\"No data found for regulator {chosen_regulator_symbol}\")\n", - " return None\n", - " \n", - " # Merge datasets on target_symbol\n", - " merged = pd.merge(\n", - " hackett_reg[['target_symbol', 'log2_shrunken_timecourses']].rename(\n", - " columns={'log2_shrunken_timecourses': 'effect_hackett'}),\n", - " kemmeren_reg[['target_symbol', 'Madj', 'pval']].rename(\n", - " columns={'Madj': 'effect_kemmeren', 'pval': 'pvalue_kemmeren'}),\n", - " on='target_symbol',\n", - " how='inner'\n", - " )\n", - " \n", - " if len(merged) < 3: # Need at least 3 points for correlation\n", - " print(f\"Insufficient data points ({len(merged)}) for regulator {chosen_regulator_symbol}\")\n", - " return None\n", - " \n", - " # Calculate correlations\n", - " # Remove any NaN values\n", - " clean_data = merged.dropna(subset=['effect_hackett', 'effect_kemmeren', 'pvalue_kemmeren'])\n", - " \n", - " if len(clean_data) < 3:\n", - " print(f\"Insufficient clean data points ({len(clean_data)}) for regulator {chosen_regulator_symbol}\")\n", - " return None\n", - " \n", - " # Spearman correlations\n", - " eff_eff_corr, eff_eff_pval = spearmanr(clean_data['effect_hackett'], clean_data['effect_kemmeren'])\n", - " eff_pval_corr, eff_pval_pval = spearmanr(clean_data['effect_hackett'], clean_data['pvalue_kemmeren'])\n", - " \n", - " correlation_results = {\n", - " 'regulator_symbol': chosen_regulator_symbol,\n", - " 'n_targets': len(clean_data),\n", - " 'eff_eff_corr': eff_eff_corr,\n", - " 'eff_eff_pval': eff_eff_pval,\n", - " 'eff_pval_corr': eff_pval_corr,\n", - " 'eff_pval_pval': eff_pval_pval\n", - " }\n", - " \n", - " return correlation_results" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "f116659e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'regulator_symbol': 'ACA1',\n", - " 'n_targets': 6075,\n", - " 'eff_eff_corr': np.float64(0.01608402414610723),\n", - " 'eff_eff_pval': np.float64(0.21004245167836072),\n", - " 'eff_pval_corr': np.float64(-0.017860635301765383),\n", - " 'eff_pval_pval': np.float64(0.1639456246498038)}" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# test the function\n", - "calculate_regulator_correlation('ACA1', hackett_final, kemmeren_final)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "89b6ad09", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Successfully calculated correlations for 145 regulators\n", - " regulator_symbol n_targets eff_eff_corr eff_eff_pval eff_pval_corr \\\n", - "0 ACA1 6075 0.016084 2.100425e-01 -0.017861 \n", - "1 ADA2 6075 -0.100878 3.243911e-15 -0.031116 \n", - "2 ARO80 6075 -0.037804 3.209219e-03 -0.027915 \n", - "3 AFT2 6075 -0.004887 7.033263e-01 0.034297 \n", - "4 ARR1 6075 -0.010794 4.002741e-01 -0.002523 \n", - "\n", - " eff_pval_pval \n", - "0 0.163946 \n", - "1 0.015293 \n", - "2 0.029576 \n", - "3 0.007507 \n", - "4 0.844150 \n" - ] - } - ], - "source": [ - "\n", - "# Extract unique regulator symbols from the filtered Hackett dataset\n", - "regulator_symbols = hackett_final['regulator_symbol'].unique().tolist()\n", - "\n", - "# Initialize an empty list to store all correlation results\n", - "all_correlation_results = []\n", - "\n", - "# Loop through each regulator symbol and calculate correlations\n", - "for regulator_symbol in regulator_symbols:\n", - " # Calculate correlations for this regulator\n", - " correlation_result = calculate_regulator_correlation(\n", - " regulator_symbol, \n", - " hackett_final, \n", - " kemmeren_final\n", - " )\n", - " \n", - " # Only add to results if we got a valid result (not None)\n", - " if correlation_result is not None:\n", - " all_correlation_results.append(correlation_result)\n", - "\n", - "# Convert the list of dictionaries to a DataFrame for easier analysis\n", - "results_df = pd.DataFrame(all_correlation_results)\n", - "\n", - "print(f\"Successfully calculated correlations for {len(results_df)} regulators\")\n", - "print(results_df.head()) # Display first few rows" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "7df98119", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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n2jYsGGWsbGxsRo3blye6wgDwPWE3NVx5K7O4e65q72TC6pXr6569eppyZIlOnXqlEqVKqWVK1fq4MGD6tevn1Ux+d9//1WDBg2UmpqqZs2aqX379goICJCHh4dWrlypVatWFThnutqpU6dkGIYOHTpk87yzZD3Pubn55pu1Y8cOHTp0SFWrVrXr+IWV25YpUybbByTSf79X06ZNyzWutLS0XAu0AwcO1LvvvquIiAh16NBBYWFhlnV1R48efV3ktm+99ZZuueUWxcXFacyYMRozZox8fX3VtWtXTZo0Kdfzh/ugQAvk08qVKyVJ9evXz7NvdHS04uPjdfHiRW3atEnff/+93n77bT300EMKDw9X48aN7T6urf+U7HH06NEc200mk+XTew8PD0nSpUuXsvXNSpIKKus/mJxiOnLkiFU/ZwoICJDZbNbx48etFp2XLt+EwDCMQjluYciK8/nnn7drkf+sG0QcOnSoQMeNjIy0minpKlnn/8033+i+++7Ls7+zzj83jRs3VmJiopYvX253gbYgr4fc3g9y2pa1n9TU1Dxn7eTk999/1zfffKNWrVrpu+++k6enp2XbunXrLDcrKYhr8T5RvHhxS/K6d+9erVixQu+9957eeustpaen6/3333d43wDgbshdHUfu6hxFKXft3r27nnvuOS1YsEBPPvmk5s+fb2m/0pQpU3Tq1CnNnz9fjz32mNW2p556SqtWrbLreB4eHrpw4YLNbVf/nmc9z/Xq1dPGjRvt2n9OGjdurJUrV2r58uVq3ry5XWNcldv+9ddfio6OtivGqx07dkzTpk1TrVq1tHbtWqsP6Y8cOZJrodte1+J9pFixYho8eLAGDx6sw4cPa9WqVYqLi9O8efN05MgR/fjjjw7vG9eOh6sDAK4nu3bt0oIFC+Tj46P777/f7nFeXl664447NHr0aL399tsyDEPffvutZXtWkcOeT9Pz69dff83Wtm/fPh04cEA1atSwXCKWdXdLW4lQ1iVWV/P09MxXzFl3t/zll1+yJUuGYeiXX36x6udMt912m6T//ki5UlZbYRy3MNSvX18mk0lr1661q//tt98uDw8PrVixwnJpU248PDwK5XfRWRo2bChJdp9/1iVTP/30U559HX0tZt1ZedKkSUpPT8+1b9an8Nf69ZD1vGVdDuaIrMvH2rVrZ1WclWy/10ju/z5xyy23qHfv3lq1apVuuukmLV682Cn7BQB3QO5qzd3/T7oSuat75q4PP/ywihUrpo8++kjp6elauHChKleurDvuuMOqX1bO1LFjR6t2wzBynUV8tVKlSunYsWPZPohIS0uzLA2Rxd/fX9WrV9c///xjdfm7I3r16iVPT0/NnDlTx48fz7VvVm5brVo1+fr66vfff9e5c+ey9XP2721+/yawZc+ePTIMQy1atMh2BVVuua1k//vftX5ewsPD9fDDD+uHH35Q5cqVtWzZsjz/PoF7oEAL2Om3335Tq1atlJGRoZdffjnPS242bdpk8zKFrE/OfH19LW1BQUGSpAMHDjgx4svmzZunP//80/LYMAwNHTpUmZmZ6tWrl6W9atWq8vf31+LFiy2Xi2TFO2bMGJv7DgoK0okTJ+xes6p8+fJq1qyZtm3bptmzZ1ttmzlzpv755x81b9482xpeztCzZ09Jly9TufLnkpKSYvlkNKuPu7v55pvVtWtXrVmzRhMmTLA5M2D9+vWWBCA0NFQPPvig1fpaV7o66QsKCtLBgwcL7wQKqGPHjipfvrwmT55s+cPoShcvXtTq1astjzt06KBy5crpo48+svnp8ZV/2Dn6WmzWrJkefvhh7dy5Uw888ICOHTuWrU9qaqqGDh2qmTNnSrr2r4d+/fqpWLFieuaZZ7R///5s20+fPp3jH7RZstZJu/L5laRt27Zp/PjxNse42/vE8ePH9ffff2drP3XqlDIyMqzemwHgekbump27/Z+UG3JX98xdy5Qpo3vvvVe//fabpk6dqtTU1GwzZKWcc6bXX3/dZh6Sk/r16+vixYtWS3sZhqEhQ4bYXKpg4MCBOnfunJ544gmb2/fu3avExMQ8j1u5cmW9+OKLOnHihNq0aaO9e/dm63P+/HlNnjzZsmSEt7e3Hn74YZ04cSJbXvjDDz/oxx9/VOXKlfM1Ez83sbGx8vf317Bhw7Rt27Zs28+dO5fnxISsn9OaNWus1p09ePCghgwZYnNMft//Cvt5ycjI0Jo1a7K1p6Wl6ezZs/Ly8rJccQD3xhIHwFX+/fdfy38yFy5c0LFjx7Rhwwb99ddf8vT01PDhwzVy5Mg89zN//ny9//77iomJUaVKlRQQEKDt27dryZIlCgoKUmxsrKVv8+bNFR8frwcffFBt2rSRr6+vateurfbt2xf4fFq1aqVGjRqpW7duCgkJ0fLly7Vx40bdcccdeuaZZyz9vL299cwzz2jcuHGqW7euOnbsqDNnzuibb75RkyZNbC683rx5c23cuFFt2rTR3XffLW9vb8XExCgmJibHeGbMmKG77rpLTzzxhL755hvdeuut2rZtmxYvXqyQkBDNmDGjwOdsS0xMjJ555hm98847io6O1oMPPijDMPTll1/q4MGDGjhwYK5x58elS5dyXZe1W7duqlatWoGOMX36dO3cuVMvvvii5s+fr0aNGikwMFAHDhzQxo0btXv3biUlJVk+CZ4+fbr+/vtvjR07VkuWLFHz5s1lGIZ27dqln376SUePHrVcTta8eXMtWLBAnTp10m233SZPT0916NBBtWrVsiu2K19Dtrz88ssFKoL5+PgoPj5ebdq0UZMmTdS8eXPVrFlTJpNJ+/bt06+//qrg4GDLDRB8fHy0YMECtW7dWm3atFHr1q1Vu3ZtpaamasuWLTp37pylMFmtWjWFh4frs88+k4+Pj8qVKyeTyaRnnnlGJUuWzDWuWbNmyTAMffbZZ7rlllt07733qkqVKjIMQ7t379by5ct15swZy6Vw0rV9PURHR2v69Ol6+umnVbVqVbVt21aVKlXSmTNntGfPHq1atUq9evXSe++9l+M+GjRooAYNGmjBggVKSkrSHXfcof3792vx4sVq166d4uPjs41xt/eJQ4cO6bbbblPt2rVVq1YtlS1bVidPntTXX3+tixcvavDgwQ7vGwBcgdyV3LWgbvTcNb+6d++uJUuWWF5Xtgq0Tz31lOLi4vTggw+qa9euCg4O1rp16/THH3+oXbt2+u677+w61oABAxQXF6fHH39cS5cuVUhIiH799VedPn1atWvX1tatW636P/nkk1q3bp3mzp2r3377TS1atFB4eLiOHj2qHTt2aP369frkk08UGRmZ57HHjBmj8+fPa8qUKapataqaN2+u6OhoeXl5ae/evVq2bJlOnjxp9YHIG2+8oVWrVmnMmDFas2aNGjZsqMTERH3xxRcqXry44uLinFYsDAkJ0aeffqouXbqodu3aat26tapVq6aMjAwlJiZq1apVuvPOO3NdXzgsLEwPPvigvvzyS91+++265557dPToUX377be65557cnwfye/7X2E+L+np6WrcuLGqVKmievXqqXz58jp79qy+/fZbHTlyRIMH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w+Xn56eGDRtq6dKldo09dOiQunbtqsDAQAUEBKhjx47as2dPtn4mk8nm1+uvv+7s0wEAAMANws/Pz2qGapbz589bttvj119/VZ8+fdSqVSuNHTs22zEk5Xic3I4xZMgQpaSkWL5yWxMXAAAA+Vdkljjo1auX4uPj9dxzzykqKkpz5sxR27ZttWLFCt111105jjt79qyaNWumlJQUDR06VF5eXpoyZYqaNGmiLVu2KDg42Kp/y5Yt1aNHD6u22267rVDOCQAAAEVfWFiYDh06lK09a1mC8PDwPPexdetWdejQQdHR0YqPj7e6EVjWMbL2efUM2KSkJMt6t7b4+PjYnHkLAAAA5ygSBdoNGzbos88+04QJEzR48GBJUo8ePRQdHa0XX3xRa9asyXHs9OnTtXv3bm3YsEH169eXJLVp00bR0dGaNGmSxo0bZ9W/SpUqeuyxxwrvZAAAAHBDqVOnjlasWKHU1FSrG4WtX7/esj03CQkJat26tcqUKaMlS5bopptusnkMSdq4caNVMfbw4cM6ePCg+vbtW/ATAQAAgEOKxBIH8fHx8vT0tEosfX191adPH61duzbXy7Di4+NVv359S3FWkqpVq6Z77rlHCxYssDkmPT3dcskZAAAAUBCdO3dWZmamZs6caWnLyMhQXFycGjZsaJnxun//fu3YscNq7JEjR3TvvffKw8NDP/74o0JCQmweo0aNGqpWrZpmzpypzMxMS/uMGTNkMpmsbigGAACAa6tIzKDdvHmzqlSpYjXjQJJldsCWLVts3szAbDbrzz//VO/evbNta9CggX766SedOXNG/v7+lvY5c+Zo+vTpMgxD1atX1/Dhw/XII484+YwAAABwo2jYsKG6dOmiIUOG6NixY6pcubLmzp2rxMREzZo1y9KvR48eWrVqlQzDsLS1bt1ae/bs0YsvvqjVq1dr9erVlm2hoaFq2bKl5fGECRPUoUMH3XvvverWrZv+/vtvvfvuu3r88cdVvXr1a3OyAAAAyKZIFGiTkpIs62pdKavt8OHDNsclJycrIyMjz7FVq1aVJN15553q2rWrbrnlFh0+fFjTpk3To48+qpSUFD399NM5xpeRkWF1Q4bU1FT7Tw4AAABF3rx58zRixAjNnz9fp06dUq1atfTtt98qJiYm13Fbt26VJL355pvZtjVp0sSqQHvfffdp4cKFGj16tJ555hmFhIRo6NCheuWVV5x7MgAAAMiXIlGgTU9Pt3njAl9fX8v2nMZJsnvsb7/9ZtWnd+/eqlevnoYOHapevXrlePfb8ePHa/To0XacCQAAAG5Evr6+mjBhgiZMmJBjn5UrV2Zru3I2rT06deqkTp065TM6AAAAFKYisQatn5+f1QzVLFnrxOZUOM1qd2SsJHl7e2vAgAE6ffq0Nm3alGO/IUOGKCUlxfKV25q4AAAAAAAAAG4cRWIGbVhYmA4dOpStPSkpSZIUHh5uc1xQUJB8fHws/fIzNkvW2rbJyck59vHx8bE5SxcAAAAAAADAja1IzKCtU6eOdu3alW1t1/Xr11u22+Lh4aGaNWtq48aN2batX79eFStWtLpBmC179uyRpBzvmAsAAAAAAAAAOSkSBdrOnTsrMzNTM2fOtLRlZGQoLi5ODRs2tMxy3b9/v3bs2JFt7O+//25VpN25c6d+/vlndenSxdJ2/PjxbMc9c+aMpk6dqtKlS6tevXrOPi0AAAAAAAAARVyRWOKgYcOG6tKli4YMGaJjx46pcuXKmjt3rhITEzVr1ixLvx49emjVqlVWN1Po16+fPvjgA7Vr106DBw+Wl5eXJk+erNDQUD3//POWftOmTdOiRYvUvn17lS9fXklJSZo9e7b279+v+fPny9vb+5qeMwAAAAAAAIDrX5Eo0ErSvHnzNGLECM2fP1+nTp1SrVq19O233yomJibXcf7+/lq5cqUGDRqkMWPGyGw2q2nTppoyZYrVsgWNGzfWmjVr9OGHH+rkyZMqUaKEGjRooNmzZ6t58+aFfXoAAAAAAAAAiiCTceV0UlwTqampKlmypFJSUhQQEODqcAAAANwSOZN74ucCwN2YzWYlJCQoNTVVAQEBqlSpkjw8isSKjgCuY/nJmYrMDFrYlpmZqU2bNumPP/7Q2bNnRT3+Mh8fH5UrV05NmjRRcHCwq8MBAACADeSytpHLAv/ZunWrFi1apOTkZEtbUFCQOnXqpNq1a7swMgCwHzNoXeBazTrYsGGDhg4dqtOnTysoKEilSpXiU8T/ycjI0MGDB2UYhtq2basRI0aoWDE+rwAAwJ0wU9M9kcu6HrkscNnWrVsVFxenGjVqqGXLlgoLC1NSUpKWLl2qbdu2KTY2liItAJdhBi20ZcsWPffcc6pbt6769eun6tWry2QyuTost5KSkqIffvhBkydP1qVLlzR27FhXhwQAAACRy9qDXBY3OrPZrEWLFqlGjRrq06eP5QOcyMhI9enTR7NmzdLXX3+tmjVr8uEOALdHgbaImjdvniIjIzV58mR5e3u7Ohy3VLJkST300EPy9fXVa6+9pieffFLly5d3dVjXhQsXLuj48eOuDgOAnUJCQvi/AMB1hVw2b+SyuNElJCQoOTlZPXv2zFaA9fDwUIsWLTR16lQlJCQoKirKRVECgH0o0BZBaWlpWrt2rZ555hkSWju0bt1aEydO1LJly9S7d29Xh3NdOH78uKZNm+bqMADYqX///ipbtqyrwwAAu5DL5g+5LG5UqampkqSwsDCb27Pas/oBgDujQFsEHTp0SBcvXlStWrVcHcp1wcfHR1WqVFFiYqKrQ7luhISEqH///q4OA052/PhxLViwQF27dlVISIirw4ET8fMEcD0hl80fclncqLLWc0xKSlJkZGS27UlJSVb9AMCdUaAtgjIyMiRJvr6+Lo7k+uHn56fz58+7Oozrhre3N7PxirCQkBB+vgAAlyGXzT9yWdyIKlWqpKCgIC1dutRqDVrp8vq0y5YtU3BwsCpVquTCKAHAPqyUXYS5+40UkpKS9PLLL6tZs2by9/eXyWTSypUr8xx3+vRplSlTRiaTSfHx8VbbVq5cKZPJZPNr3bp1Oe7T3Z8rAACAG42752fksoBreXh4qFOnTtq2bZtmzZqlvXv36vz589q7d69mzZqlbdu2qWPHjtwgDMB1gRm0cJmdO3fqjTfeUFRUlGrWrKm1a9faNe6VV17RuXPncu0zcOBA1a9f36qtcuXKDscKAAAAXIlcFnC92rVrKzY2VosWLdLUqVMt7cHBwYqNjVXt2rVdFxwA5AMFWrhMvXr1dPLkSQUFBSk+Pl5dunTJc8zff/+tGTNm6JVXXtErr7ySY7+7775bnTt3dma4AAAAgAW5LOAeateurZo1ayohIUGpqakKCAhQpUqVmDkL4LrCO9YNZtSoUTKZTPr333/Vq1cvBQYGqmTJkoqNjc3zk3xn8/f3V1BQUL7GPPvss7r//vt1991359n3zJkzunTpkqPhAQAAwM2QywKwxcPDQ1FRUapXr56ioqIozgK47jCD9gbVtWtX3XLLLRo/frz++OMPffjhhypTpozeeOONXMedO3fOruTX09NTpUqVcla4kqQvvvhCa9as0T///JPnXWpjY2N19uxZeXp66u6779aECRN0++23OzUeAAAAuAa5LIArmc1mZtACuK5RoL1B3XbbbZo1a5bl8cmTJzVr1qw8k9o333xTo0ePznP/FSpUyDPxzI/09HQNHjxYgwYNUmRkZI779vb21oMPPqi2bduqdOnS2r59uyZOnKi7775ba9as0W233ea0mAAAAOAa5LIAsmzdulWLFi1ScnKypS0oKEidOnViDVoA1w0KtDeop556yurx3Xffra+++sryiWNOevToobvuuivP/fv5+RU4xiu9/vrrunjxooYOHZprvzvvvFN33nmn5XGHDh3UuXNn1apVS0OGDNEPP/zg1LgAAABw7ZHLApAuF2fj4uJUo0YN9ezZU2FhYUpKStLSpUsVFxfHjcIAXDco0N6gypcvb/U46xKuU6dO5ZrUVqxYURUrVizU2K6WmJioCRMmaNq0abrpppvyPb5y5crq2LGjFi5cqMzMTHl6ehZClAAAALhWyGUBmM1mLVq0SDVq1FCfPn0sSxpERkaqT58+mjVrlr7++mvVrFmT5Q4AuD0KtDeonBI7wzByHXf27FmdPXvWrv2HhIQ4FNvVXnnlFZUtW1ZNmza1XA525MgRSdLx48eVmJio8uXL5/qfbkREhC5cuKC0tLRck3YAAAC4P3JZAAkJCUpOTlbPnj2zvX48PDzUokULTZ06VQkJCYqKinJRlABgHwq0yJeJEyde83W79u/fr3///dfmbId+/fpJujxbIjAwMMd97NmzR76+vg7NWgAAAEDRQC4LFB2pqamSpLCwMJvbs9qz+gGAO6NAi3xxxbpdY8aM0YkTJ6za/v77b40YMUIvvviiGjVqpBIlSki6PAvh6tkOW7du1eLFi9WmTRsubQEAALiBkcsCRUfWbPKkpCRFRkZm256UlGTVDwDcGQVa5Iuz1+0aM2aMJGnbtm2SpPnz52v16tWSpOHDh0uSzSQ6a4ZB/fr11alTJ0v7Qw89JD8/P915550qU6aMtm/frpkzZ6p48eJ6/fXXnRY3AAAArj/kskDRUalSJQUFBWnp0qVWa9BKl9enXbZsmYKDg1WpUiUXRgkA9qFAC5caMWKE1ePZs2dbvs9KavOjU6dO+vjjjzV58mSlpqYqJCREDzzwgEaOHKnKlSsXOF4AAAAgC7ks4DoeHh7q1KmT4uLiNGvWLLVo0UJhYWFKSkrSsmXLtG3bNsXGxjLzHMB1wWTktZI+nC41NVUlS5ZUSkpKoVxu8ddffyk2Nlaff/45nxba6ZlnnpGfn5/efPNNV4cCuMyhQ4c0bdo09e/fX2XLlnV1OABQ6DkTHEMu637IZXEj27p1qxYtWqTk5GRLW3BwsDp27KjatWu7MDIAN7r85EzMoAUAAAAAANel2rVrq2bNmkpISFBqaqoCAgJUqVIlZs4CuK5QoAUAAAAAANctDw8PRUVFuToMAHAYHykBAAAAAAAAgItQoC2CihW7PDH6woULLo7k+pGRkSEvLy9XhwEAAHDDI5fNP3JZAACubxRoi6AyZcpIkvbu3eviSK4PZrNZ+/btU2hoqKtDAQAAuOGRy+YPuSwAANc/CrRFUHBwsGrWrKmffvrJ1aFcFzZv3qyTJ0+qadOmrg4FAADghkcumz/ksgAAXP8o0BZRDzzwgFavXq333ntPFy9edHU4bmvXrl0aPny4KleurOjoaFeHAwAAAJHL2otcFgCAoqGYqwNA4Wjfvr2OHz+u6dOn6/PPP1fDhg1VqlQpeXhQkzcMQxkZGfrnn3+0a9cu3XLLLZo2bRrPDQAAgJsgl80ZuSwAAEUPBdoirHfv3oqJidGyZcv0xx9/aP/+/TIMw9VhuQVvb29FRkaqT58+aty4sXx9fV0dEgAAAK5ALpszclkAAIoWCrRFXOXKlVW5cmVXhwEAAADkG7ksAAC4EXAdDAAAAAAAAAC4CAVaAAAAAAAAAHARCrQAAAAAAAAA4CIUaAEAAAAAAADARSjQAgAAAAAAAICLUKAFAAAAAAAAABehQAsAAAAAAAAALkKBFgAAAAAAAABchAItAAAAAAAAALgIBVoAAAAAAAAAcBEKtAAAAAAAAADgIhRoAQAAAAAAAMBFKNACAAAAAAAAgItQoAUAAAAAAAAAF6FACwAAAAAAAAAuQoEWAAAAAAAAAFyEAi0AAAAAAAAAuAgFWgAAAAAAAABwEQq0AAAAgItlZGTopZdeUnh4uPz8/NSwYUMtXbo0z3E7d+7UoEGDdOedd8rX11cmk0mJiYk2+0ZGRspkMmX7euqpp5x8NgAAAMiPYq4OAAAAALjR9erVS/Hx8XruuecUFRWlOXPmqG3btlqxYoXuuuuuHMetXbtWb7/9tm699VZVr15dW7ZsyfU4derU0fPPP2/VVqVKFWecAgAAABxEgRYAAABwoQ0bNuizzz7ThAkTNHjwYElSjx49FB0drRdffFFr1qzJcWyHDh10+vRp+fv7a+LEiXkWaMuWLavHHnvMmeEDAACggFjiAAAAAHCh+Ph4eXp6qm/fvpY2X19f9enTR2vXrtWBAwdyHBsUFCR/f/98He/ChQtKS0tzOF4AAAA4FwVaAAAAwIU2b96sKlWqKCAgwKq9QYMGkpTnrNj8+Pnnn1W8eHHddNNNioyM1FtvveW0fQMAAMAxLHEAAAAAuFBSUpLCwsKytWe1HT582CnHqVWrlu666y5VrVpVJ0+e1Jw5c/Tcc8/p8OHDeuONN3Icl5GRoYyMDMvj1NRUp8QDAACAyyjQAgAAAC6Unp4uHx+fbO2+vr6W7c6wePFiq8exsbFq06aNJk+erGeeeUblypWzOW78+PEaPXq0U2IAAABAdixxAAAAALiQn5+f1QzVLOfPn7dsLwwmk0mDBg3SpUuXtHLlyhz7DRkyRCkpKZav3NbEBQAAQP4xgxYAAABwobCwMB06dChbe1JSkiQpPDy80I4dEREhSUpOTs6xj4+Pj80ZvgAAAHCOIjODNiMjQy+99JLCw8Pl5+enhg0baunSpXaNPXTokLp27arAwEAFBASoY8eO2rNnj82+s2bNUvXq1eXr66uoqCi98847zjwNAAAA3GDq1KmjXbt2ZVvbdf369ZbthSUr5w0JCSm0YwAAACB3RaZA26tXL02ePFmPPvqo3nrrLXl6eqpt27ZavXp1ruPOnj2rZs2aadWqVRo6dKhGjx6tzZs3q0mTJjp58qRV3/fff1+PP/64atSooXfeeUeNGjXSwIEDc72pAgAAAJCbzp07KzMzUzNnzrS0ZWRkKC4uTg0bNrTMct2/f7927Njh0DGSk5OVmZlp1Xbx4kW9/vrr8vb2VrNmzRw/AQAAABRIkVjiYMOGDfrss880YcIEDR48WJLUo0cPRUdH68UXX9SaNWtyHDt9+nTt3r1bGzZsUP369SVJbdq0UXR0tCZNmqRx48ZJunxzhmHDhqldu3aKj4+XJD3xxBMym8167bXX1LdvX5UqVaqQzxQAAABFTcOGDdWlSxcNGTJEx44dU+XKlTV37lwlJiZq1qxZln49evTQqlWrZBiGpS0lJcVyRddvv/0mSXr33XcVGBiowMBADRgwQNLlG4SNGTNGnTt31i233KLk5GR98skn+vvvvzVu3DjdfPPN1/CMAQAAcKUiUaCNj4+Xp6en+vbta2nz9fVVnz59NHToUB04cMAy88DW2Pr161uKs5JUrVo13XPPPVqwYIGlQLtixQqdPHlS/fr1sxrfv39/ffzxx/ruu+/02GOPFcLZAQAAoKibN2+eRowYofnz5+vUqVOqVauWvv32W8XExOQ67tSpUxoxYoRV26RJkyRJFSpUsBRoa9asqVtvvVUfffSRjh8/Lm9vb9WpU0cLFixQly5dCuekAAAAYJciUaDdvHmzqlSpooCAAKv2Bg0aSJK2bNlis0BrNpv1559/qnfv3tm2NWjQQD/99JPOnDkjf39/bd68WZJ0++23W/WrV6+ePDw8tHnz5vwXaM+fl7y9s7d7eFi3/+8OvjYVpG9GhnTFDAwrJpN05c0g8tP3wgXJbM45Dl9f1/f18bkctyRdvChddcmfU/peunT5yxl9vb0v//yc3dfLS/L0zH/fzMzLz0VOihW7/JXfvmbz5Z+ds/saxuXfYWf09fS8/Fw4u++1et3n1vfq55P3iMvf8x6R/768Rzi/rzu8R7gij8gtniLG19dXEyZM0IQJE3Lss3LlymxtkZGRVjNqc1KvXj0tXry4ICECAACgkBSJAm1SUpLCwsKytWe1HT582Oa45ORkZWRk5Dm2atWqSkpKkqenp8qUKWPVz9vbW8HBwTkeQ7q8hljGFX90WW4A0aPHf3+YXen226WRI/97/NhjOf/RFh0tjR//3+M+faSrbjBhERUlTZ783+N+/aRjx2z3jYiQpk//7/GgQdKBA7b7likjXXH5nV5+Wdq923bfgADp44//ezxypPT337b7+vhI/1tOQtLl89y40XZfSfrmm/++nzxZ+t9lfjZ98cV/xZpp06Tly3Pu+9FHUsmSl7//8ENpyZKc+86adfn5kKR586Svvsq577RpUvnyl79fsED69NOc+06efPnnJ0mLF0txcTn3HTdOqlnz8vc//ii9917OfV95RcqaPb5qlTR1as59X3pJuuuuy9+vXSvltvbyc89J99xz+fs//pBefTXnvk89JbVrd/n7bdukoUNz7hsbKz3wwOXvExKk//u/nPs+/LD0yCOXvz9wQOrfP+e+998vZX1Qc/z45ddRTtq2lZ5++vL3qamXX585ueeey8+F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xwUFBTklSAAAAMBR5LIAAABwZ3YVaIcOHarY2Fh99913kiSTyaSnnnpKTz31VK7jMjMzCx4hAAAAUADksgAAAHBndhVou3fvrgYNGmjlypU6evSoRo0apfvvv5+1uwAAAOD2yGUBAADgzuwq0EpS1apVVbVqVUlSXFycevbsqQ4dOhRaYAAAAICzkMsCAADAXdldoL3S3r17nR0HAAAAcE2QywIAAMCdeDg6MDU1Va+//rpatWql2267TRs2bJAkJScna/Lkyfr333+dFiQAAADgTOSyAAAAcBcOzaA9ePCgmjRpogMHDigqKko7duzQ2bNnJV2+2+3777+vffv26a233nJqsAAAAEBBkcsCAADAnThUoH3hhRd05swZbdmyRWXKlFGZMmWstnfq1EnffvutUwIEAAAAnIlcFgAAAO7EoSUOfvrpJw0cOFC33nqrTCZTtu0VK1bUgQMHChwcAAAA4GzksgAAAHAnDhVo09PTFRISkuP2M2fOOBwQAAAAUJjIZQEAAOBOHCrQ3nrrrfrll19y3L5o0SLddtttDgcFAAAAFBZyWQAAALgThwq0zz33nD777DO98cYbSklJkSSZzWb9+++/6t69u9auXatBgwY5NVAAAADAGchlAQAA4E4cuknYY489pn379mn48OEaNmyYJKl169YyDEMeHh4aN26cOnXq5Mw4AQAAAKcglwUAAIA7cahAK0nDhg1T9+7d9eWXX+rff/+V2WxWpUqV9MADD6hixYrOjBEAAABwKnJZAAAAuAuHC7SSVL58eS7/AgAAwHWJXBYAAADuoEAF2r179+r777/Xvn37JEmRkZFq3bq1brnlFqcEBwAAABQWclkAAAC4A4cLtM8//7zeeustmc1mq3YPDw8999xzmjhxYoGDAwAAAAoDuSwAAADchYcjgyZNmqQpU6bogQce0Nq1a3X69GmdPn1aa9euVefOnTVlyhRNmTLF2bECAAAABUYuCwAAAHfi0AzaDz74QB06dNCCBQus2hs2bKjPPvtM58+f1/vvv8+aXgAAAHA75LIAAABwJw7NoE1MTFSrVq1y3N6qVSslJiY6GhMAAABQaMhlAQAA4E4cKtCWKVNGW7duzXH71q1bFRIS4nBQAAAAQGEhlwUAAIA7cahA26VLF3344Yd6/fXXlZaWZmlPS0vTG2+8oQ8//FAPPfSQ04IEAAAAnIVcFgAAAO7EoTVoX3vtNW3ZskVDhw7VK6+8ovDwcEnS4cOHdenSJTVr1kyvvvqqUwMFAAAAnIFcFgAAAO7EoQJt8eLFtXz5cn399df6/vvvtW/fPklS69at1bZtW7Vv314mk8mpgQIAAADOQC4LAAAAd+JQgTZLx44d1bFjR2fFAgAAAFwz5LIAAABwB3avQWs2m/X6669r3rx5ufabN2+e3njjjQIHBgAAADiLu+eyGRkZeumllxQeHi4/Pz81bNhQS5cutWvsoUOH1LVrVwUGBiogIEAdO3bUnj17svUzmUw2v15//XVnnw4AAADywe4ZtPPmzdPw4cO1YcOGXPvVqFFDvXv3Vrly5fToo48WOEAAAACgoNw9l+3Vq5fi4+P13HPPKSoqSnPmzFHbtm21YsUK3XXXXTmOO3v2rJo1a6aUlBQNHTpUXl5emjJlipo0aaItW7YoODjYqn/Lli3Vo0cPq7bbbrutUM4JAAAA9rG7QPvxxx+rXbt2qlu3bq796tWrpw4dOmju3LkUaAEAAOAW3DmX3bBhgz777DNNmDBBgwcPliT16NFD0dHRevHFF7VmzZocx06fPl27d+/Whg0bVL9+fUlSmzZtFB0drUmTJmncuHFW/atUqaLHHnus8E4GAAAA+Wb3Egd//PGH7rnnHrv6Nm3aVH/88YfDQQEAAADO5M65bHx8vDw9PdW3b19Lm6+vr/r06aO1a9fqwIEDuY6tX7++pTgrSdWqVdM999yjBQsW2ByTnp6u8+fPO+8EAAAAUCB2F2jT0tLk7+9vV19/f3+dPXvW4aAAAAAAZ3LnXHbz5s2qUqWKAgICrNobNGggSdqyZYvNcWazWX/++aduv/32bNsaNGighIQEnTlzxqp9zpw5KlGihPz8/HTrrbfqk08+cc5JAAAAwGF2L3FQpkwZ7d69266+u3fvVkhIiMNBAQAAAM7kzrlsUlKSwsLCsrVntR0+fNjmuOTkZGVkZOQ5tmrVqpKkO++8U127dtUtt9yiw4cPa9q0aXr00UeVkpKip59+Osf4MjIylJGRYXmcmppq/8kBAAAgT3YXaGNiYjR//nwNHz5cxYsXz7FfWlqa5s+fr6ZNmzojPhQR51JOuToEAACKBP5PdYw757Lp6eny8fHJ1u7r62vZntM4SXaP/e2336z69O7dW/Xq1dPQoUPVq1cv+fn52TzO+PHjNXr0aDvOBAAAAI6wu0A7ePBgff7552rbtq0+/vhjlS1bNlufQ4cOqXv37jpy5Iief/55pwaK61OJEiVUzMtLu9escHUoAAAUGcW8vFSiRAlXh3Fdcedc1s/Pz2qGapasdWJzKpxmtTsyVpK8vb01YMAAPfXUU9q0aZPuuusum/2GDBmi//u//7M8Tk1NVURERI77BQAAQP7YXaCtU6eOZsyYoaeffloVK1ZUTEyMatasKX9/f505c0Z//fWXfvnlF5nNZk2bNk116tQpxLBxvQgMDNT/DRqktLQ0V4cCIA/Hjx/XggUL1LVrV5apAdxciRIlFBgY6OowrivunMuGhYXp0KFD2dqTkpIkSeHh4TbHBQUFycfHx9IvP2OzZBVak5OTc+zj4+Njc5YuAAAAnMPuAq0kPf7444qOjtbo0aP1888/a/ny5f/tqFgxNW/eXCNHjlSjRo2cHiiuX4GBgfwRCVxHQkJCbM4sA4DrnbvmsnXq1NGKFSuUmppqdaOw9evXW7bb4uHhoZo1a2rjxo3Ztq1fv14VK1bM88Zoe/bskSQ+mAMAAHAhj/wOuOOOO/T999/r9OnT2rp1q3799Vdt3bpVKSkp+uGHHyjOAgAAwG25Yy7buXNnZWZmaubMmZa2jIwMxcXFqWHDhpZZrvv379eOHTuyjf3999+tirQ7d+7Uzz//rC5duljajh8/nu24Z86c0dSpU1W6dGnVq1fP2acFAAAAO+VrBu2V/Pz8VLNmTWfGAgAAAFwT7pTLNmzYUF26dNGQIUN07NgxVa5cWXPnzlViYqJmzZpl6dejRw+tWrVKhmFY2vr166cPPvhA7dq10+DBg+Xl5aXJkycrNDTUah3dadOmadGiRWrfvr3Kly+vpKQkzZ49W/v379f8+fPl7e19Tc8ZAAAA/3G4QAsAAADAOebNm6cRI0Zo/vz5OnXqlGrVqqVvv/1WMTExuY7z9/fXypUrNWjQII0ZM0Zms1lNmzbVlClTrJYtaNy4sdasWaMPP/xQJ0+eVIkSJdSgQQPNnj1bzZs3L+zTAwAAQC4o0AIAAAAu5uvrqwkTJmjChAk59lm5cqXN9nLlyumLL77Idf8tW7ZUy5YtCxIiAAAACkm+16B1R6dPn1bfvn0VEhKiEiVKqFmzZvrjjz/sGrthwwb169dP9erVk5eXl0wmU679Z82aperVq8vX11dRUVF65513nHEKAAAAAAAAAG5A132B1mw2q127dvrkk080YMAAvfnmmzp27JiaNm2q3bt35zl+yZIl+vDDD2UymVSxYsVc+77//vt6/PHHVaNGDb3zzjtq1KiRBg4cqDfeeMNZpwMAAAAAAADgBnLdF2jj4+O1Zs0azZkzRyNHjlT//v21cuVKeXp6auTIkXmOf/rpp5WSkqKNGzfmetlXenq6hg0bpnbt2ik+Pl5PPPGE5s2bp0cffVSvvfaaTp065czTAgAAAAAAAHADKNAatGfOnNG+fft06tQpq7vJZsnrpgbOEB8fr9DQUD3wwAOWtpCQEHXt2lUfffSRMjIy5OPjk+P40NBQu46zYsUKnTx5Uv369bNq79+/vz7++GN99913euyxxxw7CQAAAFxz7pDLAgAAAA4VaE+ePKkBAwboyy+/VGZmZrbthmHIZDLZ3OZsmzdvVt26deXhYT0ZuEGDBpo5c6Z27dqlmjVrOuU4knT77bdbtderV08eHh7avHkzBVoAAIDrgDvlsgAAAIBDBdonnnhC33zzjQYOHKi7775bpUqVcnZcdktKSrI5uyEsLEySdPjwYacUaJOSkuTp6akyZcpYtXt7eys4OFiHDx/OcWxGRoYyMjIsj1NTUwscDwAAABzjTrksAAAA4FCB9qefftKgQYP05ptvOjUYs9msCxcu2NXXx8dHJpNJ6enpNpcw8PX1lXR57VhnSE9Pl7e3t81tvr6+uR5n/PjxGj16tFPiAAAAQMEUVi4LAAAAOMKhm4QVL15ckZGRTg5F+uWXX+Tn52fX186dOyVJfn5+VrNTs5w/f96y3Rn8/PxyLB6fP38+1+MMGTJEKSkplq8DBw44JSYAAADkX2HlsgAAAIAjHJpB+9hjj+mrr77KdsOsgqpWrZri4uLs6pu1hEFYWJiSkpKybc9qCw8Pd0psYWFhyszM1LFjx6yWObhw4YJOnjyZ63F8fHxyvVEZAAAArp3CymUBAAAARzhUoO3cubNWrVql1q1bq2/fvoqIiJCnp2e2fnXr1s3Xfm+++Wb16tUrX2Pq1KmjX3/9VWaz2epGYevXr1fx4sVVpUqVfO0vt+NI0saNG9W2bVtL+8aNG2U2my3bAQAA4N4KK5cFAAAAHOFQgfauu+6yfL906dJs26/lnW87d+6s+Ph4LVy4UJ07d5YknThxQl988YXat29vNXM1ISFBklSpUqV8H6d58+YKCgrSjBkzrAq0M2bMUPHixdWuXbsCngkAAACuBXfKZQEAAACHCrT2LkNwLXTu3Fl33HGHYmNjtX37dpUuXVrTp09XZmZmthtz3XPPPZKkxMRES9u+ffs0f/58SZdnw0rSmDFjJEkVKlRQ9+7dJV1eg/a1115T//791aVLF7Vq1Uq//vqrPvroI40dO1ZBQUGFfaoAAABwAnfKZQEAAACHCrQ9e/Z0dhwO8/T01JIlS/TCCy/o7bffVnp6uurXr685c+aoatWqeY7fu3evRowYYdWW9bhJkyaWAq0k9evXT15eXpo0aZIWL16siIgITZkyRc8++6xzTwoAAACFxp1yWQAAAMChAu2Vzp49qwMHDkiSIiIidNNNNxU4qPwqVaqUPvzwQ3344Ye59rty5myWpk2byjAMu4/1xBNP6IknnshviAAAAHBD7pDLAgAA4MbmkXcX237//Xc1a9ZMpUqVUnR0tKKjo1WqVCk1b97cslQAAAAA4I7IZQEAAOAuHJpBu379ejVt2lTe3t56/PHHVb16dUnSP//8o08//VQxMTFauXKlGjRo4NRgAQAAgIIilwUAAIA7cahAO2zYMJUtW1arV6/WzTffbLVt1KhRaty4sYYNG2bzrrgAAACAK5HLAgAAwJ04tMTB+vXr9eSTT2ZLaCUpNDRUffv21bp16wocHAAAAOBs5LIAAABwJw4VaD08PHTp0qUct2dmZsrDw+HlbQEAAIBCQy4LAAAAd+JQ5nnnnXdq2rRp2rdvX7Zt+/fv1/Tp09W4ceMCBwcAAAA4G7ksAAAA3IlDa9COGzdOMTExqlatmu6//35VqVJFkrRz5059/fXXKlasmMaPH+/UQAEAAABnIJcFAACAO3GoQHvbbbdp/fr1GjZsmBYvXqxz585JkooXL67WrVtrzJgxuvXWW50aKAAAAOAM5LIAAABwJw4VaCXp1ltv1VdffSWz2azjx49LkkJCQlivCwAAAG6PXBYAAADuwuECbRYPDw+FhoY6IxYAAADgmiKXBQAAgKvZVaB99dVXZTKZNGzYMHl4eOjVV1/Nc4zJZNKIESMKHCAAAABQEOSyAAAAcGcmwzCMvDp5eHjIZDIpPT1d3t7edl36ZTKZlJmZ6ZQgi5rU1FSVLFlSKSkpCggIcHU4ACBJOnTokKZNm6b+/furbNmyrg4HAJyWM5HLOhe5LAB3dODAAU2cOFGDBw9WRESEq8MBgHzlTHbNoDWbzbk+BgAAANwVuSwAAADcGXdBAAAAAAAAAAAXcahA6+npqU8++STH7Z9//rk8PT0dDgoAAAAoLOSyAAAAcCcOFWjzWrY2MzNTJpPJoYAAAACAwkQuCwAAAHfi8BIHOSWtqamp+vHHH1W6dGmHgwIAAAAKE7ksAAAA3IXdBdrRo0fL09NTnp6eMplMeuyxxyyPr/wqVaqU5s+fr27duhVm3AAAAIDdyGUBAADgrorZ27FBgwbq16+fDMPQ9OnT1bJlS1WpUsWqj8lkUokSJVSvXj098MADTg8WAAAAcAS5LAAAANyV3QXaNm3aqE2bNpKktLQ0PfXUU2rYsGGhBQYAAAA4C7ksAAAA3JXdBdorxcXFOTsOAAAA4JoglwUAAIA7cahAm+XgwYPavHmzUlJSZDabs23v0aNHQXYPAAAAFBpyWdgjOTlZaWlprg4DQB6OHj1q9S8A91WiRAkFBQW5Ogy34lCB9vz58+rZs6e+/PJLmc1mmUwmGYYhyfqOuCS1AAAAcDfksrBXcnKyxo0dq4uXLrk6FAB2mj9/vqtDAJAHr2LFNHTYMIq0V3CoQDt06FAtXLhQY8eOVaNGjdS0aVPNnTtXYWFhmjp1qg4fPqx58+Y5O1YAAACgwMhlYa+0tDRdvHRJbUuXVrCXl6vDAQDgunfy4kUtOXFCaWlpFGiv4FCBNj4+XrGxsXrppZd08uRJSVLZsmXVvHlztWjRQs2bN9e0adM0Y8YMpwYLAAAAFBS5LPIr2MtLoT4+rg4DAAAUUR6ODDp27JgaNGggSfLz85Mkq3WZHnzwQS1cuNAJ4QEAAADORS4LAAAAd+JQgTY0NNQy26B48eIqVaqUdu7cadmempqq8+fPOydCAAAAwInIZQEAAOBOHFrioGHDhlq9erVeeuklSVL79u01YcIEhYWFyWw2a8qUKbrjjjucGigAAADgDOSyAAAAcCcOzaAdOHCgKlasqIyMDEnSa6+9psDAQHXv3l09e/ZUyZIl9fbbbzs1UAAAAMAZyGUBAADgThyaQXvXXXfprrvusjyOiIjQP//8o7/++kuenp6qVq2aihVzaNcAAABAoSKXBQAAgDtxWubp4eGh2rVrO2t3AAAAwDVDLgsAAABXsatA+8svvzi085iYGIfGAQAAAM5CLgsAAAB3ZleBtmnTpjKZTHbv1DAMmUwmZWZmOhwYAAAA4AzksgAAAHBndhVoV6xYUdhxAAAAAIWCXBYAAADuzK4CbZMmTQo7DgAAAKBQkMsCAADAnXkUdAdJSUnaunWr0tLSnBEPAAAAcM2QywIAAMDVHC7Qfv3116pWrZrKlSununXrav369ZKkEydO6LbbbtOiRYucFSMAAADgVOSyAAAAcBcOFWi/+eYbPfDAAypdurRGjhwpwzAs20qXLq2yZcsqLi7OaUECAAAAzkIuCwAAAHfiUIH21VdfVUxMjFavXq3+/ftn296oUSNt3ry5wMEBAAAAzkYuCwAAAHfiUIH277//VteuXXPcHhoaqmPHjjkcFAAAAFBYyGUBAADgThwq0BYvXjzXGyns2bNHwcHBDgcFAAAAFBZyWQAAALgThwq0zZo109y5c3Xp0qVs244cOaIPPvhA9957b4GDAwAAAJzNHXPZjIwMvfTSSwoPD5efn58aNmyopUuX2jX20KFD6tq1qwIDAxUQEKCOHTtqz549NvvOmjVL1atXl6+vr6KiovTOO+848zQAAADgAIcKtGPGjNHBgwdVv359vf/++zKZTPrxxx81fPhw1axZU4ZhaOTIkc6OFQAAACgwd8xle/XqpcmTJ+vRRx/VW2+9JU9PT7Vt21arV6/OddzZs2fVrFkzrVq1SkOHDtXo0aO1efNmNWnSRCdPnrTq+/777+vxxx9XjRo19M4776hRo0YaOHCg3njjjcI8NQAAAOShmCODqlWrpt9++00DBw7UiBEjZBiGJkyYIElq2rSppk2bpsjISGfGCQAAADiFu+WyGzZs0GeffaYJEyZo8ODBkqQePXooOjpaL774otasWZPj2OnTp2v37t3asGGD6tevL0lq06aNoqOjNWnSJI0bN06SlJ6ermHDhqldu3aKj4+XJD3xxBMym8167bXX1LdvX5UqVaqQzxQAAAC25HsG7cWLF/Xnn38qICBAy5Yt04kTJ7R+/XqtXbtWR48e1c8//6zq1asXRqwAAABAgbhjLhsfHy9PT0/17dvX0ubr66s+ffpo7dq1OnDgQK5j69evbynOSpcL0Pfcc48WLFhgaVuxYoVOnjypfv36WY3v37+/0tLS9N133znxjAAAAJAf+Z5B6+HhoXr16mnSpEkaOHCgSpUqZZUQAgAAAO7KHXPZzZs3q0qVKgoICLBqb9CggSRpy5YtioiIyDbObDbrzz//VO/evbNta9CggX766SedOXNG/v7+2rx5syTp9ttvt+pXr149eXh4aPPmzXrsscfyF/j585K3d/Z2Dw/r9vPnc95HQfpmZEiGYbuvyST5+DjW98IFyWy2xFPs0iWZLl68fHxJ8vL6r++lSznvtzD7Fit2OW5Jysz8L1537evp+d/z5w59zebL/XPi4XG5v7v0NYzLvxPu3Ndkuvw7keXiRdf3ze11VFh9Jd4jHOnLe0TB+l6H7xHFLl26/H++rf/3i1geYa98F2g9PT1VoUIFZWRk5HcoAAAA4FLumMsmJSUpLCwsW3tW2+HDh22OS05OVkZGRp5jq1atqqSkJHl6eqpMmTJW/by9vRUcHJzjMaTLNzC78vlKTU29/E2PHtYFgyy33y5duYbvY49d/sPGluhoafz4/x736SNl7f9qUVHS5Mn/Pe7XTzp2zHbfiAhp+vT/Hg8aJOU0E7lMGWnWrP8ev/yytHu3JKl0erpiExJUztdXPh4eMvv6KvmKQnbJH36Q15EjNndrFCumk716WR4HLF8u71xmQ594/HHL9/6rVsln796c+/bsaXnub1q9Wr7/i9eWk48+KsPPT5JUYt06+f3zT459kx96SGZ//8t9N26U319/5dj31IMPKvN/y2IU37JFxf/3IYAtpzt21KWQEEmS37ZtKrFhQ459U9q21cXwcEmS786duimXJT5S771XF8qXlyT5JCTI/5dfcu7bvLkuVKwoSfJOTFTAzz/n2PdMTIwyqlS53PfgQQX89FOOfc/eeafO33qrJMnryBGVXLIkx75pDRoovVYtSVKxkycV+PXXOfY9d9ttOlevniTJ8/Rplfryyxz7ptesqbSGDSVJHmfPKujzz3PuW7260ho3liSZzp9X8Mcf59j3fFSUzjZpcvnBpUsqPXdujn0zbrlFZ+65x/I4t74XIiKU2qqV5XHwxx/LlENh5+LNNyvlvvssj4M+/1weORQ8LoWE6HTHjpbHpeLj5Xn2rM2+mYGBOtW58399Fy2S5+nTtvvedJNOdetmeRz43Xcqdvy4zb68R/yH94jLeI+47Or3iNglS1R6507pf797VopQHpHrh0pXcegmYc8884xmzpyp5ORkR4YDAAAALuNuuWx6erp8rpx58T++vr6W7TmNk2TX2PT0dHnbmu36v745HUOSxo8fr5IlS1q+bM3mBQAAgONMhpHbvHjbJk+erNmzZ+vQoUPq3LmzIiMj5XdV1dtkMmnQoEFOC7QoSU1NVcmSJZWSkpLtUjYAcJVDhw5p2rRp6t+/v8qWLevqcACg0HImd8tlo6OjFRoaquXLl1u1b9++XTVq1NB7772nJ598Mtu4EydOKCQkRK+++qpGjBhhtW369Onq37+/duzYoapVq2rAgAF67733dMnGLLUyZcronnvu0aeffmozPlszaCMiIpRy9Kjtn0sRujTxwIEDmjJ1qlqXLq2grNnCV17qnJmZ92XGhdHX0/O/S4fN5rwv26WvdV/DyPvS4axLnelrX1+T6b9LnaW8L3W+Fn1zex0VVl+J94ii0Pd6eM1db32veB2dvHhRPx45okHPPWf7Q98ilEekpqaqZGioXblsvpc4kGS5u6wkzbpyGq9VfBRoAQAA4H7cLZcNCwvToUOHsrUnJSVJksL/dynn1YKCguTj42Ppl9vYsLAwZWZm6tixY1bLHFy4cEEnT57M8RjS5Rm6tmbpytf38lde7OnjSF9bMTmj75V/vPn6KrNYMX2Xw6XPAADAAcWK3Rh5xIULdg9zqEC7N5e1TgAAAAB35m65bJ06dbRixQqlpqZaza5Yv369ZbstHh4eqlmzpjZu3Jht2/r161WxYkX5/2+twKx9bNy4UW3btrX027hxo8xmc47HwGVtS5dWsK31dgEAQL6cvHhRS06ccHUYbiffBdr09HS99dZbatasmdq3b18YMQEAAACFwh1z2c6dO2vixImaOXOmZXZvRkaG4uLi1LBhQ8vlf/v379e5c+dUrVo1q7Evv/yyNm7cqNtvv12StHPnTv38889WM4WbN2+uoKAgzZgxw6pAO2PGDBUvXlzt2rW7Fqd63Qr28lJofmbPAAAA5EO+C7R+fn56//33dev/7kIHAAAAXC/cMZdt2LChunTpoiFDhujYsWOqXLmy5s6dq8TERKslGHr06KFVq1bpyltI9OvXTx988IHatWunwYMHy8vLS5MnT1ZoaKief/55Sz8/Pz+99tpr6t+/v7p06aJWrVrp119/1UcffaSxY8cqKCjomp4zAAAA/uPQEgf16tXT33//7exYAAAAgELnjrnsvHnzNGLECM2fP1+nTp1SrVq19O233yomJibXcf7+/lq5cqUGDRqkMWPGyGw2q2nTppoyZYpCQkKs+vbr109eXl6aNGmSFi9erIiICE2ZMkXPPvtsYZ4aAAAA8uBQgXbq1Klq27atoqOj1atXLxUr5tBuAAAAgGvOHXNZX19fTZgwQRMmTMixz8qVK222lytXTl988YVdx3niiSf0xBNPOBIiAAAAColD2WivXr3k4eGhJ598UgMHDlTZsmXl5+dn1cdkMmnr1q1OCRIAAABwFnJZAAAAuBOHCrRBQUEKDg5W1apVnR0PAAAAUKjIZQEAAOBOHCrQ5nR5FQAAAODuyGUBAADgTjxcHYAznD59Wn379lVISIhKlCihZs2a6Y8//rBr7IYNG9SvXz/Vq1dPXl5eMplMOfY1mUw2v15//XVnnQoAAAAAAACAG4jDd0TIzMzURx99pO+++0779u2TJFWoUEH33XefHn30UXl6ejotyNyYzWa1a9dOW7du1QsvvKDSpUtr+vTpatq0qTZt2qSoqKhcxy9ZskQffvihatWqpYoVK2rXrl259m/ZsqV69Ohh1XbbbbcV+DwAAABw7bhLLgsAAAA4VKBNSUlRq1at9Pvvv8vf318VK1aUJC1dulRffvmlZsyYoR9//FEBAQFODdaW+Ph4rVmzRl988YU6d+4sSeratauqVKmikSNH6pNPPsl1/NNPP62XXnpJfn5+GjBgQJ4F2ipVquixxx5zWvwAAAC4ttwplwUAAAAcWuJg2LBh2rRpk9555x0dP35cf/zxh/744w8dO3ZM7777rjZu3Khhw4Y5O1ab4uPjFRoaqgceeMDSFhISoq5du+rrr79WRkZGruNDQ0Oz3bU3L+np6Tp//rxD8QIAAMC13CmXBQAAABwq0H711Vfq16+f+vXrJy8vL0u7l5eXnn76aT399NP68ssvnRZkbjZv3qy6devKw8P6VBo0aKBz587lOSM2v+bMmaMSJUrIz89Pt956a54zdAEAAOBe3CmXBQAAABwq0J48eVJVq1bNcXu1atWUnJzscFD5kZSUpLCwsGztWW2HDx922rHuvPNOjR07VosWLdKMGTPk6empRx99VDNmzMh1XEZGhlJTU62+AAAA4BrulMsCAAAADhVoK1eurMWLF+e4ffHixapUqVK+92s2m3X+/Hm7vgzDkHR5uQEfH59s+/L19bVsd5bffvtNzz77rDp06KCnnnpKmzZtUnR0tIYOHZrrccaPH6+SJUtaviIiIpwWEwAAAPKnsHJZAAAAwBEOFWj79eunn376SW3bttVPP/2kxMREJSYm6scff1S7du20dOlSDRgwIN/7/eWXX+Tn52fX186dOyVJfn5+NteZzVojNr/ry+aHt7e3BgwYoNOnT2vTpk059hsyZIhSUlIsXwcOHCi0mAAAAJC7wsplAQAAAEcUc2RQv379dOzYMb3++uv68ccfrbZ5eXnplVde0dNPP53v/VarVk1xcXF29c1awiAsLExJSUnZtme1hYeH5zuO/MiaDZvbZXA+Pj42Z/kCAADg2iusXBYAAABwhEMFWkkaNWqUBgwYoGXLlmnfvn2SpAoVKqhFixYqXbq0Q/u8+eab1atXr3yNqVOnjn799VeZzWarG4WtX79exYsXV5UqVRyKxV579uyRJIWEhBTqcQAAAOA8hZHLAgAAAI5wuEArSaVLl1a3bt2cFYtDOnfurPj4eC1cuFCdO3eWJJ04cUJffPGF2rdvbzVzNSEhQZIcWlPs+PHj2YqwZ86c0dSpU1W6dGnVq1evAGcBAACAa80dclkAAADA7gJtSkqKHnroIcXExGjo0KE59hs7dqxWr16tL774QjfddJNTgsxN586ddccddyg2Nlbbt29X6dKlNX36dGVmZmr06NFWfe+55x5JUmJioqVt3759mj9/viRp48aNkqQxY8ZIujyLonv37pKkadOmadGiRWrfvr3Kly+vpKQkzZ49W/v379f8+fPl7e1d2KcKAAAAB7lrLgsAAADYXaB99913tWbNGksxMydPPPGE3nzzTU2bNk0vvfRSgQPMi6enp5YsWaIXXnhBb7/9ttLT01W/fn3NmTNHVatWzXP83r17NWLECKu2rMdNmjSxFGgbN26sNWvW6MMPP9TJkydVokQJNWjQQLNnz1bz5s2df2IAAABwGnfNZQEAAAC7C7RfffWVunXrludaq2XKlNHDDz+sL7/88poltaVKldKHH36oDz/8MNd+V86czdK0aVMZhpHnMVq2bKmWLVs6GiIAAABcyJ1zWQAAANzYPPLuctmOHTt0++2329W3bt26+ueffxwOCgAAAHAmclkAAAC4K7sLtPbMMr2S2WzOdzAAAABAYSCXBQAAgLuyu0Bbvnx5bdq0ya6+mzZt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Bw5URkaGbd2PfvQjlZSUaPTo0QoNDVVpaaneeOMNXbp0Sb/85S9vyL0CAACgdR1+SJjZbFZCQoI2bdrUGfEAAAAAncbdclkPDw9t375d06ZN07Jly5SZmamgoCD95S9/sbViaEtYWJj++te/KioqSs8884wWL16scePGaceOHXanb++55x6ZTCa99tprmj17tt58802NGDFCH330kZKSkjrxDgEAAHAtHT5B6+Hhodtuu63Vp8UCAAAA7swdc9mAgACtWrVKq1atanPdf56c/U8DBgzQH//4xzb3pqWlKS0tzdEQAQAA0Ik6fIJWkr7//e/rzTff1Pnz550dDwAAANCpyGUBAADgThx6SFhjY6MsFouioqKUmpqqiIgIeXl52a0xmUx2DyYAAAAA3AG5LAAAANyJQwXap556yvbfq1evbnUNSS1w82poaNDZs2ddHQacrPkz5bO9+QQHB8vT09PVYQBug1wWAAAA7sShAu2JEyecHQeALuTs2bN67bXXXB0GOsmGDRtcHQKcbM6cObr11ltdHQbgNshlAQAA4E4cKtDedtttzo4DQBcSHBysOXPmuDoMAO0UHBzs6hAAt0IuCwAAAHfiUIEWQPfm6enJaTwAAAAAAAAnMDu68eOPP9bDDz+shIQE3X777YqMjLR7RUVFOTPONl24cEGzZs1ScHCwfHx8NHLkSB08ePCa+wzD0Nq1a3X//fcrLCxMPj4+iouL00svvaS6urpW96xevVpf+cpX1KtXL0VHR2v58uXOvh0AAAB0MnfKZQEAANC9OVSgzc/PV2JiorZu3arQ0FAdP35ckZGRCg0N1WeffabevXtrxIgRzo61VYZhKCUlRevXr9fcuXO1ePFinTlzRklJSfr000/b3Hv58mVlZGTo7NmzevTRR/Xqq68qMTFR8+fPV3JyspqamuzWv/HGG3rooYc0YMAALV++XHfddZcef/xx/exnP+vMWwQAAIATuVMuCwAAAJia/m8Vsh1GjBihc+fOac+ePWpoaFDfvn21c+dOjRo1Snv37lVycrJ+85vfKDk5uTNitrNhwwZNmzZNOTk5Sk1NlfTFA4xiYmKUnJys9evXX3VvQ0OD9u/fr7vvvttu/H/+5380f/587dixQ2PGjJEk1dbWKiwsTMOGDdPWrVtta9PT05Wbm6tTp04pICCgXTFXV1fL399fVVVV8vPz6+gtAwAAdAudlTO5Uy7bFZHLAnBHp06d0pIlS/TUU08pLCzM1eEAQIdyJodO0B48eFAzZ86Un5+fPDw8JEmNjY2SpK997Wt65JFH9Pzzzzty6Q7buHGjrFarJk+ebBsLDg7W1KlTtXnzZtXX1191r6enZ4virCRNmjRJknT06FHb2Pvvv6+KigrNnj3bbu2cOXN06dIlbdu27XpvBQAAADeAO+WyAAAAgEMF2h49esjX11eSdMstt6hnz546c+aMbT4yMlJHjhxxToTXcOjQIQ0ePFhms/2tJCYm6vLlyzp27FiHr/mvf/1LkhQUFGT3PpI0ZMgQu7UJCQkym822eQAAALg3d8plAQAAAIcKtLfffrutv6vJZFL//v313nvv2ea3bdumfv36OSfCaygrK1NISEiL8eax06dPd/iaixcvlp+fn93X2srKyuTh4aG+ffvarfX09FSfPn3afJ/6+npVV1fbvQAAAOAa7pTLAgAAAA4VaMeNG6fs7GxduXJFkvTkk09q06ZNio6OVnR0tLZs2aJHHnmkw9c1DEN1dXXtejW3zq2trZXFYmlxrV69etnmO+Lll1/Wzp079dOf/lS33HKLbby2tlaenp6t7unVq1eb77No0SL5+/vbXvTDAQAAcJ3OymUBAAAARzhUoH3++edVWFho69k1ffp0/frXv1ZcXJzi4+O1Zs0aPf300x2+7gcffCAvL692vYqLiyVJXl5erfaZraurs82317vvvqvnnntOM2fO1GOPPWY35+XlpYaGhlb31dXVtfk+8+bNU1VVle116tSpdscEAAAA5+qsXBYAAABwRA9HNvXs2VN9+vSxG0tPT1d6evp1BdO/f39lZWW1a21zC4OQkBCVlZW1mG8eCw0Nbdf1duzYoQcffFApKSl6/fXXW32/xsZGnTlzxq7NQUNDgyoqKtp8H4vF0uopXwAAANx4nZXLAgAAAI5wqEDbrL6+XgcPHtSZM2c0fPhwu4dqOaJfv36aMWNGh/YMGjRIBQUFMgzD7kFhe/fulbe3t2JiYq55jb1792rSpEkaMmSINmzYoB49Wv5vGTRokCRp//79GjdunG18//79MgzDNg8AAICuwdm5LAAAAOAIh1ocSNKyZcsUEhKir3/965o8ebI+/vhjSdK5c+cUFBSkNWvWOC3ItqSmpqq8vFybNm2yjZ07d045OTkaP3683cnVkpISlZSU2O0/evSoUlJSFBERoa1bt161VcGoUaMUGBiolStX2o2vXLlS3t7eSklJceJdAQAAoDO5Sy4LAAAAOHSCNisrS//93/+t//qv/9I999yj733ve7a5oKAgjRo1Sr/97W/txjtLamqqhg0bpoyMDB05ckRBQUFasWKFGhsbtWDBAru1o0ePliSVlpZKkmpqanTvvfeqsrJSmZmZ2rZtm936qKgo3XXXXZK+6EH74osvas6cOZoyZYruvfdeFRQUaN26dVq4cKECAwM7/V4BAABw/dwplwUAAAAcKtAuXbpUEyZM0Pr161VRUdFiPiEhQcuWLbvu4NrDw8ND27dvV2ZmppYtW6ba2loNHTpUa9euVWxsbJt7KyoqbA/seuaZZ1rMT58+3VaglaTZs2erZ8+eWrp0qbZs2aKwsDC98soreuKJJ5x7UwAAAOg07pTLAgAAAA4VaP/3f/9Xjz/++FXnAwMDW012O0tAQIBWrVqlVatWtbmu+eRss4iICDU1NXXovR5++GE9/PDDHQ0RAAAAbsLdclkAAAB0bw71oL3lllt07ty5q84fOXJE/fr1czgoAAAAoLOQywIAAMCdOFSgHTdunN58801duHChxdzhw4f11ltv6f7777/e2AAAAACnI5cFAACAO3GoQPvSSy+psbFRcXFxeu6552QymfT2228rPT1dQ4YMUd++ffXCCy84O1YAAADgupHLAgAAwJ04VKANDQ3VgQMH9K1vfUvvvvuumpqa9M477+j3v/+90tLStGfPHgUFBTk7VgAAAOC6kcsCAADAnTj0kDBJ6tu3r+3BXGfPnpVhGAoODpbZ7FDNFwAAALhhyGUBAADgLq47A21qalJTU5NMJpNMJpMzYgIAAABuCHJZAAAAuJrDBdojR44oNTVVfn5+CgkJUUhIiPz8/JSamqqioiJnxggAAAA4FbksAAAA3IVDLQ4KCgqUnJwswzA0YcIExcTESJKKi4u1ZcsW5eXl6Q9/+IO+8Y1vODVYAAAA4HqRywIAAMCdOFSg/cEPfqC+ffvqr3/9q8LCwuzmTp06pREjRujJJ5/U3/72N6cECQAAADgLuSwAAADciUMtDg4fPqzZs2e3SGglKSwsTI899pgOHz583cEBAAAAzkYuCwAAAHfiUIH2tttuU319/VXnGxoaWk14AQAAAFcj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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Effect-Effect Correlation Summary:\n", - "count 145.000000\n", - "mean -0.058967\n", - "std 0.072261\n", - "min -0.296206\n", - "25% -0.091059\n", - "50% -0.037956\n", - "75% -0.008329\n", - "max 0.042079\n", - "Name: eff_eff_corr, dtype: float64\n", - "\n", - "Effect-Pvalue Correlation Summary:\n", - "count 145.000000\n", - "mean -0.002324\n", - "std 0.047957\n", - "min -0.164093\n", - "25% -0.028492\n", - "50% -0.001771\n", - "75% 0.017656\n", - "max 0.186708\n", - "Name: eff_pval_corr, dtype: float64\n" - ] - } - ], - "source": [ - "# Set up the plotting style and parameters\n", - "plt.style.use('default')\n", - "plt.rcParams['figure.figsize'] = (14, 6)\n", - "plt.rcParams['font.size'] = 12\n", - "\n", - "# Create a figure with two subplots side by side\n", - "fig, (ax1, ax2) = plt.subplots(1, 2)\n", - "\n", - "# First boxplot: Effect-Effect Correlation Distribution\n", - "sns.boxplot(data=results_df, y='eff_eff_corr', ax=ax1, color='lightblue')\n", - "ax1.set_title('Distribution of Effect-Effect Correlations', fontsize=14)\n", - "ax1.set_ylabel('Spearman Correlation Coefficient', fontsize=12)\n", - "# Add a horizontal line at y=0 for reference\n", - "ax1.axhline(y=0, color='r', linestyle='--', alpha=0.7)\n", - "# Add text showing the number of regulators\n", - "ax1.text(0.05, 0.95, f'n = {len(results_df)}', transform=ax1.transAxes, \n", - " fontsize=12, verticalalignment='top',\n", - " bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))\n", - "\n", - "# Second boxplot: Effect-Pvalue Correlation Distribution\n", - "sns.boxplot(data=results_df, y='eff_pval_corr', ax=ax2, color='lightcoral')\n", - "ax2.set_title('Distribution of Effect-Pvalue Correlations', fontsize=14)\n", - "ax2.set_ylabel('Spearman Correlation Coefficient', fontsize=12)\n", - "# Add a horizontal line at y=0 for reference\n", - "ax2.axhline(y=0, color='r', linestyle='--', alpha=0.7)\n", - "# Add text showing the number of regulators\n", - "ax2.text(0.05, 0.95, f'n = {len(results_df)}', transform=ax2.transAxes, \n", - " fontsize=12, verticalalignment='top',\n", - " bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))\n", - "\n", - "# Adjust layout and display the plot\n", - "plt.tight_layout()\n", - "plt.show()\n", - "\n", - "# Optional: Print summary statistics\n", - "print(\"Effect-Effect Correlation Summary:\")\n", - "print(results_df['eff_eff_corr'].describe())\n", - "print(\"\\nEffect-Pvalue Correlation Summary:\")\n", - "print(results_df['eff_pval_corr'].describe())\n" - ] - }, - { - "cell_type": "markdown", - "id": "4becea1e", - "metadata": {}, - "source": [ - "## Check if all final_common regulators have other timepoints" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "id": "2cb70685", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Dataset 'time_0' contains 1037400 rows of data\n", - "Dataset 'time_5' contains 1025050 rows of data\n", - "Dataset 'time_10' contains 1018875 rows of data\n", - "Dataset 'time_15' contains 1012700 rows of data\n", - "Dataset 'time_20' contains 1006525 rows of data\n", - "Dataset 'time_30' contains 1031225 rows of data\n", - "Dataset 'time_45' contains 1018875 rows of data\n", - "Dataset 'time_60' contains 6175 rows of data\n", - "Dataset 'time_90' contains 1018875 rows of data\n", - "Dataset 'time_100' contains 12350 rows of data\n", - "Dataset 'time_120' contains 6175 rows of data\n", - "Dataset 'time_180' contains 6175 rows of data\n", - "Dataset 'time_290' contains 6175 rows of data\n" - ] - } - ], - "source": [ - "hackett_2020_copy = HfQueryAPI(repo_id=\"BrentLab/hackett_2020\")\n", - "\n", - "hackett_2020_copy.set_sql_filter(\n", - " \"hackett_2020\",\n", - " f\"\"\"\n", - " mechanism = 'ZEV' \n", - " AND restriction = 'P' \n", - " \"\"\")\n", - "\n", - "hackett_data_copy = hackett_2020_copy.query(\"SELECT * FROM hackett_2020\", \"hackett_2020\")\n", - "\n", - "# Get all unique time points\n", - "unique_times = hackett_data_copy['time'].unique()\n", - "unique_times.sort() # Sort in chronological order\n", - "\n", - "# Create a dictionary to store datasets split by time point\n", - "time_datasets = {}\n", - "\n", - "# Split data by each time point\n", - "for time_point in unique_times:\n", - " # Filter data for the current time point\n", - " time_data = hackett_data_copy[hackett_data_copy['time'] == time_point].copy()\n", - " \n", - " # Create meaningful key names using time point markers\n", - " key_name = f\"time_{int(time_point)}\" # Convert float to integer for naming\n", - " \n", - " # Store the dataset in the dictionary\n", - " time_datasets[key_name] = time_data\n", - " \n", - " # Print information about each dataset\n", - " print(f\"Dataset '{key_name}' contains {len(time_data)} rows of data\")" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "id": "8654baec", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Time 0: Contains 145/145 regulators (100.0%) - All present? True\n", - "Time 5: Contains 144/145 regulators (99.3%) - All present? False\n", - "Time 10: Contains 144/145 regulators (99.3%) - All present? False\n", - "Time 15: Contains 145/145 regulators (100.0%) - All present? True\n", - "Time 20: Contains 142/145 regulators (97.9%) - All present? False\n", - "Time 30: Contains 144/145 regulators (99.3%) - All present? False\n", - "Time 45: Contains 143/145 regulators (98.6%) - All present? False\n", - "Time 60: Contains 1/145 regulators (0.7%) - All present? False\n", - "Time 90: Contains 143/145 regulators (98.6%) - All present? False\n", - "Time 100: Contains 2/145 regulators (1.4%) - All present? False\n", - "Time 120: Contains 1/145 regulators (0.7%) - All present? False\n", - "Time 180: Contains 1/145 regulators (0.7%) - All present? False\n", - "Time 290: Contains 1/145 regulators (0.7%) - All present? False\n" - ] - } - ], - "source": [ - "# Check the percentage of final_common regulators present in each time-split dataset\n", - "\n", - "# Initialize a list to store the results\n", - "coverage_results = []\n", - "\n", - "# Get the total number of regulators in final_common for percentage calculation\n", - "total_regulators = len(final_common)\n", - "\n", - "# Iterate through each time-split dataset\n", - "for time_key, dataset in time_datasets.items():\n", - " # Extract the time point from the key name\n", - " time_point = time_key.replace('time_', '')\n", - " \n", - " # Get unique regulators in this time dataset\n", - " regulators_in_dataset = set(dataset['regulator_symbol'].unique())\n", - " \n", - " # Find the intersection with final_common regulators\n", - " common_regulators = regulators_in_dataset.intersection(set(final_common))\n", - " \n", - " # Calculate the percentage of final_common regulators present\n", - " percentage_present = (len(common_regulators) / total_regulators) * 100\n", - " \n", - " # Check if all final_common regulators are present\n", - " contains_all = len(common_regulators) == total_regulators\n", - " \n", - " # Store the result\n", - " coverage_results.append({\n", - " 'time_point': time_point,\n", - " 'contains_all_shared_regulators': contains_all,\n", - " 'percentage_present': percentage_present,\n", - " 'regulators_present': len(common_regulators),\n", - " 'total_regulators': total_regulators\n", - " })\n", - " \n", - " # Print the result for this time point\n", - " print(f\"Time {time_point}: Contains {len(common_regulators)}/{total_regulators} regulators ({percentage_present:.1f}%) - All present? {contains_all}\")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "tfbpapi-py3.11", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.9" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/docs/tutorials/datacard_tutorial.ipynb b/docs/tutorials/datacard_tutorial.ipynb index 055f8fe..e3e8ba8 100644 --- a/docs/tutorials/datacard_tutorial.ipynb +++ b/docs/tutorials/datacard_tutorial.ipynb @@ -25,17 +25,9 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chase/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", - " from .autonotebook import tqdm as notebook_tqdm\n" - ] - }, { "name": "stdout", "output_type": "stream", @@ -45,7 +37,7 @@ } ], "source": [ - "from tfbpapi.datainfo import DataCard\n", + "from tfbpapi.datacard import DataCard\n", "\n", "card = DataCard('BrentLab/harbison_2004')\n", "\n", @@ -63,7 +55,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -81,7 +73,7 @@ "num_configs : 1\n", "dataset_types : ['annotated_features']\n", "total_files : 7\n", - "last_modified : 2025-12-11T01:11:35+00:00\n", + "last_modified : 2025-12-16T20:28:09+00:00\n", "has_default_config : True\n" ] } @@ -98,7 +90,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -138,7 +130,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -172,34 +164,18 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Main Configuration Details:\n", - "========================================\n", - "Config name: harbison_2004\n", - "Dataset type: annotated_features\n", - "Number of features: 7\n", - "\n", - "Features:\n", - " • condition [experimental_condition]\n", - " Environmental condition of the experiment. Nearly all of the 204 regulators have a YPD condition, and some have others in addition.\n", - " • regulator_locus_tag [regulator_identifier]\n", - " Systematic gene name (ORF identifier) of the ChIPd transcription factor\n", - " • regulator_symbol [regulator_identifier]\n", - " Standard gene symbol of the ChIPd transcription factor\n", - " • target_locus_tag [target_identifier]\n", - " Systematic gene name (ORF identifier) of the target gene measured\n", - " • target_symbol [target_identifier]\n", - " Standard gene symbol of the target gene measured\n", - " • effect [quantitative_measure]\n", - " The chip channel ratio (effect size)\n", - " • pvalue [quantitative_measure]\n", - " pvalue of the chip channel ratio (effect)\n" + "ename": "AttributeError", + "evalue": "'DataCard' object has no attribute 'explore_config'", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mAttributeError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[6]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;66;03m# Explore the main data configuration in detail\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m config_info = \u001b[43mcard\u001b[49m\u001b[43m.\u001b[49m\u001b[43mexplore_config\u001b[49m(\u001b[33m'\u001b[39m\u001b[33mharbison_2004\u001b[39m\u001b[33m'\u001b[39m)\n\u001b[32m 4\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33mMain Configuration Details:\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 5\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33m=\u001b[39m\u001b[33m\"\u001b[39m * \u001b[32m40\u001b[39m)\n", + "\u001b[31mAttributeError\u001b[39m: 'DataCard' object has no attribute 'explore_config'" ] } ], @@ -246,7 +222,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -286,7 +262,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -326,7 +302,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -350,7 +326,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -394,7 +370,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -447,7 +423,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -508,7 +484,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -549,7 +525,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -577,7 +553,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -633,7 +609,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "metadata": {}, "outputs": [ { diff --git a/docs/tutorials/hfqueryapi_tutorial.ipynb b/docs/tutorials/hfqueryapi_tutorial.ipynb deleted file mode 100644 index 70c3ebb..0000000 --- a/docs/tutorials/hfqueryapi_tutorial.ipynb +++ /dev/null @@ -1,2214 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# HfQueryAPI Tutorial: Metadata-Driven Data Exploration\n", - "\n", - "This tutorial demonstrates how to use the HfQueryAPI for efficient metadata-driven exploration and querying of Hugging Face datasets, using the Hackett 2020 transcription factor overexpression dataset as an example.\n", - "\n", - "## Overview\n", - "\n", - "The HfQueryAPI provides a streamlined workflow for:\n", - "1. **Exploring metadata** to understand dataset structure\n", - "2. **Setting filters** based on metadata values\n", - "3. **Querying data** with automatic filter application\n", - "4. **Efficient SQL-based filtering** on large datasets\n", - "\n", - "## Dataset: Hackett 2020\n", - "\n", - "The `BrentLab/hackett_2020` dataset contains transcription factor overexpression data with embedded metadata fields including:\n", - "- `regulator_locus_tag` & `regulator_symbol`: Transcription factor identifiers\n", - "- `time`: Time point (0, 15, 30, 90 minutes)\n", - "- `mechanism`: Experimental mechanism (ZEV)\n", - "- `restriction`: Restriction enzyme treatment (P, NP)\n", - "- `date`, `strain`: Experimental metadata\n", - "\n", - "Let's start exploring!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Initialize HfQueryAPI and Getting Metadata\n", - "\n", - "First, we'll initialize the API client for the Hackett 2020 dataset:" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Initialized HfQueryAPI for BrentLab/hackett_2020\n", - "Repository type: dataset\n", - "Available configurations: ['hackett_2020']\n" - ] - } - ], - "source": [ - "from tfbpapi.HfQueryAPI import HfQueryAPI\n", - "import pandas as pd\n", - "\n", - "# Initialize the API client\n", - "api = HfQueryAPI(repo_id=\"BrentLab/hackett_2020\", repo_type=\"dataset\")\n", - "\n", - "print(f\"Initialized HfQueryAPI for {api.repo_id}\")\n", - "print(f\"Repository type: {api.repo_type}\")\n", - "print(f\"Available configurations: {[c.config_name for c in api.configs]}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "metadata = api.get_metadata(\"hackett_2020\")" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Experimental Conditions Summary\n", - "==================================================\n", - "\n", - "⏱️ Time Points:\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Time Point (min)", - "rawType": "float64", - "type": "float" - }, - { - "name": "Frequency", - "rawType": "int64", - "type": "integer" - }, - { - "name": "% of Records", - "rawType": "float64", - "type": "float" - } - ], - "ref": "537d2492-acbe-4e3d-a2cc-3c1129ec2ed1", - "rows": [ - [ - "0", - "0.0", - "217", - "12.8" - ], - [ - "1", - "2.5", - "8", - "0.5" - ], - [ - "2", - "5.0", - "212", - "12.5" - ], - [ - "3", - "7.5", - "2", - "0.1" - ], - [ - "4", - "8.0", - "2", - "0.1" - ], - [ - "5", - "10.0", - "187", - "11.0" - ], - [ - "6", - "12.5", - "2", - "0.1" - ], - [ - "7", - "15.0", - "212", - "12.5" - ], - [ - "8", - "18.0", - "1", - "0.1" - ], - [ - "9", - "20.0", - "184", - "10.9" - ], - [ - "10", - "30.0", - "216", - "12.8" - ], - [ - "11", - "45.0", - "213", - "12.6" - ], - [ - "12", - "60.0", - "20", - "1.2" - ], - [ - "13", - "90.0", - "212", - "12.5" - ], - [ - "14", - "100.0", - "2", - "0.1" - ], - [ - "15", - "120.0", - "1", - "0.1" - ], - [ - "16", - "180.0", - "1", - "0.1" - ], - [ - "17", - "290.0", - "1", - "0.1" - ] - ], - "shape": { - "columns": 3, - "rows": 18 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Time Point (min)Frequency% of Records
00.021712.8
12.580.5
25.021212.5
37.520.1
48.020.1
510.018711.0
612.520.1
715.021212.5
818.010.1
920.018410.9
1030.021612.8
1145.021312.6
1260.0201.2
1390.021212.5
14100.020.1
15120.010.1
16180.010.1
17290.010.1
\n", - "
" - ], - "text/plain": [ - " Time Point (min) Frequency % of Records\n", - "0 0.0 217 12.8\n", - "1 2.5 8 0.5\n", - "2 5.0 212 12.5\n", - "3 7.5 2 0.1\n", - "4 8.0 2 0.1\n", - "5 10.0 187 11.0\n", - "6 12.5 2 0.1\n", - "7 15.0 212 12.5\n", - "8 18.0 1 0.1\n", - "9 20.0 184 10.9\n", - "10 30.0 216 12.8\n", - "11 45.0 213 12.6\n", - "12 60.0 20 1.2\n", - "13 90.0 212 12.5\n", - "14 100.0 2 0.1\n", - "15 120.0 1 0.1\n", - "16 180.0 1 0.1\n", - "17 290.0 1 0.1" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Experimental Design Overview:\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Condition Type", - "rawType": "object", - "type": "string" - }, - { - "name": "Unique Values", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Most Common", - "rawType": "object", - "type": "string" - } - ], - "ref": "fc6ebeb5-026f-49dc-ab9a-89995ff14cb9", - "rows": [ - [ - "0", - "Mechanisms", - "2", - "ZEV" - ], - [ - "1", - "Restriction Treatments", - "3", - "P" - ], - [ - "2", - "Strains", - "215", - "SMY2207" - ], - [ - "3", - "Experimental Dates", - "23", - "20150101 to 20161117" - ] - ], - "shape": { - "columns": 3, - "rows": 4 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Condition TypeUnique ValuesMost Common
0Mechanisms2ZEV
1Restriction Treatments3P
2Strains215SMY2207
3Experimental Dates2320150101 to 20161117
\n", - "
" - ], - "text/plain": [ - " Condition Type Unique Values Most Common\n", - "0 Mechanisms 2 ZEV\n", - "1 Restriction Treatments 3 P\n", - "2 Strains 215 SMY2207\n", - "3 Experimental Dates 23 20150101 to 20161117" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Create summary tables for experimental conditions\n", - "print(\"Experimental Conditions Summary\")\n", - "print(\"=\" * 50)\n", - "\n", - "# Time points summary\n", - "time_summary = pd.DataFrame({\n", - " 'Time Point (min)': metadata['time'].value_counts().sort_index().index,\n", - " 'Frequency': metadata['time'].value_counts().sort_index().values,\n", - "})\n", - "time_summary['% of Records'] = (time_summary['Frequency'] / time_summary['Frequency'].sum() * 100).round(1)\n", - "\n", - "print(\"\\n⏱️ Time Points:\")\n", - "display(time_summary)\n", - "\n", - "# Experimental conditions table\n", - "conditions_summary = pd.DataFrame({\n", - " 'Condition Type': ['Mechanisms', 'Restriction Treatments', 'Strains', 'Experimental Dates'],\n", - " 'Unique Values': [\n", - " metadata['mechanism'].nunique(),\n", - " metadata['restriction'].nunique(), \n", - " metadata['strain'].nunique(),\n", - " metadata['date'].nunique()\n", - " ],\n", - " 'Most Common': [\n", - " metadata['mechanism'].mode().iloc[0],\n", - " metadata['restriction'].mode().iloc[0],\n", - " metadata['strain'].mode().iloc[0],\n", - " f\"{metadata['date'].min()} to {metadata['date'].max()}\"\n", - " ]\n", - "})\n", - "\n", - "print(\"\\nExperimental Design Overview:\")\n", - "display(conditions_summary)" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Transcription Factor Analysis\n", - "==================================================\n", - "\n", - "Dataset Overview:\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Metric", - "rawType": "object", - "type": "string" - }, - { - "name": "Value", - "rawType": "object", - "type": "string" - } - ], - "ref": "bd31a290-891e-4027-8987-337b87e04e47", - "rows": [ - [ - "0", - "Total Unique Transcription Factors", - "203" - ] - ], - "shape": { - "columns": 2, - "rows": 1 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
MetricValue
0Total Unique Transcription Factors203
\n", - "
" - ], - "text/plain": [ - " Metric Value\n", - "0 Total Unique Transcription Factors 203" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Create comprehensive transcription factor analysis tables\n", - "print(\"Transcription Factor Analysis\")\n", - "print(\"=\" * 50)\n", - "\n", - "print(f\"\\nDataset Overview:\")\n", - "tf_overview = pd.DataFrame({\n", - " 'Metric': [\n", - " 'Total Unique Transcription Factors',\n", - " ],\n", - " 'Value': [\n", - " f\"{metadata['regulator_locus_tag'].nunique():,}\",\n", - " ]\n", - "})\n", - "display(tf_overview)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Setting Simple Filters" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Filter Analysis\n", - "========================================\n", - "\n", - "Applied Filters:\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Filter Component", - "rawType": "object", - "type": "string" - }, - { - "name": "Value", - "rawType": "object", - "type": "string" - } - ], - "ref": "6c3fe59a-d2b0-49aa-b067-c472c07db1e0", - "rows": [ - [ - "0", - "Time Point", - "15 minutes" - ], - [ - "1", - "Mechanism", - "ZEV" - ], - [ - "2", - "Restriction", - "P (Restriction)" - ], - [ - "3", - "SQL Filter", - "time = 15 AND mechanism = 'ZEV' AND restriction = 'P'" - ] - ], - "shape": { - "columns": 2, - "rows": 4 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Filter ComponentValue
0Time Point15 minutes
1MechanismZEV
2RestrictionP (Restriction)
3SQL Filtertime = 15 AND mechanism = 'ZEV' AND restrictio...
\n", - "
" - ], - "text/plain": [ - " Filter Component Value\n", - "0 Time Point 15 minutes\n", - "1 Mechanism ZEV\n", - "2 Restriction P (Restriction)\n", - "3 SQL Filter time = 15 AND mechanism = 'ZEV' AND restrictio..." - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Top Transcription Factors in Filtered Dataset:\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Locus Tag", - "rawType": "object", - "type": "string" - }, - { - "name": "Symbol", - "rawType": "object", - "type": "string" - }, - { - "name": "Records in Subset", - "rawType": "int64", - "type": "integer" - } - ], - "ref": "183800f8-03b3-4fcb-846a-7585ea8533b5", - "rows": [ - [ - "0", - "YPL016W", - "SWI1", - "18525" - ], - [ - "1", - "YEL009C", - "GCN4", - "12350" - ], - [ - "2", - "YPL133C", - "RDS2", - "12350" - ], - [ - "3", - "Z3EV", - "Z3EV", - "12350" - ], - [ - "4", - "YBL008W", - "HIR1", - "6175" - ], - [ - "5", - "YBL021C", - "HAP3", - "6175" - ], - [ - "6", - "YBL025W", - "RRN10", - "6175" - ], - [ - "7", - "YBR239C", - "ERT1", - "6175" - ] - ], - "shape": { - "columns": 3, - "rows": 8 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Locus TagSymbolRecords in Subset
0YPL016WSWI118525
1YEL009CGCN412350
2YPL133CRDS212350
3Z3EVZ3EV12350
4YBL008WHIR16175
5YBL021CHAP36175
6YBL025WRRN106175
7YBR239CERT16175
\n", - "
" - ], - "text/plain": [ - " Locus Tag Symbol Records in Subset\n", - "0 YPL016W SWI1 18525\n", - "1 YEL009C GCN4 12350\n", - "2 YPL133C RDS2 12350\n", - "3 Z3EV Z3EV 12350\n", - "4 YBL008W HIR1 6175\n", - "5 YBL021C HAP3 6175\n", - "6 YBL025W RRN10 6175\n", - "7 YBR239C ERT1 6175" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Set filters for 15-minute timepoint with ZEV mechanism and P restriction\n", - "api.set_filter(\"hackett_2020\", time=15, mechanism=\"ZEV\", restriction=\"P\")\n", - "\n", - "# Create comprehensive filter analysis\n", - "print(\"Filter Analysis\")\n", - "print(\"=\" * 40)\n", - "\n", - "# Show current filter\n", - "current_filter = api.get_current_filter(\"hackett_2020\")\n", - "filter_info = pd.DataFrame({\n", - " 'Filter Component': ['Time Point', 'Mechanism', 'Restriction', 'SQL Filter'],\n", - " 'Value': ['15 minutes', 'ZEV', 'P (Restriction)', current_filter]\n", - "})\n", - "\n", - "print(\"\\nApplied Filters:\")\n", - "display(filter_info)\n", - "\n", - "# Analyze filter impact\n", - "filtered_metadata = metadata[\n", - " (metadata['time'] == 15) & \n", - " (metadata['mechanism'] == 'ZEV') & \n", - " (metadata['restriction'] == 'P')\n", - "]\n", - "\n", - "# Show top TFs in filtered data\n", - "filtered_tf_summary = filtered_metadata.groupby(['regulator_locus_tag', 'regulator_symbol'])['count'].sum().sort_values(ascending=False).head(8)\n", - "tf_filtered_df = pd.DataFrame({\n", - " 'Locus Tag': [idx[0] for idx in filtered_tf_summary.index],\n", - " 'Symbol': [idx[1] for idx in filtered_tf_summary.index],\n", - " 'Records in Subset': filtered_tf_summary.values,\n", - "})\n", - "\n", - "print(\"\\nTop Transcription Factors in Filtered Dataset:\")\n", - "display(tf_filtered_df)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Query Data with Automatic Filter Application\n", - "\n", - "Now when we query the data, our filters will be automatically applied:" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Setting Complex SQL Filters\n", - "\n", - "For more sophisticated filtering, we can use the `set_sql_filter()` method with full SQL expressions:" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Complex SQL Filter Analysis\n", - "==================================================\n", - "\n", - "Applied Complex Filter:\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Filter Component", - "rawType": "object", - "type": "string" - }, - { - "name": "Value", - "rawType": "object", - "type": "string" - } - ], - "ref": "1063532f-04e7-453f-9fac-2261490f4701", - "rows": [ - [ - "0", - "Time Points", - "15, 30 minutes" - ], - [ - "1", - "Mechanism", - "ZEV (overexpression)" - ], - [ - "2", - "Restriction", - "P (restriction enzyme)" - ], - [ - "3", - "Selected TFs", - "YER040W (GLN3), YER028C (RSF2), YPL016W (SWI1)" - ], - [ - "4", - "Complete SQL Filter", - "time IN (15, 30) AND \n mechanism = 'ZEV' AND \n restriction = 'P' AND \n regulator_locus_tag IN ('YER040W', 'YER028C', 'YPL016W')" - ] - ], - "shape": { - "columns": 2, - "rows": 5 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Filter ComponentValue
0Time Points15, 30 minutes
1MechanismZEV (overexpression)
2RestrictionP (restriction enzyme)
3Selected TFsYER040W (GLN3), YER028C (RSF2), YPL016W (SWI1)
4Complete SQL Filtertime IN (15, 30) AND \\n mechanism = 'ZEV' A...
\n", - "
" - ], - "text/plain": [ - " Filter Component Value\n", - "0 Time Points 15, 30 minutes\n", - "1 Mechanism ZEV (overexpression)\n", - "2 Restriction P (restriction enzyme)\n", - "3 Selected TFs YER040W (GLN3), YER028C (RSF2), YPL016W (SWI1)\n", - "4 Complete SQL Filter time IN (15, 30) AND \\n mechanism = 'ZEV' A..." - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Set a complex filter for multiple time points and specific transcription factors\n", - "api.set_sql_filter(\"hackett_2020\", \"\"\"\n", - " time IN (15, 30) AND \n", - " mechanism = 'ZEV' AND \n", - " restriction = 'P' AND \n", - " regulator_locus_tag IN ('YER040W', 'YER028C', 'YPL016W')\n", - "\"\"\")\n", - "\n", - "print(\"Complex SQL Filter Analysis\")\n", - "print(\"=\" * 50)\n", - "\n", - "# Show filter details\n", - "filter_details = pd.DataFrame({\n", - " 'Filter Component': [\n", - " 'Time Points',\n", - " 'Mechanism', \n", - " 'Restriction',\n", - " 'Selected TFs',\n", - " 'Complete SQL Filter'\n", - " ],\n", - " 'Value': [\n", - " '15, 30 minutes',\n", - " 'ZEV (overexpression)',\n", - " 'P (restriction enzyme)',\n", - " 'YER040W (GLN3), YER028C (RSF2), YPL016W (SWI1)',\n", - " api.get_current_filter('hackett_2020')\n", - " ]\n", - "})\n", - "\n", - "print(\"\\nApplied Complex Filter:\")\n", - "display(filter_details)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [], - "source": [ - "## Query with the complex filter" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Time Course Comparison for Selected TFs:\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Locus Tag", - "rawType": "object", - "type": "string" - }, - { - "name": "Symbol", - "rawType": "object", - "type": "string" - }, - { - "name": "Time (min)", - "rawType": "float64", - "type": "float" - }, - { - "name": "Target Count", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Avg Response", - "rawType": "float64", - "type": "float" - }, - { - "name": "Strong Responders", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Max |Response|", - "rawType": "float64", - "type": "float" - }, - { - "name": "% Strong", - "rawType": "float64", - "type": "float" - } - ], - "ref": "fbfefef3-454e-4318-99ae-3d43d79d3800", - "rows": [ - [ - "0", - "YER028C", - "MIG3", - "15.0", - "6175", - "-0.01", - "99", - "5.894", - "1.6" - ], - [ - "1", - "YER028C", - "MIG3", - "30.0", - "6175", - "-0.028", - "246", - "5.516", - "4.0" - ], - [ - "2", - "YER040W", - "GLN3", - "15.0", - "6175", - "0.018", - "81", - "7.923", - "1.3" - ], - [ - "3", - "YER040W", - "GLN3", - "30.0", - "6175", - "0.042", - "631", - "10.459", - "10.2" - ], - [ - "4", - "YPL016W", - "SWI1", - "15.0", - "18525", - "0.001", - "431", - "6.216", - "2.3" - ], - [ - "5", - "YPL016W", - "SWI1", - "30.0", - "18525", - "0.033", - "762", - "6.753", - "4.1" - ] - ], - "shape": { - "columns": 8, - "rows": 6 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Locus TagSymbolTime (min)Target CountAvg ResponseStrong RespondersMax |Response|% Strong
0YER028CMIG315.06175-0.010995.8941.6
1YER028CMIG330.06175-0.0282465.5164.0
2YER040WGLN315.061750.018817.9231.3
3YER040WGLN330.061750.04263110.45910.2
4YPL016WSWI115.0185250.0014316.2162.3
5YPL016WSWI130.0185250.0337626.7534.1
\n", - "
" - ], - "text/plain": [ - " Locus Tag Symbol Time (min) Target Count Avg Response Strong Responders \\\n", - "0 YER028C MIG3 15.0 6175 -0.010 99 \n", - "1 YER028C MIG3 30.0 6175 -0.028 246 \n", - "2 YER040W GLN3 15.0 6175 0.018 81 \n", - "3 YER040W GLN3 30.0 6175 0.042 631 \n", - "4 YPL016W SWI1 15.0 18525 0.001 431 \n", - "5 YPL016W SWI1 30.0 18525 0.033 762 \n", - "\n", - " Max |Response| % Strong \n", - "0 5.894 1.6 \n", - "1 5.516 4.0 \n", - "2 7.923 1.3 \n", - "3 10.459 10.2 \n", - "4 6.216 2.3 \n", - "5 6.753 4.1 " - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Overall TF Performance Summary:\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Locus Tag", - "rawType": "object", - "type": "string" - }, - { - "name": "Symbol", - "rawType": "object", - "type": "string" - }, - { - "name": "Total Targets", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Total Strong", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Avg Response", - "rawType": "float64", - "type": "float" - }, - { - "name": "% Strong Overall", - "rawType": "float64", - "type": "float" - } - ], - "ref": "ca80d3c1-4fec-4613-9f73-12ba5359ed44", - "rows": [ - [ - "0", - "YER028C", - "MIG3", - "12350", - "345", - "-0.019", - "2.8" - ], - [ - "1", - "YER040W", - "GLN3", - "12350", - "712", - "0.03", - "5.8" - ], - [ - "2", - "YPL016W", - "SWI1", - "37050", - "1193", - "0.017", - "3.2" - ] - ], - "shape": { - "columns": 6, - "rows": 3 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Locus TagSymbolTotal TargetsTotal StrongAvg Response% Strong Overall
0YER028CMIG312350345-0.0192.8
1YER040WGLN3123507120.0305.8
2YPL016WSWI13705011930.0173.2
\n", - "
" - ], - "text/plain": [ - " Locus Tag Symbol Total Targets Total Strong Avg Response \\\n", - "0 YER028C MIG3 12350 345 -0.019 \n", - "1 YER040W GLN3 12350 712 0.030 \n", - "2 YPL016W SWI1 37050 1193 0.017 \n", - "\n", - " % Strong Overall \n", - "0 2.8 \n", - "1 5.8 \n", - "2 3.2 " - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "\n", - "time_comparison = api.query(\"\"\"\n", - " SELECT \n", - " regulator_locus_tag,\n", - " regulator_symbol,\n", - " time,\n", - " COUNT(*) as target_count,\n", - " ROUND(AVG(log2_shrunken_timecourses), 3) as avg_response,\n", - " COUNT(CASE WHEN ABS(log2_shrunken_timecourses) > 0.5 THEN 1 END) as strong_responders,\n", - " ROUND(MAX(ABS(log2_shrunken_timecourses)), 3) as max_abs_response\n", - " FROM hackett_2020 \n", - " GROUP BY regulator_locus_tag, regulator_symbol, time\n", - " ORDER BY regulator_locus_tag, time\n", - "\"\"\", \"hackett_2020\")\n", - "\n", - "# Format display\n", - "time_comparison_display = time_comparison.copy()\n", - "time_comparison_display.columns = ['Locus Tag', 'Symbol', 'Time (min)', 'Target Count', 'Avg Response', 'Strong Responders', 'Max |Response|']\n", - "time_comparison_display['% Strong'] = (time_comparison_display['Strong Responders'] / time_comparison_display['Target Count'] * 100).round(1)\n", - "\n", - "print(\"\\nTime Course Comparison for Selected TFs:\")\n", - "display(time_comparison_display)\n", - "\n", - "# Summary analysis\n", - "tf_summary = time_comparison.groupby(['regulator_locus_tag', 'regulator_symbol']).agg({\n", - " 'target_count': 'sum',\n", - " 'strong_responders': 'sum',\n", - " 'avg_response': 'mean'\n", - "}).reset_index()\n", - "\n", - "tf_summary['total_%_strong'] = (tf_summary['strong_responders'] / tf_summary['target_count'] * 100).round(1)\n", - "tf_summary_display = tf_summary.copy()\n", - "tf_summary_display.columns = ['Locus Tag', 'Symbol', 'Total Targets', 'Total Strong', 'Avg Response', '% Strong Overall']\n", - "\n", - "print(\"\\nOverall TF Performance Summary:\")\n", - "display(tf_summary_display)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Filter Management\n", - "\n", - "The filtering system provides full control over active filters:" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "🔧 Filter Management Demonstration\n", - "==================================================\n", - "\n", - "Current filter:\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Status", - "rawType": "object", - "type": "string" - }, - { - "name": "SQL WHERE clause", - "rawType": "object", - "type": "string" - } - ], - "ref": "e93f9433-b31e-49db-b98d-5c5b4116e1d6", - "rows": [ - [ - "0", - "Active Filter", - "time IN (15, 30) AND \n mechanism = 'ZEV' AND \n restriction = 'P' AND \n regulator_locus_tag IN ('YER040W', 'YER028C', 'YPL016W')" - ] - ], - "shape": { - "columns": 2, - "rows": 1 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
StatusSQL WHERE clause
0Active Filtertime IN (15, 30) AND \\n mechanism = 'ZEV' A...
\n", - "
" - ], - "text/plain": [ - " Status SQL WHERE clause\n", - "0 Active Filter time IN (15, 30) AND \\n mechanism = 'ZEV' A..." - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "After clearing filters:\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Status", - "rawType": "object", - "type": "string" - }, - { - "name": "SQL WHERE clause", - "rawType": "object", - "type": "string" - } - ], - "ref": "7ca71950-6a31-404e-baa4-d096e56c4991", - "rows": [ - [ - "0", - "After Clearing", - "None" - ] - ], - "shape": { - "columns": 2, - "rows": 1 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
StatusSQL WHERE clause
0After ClearingNone
\n", - "
" - ], - "text/plain": [ - " Status SQL WHERE clause\n", - "0 After Clearing None" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "📊 Dataset Size Comparison:\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Dataset State", - "rawType": "object", - "type": "string" - }, - { - "name": "Total Records", - "rawType": "object", - "type": "string" - }, - { - "name": "Percentage", - "rawType": "object", - "type": "string" - } - ], - "ref": "d30810e1-4013-4929-a14a-522ee08c48fe", - "rows": [ - [ - "0", - "Unfiltered (Full Dataset)", - "10,454,275", - "100.0%" - ], - [ - "1", - "Filtered (time=15, ZEV, P)", - "1,012,700", - "9.7%" - ], - [ - "2", - "Data Reduction", - "9,441,575", - "90.3%" - ] - ], - "shape": { - "columns": 3, - "rows": 3 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Dataset StateTotal RecordsPercentage
0Unfiltered (Full Dataset)10,454,275100.0%
1Filtered (time=15, ZEV, P)1,012,7009.7%
2Data Reduction9,441,57590.3%
\n", - "
" - ], - "text/plain": [ - " Dataset State Total Records Percentage\n", - "0 Unfiltered (Full Dataset) 10,454,275 100.0%\n", - "1 Filtered (time=15, ZEV, P) 1,012,700 9.7%\n", - "2 Data Reduction 9,441,575 90.3%" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Complete HfQueryAPI Workflow:\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "Step", - "rawType": "object", - "type": "string" - }, - { - "name": "Method", - "rawType": "object", - "type": "string" - }, - { - "name": "Purpose", - "rawType": "object", - "type": "string" - } - ], - "ref": "f8e9e25a-c9d1-479d-b664-f70bac590d68", - "rows": [ - [ - "0", - "1. Explore Metadata", - "api.get_metadata()", - "Understand dataset structure" - ], - [ - "1", - "2. Set Simple Filters", - "api.set_filter(config, **kwargs)", - "Filter by metadata values" - ], - [ - "2", - "3. Set Complex SQL Filters", - "api.set_sql_filter(config, sql)", - "Complex multi-condition filtering" - ], - [ - "3", - "4. Query with Auto-Apply", - "api.query(sql, config)", - "Analyze with automatic filtering" - ], - [ - "4", - "5. Clear/Manage Filters", - "api.clear_filter() / get_current_filter()", - "Reset or inspect current state" - ] - ], - "shape": { - "columns": 3, - "rows": 5 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
StepMethodPurpose
01. Explore Metadataapi.get_metadata()Understand dataset structure
12. Set Simple Filtersapi.set_filter(config, **kwargs)Filter by metadata values
23. Set Complex SQL Filtersapi.set_sql_filter(config, sql)Complex multi-condition filtering
34. Query with Auto-Applyapi.query(sql, config)Analyze with automatic filtering
45. Clear/Manage Filtersapi.clear_filter() / get_current_filter()Reset or inspect current state
\n", - "
" - ], - "text/plain": [ - " Step Method \\\n", - "0 1. Explore Metadata api.get_metadata() \n", - "1 2. Set Simple Filters api.set_filter(config, **kwargs) \n", - "2 3. Set Complex SQL Filters api.set_sql_filter(config, sql) \n", - "3 4. Query with Auto-Apply api.query(sql, config) \n", - "4 5. Clear/Manage Filters api.clear_filter() / get_current_filter() \n", - "\n", - " Purpose \n", - "0 Understand dataset structure \n", - "1 Filter by metadata values \n", - "2 Complex multi-condition filtering \n", - "3 Analyze with automatic filtering \n", - "4 Reset or inspect current state " - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Demonstrate filter management capabilities\n", - "print(\"🔧 Filter Management Demonstration\")\n", - "print(\"=\" * 50)\n", - "\n", - "# Show current filter\n", - "current_filter = api.get_current_filter('hackett_2020')\n", - "print(f\"\\nCurrent filter:\")\n", - "current_filter_df = pd.DataFrame({\n", - " 'Status': ['Active Filter'],\n", - " 'SQL WHERE clause': [current_filter if current_filter else 'None']\n", - "})\n", - "display(current_filter_df)\n", - "\n", - "# Clear all filters and show impact\n", - "api.clear_filter(\"hackett_2020\")\n", - "print(f\"\\nAfter clearing filters:\")\n", - "cleared_filter_df = pd.DataFrame({\n", - " 'Status': ['After Clearing'],\n", - " 'SQL WHERE clause': [api.get_current_filter('hackett_2020') or 'None']\n", - "})\n", - "display(cleared_filter_df)\n", - "\n", - "# Query unfiltered vs filtered data comparison\n", - "total_records = api.query(\"SELECT COUNT(*) as total FROM hackett_2020\", \"hackett_2020\")\n", - "\n", - "# Set filters again for comparison\n", - "api.set_filter(\"hackett_2020\", time=15, mechanism=\"ZEV\", restriction=\"P\")\n", - "filtered_records = api.query(\"SELECT COUNT(*) as total FROM hackett_2020\", \"hackett_2020\")\n", - "\n", - "# Create comprehensive comparison table\n", - "comparison_results = pd.DataFrame({\n", - " 'Dataset State': [\n", - " 'Unfiltered (Full Dataset)',\n", - " 'Filtered (time=15, ZEV, P)',\n", - " 'Data Reduction'\n", - " ],\n", - " 'Total Records': [\n", - " f\"{total_records.iloc[0]['total']:,}\",\n", - " f\"{filtered_records.iloc[0]['total']:,}\",\n", - " f\"{total_records.iloc[0]['total'] - filtered_records.iloc[0]['total']:,}\"\n", - " ],\n", - " 'Percentage': [\n", - " '100.0%',\n", - " f\"{(filtered_records.iloc[0]['total'] / total_records.iloc[0]['total'] * 100):.1f}%\",\n", - " f\"{((total_records.iloc[0]['total'] - filtered_records.iloc[0]['total']) / total_records.iloc[0]['total'] * 100):.1f}%\"\n", - " ]\n", - "})\n", - "\n", - "print(\"\\n📊 Dataset Size Comparison:\")\n", - "display(comparison_results)\n", - "\n", - "# Show filter workflow summary\n", - "workflow_summary = pd.DataFrame({\n", - " 'Step': [\n", - " '1. Explore Metadata',\n", - " '2. Set Simple Filters', \n", - " '3. Set Complex SQL Filters',\n", - " '4. Query with Auto-Apply',\n", - " '5. Clear/Manage Filters'\n", - " ],\n", - " 'Method': [\n", - " 'api.get_metadata()',\n", - " 'api.set_filter(config, **kwargs)',\n", - " 'api.set_sql_filter(config, sql)',\n", - " 'api.query(sql, config)',\n", - " 'api.clear_filter() / get_current_filter()'\n", - " ],\n", - " 'Purpose': [\n", - " 'Understand dataset structure',\n", - " 'Filter by metadata values',\n", - " 'Complex multi-condition filtering',\n", - " 'Analyze with automatic filtering',\n", - " 'Reset or inspect current state'\n", - " ]\n", - "})\n", - "\n", - "print(\"\\nComplete HfQueryAPI Workflow:\")\n", - "display(workflow_summary)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "tfbpapi-py3.11", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.9" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/tutorials/metadata_builder_tutorial.ipynb b/docs/tutorials/metadata_builder_tutorial.ipynb deleted file mode 100644 index 774f413..0000000 --- a/docs/tutorials/metadata_builder_tutorial.ipynb +++ /dev/null @@ -1,938 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# MetadataBuilder Tutorial\n", - "\n", - "This tutorial demonstrates how to use the `MetadataBuilder` to create standardized metadata tables across heterogeneous datasets using optional alias mappings.\n", - "\n", - "## Overview\n", - "\n", - "The `MetadataBuilder` solves a common problem when working with multiple genomics datasets: **factor levels are named inconsistently across datasets**. For example, carbon sources might be labeled as \"D-glucose\", \"dextrose\", \"glucose\", etc.\n", - "\n", - "**Key difference from filtering**: This system does NOT filter or exclude any data. Instead, it normalizes factor level names to create standardized metadata views. ALL data is included, just with standardized names.\n", - "\n", - "You specify:\n", - "\n", - "1. **Factor aliases** (optional): Mappings to normalize factor level names (e.g., `glucose: [\"D-glucose\", \"dextrose\"]`)\n", - "2. **Property mappings**: Where to find each property in each dataset (e.g., repo-level vs field-level)\n", - "\n", - "## Key Features\n", - "\n", - "- **No filtering**: ALL data is included with normalized names\n", - "- **Optional aliases**: If no alias is configured, original values pass through unchanged\n", - "- **Case-insensitive matching**: \"D-glucose\" matches \"d-glucose\"\n", - "- **Three output modes**:\n", - " - `conditions`: Just normalized metadata (no data retrieval)\n", - " - `samples`: Sample-level metadata (one row per sample_id)\n", - " - `full_data`: All measurements with normalized metadata\n", - "- **External configuration**: No hardcoded dataset logic in your analysis code" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Setup" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chase/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", - " from .autonotebook import tqdm as notebook_tqdm\n" - ] - } - ], - "source": [ - "from pathlib import Path\n", - "import yaml\n", - "import pandas as pd\n", - "from tfbpapi.datainfo.metadata_builder import MetadataBuilder, normalize_value\n", - "from tfbpapi.datainfo import DataCard" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Understanding Dataset Structures\n", - "\n", - "Before normalizing, let's examine how experimental conditions are structured in two representative datasets.\n", - "\n", - "### Repo-Level Conditions: Kemmeren 2014\n", - "\n", - "All samples in this dataset share the same experimental conditions defined at the repository level." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Kemmeren 2014 - Repo-Level Experimental Conditions\n", - "============================================================\n", - "Temperature: 30°C\n", - "Media: synthetic_complete\n", - "Carbon source: ['D-glucose']\n", - "\n", - "All samples in this dataset share these conditions.\n" - ] - } - ], - "source": [ - "# Load Kemmeren 2014 datacard\n", - "kemmeren_card = DataCard(\"BrentLab/kemmeren_2014\")\n", - "\n", - "# Get repo-level experimental conditions\n", - "exp_conds = kemmeren_card.get_experimental_conditions(\"kemmeren_2014\")\n", - "\n", - "print(\"Kemmeren 2014 - Repo-Level Experimental Conditions\")\n", - "print(\"=\" * 60)\n", - "print(f\"Temperature: {exp_conds.get('temperature_celsius')}°C\")\n", - "print(f\"Media: {exp_conds.get('media', {}).get('name')}\")\n", - "\n", - "carbon_source = exp_conds.get('media', {}).get('carbon_source', [])\n", - "if carbon_source:\n", - " compounds = [cs.get('compound') for cs in carbon_source]\n", - " print(f\"Carbon source: {compounds}\")\n", - "\n", - "print(\"\\nAll samples in this dataset share these conditions.\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Field-Level Conditions: Harbison 2004\n", - "\n", - "This dataset has different experimental conditions for different samples, defined in the `condition` field's definitions." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Field: condition\n", - "Levels: 14\n", - "\n", - "YPD:\n", - " media.name: YPD\n", - " media.carbon_source: [{'compound': 'D-glucose', 'concentration_percent': 2}]\n", - "\n", - "SM:\n", - " media.name: synthetic_complete\n", - " media.carbon_source: None\n", - "\n", - "RAPA:\n", - " media.name: YPD\n", - " media.carbon_source: [{'compound': 'D-glucose', 'concentration_percent': 2}]\n", - "\n", - "H2O2Hi:\n", - " media.name: YPD\n", - " media.carbon_source: [{'compound': 'D-glucose', 'concentration_percent': 2}]\n", - "\n", - "H2O2Lo:\n", - " media.name: YPD\n", - " media.carbon_source: [{'compound': 'D-glucose', 'concentration_percent': 2}]\n", - "\n", - "Acid:\n", - " media.name: YPD\n", - " media.carbon_source: [{'compound': 'D-glucose', 'concentration_percent': 2}]\n", - "\n", - "... and 8 more levels\n" - ] - } - ], - "source": [ - "# Load Harbison 2004 datacard\n", - "harbison_card = DataCard(\"BrentLab/harbison_2004\")\n", - "\n", - "# Get a summary of condition field levels\n", - "print(harbison_card.summarize_field_levels(\n", - " \"harbison_2004\",\n", - " \"condition\",\n", - " properties=[\"media.name\", \"media.carbon_source\"],\n", - " max_levels=6\n", - "))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Example 8: Extracting Properties from Field Definitions\n", - "\n", - "The MetadataBuilder can extract properties from field definitions and add them as new columns. This is useful for creating analysis-ready metadata tables.\n", - "\n", - "For harbison_2004, the base metadata has `condition` as a field, but we want to extract:\n", - "- `growth_media` from `media.name`\n", - "- `carbon_source` from `media.carbon_source.compound`\n", - "\n", - "And normalize carbon source names using aliases." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Fetching 1 files: 100%|██████████| 1/1 [00:00<00:00, 2.88it/s]\n" - ] - }, - { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, - { - "name": "sample_id", - "rawType": "int32", - "type": "integer" - }, - { - "name": "regulator_locus_tag", - "rawType": "object", - "type": "string" - }, - { - "name": "regulator_symbol", - "rawType": "object", - "type": "string" - }, - { - "name": "condition", - "rawType": "object", - "type": "string" - }, - { - "name": "carbon_source", - "rawType": "object", - "type": "unknown" - }, - { - "name": "growth_media", - "rawType": "object", - "type": "string" - } - ], - "ref": "95a0f05b-86e0-4ac9-bda0-b91b5f5e4de5", - "rows": [ - [ - "0", - "1", - "YSC0017", - "MATA1", - "YPD", - "glucose", - "YPD" - ], - [ - "1", - "2", - "YAL051W", - "OAF1", - "YPD", - "glucose", - "YPD" - ], - [ - "2", - "3", - "YBL005W", - "PDR3", - "YPD", - "glucose", - "YPD" - ], - [ - "3", - "4", - "YBL008W", - "HIR1", - "YPD", - "glucose", - "YPD" - ], - [ - "4", - "5", - "YBL021C", - "HAP3", - "YPD", - "glucose", - "YPD" - ], - [ - "5", - "6", - "YBL054W", - "TOD6", - "YPD", - "glucose", - "YPD" - ], - [ - "6", - "7", - "YBL103C", - "RTG3", - "H2O2Hi", - "glucose", - "YPD" - ], - [ - "7", - "8", - "YBL103C", - "RTG3", - "H2O2Lo", - "glucose", - "YPD" - ], - [ - "8", - "9", - "YBL103C", - "RTG3", - "RAPA", - "glucose", - "YPD" - ], - [ - "9", - "10", - "YBL103C", - "RTG3", - "SM", - null, - "synthetic_complete" - ], - [ - "10", - "11", - "YBL103C", - "RTG3", - "YPD", - "glucose", - "YPD" - ], - [ - "11", - "12", - "YBR033W", - "EDS1", - "YPD", - "glucose", - "YPD" - ], - [ - "12", - "13", - "YBR049C", - "REB1", - "H2O2Hi", - "glucose", - "YPD" - ], - [ - "13", - "14", - "YBR049C", - "REB1", - "H2O2Lo", - "glucose", - "YPD" - ], - [ - "14", - "15", - "YBR049C", - "REB1", - "YPD", - "glucose", - "YPD" - ], - [ - "15", - "16", - "YBR083W", - "TEC1", - "Alpha", - "glucose", - "YPD" - ], - [ - "16", - "17", - "YBR083W", - "TEC1", - "BUT14", - "glucose", - "YPD" - ], - [ - "17", - "18", - "YBR083W", - "TEC1", - "YPD", - "glucose", - "YPD" - ], - [ - "18", - "19", - "YBR150C", - "TBS1", - "YPD", - "glucose", - "YPD" - ], - [ - "19", - "20", - "YBR182C", - "SMP1", - "YPD", - "glucose", - "YPD" - ], - [ - "20", - "21", - "YBR239C", - "ERT1", - "YPD", - "glucose", - "YPD" - ], - [ - "21", - "22", - "YBR240C", - "THI2", - "Thi-", - null, - "synthetic_complete_minus_thiamine" - ], - [ - "22", - "23", - "YBR240C", - "THI2", - "YPD", - "glucose", - "YPD" - ], - [ - "23", - "24", - "YBR267W", - "REI1", - "YPD", - "glucose", - "YPD" - ], - [ - "24", - "25", - "YBR297W", - "MAL33", - "H2O2Hi", - "glucose", - "YPD" - ], - [ - "25", - "26", - "YBR297W", - "MAL33", - "H2O2Lo", - "glucose", - "YPD" - ], - [ - "26", - "27", - "YBR297W", - "MAL33", - "YPD", - "glucose", - "YPD" - ], - [ - "27", - "28", - "YCR018C", - "SRD1", - "YPD", - "glucose", - "YPD" - ], - [ - "28", - "29", - "YCR106W", - "RDS1", - "H2O2Hi", - "glucose", - "YPD" - ], - [ - "29", - "30", - "YCR106W", - "RDS1", - "YPD", - "glucose", - "YPD" - ], - [ - "30", - "31", - "YDL020C", - "RPN4", - "H2O2Hi", - "glucose", - "YPD" - ], - [ - "31", - "32", - "YDL020C", - "RPN4", - "H2O2Lo", - "glucose", - "YPD" - ], - [ - "32", - "33", - "YDL020C", - "RPN4", - "YPD", - "glucose", - "YPD" - ], - [ - "33", - "34", - "YDL048C", - "STP4", - "YPD", - "glucose", - "YPD" - ], - [ - "34", - "35", - "YDL056W", - "MBP1", - "H2O2Hi", - "glucose", - "YPD" - ], - [ - "35", - "36", - "YDL056W", - "MBP1", - "H2O2Lo", - "glucose", - "YPD" - ], - [ - "36", - "37", - "YDL056W", - "MBP1", - "YPD", - "glucose", - "YPD" - ], - [ - "37", - "38", - "YDL106C", - "PHO2", - "H2O2Hi", - "glucose", - "YPD" - ], - [ - "38", - "39", - "YDL106C", - "PHO2", - "H2O2Lo", - "glucose", - "YPD" - ], - [ - "39", - "40", - "YDL106C", - "PHO2", - "Pi-", - null, - "synthetic_complete_minus_phosphate" - ], - [ - "40", - "41", - "YDL106C", - "PHO2", - "SM", - null, - "synthetic_complete" - ], - [ - "41", - "42", - "YDL106C", - "PHO2", - "YPD", - "glucose", - "YPD" - ], - [ - "42", - "43", - "YDL166C", - "FAP7", - "YPD", - "glucose", - "YPD" - ], - [ - "43", - "44", - "YDL170W", - "UGA3", - "RAPA", - "glucose", - "YPD" - ], - [ - "44", - "45", - "YDL170W", - "UGA3", - "SM", - null, - "synthetic_complete" - ], - [ - "45", - "46", - "YDL170W", - "UGA3", - "YPD", - "glucose", - "YPD" - ], - [ - "46", - "47", - "YDR009W", - "GAL3", - "YPD", - "glucose", - "YPD" - ], - [ - "47", - "48", - "YDR026C", - "NSI1", - "YPD", - "glucose", - "YPD" - ], - [ - "48", - "49", - "YDR043C", - "NRG1", - "H2O2Hi", - "glucose", - "YPD" - ], - [ - "49", - "50", - "YDR043C", - "NRG1", - "H2O2Lo", - "glucose", - "YPD" - ] - ], - "shape": { - "columns": 6, - "rows": 352 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sample_idregulator_locus_tagregulator_symbolconditioncarbon_sourcegrowth_media
01YSC0017MATA1YPDglucoseYPD
12YAL051WOAF1YPDglucoseYPD
23YBL005WPDR3YPDglucoseYPD
34YBL008WHIR1YPDglucoseYPD
45YBL021CHAP3YPDglucoseYPD
.....................
347348YPR104CFHL1YPDglucoseYPD
348349YPR196WYPR196WYPDglucoseYPD
349350YPR199CARR1H2O2HiglucoseYPD
350351YPR199CARR1YPDglucoseYPD
351352YKL043WPHD1BUT14glucoseYPD
\n", - "

352 rows × 6 columns

\n", - "
" - ], - "text/plain": [ - " sample_id regulator_locus_tag regulator_symbol condition carbon_source \\\n", - "0 1 YSC0017 MATA1 YPD glucose \n", - "1 2 YAL051W OAF1 YPD glucose \n", - "2 3 YBL005W PDR3 YPD glucose \n", - "3 4 YBL008W HIR1 YPD glucose \n", - "4 5 YBL021C HAP3 YPD glucose \n", - ".. ... ... ... ... ... \n", - "347 348 YPR104C FHL1 YPD glucose \n", - "348 349 YPR196W YPR196W YPD glucose \n", - "349 350 YPR199C ARR1 H2O2Hi glucose \n", - "350 351 YPR199C ARR1 YPD glucose \n", - "351 352 YKL043W PHD1 BUT14 glucose \n", - "\n", - " growth_media \n", - "0 YPD \n", - "1 YPD \n", - "2 YPD \n", - "3 YPD \n", - "4 YPD \n", - ".. ... \n", - "347 YPD \n", - "348 YPD \n", - "349 YPD \n", - "350 YPD \n", - "351 YPD \n", - "\n", - "[352 rows x 6 columns]" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Configuration with property extraction and aliases\n", - "extraction_config = {\n", - " \"factor_aliases\": {\n", - " \"carbon_source\": {\n", - " \"glucose\": [\"D-glucose\", \"Glucose\", \"dextrose\", \"Dextrose\"],\n", - " \"galactose\": [\"D-galactose\", \"Galactose\"],\n", - " \"raffinose\": [\"D-raffinose\", \"Raffinose\"]\n", - " }\n", - " },\n", - " \"BrentLab/harbison_2004\": {\n", - " \"dataset\": {\n", - " \"harbison_2004\": {\n", - " \"growth_media\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"media.name\"\n", - " },\n", - " \"carbon_source\": {\n", - " \"field\": \"condition\",\n", - " \"path\": \"media.carbon_source.compound\"\n", - " }\n", - " }\n", - " }\n", - " }\n", - "}\n", - "\n", - "config_path_extraction = Path(\"/tmp/metadata_extraction.yaml\")\n", - "with open(config_path_extraction, 'w') as f:\n", - " yaml.dump(extraction_config, f)\n", - "\n", - "# Build metadata with extraction\n", - "builder_extraction = MetadataBuilder(config_path_extraction)\n", - "results_extraction = builder_extraction.build_metadata(\n", - " repos=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", - " mode=\"samples\"\n", - ")\n", - "\n", - "df_extracted = results_extraction[\"BrentLab/harbison_2004\"][\"data\"]\n", - "\n", - "df_extracted\n", - "\n", - "# print(f\"Columns: {list(df_extracted.columns)}\")\n", - "# print(f\"\\nShape: {df_extracted.shape}\")\n", - "# print(\"\\nSample data showing base + extracted columns:\")\n", - "# print(df_extracted[['sample_id', 'condition', 'growth_media', 'carbon_source']].head(10))" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "tfbpapi-py3.11", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.9" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/tutorials/metadata_manager_tutorial.ipynb b/docs/tutorials/metadata_manager_tutorial.ipynb deleted file mode 100644 index 891df65..0000000 --- a/docs/tutorials/metadata_manager_tutorial.ipynb +++ /dev/null @@ -1,586 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# MetadataManager Tutorial: Enriching Data with Condition Metadata\n", - "\n", - "This tutorial demonstrates how to create enriched metadata views by combining:\n", - "1. Actual data fields from HuggingFace datasets (via HfQueryAPI)\n", - "2. Condition metadata from DataCard definitions\n", - "\n", - "We'll use the **BrentLab/harbison_2004** ChIP-chip dataset, which has 14 experimental conditions with detailed metadata about media composition, temperature, and other environmental factors.\n", - "\n", - "## Why Enrich Metadata?\n", - "\n", - "The raw data contains a `condition` field with values like \"YPD\", \"HEAT\", \"GAL\". But to analyze data by media type or carbon source, we need to:\n", - "- Extract nested information from condition definitions\n", - "- Create queryable columns for filtering and grouping\n", - "- Join this enriched metadata with the actual measurements\n", - "\n", - "This tutorial teaches you how to:\n", - "1. Use DataCard to explore nested condition metadata\n", - "2. Manually construct SQL CASE expressions to enrich your data\n", - "3. Create metadata views that combine data with DataCard information" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 1. Setup and Imports" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [], - "source": [ - "from tfbpapi.datainfo import DataCard\n", - "from tfbpapi import HfQueryAPI\n", - "import duckdb\n", - "import pandas as pd\n", - "import json\n", - "\n", - "pd.set_option('display.max_columns', None)\n", - "pd.set_option('display.width', None)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2. Explore Condition Metadata with DataCard\n", - "\n", - "First, let's load the DataCard and explore what condition metadata is available." - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Dataset type: DatasetType.ANNOTATED_FEATURES\n", - "Number of features: 7\n", - "\n", - "Feature names:\n", - " - condition ({'class_label': {'names': ['YPD', 'SM', 'RAPA', 'H2O2Hi', 'H2O2Lo', 'Acid', 'Alpha', 'BUT14', 'BUT90', 'Thi-', 'GAL', 'HEAT', 'Pi-', 'RAFF']}})\n", - " - regulator_locus_tag (string)\n", - " - regulator_symbol (string)\n", - " - target_locus_tag (string)\n", - " - target_symbol (string)\n", - " - effect (float64)\n", - " - pvalue (float64)\n" - ] - } - ], - "source": [ - "# Load DataCard\n", - "card = DataCard('BrentLab/harbison_2004')\n", - "\n", - "# Get the config to understand the data structure\n", - "config = card.get_config('harbison_2004')\n", - "print(f\"Dataset type: {config.dataset_type}\")\n", - "print(f\"Number of features: {len(config.dataset_info.features)}\")\n", - "print(f\"\\nFeature names:\")\n", - "for feature in config.dataset_info.features:\n", - " print(f\" - {feature.name} ({feature.dtype})\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Extract Media Specifications Using get_field_attribute()\n", - "\n", - "The new `get_field_attribute()` method makes it easy to extract nested specifications from field definitions. Let's use it to explore the media specifications for each condition." - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [ - { - "ename": "AttributeError", - "evalue": "'DataCard' object has no attribute 'get_field_attribute'", - "output_type": "error", - "traceback": [ - "\u001b[31m---------------------------------------------------------------------------\u001b[39m", - "\u001b[31mAttributeError\u001b[39m Traceback (most recent call last)", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[34]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;66;03m# Extract media specifications for all conditions\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m media_specs = \u001b[43mcard\u001b[49m\u001b[43m.\u001b[49m\u001b[43mget_field_attribute\u001b[49m(\u001b[33m'\u001b[39m\u001b[33mharbison_2004\u001b[39m\u001b[33m'\u001b[39m, \u001b[33m'\u001b[39m\u001b[33mcondition\u001b[39m\u001b[33m'\u001b[39m, \u001b[33m'\u001b[39m\u001b[33mmedia\u001b[39m\u001b[33m'\u001b[39m)\n\u001b[32m 4\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mFound media specifications for \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mlen\u001b[39m(media_specs)\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m conditions\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m\"\u001b[39m)\n\u001b[32m 6\u001b[39m \u001b[38;5;66;03m# Display them in a readable format\u001b[39;00m\n", - "\u001b[31mAttributeError\u001b[39m: 'DataCard' object has no attribute 'get_field_attribute'" - ] - } - ], - "source": [ - "# Extract media specifications for all conditions\n", - "media_specs = card.get_field_attribute('harbison_2004', 'condition', 'media')\n", - "\n", - "print(f\"Found media specifications for {len(media_specs)} conditions\\n\")\n", - "\n", - "# Display them in a readable format\n", - "for condition in sorted(media_specs.keys()):\n", - " print(f\"\\n{condition}:\")\n", - " print(json.dumps(media_specs[condition], indent=2))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Understanding the Nested Structure\n", - "\n", - "Notice that each media specification contains:\n", - "- `name`: The media name (e.g., \"YPD\", \"synthetic_complete\")\n", - "- `carbon_source`: Either a list of dicts with compound/concentration, or \"unspecified\"\n", - "- `nitrogen_source`: Either a list of dicts with compound/concentration, or \"unspecified\"\n", - "- Sometimes `additives`: Additional compounds like butanol\n", - "\n", - "To create queryable metadata columns, we need to flatten these nested structures." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 3. Load Data with HfQueryAPI\n", - "\n", - "Now let's load the actual data using HfQueryAPI. We'll create a shared DuckDB connection that we can use for building our enriched metadata view." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Create shared DuckDB connection\n", - "conn = duckdb.connect(':memory:')\n", - "\n", - "# Initialize HfQueryAPI with shared connection\n", - "api = HfQueryAPI('BrentLab/harbison_2004', duckdb_conn=conn)\n", - "\n", - "# Load the harbison_2004 config data\n", - "api.load_config('harbison_2004')\n", - "\n", - "print(\"Data loaded successfully!\")\n", - "print(\"\\nAvailable views in DuckDB:\")\n", - "views = conn.execute(\"SHOW TABLES\").fetchall()\n", - "for view in views:\n", - " print(f\" - {view[0]}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Examine the Base Data\n", - "\n", - "Let's look at the structure of the base data to see what fields are available." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Query the base view\n", - "base_df = conn.execute(\"\"\"\n", - " SELECT *\n", - " FROM harbison_2004_train\n", - " LIMIT 5\n", - "\"\"\").df()\n", - "\n", - "print(f\"Base data shape: {base_df.shape}\")\n", - "print(f\"\\nColumn names: {list(base_df.columns)}\")\n", - "print(\"\\nSample rows:\")\n", - "display(base_df)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Check unique conditions in the data\n", - "conditions_df = conn.execute(\"\"\"\n", - " SELECT DISTINCT condition\n", - " FROM harbison_2004_train\n", - " ORDER BY condition\n", - "\"\"\").df()\n", - "\n", - "print(f\"Unique conditions in data: {len(conditions_df)}\")\n", - "print(conditions_df['condition'].tolist())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 4. Flatten Media Specifications\n", - "\n", - "Before we can build SQL CASE expressions, we need to flatten the nested media specifications into simple strings. We'll create helper functions to extract:\n", - "- Media name\n", - "- Carbon source (as comma-separated compound names)\n", - "- Nitrogen source (as comma-separated compound names)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def flatten_carbon_source(media_spec):\n", - " \"\"\"Extract carbon source as comma-separated string.\"\"\"\n", - " if media_spec == 'unspecified':\n", - " return 'unspecified'\n", - " \n", - " carbon = media_spec.get('carbon_source', 'unspecified')\n", - " if carbon == 'unspecified':\n", - " return 'unspecified'\n", - " \n", - " if isinstance(carbon, list):\n", - " compounds = [c['compound'] for c in carbon]\n", - " return ', '.join(compounds)\n", - " \n", - " return str(carbon)\n", - "\n", - "def flatten_nitrogen_source(media_spec):\n", - " \"\"\"Extract nitrogen source as comma-separated string.\"\"\"\n", - " if media_spec == 'unspecified':\n", - " return 'unspecified'\n", - " \n", - " nitrogen = media_spec.get('nitrogen_source', 'unspecified')\n", - " if nitrogen == 'unspecified':\n", - " return 'unspecified'\n", - " \n", - " if isinstance(nitrogen, list):\n", - " compounds = [n['compound'] for n in nitrogen]\n", - " return ', '.join(compounds)\n", - " \n", - " return str(nitrogen)\n", - "\n", - "# Create flattened mapping for all conditions\n", - "flattened_media = {}\n", - "for condition, media_spec in media_specs.items():\n", - " flattened_media[condition] = {\n", - " 'media_name': media_spec.get('name', 'unspecified') if media_spec != 'unspecified' else 'unspecified',\n", - " 'carbon_source': flatten_carbon_source(media_spec),\n", - " 'nitrogen_source': flatten_nitrogen_source(media_spec)\n", - " }\n", - "\n", - "# Display flattened mapping\n", - "print(\"Flattened media specifications:\\n\")\n", - "for condition in sorted(flattened_media.keys()):\n", - " print(f\"{condition}:\")\n", - " for key, value in flattened_media[condition].items():\n", - " print(f\" {key}: {value}\")\n", - " print()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 5. Build SQL CASE Expressions Manually\n", - "\n", - "Now we'll construct SQL CASE expressions to map each condition value to its media specifications. This is the core of creating enriched metadata." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Build CASE expression for media_name\n", - "media_name_cases = []\n", - "for condition, specs in sorted(flattened_media.items()):\n", - " media_name_cases.append(f\" WHEN '{condition}' THEN '{specs['media_name']}'\")\n", - "media_name_sql = \"\\n\".join(media_name_cases)\n", - "\n", - "# Build CASE expression for carbon_source\n", - "carbon_source_cases = []\n", - "for condition, specs in sorted(flattened_media.items()):\n", - " carbon_source_cases.append(f\" WHEN '{condition}' THEN '{specs['carbon_source']}'\")\n", - "carbon_source_sql = \"\\n\".join(carbon_source_cases)\n", - "\n", - "# Build CASE expression for nitrogen_source\n", - "nitrogen_source_cases = []\n", - "for condition, specs in sorted(flattened_media.items()):\n", - " nitrogen_source_cases.append(f\" WHEN '{condition}' THEN '{specs['nitrogen_source']}'\")\n", - "nitrogen_source_sql = \"\\n\".join(nitrogen_source_cases)\n", - "\n", - "# Construct the complete CREATE VIEW SQL\n", - "create_view_sql = f\"\"\"\n", - "CREATE OR REPLACE VIEW harbison_2004_enriched_metadata AS\n", - "SELECT\n", - " sample_id,\n", - " regulator_locus_tag,\n", - " regulator_symbol,\n", - " condition,\n", - " CASE condition\n", - "{media_name_sql}\n", - " END AS media_name,\n", - " CASE condition\n", - "{carbon_source_sql}\n", - " END AS carbon_source,\n", - " CASE condition\n", - "{nitrogen_source_sql}\n", - " END AS nitrogen_source\n", - "FROM harbison_2004_train\n", - "\"\"\"\n", - "\n", - "print(\"Generated SQL:\")\n", - "print(\"=\"*80)\n", - "print(create_view_sql)\n", - "print(\"=\"*80)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 6. Execute SQL and Create Enriched Metadata View\n", - "\n", - "Now let's execute the SQL to create our enriched metadata view." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Execute the CREATE VIEW statement\n", - "conn.execute(create_view_sql)\n", - "\n", - "print(\"Enriched metadata view created successfully!\")\n", - "print(\"\\nAvailable views:\")\n", - "views = conn.execute(\"SHOW TABLES\").fetchall()\n", - "for view in views:\n", - " print(f\" - {view[0]}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Query the Enriched Metadata View\n", - "\n", - "Let's examine the enriched metadata to see the results." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Query enriched metadata\n", - "enriched_df = conn.execute(\"\"\"\n", - " SELECT *\n", - " FROM harbison_2004_enriched_metadata\n", - " LIMIT 10\n", - "\"\"\").df()\n", - "\n", - "print(f\"Enriched metadata shape: {enriched_df.shape}\")\n", - "print(\"\\nSample rows:\")\n", - "display(enriched_df)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 7. Practical Examples: Querying with Enriched Metadata\n", - "\n", - "Now that we have enriched metadata, we can perform analyses that wouldn't be possible with just the raw condition values." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Example 1: Find All Experiments with D-glucose as Carbon Source" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "glucose_df = conn.execute(\"\"\"\n", - " SELECT DISTINCT\n", - " condition,\n", - " media_name,\n", - " carbon_source,\n", - " nitrogen_source,\n", - " COUNT(*) as sample_count\n", - " FROM harbison_2004_enriched_metadata\n", - " WHERE carbon_source LIKE '%D-glucose%'\n", - " GROUP BY condition, media_name, carbon_source, nitrogen_source\n", - " ORDER BY condition\n", - "\"\"\").df()\n", - "\n", - "print(f\"Found {len(glucose_df)} condition(s) with D-glucose\")\n", - "display(glucose_df)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Example 2: Compare Rich Media vs Synthetic Media Experiments" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "media_comparison_df = conn.execute(\"\"\"\n", - " SELECT\n", - " CASE\n", - " WHEN media_name LIKE '%YPD%' OR media_name LIKE '%yeast_extract%'\n", - " THEN 'Rich Media'\n", - " WHEN media_name LIKE '%synthetic%'\n", - " THEN 'Synthetic Media'\n", - " ELSE 'Other'\n", - " END AS media_type,\n", - " COUNT(DISTINCT condition) as num_conditions,\n", - " COUNT(DISTINCT sample_id) as num_samples,\n", - " COUNT(DISTINCT regulator_symbol) as num_regulators\n", - " FROM harbison_2004_enriched_metadata\n", - " GROUP BY media_type\n", - " ORDER BY media_type\n", - "\"\"\").df()\n", - "\n", - "print(\"Media type comparison:\")\n", - "display(media_comparison_df)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Example 3: Analyze Carbon Source Diversity" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "carbon_diversity_df = conn.execute(\"\"\"\n", - " SELECT\n", - " carbon_source,\n", - " COUNT(DISTINCT condition) as num_conditions,\n", - " COUNT(DISTINCT sample_id) as num_samples,\n", - " STRING_AGG(DISTINCT condition, ', ' ORDER BY condition) as conditions\n", - " FROM harbison_2004_enriched_metadata\n", - " GROUP BY carbon_source\n", - " ORDER BY num_conditions DESC\n", - "\"\"\").df()\n", - "\n", - "print(\"Carbon source diversity:\")\n", - "display(carbon_diversity_df)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Example 4: Find Regulators Tested Across Multiple Carbon Sources" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "regulator_diversity_df = conn.execute(\"\"\"\n", - " SELECT\n", - " regulator_symbol,\n", - " regulator_locus_tag,\n", - " COUNT(DISTINCT carbon_source) as num_carbon_sources,\n", - " COUNT(DISTINCT condition) as num_conditions,\n", - " STRING_AGG(DISTINCT carbon_source, ', ') as carbon_sources_tested\n", - " FROM harbison_2004_enriched_metadata\n", - " GROUP BY regulator_symbol, regulator_locus_tag\n", - " HAVING COUNT(DISTINCT carbon_source) > 1\n", - " ORDER BY num_carbon_sources DESC\n", - " LIMIT 20\n", - "\"\"\").df()\n", - "\n", - "print(f\"Found {len(regulator_diversity_df)} regulators tested across multiple carbon sources\")\n", - "print(\"\\nTop 20 regulators by carbon source diversity:\")\n", - "display(regulator_diversity_df)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 8. Summary and Key Takeaways\n", - "\n", - "In this tutorial, we learned how to:\n", - "\n", - "1. **Explore nested metadata** using DataCard's `get_field_attribute()` method\n", - "2. **Flatten nested structures** into queryable strings\n", - "3. **Build SQL CASE expressions** manually to map condition values to metadata attributes\n", - "4. **Create enriched metadata views** that combine data fields with DataCard information\n", - "5. **Query by metadata attributes** that aren't present in the raw data\n", - "\n", - "### When to Use This Approach\n", - "\n", - "Use this pattern when:\n", - "- You need to analyze data by attributes that are defined in the DataCard but not in the data itself\n", - "- You want full control over the SQL being generated\n", - "- You're working with a single dataset and need custom metadata columns\n", - "\n", - "### Next Steps\n", - "\n", - "- Explore other attributes like `temperature_celsius` or `cultivation_method`\n", - "- Combine multiple attributes in your enriched views\n", - "- Use this pattern with other datasets in the BrentLab collection\n", - "- Build reusable helper functions for your specific analysis needs" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.0" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/tutorials/my_metadata_config.yaml b/docs/tutorials/my_metadata_config.yaml deleted file mode 100644 index 6338159..0000000 --- a/docs/tutorials/my_metadata_config.yaml +++ /dev/null @@ -1,39 +0,0 @@ -BrentLab/harbison_2004: - nitrogen_source: - path: media.nitrogen_source.name - dataset: - harbison_2004: - phosphate_source: - path: media.phosphate_source.compound - carbon_source: - field: condition - path: media.carbon_source.compound - temperature_celsius: - field: condition - path: temperature_celsius - -BrentLab/kemmeren_2014: - dataset: - kemmeren_2014: - carbon_source: - path: media.carbon_source.compound - temperature_celsius: - path: temperature_celsius - -factor_aliases: - carbon_source: - galactose: - - D-galactose - # should not have to include the actual name of the column -- that can be inferred - - Galactose - glucose: - - D-glucose - - dextrose - - glucose - raffinose: - - D-raffinose - - raffinose - -description: - carbon_source: - media: \ No newline at end of file diff --git a/docs/tutorials/passing_callingcards.csv b/docs/tutorials/passing_callingcards.csv deleted file mode 100644 index acaea06..0000000 --- a/docs/tutorials/passing_callingcards.csv +++ /dev/null @@ -1,82 +0,0 @@ -id,uploader,modifier,source_name,regulator_symbol,regulator_locus_tag,condition,data_usable,preferred_replicate,background_name,promoterset,batch,lab,assay,source_orig_id,upload_date,modified_date,file,single_binding,composite_binding,promoter,background,fileformat -8573,admin,admin,brent_nf_cc,SOK2,YMR016C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.335275-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8573.csv.gz,NA,1328,4,1,5 -8574,admin,admin,brent_nf_cc,RDR1,YOR380W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.373096-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8574.csv.gz,NA,1329,4,1,5 -8575,admin,admin,brent_nf_cc,PGD1,YGL025C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.674607-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8575.csv.gz,NA,1330,4,1,5 -8576,admin,admin,brent_nf_cc,TOG1,YER184C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.701098-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8576.csv.gz,NA,1331,4,1,5 -8577,admin,admin,brent_nf_cc,MET4,YNL103W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.787380-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8577.csv.gz,NA,1332,4,1,5 -8578,admin,admin,brent_nf_cc,CST6,YIL036W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.794787-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8578.csv.gz,NA,1333,4,1,5 -8579,admin,admin,brent_nf_cc,YOX1,YML027W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.813536-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8579.csv.gz,NA,1334,4,1,5 -8580,admin,admin,brent_nf_cc,RPD3,YNL330C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.866654-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8580.csv.gz,NA,1336,4,1,5 -8581,admin,admin,brent_nf_cc,MSN1,YOL116W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.844507-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8581.csv.gz,NA,1335,4,1,5 -8582,admin,admin,brent_nf_cc,HAP3,YBL021C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.918717-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8582.csv.gz,NA,1338,4,1,5 -8583,admin,admin,brent_nf_cc,SKN7,YHR206W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.924313-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8583.csv.gz,NA,1337,4,1,5 -8584,admin,admin,brent_nf_cc,SMP1,YBR182C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:42.984147-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8584.csv.gz,NA,1339,4,1,5 -8585,admin,admin,brent_nf_cc,MBP1,YDL056W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.068717-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8585.csv.gz,NA,1340,4,1,5 -8586,admin,admin,brent_nf_cc,RTG3,YBL103C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.078145-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8586.csv.gz,NA,1341,4,1,5 -8587,admin,admin,brent_nf_cc,DAT1,YML113W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.146444-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8587.csv.gz,NA,1342,4,1,5 -8588,admin,admin,brent_nf_cc,GZF3,YJL110C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.294324-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8588.csv.gz,NA,1344,4,1,5 -8589,admin,admin,brent_nf_cc,RDS2,YPL133C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.280370-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8589.csv.gz,NA,1343,4,1,5 -8590,admin,admin,brent_nf_cc,PDR3,YBL005W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.343977-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8590.csv.gz,NA,1346,4,1,5 -8591,admin,admin,brent_nf_cc,RFX1,YLR176C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.304403-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8591.csv.gz,NA,1345,4,1,5 -8592,admin,admin,brent_nf_cc,MET31,YPL038W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:43.521624-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8592.csv.gz,NA,1347,4,1,5 -8593,admin,admin,brent_nf_cc,INO4,YOL108C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:44.341119-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8593.csv.gz,NA,1349,4,1,5 -8594,admin,admin,brent_nf_cc,RGT1,YKL038W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:44.301633-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8594.csv.gz,NA,1348,4,1,5 -8595,admin,admin,brent_nf_cc,STP2,YHR006W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:44.653232-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8595.csv.gz,NA,1350,4,1,5 -8596,admin,admin,brent_nf_cc,AZF1,YOR113W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.090708-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8596.csv.gz,NA,1352,4,1,5 -8597,admin,admin,brent_nf_cc,ZAP1,YJL056C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.085289-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8597.csv.gz,NA,1351,4,1,5 -8598,admin,admin,brent_nf_cc,MOT3,YMR070W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.171772-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8598.csv.gz,NA,1353,4,1,5 -8599,admin,admin,brent_nf_cc,VHR1,YIL056W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.215135-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8599.csv.gz,NA,1354,4,1,5 -8600,admin,admin,brent_nf_cc,URC2,YDR520C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.286065-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8600.csv.gz,NA,1355,4,1,5 -8601,admin,admin,brent_nf_cc,INO2,YDR123C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.339489-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8601.csv.gz,NA,1356,4,1,5 -8602,admin,admin,brent_nf_cc,FKH2,YNL068C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.395206-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8602.csv.gz,NA,1357,4,1,5 -8603,admin,admin,brent_nf_cc,CHA4,YLR098C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.445223-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8603.csv.gz,NA,1360,4,1,5 -8604,admin,admin,brent_nf_cc,TYE7,YOR344C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.446764-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8604.csv.gz,NA,1359,4,1,5 -8605,admin,admin,brent_nf_cc,OAF1,YAL051W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.447196-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8605.csv.gz,NA,1358,4,1,5 -8606,admin,admin,brent_nf_cc,MET28,YIR017C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.551152-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8606.csv.gz,NA,1361,4,1,5 -8607,admin,admin,brent_nf_cc,AFT1,YGL071W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.825510-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8607.csv.gz,NA,1362,4,1,5 -8608,admin,admin,brent_nf_cc,GAT2,YMR136W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.897217-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8608.csv.gz,NA,1363,4,1,5 -8609,admin,admin,brent_nf_cc,PHO2,YDL106C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:46.029628-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8609.csv.gz,NA,1366,4,1,5 -8610,admin,admin,brent_nf_cc,DAL80,YKR034W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.927940-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8610.csv.gz,NA,1364,4,1,5 -8611,admin,admin,brent_nf_cc,UME1,YPL139C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:45.951004-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8611.csv.gz,NA,1365,4,1,5 -8612,admin,admin,brent_nf_cc,RLM1,YPL089C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:46.219541-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8612.csv.gz,NA,1367,4,1,5 -8613,admin,admin,brent_nf_cc,HAP5,YOR358W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:46.366453-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8613.csv.gz,NA,1368,4,1,5 -8614,admin,admin,brent_nf_cc,TUP1,YCR084C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:46.441695-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8614.csv.gz,NA,1369,4,1,5 -8615,admin,admin,brent_nf_cc,IXR1,YKL032C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:46.605683-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8615.csv.gz,NA,1370,4,1,5 -8616,admin,admin,brent_nf_cc,CYC8,YBR112C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.115289-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8616.csv.gz,NA,1371,4,1,5 -8617,admin,admin,brent_nf_cc,UPC2,YDR213W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.185506-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8617.csv.gz,NA,1372,4,1,5 -8618,admin,admin,brent_nf_cc,ARO80,YDR421W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.300436-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8618.csv.gz,NA,1373,4,1,5 -8619,admin,admin,brent_nf_cc,YHP1,YDR451C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.432083-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8619.csv.gz,NA,1374,4,1,5 -8620,admin,admin,brent_nf_cc,GAL11,YOL051W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.457453-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8620.csv.gz,NA,1375,4,1,5 -8621,admin,admin,brent_nf_cc,MED2,YDL005C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.457928-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8621.csv.gz,NA,1376,4,1,5 -8622,admin,admin,brent_nf_cc,GIS1,YDR096W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.495815-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8622.csv.gz,NA,1377,4,1,5 -8623,admin,admin,brent_nf_cc,STP1,YDR463W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.533662-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8623.csv.gz,NA,1379,4,1,5 -8624,admin,admin,brent_nf_cc,RTG1,YOL067C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.529096-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8624.csv.gz,NA,1378,4,1,5 -8625,admin,admin,brent_nf_cc,HAP2,YGL237C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.546475-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8625.csv.gz,NA,1380,4,1,5 -8626,admin,admin,brent_nf_cc,MSN2,YMR037C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:47.876943-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8626.csv.gz,NA,1381,4,1,5 -8627,admin,admin,brent_nf_cc,LEU3,YLR451W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:48.130584-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8627.csv.gz,NA,1382,4,1,5 -8628,admin,admin,brent_nf_cc,GAL4,YPL248C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:48.383735-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8628.csv.gz,NA,1383,4,1,5 -8629,admin,admin,brent_nf_cc,GCN4,YEL009C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:48.661669-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8629.csv.gz,NA,1384,4,1,5 -8630,admin,admin,brent_nf_cc,CRZ1,YNL027W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:48.724198-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8630.csv.gz,NA,1385,4,1,5 -8631,admin,admin,brent_nf_cc,STE12,YHR084W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:48.827903-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8631.csv.gz,NA,1386,4,1,5 -8632,admin,admin,brent_nf_cc,STP3,YLR375W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.012518-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8632.csv.gz,NA,1387,4,1,5 -8633,admin,admin,brent_nf_cc,PHD1,YKL043W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.172299-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8633.csv.gz,NA,1388,4,1,5 -8634,admin,admin,brent_nf_cc,RPN4,YDL020C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.219434-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8634.csv.gz,NA,1389,4,1,5 -8635,admin,admin,brent_nf_cc,ASH1,YKL185W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.252413-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8635.csv.gz,NA,1390,4,1,5 -8636,admin,admin,brent_nf_cc,TEA1,YOR337W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.600060-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8636.csv.gz,NA,1391,4,1,5 -8637,admin,admin,brent_nf_cc,SUM1,YDR310C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.893152-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8637.csv.gz,NA,1392,4,1,5 -8638,admin,admin,brent_nf_cc,NRG1,YDR043C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.991998-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8638.csv.gz,NA,1394,4,1,5 -8639,admin,admin,brent_nf_cc,TEC1,YBR083W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:49.962020-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8639.csv.gz,NA,1393,4,1,5 -8640,admin,admin,brent_nf_cc,CSE2,YNR010W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:50.509234-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8640.csv.gz,NA,1395,4,1,5 -8641,admin,admin,brent_nf_cc,ROX1,YPR065W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:50.797488-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8641.csv.gz,NA,1397,4,1,5 -8642,admin,admin,brent_nf_cc,FKH1,YIL131C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:50.782743-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8642.csv.gz,NA,1396,4,1,5 -8643,admin,admin,brent_nf_cc,GAL80,YML051W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:50.868439-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8643.csv.gz,NA,1398,4,1,5 -8644,admin,admin,brent_nf_cc,SKO1,YNL167C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:51.214395-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8644.csv.gz,NA,1399,4,1,5 -8645,admin,admin,brent_nf_cc,PDR1,YGL013C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:51.527347-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8645.csv.gz,NA,1400,4,1,5 -8646,admin,admin,brent_nf_cc,CIN5,YOR028C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:51.544390-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8646.csv.gz,NA,1401,4,1,5 -8647,admin,admin,brent_nf_cc,PPR1,YLR014C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:51.974579-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8647.csv.gz,NA,1402,4,1,5 -8648,admin,admin,brent_nf_cc,HAL9,YOL089C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:52.067382-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8648.csv.gz,NA,1403,4,1,5 -8649,admin,admin,brent_nf_cc,CBF1,YJR060W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:52.302853-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8649.csv.gz,NA,1404,4,1,5 -8650,admin,admin,brent_nf_cc,USV1,YPL230W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:52.353098-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8650.csv.gz,NA,1405,4,1,5 -8651,admin,admin,brent_nf_cc,RPH1,YER169W,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:53.152828-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8651.csv.gz,NA,1406,4,1,5 -8652,admin,admin,brent_nf_cc,ERT1,YBR239C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:53.548696-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8652.csv.gz,NA,1407,4,1,5 -8653,admin,admin,brent_nf_cc,MET32,YDR253C,NA,pass,TRUE,ad1,yiming,NA,brent,callingcards,NA,2025-08-05,2025-08-05T09:13:54.027532-05:00,https://yeastregulatorydb-htcf-data.s3.amazonaws.com/media/promotersetsig/8653.csv.gz,NA,1408,4,1,5 diff --git a/docs/tutorials/rank_response_tutorial.ipynb b/docs/tutorials/rank_response_tutorial.ipynb deleted file mode 100644 index 2e85f20..0000000 --- a/docs/tutorials/rank_response_tutorial.ipynb +++ /dev/null @@ -1,968 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Rank-Response Analysis Tutorial\n", - "\n", - "This tutorial demonstrates how to conduct comprehensive rank-response analysis using tfbpapi to compare binding data with perturbation responses across multiple datasets.\n", - "\n", - "## Overview\n", - "\n", - "Rank-response analysis evaluates whether targets with stronger binding evidence are more likely to show transcriptional responses upon regulator perturbation. This approach helps validate functional relevance of binding data.\n", - "\n", - "### Datasets Used\n", - "- **Binding data**: Calling Cards, ChEC-seq, ChIP-chip\n", - "- **Perturbation data**: McIsaac (ZEV system), Kemmeren (overexpression), Mahendrawada (RNA-seq)\n", - "\n", - "### Analysis Strategy\n", - "1. Load and filter datasets to common regulators/targets\n", - "2. Rank targets by binding strength for each regulator\n", - "3. Label targets as responsive/non-responsive based on perturbation data\n", - "4. Perform binned analysis to assess rank-response relationship\n", - "5. Compare results across different data combinations" - ] - }, - { - "cell_type": "code", - "execution_count": 61, - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "from tfbpapi.HfQueryAPI import HfQueryAPI\n", - "from tfbpapi.RankResponseAnalysis import RankResponseAnalyzer\n", - "from tfbpapi.datainfo.datacard import DataCard\n", - "from tfbpapi.errors import DataCardError, HfDataFetchError\n", - "import matplotlib.pyplot as plt\n", - "import seaborn as sns\n", - "\n", - "# Configure plotting\n", - "plt.style.use('default')\n", - "sns.set_palette(\"husl\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Dataset Exploration with DataCard\n", - "\n", - "Before loading data, let's explore dataset structure and metadata using the DataCard interface." - ] - }, - { - "cell_type": "code", - "execution_count": 62, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Exploring dataset structures...\n", - "\n", - "==================================================\n", - "Exploring BrentLab/callingcards\n", - "Dataset types in configs: {'genome_map', 'annotated_features', 'metadata'}\n", - "Configurations: 4\n", - "\n", - "Config: annotated_features\n", - " Type: annotated_features\n", - " Features: 13\n", - " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", - " Target fields: ['target_locus_tag', 'target_symbol']\n", - "\n", - "Config: genome_map\n", - " Type: genome_map\n", - " Features: 7\n", - "\n", - "Config: annotated_features_meta\n", - " Type: metadata\n", - " Features: 9\n", - " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", - "\n", - "Config: genome_map_meta\n", - " Type: metadata\n", - " Features: 7\n", - " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", - "\n", - "==================================================\n", - "Exploring BrentLab/mahendrawada_2025\n", - "Dataset types in configs: {'genome_map', 'annotated_features', 'metadata', 'genomic_features'}\n", - "Configurations: 6\n", - "\n", - "Config: genomic_features\n", - " Type: genomic_features\n", - " Features: 24\n", - "\n", - "Config: mahendrawada_2025_metadata\n", - " Type: metadata\n", - " Features: 7\n", - " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", - "\n", - "Config: chec_seq_genome_map\n", - " Type: genome_map\n", - " Features: 3\n", - "\n", - "Config: mahendrawada_chec_seq\n", - " Type: annotated_features\n", - " Features: 6\n", - " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", - " Target fields: ['target_locus_tag', 'target_symbol']\n", - " Metadata fields: ['regulator_locus_tag', 'regulator_symbol']\n", - "\n", - "Config: reprocessed_chec_seq\n", - " Type: annotated_features\n", - " Features: 6\n", - " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", - " Target fields: ['target_locus_tag', 'target_symbol']\n", - "\n", - "Config: rna_seq\n", - " Type: annotated_features\n", - " Features: 5\n", - " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", - " Target fields: ['target_locus_tag', 'target_symbol']\n", - " Metadata fields: ['regulator_locus_tag', 'regulator_symbol']\n", - "\n", - "==================================================\n", - "Exploring BrentLab/harbison_2004\n", - "Dataset types in configs: {'annotated_features'}\n", - "Configurations: 1\n", - "\n", - "Config: harbison_2004\n", - " Type: annotated_features\n", - " Features: 7\n", - " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", - " Target fields: ['target_locus_tag', 'target_symbol']\n", - " Metadata fields: ['regulator_locus_tag', 'regulator_symbol', 'condition']\n", - "\n", - "==================================================\n", - "Exploring BrentLab/hackett_2020\n", - "Dataset types in configs: {'annotated_features'}\n", - "Configurations: 1\n", - "\n", - "Config: hackett_2020\n", - " Type: annotated_features\n", - " Features: 17\n", - " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", - " Target fields: ['target_locus_tag', 'target_symbol']\n", - " Metadata fields: ['regulator_locus_tag', 'regulator_symbol', 'time', 'mechanism', 'restriction', 'date', 'strain']\n", - "\n", - "==================================================\n", - "Exploring BrentLab/kemmeren_2014\n", - "Dataset types in configs: {'annotated_features'}\n", - "Configurations: 1\n", - "\n", - "Config: kemmeren_2014\n", - " Type: annotated_features\n", - " Features: 11\n", - " Regulator fields: ['regulator_locus_tag', 'regulator_symbol']\n", - " Target fields: ['target_locus_tag', 'target_symbol']\n", - " Metadata fields: ['regulator_locus_tag', 'regulator_symbol']\n", - "\n", - "==================================================\n", - "Successfully explored 5 datasets\n" - ] - } - ], - "source": [ - "# Explore dataset structure using DataCard\n", - "print(\"Exploring dataset structures...\")\n", - "\n", - "datasets_to_explore = [\n", - " \"BrentLab/callingcards\",\n", - " \"BrentLab/mahendrawada_2025\", \n", - " \"BrentLab/harbison_2004\",\n", - " \"BrentLab/hackett_2020\",\n", - " \"BrentLab/kemmeren_2014\"\n", - "]\n", - "\n", - "dataset_info = {}\n", - "\n", - "for repo_id in datasets_to_explore:\n", - " try:\n", - " print(f\"\\n{'='*50}\")\n", - " print(f\"Exploring {repo_id}\")\n", - "\n", - " # Create DataCard instance\n", - " datacard = DataCard(repo_id)\n", - " card = datacard.dataset_card\n", - "\n", - " # DatasetCard doesn't have dataset_type directly - it's on individual configs\n", - " dataset_types = [config.dataset_type.value for config in card.configs]\n", - " print(f\"Dataset types in configs: {set(dataset_types)}\")\n", - " print(f\"Configurations: {len(card.configs)}\")\n", - "\n", - " # Store dataset info for later use\n", - " dataset_info[repo_id] = {\n", - " 'datacard': datacard,\n", - " 'card': card,\n", - " 'configs': {config.config_name: config for config in card.configs}\n", - " }\n", - "\n", - " # Display configuration details\n", - " for config in card.configs:\n", - " print(f\"\\nConfig: {config.config_name}\")\n", - " print(f\" Type: {config.dataset_type.value}\")\n", - " print(f\" Features: {len(config.dataset_info.features) if config.dataset_info.features else 0}\")\n", - "\n", - " if config.dataset_info.features:\n", - " # Show regulator and target fields if available\n", - " feature_names = [f.name for f in config.dataset_info.features]\n", - " regulator_fields = [f for f in feature_names if 'regulator' in f.lower()]\n", - " target_fields = [f for f in feature_names if 'target' in f.lower()]\n", - "\n", - " if regulator_fields:\n", - " print(f\" Regulator fields: {regulator_fields}\")\n", - " if target_fields:\n", - " print(f\" Target fields: {target_fields}\")\n", - "\n", - " if config.metadata_fields:\n", - " print(f\" Metadata fields: {config.metadata_fields}\")\n", - "\n", - " except (DataCardError, HfDataFetchError) as e:\n", - " print(f\"Error exploring {repo_id}: {e}\")\n", - " continue\n", - " except Exception as e:\n", - " print(f\"Unexpected error exploring {repo_id}: {e}\")\n", - " continue\n", - "\n", - "print(f\"\\n{'='*50}\")\n", - "print(f\"Successfully explored {len(dataset_info)} datasets\")" - ] - }, - { - "cell_type": "code", - "execution_count": 63, - "metadata": {}, - "outputs": [], - "source": [ - "## Initialize dataset connections and load data\n" - ] - }, - { - "cell_type": "code", - "execution_count": 64, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Initializing HfQueryAPI connections...\n", - "All API connections initialized\n" - ] - } - ], - "source": [ - "\n", - "print(\"Initializing HfQueryAPI connections...\")\n", - "\n", - "mahendrawada_2025 = HfQueryAPI(repo_id=\"BrentLab/mahendrawada_2025\")\n", - "hackett_2020 = HfQueryAPI(repo_id=\"BrentLab/hackett_2020\")\n", - "callingcards = HfQueryAPI(repo_id=\"BrentLab/callingcards\")\n", - "harbison_2004 = HfQueryAPI(repo_id=\"BrentLab/harbison_2004\") \n", - "kemmeren_2014 = HfQueryAPI(repo_id=\"BrentLab/kemmeren_2014\")\n", - "\n", - "print(\"All API connections initialized\")" - ] - }, - { - "cell_type": "code", - "execution_count": 65, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Getting metadata from datasets...\n", - "All metadata loaded\n", - "Found 81 common regulators across all datasets\n" - ] - } - ], - "source": [ - "# Get metadata from each dataset to find common regulators\n", - "print(\"Getting metadata from datasets...\")\n", - "\n", - "harbison_meta = harbison_2004.get_metadata(\"harbison_2004\")\n", - "kemmeren_meta = kemmeren_2014.get_metadata(\"kemmeren_2014\") \n", - "callingcards_meta = callingcards.get_metadata(\"annotated_features\")\n", - "mahendrawada_checseq_meta = mahendrawada_2025.get_metadata(\"mahendrawada_chec_seq\")\n", - "mahendrawada_rnaseq_meta = mahendrawada_2025.get_metadata(\"rna_seq\")\n", - "hackett_2020_meta = hackett_2020.get_metadata(\"hackett_2020\")\n", - "\n", - "print(\"All metadata loaded\")\n", - "\n", - "# Get the intersection of common regulators\n", - "common_regulators = (set(mahendrawada_rnaseq_meta.regulator_locus_tag.unique())\n", - " & set(mahendrawada_checseq_meta.regulator_locus_tag.unique())\n", - " & set(hackett_2020_meta.regulator_locus_tag.unique())\n", - " & set(callingcards_meta.regulator_locus_tag.unique())\n", - " & set(harbison_meta.regulator_locus_tag.unique())\n", - " & set(kemmeren_meta.regulator_locus_tag.unique()))\n", - "\n", - "print(f\"Found {len(common_regulators)} common regulators across all datasets\")\n", - "\n", - "# Create proper SQL IN clause\n", - "if common_regulators:\n", - " regulator_clause = \"(\" + \", \".join(f\"'{reg}'\" for reg in common_regulators) + \")\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Filter the data" - ] - }, - { - "cell_type": "code", - "execution_count": 66, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Loading calling cards quality filter...\n", - "Found 81 passing calling cards experiments\n", - "Applying dataset-specific filters...\n", - "All filters applied\n" - ] - } - ], - "source": [ - "# Load and filter calling cards data with passing experiments\n", - "print(\"Loading calling cards quality filter...\")\n", - "\n", - "passing_cc = pd.read_csv(\"./passing_callingcards.csv\")\n", - "db_ids_as_str = [str(id) for id in passing_cc.id.unique()]\n", - "\n", - "# filter the callingcards_data on the db_id column using passing_cc.id\n", - "db_ids = callingcards_meta[callingcards_meta.db_id.isin(db_ids_as_str)].id\n", - "cc_ids_clause = \"(\" + \", \".join(f\"'{db_id}'\" for db_id in db_ids) + \")\"\n", - "\n", - "print(f\"Found {len(db_ids)} passing calling cards experiments\")\n", - "\n", - "# Apply dataset-specific filters\n", - "print(\"Applying dataset-specific filters...\")\n", - "\n", - "hackett_2020.set_sql_filter(\n", - " \"hackett_2020\",\n", - " f\"\"\"\n", - " time = 15 \n", - " AND mechanism = 'ZEV' \n", - " AND restriction = 'P' \n", - " AND regulator_locus_tag IN {regulator_clause}\n", - " \"\"\")\n", - "\n", - "mahendrawada_2025.set_sql_filter(\n", - " \"mahendrawada_chec_seq\",\n", - " f\"\"\"\n", - " regulator_locus_tag IN {regulator_clause}\n", - " \"\"\")\n", - "\n", - "mahendrawada_2025.set_sql_filter(\n", - " \"rna_seq\",\n", - " f\"\"\"\n", - " regulator_locus_tag IN {regulator_clause}\n", - " \"\"\")\n", - "\n", - "callingcards.set_sql_filter(\n", - " \"annotated_features\",\n", - " f\"\"\"\n", - " regulator_locus_tag IN {regulator_clause} \n", - " AND id IN {cc_ids_clause}\n", - " \"\"\")\n", - "\n", - "harbison_2004.set_sql_filter(\n", - " \"harbison_2004\",\n", - " f\"\"\"\n", - " regulator_locus_tag IN {regulator_clause} \n", - " AND condition = 'YPD'\n", - " \"\"\")\n", - "\n", - "print(\"All filters applied\")" - ] - }, - { - "cell_type": "code", - "execution_count": 67, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Loading initial datasets...\n", - "Initial datasets loaded\n", - "mcisaac: 61 regulators\n", - "mahendrawada_chec: 81 regulators\n", - "callingcards: 46 regulators\n", - "harbison: 81 regulators\n", - "Final intersection: 33 regulators\n" - ] - } - ], - "source": [ - "# Load initial datasets to determine final common regulators and targets\n", - "print(\"Loading initial datasets...\")\n", - "\n", - "mcisaac = hackett_2020.query(\"SELECT * FROM hackett_2020\", \"hackett_2020\")\n", - "mahendrawada_chec = mahendrawada_2025.query(\"SELECT * FROM mahendrawada_chec_seq\", \"mahendrawada_chec_seq\") \n", - "mahendrawada_perturb = mahendrawada_2025.query(\"SELECT * FROM rna_seq\", \"rna_seq\")\n", - "callingcards_data = callingcards.query(\"SELECT * FROM annotated_features\", \"annotated_features\")\n", - "harbison_data = harbison_2004.query(\"SELECT * FROM harbison_2004\", \"harbison_2004\")\n", - "mahendrawada_features = mahendrawada_2025.query(\"SELECT * FROM genomic_features\", \"genomic_features\")\n", - "\n", - "print(\"Initial datasets loaded\")\n", - "\n", - "# After running your current queries, check what you actually got\n", - "actual_regulators = {\n", - " 'mcisaac': set(mcisaac.regulator_locus_tag.unique()),\n", - " 'mahendrawada_chec': set(mahendrawada_chec.regulator_locus_tag.unique()),\n", - " 'callingcards': set(callingcards_data.regulator_locus_tag.unique()),\n", - " 'harbison': set(harbison_data.regulator_locus_tag.unique())\n", - "}\n", - "\n", - "for name, regulators in actual_regulators.items():\n", - " print(f\"{name}: {len(regulators)} regulators\")\n", - "\n", - "final_common = set.intersection(*actual_regulators.values())\n", - "print(f\"Final intersection: {len(final_common)} regulators\")\n", - "\n", - "final_common_clause = \"(\" + \", \".join(f\"'{reg}'\" for reg in final_common) + \")\"" - ] - }, - { - "cell_type": "code", - "execution_count": 68, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Finding common targets...\n", - "Found 5525 common targets\n", - "Loading final datasets with complete filtering...\n", - "Final datasets loaded with complete filtering\n", - "McIsaac: 187,884 rows, 33 regulators\n", - "Kemmeren: 178,332 rows, 33 regulators\n", - "Mahendrawada RNA-seq: 4,411 rows, 33 regulators\n", - "Calling Cards: 182,325 rows, 33 regulators\n", - "Mahendrawada ChEC: 22,478 rows, 33 regulators\n", - "Harbison: 182,952 rows, 33 regulators\n" - ] - } - ], - "source": [ - "# Find common targets across all datasets\n", - "print(\"Finding common targets...\")\n", - "\n", - "common_targets = (set(mcisaac.target_locus_tag.unique())\n", - " & set(mahendrawada_features.locus_tag.unique())\n", - " & set(callingcards_data.target_locus_tag.unique())\n", - " & set(harbison_data.target_locus_tag.unique()))\n", - "\n", - "print(f\"Found {len(common_targets)} common targets\")\n", - "\n", - "if common_targets:\n", - " target_clause = \"(\" + \", \".join(f\"'{tgt}'\" for tgt in common_targets) + \")\"\n", - "\n", - "# Load final datasets with both regulator and target filtering \n", - "print(\"Loading final datasets with complete filtering...\")\n", - "\n", - "mcisaac = hackett_2020.query(f\"\"\"\n", - " SELECT *\n", - " FROM hackett_2020\n", - " WHERE regulator_locus_tag in {final_common_clause} \n", - " AND target_locus_tag IN {target_clause}\n", - " \"\"\", \"hackett_2020\")\n", - "\n", - "mahendrawada_perturb = mahendrawada_2025.query(f\"\"\"\n", - " SELECT *\n", - " FROM rna_seq\n", - " WHERE regulator_locus_tag IN {final_common_clause} \n", - " AND target_locus_tag IN {target_clause}\n", - " \"\"\", \"rna_seq\")\n", - "\n", - "kemmeren = kemmeren_2014.query(f\"\"\"\n", - " SELECT * FROM kemmeren_2014 WHERE regulator_locus_tag IN {final_common_clause} \n", - " AND target_locus_tag IN {target_clause} \n", - " AND variable_in_wt = 0\n", - " \"\"\",\n", - " \"kemmeren_2014\")\n", - "\n", - "mahendrawada_chec = mahendrawada_2025.query(f\"SELECT * FROM mahendrawada_chec_seq WHERE regulator_locus_tag IN {final_common_clause} AND target_locus_tag IN {target_clause}\", \"mahendrawada_chec_seq\")\n", - "\n", - "callingcards_data = callingcards.query(f\"SELECT * FROM annotated_features WHERE regulator_locus_tag IN {final_common_clause} AND target_locus_tag IN {target_clause}\",\n", - " \"annotated_features\")\n", - "\n", - "harbison = harbison_2004.query(f\"SELECT * FROM harbison_2004 WHERE regulator_locus_tag IN {final_common_clause} AND target_locus_tag IN {target_clause}\",\n", - " \"harbison_2004\")\n", - "\n", - "print(\"Final datasets loaded with complete filtering\")\n", - "\n", - "# Print final dataset sizes\n", - "datasets_info = [\n", - " ('McIsaac', mcisaac), ('Kemmeren', kemmeren), ('Mahendrawada RNA-seq', mahendrawada_perturb),\n", - " ('Calling Cards', callingcards_data), ('Mahendrawada ChEC', mahendrawada_chec), ('Harbison', harbison)\n", - "]\n", - "\n", - "for name, data in datasets_info:\n", - " if len(data) > 0:\n", - " regulators = data['regulator_locus_tag'].nunique() if 'regulator_locus_tag' in data.columns else 0\n", - " print(f\"{name}: {len(data):,} rows, {regulators} regulators\")\n", - " else:\n", - " print(f\"{name}: No data loaded\")" - ] - }, - { - "cell_type": "code", - "execution_count": 69, - "metadata": {}, - "outputs": [], - "source": [ - "## 2. Rank Binding Data by Strength" - ] - }, - { - "cell_type": "code", - "execution_count": 70, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Binding datasets ranked by strength\n" - ] - } - ], - "source": [ - "\n", - "# Rank binding datasets by strength (stronger binding = lower rank)\n", - "callingcards_ranked = (callingcards_data\n", - " .sort_values(['regulator_locus_tag', 'poisson_pval', 'callingcards_enrichment'],\n", - " ascending=[True, True, False], # Lower p-value, higher enrichment = stronger\n", - " kind='stable')\n", - " .reset_index(drop=True)\n", - " )\n", - "\n", - "mahendrawada_chec_ranked = (mahendrawada_chec\n", - " .sort_values(['regulator_locus_tag', 'peak_score'],\n", - " ascending=[True, False], # Higher peak score = stronger\n", - " kind='stable')\n", - " .reset_index(drop=True)\n", - " )\n", - "\n", - "harbison_ranked = (harbison\n", - " .sort_values(['regulator_locus_tag', 'pvalue', 'effect'],\n", - " ascending=[True, True, False], # Lower p-value, higher effect = stronger\n", - " kind='stable')\n", - " .reset_index(drop=True)\n", - " )\n", - "\n", - "print(\"Binding datasets ranked by strength\")" - ] - }, - { - "cell_type": "code", - "execution_count": 71, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Labeling functions defined\n" - ] - } - ], - "source": [ - "\n", - "def label_by_mcisaac(df):\n", - " \"\"\"Label targets as responsive based on McIsaac perturbation data.\"\"\"\n", - " out = df.merge(\n", - " mcisaac[['regulator_locus_tag', 'target_locus_tag', 'log2_shrunken_timecourses']],\n", - " on=['regulator_locus_tag', 'target_locus_tag'],\n", - " how='left',\n", - " )\n", - "\n", - " out['is_responsive'] = (\n", - " out.get('log2_shrunken_timecourses', pd.Series(0)).fillna(0).abs() > 0)\n", - "\n", - " return out\n", - "\n", - "def label_by_kemmeren(df):\n", - " \"\"\"Label targets as responsive based on Kemmeren perturbation data.\"\"\" \n", - " out = df.merge(\n", - " kemmeren[['regulator_locus_tag', 'target_locus_tag', 'Madj', 'pval']],\n", - " on=['regulator_locus_tag', 'target_locus_tag'],\n", - " how='left',\n", - " )\n", - "\n", - " out['is_responsive'] = (\n", - " out.get('pval', pd.Series(1)).fillna(1) <= 0.05)\n", - "\n", - " return out\n", - "\n", - "def label_by_mahendrawada_rnaseq(df):\n", - " \"\"\"Label targets as responsive based on Mahendrawada RNA-seq data.\"\"\"\n", - " out = df.merge(\n", - " mahendrawada_perturb[['regulator_locus_tag', 'target_locus_tag', 'log2fc']],\n", - " on=['regulator_locus_tag', 'target_locus_tag'],\n", - " how='left',\n", - " )\n", - "\n", - " out['is_responsive'] = ~out.log2fc.isna()\n", - "\n", - " return out\n", - "\n", - "print(\"Labeling functions defined\")" - ] - }, - { - "cell_type": "code", - "execution_count": 72, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Analysis functions defined\n" - ] - } - ], - "source": [ - "\n", - "def get_rr_results(df):\n", - " \"\"\"\n", - " Perform rank-response analysis for each regulator in the dataset.\n", - "\n", - " :param df: DataFrame with ranked binding data and responsive labels\n", - "\n", - " :returns: DataFrame with bin-level results for all regulators\n", - " \"\"\"\n", - " res_dict = {}\n", - "\n", - " # For each regulator separately\n", - " for regulator in df['regulator_locus_tag'].unique():\n", - " regulator_data = df[\n", - " df['regulator_locus_tag'] == regulator\n", - " ]\n", - "\n", - " try:\n", - " analyzer = RankResponseAnalyzer(\n", - " data=regulator_data[['target_locus_tag', 'is_responsive']],\n", - " target_col='target_locus_tag',\n", - " responsive_col='is_responsive',\n", - " bin_size=5 # Small bin size for detailed analysis\n", - " )\n", - "\n", - " results = analyzer.get_bin_summary()\n", - " res_dict[regulator] = results\n", - "\n", - " except Exception as e:\n", - " print(f\"Warning: Failed to analyze {regulator}: {e}\")\n", - " continue\n", - "\n", - " if not res_dict:\n", - " return pd.DataFrame()\n", - "\n", - " return (pd.concat(res_dict, names=['regulator_locus_tag', 'original_index'])\n", - " .reset_index(level=0)\n", - " .reset_index(drop=True))\n", - "\n", - "def combine_rr_results(ranked_df, binding_source):\n", - " \"\"\"\n", - " Generate rank-response results for all perturbation sources and combine filtered results.\n", - "\n", - " :param ranked_df: DataFrame with ranked binding data\n", - " :param binding_source: String identifier for the binding data source\n", - "\n", - " :returns: Combined DataFrame with bin==5 results from all perturbation sources\n", - " \"\"\"\n", - " print(f\"Analyzing {binding_source} binding data...\")\n", - "\n", - " # Generate results for each perturbation source\n", - " cc_mcisaac_rr = get_rr_results(label_by_mcisaac(ranked_df))\n", - " cc_kem_rr = get_rr_results(label_by_kemmeren(ranked_df))\n", - " cc_mahendrawada_rr = get_rr_results(label_by_mahendrawada_rnaseq(ranked_df))\n", - "\n", - " # Add source columns to each result\n", - " cc_mcisaac_rr['binding_source'] = binding_source\n", - " cc_mcisaac_rr['perturbation_source'] = 'mcisaac'\n", - "\n", - " cc_kem_rr['binding_source'] = binding_source\n", - " cc_kem_rr['perturbation_source'] = 'kemmeren'\n", - "\n", - " cc_mahendrawada_rr['binding_source'] = binding_source\n", - " cc_mahendrawada_rr['perturbation_source'] = 'mahendrawada_rnaseq'\n", - "\n", - " # Filter to bin==5 (strongest binding bin) and combine\n", - " bin5_results = [cc_mcisaac_rr[cc_mcisaac_rr.bin == 5],\n", - " cc_kem_rr[cc_kem_rr.bin == 5],\n", - " cc_mahendrawada_rr[cc_mahendrawada_rr.bin == 5]]\n", - "\n", - " combined = pd.concat(bin5_results, ignore_index=True)\n", - "\n", - " # Find regulators that have observations from all 3 perturbation sources\n", - " regulator_counts = combined.groupby('regulator_locus_tag')['perturbation_source'].nunique()\n", - " complete_regulators = regulator_counts[regulator_counts == 3].index\n", - "\n", - " # Filter to only keep rows for regulators with all 3 perturbation sources\n", - " final_result = combined[combined.regulator_locus_tag.isin(complete_regulators)]\n", - "\n", - " print(f\"Analysis complete: {len(complete_regulators)} regulators with complete data\")\n", - "\n", - " return final_result\n", - "\n", - "print(\"Analysis functions defined\")" - ] - }, - { - "cell_type": "code", - "execution_count": 73, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Binding data ranked by strength\n" - ] - } - ], - "source": [ - "def rank_by_binding_strength(df, binding_cols, ascending_order):\n", - " \"\"\"\n", - " Rank targets by binding strength within each regulator group.\n", - "\n", - " :param df: DataFrame with binding data\n", - " :param binding_cols: List of columns to sort by\n", - " :param ascending_order: List of boolean values for sort order\n", - " \"\"\"\n", - " return (df\n", - " .sort_values(['regulator_locus_tag'] + binding_cols, \n", - " ascending=[True] + ascending_order, \n", - " kind='stable')\n", - " .reset_index(drop=True))\n", - "\n", - "# Rank binding datasets\n", - "callingcards_ranked = rank_by_binding_strength(\n", - " callingcards_data,\n", - " ['poisson_pval', 'callingcards_enrichment'],\n", - " [True, False] # Lower p-value and higher enrichment = stronger binding\n", - ")\n", - "\n", - "mahendrawada_chec_ranked = rank_by_binding_strength(\n", - " mahendrawada_chec,\n", - " ['peak_score'],\n", - " [False] # Higher peak score = stronger binding\n", - ")\n", - "\n", - "harbison_ranked = rank_by_binding_strength(\n", - " harbison_data,\n", - " ['pvalue', 'effect'],\n", - " [True, False] # Lower p-value and higher effect = stronger binding\n", - ")\n", - "\n", - "print(\"Binding data ranked by strength\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Rank by binding and label each target as responsive or non-responsive" - ] - }, - { - "cell_type": "code", - "execution_count": 74, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Executing comprehensive rank-response analysis...\n", - "This will analyze each binding dataset against all perturbation sources...\n", - "Analyzing callingcards binding data...\n", - "Analysis complete: 33 regulators with complete data\n", - "Analyzing mahendrawada_chec binding data...\n", - "Analysis complete: 33 regulators with complete data\n", - "Analyzing harbison binding data...\n", - "Analysis complete: 81 regulators with complete data\n", - "\n", - "==================================================\n", - "Individual dataset results:\n", - "Calling Cards: 99 result rows\n", - "ChEC-seq: 99 result rows\n", - "Harbison: 243 result rows\n" - ] - } - ], - "source": [ - "# Run analysis for all available binding datasets\n", - "print(\"Executing comprehensive rank-response analysis...\")\n", - "print(\"This will analyze each binding dataset against all perturbation sources...\")\n", - "\n", - "# Execute analysis for each binding dataset\n", - "cc_rr_res = combine_rr_results(callingcards_ranked, \"callingcards\")\n", - "chec_rr_res = combine_rr_results(mahendrawada_chec_ranked, \"mahendrawada_chec\") \n", - "harb_rr_res = combine_rr_results(harbison_ranked, \"harbison\")\n", - "\n", - "print(f\"\\n{'='*50}\")\n", - "print(\"Individual dataset results:\")\n", - "print(f\"Calling Cards: {len(cc_rr_res)} result rows\")\n", - "print(f\"ChEC-seq: {len(chec_rr_res)} result rows\") \n", - "print(f\"Harbison: {len(harb_rr_res)} result rows\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Visualize rank-response relationships" - ] - }, - { - "cell_type": "code", - "execution_count": 75, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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7O3tBSMjI6Fhw4ZCbGysYGVlJfTr1++LxyOn/flUVsxPnz6Vm67s99LkyZMFR0dHQUdHR+4zosqxPH/+vODr6yuYmpoKxsbGQoMGDYSTJ08qLHv58mXB29tbMDQ0FBwdHYXJkycLK1euVPhsElH+Yk7PnJ45PXP63GRdIz79Z2hoKHh4eAhLlizJ9nP4ad6ZU26SXT7+4cMHYfDgwYKVlZVgYmIi+Pn5Cffv31dYpyq5fHZ554ULFwQvLy/BwMBAKF68uDB9+nRhwYIFAgDh8ePHuR6P//77T2jTpo1gYWEhmJubCx06dBAePnxYIPudnS995j7f7y9d03KKWxA+5rKDBg0SbGxsBIlEIpcnP336VGjXrp1gbGwsFC1aVOjbt69w9epVhc9FTp9VQfi/z+u///4r1K5dWzA0NBScnZ2FRYsWyZWTyWTCtGnTBGdnZ7Fe9vfffwv+/v6Cs7OzXNmTJ0+KOf6nxzO7PD89PV2YOHGi4OrqKujp6QlOTk7C6NGjhZSUFLlyqpxr2WFOT0TaSCIIHFknrwICArB582a19MtJRMp59uwZihUrhvHjx+f6+CsRqebVq1coWrQopkyZgjFjxhR2OEREBYY5PVHBY06fs+DgYCxbtgzJyck5DpBMRET0vfqmx8wgom9PVFQUMjMz0a1bt8IOhUhrffjwQWFaVr+8Pj4+BRsMERERfXeY03/0eU72/PlzrFmzBvXq1WNDBhERUTa++TEziOjbcOjQIVy/fh1Tp05F69at4eLiUtghEWmtjRs3IioqCs2bN4epqSmOHz+O9evX48cff0TdunULOzwiIiL6RjGnl1e7dm34+PigfPnyePLkCVauXIk3b97waRUiIqIcsDGDiLTCpEmTcPLkSdStWxcLFy4s7HCItFqVKlWgq6uLWbNm4c2bN+Kg4FOmTCns0IiIiOgbxpxeXvPmzbF582ZERERAIpGgWrVqWLlyJerXr1/YoREREWkkjplBREREREREREREREQajWNmEBERERERERERERGRRmNjBhERERERERERERERabTvbswMmUyGhw8fokiRIpBIJIUdDhERERUgQRDw9u1bODg4QEeH93QQaSvm9ERERN8v5vRE36/vrjHj4cOHcHJyKuwwiIiIqBDdv38fxYsXL+wwiCiPmNMTERERc3qi789315hRpEgRAB8veGZmZoUcDRERERWkN2/ewMnJScwHiEg7MacnIiL6fjGnJ/p+fXeNGVmPoZuZmbHiQ0RE9J1itzRE2o05PRERETGnJ/r+sGM5IiIiIiIiIiIiIiLSaGzMICIiIiIiIiIiIiIijcbGDCIiIiIiIiIiIiIi0mjf3ZgZRERERJogMzMT6enphR0GaQF9fX3o6PAeJCIiIiJNw5ye6OupUt9hYwYRERFRARIEAY8fP8arV68KOxTSEjo6OnB1dYW+vn5hh0JEREREYE5PpE6q1HfYmEFERERUgLIqPba2tjA2NoZEIinskEiDyWQyPHz4EI8ePUKJEiV4vhARERFpAOb0ROqhan2HjRlEREREBSQzM1Os9FhZWRV2OKQlbGxs8PDhQ2RkZEBPT6+wwyEiIiL6rjGnJ1IvVeo77HyXiIiIqIBk9adrbGxcyJGQNsl63DozM7OQIyEiIiIi5vRE6qVKfYeNGUREREQFjI+hkyp4vhARERFpHuZoROqhymeJjRlERERERERERERERKTR2JhBRERE9I1KSEiARCLBxYsXv2o9EyZMgIeHh/g6ICAArVu3/qp1ahIfHx8EBwerfb2fHzciIiIiou9ZftYjvqU6irrqcdlxcXFBWFiY2tdbUAq1MePo0aPw8/ODg4MDJBIJtm/f/sVloqOjUa1aNRgYGKB06dKIiorK9ziJiIiINE1AQAAkEon4z8rKCk2bNsXly5fFMk5OTnj06BEqVaqk1m3Pnz+/QHKwrH3s16+fwrwBAwZAIpEgICBA6fVFR0dDIpHg1atX6guSmNMTERERFbJP6wb6+vooXbo0Jk2ahIyMjK9er6Y1EOT0Q39B1VGyjvPp06flpqempsLKygoSiQTR0dFKr08Tj7EmK9TGjHfv3sHd3R2LFy9Wqnx8fDxatGiBBg0a4OLFiwgODkbv3r2xb9++fI6UiIiISPM0bdoUjx49wqNHj3Dw4EHo6uqiZcuW4nypVAp7e3vo6uqqdbvm5uawsLBQ6zpz4uTkhA0bNuDDhw/itJSUFPzxxx8oUaJEgcRAuWNOT0RERFT4suoGt2/fxvDhwzFhwgTMnj07T+vKzMyETCZTW2zqXl92CrqOEhkZKTdt27ZtMDU1LZDtf88KtTGjWbNmmDJlCtq0aaNU+aVLl8LV1RVz585F+fLlMXDgQLRv3x6//fZbPkdKREREpHkMDAxgb28Pe3t7eHh4YNSoUbh//z6ePn0KQPGupawnEw4ePIjq1avD2NgYderUwc2bN+XWO2PGDNjZ2aFIkSLo1asXUlJS5OZ/fveQj48PBg8ejJEjR8LS0hL29vaYMGGC3DI3btxAvXr1YGhoiAoVKuDAgQNK3cVfrVo1ODk5YevWreK0rVu3okSJEqhatapcWZlMhunTp8PV1RVGRkZwd3fH5s2bxWPRoEEDAEDRokUVnuqQyWS5xp+YmIhWrVrB1NQUZmZm6NixI548eaLScftWMacnIiIiKnxZdQNnZ2f0798fjRs3xo4dOwB8fGogJCQEjo6OMDExQa1ateSeHoiKioKFhQV27NiBChUqwMDAAD179sTq1avx119/iU8jREdHZ/u088WLFyGRSJCQkJDj+hITE8XyEydOhI2NDczMzNCvXz+kpaWJ8/bu3Yt69erBwsICVlZWaNmyJe7cuSPOd3V1BQBUrVoVEokEPj4+ABTrKKmpqRg8eDBsbW1haGiIevXq4ezZs+J8ZetG2fH391e44WrVqlXw9/dXKHv//n107NgRFhYWsLS0RKtWrcTjNGHChGyPcZa7d++iQYMGMDY2hru7O06dOiW37i1btqBixYowMDCAi4sL5s6dKzc/KSkJfn5+MDIygqurK9atW/fFfdN06r1NL5+dOnUKjRs3lpvm6+ubax/HqampSE1NFV+/efMmv8LLUUpKCu7fv5+v23BycoKhoWG+boOIiIg0V3JyMtauXYvSpUvDysoq17JjxozB3LlzYWNjg379+qFnz544ceIEAODPP//EhAkTsHjxYtSrVw9r1qzBggULULJkyVzXuXr1agwbNgwxMTE4deoUAgICULduXTRp0gSZmZlo3bo1SpQogZiYGLx9+xbDhw9Xet969uyJyMhIdO3aFcDHikKPHj0UHt+ePn061q5di6VLl8LNzQ1Hjx7FL7/8AhsbG9SrVw9btmxBu3btcPPmTZiZmcHIyEip+GUymdiQceTIEWRkZGDAgAH4+eefxRjyety+R8zps8d8noiIiNTJyMgIz58/BwAMHDgQ169fx4YNG+Dg4IBt27ahadOmuHLlCtzc3AAA79+/x8yZM7FixQpYWVmhWLFi+PDhA968eSM+hWBpaYmTJ08qtf3P12drawsAOHjwIAwNDREdHY2EhAT06NEDVlZWmDp1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I7LMmDED7dq1Q7du3VCtWjXExcVh3759KFq0qNKxfolEIoG1tbX4tMjnjI2NcfToUZQoUQJt27ZF+fLlxW6psuo12R1jZVSrVg1//vknNmzYgEqVKmH8+PGYNGkSAgICxDKRkZFwcHCAt7c32rZti8DAQNja2n71fhcmifBpB2ffgTdv3sDc3ByvX7/OtTJcWG7fvo2goCCEh4ezf10iIiI1K+w8ICUlBfHx8XB1dVXpR/xv0YkTJ1CvXj3ExcWhVKlShR2ORuN5o6iwP8tfwpyeiIgo/xR2HsDcjEi9VPlMadUA4ERERESknbZt2wZTU1O4ubkhLi4OQ4YMQd26ddmQQUREREREREphYwYRERER5bu3b9/if//7HxITE2FtbY3GjRur1LcuERERERERfd/YmEFERERE+a579+5yg0ATERERERERqaJQBwAnIiIiIiIiIiIiIiL6EjZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFG0y3sAIiIiIgISEpKwuvXrwtse+bm5rC1tS2w7WWJiopCcHAwXr16BQCYMGECtm/fjosXLwIAAgIC8OrVK2zfvr3AY8tvLi4uCA4ORnBwcGGHQkRERET54HvJ6bN8y7k7aSY2ZhAREREVsqSkJPTs0QOpaWkFtk0DfX2sioxUqfLz+PFjTJ06Fbt27cKDBw9ga2sLDw8PBAcHo1GjRmqJa/78+RAEQS3r+pKC2B8iIiIi+j5oS06vTgWZuxMBbMwgIiIiKnSvX79Galoa+pWtBgfjIvm+vYfv32LpzfN4/fq10hWfhIQE1K1bFxYWFpg9ezYqV66M9PR07Nu3DwMGDMCNGzfUEpu5ubla1vMl+bU/mZmZkEgk0NFhb65ERERE3xNtyOnVraByd6IsrGURERERaQgH4yJwMbXI9395qVwFBQVBIpHgzJkzaNeuHcqUKYOKFSti2LBhOH36tFhu3rx5qFy5MkxMTODk5ISgoCAkJycrvZ2AgAC0bt1afO3j44PBgwdj5MiRsLS0hL29PSZMmCC3zI0bN1CvXj0YGhqiQoUKOHDgACQSSa6Pu6trf6KiomBhYYEdO3agQoUKMDAwQGJiIpKSkuDn5wcjIyO4urpi3bp1ctsXBAET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zHD06FGEhYXhzZs3cHZ2xty5c9GsWTMAwMqVKxEYGIhq1arByckJ06ZNQ0hIiLi8Mrl3mTJl8M8//+DXX39FzZo1YWRkhFq1aqFz5875fixIM0gEVUZA/Aa8efMG5ubmeP36NczMzAo7HAW3b99GUFAQwsPD4ebmVtjhEBERfVMKOw9ISUlBfHw8XF1d5X5oT0pKQs8ePZD6FeNKqMpAXx+rIiNha2tbYNssKCdOnEC9evUQFxeHUqVKFXY4Xy2n8+Z7Vtif5S9hTk9ERJR/CjsPYE5PpF6q1Hf4ZAYRERFRIbO1tcWqyEi8fv26wLZpbm7+zVR6tm3bBlNTU7i5uSEuLg5DhgxB3bp1v4mGDCIiIiLSDszpifIfGzOIiIiINICtrS0rInn09u1b/O9//0NiYiKsra3RuHFjzJ07t7DDIiIiIqLvDHN6ovzFxgwiIiIi0mrdu3fP13E5iIiIiIiIqPDpFHYAREREREREREREREREuWFjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNN3CDoCIiIiIgKSkJLx+/brAtmdubg5bW1u1r1cikWDbtm1o3bq12tdNRERERKTJND2n9/HxgYeHB8LCwvIvKKJ8xMYMIiIiokKWlJSEnj0CkJqWXmDbNNDXw6rIKJUbNB4/foypU6di165dePDgAWxtbeHh4YHg4GA0atToi8snJCTA1dU123mnTp3CDz/8AABIS0tDWFgY1q1bh9u3b8PY2Bhly5ZF79698csvv0BPT0+luImIiIiI8pM25fRE2oqNGURERESF7PXr10hNS0cDDwFFTfN/ey+TgcMX0/H69WuVKj4JCQmoW7cuLCwsMHv2bFSuXBnp6enYt28fBgwYgBs3bii9rgMHDqBixYpy06ysrAB8bMjw9fXFpUuXMHnyZNStWxdmZmY4ffo05syZg6pVq8LDw0PpbRERERER5Tdtyem/Venp6bzh6TvAMTOIiIiINERRU8DaPP//5bVyFRQUBIlEgjNnzqBdu3YoU6YMKlasiGHDhuH06dNiuWfPnqFNmzYwNjaGm5sbduzYobAuKysr2Nvby/3LqnyEhYXh6NGjOHjwIAYMGAAPDw+ULFkSXbp0QUxMDNzc3HKM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b/VcZX6Xd9p67FiPIyLKX4q3PRWgYcOGYfny5Vi9ejViY2PRv39/vHv3Dj169AAAdO/eXW4wQT8/PyxZsgQbNmxAfHw89u/fj3HjxsHPz0+jBnwizSWVShEYGIiYmBiEhobi+vXreP/+Pa5fv47Q0FDExMQgMDCQ5xMVOn19fbRt2xYvX75Ely5dsGvXLjx79gy7du1Cly5d8PLlS7Rt25aDxn1DeH36Pujp6QH4+GMukbLS0tIAQGM//8zpiYiyx/wu77T12LEe932RyWSFHQLRN0GVm7cKdcyMn3/+GU+fPsX48ePx+PFjeHh4YO/eveIAgomJiXKt72PHjoVEIsHYsWPx4MED2NjYwM/PD1OnTi2sXSAt5OXlhXHjxiEiIgJDhgwRp9vb22PcuHHw8vIqxOiI/k9gYCAAYOvWrQgLCxOnS6VSdOjQQZxP3w5en759UqkUFhYWSEpKAgAYGxtDIpEUclSkyWQyGZ4+fQpjY2OFgUQ1BXN6IqKcMb/LO209dqzHffv09fWho6ODhw8fwsbGBvr6+szpifJIEAQ8ffoUEolEvPkvN4VeIxo4cGCOj6BHR0fLvdbV1UVoaChCQ0MLIDL6lnl5eaFOnTq4evUqnj9/DisrK1SqVEnj7uogCgwMREBAAHbu3ImHDx/CwcEBfn5+vJPnG8br07cvaxyBrAYNoi/R0dFBiRIlNLqSzJyeiChnzO/yTluPHetx3zYdHR24urri0aNHHMydSA0kEgmKFy+u1LW90BsziAqLVCqFu7t7YYdB9EX6+vpo165dYYdBBYjXp2+bRCJBsWLFYGtri/T09MIOh7RA1t1/RESkvZjf5Z22HjvW475t+vr6KFGiBDIyMpCZmVnY4RBpNT09PaUbqdmYQURERFQIpFKpxt9VSERERERE2cvqFkeZrnGISD14ixcREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUZjYwYREREREREREREREWk0NmYQEREREREREREREZFGY2MGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUb7qsaMlJQUdcVBRERERESFgDk9ERERERFpA5UbM2QyGSZPngxHR0eYmpri7t27AIBx48Zh5cqVag+QiIiIiIjUizk9ERERERFpG5UbM6ZMmYKoqCjMmjUL+vr64vRKlSphxYoVag2OiIiIiIjUjzk9ERERERFpG5UbM37//XdERESga9eukEql4nR3d3fcuHFDrcEREREREZH6MacnIiIiIiJto3JjxoMHD1C6dGmF6TKZDOnp6WoJioiIiIiI8g9zeiIiIiIi0jYqN2ZUqFABx44dU5i+efNmVK1aVS1BERERERFR/mFOT0RERERE2kZX1QXGjx8Pf39/PHjwADKZDFu3bsXNmzfx+++/4++//86PGImIiIiISI2Y0xMRERERkbZR+cmMVq1aYefOnThw4ABMTEwwfvx4xMbGYufOnWjSpEl+xEhERERERGrEnJ6IiIiIiLSNyk9mAICXlxf279+v7liI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kxP27duHoUOHokqVKnB0dMSQIUPwv//9T9XdoO9cYGAgAgICsHPnTjx8+BAODg7w8/PjXQakcXiufh0vLy/UqVMHV69exfPnz2FlZYVKlSrxbs18wHOVSLswpydtx+94ou+DtuaYjJuIKH+o3Jgxbdo0vH37FgAwdepUdO/eHf3794ebmxtWrlypcgADBw7EwIEDs50XHR2tMK127do4ffq0ytsh+py+vj7atWtX2GEQfRHP1a8jlUrh7u5e2GF8F3iuEmkP5vT0LeB3PNH3QVtzTMZNRKR+KjdmVK9eXfzb1tYWe/fuVWtARERERESUv5jTExERERGRtlF5APCcnD9/Hi1btlTX6oiIiIiIqIAxpyciIiIiIk2lUmPGvn37EBISgl9//RV3794FANy4cQOtW7dGjRo1IJPJ8iVIIiIiIiJSD+b0RERERESkjZTuZmrlypXo06cPLC0t8fLlS6xYsQLz5s3DoEGD8PPPP+Pq1asoX758fsZKRERERERfgTk9ERERERFpK6WfzJg/fz5mzpyJZ8+e4c8//8SzZ88QHh6OK1euYOnSpaz0EBERERFpOOb0RERERESkrZRuzLhz5w46dOgAAGjbti10dXUxe/ZsFC9ePN+CIyIiIiIi9WFOT0RERERE2krpxowPHz7A2NgYACCRSGBgYIBixYrlW2BERERERKRezOmJiIiIiEhbKT1mBgCsWLECpqamAICMjAxERUXB2tparszgwYPVFx0REREREakVc3oiIiIiItJGSjdmlChRAsuXLxdf29vbY82aNXJlJBIJKz5ERERERBqKOT0REREREWkrpRszEhIS8jEMIiIiIiLKb8zpiYiIiIhIWyk9ZgYREREREREREREREVFhYGMGERERERERERERERFpNDZmEBERERERERERERGRRmNjBhERERERERERERERaTQ2ZhARERERERERERERkUbLU2PGnTt3MHbsWHTu3BlJSUkAgD179uDatWtqDY6IiIiIiPIHc3oiIiIiItImKjdmHDlyBJUrV0ZMTAy2bt2K5ORkAMClS5cQGhqq9gCJiIiIiEi9mNMTEREREZG2UbkxY9SoUZgyZQr2798PfX19cXrDhg1x+vRptQZHRERERETqx5yeiIiIiIi0jcqNGVeuXEGbNm0Uptva2uLZs2dqCYqIiIiIiPIPc3oiIiIiItI2KjdmWFhY4NGjRwrTL1y4AEdHR7UERURERERE+Yc5PRERERERaRuVGzM6deqE//3vf3j8+DEkEglkMhlOnDiBkJAQdO/ePT9iJCIiIiIiNWJOT0RERERE2kblxoxp06ahXLlycHJyQnJyMipUqID69eujTp06GDt2bH7ESEREREREasScnoiIiIiItI2uqgvo6+tj+fLlGD9+PK5cuYLk5GRUrVoVbm5u+REfERERERGpGXN6IiIiIiLSNio3ZmRxcnKCk5MTMjMzceXKFbx8+RJFixZVZ2xERERERJSPmNMTEREREZG2ULmbqeDgYKxcuRIAkJmZCW9vb1SrVg1OTk6Ijo5Wd3xERERERKRmzOmJiIiIiEjbqNyYsXnzZri7uwMAdu7cibt37+LGjRsYOnQoxowZo/YAiYiIiIhIvZjTExERERGRtlG5MePZs2ewt7cHAOzevRsdO3ZEmTJl0LNnT1y5ckXtARIRERERkXoxpyciIiIiIm2jcmOGnZ0drl+/jszMTOzduxdNmjQBALx//x5SqVTtARIRERERkXoxpyciIiIiIm2j8gDgPXr0QMeOHVGsWDFIJBI0btwYABATE4Ny5cqpPUAiIiIiIlIv5vRERERERKRtVG7MmDBhAipVqoT79++jQ4cOMDAwAABIpVKMGjVK7QESEREREZF6MacnIiIiIiJto3JjBgC0b99eYZq/v/9XB0NERERERAWDOT0REREREWmTPDVmHDx4EAcPHkRSUhJkMpncvFWrVqklMCIiIiIiyj/M6YmIiIiISJuo3JgxceJETJo0CdWrVxf72CUiIiIiIu3BnJ6IiIiIiLSNyo0ZS5cuRVRUFLp165Yf8RARERERUT5jTk9ERERERNpGR9UF0tLSUKdOnfyIhYiIiIiICgBzeiIiIiIi0jYqN2b07t0bf/zxR37EQkREREREBYA5PRERERERaRuVu5lKSUlBREQEDhw4gCpVqkBPT09u/rx589QWHBERERERqR9zeiIiIiIi0jYqN2ZcvnwZHh4eAICrV6/KzePAgUREREREmo85PRERERERaRuVGzMOHz6cH3EQEREREVEBYU5PRERERETaRuUxMz7133//4b///lNXLEREREREVMCY0xMRERERkTZQuTFDJpNh0qRJMDc3h7OzM5ydnWFhYYHJkydDJpPlR4xERERERKRGzOmJiIiIiEjbqNzN1JgxY7By5UrMmDEDdevWBQAcP34cEyZMQEpKCqZOnar2IImIiIiISH2Y0xMRERERkbZRuTFj9erVWLFiBX766SdxWpUqVeDo6IigoCBWfIiIiIiINBxzeiIiIiIi0jYqdzP14sULlCtXTmF6uXLl8OLFC7UERURERERE+Yc5PRERERERaRuVGzPc3d2xaNEihemLFi2Cu7u7WoIiIiIiIqL8w5yeiIiIiIi0jcrdTM2aNQstWrTAgQMHULt2bQDAqVOncP/+fezevVvtARIRERERkXoxpyciIiIiIm2j8pMZ3t7euHXrFtq0aYNXr17h1atXaNu2LW7evAkvL6/8iJGIiIiIiNSIOT0REREREWkblZ/MAAAHBwcOCkhEREREpMWY0xMRERERkTbJU2PGy5cvsXLlSsTGxgIAKlSogB49esDS0lKtwRERERERUf5gTk9ERERERNpE5W6mjh49ChcXFyxYsAAvX77Ey5cvsWDBAri6uuLo0aP5ESMREREREakRc3oiIiIiItI2Kj+ZMWDAAPz8889YsmQJpFIpACAzMxNBQUEYMGAArly5ovYgiYiIiIhIfZjTExERERGRtlH5yYy4uDgMHz5crPQAgFQqxbBhwxAXF6fW4IiIiIiISP2Y0xMRERERkbZRuTGjWrVqYr+6n4qNjYW7u7tagiIiIiIiovzDnJ6IiIiIiLSNyt1MDR48GEOGDEFcXBx++OEHAMDp06exePFizJgxA5cvXxbLVqlSRX2REhERERGRWjCnJyIiIiIibaNyY0bnzp0BACNHjsx2nkQigSAIkEgkyMzM/PoIiYiIiIhIrZjTExERERGRtlG5MSM+Pj4/4iAiIiIiogLCnJ6IiIiIiLSNyo0Zzs7O+REHEREREREVEOb0RERERESkbVQeAHz16tXYtWuX+HrkyJGwsLBAnTp1cO/ePbUGR0RERERE6secnoiIiIiItI3KjRnTpk2DkZERAODUqVNYtGgRZs2aBWtrawwdOlTtARIRERERkXoxpyciIiIiIm2jcjdT9+/fR+nSpQEA27dvR/v27REYGIi6devCx8dH3fEREREREZGaMacnIiIiIiJto/KTGaampnj+/DkA4J9//kGTJk0AAIaGhvjw4UOegli8eDFcXFxgaGiIWrVq4cyZM0ott2HDBkgkErRu3TpP26XvW3JyMsaPH48+ffpg/PjxSE5OLuyQiLLFc5WIiNRN3Tk983kiIsov2lof0ta4iYg0mcpPZjRp0gS9e/dG1apVcevWLTRv3hwAcO3aNbi4uKgcwMaNGzFs2DAsXboUtWrVQlhYGHx9fXHz5k3Y2trmuFxCQgJCQkLg5eWl8jaJBgwYgFu3bomvExIS0KZNG5QpUwaLFy8uxMiI5PFcJSKi/KDOnJ75PBER5RdtrQ9pa9xERJpO5SczFi9ejNq1a+Pp06fYsmULrKysAADnzp1D586dVQ5g3rx56NOnD3r06IEKFSrg/7F31+FRXH0bx++NEwLB3b24S/Hi7l4gENxdi7t7kWCF4hC8BAlFixT3YMWKBA8hENnN+wdv9iEtbaFNspvk+7mu53rI7Mzsb+khe2buOecsWLBAzs7OWrp06V8eYzQa1aJFC40aNUqZMmX64vdE7BbWqTAYDKpYsaIWLlyoihUrymAw6Pr16+rataulSwQk0VYBAJEnIvv09OcBAJEhul4PRde6ASA6+OKRGQkSJNDcuXP/tH3UqFFf/OZBQUE6ffq0Bg8ebN5mY2OjihUr6tixY3953OjRo5UsWTK5u7vr8OHDX/y+iL38/f3NnYpt27bJyclJkjRw4ED17NlTtWvX1vXr1+Xv7y8XFxcLV4vYjLYKAIhMEdWnpz8PAIgM0fV6KLrWDQDRxReHGZJ0+PBhLVy4ULdv39aGDRuUOnVqrVy5UhkzZlSpUqU++zzPnj2T0WhU8uTJw21Pnjy5rl279sljjhw5oiVLlujcuXOf9R6BgYEKDAw0/+zn5/fZ9f0dX19fvX79OkLO9bF79+6F+/+IFhwcLHt7+wg/b2TX7erq+rfTFHyuyZMnS5IqVKhg7lSEcXJyUvny5bV//35NnjxZo0eP/s/vZy18fHz04MGDCD3n48ePJUknT56MlP/uzs7OSpIkSYSfl7YaM7x//17379+P1PdImzbtn/7u/6voWjeAmCki+vRR0Z+X6NP/0bNnzxQQEBDh543s/l2aNGmUPXv2f9yP78s/i65tNaZff9JWI090vR6KrnUDQHTxxWHGpk2b1LJlS7Vo0UJnzpwxX1S8fv1a48eP108//RThRYZ58+aNWrZsKQ8Pj8++yTlhwoR/NWrk7/j6+qptmzYKDAqK0PN+bOLEiZFyXoOk0Eg58weRVbejg72WLlv+n28SP3r0SJLUqFGjT77eoEED7d+/37xfTODr66uePXrIaDJFyvmXL18eKeeNbLTV6O3+/fvq0qVLpL7H999/r6xZs0boOaNr3QBiHkv16f9Nf16iTx/VIqt/Z2tjoxUrV/5jP4nvy/Cic1uN6deftNXIE12vh6Jr3QAQXXxxmDF27FgtWLBArVq10tq1a83bS5YsqbFjx37RuZIkSSJbW1s9efIk3PYnT54oRYoUf9r/1q1bunPnjmrVqmXeZvr/G7R2dnby8fFR5syZwx0zePBg9enTx/yzn5+f0qZN+0V1/tHr168VGBSkTtkLKpVzvP90rqh0/sUTbbp7TeXzhyphNBrN+NJf+vlcsF6/fv2fbxCnTJlSd+7c0YYNGzRw4MA/vb5p0ybzfjHF69evZTSZ1CB9DiV1crZ0OZ/l+usX2v/4Dm01lrXVL5E2bVp9//33n73/vXv3NHHiRA0aNEjp0qX77PeIaF9S97+pOew9AOCfRFSfPir68xJ9+o+F9ekLZwtVvOjRtZMkvQmQTl03fVY/ie/L8KJ7W43Jffro2ieNDqLr9VB0rRsAoosvDjN8fHxUpkyZP213dXXVq1evvuhcDg4OKlSokLy9vVW3bl1JHy5mvL291a1btz/tnyNHDl28eDHctu+++05v3rzRrFmzPvkl7+joKEdHxy+q63Olco6nDC4JIuXckeFhwBtJUkIXKYmrhYuxkAEDBqhevXry9vZWz549ww37fP/+vX7++WfzfjFNvkTJo1V73f/4Dm01lrbVz+Hk5PSvnlBLly6dRZ9s+zd1W7pmADFTRPXpo6I/L9Gn/1hYnz5dsujVT3r2Wjp1/fP25fvy06JrW43Jffro2ieNDqLr9VB0rRsAoosvDjNSpEihmzdvKkOGDOG2HzlyRJkyZfriAvr06aPWrVurcOHCKlq0qGbOnKm3b9+qTZs2kqRWrVopderUmjBhgpycnJQ7d+5wxydIkECS/rQd+BQXFxdly5ZN169fV+3atVW+fHk1aNBAmzZt0s8//6zQ0FBly5aNhbhgcbRVAEBkisg+Pf15AEBEi67XQ9G1bgCILr44zGjfvr169uyppUuXymAw6OHDhzp27Jj69eunYcOGfXEBTZo00dOnTzV8+HA9fvxY+fPnl5eXl3kRwXv37snGxuaLzwv8lXnz5qlr1666fv269u/fr/3795tfy5Ytm+bNm2fB6oD/oa0CACJLRPbp6c8DACJDdL0eiq51A0B08MVhxqBBg2QymVShQgUFBASoTJkycnR0VL9+/dS9e/d/VUS3bt0+OQxdkg4cOPC3x0bXxYdhWfPmzZO/v78mT56sR48eKWXKlBowYABPR8Dq0FYBAJEhovv09OcBAJEhul4PRde6AcDafXGYYTAYNHToUPXv3183b96Uv7+/cubMKRcXF717905x4sSJjDqBCOfi4qLRo0dbugzgH9FWAQARjT49ACC6iK7XQ9G1bgCwZv96vLeDg4Ny5sypokWLyt7eXtOnT1fGjBkjsjYAAAAAkYg+PQAAAIDo4rPDjMDAQA0ePFiFCxfW119/rS1btkiSli1bpowZM2rGjBnq3bt3ZNUJAAAA4D+iTw8AAAAguvrsaaaGDx+uhQsXqmLFivrll1/UqFEjtWnTRsePH9f06dPVqFEj2draRmatAAAAAP4D+vQAAAAAoqvPDjM2bNigFStWqHbt2rp06ZLy5s2rkJAQnT9/XgaDITJrBAAAABAB6NMDAAAAiK4+e5qpBw8eqFChQpKk3Llzy9HRUb179+aiBwAAAIgm6NMDAAAAiK4+O8wwGo1ycHAw/2xnZycXF5dIKQoAAABAxKNPDwAAACC6+uxppkJDQ+Xm5iZHR0dJ0vv379WpUyfFjRs33H6enp4RWyEAAACACEGfHgAAAEB09dlhRuvWrcP9/O2330Z4MQAAAAAiD316AAAAANHVZ4cZy5Yti8w6AAAAAEQy+vQAAAAAoqvPXjMDAAAAAAAAAADAEggzAAAAAAAAAACAVSPMAAAAAAAAAAAAVo0wAwAAAAAAAAAAWDXCDAAAAAAAAAAAYNUIMwAAAAAAAAAAgFUjzAAAAAAAAAAAAFaNMAMAAAAAAAAAAFg1wgwAAAAAAAAAAGDVCDMAAAAAAAAAAIBVI8wAAAAAAAAAAABWjTADAAAAAAAAAABYNcIMAAAAAAAAAABg1QgzAAAAAAAAAACAVSPMAAAAAAAAAAAAVo0wAwAAAAAAAAAAWDXCDAAAAAAAAAAAYNUIMwAAAAAAAAAAgFUjzAAAAAAAAAAAAFaNMAMAAAAAAAAAAFg1wgwAAAAAAAAAAGDVCDMAAAAAAAAAAIBVI8wAAAAAAAAAAABWjTADAAAAAAAAAABYNcIMAAAAAAAAAABg1QgzAAAAAAAAAACAVSPMAAAAAAAAAAAAVo0wAwAAAAAAAAAAWDXCDAAAAAAAAAAAYNUIMwAAAAAAAAAAgFUjzAAAAAAAAAAAAFaNMAMAAAAAAAAAAFg1wgwAAAAAAAAAAGDVCDMAAAAAAAAAAIBVI8wAAAAAAAAAAABWjTADAAAAAAAAAABYNcIMAAAAAAAAAABg1QgzAAAAAAAAAACAVSPMAAAAAAAAAAAAVo0wAwAAAAAAAAAAWDXCDAAAAAAAAAAAYNUIMwAAAAAAAAAAgFUjzAAAAAAAAAAAAFaNMAMAAAAAAAAAAFg1wgwAAAAAAAAAAGDVCDMAAAAAAAAAAIBVI8wAAAAAAAAAAABWjTADAAAAAAAAAABYNcIMAAAAAAAAAABg1QgzAAAAAAAAAACAVbOKMGPevHnKkCGDnJycVKxYMZ08efIv9/Xw8FDp0qWVMGFCJUyYUBUrVvzb/YG/4uvrq8aNG6t69epq3LixfH19LV0S8Em0VUQXQUFB2rRpk+bMmaNNmzYpKCjI0iUBiCL05wEAkSW6Xg9F17rp0wOwZnaWLmDdunXq06ePFixYoGLFimnmzJmqUqWKfHx8lCxZsj/tf+DAATVr1kxff/21nJycNGnSJFWuXFmXL19W6tSpLfAJEB3VqFEj3Bfyy5cv1aJFCzk4OGjnzp0WrAwIj7aK6GLRokXy9PSU0Wg0b/Pw8FD9+vXVoUMHC1YGILLRnwcARJboej0UXeumTw/A2ll8ZMb06dPVvn17tWnTRjlz5tSCBQvk7OyspUuXfnL/VatWqUuXLsqfP79y5MihxYsXy2QyydvbO4orR3T1caciRYoU+u6775QiRQpJH55AqFGjhiXLA8xoq4guFi1apA0bNih+/Pjq3bu31q1bp969eyt+/PjasGGDFi1aZOkSAUQi+vMAgMgQXa+Homvd9OkBRAcWHZkRFBSk06dPa/DgweZtNjY2qlixoo4dO/ZZ5wgICFBwcLASJUoUWWUiBvH19TV3KjZs2KAECRJIksqWLatXr16pUaNGCgoKkq+v7yefJASiCm0V0UVQUJA8PT2VMGFCrV69WnZ2H7oW1atXV+XKldW8eXN5enrKzc1NDg4OFq4WQESjPw8AiAzR9XooutZNnx5AdGHRMOPZs2cyGo1Knjx5uO3JkyfXtWvXPuscAwcOVKpUqVSxYsVPvh4YGKjAwEDzz35+fv++4D94GPAmws4VFZ6+D5AkvfS3cCFfKCLr7datm6QPT0eEdSrCJEiQQMmTJ9eTJ0/UrVs3rV+/PuLeGP8KbTX2tVVfX1+9fv06ws977969cP8fkYKDg2Vvbx/h543MmiXJ1dU1Qi6gtm/fLqPRKDc3N/NFTxg7Ozu1bt1aM2fO1Pbt29WgQYP//H4ArEtU9Ocl+vQfC+vTR1cR/b0WXb4vI0J0bavRtU8fGW0qMturNbXViBBdr4eia9306QFEFxZfM+O/mDhxotauXasDBw7Iycnpk/tMmDBBo0aNipT3X+BzJlLOG9l+PmewdAkW4+//oWfarl27T77u5uamSZMmmfeDZdFWY1db9fX1Vds2bRQYiQvMTZw4McLPaZAUGuFn/Z/IqFmSHB3stXTZ8v980fvw4UNJUvHixT/5erFixcLtBwAf+5z+vESfPiYIeC9JoZH2vRZZ53VwsNeyCPi+jAjRta1Gzz595LVVKXLaqzW11YgQXa+Homvd9OkBRBcWDTOSJEkiW1tbPXnyJNz2J0+emOcT/CtTp07VxIkTtW/fPuXNm/cv9xs8eLD69Olj/tnPz09p06b9b4X/v07ZCyqVc7wIOVdUOP/iiTbdvaby+UOV0MXS1Xy+l/4R1wF2cXHRy5cvtXjxYpUtW/ZPry9fvty8HyyPthq72urr168VGBQUrX63Ru/fq8F6/fr1f77gTZUqlSTp+PHjql69+p9eP3HiRLj9AMQsUdGfl+jTfyzsuye6CQyRJINcSppk5xqZjwFEnJDXBvkfjZjvy4gQXdtqdOsn3fOVTl2nrVpadL0eiq5106cHEF1YNMxwcHBQoUKF5O3trbp160qSefG/sKF5nzJ58mSNGzdOu3fvVuHChf/2PRwdHeXo6BiRZZulco6nDC4JIuXckSFsWHJCFymJq4WLsZC5c+eqRYsWevz4sV69ehVu2OerV6/MF+Jz5861UIX4GG01drbV6PS7ld+rUq1ateTh4aHly5ercuXK4Yalh4SE6IcffpCtra1q1aplwSoBRJao6M9L9Ok/Ft2mGvojO9dQ2SW2dBWfy7puZEfXthrd+klh00zRVi0rul4PRde66dMDiC4sPs1Unz591Lp1axUuXFhFixbVzJkz9fbtW7Vp00aS1KpVK6VOnVoTJkyQJE2aNEnDhw/X6tWrlSFDBj1+/FjSh1Tb2pJtWJ9kyZLJwcFBQUFBatSokZInTy43NzctX77c3KlwcHCIMU+zIPqirSK6cHBwUP369bVhwwY1b95crVu3VrFixXTixAn98MMPevnypRo1asRCgUAMRn8eABDRouv1UHStmz49gOjC4mFGkyZN9PTpUw0fPlyPHz9W/vz55eXlZV5E8N69e7KxsTHvP3/+fAUFBalhw4bhzjNixAiNHDkyKktHNLVz507VqFFDQUFBevLkiSZNmmR+zcHBQTt37rRgdcD/0FYRXXTo0EGS5OnpqZkzZ5q329raqlGjRubXAcRM9OcBAJEhul4PRde66dMDiA4sHmZIUrdu3f5yGPqBAwfC/Xznzp3ILwgx3s6dO+Xr66tu3brJ399fLi4umjt3rtU9HQHQVhFddOjQQW5ubtq+fbsePnyoVKlSqVatWjy9BcQS9OcBAJEhul4PRde66dMDsHZWEWYAlpAsWTKtX7/e0mUA/4i2iujCwcFBDRo0sHQZAAAAiEGi6/VQdK2bPj0Aa2bzz7sAAAAAAAAAAABYDmEGAAAAAAAAAACwaoQZAAAAAAAAAADAqhFmAAAAAAAAAAAAq0aYAQAAAAAAAAAArBphBgAAAAAAAAAAsGqEGQAAAAAAAAAAwKoRZgAAAAAAAAAAAKtGmAEAAAAAAAAAAKwaYQYAAAAAAAAAALBqhBkAAAAAAAAAAMCqEWYAAAAAAAAAAACrRpgBAAAAAAAAAACsGmEGAAAAAAAAAACwaoQZAAAAAAAAAADAqhFmAAAAAAAAAAAAq0aYAQAAAAAAAAAArBphBgAAAAAAAAAAsGqEGQAAAAAAAAAAwKoRZgAAAAAAAAAAAKtGmAEAAAAAAAAAAKwaYQYAAAAAAAAAALBqhBkAAAAAAAAAAMCqEWYAAAAAAAAAAACrRpgBAAAAAAAAAACsGmEGAAAAAAAAAACwaoQZAAAAAAAAAADAqhFmAAAAAAAAAAAAq0aYAQAAAAAAAAAArBphBgAAAAAAAAAAsGqEGQAAAAAAAAAAwKoRZgAAAAAAAAAAAKtGmAEAAAAAAAAAAKwaYQYAAAAAAAAAALBqhBkAAAAAAAAAAMCqEWYAAAAAAAAAAACrRpgBAAAAAAAAAACsGmEGAAAAAAAAAACwaoQZAAAAAAAAAADAqhFmAAAAAAAAAAAAq0aYAQAAAAAAAAAArBphBgAAAAAAAAAAsGqEGQAAAAAAAAAAwKoRZgAAAAAAAAAAAKtGmAEAAAAAAAAAAKwaYQYAAAAAAAAAALBqhBkAAAAAAAAAAMCqEWYAAAAAAAAAAACrRpgBAAAAAAAAAACsGmEGAAAAAAAAAACwaoQZAAAAAAAAAADAqhFmAAAAAAAAAAAAq0aYAQAAAAAAAAAArBphBgAAAAAAAAAAsGqEGQAAAAAAAAAAwKoRZgAAAAAAAAAAAKtGmAEAAAAAAAAAAKwaYQYAAAAAAAAAALBqhBkAAAAAAAAAAMCqEWYAAAAAAAAAAACrZhVhxrx585QhQwY5OTmpWLFiOnny5N/uv2HDBuXIkUNOTk7KkyePfvrppyiqFAAAAMAf0Z8HAAAAENksHmasW7dOffr00YgRI3TmzBnly5dPVapUka+v7yf3/+WXX9SsWTO5u7vr7Nmzqlu3rurWratLly5FceUAAAAA6M8DAAAAiAoWDzOmT5+u9u3bq02bNsqZM6cWLFggZ2dnLV269JP7z5o1S1WrVlX//v311VdfacyYMSpYsKDmzp0bxZUDAAAAoD8PAAAAICrYWfLNg4KCdPr0aQ0ePNi8zcbGRhUrVtSxY8c+ecyxY8fUp0+fcNuqVKmiLVu2fHL/wMBABQYGmn/28/P774X/v4cBbz5rvyCTUc/eB0TY+35KEidnOdjY/u0+T/+/hpf+/3y+EKP05l1EVPbX4sWR7P6+ZEn/q/fkyZO6d+/e3+4bEBCg3377LQKq+2sZM2aUs7PzP+6XPn16ZcmSJVJr+RKf016jY1uVIr+9xvS2KllXez3/4sk/ttdgk0mvgt5Hah0JHJxkb/P3mf/11y8kSfd8P6+9Go1SQOA/7/dfODtKtv/QXt/8/z9z2iqA/yoq+vNS7OjTf04fSYq+ffqw757A3w0KeR369zubJGPkdkll66x/fLTP6G+I3CK+kLW0VSlmX3/SVv+7mzdv6u7du/+4nzX1M21sbGQymT7rnJFd95f0jT+3bmv6u5bo0wP49ywaZjx79kxGo1HJkycPtz158uS6du3aJ495/PjxJ/d//PjxJ/efMGGCRo0aFTEF/z9XV1c5Ojhogc+ZCD1vVDBI+vmcdXV0PodB0vLlyy1dxhfJmzevpk2bZukyom17pa1GLWtor66urrK1sdGmu5/+/W/NTl2Pfm1Voq0C+O+ioj8v0af/o+jaT5Kkd+ctPjnAF7GxsZGrq6tFa6CtWgZt9d+bP3++Lly4YOkyvoizs7MCAiI5mYoE0bVu+vQA/i2LhhlRYfDgweGe/PLz81PatGn/0zmTJUumpcuW6fXr15+1f1BQ0N9enEWEFClSyMHB4R/3Cw4Olr29/T/uZ001S9LLly/15s0/P4lkTU8bpE+fPlLr+Fxf0l6t6b/757ZVKfLrjultVbKO9posWTLNmj1bDx48+Md9g4OD9fz580itJ3HixJ/VBo1Go2z/aSjE/7OmukNDQ2Uw/PPNBdoqAGsQG/r0X9LfiK59+mfPnn3WTTdr+r5MkyaNkiVLFqm1/BNra6tSzL/+pK3+N507d2Zkxn8QW0ZmAMC/YdEwI0mSJLK1tdWTJ0/CbX/y5IlSpEjxyWNSpEjxRfs7OjrK0dExYgr+SLJkyb6oo5ArV64IryGyRcea8Wlf0l6j63/36Fo3wsuePbuyZ89u6TIAAJ8pKvrzEn36/8Kaas6aNaulS4i2aKtRi7b632TJkoUphAAAkcKi4yYdHBxUqFAheXt7m7eZTCZ5e3urRIkSnzymRIkS4faXpL179/7l/gAAAAAiB/15AAAAAFHF4tNM9enTR61bt1bhwoVVtGhRzZw5U2/fvlWbNm0kSa1atVLq1Kk1YcIESVLPnj1VtmxZTZs2TTVq1NDatWt16tQpLVq0yJIfAwAAAIiV6M8DAAAAiAoWDzOaNGmip0+favjw4Xr8+LHy588vLy8v86KA9+7dk43N/waQfP3111q9erW+++47DRkyRFmzZtWWLVuUO3duS30EAAAAINaiPw8AAAAgKhhCQ0NDLV1EVPLz85Orq6tev36t+PHjW7ocAAAQhegHADED/5YBAIi96AcAsZdF18wAAAAAAAAAAAD4J4QZAAAAAAAAAADAqhFmAAAAAAAAAAAAq0aYAQAAAAAAAAAArBphBgAAAAAAAAAAsGqEGQAAAAAAAAAAwKoRZgAAAAAAAAAAAKtGmAEAAAAAAAAAAKwaYQYAAAAAAAAAALBqhBkAAAAAAAAAAMCq2Vm6gKgWGhoqSfLz87NwJQAAIKqFff+H9QcARE/06QEAiL3o0wOxV6wLM968eSNJSps2rYUrAQAAlvLmzRu5urpaugwA/xJ9egAAQJ8eiH0MobEsxjSZTHr48KHixYsng8Fg6XJiDD8/P6VNm1b3799X/PjxLV0O8Jdoq4guaKuRIzQ0VG/evFGqVKlkY8Nsm0B0RZ8+cvDdg+iCtorogrYaOejTA7FXrBuZYWNjozRp0li6jBgrfvz4fEEjWqCtIrqgrUY8nt4Coj/69JGL7x5EF7RVRBe01YhHnx6InYgvAQAAAAAAAACAVSPMAAAAAAAAAAAAVo0wAxHC0dFRI0aMkKOjo6VLAf4WbRXRBW0VABDV+O5BdEFbRXRBWwWAiBXrFgAHAAAAAAAAAADRCyMzAAAAAAAAAACAVSPMAAAAAAAAAAAAVo0wAwAAAAAAAAAAWDXCDAAAAAAAAAAAYNUIMwAAAAAAAAAAgFUjzAAAAAAARBmTyRTu59DQUAtVAgAAgOiEMAMAAES6P964+uPPAIDYwWQyycbmw2Xonj179Ntvv8lgMFi4KsRGfwzR6JsAAGD9CDNgUWEdyCtXrsjX19fC1QBf7lNPEnIhBIQXGhpqvnG1evVqSZKNjQ3/VgAglvn4+2Dw4MHq06ePtm/frnfv3jE6A1EqNDTUHKJNnTpVp06dom8Cq8XvRwD4H8IMWExYB3Lz5s2qVq2aFi5cKH9/f0uXBXy2sDbs7e2tIUOGqEePHrpz5475Ih3Ah3Av7GbB9evX1aVLF9WvX18SgQYAxDZh3wcjRoyQh4eHFi5cKHd3d8WJE4fRGYgyH/dN7ty5o1WrVqlmzZq6dOkSfRNYlbAQ4927d5/cDgCxEXfcYDEGg0F79uxRixYtNGzYMLVq1UouLi6WLgv4bAaDQTt37lTNmjV1+vRp7d69W4UKFdKOHTu4CAIU/gncSZMmaeTIkUqYMKG2bNmi2rVrSyLQAICYztPTUw8fPjT/fOfOHXl5eWnlypUqWbKk3rx5o+PHj6tXr17asGEDN+kQ6cL6JkOHDlWbNm0UL148PX/+XOXKldO5c+fom8BqGAwG7dq1S02aNFGDBg20YMECvX37VgaDgd+VAGItwgxYjMlk0rp169S6dWu1a9dO6dOnN28HogN/f38dPHhQc+fO1e7du3XlyhXVq1dPzZs31/bt22nLiPXCnnqcOHGixo8fr5YtW2rlypUaN26cLly4oGrVqkki0ACAmGr9+vVq2rSpVqxYoSdPnkiS4sePr+fPn+vMmTP69ddf1a9fP3Xq1EknT55UkyZN9OOPP1q4asQGixcv1qxZszR+/HitX79eBw4cUNGiRVW+fHkCDViNX375RXXq1FGWLFn04sUL/fDDD+rWrZvevHlDoAEg1iLMgMUEBwfr9OnTihcvnnnbx0/xvn792lKlAf/o9OnTypIliw4dOqR06dJJkmxtbbV48WI1btxY3377LSM0AH0YFn/8+HH17dtX1apVU6lSpdSrVy9NmTJFZ8+eZcopAIjBGjdurO+++07z58/X0qVL9fvvvytRokRq0aKFli1bplKlSilZsmSaMGGCfvnlFzVo0EAnT560dNmIBW7duqWqVauqRIkSSpEihUqWLKl58+YpX758qlq1qi5fvkzfBBZ148YN/fLLL5o4caJmzJihffv2qXnz5vLx8VHXrl3NgQZtFEBsQ5iBKPXxkwOOjo4qXLiwLl++bH5SK4yPj48mT56s58+fR3WJwGf56quvVKxYMZ08eVLPnj2T9L9RRYsXL1aLFi1Ut25deXl5WbJMwOLixImjFy9e6NKlS+G21atXT1WqVGHKKQCIoQIDAyVJw4cPV6tWrbRkyRKtXLlS/v7+Gjx4sLy8vHTy5ElNnz5d1apVk8lk0qNHj5Q6dWoLV47YwMbGRr/++qv559DQUGXMmFHNmzeXr6+vypcvrzNnztA3gUXcuHFD7dq10+zZs5UwYUJJHx6c69ixo5o3b64bN26oR48e8vPzY71GALEOv/UQJcJCjD8u7FeoUCH5+Pho5cqVevLkifn11atXa/PmzQoODo7yWoHP4ezsrA0bNqh69erq06ePTp06Fa4juWDBAnXv3l2ZMmWyYJVA1Pqri/169erpwYMH2rt3r3mbnZ2d8ubNq0aNGun+/fuqUqWKJAINAIgJQkND5ejoKEmaO3eu4sSJo0ePHmncuHGaNWuWXr9+rSxZsihfvnx6+/atzpw5o1q1asnf31/9+vWzcPWISf6qT1G/fn25urpq1KhR5ifcJSlDhgxq166dqlSpom+++UZnz56VjY0N0/kgSqVIkUJFixaV0WjUTz/9ZG5/Dg4O6tixo1q1aqXjx49r4MCBtE0AsY4hlN98iGShoaEyGAw6dOiQNm3aJJPJpKxZs6pHjx6SpIEDB2rnzp1KmjSpMmfOrBcvXsjb21sHDx5U/vz5LVs8oP+14V9//VWnTp1SUFCQsmfPrqpVqyo0NFTVq1fXhQsXtHXrVhUuXNi8PxCbmEwmc6B38OBB+fr6KmnSpMqfP7+CgoJUt25dJUuWTG3btlXt2rXl5+enVq1aqXjx4sqVK5cGDhwoV1dXHTt2zMKfBAAQUcaMGaNp06Zp+fLlkqQ9e/Zo9erVGjBggNq1a6dkyZJp3bp1Wrt2rfz8/OTl5SV7e3sZjUbZ2tpatnhEex/3TdauXavr168rNDRUZcqUUfny5TVs2DDt379fxYoVU+/evRUSEqIePXooZcqUGj16tHr16qX169fr3Llzyps3r4U/DWKyT10/+vv7a8qUKdq6dauqVq2qMWPGyN7eXtKHKbuXL1+uSpUqKUOGDBaoGAAshzADUWLz5s1yd3dX5cqV5erqqk2bNqlZs2aaM2eOJGnVqlW6cOGCTp06pdy5c6tjx47KmTOnhasG/mfTpk3q0KGDSpUqpYCAAP3222+qW7eupk6dqvfv36tBgwa6fPmy1q5dq+LFi1u6XMBiBg0apDVr1ihlypR69eqV0qRJoxkzZsjBwUGdO3eWr6+v/P39FT9+fIWEhOjKlSuSpI0bN2rSpEnatGmTeR0aAED0Enbz2GQy6e3bt6pQoYIaNGiggQMHmvcZMmSIZs6cqWHDhqlr164yGo06f/68ypQpIxsbG4WEhMjOzs6CnwIxzYABA7Ry5UrVqFFDjx490pUrV9SrVy916dJFY8eO1a5du3Tq1CllyZJFTk5OOnv2rGxtbfX7779r6NChGjx4sLJnz27pj4EYKizIOHHihI4fPy6j0aiCBQuqXLlyevv2rSZMmKC9e/eqfPnyGjt2LL8fAcR6hBmIdGfOnDFfxHTq1Em3bt1SsWLF9OLFCzVt2lSrV68272s0GmVjY8NT7bAqV65cUeXKlTV06FB17txZZ8+eVZkyZdSuXTvNmDFD0oenY8qWLatXr17pzJkzcnJysnDVQNTz8PDQiBEjtHHjRn399dcaM2aMJkyYoA0bNphvINy6dUsHDhxQ0qRJ5e7ubr4gCw4OVmBgoFxcXCz8KQAA/8bHTxb/9ttvSpcunYoXL64mTZqoX79+ev/+vbl/VKNGDV24cEGtWrXSgAED5OrqKin8k/RARNi2bZu6d++u9evXq1ixYvrxxx/Vvn17LVq0SC1btpTJZFJQUJC8vb0VL148lSxZUra2tua2yCghRIVNmzapbdu2ypkzp96/f6/z589ryJAhGj16tN69e6fx48ebZ66YOXMmgQaAWI3fgIh0Pj4+atCggTp16qT79++rUqVKql+/vr755hs1b95cyZIl08yZMyWJjiKs0oMHD5Q6dWp17txZd+7cUd26dfXtt9+ag4yzZ8+qQIECOnDggJ48eUKQgVjrzJkzatGihb7++mt5enpq6tSpmjFjhmrUqKG3b9/K1tZWpUqVUqlSpczHhD2Ba29vbx46DwCIXj4OMrp06aKDBw/q7NmzypEjhxYtWqQ+ffrIyclJwcHBsre3V+rUqXX9+nXduHFD8ePHN5+HIAMR7c6dO8qVK5eKFSumjRs3qkuXLpoxY4ZatmwpPz8/+fj4qEiRIqpRo4b5mI8DDK5PEdmuX7+uHj16aNq0aWrbtq1CQkK0bt06ubu7y9bWVqNGjdLAgQP19u1bXb58WS9evFCyZMksXTYAWAxhBiJd3bp1lSVLFoWEhKh9+/YqU6aMFi1aJF9fX2XOnFmzZ89WQECAFi1aZOlSAUnhh/omSJBA9vb2Spw4sS5evKgaNWqoWrVqmjt3riTp5MmTWrNmjRIlSqT06dMrbdq0Fq4eiBp/nNs3NDRUAQEBKl26tI4cOaLWrVtr6tSp6tixo0JCQrRq1Sq5uLioUaNG4UILniwDgOgv7Pvg6dOnevbsmebOnSsHBweNGzdO1atXV6lSpbRv3z7Z29srNDRUL1++1KJFi1SuXDkZDAbWG0OE+NTIHjs7O2XIkEG7d+9WmzZtNGXKFHXq1EnShzVcLl26pCxZsihhwoTmYwgwEJX8/Pzk4uKiChUqyGAwyMHBQS1btpTRaJS7u7uqVq2qEiVKaNy4cQoICFDSpEktXTIAWBSPviBCfWrWsjhx4qhIkSLy9fXVkydP1Lp1a0mSk5OTSpcurQ0bNqh///5RXSrwlwwGg7y8vFS6dGk9ePBA8eLF0+nTp1WkSBFVr15dCxcuNF/krFq1StevXzdPjwDEBiaTyXzT6fLly5I+/LvJkiWLWrdurYoVK8rDw0MdO3aU9GEBw3Xr1un27duMvgCAGGr+/PkqUaKEnj17pty5c0uS0qRJo/nz5ysgIECZM2dWtWrVlC9fPp0/f16lS5eWwWAI950C/FsfBxl79uyRn5+fJClv3rxasGCB+WGksCAjICBAHh4e8vX1DRdkAFEtODhYN27c0IsXL2QwGBQSEiLpfw+F+vj4SJLixo1LkAEAIsxABAp7ourIkSOaMWOG+vTpo6tXr+rdu3eSPgwbv3nzpvbs2aNXr15p/PjxOn36tMqWLausWbNauHrgf549e6ZLly5p7NixqlChggoXLqypU6cqKChIadOm1aVLl3Tjxg31799fK1eu1KRJk5QgQQJLlw1EiY9vFowcOVJdunTR5s2bJUl9+vRRkyZNFCdOHBUtWlQvXrzQ/fv31bRpU71580aDBg2yZOkAgEhiNBqVIEECxY0bV9euXTNPHWVjY6PSpUvr6NGj6t27t4oXL646deroypUrsrOzM6+XB/wXoaGh5nY0dOhQde3aVcuXL1dQUJBKlSql+fPny9bWVs+fP9fJkyd14sQJ1atXT0+ePNHs2bPN5wAiW1g7O3v2rPbt26eQkBCVKFFCtWvX1oABA3Tt2jXzqGUnJyc5OzvzOxIA/oAFwBEhwoKMzZs3y93dXUWLFpWfn5/u3r2roUOHqmHDhkqWLJkWLFig7t27K126dPL395eXl5cKFChg6fIBMx8fH+XKlUupUqXSiBEj5O7ubn5t5syZGjdunGxsbJQiRQqZTCatWLGCNoxYadCgQVqyZIlWrVqlXLlyKXXq1JKkixcvqm/fvjpy5IhSpUplnqrt0KFDsre3ZyFNAIgBPjUt1Nu3b7Vnzx517dpVefLk0e7duyXJvE7GH4WtmQRElOHDh2v+/Pnatm2bcuTIEW7ExcyZMzVmzBjzmi1JkybV9u3b6ZsgyoT93vT09FTXrl3Vs2dPNWrUSJkzZ5anp6cWLFigwMBAjRs3Ti4uLtqwYYMWL16sEydOKEOGDJYuHwCsBmEGIswvv/yihg0baty4cWrTpo0CAwPl7OystGnTqkePHmrfvr3ixYunq1ev6s6dO8qbN6/55hdgaR9flA8aNEiTJ0/WkCFDNGrUqHAXN9evX9fTp0/l7OysNGnSMNQXsdKxY8fk5uamlStXqmjRogoICNCzZ890/PhxVahQQYkTJ5anp6eCgoKUMGFCVaxYUba2tty4AoAY4OMRevfu3VOcOHFka2urRIkSmQONPn36KG/evNq6daskKSgoSA4ODpYsGzHcvXv31LhxY40cOVJVq1aVr6+v7t27p/Xr16tChQqqUqWK7ty5Iz8/Pzk5OSlLliyysbGhb4IotXfvXtWvX19TpkyRm5ubnJyczK/9/PPPmjlzprZv367s2bPLaDRq3bp1PDgHAH9AmIEIYTQa9eOPP+rKlSuaNGmSbt++rQoVKqhmzZoyGAxatGiRJk6cqKZNmypFihSWLhcw+6sFJ/v3769Zs2ZpxYoVatq0qQUqA6zX4cOH1bBhQ/3666/y9/fX0qVLtXXrVj1//lyJEiXS6dOn/7SODE89AkD093GQMW7cOHl6eurdu3eKHz++FixYoPz58ysgIEBeXl4aMGCA8uTJY56KEIhMDx8+VJ48eTRx4kQVKlRIs2fP1tmzZ2U0GnX16lVt2rRJdevWDXfMpxYMByLKvHnz1KhRIyVLlkyhoaEyGo1yc3NTvHjxNH/+fL1580a3b9/W2rVrZW9vr8GDBytOnDg6d+6c4saNK1dXVyVLlszSHwMArA5hBv61P94E9vHxkdFoVMaMGVWrVi1lyJBBixcvltFoVKpUqWQymTRq1Ch17NiRG1qwCh+v83Lw4EG9fPlSuXPnlpubm6QP8//PmzdPK1euVOPGjS1bLGAhH/+uD/vzrVu31L59e/322296/fq1GjdurFKlSqlmzZrKnDmzJk+eHG6KNgBAzDJs2DAtWrRI8+bNU/LkyTV06FBdunRJ27ZtU6lSpRQQEKA9e/bo22+/VdeuXTVp0iRLl4wYLKx/8t133+n777/X+/fv1bFjR/PDdZUqVVLu3Lk1Y8YMS5eKWOLZs2cqW7astm7dqixZspi3d+zYUY8fP1afPn20YsUKPXjwQA8ePJCtra3ix4+v/fv3M4oNAP4B4ynxr4R1GA8cOKDffvtNbdq0Ufbs2SV9mIbH19dXgwcPliTdv39fFSpUUJIkSVS5cmWCDFiNsDlL3dzc1LBhQ/M6Lhs3btSOHTs0ffp02djYqG3btgoMDFTLli0tXTIQpT5+YtHPz0/v3r1T8uTJlTlzZs2ePVtHjx5VhgwZVKpUKcWNG1d+fn7KmjWrkidPbuHKAQCR5fDhw9qzZ482bNigMmXKaPv27bp48aIyZMigKlWqaPfu3SpVqpQqVaqknTt3qlSpUpYuGTHYx89m9u/fX02bNlVISIjy588v6cPI0KCgIKVNm9ZCFSI2SpIkic6ePSsHBwedPHlS6dOnV/LkyVWkSBGtWLFCVatWVd26ddWpUyfVqFFDS5Yskaenp6XLBoBogTADX+zjhas6duyoBg0aqEyZMsqcObMk6enTp/L19dWzZ8/0+PFj/fDDD3r+/LmWLl0abk5IwNJu376tgQMHatKkSercubNu3Lih4sWLq3z58uZ9pk6dKj8/P/Xv319169ZVvHjxLFgxEHX+OJXIrl27dPv2bVWqVEn9+vVTnjx5lDt3bklSYGCg7t+/ry5dushkMqlatWqWLB0AEIEePnyo58+fK0+ePJIkV1dX1axZU2XKlNGePXvUrl07jRs3TnXq1FHFihVVv359rVmzRhUqVFDZsmUlMdUgIoafn59CQ0MVP378cDMEGAwG7dixQ8+ePTOPsA4ICJCPj4+GDRum169fq0ePHhaqGrGVg4ODAgIC1LBhQyVLlkx79+5Vu3btVKFCBb18+VIFCxY031u5du2a7OzsFBISwsgMAPgHTDOFf+XQoUOqUaOG5syZY+4wfszNzU2bNm1SypQp9fLlS+3evVsFCxaM+kKBv3H8+HG5u7vr8uXLunv3rkqXLq3q1atrwYIFkj608zJlykiSnjx5wtPmiJWGDRumxYsXa8SIEcqbN6/q16+vwoULq2fPnqpUqZJCQ0O1fPlyrVq1Sm/fvtWhQ4dkb2/PjSsAiAHWr1+vBQsW6NmzZ+ratas6duwoSXr+/LkSJ06sunXrKkuWLJo6daqMRqPq16+vkydPKkeOHPr5558tXD1iEk9PT23atEkPHjzQrFmzlCdPHnM/Y9OmTWrUqJGWLFmiNm3ayGQyafv27Vq0aJHevXun3bt30zeBxfj4+KhGjRpKnTq1Nm7cqKRJk4Z7zcPDQ0uWLNGhQ4fMoTEA4K8xMgP/aObMmUqYMKFat24t6cPIjF9++UV16tSRm5ubXr16pV9//VU//PCDgoOD1aFDBy1fvly1atWSwWBQgQIFlDFjRgt/CuB/wp6AcXZ2VqpUqXT8+HE1btxY1apV07x58yRJ58+f19q1a5U4cWLlypWLIAOxkre3tzw9PbV+/XqVLl1ax48f16tXr3Tu3Dl99913srOzU/ny5ZUjRw41bdpUbdq0ka2trUJCQmRnRxcDAKKzpUuXqk+fPpo1a5ZKlCihbNmymV9LnDixnj9/rkuXLql27dqSpHfv3sne3l4bN27U119/bamyEQMtXbpUgwcP1siRI5UqVSrzFFKSdPHiRbVt21bz5s1TmzZtJEk2NjYqWLCg+vfvr9KlS9M3QZQIDQ1VaGiobGxsFBISIhsbG9nY2Ch79uzatWuXKlSooMaNG2vt2rVKnjy5Tpw4oRkzZujWrVs6cOAAQQYAfCZGZuBv+fr6avjw4erbt6+yZs1q3j506FDNmjVLXl5emjx5soKCguTo6Kjnz5/Lz89Px44dU9y4cS1YORDeHxesl6QHDx6odOnSunv3rtq1a6dFixaZX+vTp4/OnDmjjRs3KkmSJFFdLmAVzpw5o5MnT6pTp07as2ePmjVrplmzZqlatWrKnDmzihcvrh49eqh69ermY3jqEQCiv6NHj6pZs2YaO3asWrVqZd7+x/5Us2bN9PPPP2vgwIHatGmTgoOD9csvv8jW1jbcdIXAv+Xl5aXmzZtrzpw5atGihXl7WFu8deuWnj59quLFi//lOWiLiExXr15VhgwZFCdOHEnSzp07tWHDBt2/f1+NGjVSoUKFVKRIEV2/fl0VK1ZUlixZtGHDBiVOnFgnTpxQunTplDJlSgt/CgCIPggz8I8CAwPl6OioY8eO6cKFC+rYsaMCAgJUt25dnT9/XpUrV5abm5sqVKigy5cvq2HDhtq5c6cyZcpk6dIBSf+72Dl8+LCOHTumJEmSqGLFikqXLp2OHj2qihUrqmnTpnJzc1OcOHG0du1aLV26VIcPH+YJGcQan7rQDwgIkJ+fn+LHj6+6deuqZMmSGj58uEJDQ1WiRAmdPXtWnTt31qxZsyxUNQAgMkyfPl1bt26Vp6enEidO/KfXw/pWN27c0IgRI3Tr1i2lTZtWa9askb29PTeP8Z+ZTCYZDAbzelyzZ8+Wo6OjpcsCwtm5c6dq1aqlVatWqVmzZtq/f7+qV6+uFi1a6Pnz57p3756cnZ01dOhQVatWTTdu3FC1atXk4uKi/fv3K1GiRJb+CAAQ7TDOEn8p7CIlbOGq+fPn69y5c7Kzs5O7u7v27NmjW7dumRf+lqQVK1bI1dVVCRMmtGDlQHhhiwI2a9ZMWbJk0bt37zR79mz9+OOPKlmypDZu3Kju3btr3759ihcvnuLHj89QX8QqH990On/+vOLGjat48eIpefLkcnZ21suXL/X8+XOlSpVKBoNBQUFBypcvn2bNmqUiRYpYuHoAQETz9vZW3Lhx/zbIuH//vrJmzarVq1frxYsXSpgwoQwGA9P5IELY2NjIZDLpwIEDatiwoRwdHf80Miis//L8+XO5urrS7hDlatSoodatW6tz585ycHDQiRMnNG7cOPXt21fShzUYFy9erAkTJihlypTKnz+/duzYoSZNmujNmzeEGQDwL/C4DP6Rv7+/nJ2d9d1336lo0aJavHixeYHksCDD29tbvXr10qJFi7RgwQLCDFidgwcPavbs2Tp79qwWLVqkdOnSqVq1arp8+bJq1KihY8eOac+ePdq+fbu8vLzCzcULxHRhQcbgwYNVpUoVVapUSTVq1NDZs2clScHBwQoKCtK2bds0Y8YM1apVS6dPn1bRokVla2sro9FoyfIBABEsU6ZMun37th49evSn1wwGg4KDg+Xu7q65c+dKkhIlSiSDwaDQ0FBuKCNCmUwm+fn5SZK5jYWxsbHRq1ev5ObmJh8fH0uViFgqODhYkrRs2TI1bdpULVu21LZt28IFFGXKlJG7u7tevnypixcvSpJy5MihU6dOKX369BapGwCiO8IMfFLYUy87d+6Uu7u7jh8/rmzZsql///7Knj27VqxYIQ8PD0nSkydP9PPPP+v06dM6dOgQN4FhFcIudB48eKBHjx7pzp07Sps2raQPncrRo0crf/78qlKlii5fvqzkyZPrq6++UubMmZUgQQILVg5EnY9vCBw9elSrVq3SmjVrNG7cOGXNmlUlS5bUiRMnlCxZMv3444968OCB1q9fLxsbGx0/ftz81CRrZABAzJIzZ07duHFDXl5eev/+vaTw3xnPnz+Xi4tLuEXBJf1pfTLgS61fv16HDx+W0WiUwWBQ2bJl9dNPP+ngwYOSPrSxjx+iePr0qYKCgixVLmKxsOD29OnTWrBggXr27Knr16/r/Pnz8vf3N+9XtmxZJUuWTFu3bjVvs7e3j/J6ASCmIMzAJxkMBm3ZskWNGzdWjhw5FC9ePElS9uzZNWTIEGXLlk3Lli3T8uXLlTx5cvXr109btmxhWh5YDYPBIE9PTxUuXFi1atXSvn37wnUq8+fPrzFjxqhIkSLmBdmA2CbsptO8efN05MgR9ejRQ+XLl1fz5s01ceJE1alTR+XKldOxY8eUL18+7d+/X15eXtq1a5fs7e0VEhLCnOgAEAN17txZNWvWVP/+/eXp6akXL17IYDDIZDLp1atX6tChg/z9/VWhQgVLl4oYxMPDQ02bNlVQUJBsbW1lMBjUpEkTPX78WJMnT9axY8ckyfwQxZs3bzRw4EDFiRNHX331lSVLRyxkMBi0b98+lSpVSl5eXpowYYI6d+6s+fPna/PmzXr79q15X2dnZ6VJk0YsWQsA/x1jgPFJDx480NChQzV+/Hj17NnTvN1kMilbtmwaPHiwpkyZookTJ8rW1lYtW7a0YLXA/4SNKrp165Z69+6tIUOGyNHRUdu3b5ebm5sOHjyofPnySfoQaAwdOlROTk48SYhY5eM1Mi5duqTdu3drx44dGjhwoKQP/47Sp0+vSZMmyWAwqHLlytq1a5dKlSoV7hxMJQIAMU9YX2rWrFnq3Lmz3NzcVKNGDVWuXFm3b9/W6dOn9fz5c506dUq2trYs9o0IsXDhQnXr1k2bN28OF5JVqFBBs2bNUocOHfTs2TM1a9ZMFSpU0NmzZ7V8+XJzWwwbLUpbRFS5f/++tm/frgkTJqhq1aqSPjwgFBQUJHd3dx0+fFh58uTR3bt3dfDgQU2YMIFrTgCIAIZQomF8wvXr11W9enVt2rRJ+fLlMz9B8PGXr4+Pj2bPnq3+/fsrQ4YMFqoU+LMDBw7o9u3bunz5sqZNmyZJevjwobp166YDBw7o559/NgcakhQYGChHR0dLlQtYzHfffadnz56pUaNGmj17to4cOaITJ04oS5Ys4RZ4bdeunYKDg7V//35LlwwAiCCfc+PXaDRq2LBh2rt3r65fv64iRYqoQIECmjBhguzs7FjsGxFiyZIl6tKli9avX686deqYtw8cOFBjx46Vvb29tmzZojlz5ujYsWMKCQlRrly5lCdPHi1dulR2dnYyGo1Me4koc/bsWQ0aNEgPHjzQ1KlTVa1atXDXlN27d9e8efOUN29e1a1bV40bN1bOnDktXDUAxAyEGfikc+fOqXDhwtq9e7cqVKgQLsw4deqUgoODVaJECQUHBzPfI6xOixYttGbNGn399dfavXu34saNK+l/gcbRo0f1008/qVChQhauFIhaYQGFJO3Zs0c9evTQunXrlC9fPp09e1YDBw7U9evXtW/fvnCBxpMnT5Q0aVKedgSAGOjx48dKnjz53z4xHBwcrNevXytJkiTmbdw8RkQ4fvy4vv76a02bNk29e/c2b2/cuLEOHjyoM2fOKHXq1JI+rNUSEBCghw8fKlOmTEqSJIkMBgOhGqLcgwcP1KFDB3l7e6t3796aOHGiJCkoKEgODg6SJHd3d3l7e+vy5cvm61EAwH9HmAHzzaqrV6/q+fPnSp06tdKnT686derI3t5ew4cPD7eod8eOHRUYGKiFCxfyNDusUlBQkPr16ycPDw9t3rzZPOxXkh49eqSWLVvq9u3bunr1Km0YsdK6det0/Phx2dnZacqUKebtZ86c0dChQ+Xj46N9+/YpU6ZM4Y5j+gYAiP4+/l2+aNEiTZ8+XStWrFCRIkX+FGh8HIJ/znbgS7169Up169bV48ePtX37dmXNmlUNGzbU9evXtX37dqVPn94cnH2q3dE3gaU8fvxY3bp102+//abu3bvLzc1NUvhA4+HDh0qVKpUFqwSAmIcwA5KkLVu2qGXLlkqRIoXu37+vxYsX682bN1qzZo1cXV3Vrl07xY8fX9u3b9fy5ct16NAh5c6d29JlA+aLmpcvXyokJERJkyY1v9aiRQv99NNP2rRpk7755hvz9idPnig4OFhp0qSxRMmARRmNRpUsWVInT55U5cqV5eXlFe71M2fOaNiwYdq/f79u3rxpfhoSABD9fXzj18vLS3fv3lXnzp1VoUIFTZw4UQULFiSkQJTz9/dXrVq1dP/+fWXMmFFPnz7Vtm3blC5dunABhre3t4oXL85T7ohSH0+9+uzZM6VMmVIuLi5ycXHR/fv31b17d718+VJt27ZV69atJf0v0CD4BYCIR5gRy5lMJr169Uq1a9dWq1at9M0332jt2rUaPXq0Zs6cKScnJ+3fv18bN25UhgwZFDduXC1ZsiTcSA3A0jZv3qwJEybo+fPnatSokdzc3JQjRw5JUvPmzbVr1y55enqqfPnyFq4UsKywC6rAwEB9++23On78uCZMmKDGjRubnyCTPkz5sGHDBk2ePJkpRAAgBho8eLCWLl2qwYMH6/79+9q4caOSJEkiDw8PFShQgJtviFSfusH75s0bffvtt9q+fbt27dqlKlWqhHu9bNmysrW1lbe3N+0TUSasrW7evFmDBw/W27dvlSBBAlWtWlVdu3ZVhgwZdO/ePfXo0UNv3rxR48aN1bFjR0uXDQAxGmFGLBX2pfz+/XuFhoZq7Nix6tevnxImTChJmjFjhvr376+pU6eqdevWevfunSQpTpw45n0Aa3Dx4kVVq1ZN7dq1U5w4cTRt2jSVLVtWffv2VfHixSVJrVq10o8//qgDBw6oTJkyFq4YsKywqRoCAwNVp04dPXv2TEOGDFGtWrU+uQYSc6IDQMxy+fJlVahQQUuXLlX16tUlSb6+vipVqpTix4+vhQsXMkIDkebj0UEvX76UwWBQggQJJH0INOrWravffvtNmzdvVr58+SRJNWrU0O3bt3XhwgXZ29vztDsizcftM+zPXl5eatKkiUaNGqUOHTpo4sSJWrhwoSpWrKjRo0crc+bMun//vlq3bi1nZ2etWrVKrq6uFv4kABBzMblkLGUwGLR161bVrVtXhQsXlqenp+7fv29+vXfv3po6daoGDBigqVOnKl68eEqVKhVBBiwuNDRUH2ewdnZ2atq0qUaOHKmBAwdq165dunDhgqZMmaITJ05IklasWCF3d3clT57cUmUDVsPW1lZGo1GOjo7asmWLEidOrAkTJmjHjh0KDg7+5P4AgJjDZDLJYDCYpxEMCgpSsmTJtGfPHt2+fVvfffedzp49a+EqEdN4enrqxYsX5hvFw4cPV61atVSgQAHNmzdPvr6+ihcvnnl6qQYNGujixYuqXbu2bt68aQ4yQkJCCDIQaWxsbHTnzh29fv1aNjY28vX11dy5c9W/f3/16tVLb9++1YoVK5QtWzZdunRJw4cP1507d5Q2bVqtWLFCCxYsIMgAgEhGmBFLnTp1Sq1atVLGjBlVtGhR3bp1S0uXLtXdu3fN+/Tq1UtjxozR/PnzFRQUZMFqgfAMBoMOHjyo0aNHa9y4ceaRQ5JUqFAhrVq1ShcvXtS0adN05MgRSZKHh4eyZ89uqZIBqxIWaDg5OWnr1q1KkiSJevXqpV9++cXSpQEAItCnBuFnyJBBISEh8vT0lCQ5ODjIaDQqUaJEypYtmw4fPqwePXooICDgL88BfImdO3eqYcOGWrhwoQIDA7VgwQJ5eHioYcOGqlu3rnr37q0JEybozp07ihs3rnbu3Kn06dMrX758unXrli5dumQOMuzs7Cz9cRCDBQcHq23btvrqq6/06tUrJUuWTG5ubqpbt66ePXumsmXLqkqVKjp8+LC++eYbbdu2Td26ddOtW7eUJk0a1mQEgChAmBEL3bp1S9u3b9fgwYM1f/58LVu2TLNmzdKmTZu0YMGCcIHGwIEDdevWLSVOnNiCFQP/YzAY5OXlpfLly+vQoUNat26ddu7cqa1bt5r3KVy4sNauXav9+/dr0aJFev/+vQUrBqzTx4GGp6enGjRooFKlSlm6LABABAkbgSFJDx8+1Lt37xQcHKx48eLpu+++09KlSzVr1ixJH74TnJyclDdvXh04cEA+Pj6aMGGCJPEUPP6zGjVqaP78+Ro6dKgWLFigR48eacGCBerVq5dmzJihlStXaunSpZo5c6bu3r2ruHHjavPmzfruu+907tw5ggxEGXt7e82ePVtp0qRRyZIl9fLlSzVs2FC5c+fWmjVrlDZtWo0fP16SlDt3bmXIkEEuLi5ycnKycOUAEHvQG4hl/Pz81LRpU925c0cdOnQwb+/cubNMJpMmTJggW1tbubu7K2PGjJJknsMUsAZ37tzR4cOHNX/+fHXs2FFnzpxR7969tWTJEtnZ2alGjRqSpIIFC2rfvn2KFy8enUvEGl86h7Stra1CQkIUJ04cTZ8+XdKHJ9I+tXYGACB6CZvOZ8SIEdq5c6f8/PzUoUMHNWnSRO3atZOvr69Gjx6tX3/9VTly5NDevXv16tUrLVy4UF9//bXu3btn4U+AmODMmTO6d++eqlWrpqVLl6pt27ZydnaWh4eHeZ8mTZpIkjp27CgbGxt17dpVmTNn1ujRoyWJIANRIqwfnTNnTq1YsUJt27ZVpUqVtHfvXiVMmFC+vr569OiRTCaTJMnHx0fNmjVT586dmY4bAKIQIzNimfjx42vRokVKmDChDh48qEuXLplf69q1q7777jtNmzZNK1euVEhIiCSexoL1uHDhgtzd3bVt2zblzp1b0ofQYurUqfLz89P333+vXbt2mffPnz+/MmfObKlygSj18RO4wcHBnz0i6Y9rYhBkAED0FRoaar7RJn1YN2z+/Pnq2bOnypYtqzVr1mjYsGF69uyZRo0apYULF+r69ev6+eeflTx5cp06dUoGg0FBQUFKkSKF+ZzAv7Fq1Sq5u7tr8eLFWrBggdzc3LR48WIFBATo+PHjevnypXnfJk2ayMPDQzNnztSOHTvCnYcgA5Eh7HdlWJ/ZYDAoODhYNjY2ypEjh77++mudOXNG5cqV08uXL1W4cGE5ODioZcuWaty4sebNm6cGDRoQZABAFCPMiIUKFCigjRs36u3bt5ozZ44uX75sfq1Tp06aO3eumjVrRqcRVickJEQuLi767bff9Ouvv5q3FylSRFOnTlVQUJDGjx+vPXv2WLBKIOqZTCbzE7hTpkxRs2bNVKBAAS1YsEA+Pj5/edzHIzmmT5+uli1bRkm9AIDIYTAYzN8Hx44d0+nTpzV79my1bNlSHh4e6ty5s65fv67hw4fr9u3batiwoY4dOyZvb2+tX79ednZ2GjBggM6dOyd3d3fzOYEvtWLFCrVv314DBw7Ujz/+aJ6ap23btpozZ47mzJmjhQsX6vXr1+ZjGjVqpH379qlr166WKhuxiI2NjX7//Xe1atVKP//8s6T/PdQzefJkLV++XB4eHrK3t1fFihVVrlw5derUSYkTJ1ZISIhOnDjBmowAYAGGUB61ibXOnj2rdu3aqWDBgurdu7dy5sxp6ZKAcD41Zc7ly5c1evRo3bhxQ3379lWLFi3Mrx0/flzjxo3T999/r7Rp00Z1uUCU+zjEkKQhQ4Zo8eLFGjBggN69e6fly5erbNmy6tatmwoWLBju2I//fS1atEgDBw7UnDlz9O2330bpZwAA/HddunRR3bp1VblyZUnSoUOH1KZNG71+/Vpz5sxRs2bNzPsuWbJES5cuVbZs2dSzZ0/lz59f0ocRsD/++KPWr1+vzZs3q0CBApb4KIgBLl++rCZNmqhXr15q166defvH00XNnj1bvXr10vjx49WlSxfFjx8/3DmYWgpR4fbt2/r222+VMGFCDRkyRCVLltTEiRM1ZcoUrVu3ThUrVtTVq1fVtGlTOTs7a+fOnUqUKJGCgoLk4OBg6fIBIFZiZEYsVqBAAS1evFgXLlzQmDFjdO3aNUuXBJiF3Wg9fvy4Fi9erKFDh+rq1avKlSuXxowZo+zZs2v+/PlatWqV+ZjixYtr48aNBBmINWxsbGQ0GiVJmzZt0oYNG/TTTz+pX79+qly5sn777TcdOnRIU6dO1YULF8zHGY1Gc5CxcOFC9e/fX0uWLCHIAIBo6MqVK3JxcVH58uXN28qUKaMOHTrI0dFRW7Zs0YMHD8yvubu7q127djp69Gi46Xy++uor1ahRQ4cPHybIwH/y+++/KyAgQGXKlAk3TZmdnZ1MJpNCQ0PVo0cPff/99xoyZIgmTpyot2/fhjsHQQaiQqZMmfTDDz/IZDJpypQp6tChg6ZPn641a9aoYsWKkj78bly3bp0ePXqk6tWry2QyMS0rAFgQIzOgX3/9Vf3799eaNWuUMmVKS5cDmG3atEmdO3dWwYIFFRwcrJMnT2ro0KEaNGiQzp8/r0mTJunhw4dq1aqV2rZta+lygSjToUMH3b9/37xGTEhIiA4ePKizZ8+qX79+2r59u1q1aqVZs2bJyclJLVu2VJMmTdS5c2eVKFHCfJ5Fixapf//+Wrp0qRo0aGCpjwMA+JeMRqNsbW0VHBwse3t7rVixQkajUW3atJEkTZgwQevWrVOVKlXUo0cPpU6d2nzsjh07VK1aNdna2n5yNCzwb02YMEHTp0/X06dPJX16tPWVK1cUN25c7dy5U6tWrdKRI0dog7CY69evq1u3bjpy5IjGjBmjvn37Sgo/Cvr69euyt7dXxowZLVkqAMR6jMyAihQpIi8vL4IMWJWLFy+qR48emjx5sry8vOTl5aW3b9+an0LPly+fBg8eLBcXF23YsEF+fn4WrhiIGkFBQSpevLiuXLliHklhZ2enfPnyqXXr1nrx4oUmTZqkIUOGqFWrVmrQoIHSp0+v3bt3a//+/ebzLFq0SF26dNHy5csJMgAgGurfv7+qV6+ukJAQ2dvb6/79+1q9erU8PDy0du1aSdLgwYNVv3597d27V7Nnz9bDhw/Nx9esWVO2trbhRusBESFLlix6+/ateR27T7Wv5cuXa9y4cerSpYs5yOA5S1hKtmzZNH/+fJUuXVre3t46cuSIpA+joMMWCs+WLRtBBgBYAcIMSJKcnJwsXQIQ7gLmzZs3ypEjh9zc3HTt2jVlzpxZ7u7uGjp0qCTp4cOHypMnjyZMmKDFixf/aZ5dIKZycHBQ8+bNNWnSJB0/fty8bkySJEmUNGlSvX79Wk+ePFG2bNkkSY8fP1bp0qU1ffp0DR48WJIUGBio9+/fa/369apXr57FPgsA4N8JDAxUypQp9eLFC7m5uSk4OFhp06bVyJEjlSFDBn3//fdas2aNJGn48OGqV6+e9u/fr9GjR+vZs2fhzmVra2uJj4AYrFChQnJwcNCiRYt079498/awvr6fn59u376tXLlyhXuNUA2WlDlzZs2dO1ehoaEaO3asjh49Kknh1qcDAFgev5UBWA2DwaDt27dr+vTp8vHxka+vr548eaJq1aqpatWqWrhwoSRp165dGj16tF6+fKk8efKEmzIBiMnCRiY5OTkpQYIEatSokdasWaNOnTqZ9/Hz85OTk5OOHDkiT09PdejQQQ8ePFDz5s3NT5c5OjqqS5cuql+/vqU+CgDgX7pw4YIcHR3VsWNHtW/fXteuXVPLli0VHBys4sWLq2fPnkqZMqXmz59vDjSGDRumcuXKKTAwUIkTJ7bwJ0BMlylTJi1YsEA7duzQ4MGDdfbsWUkf+voPHz5U06ZN9fjxY3Xt2tW8nSAD1iBr1qyaPXu27O3t1a9fPx0/ftzSJQEA/oA1MwBYzE8//aQ0adIob9685qexGjVqpLJly6pjx44qVaqUzpw5o1atWmnJkiXmfQYMGKDz589rzZo1SpQokaU/BhDl+vXrpz179qhkyZI6deqUrly5orp162rVqlWSpGnTpmn58uV6//69UqVKpX379sne3p6nHgEgmps7d6569OihPXv2qGLFinr79q1+/PFHeXh4KEuWLFq5cqXs7e114sQJTZ8+XY8fP1bnzp3VtGlTSf97+p3vA0Q2o9GoZcuWqUuXLkqePLly584tk8mk169fy2Qy6ejRo7K3tzev+wJYk2vXrmnYsGGaNm2a0qVLZ+lyAAAfIcwAYBFPnjxRiRIlVK5cOfXr1085c+aUJJUpU0b16tVTjx49NGfOHC1ZskQlS5bU9OnTdevWLa1evVoLFizQ4cOHlTt3bgt/CiDq/fzzz2rYsKG2bNmi0qVL682bN1qzZo1GjRqlsmXLavXq1ZKkmzdvysHBQWnSpJGNjY1CQkJkZ2dn4eoBAP/F1atXNWXKFG3fvl1r1qz5x0Bj5syZOn/+vGbNmqVKlSpJYjofRK1z585p6dKl8vHxUdq0aVWgQAF16tRJtra29E1g1YKCguTg4GDpMgAAf0DPAYBFJE+eXBs3blSnTp00Y8YM9ezZU7lz51acOHGUJEkS2draqnnz5nr79q1WrlypBAkSKHv27AoNDdXPP/9MkIFY68mTJ4oTJ44KFCggSYoXL56aNGmiV69eadCgQXJ1ddX8+fOVJUsW8zEmk4mbBQAQA3z11VcaMmSIQkND1bhxY61bt06VKlXSt99+K4PBoEWLFqlly5ZasWKFihUrpi5dumjv3r365ptvzOcgyEBUyp8/v2bPnv2n7Uajkb4JrBpBBgBYJ0ZmALCos2fPql27dsqfP7969+6tsWPHqn379qpQoYJ5H5PJpA0bNqhEiRJycnJSsmTJLFgxYBlhT9JeuHBBtWrV0ty5c1WrVi3z61evXlW5cuX09OlTDR06VGPGjLFgtQCAiPbxiIobN25o/Pjx2rp1qznQePv2rVatWiUPDw9lzZpVy5Ytk6Ojo/l4pvOBpTAaCAAARBTCDAAWd/bsWXXo0EG5cuXSpk2blCxZMmXKlEmhoaEymUyysbFRmjRptHjxYp7gQqwR1vb/6MGDB2rTpo0SJUqkXr16qUSJEpKk27dva/DgwWrTpo0qVarEDSsAiCH+6vvg6tWrmjx58p8CjdWrV2vs2LFq3bq1Ro8ezY1kAAAAxBiEGQCswpkzZ+Tm5iYbGxvlypVLVapU0atXr/TixQs5Ojqqdu3aypUrl6XLBKLExzeutm7dqt9//10Gg0FNmjRRokSJdPDgQXXv3l1p06ZV6dKlVaRIEU2cOFGOjo7avn27DAYDT+ACQAzw8ffBsWPHZDQaFRoaqtKlS0v6sEjtpEmTwgUa/v7+8vb2Vs2aNfkeAAAAQIxCmAHAapw7d04dOnRQvnz5NHToUGXIkMHSJQFR7uMnaAcNGqR169YpadKkcnR01KNHj7Rv3z5lyJBBv/zyi3744Qft3LlTCRIkUKJEieTt7S17e/u/fIoXABB9fPx9MHToUG3YsEHBwcGys7NT9erVNWvWLEmSj4+PJk2apB07dmjJkiXhpiAk2AYAAEBMQpgBwKqcPXtWHTt2VKZMmTRixAh99dVXli4JsIjZs2ebn7YtXLiwlixZovbt2ytVqlTau3evvvrqKwUGBur9+/d6/fq10qZNK4PBoJCQEKZjA4AYZPz48Zo1a5Y8PT2VN29eTZw4URMmTJC7u7s8PDwkfQg0Bg4cqMDAQO3atYuppQAAABAj8dgmAKtSoEABzZs3T48fP1aCBAksXQ4QZT5+tuDJkye6dOmSpk2bpsKFC2vHjh3q3bu3xo0bpyxZsqhq1aq6deuWHB0d5erqqnTp0slgMMhkMhFkAEAMcu3aNR07dkzLly9XyZIldejQIc2bN08dO3bUmjVr1LFjR0lS9uzZNXv2bO3cuVOSCDIAAAAQIzEyA4BVev/+vZycnCxdBmAxe/bsUY4cOfTq1SvVrVtX/fr1U5cuXbR48WJ16NBBdnZ2unnzptKlS2fpUgEAEeT8+fO6c+eOkiRJopIlSyowMFDLli1T06ZNdenSJTVr1kxDhw5Vp06d1KlTJy1atEj169fXxo0bzedgqkEAAADEVDy+CcAqEWQgNpozZ4527twpLy8vVa5cWZK0d+9eZcmSRd9++60kKUmSJGrXrp2SJ0+u1KlTW7JcAEAEWrVqlaZOnap06dIpV65cKlmypBwdHdW+fXvZ2tpqx44dKleunFq1aiVJSpMmjWrXrq3379+HCzAIMgAAABBT0dMFAMBCTCaTpP9NMZU9e3Y9fPhQa9euNe/z+PFjHTt2TLa2tnrz5o2WL18uV1dXjRkzRra2tjIajRapHQAQcVasWKH27dtr4MCB+uGHHzR+/Hjza7a2tjKZTLpw4YKeP38uZ2dnvXv3TmfOnFGdOnW0Y8cO2djYmL9TAAAAgJiKaaYAALCAjxdnDQwMlKOjo54+faoBAwYoODhYc+bMUcKECXX37l3Vr19fV65cUYYMGWRjY6Pz58+zNgYAxBCXL19WkyZN1KtXL7Vr1868/Y+LeG/evFlNmzZV8eLF5efnJ6PRqDNnzsjOzo4FvwEAABArMDIDAAALCLvpNHHiROXJk0fHjx9XokSJ1K9fP23ZskXr16+X9GEaka1bt2rGjBnq27evOchgRAYAxAy///67AgICVKZMGX38nFnY90TYturVq2vdunXKlCmTqlSpYg4yjEYjQQYAAABiBUZmAABgIaGhoapXr562bdumypUrq0CBAmratKlu3Lih7t27a9u2bSpSpMifjjMajbK1tbVAxQCAiDZhwgRNnz5dT58+lfTnERmSdPXqVb148UIlS5YMtz0kJISRegAAAIg1GJkBAEAU+fj5geDgYBkMBi1ZskTlypWTs7Oz4sWLp2bNmmnXrl0qXLiwVq1apTdv3vzpPAQZABBzZMmSRW/fvtWePXsk6ZOjLFasWKEffvjhT+tiEGQAAAAgNiHMAAAgioTdoJo+fbrmzp2rCxcuKHHixGrcuLFSp06tGjVqaPHixTpx4oQOHz6s2bNn6+bNmxauGgAQmQoVKiQHBwctWrRI9+7dM28PC8D9/Px048YN5cmTRzY2XL4BAAAg9qI3DABAFHv8+LE2bNig7t27y9PTUzVq1NDVq1d17tw5ff311/L29tagQYPUqFEj5c2b19LlAgAiUaZMmbRgwQLt2LFDgwcP1tmzZyV9CMAfPnyopk2b6vHjx+rcubOFKwUAAAAsizUzAACIRCaT6ZNP0h49elS7du3ShAkTNHToUD1//lyrV6/W4cOHlTt3bgUFBcnBwUESa2QAQExnNBq1bNkydenSRcmTJ1fu3LllMpn0+vVrmUwmHT16VPb29nwfAAAAIFYjzAAAIJJ8HGQcOHBA7969k8lkUo0aNcz7HD58WL1791bGjBnl6emp5s2ba+7cuXJ1dbVU2QAACzl37pyWLl0qHx8fpU2bVgUKFFCnTp1ka2vLYt8AAACI9QgzAACIBKGhoeY1MoYMGaINGzYoNDRU9vb2ypMnj9avX2/e9/79+9q2bZsmTZqkbNmyae/evZ9cABYAEDsxIgMAAAAgzAAAIFJNmjRJM2bM0JYtW1SkSBFNmTJFQ4YMUc2aNbV161ZzaGEymfT8+XMlTpxYNjY24cIQAEDswe9/AAAA4NNYABwAgAhkMpnMf/7999917NgxLV68WMWLF5eXl5cmTpyoPn366OTJk6pfv755f4PBoKRJk8rGxkZGo5EbWQAQS/H7HwAAAPg0wgwAACJIaGioeY2MPXv2KHXq1GrUqJGKFSum48ePq3Pnzpo4caKmTp0qNzc3bd26VWXKlJEU/uYVU4kAAAAAAACER5gBAEAE+HhakNGjR6tdu3a6e/euWrRooaRJk2rv3r0qUaKEWrZsKUlKlSqVmjZtqjRp0shoNFqydAAAAAAAAKtHmAEAwH8UEhJiDjJOnz6ty5cva+XKlUqfPr15n6tXr+r27duKGzeu3r17p59//lnFixfX2rVrZWtrG256KgAAAAAAAITHAuAAAPxLK1euVNOmTWVvb2/+efHixQoICNCOHTuUPHlyGY1G2dra6tChQ6pTp45Sp04tW1tbGY1GnTt3TnZ2dhb+FAAAAAAAANaPkRkAAPwLq1ev1siRIzV8+HCFhIRIkhIlSiR/f39duXJFR44ckfS/9S+KFi2qnTt3qnLlymrYsKE5yGCKKQAAAAAAgH/GyAwAAP6F169fa+rUqdq7d69Kly6tcePGycHBQUeOHFG/fv2UMGFCDRgwQOXLl//Lc4SEhDAyAwAAAAAA4DMwMgMAgC8UFBQkV1dXjRo1SpUqVdKJEyc0atQoBQcHq1SpUho7dqz8/Pw0d+5cHThwwHzcH9fFIMgAAADW5s6dOzIYDDp37tx/Os/IkSOVP39+889ubm6qW7fufzonAACI3QgzAAD4AqGhoXJwcJAk/fjjj/L19dWNGzc0b948jRw5UsHBwapYsaJGjhypJ0+eaN68edq9e7ckycaGr10AAGA5bm5uMhgM5v8lTpxYVatW1YULF8z7pE2bVo8ePVLu3Lkj9L1nzZql5cuXR+g5P+Xp06fq3Lmz0qVLJ0dHR6VIkUJVqlTR0aNHI/29AQBA5OKuCgAAX8BgMEj68LRhr169VKpUKX3//fcqW7asdu7cqaFDhyo4OFiVKlXSyJEjdf78eR08eNDCVQMAAHxQtWpVPXr0SI8ePZK3t7fs7OxUs2ZN8+u2trZKkSJFhI8gdXV1VYIECSL0nJ/SoEEDnT17Vj/88IOuX7+ubdu2qVy5cnr+/Hmkvm9QUFCknh8AABBmAADwRUJDQ/X06VPt2LFDkyZNUsuWLVWvXj2tXLlSFStW1KZNmzRmzBjzCI1Vq1ZpzJgxli4bAABAksyjFVKkSKH8+fNr0KBBun//vp4+fSrpz9NMHThwQAaDQd7e3ipcuLCcnZ319ddfy8fHJ9x5J06cqOTJkytevHhyd3fX+/fvw73+x2mmypUrpx49emjAgAFKlCiRUqRIoZEjR4Y75tq1aypVqpScnJyUM2dO7du3TwaDQVu2bPnkZ3v16pUOHz6sSZMmqXz58kqfPr2KFi2qwYMHq3bt2ub97t27pzp16sjFxUXx48dX48aN9eTJk7+sVZJ69eqlcuXKhau/W7du6tWrl5IkSaIqVapIki5fvqyaNWsqfvz4ihcvnkqXLq1bt26Zj1u8eLG++uorOTk5KUeOHPr+++8/+VkAAMCfEWYAAPAFDAaDXF1dZTKZ9OjRI0kfAo748eNr4sSJSpgwoTw8PNS9e3eFhISoSJEisrW1ldFotHDlAAAA4fn7++vHH39UlixZlDhx4r/dd+jQoZo2bZpOnTolOzs7tW3b1vza+vXrNXLkSI0fP16nTp1SypQpP+sm/Q8//KC4cePqxIkTmjx5skaPHq29e/dKkoxGo+rWrStnZ2edOHFCixYt0tChQ//2fC4uLnJxcdGWLVsUGBj4yX1MJpPq1KmjFy9e6ODBg9q7d69u376tJk2a/GO9n6rfwcFBR48e1YIFC/T777+rTJkycnR01P79+3X69Gm1bdtWISEhkqRVq1Zp+PDhGjdunK5evarx48dr2LBh+uGHH774vQEAiI1YeRQAgL9hMpn+tNZFSEiI0qVLp5MnT+rZs2fmi387OzsVLlxYoaGhcnZ2Dnecra1tlNYNAADwKTt27JCLi4sk6e3bt0qZMqV27Njxj2t7jRs3TmXLlpUkDRo0SDVq1ND79+/l5OSkmTNnyt3dXe7u7pKksWPHat++fX8anfFHefPm1YgRIyRJWbNm1dy5c+Xt7a1KlSpp7969unXrlg4cOKAUKVKYa6hUqdJfns/Ozk7Lly9X+/bttWDBAhUsWFBly5ZV06ZNlTdvXkmSt7e3Ll68qN9++01p06aVJK1YsUK5cuXSr7/+qiJFivzTX6FZ1qxZNXnyZPPPQ4YMkaurq9auXSt7e3tJUrZs2cyvjxgxQtOmTVP9+vUlSRkzZtSVK1e0cOFCtW7d+rPfFwCA2IqRGQAA/IWPg4wzZ87Ix8dHv//+u5ydnTVmzBgdOHBAffv21YMHDxQaGqqQkBA9f/5c3bp107Rp02RjYyOTyWThTwEAAPA/5cuX17lz53Tu3DmdPHlSVapUUbVq1XT37t2/PS4sDJCklClTSpJ8fX0lSVevXlWxYsXC7V+iRIl/rOXjc4adN+ycPj4+Sps2rTnIkKSiRYv+4zkbNGighw8fatu2bapataoOHDigggULmhcfv3r1qtKmTWsOMiQpZ86cSpAgga5evfqP5/9YoUKFwv187tw5lS5d2hxkfOzt27e6deuW3N3dzSNIXFxcNHbs2HDTUAEAgL/GyAwAAP5CWJAxaNAgLV++XE5OTkqQIIHmzp2rUqVKadeuXapdu7Z8fHzk5OSkgIAA+fn5ae3atTIYDJ8c1QEAAGBJcePGVZYsWcw/L168WK6urvLw8NDYsWP/8riPb9AbDAZJ+s8Pbfzxpn9Y/+m/cnJyUqVKlVSpUiUNGzZM7dq104gRI+Tm5vZZx9vY2Cg0NDTctuDg4D/tFzdu3HA/x4kT5y/P6e/vL0ny8PD4U/DDCF4AAD4Pd1gAAPiDjy9ejx8/rjVr1mjdunWaPHmy8ubNqwoVKujQoUMqXbq0Tp8+rXr16ilv3ryqWLGiLl26ZF4jgyADAABYO4PBIBsbG7179+5fn+Orr77SiRMnwm07fvz4f6ore/bsun//friFuX/99dd/da6cOXPq7du3kj7Uev/+fd2/f9/8+pUrV/Tq1SvlzJlTkpQ0aVLz2mhhwhZE/zt58+bV4cOHPxl8JE+eXKlSpdLt27eVJUuWcP/LmDHjv/pcAADENozMAADgD8KeNpw3b578/PzUpUsX8xzRX3/9tSSpUqVK2rNnj8qWLau+ffvKzu5/X6khISHhfgYAALAWgYGBevz4sSTp5cuXmjt3rvz9/VWrVq1/fc6ePXvKzc1NhQsXVsmSJbVq1SpdvnxZmTJl+tfnrFSpkjJnzqzWrVtr8uTJevPmjb777jtJ/+ur/dHz58/VqFEjtW3bVnnz5lW8ePF06tQpTZ48WXXq1JEkVaxYUXny5FGLFi00c+ZMhYSEmPt6hQsXliR98803mjJlilasWKESJUroxx9/1KVLl1SgQIG/rblbt26aM2eOmjZtqsGDB8vV1VXHjx9X0aJFlT17do0aNUo9evSQq6urqlatqsDAQJ06dUovX75Unz59/vXfFQAAsQWPjAIA8AlPnjzR5s2bNXToUD19+lTShxEbadKk0fjx49WsWTNVq1ZN3t7efwouCDIAAIC18vLyUsqUKZUyZUoVK1ZMv/76qzZs2KBy5cr963M2adJEw4YN04ABA1SoUCHdvXtXnTt3/k912traasuWLfL391eRIkXUrl07DR06VNKHaaQ+xcXFRcWKFdOMGTNUpkwZ5c6dW8OGDVP79u01d+5cSR+CkK1btyphwoQqU6aMKlasqEyZMmndunXm81SpUsX8eYoUKaI3b96oVatW/1hz4sSJtX//fvn7+6ts2bIqVKiQPDw8zNNptWvXTosXL9ayZcuUJ08elS1bVsuXL2dkBgAAn8kQ+seJIAEAiIVCQ0P/9JTfqVOnNH78eO3fv1+//PKLcubMad7v999/V+fOneXn56cDBw5YpmgAAIBY5OjRoypVqpRu3rypzJkzW7ocAAAQxQgzAACx3scLdT979kyvX782XyDfvHlT3bp106VLl7Rnz55wgcbTp0+VOHFi1sYAAACIBJs3b5aLi4uyZs2qmzdvqmfPnkqYMKGOHDli6dIAAIAFcPcFABCrhYaGmsOIESNGqE6dOipQoIAaNGigWbNmKUuWLJo6daoKFiyoqlWr6tq1a+YRHEmTJpWNjY1MJpMlPwIAAECM9ObNG3Xt2lU5cuSQm5ubihQpoq1bt1q6LAAAYCGMzAAAQNKYMWM0Z84cLV68WHny5FHr1q318OFD7dixQzly5ND58+c1bNgw7dq1Szdv3lT69OktXTIAAAAAAECswQqlAIBYLTQ0VI8fP9auXbvk4eGh2rVr68CBAzp9+rRmz56tHDlyKDQ0VPny5dOoUaOUI0cOpUmTxtJlAwAAAAAAxCqEGQCAWM1gMMjJyUlBQUEqU6aMtm7dqm+//VbTpk2Tu7u73r17p/Xr16tkyZIqUKCAChQoIEkyGo2ytbW1cPUAAAAAAACxA2EGACBWCVu8+2Mmk0mvX79Wjx49tHPnTk2ePFmdOnWSJN25c0erVq1S0qRJlSVLFvMxBBkAAAAAAABRhzUzAACxRlBQkBwcHCRJDx48UOLEiSVJceLE0cqVK9W5c2fVrl1bq1evlslk0vv379W4cWMFBgbKy8uLAAMAAAAAAMBCGJkBAIjxvv/+e9WvX18pUqSQJI0YMUKenp4yGAyqWbOmOnXqpJYtW+ratWuaMGGCeQqpR48e6fnz5zp9+rRsbW1lMplkY2Nj4U8DAAAAAAAQ+3BHBgAQo23fvl0zZ87U8OHD9ebNG23ZskULFizQ4MGDVapUKR09elTdu3fXgwcPNG7cOG3ZskUmk0lx48ZVxYoVdebMGdnb2yskJIQgAwAAAAAAwEKYZgoAEOPNmDFDGzduVJ48eZQwYULlzJlTLVu2lCStWrVKixcvlouLi2bNmqVMmTIpODhY9vb25uNZ7BsAAAAAAMCyeMQUABBjmUwmSVLv3r1Vv359Xbt2TUuXLg0XVLRo0ULt27dXQECAevXqpevXr5tfD8v7CTIAAAAAAAAsizADABBj2djYyGg0SpL69u2r+vXry9nZWUuXLpWvr695v+bNm6t9+/a6e/euPDw8zNsNBkOU1wwAAAAAAIA/Y5opAECM83cLdc+aNUtr165V7ty5NX78eCVNmtT82r59+1S+fHlGYgAAAAAAAFgZwgwAQIzycZCxdetWXbt2TSlSpFCuXLlUuHBhSdLUqVO1efNm5cyZ80+BhsQaGQAAAAAAANaGMAMAEGOEhoaap4YaOHCgVq9ercyZM8tkMsloNGrQoEGqVauWJGnatGnaunWrkiVLpsWLFytBggQWrBwAAAAAAAB/hzUzAAAxRliQMWfOHK1bt07r16/XgQMHVK9ePZ06dUr9+vXThg0bJH1YQ6NChQpKkiSJ4sePb8myAQAAAAAA8A8YmQEAiFHevHmjbt26qUiRIurWrZu2b9+uli1bqkuXLrp8+bIuXbqkWbNmqWbNmpL+N5rj79bZAAAAAAAAgGURZgAAorVPhRA3btyQra2t3r9/r5o1a6pXr17q0aOHli9frvbt28vFxUXr1q1T5cqVJYWfngoAAAAAAADWx87SBQAA8G99HGR4eXnp9evXypMnj3LmzClJ8vDwUJo0aeTu7i5JSpgwoWrVqqUKFSqoQoUK5vMQZAAAAAAAAFg35tMAAERbYUHG4MGD1bBhQw0bNkz58uXT3LlzFRwcLHt7e928eVNnzpxRUFCQlixZohw5cqhLly6ytbWV0Wi08CcAAAAAAADA52BkBgAg2gmbFio0NFR3797VkSNHtHfvXmXPnl3Lli1Tjx499PbtWxUtWlTFixdX3bp1lShRIjk4OMjT09N8rK2traU/CgAAAAAAAD4Da2YAAKKVj6eWevHihZ4/f66lS5dq7Nix5nBi1qxZ6tOnj2bOnKk8efLo1atXevz4sdq1ayc7OzsZjUaCDAAAAAAAgGiEMAMAEC0NHTpUe/fu1fXr15U+fXqtX79e2bNnN78+c+ZMDRw4UP3799fYsWPN2wkyAAAAAAAAoh/WzAAARAsmk8n857Vr12rZsmVq2bKl2rRpo5s3b2rx4sW6e/eueZ9evXpp+PDh+vnnn/Vxbk+QAQAAAAAAEP0wMgMAEK0cPHhQ69evV7FixdSqVStJ0vfff68JEyaoRYsW6ty5s9KnT2/e/+P1NQwGg6XKBgAAAAAAwH/AAuAAgGjj8ePHcnd315MnT5QtWzbz9i5duig0NFQTJ06Ura2t3N3dlSlTJkkiyAAAAAAAAIgBmGYKABBtpEiRQp6enkqVKpV27typixcvml/r2rWrhgwZokmTJmnPnj3hjiPIAAAAAAAAiN6YZgoAEO2cP39ebdq0UeHChdWzZ0/lypXL/Jqnp6fq1KnD2hgAAAAAAAAxCGEGACBaOnv2rNq1a6dChQqpV69eypkzZ7jXjUYjgQYAAAAAAEAMQZgBAIi2zp49q44dOyp9+vSaPHmyMmbMaOmSAAAAAAAAEAlYMwMAEG0VKFBAc+fOVbx48ZQ+fXpLlwMAAAAAAIBIwsgMAEC0FxoaKoPBIJPJJBsbcnoAAAAAAICYhjADABAjhAUaAAAAAAAAiHl4fBUAECMQZAAAAAAAAMRchBkAAAAAAAAAAMCqEWYAAAAAAAAAAACrRpgBAAAAAAAAAACsGmEGAAAAAAAAAACwaoQZAAAAAAAAAADAqhFmALAabm5uqlu3brQ7NwAAAAAAAIDIRZgBxAJubm4yGAwyGAxycHBQlixZNHr0aIWEhPzn81pbQHDnzh0ZDAadO3cu3PZZs2Zp+fLlkf7+Bw8e1DfffKNEiRLJ2dlZWbNmVevWrRUUFBTp7w0AAAAAAADEVIQZQCxRtWpVPXr0SDdu3FDfvn01cuRITZky5V+dy2g0ymQyRVhtEX2+T3F1dVWCBAki9T2uXLmiqlWrqnDhwjp06JAuXryoOXPmyMHBQUajMVLfOzg4OFLPDwAAAAAAAFgSYQYQSzg6OipFihRKnz69OnfurIoVK2rbtm2SpMDAQPXr10+pU6dW3LhxVaxYMR04cMB87PLly5UgQQJt27ZNOXPmlKOjo9q2basffvhBW7duNY/6OHDggA4cOCCDwaBXr16Zjz937pwMBoPu3Lnzl+e7d++eef9Ro0YpadKkih8/vjp16hRuVIOXl5dKlSqlBAkSKHHixKpZs6Zu3bplfj1jxoySpAIFCshgMKhcuXKS/jyKJDAwUD169FCyZMnk5OSkUqVK6ddffzW/HvY5vL29VbhwYTk7O+vrr7+Wj4/PX/4d79mzRylSpNDkyZOVO3duZc6cWVWrVpWHh4fixIlj3m/Tpk3KlSuXHB0dlSFDBk2bNi3ceQwGg7Zs2RJuW4IECcwjS8JGn6xbt05ly5aVk5OTVq1aJUlaunSp+dwpU6ZUt27dzOd49eqV2rVrZ/67/eabb3T+/Pm//DwAAAAAAACAtSDMAGKpOHHimEOCbt266dixY1q7dq0uXLigRo0aqWrVqrpx44Z5/4CAAE2aNEmLFy/W5cuXNXv2bDVu3Ng84uPRo0f6+uuvP/v9/3i+ZMmSSZK8vb119epVHThwQGvWrJGnp6dGjRplPu7t27fq06ePTp06JW9vb9nY2KhevXrmkR0nT56UJO3bt0+PHj2Sp6fnJ99/wIAB2rRpk3744QedOXNGWbJkUZUqVfTixYtw+w0dOlTTpk3TqVOnZGdnp7Zt2/7lZ0qRIoUePXqkQ4cO/eU+p0+fVuPGjdW0aVNdvHhRI0eO1LBhw/7VFFiDBg1Sz549dfXqVVWpUkXz589X165d1aFDB128eFHbtm1TlixZzPs3atRIvr6+2rVrl06fPq2CBQuqQoUKf/rMAAAAAAAAgLWxs3QBAKJWaGiovL29tXv3bnXv3l337t3TsmXLdO/ePaVKlUqS1K9fP3l5eWnZsmUaP368pA/TGH3//ffKly+f+Vxx4sRRYGCgUqRI8cV1fOp8kuTg4KClS5fK2dlZuXLl0ujRo9W/f3+NGTNGNjY2atCgQbj9ly5dqqRJk+rKlSvKnTu3kiZNKklKnDjxX9b19u1bzZ8/X8uXL1e1atUkSR4eHtq7d6+WLFmi/v37m/cdN26cypYtK+lDeFCjRg29f/9eTk5Ofzpvo0aNtHv3bpUtW1YpUqRQ8eLFVaFCBbVq1Urx48eXJE2fPl0VKlTQsGHDJEnZsmXTlStXNGXKFLm5uX3R32GvXr1Uv359889jx45V37591bNnT/O2IkWKSJKOHDmikydPytfXV46OjpKkqVOnasuWLdq4caM6dOjwRe8NAAAAAAAARCVGZgCxxI4dO+Ti4iInJydVq1ZNTZo00ciRI3Xx4kUZjUZly5ZNLi4u5v8dPHgw3PRNDg4Oyps3b4TV81fny5cvn5ydnc0/lyhRQv7+/rp//74k6caNG2rWrJkyZcqk+PHjK0OGDJIUbpqqf3Lr1i0FBwerZMmS5m329vYqWrSorl69Gm7fj2tMmTKlJMnX1/eT57W1tdWyZcv04MEDTZ48WalTp9b48eOVK1cuPXr0SJJ09erVcO8rSSVLltSNGze+eF2NwoULm//s6+urhw8fqkKFCp/c9/z58/L391fixInD/Xf+7bffwv13BgAAAAAAAKwRIzOAWKJ8+fKaP3++HBwclCpVKtnZffjn7+/vL1tbW50+fVq2trbhjnFxcTH/OU6cODIYDP/4PjY2HzLS0NBQ87ZPLU79uef7o1q1ail9+vTy8PBQqlSpZDKZlDt37nDrakQke3t785/D6v2nxcpTp06tli1bqmXLlhozZoyyZcumBQsWhJsu6+8YDIZwf3/Sp/8O48aNa/7zx2tyfIq/v79SpkwZbi2UMJG9MDoAAAAAAADwXxFmALFE3Lhxw62fEKZAgQIyGo3y9fVV6dKlv+icDg4OfxpNEDbN06NHj5QwYUJJHxYA/1znz5/Xu3fvzDfnjx8/LhcXF6VNm1bPnz+Xj4+PPDw8zLUeOXLkTzVJ+ttRDpkzZ5aDg4OOHj2q9OnTS/oQFvz666/q1avXZ9f6ORImTKiUKVPq7du3kqSvvvpKR48eDbfP0aNHlS1bNnOYlDRpUvNIDunDaJSAgIC/fZ948eIpQ4YM8vb2Vvny5f/0esGCBfX48WPZ2dmZR7MAAAAAAAAA0QVhBhDLZcuWTS1atFCrVq00bdo0FShQQE+fPpW3t7fy5s2rGjVq/OWxGTJk0O7du+Xj46PEiRPL1dVVWbJkUdq0aTVy5EiNGzdO169f17Rp0z67nqCgILm7u+u7777TnTt3NGLECHXr1k02NjZKmDChEidOrEWLFillypS6d++eBg0aFO74ZMmSKU6cOPLy8lKaNGnk5OQkV1fXcPvEjRtXnTt3Vv/+/ZUoUSKlS5dOkydPVkBAgNzd3b/sL/AjCxcu1Llz51SvXj1lzpxZ79+/14oVK3T58mXNmTNHktS3b18VKVJEY8aMUZMmTXTs2DHNnTtX33//vfk833zzjebOnasSJUrIaDRq4MCB4UaI/JWRI0eqU6dOSpYsmapVq6Y3b97o6NGj6t69uypWrKgSJUqobt26mjx5srJly6aHDx9q586dqlevXrgpqwAAAAAAAABrw5oZALRs2TK1atVKffv2Vfbs2VW3bl39+uuvSpcu3d8e1759e2XPnl2FCxdW0qRJdfToUdnb22vNmjW6du2a8ubNq0mTJmns2LGfXUuFChWUNWtWlSlTRk2aNFHt2rU1cuRISR+msFq7dq1Onz6t3Llzq3fv3poyZUq44+3s7DR79mwtXLhQqVKlUp06dT75PhMnTlSDBg3UsmVLFSxYUDdv3tTu3bvNo0n+jaJFi8rf31+dOnVSrly5VLZsWR0/flxbtmwxLyJesGBBrV+/XmvXrlXu3Lk1fPhwjR49Otzi39OmTVPatGlVunRpNW/eXP369Qu3jshfad26tWbOnKnvv/9euXLlUs2aNXXjxg1JH6au+umnn1SmTBm1adPm/9q5YyKAoRCIgkTFd4Am7KMk6VOmyFyx64D6zVHdXTNTu1vnnM83AwAAAMAfrvv9mB0AAAAAACCIZQYAAAAAABBNzAAAAAAAAKKJGQAAAAAAQDQxAwAAAAAAiCZmAAAAAAAA0cQMAAAAAAAgmpgBAAAAAABEEzMAAAAAAIBoYgYAAAAAABBNzAAAAAAAAKKJGQAAAAAAQDQxAwAAAAAAiPYAVlK9eOu8rfcAAAAASUVORK5CYII=", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Summary Statistics by Method Combination:\n", - "============================================================\n", - " Count Mean Median Std Min Max\n", - "binding_source perturbation_source \n", - "Calling Cards kemmeren 33 0.309 0.4 0.265 0.0 0.8\n", - " mahendrawada_rnaseq 33 0.103 0.0 0.167 0.0 0.6\n", - " mcisaac 33 0.297 0.2 0.292 0.0 1.0\n", - "ChEC-seq kemmeren 33 0.261 0.2 0.247 0.0 0.8\n", - " mahendrawada_rnaseq 33 0.091 0.0 0.181 0.0 0.8\n", - " mcisaac 33 0.255 0.2 0.284 0.0 0.8\n", - "Harbison kemmeren 81 0.074 0.0 0.177 0.0 0.8\n", - " mahendrawada_rnaseq 81 0.020 0.0 0.098 0.0 0.6\n", - " mcisaac 81 0.064 0.0 0.158 0.0 0.8\n", - "\n", - "Overall Performance by Binding Method:\n", - "========================================\n", - " Count Mean Median Std\n", - "binding_source \n", - "Calling Cards 99 0.236 0.2 0.262\n", - "ChEC-seq 99 0.202 0.2 0.252\n", - "Harbison 243 0.053 0.0 0.149\n" - ] - } - ], - "source": [ - "\n", - "# Combine all rank-response results for comparison\n", - "combined_results = []\n", - "\n", - "if len(cc_rr_res) > 0:\n", - " cc_rr_res['binding_source'] = 'Calling Cards'\n", - " combined_results.append(cc_rr_res)\n", - "\n", - "if len(chec_rr_res) > 0:\n", - " chec_rr_res['binding_source'] = 'ChEC-seq' \n", - " combined_results.append(chec_rr_res)\n", - "\n", - "if len(harb_rr_res) > 0:\n", - " harb_rr_res['binding_source'] = 'Harbison'\n", - " combined_results.append(harb_rr_res)\n", - "\n", - "if combined_results:\n", - " all_rr_data = pd.concat(combined_results, ignore_index=True)\n", - "\n", - " # Create comparative boxplot visualizations\n", - " fig, axes = plt.subplots(1, 2, figsize=(16, 6))\n", - "\n", - " # 1. Combined boxplot - binding methods grouped by perturbation source\n", - " sns.boxplot(data=all_rr_data, x='perturbation_source', y='response_rate', \n", - " hue='binding_source', ax=axes[0])\n", - " axes[0].set_title('Response Rate Performance by Perturbation and Binding Method')\n", - " axes[0].set_xlabel('Perturbation Source')\n", - " axes[0].set_ylabel('Response Rate')\n", - " axes[0].legend(title='Binding Method', bbox_to_anchor=(1.05, 1), loc='upper left')\n", - " axes[0].tick_params(axis='x', rotation=45)\n", - "\n", - " # 2. Faceted view - binding methods grouped by binding source \n", - " sns.boxplot(data=all_rr_data, x='binding_source', y='response_rate',\n", - " hue='perturbation_source', ax=axes[1])\n", - " axes[1].set_title('Response Rate Performance by Binding and Perturbation Method')\n", - " axes[1].set_xlabel('Binding Source')\n", - " axes[1].set_ylabel('Response Rate')\n", - " axes[1].legend(title='Perturbation Method', bbox_to_anchor=(1.05, 1), loc='upper left')\n", - " axes[1].tick_params(axis='x', rotation=45)\n", - "\n", - " plt.tight_layout()\n", - " plt.show()\n", - "\n", - " # Summary statistics table\n", - " print(\"Summary Statistics by Method Combination:\")\n", - " print(\"=\"*60)\n", - " summary_stats = all_rr_data.groupby(['binding_source', 'perturbation_source']).agg({\n", - " 'response_rate': ['count', 'mean', 'median', 'std', 'min', 'max']\n", - " }).round(3)\n", - " summary_stats.columns = ['Count', 'Mean', 'Median', 'Std', 'Min', 'Max']\n", - " print(summary_stats)\n", - "\n", - " # Overall performance by binding method\n", - " print(f\"\\nOverall Performance by Binding Method:\")\n", - " print(\"=\"*40)\n", - " binding_performance = all_rr_data.groupby('binding_source').agg({\n", - " 'response_rate': ['count', 'mean', 'median', 'std']\n", - " }).round(3)\n", - " binding_performance.columns = ['Count', 'Mean', 'Median', 'Std']\n", - " print(binding_performance)\n", - "\n", - "else:\n", - " print(\"No results available for comparison visualization\")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "tfbpapi-py3.11", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.9" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/tutorials/sample_manager_tutorial.ipynb b/docs/tutorials/sample_manager_tutorial.ipynb deleted file mode 100644 index 1c73ef2..0000000 --- a/docs/tutorials/sample_manager_tutorial.ipynb +++ /dev/null @@ -1,814 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# SampleManager Tutorial: Node-Based Filtering Across Heterogeneous Datasets\n", - "\n", - "This tutorial introduces `SampleManager`, a flexible node-based system for filtering samples across multiple datasets with varying experimental condition structures.\n", - "\n", - "## Why SampleManager?\n", - "\n", - "Traditional table-based approaches struggle with heterogeneous metadata:\n", - "- Different datasets structure conditions differently (e.g., `media.carbon_source` vs `environmental_conditions.media.carbon_source`)\n", - "- Missing fields and optional properties vary by dataset\n", - "- Multi-level hierarchies (repo/config/field) require complex joins\n", - "\n", - "**SampleManager** solves this by:\n", - "- Representing each sample as a node with dynamically discovered properties\n", - "- Supporting flexible MongoDB-style queries across heterogeneous structures\n", - "- Enabling cross-dataset filtering and set operations\n", - "- Respecting hierarchical overrides (field > config > repo)\n", - "\n", - "## Key Concepts\n", - "\n", - "- **SampleNode**: A sample with flattened properties (experimental conditions + metadata)\n", - "- **ActiveSet**: A filtered collection of sample IDs supporting set operations\n", - "- **Query Language**: MongoDB-style filters with operators like `$contains`, `$gte`, `$or`\n", - "- **Property Discovery**: No hardcoded schemas - properties are discovered from the data" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 1. Setup and Basic Usage" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "SampleManager(0 datasets, 0 samples)\n" - ] - } - ], - "source": [ - "from tfbpapi.datainfo.sample_manager import SampleManager, SampleNode\n", - "from tfbpapi.datainfo import DataCard\n", - "import json\n", - "\n", - "# Initialize manager\n", - "manager = SampleManager()\n", - "print(manager)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2. Understanding Property Discovery\n", - "\n", - "Let's explore how SampleManager discovers properties from a DataCard without hardcoded schemas." - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Repo-level conditions:\n", - "{}\n" - ] - } - ], - "source": [ - "# Load a DataCard to see its experimental conditions structure\n", - "card = DataCard('BrentLab/harbison_2004')\n", - "\n", - "# Get repo-level conditions\n", - "repo_conditions = card.get_experimental_conditions('harbison_2004')\n", - "print(\"Repo-level conditions:\")\n", - "print(json.dumps(repo_conditions, indent=2, default=str))" - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Field-level definition for 'YPD':\n", - "{\n", - " \"description\": \"Rich media baseline condition\",\n", - " \"temperature_celsius\": 30,\n", - " \"cultivation_method\": \"unspecified\",\n", - " \"growth_phase_at_harvest\": {\n", - " \"od600\": 0.8\n", - " },\n", - " \"media\": {\n", - " \"name\": \"YPD\",\n", - " \"carbon_source\": [\n", - " {\n", - " \"compound\": \"D-glucose\",\n", - " \"concentration_percent\": 2\n", - " }\n", - " ],\n", - " \"nitrogen_source\": [\n", - " {\n", - " \"compound\": \"yeast_extract\",\n", - " \"concentration_percent\": 1\n", - " },\n", - " {\n", - " \"compound\": \"peptone\",\n", - " \"concentration_percent\": 2\n", - " }\n", - " ]\n", - " }\n", - "}\n" - ] - } - ], - "source": [ - "# Get field-level condition definitions\n", - "field_defs = card.get_field_definitions('harbison_2004', 'condition')\n", - "\n", - "# Show one condition definition\n", - "print(\"\\nField-level definition for 'YPD':\")\n", - "print(json.dumps(field_defs.get('YPD', {}), indent=2, default=str))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### How Properties Are Flattened\n", - "\n", - "SampleManager recursively flattens nested structures using dot notation:\n", - "\n", - "```python\n", - "# Original nested structure:\n", - "{\n", - " \"environmental_conditions\": {\n", - " \"media\": {\n", - " \"name\": \"YPD\",\n", - " \"carbon_source\": [{\"compound\": \"D-glucose\", \"concentration_percent\": 2}]\n", - " },\n", - " \"temperature_celsius\": 30\n", - " }\n", - "}\n", - "\n", - "# Becomes flattened properties:\n", - "{\n", - " \"environmental_conditions.media.name\": \"YPD\",\n", - " \"environmental_conditions.media.carbon_source\": \"D-glucose\", # Simple string\n", - " \"_environmental_conditions.media.carbon_source_structured\": [{...}], # Full structure\n", - " \"environmental_conditions.temperature_celsius\": 30\n", - "}\n", - "```\n", - "\n", - "**Key Points:**\n", - "- Nested dicts become dot-notation keys\n", - "- Lists of dicts (compounds) get both simple and `_structured` versions\n", - "- No hardcoded field names - discovers whatever exists\n", - "- Different datasets can have different structures" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 3. Creating Sample Nodes Manually\n", - "\n", - "Before we load from DataCard, let's manually create nodes to understand the structure." - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Flattened properties:\n", - " environmental_conditions.cultivation_method: liquid_culture (from repo)\n", - " environmental_conditions.media.carbon_source: D-glucose (from field)\n", - " environmental_conditions.media.name: YPD (from field)\n", - " environmental_conditions.media.nitrogen_source: yeast_extract, peptone (from field)\n", - " environmental_conditions.temperature_celsius: 30 (from repo)\n", - " strain_background: BY4741 (from repo)\n" - ] - } - ], - "source": [ - "from tfbpapi.datainfo.sample_manager import SampleNode, ConditionFlattener\n", - "\n", - "# Simulate experimental conditions from different levels\n", - "repo_conditions = {\n", - " \"environmental_conditions\": {\n", - " \"temperature_celsius\": 30,\n", - " \"cultivation_method\": \"liquid_culture\"\n", - " },\n", - " \"strain_background\": \"BY4741\"\n", - "}\n", - "\n", - "field_conditions = {\n", - " \"environmental_conditions\": {\n", - " \"media\": {\n", - " \"name\": \"YPD\",\n", - " \"carbon_source\": [\n", - " {\"compound\": \"D-glucose\", \"concentration_percent\": 2}\n", - " ],\n", - " \"nitrogen_source\": [\n", - " {\"compound\": \"yeast_extract\", \"concentration_percent\": 1},\n", - " {\"compound\": \"peptone\", \"concentration_percent\": 2}\n", - " ]\n", - " }\n", - " }\n", - "}\n", - "\n", - "# Flatten conditions\n", - "properties, sources = ConditionFlattener.flatten_conditions(\n", - " repo_conditions, None, field_conditions\n", - ")\n", - "\n", - "print(\"Flattened properties:\")\n", - "for key, value in sorted(properties.items()):\n", - " source = sources.get(key, 'unknown')\n", - " # Skip structured versions for cleaner output\n", - " if not key.startswith('_'):\n", - " print(f\" {key}: {value} (from {source})\")" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Node: SampleNode(BrentLab/harbison_2004:harbison_2004:sample_001, 8 properties)\n", - "Global ID: BrentLab/harbison_2004:harbison_2004:sample_001\n", - "\n", - "Sample properties:\n", - " Temperature: 30\n", - " Carbon source: D-glucose\n" - ] - } - ], - "source": [ - "# Create a sample node\n", - "node = SampleNode(\n", - " sample_id=\"sample_001\",\n", - " repo_id=\"BrentLab/harbison_2004\",\n", - " config_name=\"harbison_2004\",\n", - " properties=properties,\n", - " property_sources=sources\n", - ")\n", - "\n", - "print(f\"Node: {node}\")\n", - "print(f\"Global ID: {node.global_id()}\")\n", - "print(f\"\\nSample properties:\")\n", - "print(f\" Temperature: {node.get_property('environmental_conditions.temperature_celsius')}\")\n", - "print(f\" Carbon source: {node.get_property('environmental_conditions.media.carbon_source')}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 4. Filtering with the Query Language\n", - "\n", - "SampleManager uses MongoDB-style queries for flexible filtering." - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Query 1 matches: True\n", - "Query 2 matches: True\n", - "Query 3 matches: True\n", - "Query 4 matches: True\n" - ] - } - ], - "source": [ - "from tfbpapi.datainfo.sample_manager import SampleFilter\n", - "\n", - "# Simple equality\n", - "query1 = {\"environmental_conditions.temperature_celsius\": 30}\n", - "print(f\"Query 1 matches: {SampleFilter.matches(node, query1)}\")\n", - "\n", - "# Contains check (case-insensitive)\n", - "query2 = {\"environmental_conditions.media.carbon_source\": {\"$contains\": \"glucose\"}}\n", - "print(f\"Query 2 matches: {SampleFilter.matches(node, query2)}\")\n", - "\n", - "# Numeric comparison\n", - "query3 = {\"environmental_conditions.temperature_celsius\": {\"$gte\": 25, \"$lte\": 35}}\n", - "print(f\"Query 3 matches: {SampleFilter.matches(node, query3)}\")\n", - "\n", - "# Logical OR\n", - "query4 = {\n", - " \"$or\": [\n", - " {\"environmental_conditions.media.carbon_source\": {\"$contains\": \"galactose\"}},\n", - " {\"environmental_conditions.media.carbon_source\": {\"$contains\": \"glucose\"}}\n", - " ]\n", - "}\n", - "print(f\"Query 4 matches: {SampleFilter.matches(node, query4)}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Available Query Operators\n", - "\n", - "| Operator | Description | Example |\n", - "|----------|-------------|----------|\n", - "| `$eq` | Equal (default) | `{\"temp\": 30}` or `{\"temp\": {\"$eq\": 30}}` |\n", - "| `$ne` | Not equal | `{\"temp\": {\"$ne\": 30}}` |\n", - "| `$gt` | Greater than | `{\"temp\": {\"$gt\": 30}}` |\n", - "| `$gte` | Greater than or equal | `{\"temp\": {\"$gte\": 30}}` |\n", - "| `$lt` | Less than | `{\"temp\": {\"$lt\": 30}}` |\n", - "| `$lte` | Less than or equal | `{\"temp\": {\"$lte\": 30}}` |\n", - "| `$in` | In list | `{\"strain\": {\"$in\": [\"BY4741\", \"W303\"]}}` |\n", - "| `$nin` | Not in list | `{\"strain\": {\"$nin\": [\"BY4741\"]}}` |\n", - "| `$contains` | String contains (case-insensitive) | `{\"carbon_source\": {\"$contains\": \"glucose\"}}` |\n", - "| `$exists` | Field exists | `{\"temperature\": {\"$exists\": true}}` |\n", - "| `$and` | Logical AND | `{\"$and\": [{...}, {...}]}` |\n", - "| `$or` | Logical OR | `{\"$or\": [{...}, {...}]}` |" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 5. Working with ActiveSets\n", - "\n", - "ActiveSets represent filtered collections of samples and support set operations." - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Set 1: ActiveSet(name=glucose_samples, size=3)\n", - "Set 2: ActiveSet(name=heat_stress_samples, size=3)\n" - ] - } - ], - "source": [ - "from tfbpapi.datainfo.sample_manager import ActiveSet\n", - "\n", - "# Create some example active sets\n", - "set1 = ActiveSet(\n", - " sample_ids={\"BrentLab:harbison_2004:sample_001\", \"BrentLab:harbison_2004:sample_002\", \"BrentLab:harbison_2004:sample_003\"},\n", - " name=\"glucose_samples\",\n", - " description=\"Samples grown on glucose\"\n", - ")\n", - "\n", - "set2 = ActiveSet(\n", - " sample_ids={\"BrentLab:harbison_2004:sample_002\", \"BrentLab:harbison_2004:sample_003\", \"BrentLab:harbison_2004:sample_004\"},\n", - " name=\"heat_stress_samples\",\n", - " description=\"Samples with heat stress\"\n", - ")\n", - "\n", - "print(f\"Set 1: {set1}\")\n", - "print(f\"Set 2: {set2}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Union: ActiveSet(name=glucose_or_heat, size=4)\n", - " Sample IDs: ['BrentLab:harbison_2004:sample_001', 'BrentLab:harbison_2004:sample_002', 'BrentLab:harbison_2004:sample_003', 'BrentLab:harbison_2004:sample_004']\n", - "\n", - "Intersection: ActiveSet(name=glucose_and_heat, size=2)\n", - " Sample IDs: ['BrentLab:harbison_2004:sample_002', 'BrentLab:harbison_2004:sample_003']\n", - "\n", - "Difference: ActiveSet(name=glucose_no_heat, size=1)\n", - " Sample IDs: ['BrentLab:harbison_2004:sample_001']\n" - ] - } - ], - "source": [ - "# Set operations\n", - "\n", - "# Union - samples in either set\n", - "union = set1.union(set2, name=\"glucose_or_heat\")\n", - "print(f\"Union: {union}\")\n", - "print(f\" Sample IDs: {union.to_sample_ids()}\")\n", - "\n", - "# Intersection - samples in both sets\n", - "intersection = set1.intersection(set2, name=\"glucose_and_heat\")\n", - "print(f\"\\nIntersection: {intersection}\")\n", - "print(f\" Sample IDs: {intersection.to_sample_ids()}\")\n", - "\n", - "# Difference - samples in set1 but not set2\n", - "difference = set1.difference(set2, name=\"glucose_no_heat\")\n", - "print(f\"\\nDifference: {difference}\")\n", - "print(f\" Sample IDs: {difference.to_sample_ids()}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 6. Real-World Example: Exploring Harbison 2004\n", - "\n", - "Now let's work with real data. Note that we're manually creating nodes here since the `load_from_datacard` method needs to be implemented. This demonstrates the intended workflow." - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Found 14 condition definitions\n", - "Conditions: ['Acid', 'Alpha', 'BUT14', 'BUT90', 'GAL', 'H2O2Hi', 'H2O2Lo', 'HEAT', 'Pi-', 'RAFF', 'RAPA', 'SM', 'Thi-', 'YPD']\n" - ] - } - ], - "source": [ - "# For this tutorial, we'll manually create nodes from DataCard information\n", - "# In production, you would use: manager.load_from_datacard(\"BrentLab/harbison_2004\", \"harbison_2004\")\n", - "\n", - "from tfbpapi.datainfo import DataCard\n", - "\n", - "card = DataCard('BrentLab/harbison_2004')\n", - "\n", - "# Get repo-level and field-level conditions\n", - "repo_conds = card.get_experimental_conditions('harbison_2004')\n", - "field_defs = card.get_field_definitions('harbison_2004', 'condition')\n", - "\n", - "print(f\"Found {len(field_defs)} condition definitions\")\n", - "print(f\"Conditions: {sorted(field_defs.keys())}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Loaded 14 sample nodes\n", - "SampleManager(1 datasets, 14 samples)\n" - ] - } - ], - "source": [ - "# Manually create nodes for each condition (simulating samples)\n", - "# This demonstrates what load_from_datacard will do automatically\n", - "\n", - "demo_manager = SampleManager()\n", - "\n", - "for condition_name, condition_def in field_defs.items():\n", - " # Flatten conditions\n", - " properties, sources = ConditionFlattener.flatten_conditions(\n", - " repo_conds, None, condition_def\n", - " )\n", - " \n", - " # Create node (using condition name as sample_id for demo)\n", - " node = SampleNode(\n", - " sample_id=condition_name,\n", - " repo_id=\"BrentLab/harbison_2004\",\n", - " config_name=\"harbison_2004\",\n", - " properties=properties,\n", - " property_sources=sources\n", - " )\n", - " \n", - " # Add to manager's collection\n", - " demo_manager._collection.add_node(node)\n", - "\n", - "print(f\"\\nLoaded {demo_manager._collection.count_total_nodes()} sample nodes\")\n", - "print(demo_manager)" - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Properties available in YPD sample:\n", - " cultivation_method: unspecified\n", - " description: Rich media baseline condition\n", - " growth_phase_at_harvest.od600: 0.8\n", - " media.carbon_source: D-glucose\n", - " media.name: YPD\n", - " media.nitrogen_source: yeast_extract, peptone\n", - " temperature_celsius: 30\n" - ] - } - ], - "source": [ - "# Explore what properties are available\n", - "sample_node = demo_manager.get_sample(\"BrentLab/harbison_2004:harbison_2004:YPD\")\n", - "\n", - "print(\"Properties available in YPD sample:\")\n", - "for key in sorted(sample_node.properties.keys()):\n", - " if not key.startswith('_'): # Skip structured versions\n", - " print(f\" {key}: {sample_node.properties[key]}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 7. Cross-Condition Filtering\n", - "\n", - "Now we can filter samples based on their experimental conditions." - ] - }, - { - "cell_type": "code", - "execution_count": 46, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Found 0 conditions with D-glucose\n", - "Conditions: []\n" - ] - } - ], - "source": [ - "# Find all samples with D-glucose as carbon source\n", - "glucose_samples = demo_manager.filter_all({\n", - " \"environmental_conditions.media.carbon_source\": {\"$contains\": \"D-glucose\"}\n", - "}, name=\"glucose_conditions\")\n", - "\n", - "print(f\"Found {len(glucose_samples)} conditions with D-glucose\")\n", - "print(f\"Conditions: {[sid.split(':')[-1] for sid in glucose_samples.to_sample_ids()]}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Found 0 conditions with alternative carbon sources\n", - "Conditions: []\n" - ] - } - ], - "source": [ - "# Find samples with alternative carbon sources\n", - "alt_carbon = demo_manager.filter_all({\n", - " \"$or\": [\n", - " {\"environmental_conditions.media.carbon_source\": {\"$contains\": \"galactose\"}},\n", - " {\"environmental_conditions.media.carbon_source\": {\"$contains\": \"raffinose\"}}\n", - " ]\n", - "}, name=\"alternative_carbon\")\n", - "\n", - "print(f\"\\nFound {len(alt_carbon)} conditions with alternative carbon sources\")\n", - "print(f\"Conditions: {[sid.split(':')[-1] for sid in alt_carbon.to_sample_ids()]}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Found 0 conditions with additives\n", - "Conditions: []\n" - ] - } - ], - "source": [ - "# Find samples with media additives (like butanol)\n", - "with_additives = demo_manager.filter_all({\n", - " \"environmental_conditions.media.additives\": {\"$contains\": \"butanol\"}\n", - "}, name=\"additive_conditions\")\n", - "\n", - "print(f\"\\nFound {len(with_additives)} conditions with additives\")\n", - "print(f\"Conditions: {[sid.split(':')[-1] for sid in with_additives.to_sample_ids()]}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 8. Property Distribution Analysis" - ] - }, - { - "cell_type": "code", - "execution_count": 49, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Carbon source distribution:\n", - " missing: 14 samples\n" - ] - } - ], - "source": [ - "# Analyze carbon source distribution\n", - "carbon_dist = demo_manager.get_property_distribution(\n", - " \"environmental_conditions.media.carbon_source\"\n", - ")\n", - "\n", - "print(\"Carbon source distribution:\")\n", - "for value, count in sorted(carbon_dist.items(), key=lambda x: x[1], reverse=True):\n", - " print(f\" {value}: {count} samples\")" - ] - }, - { - "cell_type": "code", - "execution_count": 50, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Media type distribution:\n", - " missing: 14 samples\n" - ] - } - ], - "source": [ - "# Analyze media names\n", - "media_dist = demo_manager.get_property_distribution(\n", - " \"environmental_conditions.media.name\"\n", - ")\n", - "\n", - "print(\"\\nMedia type distribution:\")\n", - "for value, count in sorted(media_dist.items(), key=lambda x: x[1], reverse=True):\n", - " print(f\" {value}: {count} samples\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 9. Summary Information" - ] - }, - { - "cell_type": "code", - "execution_count": 51, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Summary of loaded datasets:\n", - " repo_id config_name sample_count \\\n", - "0 BrentLab/harbison_2004 harbison_2004 14 \n", - "\n", - " properties \n", - "0 [description, temperature_celsius, cultivation... \n" - ] - } - ], - "source": [ - "# Get summary of loaded data\n", - "summary = demo_manager.get_summary()\n", - "print(\"\\nSummary of loaded datasets:\")\n", - "print(summary)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 10. Key Takeaways\n", - "\n", - "### Advantages of Node-Based Approach\n", - "\n", - "1. **Flexible Schema**: No hardcoded field names - discovers whatever exists in the data\n", - "2. **Heterogeneity Handling**: Different datasets can have different structures (e.g., `media.carbon_source` vs `environmental_conditions.media.carbon_source`)\n", - "3. **Cross-Dataset Queries**: Filter samples across multiple datasets with varying structures\n", - "4. **Set Operations**: Build complex filters incrementally using union/intersection/difference\n", - "5. **Property Discovery**: Automatically flattens nested structures using dot notation\n", - "6. **Dual Representation**: Compound lists get both simple strings and structured versions\n", - "\n", - "### How to Query Different Property Paths\n", - "\n", - "Since different datasets structure their conditions differently, you need to use the actual property path:\n", - "\n", - "```python\n", - "# Dataset A (harbison_2004): media nested under environmental_conditions\n", - "manager.filter_all({\"environmental_conditions.media.carbon_source\": {\"$contains\": \"glucose\"}})\n", - "\n", - "# Dataset B (hypothetical): media at top level \n", - "manager.filter_all({\"media.carbon_source\": {\"$contains\": \"glucose\"}})\n", - "\n", - "# To query across both, use $or:\n", - "manager.filter_all({\n", - " \"$or\": [\n", - " {\"environmental_conditions.media.carbon_source\": {\"$contains\": \"glucose\"}},\n", - " {\"media.carbon_source\": {\"$contains\": \"glucose\"}}\n", - " ]\n", - "})\n", - "```\n", - "\n", - "### Future: External Property Mappings\n", - "\n", - "For cross-dataset harmonization, you can provide external YAML mappings:\n", - "\n", - "```yaml\n", - "# property_mappings.yaml\n", - "carbon_source:\n", - " paths:\n", - " - environmental_conditions.media.carbon_source\n", - " - media.carbon_source\n", - " value_aliases:\n", - " D-glucose: [glucose, dextrose, D-dextrose]\n", - "```\n", - "\n", - "This would enable canonical queries like `{\"carbon_source\": {\"$contains\": \"glucose\"}}` to work across all datasets." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Next Steps\n", - "\n", - "- Implement `load_from_datacard()` to automatically load samples from HuggingFace\n", - "- Implement `load_from_duckdb()` for integration with HfQueryAPI\n", - "- Implement `export_to_duckdb()` to convert ActiveSets back to SQL views\n", - "- Add external property mapping support via YAML configuration\n", - "- Build UI for interactive filtering and set operations" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "tfbpapi-py3.11", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.9" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/tutorials/virtual_db_tutorial.ipynb b/docs/tutorials/virtual_db_tutorial.ipynb new file mode 100644 index 0000000..14c613e --- /dev/null +++ b/docs/tutorials/virtual_db_tutorial.ipynb @@ -0,0 +1,721 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# VirtualDB Tutorial: Unified Cross-Dataset Queries\n", + "\n", + "The `VirtualDB` class provides a unified query interface across heterogeneous datasets with different experimental condition structures and terminologies. Each dataset defines conditions in its own way, with properties at different hierarchy levels and using different naming conventions. VirtualDB uses external YAML configuration to:\n", + "\n", + "- Map varying structures to a common schema\n", + "- Normalize factor level names (e.g., \"D-glucose\", \"dextrose\", \"glu\" all become \"glucose\")\n", + "- Enable cross-dataset queries with standardized field names and values\n", + "\n", + "In this tutorial, we'll explore how to use VirtualDB to query and compare data across multiple datasets." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1. Introduction: Why VirtualDB?\n", + "\n", + "### The Problem\n", + "\n", + "When working with multiple genomic datasets, you'll encounter:\n", + "\n", + "1. **Inconsistent terminology**: One dataset uses \"D-glucose\", another uses \"glucose\", yet another uses \"glu\"\n", + "2. **Different hierarchies**: Conditions stored at repo-level vs config-level vs field-level\n", + "3. **Varying structures**: Different nesting patterns for the same logical information\n", + "\n", + "### The Solution\n", + "\n", + "VirtualDB abstracts these differences:\n", + "\n", + "- **Common schema**: Query all datasets using standardized field names\n", + "- **Automatic normalization**: \"D-glucose\" and \"glucose\" both match `carbon_source=\"glucose\"`\n", + "- **Unified interface**: Single query retrieves data from multiple datasets" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2. Basic Setup\n", + "\n", + "VirtualDB requires a YAML configuration file that defines:\n", + "- Which datasets to include\n", + "- How to map their fields to common names\n", + "- How to normalize varying terminologies" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chase/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Configuration saved to: /tmp/tmpf7f_eml_/vdb_config.yaml\n" + ] + } + ], + "source": [ + "from tfbpapi.virtual_db import VirtualDB\n", + "import pandas as pd\n", + "\n", + "# For this tutorial, we'll create a sample configuration\n", + "# In practice, you'd load this from a YAML file\n", + "config_yaml = \"\"\"\n", + "repositories:\n", + " BrentLab/harbison_2004:\n", + " dataset:\n", + " harbison_2004:\n", + " carbon_source:\n", + " field: condition\n", + " path: media.carbon_source.compound\n", + " temperature_celsius:\n", + " field: condition\n", + " path: temperature_celsius\n", + " environmental_condition:\n", + " field: condition\n", + "\n", + " BrentLab/kemmeren_2014:\n", + " dataset:\n", + " kemmeren_2014:\n", + " carbon_source:\n", + " path: media.carbon_source.compound\n", + " temperature_celsius:\n", + " path: temperature_celsius\n", + "\n", + "factor_aliases:\n", + " carbon_source:\n", + " glucose: [D-glucose, dextrose, glu]\n", + " galactose: [D-galactose, gal]\n", + " raffinose: [D-raffinose]\n", + "\n", + "missing_value_labels:\n", + " carbon_source: \"unspecified\"\n", + "\n", + "description:\n", + " carbon_source: The carbon source provided during growth\n", + " temperature_celsius: Growth temperature in degrees Celsius\n", + " environmental_condition: Named environmental condition\n", + "\"\"\"\n", + "\n", + "# Save config to temporary file\n", + "import tempfile\n", + "from pathlib import Path\n", + "\n", + "temp_config = Path(tempfile.mkdtemp()) / \"vdb_config.yaml\"\n", + "temp_config.write_text(config_yaml)\n", + "\n", + "print(f\"Configuration saved to: {temp_config}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "ename": "ValidationError", + "evalue": "1 validation error for MetadataConfig\n Value error, Invalid configuration for repository 'repositories': 1 validation error for RepositoryConfig\n Value error, Invalid repo-wide property 'BrentLab/harbison_2004': 1 validation error for PropertyMapping\npath\n Field required [type=missing, input_value={'dataset': {'harbison_20...'field': 'condition'}}}}, input_type=dict]\n For further information visit https://errors.pydantic.dev/2.12/v/missing [type=value_error, input_value={'BrentLab/harbison_2004'...emperature_celsius'}}}}}, input_type=dict]\n For further information visit https://errors.pydantic.dev/2.12/v/value_error [type=value_error, input_value={'repositories': {'BrentL...vironmental condition'}}, input_type=dict]\n For further information visit https://errors.pydantic.dev/2.12/v/value_error", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mValidationError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[2]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;66;03m# Initialize VirtualDB with the configuration\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m vdb = \u001b[43mVirtualDB\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mstr\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mtemp_config\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 4\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33mVirtualDB initialized successfully!\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 5\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mConfigured repositories: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mlen\u001b[39m(vdb.config.repositories)\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/code/tfbp/tfbpapi/tfbpapi/virtual_db.py:191\u001b[39m, in \u001b[36mVirtualDB.__init__\u001b[39m\u001b[34m(self, config_path, token)\u001b[39m\n\u001b[32m 181\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__init__\u001b[39m(\u001b[38;5;28mself\u001b[39m, config_path: Path | \u001b[38;5;28mstr\u001b[39m, token: \u001b[38;5;28mstr\u001b[39m | \u001b[38;5;28;01mNone\u001b[39;00m = \u001b[38;5;28;01mNone\u001b[39;00m):\n\u001b[32m 182\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m 183\u001b[39m \u001b[33;03m Initialize VirtualDB with configuration and optional auth token.\u001b[39;00m\n\u001b[32m 184\u001b[39m \n\u001b[32m (...)\u001b[39m\u001b[32m 189\u001b[39m \n\u001b[32m 190\u001b[39m \u001b[33;03m \"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m191\u001b[39m \u001b[38;5;28mself\u001b[39m.config = \u001b[43mMetadataConfig\u001b[49m\u001b[43m.\u001b[49m\u001b[43mfrom_yaml\u001b[49m\u001b[43m(\u001b[49m\u001b[43mconfig_path\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 192\u001b[39m \u001b[38;5;28mself\u001b[39m.token = token\n\u001b[32m 193\u001b[39m \u001b[38;5;28mself\u001b[39m.cache: \u001b[38;5;28mdict\u001b[39m[\u001b[38;5;28mtuple\u001b[39m[\u001b[38;5;28mstr\u001b[39m, \u001b[38;5;28mstr\u001b[39m], pd.DataFrame] = {}\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/code/tfbp/tfbpapi/tfbpapi/models.py:531\u001b[39m, in \u001b[36mMetadataConfig.from_yaml\u001b[39m\u001b[34m(cls, path)\u001b[39m\n\u001b[32m 528\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(data, \u001b[38;5;28mdict\u001b[39m):\n\u001b[32m 529\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[33m\"\u001b[39m\u001b[33mConfiguration must be a YAML dict\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m--> \u001b[39m\u001b[32m531\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mcls\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mmodel_validate\u001b[49m\u001b[43m(\u001b[49m\u001b[43mdata\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/pydantic/main.py:716\u001b[39m, in \u001b[36mBaseModel.model_validate\u001b[39m\u001b[34m(cls, obj, strict, extra, from_attributes, context, by_alias, by_name)\u001b[39m\n\u001b[32m 710\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m by_alias \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mFalse\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m by_name \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mTrue\u001b[39;00m:\n\u001b[32m 711\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m PydanticUserError(\n\u001b[32m 712\u001b[39m \u001b[33m'\u001b[39m\u001b[33mAt least one of `by_alias` or `by_name` must be set to True.\u001b[39m\u001b[33m'\u001b[39m,\n\u001b[32m 713\u001b[39m code=\u001b[33m'\u001b[39m\u001b[33mvalidate-by-alias-and-name-false\u001b[39m\u001b[33m'\u001b[39m,\n\u001b[32m 714\u001b[39m )\n\u001b[32m--> \u001b[39m\u001b[32m716\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mcls\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m__pydantic_validator__\u001b[49m\u001b[43m.\u001b[49m\u001b[43mvalidate_python\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 717\u001b[39m \u001b[43m \u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 718\u001b[39m \u001b[43m \u001b[49m\u001b[43mstrict\u001b[49m\u001b[43m=\u001b[49m\u001b[43mstrict\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 719\u001b[39m \u001b[43m \u001b[49m\u001b[43mextra\u001b[49m\u001b[43m=\u001b[49m\u001b[43mextra\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 720\u001b[39m \u001b[43m \u001b[49m\u001b[43mfrom_attributes\u001b[49m\u001b[43m=\u001b[49m\u001b[43mfrom_attributes\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 721\u001b[39m \u001b[43m \u001b[49m\u001b[43mcontext\u001b[49m\u001b[43m=\u001b[49m\u001b[43mcontext\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 722\u001b[39m \u001b[43m \u001b[49m\u001b[43mby_alias\u001b[49m\u001b[43m=\u001b[49m\u001b[43mby_alias\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 723\u001b[39m \u001b[43m \u001b[49m\u001b[43mby_name\u001b[49m\u001b[43m=\u001b[49m\u001b[43mby_name\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 724\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[31mValidationError\u001b[39m: 1 validation error for MetadataConfig\n Value error, Invalid configuration for repository 'repositories': 1 validation error for RepositoryConfig\n Value error, Invalid repo-wide property 'BrentLab/harbison_2004': 1 validation error for PropertyMapping\npath\n Field required [type=missing, input_value={'dataset': {'harbison_20...'field': 'condition'}}}}, input_type=dict]\n For further information visit https://errors.pydantic.dev/2.12/v/missing [type=value_error, input_value={'BrentLab/harbison_2004'...emperature_celsius'}}}}}, input_type=dict]\n For further information visit https://errors.pydantic.dev/2.12/v/value_error [type=value_error, input_value={'repositories': {'BrentL...vironmental condition'}}, input_type=dict]\n For further information visit https://errors.pydantic.dev/2.12/v/value_error" + ] + } + ], + "source": [ + "# Initialize VirtualDB with the configuration\n", + "vdb = VirtualDB(str(temp_config))\n", + "\n", + "print(\"VirtualDB initialized successfully!\")\n", + "print(f\"Configured repositories: {len(vdb.config.repositories)}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3. Schema Discovery\n", + "\n", + "Before querying, let's explore what fields are available." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Get all fields defined in any dataset\n", + "all_fields = vdb.get_fields()\n", + "\n", + "print(\"All available fields:\")\n", + "for field in sorted(all_fields):\n", + " print(f\" - {field}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Get fields present in ALL datasets (common fields)\n", + "common_fields = vdb.get_common_fields()\n", + "\n", + "print(\"Common fields (present in all datasets):\")\n", + "for field in sorted(common_fields):\n", + " print(f\" - {field}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Get fields for a specific dataset\n", + "harbison_fields = vdb.get_fields(\"BrentLab/harbison_2004\", \"harbison_2004\")\n", + "\n", + "print(\"Fields in harbison_2004:\")\n", + "for field in sorted(harbison_fields):\n", + " print(f\" - {field}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Discovering Valid Values\n", + "\n", + "VirtualDB can tell you what values exist for each field." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Get all unique values for a field (normalized)\n", + "carbon_sources = vdb.get_unique_values(\"carbon_source\")\n", + "\n", + "print(\"Unique carbon sources (normalized):\")\n", + "for source in sorted(carbon_sources):\n", + " print(f\" - {source}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Get values broken down by dataset\n", + "carbon_by_dataset = vdb.get_unique_values(\"carbon_source\", by_dataset=True)\n", + "\n", + "print(\"Carbon sources by dataset:\")\n", + "for dataset, sources in carbon_by_dataset.items():\n", + " print(f\"\\n{dataset}:\")\n", + " for source in sorted(sources):\n", + " print(f\" - {source}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4. Simple Queries\n", + "\n", + "Now let's start querying data. The `query()` method is the primary interface." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Basic Query: All Samples with Glucose\n", + "\n", + "By default, queries return sample-level data (one row per sample) with all configured fields." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Query all datasets for samples grown on glucose\n", + "glucose_samples = vdb.query(filters={\"carbon_source\": \"glucose\"})\n", + "\n", + "print(f\"Found {len(glucose_samples)} samples with glucose\")\n", + "print(f\"\\nColumns: {list(glucose_samples.columns)}\")\n", + "print(f\"\\nFirst few rows:\")\n", + "glucose_samples.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Query Specific Datasets\n", + "\n", + "Limit your query to specific datasets using the `datasets` parameter." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Query only harbison_2004\n", + "harbison_glucose = vdb.query(\n", + " filters={\"carbon_source\": \"glucose\"},\n", + " datasets=[(\"BrentLab/harbison_2004\", \"harbison_2004\")]\n", + ")\n", + "\n", + "print(f\"Found {len(harbison_glucose)} samples from harbison_2004\")\n", + "harbison_glucose.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Select Specific Fields\n", + "\n", + "Return only the fields you need with the `fields` parameter." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Get just sample_id, carbon_source, and temperature\n", + "minimal_data = vdb.query(\n", + " filters={\"carbon_source\": \"glucose\"},\n", + " fields=[\"sample_id\", \"carbon_source\", \"temperature_celsius\"]\n", + ")\n", + "\n", + "print(f\"Columns: {list(minimal_data.columns)}\")\n", + "minimal_data.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 5. Advanced Queries\n", + "\n", + "VirtualDB supports more sophisticated query patterns." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Multiple Filter Conditions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Samples with glucose at 30C\n", + "glucose_30c = vdb.query(\n", + " filters={\n", + " \"carbon_source\": \"glucose\",\n", + " \"temperature_celsius\": 30\n", + " }\n", + ")\n", + "\n", + "print(f\"Found {len(glucose_30c)} samples with glucose at 30C\")\n", + "glucose_30c.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Numeric Range Queries" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Samples at temperature >= 30C\n", + "warm_samples = vdb.query(\n", + " filters={\"temperature_celsius\": (\">=\", 30)}\n", + ")\n", + "\n", + "print(f\"Found {len(warm_samples)} samples at >= 30C\")\n", + "\n", + "# Samples between 28C and 32C\n", + "moderate_temp = vdb.query(\n", + " filters={\"temperature_celsius\": (\"between\", 28, 32)}\n", + ")\n", + "\n", + "print(f\"Found {len(moderate_temp)} samples between 28-32C\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Factor Alias Expansion\n", + "\n", + "When you query for a normalized value, VirtualDB automatically expands to all original aliases." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Query for \"galactose\" matches \"D-galactose\", \"gal\", and \"galactose\"\n", + "galactose_samples = vdb.query(filters={\"carbon_source\": \"galactose\"})\n", + "\n", + "print(f\"Found {len(galactose_samples)} galactose samples\")\n", + "print(\"\\nThis query internally expanded to:\")\n", + "print(\" WHERE carbon_source IN ('D-galactose', 'gal', 'galactose')\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Complete Data Retrieval\n", + "\n", + "By default, `query()` returns sample-level metadata (one row per sample). \n", + "Set `complete=True` to get all measurements (many rows per sample)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Get complete data with measurements\n", + "complete_data = vdb.query(\n", + " filters={\"carbon_source\": \"glucose\"},\n", + " datasets=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", + " complete=True\n", + ")\n", + "\n", + "print(f\"Complete data: {len(complete_data)} rows\")\n", + "print(f\"Columns: {list(complete_data.columns)}\")\n", + "print(\"\\nFirst few measurements:\")\n", + "complete_data.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# You can combine complete=True with field selection\n", + "# Get just the binding data columns\n", + "binding_data = vdb.query(\n", + " filters={\"carbon_source\": \"glucose\"},\n", + " datasets=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", + " fields=[\"sample_id\", \"regulator_symbol\", \"target_symbol\", \"effect\", \"pvalue\"],\n", + " complete=True\n", + ")\n", + "\n", + "print(f\"Binding data: {len(binding_data)} measurements\")\n", + "binding_data.head(10)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 6. Understanding the Configuration\n", + "\n", + "Let's examine the configuration structure in more detail." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Property Mapping Levels\n", + "\n", + "Properties can be extracted at three hierarchy levels:\n", + "\n", + "1. **Repository-wide**: Common to all datasets\n", + "```yaml\n", + "repositories:\n", + " BrentLab/some_repo:\n", + " nitrogen_source: # Applies to all datasets in this repo\n", + " path: media.nitrogen_source.name\n", + "```\n", + "\n", + "2. **Dataset-specific**: Specific to one config\n", + "```yaml\n", + " dataset:\n", + " config_name:\n", + " phosphate_source: # Only for this dataset\n", + " path: media.phosphate_source.compound\n", + "```\n", + "\n", + "3. **Field-level**: Varies per sample\n", + "```yaml\n", + " carbon_source:\n", + " field: condition # Extract from 'condition' field\n", + " path: media.carbon_source.compound\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Field Aliases (Column Renaming)\n", + "\n", + "When you specify just a `field` without a `path`, it creates a column alias:\n", + "\n", + "```yaml\n", + "environmental_condition:\n", + " field: condition # Renames 'condition' to 'environmental_condition'\n", + "```\n", + "\n", + "This is useful for:\n", + "- Standardizing field names across datasets\n", + "- Enabling normalization of the field values" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Factor Aliases (Value Normalization)\n", + "\n", + "The `factor_aliases` section maps varying terminologies to standard values:\n", + "\n", + "```yaml\n", + "factor_aliases:\n", + " carbon_source:\n", + " glucose: [D-glucose, dextrose, glu]\n", + " galactose: [D-galactose, gal]\n", + "```\n", + "\n", + "This means:\n", + "- Querying for `\"glucose\"` matches `\"D-glucose\"`, `\"dextrose\"`, and `\"glu\"`\n", + "- All returned values are normalized to the canonical form (`\"glucose\"`)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 7. Real-World Example: Cross-Dataset Comparison\n", + "\n", + "Let's do a practical analysis comparing experiments across datasets." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Compare number of samples by carbon source across datasets\n", + "\n", + "# Get all samples\n", + "all_samples = vdb.query()\n", + "\n", + "# Count by dataset and carbon source\n", + "summary = all_samples.groupby(['_repo_id', '_config_name', 'carbon_source']).size()\n", + "summary = summary.reset_index(name='num_samples')\n", + "\n", + "print(\"Sample counts by dataset and carbon source:\")\n", + "print(summary.to_string(index=False))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Compare glucose experiments at different temperatures\n", + "\n", + "glucose_by_temp = vdb.query(\n", + " filters={\"carbon_source\": \"glucose\"},\n", + " fields=[\"sample_id\", \"temperature_celsius\", \"environmental_condition\"]\n", + ")\n", + "\n", + "# Count samples by temperature\n", + "temp_counts = glucose_by_temp['temperature_celsius'].value_counts().sort_index()\n", + "\n", + "print(\"Glucose samples by temperature:\")\n", + "for temp, count in temp_counts.items():\n", + " print(f\" {temp}C: {count} samples\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Get binding data for a specific regulator across datasets\n", + "\n", + "# Query for FHL1 binding in glucose conditions\n", + "fhl1_binding = vdb.query(\n", + " filters={\n", + " \"carbon_source\": \"glucose\",\n", + " \"regulator_symbol\": \"FHL1\"\n", + " },\n", + " fields=[\"sample_id\", \"regulator_symbol\", \"target_symbol\", \"effect\", \"pvalue\"],\n", + " complete=True\n", + ")\n", + "\n", + "print(f\"Found {len(fhl1_binding)} FHL1 binding measurements in glucose\")\n", + "\n", + "# Find significant targets (p < 0.001)\n", + "significant = fhl1_binding[fhl1_binding['pvalue'] < 0.001]\n", + "print(f\"Significant targets: {len(significant)}\")\n", + "\n", + "# Top 10 by effect size\n", + "top_targets = significant.nlargest(10, 'effect')[['target_symbol', 'effect', 'pvalue']]\n", + "print(\"\\nTop 10 targets by effect size:\")\n", + "print(top_targets.to_string(index=False))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 8. Best Practices\n", + "\n", + "### 1. Use Field Selection\n", + "\n", + "Only request the fields you need - it's more efficient:\n", + "\n", + "```python\n", + "# Good: Only get what you need\n", + "df = vdb.query(\n", + " filters={\"carbon_source\": \"glucose\"},\n", + " fields=[\"sample_id\", \"temperature_celsius\"]\n", + ")\n", + "\n", + "# Less efficient: Get all fields\n", + "df = vdb.query(filters={\"carbon_source\": \"glucose\"})\n", + "```\n", + "\n", + "### 2. Filter Before Complete Data\n", + "\n", + "Use filters to reduce the dataset before getting complete data:\n", + "\n", + "```python\n", + "# Good: Filter first\n", + "df = vdb.query(\n", + " filters={\"carbon_source\": \"glucose\", \"temperature_celsius\": 30},\n", + " complete=True\n", + ")\n", + "\n", + "# Less efficient: Get all data then filter in pandas\n", + "df = vdb.query(complete=True)\n", + "df = df[(df['carbon_source'] == 'glucose') & (df['temperature_celsius'] == 30)]\n", + "```\n", + "\n", + "### 3. Use Normalized Values\n", + "\n", + "Always query using the normalized (canonical) values from your config:\n", + "\n", + "```python\n", + "# Good: Use canonical value\n", + "df = vdb.query(filters={\"carbon_source\": \"glucose\"})\n", + "\n", + "# Might miss data: Using original value\n", + "df = vdb.query(filters={\"carbon_source\": \"D-glucose\"}) # Only matches exact string\n", + "```\n", + "\n", + "### 4. Check Available Values First\n", + "\n", + "Use `get_unique_values()` to discover what values exist:\n", + "\n", + "```python\n", + "# See what carbon sources are available\n", + "sources = vdb.get_unique_values(\"carbon_source\")\n", + "print(sources)\n", + "\n", + "# Then query for specific ones\n", + "df = vdb.query(filters={\"carbon_source\": \"galactose\"})\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "VirtualDB provides a powerful abstraction for querying heterogeneous genomic datasets:\n", + "\n", + "1. **Schema Discovery**: Explore available fields and values with `get_fields()` and `get_unique_values()`\n", + "2. **Unified Queries**: Query across datasets using standardized field names\n", + "3. **Automatic Normalization**: Varying terminologies automatically mapped to common values\n", + "4. **Flexible Filtering**: Support for exact matches, ranges, and complex conditions\n", + "5. **Sample or Complete Data**: Choose between metadata-only or full measurements\n", + "\n", + "For more details on the configuration format and design principles, see the [Virtual Database Design](../virtual_database_concepts.md) documentation." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "tfbpapi-py3.11", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/virtual_database_concepts.md b/docs/virtual_database_concepts.md new file mode 100644 index 0000000..093502a --- /dev/null +++ b/docs/virtual_database_concepts.md @@ -0,0 +1,295 @@ +# Virtual Database + +VirtualDB provides a unified query interface across heterogeneous datasets with +different experimental condition structures and terminologies. Each dataset +defines experimental conditions in its own way, with properties stored at +different hierarchy levels (repository, dataset, or field) and using different +naming conventions. VirtualDB uses an external YAML configuration to map these +varying structures to a common schema, normalize factor level names (e.g., +"D-glucose", "dextrose", "glu" all become "glucose"), and enable cross-dataset +queries with standardized field names and values. + +## Configuration Structure + +A configuration file defines the virtual database schema with four sections: + +```yaml +# ===== Repository Configurations ===== +repositories: + # Each repository defines a "table" in the virtual database + BrentLab/harbison_2004: + # Repository-wide properties (apply to all datasets in this repository) + nitrogen_source: + path: media.nitrogen_source.name + + dataset: + # Each dataset gets its own view with standardized fields + harbison_2004: + # Dataset-specific properties (constant for all samples) + phosphate_source: + path: media.phosphate_source.compound + + # Field-level properties (vary per sample) + carbon_source: + field: condition + path: media.carbon_source.compound + + # Field without path (column alias with normalization) + environmental_condition: + field: condition + + BrentLab/kemmeren_2014: + dataset: + kemmeren_2014: + # Same logical fields, different physical paths + carbon_source: + path: media.carbon_source.compound + temperature_celsius: + path: temperature_celsius + +# ===== Normalization Rules ===== +# Map varying terminologies to standardized values +factor_aliases: + carbon_source: + glucose: [D-glucose, glu, dextrose] + galactose: [D-galactose, gal] + +# Handle missing values with defaults +missing_value_labels: + carbon_source: "unspecified" + +# ===== Documentation ===== +description: + carbon_source: The carbon source provided to the cells during growth +``` + +### Property Hierarchy + +Properties are extracted at three hierarchy levels: + +1. **Repository-wide**: Common to all datasets in a repository + - Paths relative to repository-level `experimental_conditions` + - Example: `path: media.nitrogen_source.name` + +2. **Dataset-specific**: Specific to one dataset configuration + - Paths relative to config-level `experimental_conditions` + - Example: `path: media.phosphate_source.compound` + +3. **Field-level**: Vary per sample, defined in field definitions + - `field` specifies which field to extract from + - `path` relative to field definitions (not `experimental_conditions`) + - Example: `field: condition, path: media.carbon_source.compound` + +**Special case**: Field without path creates a column alias +- `field: condition` (no path) to renames `condition` column, enables normalization + +### Path Resolution + +Paths use dot notation to navigate nested structures: + +**Repository/Dataset-level** (automatically prepends `experimental_conditions.`): +- `path: temperature_celsius` to `experimental_conditions.temperature_celsius` +- `path: media.carbon_source.compound` to + `experimental_conditions.media.carbon_source.compound` + +**Field-level** (paths relative to field definitions): +- `field: condition, path: media.carbon_source.compound` to looks in field +`condition`'s definitions to navigates to `media.carbon_source.compound` + +## VirtualDB Structure + +VirtualDB maintains a collection of dataset-specific metadata tables, one per +configured dataset. Each table has the same structure (standardized schema) but +contains data specific to that dataset. + +### Internal Structure + +When materialized (or conceptually if not materialized), VirtualDB contains: + +```python +{ + ("BrentLab/harbison_2004", "harbison_2004"): DataFrame( + # Columns: sample_id, carbon_source, temperature_celsius, nitrogen_source, ... + # Values: Normalized according to factor_aliases + # Example rows: + # sample_id carbon_source temperature_celsius nitrogen_source + # harbison_001 glucose 30 yeast nitrogen base + # harbison_002 galactose 30 yeast nitrogen base + ), + + ("BrentLab/kemmeren_2014", "kemmeren_2014"): DataFrame( + # Columns: sample_id, carbon_source, temperature_celsius, ... + # Note: Different physical source paths, same logical schema + # Example rows: + # sample_id carbon_source temperature_celsius + # kemmeren_001 glucose 30 + # kemmeren_002 raffinose 30 + ) +} +``` + +### View Materialization + +By default, VirtualDB computes views on-demand. For performance, views can be +materialized (cached): + +```python +# Cache all views for faster subsequent queries +vdb.materialize_views() + +# Cache specific datasets +vdb.materialize([("BrentLab/harbison_2004", "harbison_2004")]) + +# Invalidate cache (e.g., after data updates) +vdb.invalidate_cache() +vdb.invalidate_cache([("BrentLab/harbison_2004", "harbison_2004")]) +``` + +Materialized views are stored locally and reused for queries until invalidated. + +## VirtualDB Interface + +### Schema Discovery + +**List all queryable fields**: +```python +from tfbpapi.virtual_db import VirtualDB + +vdb = VirtualDB("config.yaml") + +# All fields defined in any dataset +fields = vdb.get_fields() +# ["carbon_source", "temperature_celsius", "nitrogen_source", "phosphate_source", ...] + +# Fields present in ALL datasets (common fields) +common = vdb.get_common_fields() +# ["carbon_source", "temperature_celsius"] + +# Fields for specific dataset +dataset_fields = vdb.get_fields("BrentLab/harbison_2004", "harbison_2004") +# ["carbon_source", "temperature_celsius", "nitrogen_source", "phosphate_source"] +``` + +**Discover valid values for fields**: +```python +# Unique values across all datasets (normalized) +values = vdb.get_unique_values("carbon_source") +# ["glucose", "galactose", "raffinose", "unspecified"] + +# Values broken down by dataset +values_by_dataset = vdb.get_unique_values("carbon_source", by_dataset=True) +# { +# "BrentLab/harbison_2004": ["glucose", "galactose"], +# "BrentLab/kemmeren_2014": ["glucose", "raffinose"] +# } +``` + +### Querying Data + +The `query()` method is the primary interface for retrieving data from VirtualDB. + +**Basic usage** (sample-level, all fields): +```python +# Query across all configured datasets +# Returns one row per sample with all configured fields +df = vdb.query(filters={"carbon_source": "glucose"}) +# DataFrame: sample_id, carbon_source, temperature_celsius, nitrogen_source, ... +``` + +**Query specific datasets**: +```python +# Limit query to specific datasets +df = vdb.query( + filters={"carbon_source": "glucose", "temperature_celsius": 30}, + datasets=[("BrentLab/harbison_2004", "harbison_2004")] +) +``` + +**Select specific fields**: +```python +# Return only specified fields +df = vdb.query( + filters={"carbon_source": "glucose"}, + fields=["sample_id", "carbon_source", "temperature_celsius"] +) +# DataFrame: sample_id, carbon_source, temperature_celsius +``` + +**Complete data** (measurement-level): +```python +# Set complete=True to get all measurements, not just sample-level +# Returns many rows per sample (one per target/feature/coordinate) +df = vdb.query( + filters={"carbon_source": "glucose"}, + complete=True +) +# DataFrame: sample_id, target, value, carbon_source, temperature_celsius, ... +# For annotated_features: target-level data for all matching samples +# For genome_map: coordinate-level data for all matching samples + +# Can combine with field selection +df = vdb.query( + filters={"carbon_source": "glucose"}, + fields=["sample_id", "target", "effect"], + complete=True +) +# DataFrame: sample_id, target, effect +``` + +### Factor Alias Expansion + +When querying with aliased values, VirtualDB automatically expands to all +original values: + +```python +# User queries for normalized value +df = vdb.query(filters={"carbon_source": "galactose"}) + +# Internally expands to all aliases +# WHERE carbon_source IN ('D-galactose', 'gal', 'galactose') +``` + +This ensures all samples are retrieved regardless of original terminology. + +### Numeric Field Filtering + +Numeric fields support exact matching and range queries: + +```python +# Exact match +df = vdb.query(filters={"temperature_celsius": 30}) + +# Range queries +df = vdb.query(filters={"temperature_celsius": (">=", 28)}) +df = vdb.query(filters={"temperature_celsius": ("between", 28, 32)}) + +# Missing value labels (from missing_value_labels config) +df = vdb.query(filters={"temperature_celsius": "room"}) +# Matches samples where temperature is None/missing +``` + +## Design Principles + +### Virtual Views by Default + +Views are computed on-demand unless explicitly materialized, but optionally cached, +This provides: + +- Reduces setup time when a new instance instantiates MetadataBuilder/VirtualDB +- No storage overhead for casual queries + +### External Configuration as Schema + +The YAML configuration serves as a **database schema definition**: + +- Defines what fields exist (logical schema) +- Maps to physical data structures (via paths) +- Specifies normalization rules (via aliases) +- Documents field semantics (via descriptions) + +This separation enables: + +- Schema updates without code changes + +See: +- [DataCard Documentation](huggingface_datacard.md) diff --git a/docs/virtual_db.md b/docs/virtual_db.md new file mode 100644 index 0000000..bec3ab4 --- /dev/null +++ b/docs/virtual_db.md @@ -0,0 +1,16 @@ +# VirtualDB + +::: tfbpapi.virtual_db.VirtualDB + options: + show_root_heading: true + show_source: true + +## Helper Functions + +::: tfbpapi.virtual_db.get_nested_value + options: + show_root_heading: true + +::: tfbpapi.virtual_db.normalize_value + options: + show_root_heading: true diff --git a/example_filter_config.yaml b/example_filter_config.yaml deleted file mode 100644 index a2c66ca..0000000 --- a/example_filter_config.yaml +++ /dev/null @@ -1,75 +0,0 @@ -# Example filter configuration for DatasetFilterResolver -# -# This file demonstrates how to specify filters and dataset-specific -# property mappings for heterogeneous datasets. -# -# New format: repositories as top-level keys (not nested under dataset_mappings) -# -# Usage: -# from tfbpapi.datainfo.filter_resolver import DatasetFilterResolver -# resolver = DatasetFilterResolver("example_filter_config.yaml") -# results = resolver.resolve_filters([("BrentLab/harbison_2004", "harbison_2004")]) - -# Filters specify acceptable values for each property -# Multiple values = OR logic (match any) -# Multiple properties = AND logic (match all) -filters: - carbon_source: - - "D-glucose" - - "D-galactose" - - temperature_celsius: - - 30 - - 37 - -# Repository configurations specify where to find each property -# Top-level keys are repository IDs (e.g., "BrentLab/harbison_2004") - -# FIELD-LEVEL EXAMPLE: Harbison 2004 -# Properties are in field definitions (condition field) -BrentLab/harbison_2004: - dataset: - harbison_2004: - carbon_source: - field: condition # Look in the 'condition' field's definitions - path: media.carbon_source - temperature_celsius: - field: condition - path: temperature_celsius - -# REPO-WIDE + DATASET-SPECIFIC EXAMPLE: Kemmeren 2014 -# temperature_celsius is repo-wide (applies to all datasets) -# carbon_source is dataset-specific -BrentLab/kemmeren_2014: - # Repo-wide property (applies to all datasets in this repository) - temperature_celsius: - path: temperature_celsius - - # Dataset-specific properties - dataset: - kemmeren_2014: - carbon_source: - path: media.carbon_source - -# CONFIGURATION STRUCTURE EXPLAINED: -# -# Repo-wide properties: -# - Properties directly under repository ID -# - Apply to ALL datasets in that repository -# - Path is relative to experimental_conditions -# - Example: temperature_celsius: path: temperature_celsius -# -> looks in experimental_conditions.temperature_celsius -# -# Dataset-specific properties: -# - Properties under dataset.{dataset_name} -# - Override repo-wide for that specific dataset -# - Can use 'field' to specify field-level definitions -# - Example with field: carbon_source: field: condition, path: media.carbon_source -# -> looks in condition field definitions -# - Example without field: carbon_source: path: media.carbon_source -# -> looks in experimental_conditions.media.carbon_source -# -# Property name aliasing: -# - The filter property name (e.g., "temperature") can differ from the datacard property name -# - Example: temperature: path: temperature_celsius -# Filter uses "temperature", datacard has "temperature_celsius" diff --git a/mkdocs.yml b/mkdocs.yml index a751e9f..0635060 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -149,20 +149,23 @@ extra_css: nav: - Home: index.md - Tutorials: - - "DataCard: Exploring Datasets": tutorials/datacard_tutorial.ipynb - - "Cache Management": tutorials/cache_manager_tutorial.ipynb - - "Querying the Datasets": tutorials/hfqueryapi_tutorial.ipynb - - "Rank Response Analysis": tutorials/rank_response_tutorial.ipynb + - "Getting Started": + - "DataCard: Exploring Datasets": tutorials/datacard_tutorial.ipynb + - "Cache Management": tutorials/cache_manager_tutorial.ipynb + - "Querying Data": + - "VirtualDB: Unified Cross-Dataset Queries": tutorials/virtual_db_tutorial.ipynb + - Concepts: + - "Virtual Database Design": virtual_database_concepts.md - API Reference: - - Core Components: - - HfQueryAPI: HfQueryAPI.md - - HfRankResponse: HfRankResponse.md - - IncrementalAnalysisDB: IncrementalAnalysisDB.md - - Cache Management: - - HfCacheManager: HfCacheManager.md - - Dataset Information: - - DataInfo Package: datainfo.md + - Core: + - VirtualDB: virtual_db.md + - DataCard: datacard.md + - HfCacheManager: hf_cache_manager.md + - Models and Configuration: + - Pydantic Models: models.md + - Fetchers: fetchers.md - Error Handling: - Custom Exceptions: errors.md - HuggingFace Configuration: - HuggingFace Dataset Card Format: huggingface_datacard.md + - BrentLab Collection: brentlab_yeastresources_collection.md diff --git a/tfbpapi/HfQueryAPI.py b/tfbpapi/HfQueryAPI.py deleted file mode 100644 index 02ae684..0000000 --- a/tfbpapi/HfQueryAPI.py +++ /dev/null @@ -1,739 +0,0 @@ -import logging -import re -from pathlib import Path -from typing import Literal - -import duckdb -import pandas as pd - -from .constants import CACHE_DIR, SQL_FILTER_KEYWORDS -from .errors import InvalidFilterFieldError -from .HfCacheManager import HfCacheManager - - -class HfQueryAPI(HfCacheManager): - """Minimal Hugging Face API client focused on metadata retrieval.""" - - def __init__( - self, - repo_id: str, - repo_type: Literal["model", "dataset", "space"] = "dataset", - token: str | None = None, - cache_dir: str | Path | None = None, - duckdb_conn: duckdb.DuckDBPyConnection = duckdb.connect(":memory:"), - ): - """ - Initialize the minimal HF Query API client. - - :param repo_id: Repository identifier (e.g., "user/dataset") - :param repo_type: Type of repository ("dataset", "model", "space") - :param token: HuggingFace token for authentication - :param cache_dir: HF cache_dir for downloads - - """ - self._duckdb_conn = duckdb_conn - - # Initialize parent with minimal setup - super().__init__( - repo_id=repo_id, - duckdb_conn=self._duckdb_conn, - token=token, - logger=logging.getLogger(self.__class__.__name__), - ) - - # Store basic configuration - self.repo_type = repo_type - self.cache_dir = Path(cache_dir) if cache_dir is not None else CACHE_DIR - - # Filter storage system - # dict structure: - # {config_name: "SQL WHERE clause", ...} - self._table_filters: dict[str, str] = {} - - @property - def cache_dir(self) -> Path: - return self._cache_dir - - @cache_dir.setter - def cache_dir(self, value: str | Path) -> None: - """Set the cache directory for huggingface_hub downloads.""" - path = Path(value) - if not path.exists(): - raise FileNotFoundError(f"Cache directory {path} does not exist") - self._cache_dir = path - - def _get_explicit_metadata(self, config, table_name: str) -> pd.DataFrame: - """Helper function to handle explicit metadata configurations.""" - sql = f"SELECT * FROM {table_name}" - return self.duckdb_conn.execute(sql).fetchdf() - - def _get_embedded_metadata(self, config, table_name: str) -> pd.DataFrame: - """Helper function to handle embedded metadata configurations.""" - if config.metadata_fields is None: - raise ValueError(f"Config {config.config_name} has no metadata fields") - fields = ", ".join(config.metadata_fields) - where_clauses = " AND ".join( - [f"{field} IS NOT NULL" for field in config.metadata_fields] - ) - sql = f""" - SELECT DISTINCT {fields}, COUNT(*) as count - FROM {table_name} - WHERE {where_clauses} - GROUP BY {fields} - ORDER BY count DESC - """ - return self.duckdb_conn.execute(sql).fetchdf() - - def _validate_metadata_fields( - self, config_name: str, field_names: list[str] - ) -> None: - """ - Validate that field names exist in the config's metadata columns. - - :param config_name: Configuration name to validate against - :param field_names: List of field names to validate - :raises InvalidFilterFieldError: If any fields don't exist in metadata - - """ - if not field_names: - return - - try: - metadata_df = self.get_metadata(config_name) - if metadata_df.empty: - raise InvalidFilterFieldError( - config_name=config_name, - invalid_fields=field_names, - available_fields=[], - ) - - available_fields = list(metadata_df.columns) - invalid_fields = [ - field for field in field_names if field not in available_fields - ] - - if invalid_fields: - raise InvalidFilterFieldError( - config_name=config_name, - invalid_fields=invalid_fields, - available_fields=available_fields, - ) - except Exception as e: - if isinstance(e, InvalidFilterFieldError): - raise - # If metadata retrieval fails for other reasons, log warning but allow - self.logger.warning( - f"Could not validate filter fields for {config_name}: {e}" - ) - - def _extract_fields_from_sql(self, sql_where: str) -> list[str]: - """ - Extract potential field names from SQL WHERE clause. - - Uses a more robust approach to identify column references while avoiding string - literals used as values. - - :param sql_where: SQL WHERE clause (without 'WHERE' keyword) - :return: List of potential field names found in the SQL - - """ - if not sql_where.strip(): - return [] - - field_names = set() - - # Tokenize the SQL to better understand context - # This regex splits on key tokens while preserving them - tokens = re.findall( - r""" - \bIN\s*\([^)]+\)| # IN clauses with content - \bBETWEEN\s+\S+\s+AND\s+\S+| # BETWEEN clauses - (?:'[^']*')|(?:"[^"]*")| # Quoted strings - \b(?:AND|OR|NOT|IS|NULL|LIKE|BETWEEN|IN)\b| # SQL keywords - [=!<>]+| # Comparison operators - [(),]| # Delimiters - \b[a-zA-Z_][a-zA-Z0-9_]*\b| # Identifiers - \S+ # Other tokens - """, - sql_where, - re.VERBOSE | re.IGNORECASE, - ) - - # Track the context to determine if an identifier is a field name or value - i = 0 - while i < len(tokens): - token = tokens[i].strip() - if not token: - i += 1 - continue - - # Skip IN clauses entirely - they contain values, not field names - if re.match(r"\bIN\s*\(", token, re.IGNORECASE): - i += 1 - continue - - # Skip BETWEEN clauses entirely - they contain values, not field names - if re.match(r"\bBETWEEN\b", token, re.IGNORECASE): - i += 1 - continue - - # Handle quoted strings - could be identifiers or values depending on context - if token.startswith(("'", '"')): - # Extract the content inside quotes - quoted_content = token[1:-1] - - # Find next significant token to determine context - next_significant_token = None - for j in range(i + 1, len(tokens)): - next_token = tokens[j].strip() - if next_token and next_token not in [" ", "\n", "\t"]: - next_significant_token = next_token - break - - # Check if this quoted string is a field name based on context - is_quoted_field = False - - # Check what comes after this quoted string - if next_significant_token: - # If followed by comparison operators or SQL keywords, it's a field name - if ( - next_significant_token - in ["=", "!=", "<>", "<", ">", "<=", ">="] - or next_significant_token.upper() in ["IS", "LIKE", "NOT"] - or re.match( - r"\bBETWEEN\b", next_significant_token, re.IGNORECASE - ) - or re.match(r"\bIN\s*\(", next_significant_token, re.IGNORECASE) - ): - is_quoted_field = True - - # Also check what comes before this quoted string - if not is_quoted_field and i > 0: - # Find the previous significant token - prev_significant_token = None - for j in range(i - 1, -1, -1): - prev_token = tokens[j].strip() - if prev_token and prev_token not in [" ", "\n", "\t"]: - prev_significant_token = prev_token - break - - # If preceded by a comparison operator, could be a field name - # But we need to be very careful not to treat string literals as field names - if prev_significant_token and prev_significant_token in [ - "=", - "!=", - "<>", - "<", - ">", - "<=", - ">=", - ]: - # Only treat as field name if it looks like a database identifier - # AND doesn't look like a typical string value - if self._looks_like_identifier( - quoted_content - ) and self._looks_like_database_identifier(quoted_content): - is_quoted_field = True - - if is_quoted_field: - field_names.add(quoted_content) - - i += 1 - continue - - # Skip SQL keywords and operators - if token.upper() in SQL_FILTER_KEYWORDS or token in [ - "=", - "!=", - "<>", - "<", - ">", - "<=", - ">=", - "(", - ")", - ",", - ]: - i += 1 - continue - - # Skip numeric literals - if re.match(r"^-?\d+(\.\d+)?$", token): - i += 1 - continue - - # Check if this looks like an identifier (field name) - if re.match(r"^[a-zA-Z_][a-zA-Z0-9_]*$", token): - # Check the context - if the next non-whitespace token is a comparison operator, - # then this is likely a field name - next_significant_token = None - for j in range(i + 1, len(tokens)): - next_token = tokens[j].strip() - if next_token and next_token not in [" ", "\n", "\t"]: - next_significant_token = next_token - break - - # Check if followed by a comparison operator or SQL keyword that indicates a field - is_field = False - - if next_significant_token: - # Direct comparison operators - if next_significant_token in [ - "=", - "!=", - "<>", - "<", - ">", - "<=", - ">=", - ]: - is_field = True - # SQL keywords that follow field names - elif next_significant_token.upper() in ["IS", "LIKE", "NOT"]: - is_field = True - # BETWEEN clause (could be just 'BETWEEN' or 'BETWEEN ... AND ...') - elif next_significant_token.upper() == "BETWEEN" or re.match( - r"\bBETWEEN\b", next_significant_token, re.IGNORECASE - ): - is_field = True - # IN clause (could be just 'IN' or 'IN (...)') - elif next_significant_token.upper() == "IN" or re.match( - r"\bIN\s*\(", next_significant_token, re.IGNORECASE - ): - is_field = True - - # If not a field yet, check other contexts - if not is_field and i > 0: - # Find the previous significant token - prev_significant_token = None - for j in range(i - 1, -1, -1): - prev_token = tokens[j].strip() - if prev_token and prev_token not in [" ", "\n", "\t"]: - prev_significant_token = prev_token - break - - # Case 1: After AND/OR and before an operator (original logic) - if ( - prev_significant_token - and prev_significant_token.upper() in ["AND", "OR"] - and next_significant_token - ): - # Same checks as above - if next_significant_token in [ - "=", - "!=", - "<>", - "<", - ">", - "<=", - ">=", - ]: - is_field = True - elif next_significant_token.upper() in ["IS", "LIKE", "NOT"]: - is_field = True - elif next_significant_token.upper() == "BETWEEN" or re.match( - r"\bBETWEEN\b", next_significant_token, re.IGNORECASE - ): - is_field = True - elif next_significant_token.upper() == "IN" or re.match( - r"\bIN\s*\(", next_significant_token, re.IGNORECASE - ): - is_field = True - - # Case 2: After a comparison operator (second operand) - elif prev_significant_token and prev_significant_token in [ - "=", - "!=", - "<>", - "<", - ">", - "<=", - ">=", - ]: - # But exclude function names (identifiers followed by '(') - if next_significant_token != "(": - is_field = True - - # Case 3: After opening parenthesis (function parameter) - elif prev_significant_token == "(": - is_field = True - - if is_field: - field_names.add(token) - - i += 1 - - return list(field_names) - - def _looks_like_identifier(self, content: str) -> bool: - """ - Determine if quoted content looks like an identifier rather than a string - literal. - - :param content: The content inside quotes - :return: True if it looks like an identifier, False if it looks like a string - literal - - """ - if not content: - return False - - # Basic identifier pattern: starts with letter/underscore, contains only alphanumeric/underscore - if re.match(r"^[a-zA-Z_][a-zA-Z0-9_]*$", content): - return True - - # Extended identifier pattern: could contain spaces if it's a column name like "quoted field" - # but not if it contains many special characters or looks like natural language - if " " in content: - # If it contains spaces, it should still look identifier-like - # Allow simple cases like "quoted field" but not "this is a long string value" - words = content.split() - if len(words) <= 3 and all( - re.match(r"^[a-zA-Z_][a-zA-Z0-9_]*$", word) for word in words - ): - return True - return False - - return False - - def _looks_like_database_identifier(self, content: str) -> bool: - """ - Determine if content looks like a database identifier (field/table name). - - This is more strict than _looks_like_identifier and helps distinguish between - quoted identifiers like "field_name" and string values like "value1". - - :param content: The content to check - :return: True if it looks like a database identifier - - """ - if not content: - return False - - # Database identifiers typically: - # 1. Don't start with numbers (field names rarely start with numbers) - # 2. Often contain underscores or descriptive words - # 3. Don't look like simple values - - # Reject if starts with a number (like "value1", "123abc") - if content[0].isdigit(): - return False - - # Handle short simple values that could be literals or field names - if len(content) <= 6 and re.match(r"^[a-z]+\d*$", content.lower()): - # Allow common field name prefixes - field_prefixes = ["field", "col", "column", "attr", "prop"] - if any(content.lower().startswith(prefix) for prefix in field_prefixes): - return True # It's a valid field name like "field1", "col2" - else: - return False # It's likely a simple value like "value", "test" - - # Accept if it contains underscore (common in field names) - if "_" in content: - return True - - # Accept if it has multiple words (like "quoted field") - if " " in content: - return True - - # Accept if it's a longer descriptive name - if len(content) > 8: - return True - - # Reject otherwise (likely a simple value) - return False - - def get_metadata( - self, config_name: str, refresh_cache: bool = False - ) -> pd.DataFrame: - """ - Retrieve metadata as a DataFrame with actual metadata values for a specific - config. - - Supports three types of metadata retrieval: - 1. Direct metadata configs: config_name is itself a metadata config - 2. Embedded metadata: config_name has metadata_fields defined - 3. Applied metadata: config_name appears in another metadata config's applies_to list - - For explicit metadata configs (types 1 & 3), returns all rows from metadata table. - For embedded metadata (type 2), returns distinct combinations of metadata fields. - - :param config_name: Specific config name to retrieve metadata for - :param refresh_cache: If True, force refresh from remote instead of using cache - :return: DataFrame with metadata values for the specified config - :raises ValueError: If config_name has no associated metadata - :raises RuntimeError: If data loading fails for the config - - """ - # Get metadata relationships for this config - relationships = self.get_metadata_relationships(refresh_cache=refresh_cache) - - relevant_relationships = None - - # First priority: data_config matches (config_name is a data config with metadata) - data_config_matches = [r for r in relationships if r.data_config == config_name] - - if data_config_matches: - relevant_relationships = data_config_matches - else: - # Second priority: metadata_config matches (config_name is itself a metadata config) - metadata_config_matches = [ - r for r in relationships if r.metadata_config == config_name - ] - relevant_relationships = metadata_config_matches - - if not relevant_relationships: - # Check what configs are available for helpful error message - all_data_configs = {r.data_config for r in relationships} - all_metadata_configs = {r.metadata_config for r in relationships} - all_available = sorted(all_data_configs | all_metadata_configs) - - if not all_available: - return pd.DataFrame() - - raise ValueError( - f"Config '{config_name}' not found. " - f"Available configs with metadata: {all_available}" - ) - - # Get the config object to process - # For explicit relationships, use the metadata config - # For embedded relationships, use the data config - relationship = relevant_relationships[0] # Use first relationship found - - if relationship.relationship_type == "explicit": - # Find the metadata config - if relationship.metadata_config == config_name: - # config_name is itself a metadata config - config = self.get_config(config_name) - else: - # config_name is a data config with metadata applied to it - config = self.get_config(relationship.metadata_config) - else: # embedded - # config_name is a data config with embedded metadata - config = self.get_config(config_name) - - if not config: - raise ValueError(f"Could not find config object for '{config_name}'") - - # Process the single configuration - config_result = self._get_metadata_for_config( - config, force_refresh=refresh_cache - ) - - if not config_result.get("success", False): - raise RuntimeError(f"Failed to load data for config {config.config_name}") - - table_name = config_result.get("table_name") - if not table_name: - raise RuntimeError(f"No table name for config {config.config_name}") - - try: - if relationship.relationship_type == "explicit": - return self._get_explicit_metadata(config, table_name) - else: # embedded - return self._get_embedded_metadata(config, table_name) - except Exception as e: - self.logger.error(f"Error querying metadata for {config.config_name}: {e}") - raise - - def set_filter(self, config_name: str, **kwargs) -> None: - """ - Set simple filters using keyword arguments. - - Converts keyword arguments to SQL WHERE clause and stores - for automatic application. Validates that all filter fields - exist in the config's metadata columns. - - :param config_name: Configuration name to apply filters to - :param kwargs: Filter conditions as keyword arguments - (e.g., time=15, mechanism="ZEV") - :raises InvalidFilterFieldError: If any filter field doesn't exist - in the metadata columns - - Example: - api.set_filter("hackett_2020", time=15, mechanism="ZEV", restriction="P") - # Equivalent to: WHERE time = 15 AND mechanism = 'ZEV' AND restriction = 'P' - - """ - if not kwargs: - # If no kwargs provided, clear the filter - self.clear_filter(config_name) - return - - # Validate that all filter fields exist in metadata columns - self._validate_metadata_fields(config_name, list(kwargs.keys())) - - # Convert kwargs to SQL WHERE clause - conditions = [] - for key, value in kwargs.items(): - if isinstance(value, str): - # String values need quotes - conditions.append(f"{key} = '{value}'") - elif value is None: - # Handle NULL values - conditions.append(f"{key} IS NULL") - else: - # Numeric/boolean values - conditions.append(f"{key} = {value}") - - where_clause = " AND ".join(conditions) - self._table_filters[config_name] = where_clause - self.logger.info(f"Set filter for {config_name}: {where_clause}") - - def set_sql_filter( - self, config_name: str, sql_where: str, validate_fields: bool = True - ) -> None: - """ - Set complex filters using SQL WHERE clause. - - Stores raw SQL WHERE clause for automatic application to queries. - Validates that field references in the SQL exist in metadata columns - unless validation is disabled. - - :param config_name: Configuration name to apply filters to - :param sql_where: SQL WHERE clause (without the 'WHERE' keyword) - :param validate_fields: Whether to validate field names (default: True) - :raises InvalidFilterFieldError: If any field references don't exist - in the metadata columns (when validate_fields=True) - - Example: - api.set_sql_filter("hackett_2020", "time IN (15, 30) AND mechanism = 'ZEV'") - # To skip validation for complex SQL: - api.set_sql_filter("hackett_2020", "complex_expression(...)", validate_fields=False) - - """ - if not sql_where.strip(): - self.clear_filter(config_name) - return - - # Validate fields if requested - if validate_fields: - extracted_fields = self._extract_fields_from_sql(sql_where) - self._validate_metadata_fields(config_name, extracted_fields) - - self._table_filters[config_name] = sql_where.strip() - self.logger.info(f"Set SQL filter for {config_name}: {sql_where}") - - def clear_filter(self, config_name: str) -> None: - """ - Remove all filters for the specified configuration. - - :param config_name: Configuration name to clear filters for - - """ - if config_name in self._table_filters: - del self._table_filters[config_name] - self.logger.info(f"Cleared filter for {config_name}") - - def get_current_filter(self, config_name: str) -> str | None: - """ - Get the current filter for the specified configuration. - - :param config_name: Configuration name to get filter for - :return: Current SQL WHERE clause or None if no filter set - - """ - return self._table_filters.get(config_name) - - def query( - self, sql: str, config_name: str, refresh_cache: bool = False - ) -> pd.DataFrame: - """ - Execute SQL query with automatic filter application. - - Loads the specified configuration, applies any stored filters, - and executes the query. - - :param sql: SQL query to execute - :param config_name: Configuration name to query (table will be loaded if needed) - :param refresh_cache: If True, force refresh from remote instead of using cache - :return: DataFrame with query results - :raises ValueError: If config_name not found or query fails - - Example: - api.set_filter("hackett_2020", time=15, mechanism="ZEV") - df = api.query("SELECT regulator_locus_tag, target_locus_tag - FROM hackett_2020", "hackett_2020") - # Automatically applies: WHERE time = 15 AND mechanism = 'ZEV' - - """ - # Validate config exists - if config_name not in [c.config_name for c in self.configs]: - available_configs = [c.config_name for c in self.configs] - raise ValueError( - f"Config '{config_name}' not found. " - f"Available configs: {available_configs}" - ) - - # Load the configuration data - config = self.get_config(config_name) - if not config: - raise ValueError(f"Could not retrieve config '{config_name}'") - - config_result = self._get_metadata_for_config( - config, force_refresh=refresh_cache - ) - if not config_result.get("success", False): - raise ValueError( - f"Failed to load data for config '{config_name}': " - f"{config_result.get('message', 'Unknown error')}" - ) - - table_name = config_result.get("table_name") - if not table_name: - raise ValueError(f"No table available for config '{config_name}'") - - # Replace config name with actual table name in SQL for user convenience - sql_with_table = sql.replace(config_name, table_name) - - # Apply stored filters - final_sql = self._apply_filter_to_sql(sql_with_table, config_name) - - try: - return self.duckdb_conn.execute(final_sql).fetchdf() - except Exception as e: - self.logger.error(f"Query execution failed: {e}") - self.logger.error(f"Final SQL: {final_sql}") - raise ValueError(f"Query execution failed: {e}") from e - - def _apply_filter_to_sql(self, sql: str, config_name: str) -> str: - """ - Apply stored filters to SQL query. - - Modifies the SQL query to include stored WHERE clause filters. - - :param sql: Original SQL query - :param config_name: Configuration name to get filters for - :return: Modified SQL query with filters applied - - """ - if config_name not in self._table_filters: - return sql - - filter_clause = self._table_filters[config_name] - sql_upper = sql.upper() - - if "WHERE" in sql_upper: - # SQL already has WHERE clause, append with AND - return f"{sql} AND ({filter_clause})" - else: - # Add WHERE clause - # Find the position to insert WHERE (before ORDER BY, GROUP BY, LIMIT, etc.) - insert_keywords = ["ORDER BY", "GROUP BY", "HAVING", "LIMIT", "OFFSET"] - insert_position = len(sql) - - for keyword in insert_keywords: - pos = sql_upper.find(keyword) - if pos != -1 and pos < insert_position: - insert_position = pos - - if insert_position == len(sql): - # No special clauses, append WHERE at the end - return f"{sql} WHERE {filter_clause}" - else: - # Insert WHERE before the special clause - return ( - f"{sql[:insert_position].rstrip()} " - f"WHERE {filter_clause} {sql[insert_position:]}" - ) diff --git a/tfbpapi/HfRankResponse.py b/tfbpapi/HfRankResponse.py deleted file mode 100644 index 67a9c82..0000000 --- a/tfbpapi/HfRankResponse.py +++ /dev/null @@ -1,482 +0,0 @@ -import logging - -import duckdb -import pandas as pd - -from .IncrementalAnalysisDB import IncrementalAnalysisDB - - -class HfRankResponse: - """ - A class to provide an API to compute and analyze "rank response", which is defined - as the cumulative number of responsive targets (e.g., genes) binned by their binding - rank score for each regulator sample pair of binding and perturbation response data. - - Handles multiple dataset comparisons and stores all results in a shared database. - - """ - - def __init__(self, db: IncrementalAnalysisDB): - """ - Initialize RankResponse analyzer with database connection. - - :param db: IncrementalAnalysisDB instance for storing results - - """ - self.db = db - self.logger = logging.getLogger(self.__class__.__name__) - - def compute( - self, - ranking_api, - response_api, - ranking_table: str, - response_table: str, - ranking_score_column: str, - response_column: str, - comparison_id: str | None = None, - regulator_column: str = "regulator_locus_tag", - target_column: str = "target_locus_tag", - bin_size: int = 5, - force_recompute: bool = False, - responsive_condition: str | None = None, - ) -> pd.DataFrame: - """ - Compute rank response for a specific dataset comparison. - - :param ranking_api: API instance for ranking/binding data - :param response_api: API instance for response/perturbation data - :param ranking_table: Name of ranking table in ranking_api - :param response_table: Name of response table in response_api - :param ranking_score_column: Column name for ranking scores - :param response_column: Column name for response values - :param comparison_id: Unique identifier for this comparison (auto-generated if - None) - :param regulator_column: Column name for regulator identifiers - :param target_column: Column name for target identifiers - :param bin_size: Size of ranking bins - :param force_recompute: Whether to recompute existing results - :param responsive_condition: SQL condition to define responsive (default: IS NOT - NULL) - :return: DataFrame with computed results - - """ - # Generate comparison ID if not provided - if comparison_id is None: - comparison_id = f"{ranking_table}_vs_{response_table}" - - table_name = f"rank_response_{comparison_id}" - - # Get all regulators from ranking data - ranking_api._ensure_dataset_loaded(ranking_table) - all_regulators = ranking_api.query( - f"SELECT DISTINCT {regulator_column} FROM {ranking_table}" - )[regulator_column].tolist() - - # Check which regulators already have results - if not force_recompute and self.db.table_exists(table_name): - existing_regulators = set( - self.db.query(f"SELECT DISTINCT {regulator_column} FROM {table_name}")[ - regulator_column - ].tolist() - ) - - new_regulators = [ - reg for reg in all_regulators if reg not in existing_regulators - ] - self.logger.info( - f"Found {len(existing_regulators)} existing regulators, " - f"{len(new_regulators)} new ones" - ) - else: - new_regulators = all_regulators - self.logger.info(f"Computing analysis for {len(new_regulators)} regulators") - - if not new_regulators: - self.logger.info("No new regulators to analyze") - return self.db.query( - f"SELECT * FROM {table_name} ORDER BY " - f"{regulator_column}, {target_column}" - ) - - # Apply filters to focus on new regulators - # For analytical queries, escape and format the values safely - escaped_values = [ - f"'{reg.replace(chr(39), chr(39)+chr(39))}'" for reg in new_regulators - ] - regulator_filter = f"{regulator_column} IN ({', '.join(escaped_values)})" - - # Temporarily add filters for new regulators only - original_ranking_filter = ranking_api.get_table_filter(ranking_table) - original_response_filter = response_api.get_table_filter(response_table) - - new_ranking_filter = regulator_filter - new_response_filter = regulator_filter - - if original_ranking_filter: - new_ranking_filter = f"({original_ranking_filter}) AND ({regulator_filter})" - if original_response_filter: - new_response_filter = ( - f"({original_response_filter}) AND ({regulator_filter})" - ) - - ranking_api.set_table_filter(ranking_table, new_ranking_filter) - response_api.set_table_filter(response_table, new_response_filter) - - try: - # Load filtered data from both APIs - ranking_api._ensure_dataset_loaded(ranking_table) - response_api._ensure_dataset_loaded(response_table) - - ranking_conn = ranking_api._duckdb_conn - response_conn = response_api._duckdb_conn - - # Create temporary connection for analysis - temp_conn = duckdb.connect(":memory:") - - try: - # Get filtered data - ranking_df = ranking_conn.execute( - f"SELECT * FROM {ranking_table}" - ).fetchdf() - response_df = response_conn.execute( - f"SELECT * FROM {response_table}" - ).fetchdf() - - temp_conn.register("ranking_data", ranking_df) - temp_conn.register("response_data", response_df) - - # Execute intermediate analysis SQL - # Set default responsive condition if not provided - if responsive_condition is None: - responsive_condition = f"b.{response_column} IS NOT NULL" - else: - # Replace column references in the condition - responsive_condition = responsive_condition.replace( - response_column, f"b.{response_column}" - ) - - intermediate_sql = f""" - WITH binned_data AS ( - SELECT - a.{regulator_column}, - a.{target_column}, - a.{ranking_score_column}, - b.{response_column}, - CASE WHEN {responsive_condition} THEN 1 ELSE 0 END - AS responsive, - CEILING(ROW_NUMBER() OVER ( - PARTITION BY a.{regulator_column} - ORDER BY a.{regulator_column}, a.{target_column} - ) / {bin_size}.0) * {bin_size} AS bin_label - FROM ranking_data AS a - LEFT JOIN response_data AS b - ON a.{regulator_column} = b.{regulator_column} - AND a.{target_column} = b.{target_column} - ) - SELECT - {regulator_column}, - {target_column}, - {ranking_score_column}, - {response_column}, - responsive, - bin_label, - SUM(responsive) OVER ( - PARTITION BY {regulator_column} - ORDER BY bin_label - RANGE UNBOUNDED PRECEDING - ) AS cumulative_responsive - FROM binned_data - ORDER BY {regulator_column}, bin_label, {target_column} - """ - - new_results = temp_conn.execute(intermediate_sql).fetchdf() - - finally: - temp_conn.close() - - # Save new intermediate results - if len(new_results) > 0: - self.db.append_results( - new_results, - table_name, - analysis_type="response_rate_intermediate", - parameters={ - "ranking_table": ranking_table, - "response_table": response_table, - "ranking_score_column": ranking_score_column, - "response_column": response_column, - "bin_size": bin_size, - "result_type": "intermediate", - }, - description=( - f"Added intermediate data for {len(new_regulators)} " - "new regulators" - ), - deduplicate_on=[regulator_column, target_column], - ) - - self.logger.info( - f"Saved {len(new_results)} intermediate records to database" - ) - - # Return complete results from database - return self.db.query( - f"SELECT * FROM {table_name} ORDER BY {regulator_column}, bin_label, " - f"{target_column}" - ) - - finally: - # Restore original filters - if original_ranking_filter: - ranking_api.set_table_filter(ranking_table, original_ranking_filter) - else: - ranking_api.remove_table_filter(ranking_table) - - if original_response_filter: - response_api.set_table_filter(response_table, original_response_filter) - else: - response_api.remove_table_filter(response_table) - - def get_comparisons(self) -> list[str]: - """ - Get list of all computed comparisons. - - :return: List of comparison identifiers - - """ - tables = self.db.list_tables() - rank_response_tables = [ - table - for table in tables - if table.startswith("rank_response_") and table != "rank_response_metadata" - ] - return [table.replace("rank_response_", "") for table in rank_response_tables] - - def get_bin_summary( - self, - comparison_id: str, - regulator_column: str = "regulator_locus_tag", - bin_size: int = 5, - regulators_filter: list[str] | None = None, - ) -> pd.DataFrame: - """ - Generate bin-level summary for a specific comparison. - - :param comparison_id: Identifier for the comparison to summarize - :param regulator_column: Column name for regulator identifiers - :param bin_size: Bin size used in analysis - :param regulators_filter: Optional list of regulators to include - :return: DataFrame with bin summary results - - """ - intermediate_table_name = f"rank_response_{comparison_id}" - - if not self.db.table_exists(intermediate_table_name): - raise ValueError( - f"Intermediate table '{intermediate_table_name}' does not exist. " - "Run compute() first." - ) - - # Build WHERE clause for regulator filter - where_clause = "" - if regulators_filter: - # For analytical queries, escape and format the values safely - escaped_values = [ - f"'{reg.replace(chr(39), chr(39)+chr(39))}'" - for reg in regulators_filter - ] - where_clause = f"WHERE {regulator_column} IN ({', '.join(escaped_values)})" - - # Generate summary from intermediate data - summary_sql = f""" - SELECT - {regulator_column}, - bin_label, - COUNT(*) as records_in_bin, - SUM(responsive) as responsive_in_bin, - MAX(cumulative_responsive) as cumulative_responsive, - MAX(cumulative_responsive) / bin_label as response_rate - FROM {intermediate_table_name} - {where_clause} - GROUP BY {regulator_column}, bin_label - ORDER BY {regulator_column}, bin_label - """ - - summary_results = self.db.query(summary_sql) - - self.logger.info( - f"Generated summary for {len(summary_results)} regulator-bin combinations" - ) - return summary_results - - def get_regulator_summary( - self, - comparison_id: str, - regulator_column: str = "regulator_locus_tag", - max_bin_label: int | None = None, - ) -> pd.DataFrame: - """ - Generate regulator-level performance summary for a comparison. - - :param comparison_id: Identifier for the comparison - :param regulator_column: Column name for regulator identifiers - :param max_bin_label: Maximum bin label to consider (e.g., 20 for top 20 - targets) - :return: DataFrame with regulator-level summary statistics - - """ - intermediate_table_name = f"rank_response_{comparison_id}" - - if not self.db.table_exists(intermediate_table_name): - raise ValueError( - f"Intermediate table '{intermediate_table_name}' does not exist." - ) - - where_clause = "" - if max_bin_label: - where_clause = f"WHERE bin_label <= {max_bin_label}" - - regulator_summary_sql = f""" - SELECT - {regulator_column}, - COUNT(*) as total_targets, - SUM(responsive) as total_responsive, - COUNT(DISTINCT bin_label) as num_bins, - MAX(cumulative_responsive) as max_cumulative_responsive, - MAX(bin_label) as max_bin_label, - MAX(cumulative_responsive) / MAX(bin_label) - as overall_response_rate, - AVG(CASE WHEN bin_label <= 5 THEN responsive ELSE NULL END) - as top5_response_rate, - AVG(CASE WHEN bin_label <= 10 THEN responsive ELSE NULL END) - as top10_response_rate, - AVG(CASE WHEN bin_label <= 20 THEN responsive ELSE NULL END) - as top20_response_rate - FROM {intermediate_table_name} - {where_clause} - GROUP BY {regulator_column} - ORDER BY overall_response_rate DESC - """ - - return self.db.query(regulator_summary_sql) - - def summarize( - self, - comparison_id: str, - summary_type: str = "bin", - regulator_column: str = "regulator_locus_tag", - bin_size: int = 5, - regulators_filter: list[str] | None = None, - max_bin_label: int | None = None, - ) -> pd.DataFrame: - """ - Generate summary for a specific comparison. - - :param comparison_id: Identifier for the comparison to summarize - :param summary_type: Type of summary ('bin' or 'regulator') - :param regulator_column: Column name for regulator identifiers - :param bin_size: Bin size used in analysis - :param regulators_filter: Optional list of regulators to include - :param max_bin_label: Maximum bin label to consider (for regulator summaries) - :return: DataFrame with summary results - - """ - if summary_type == "bin": - return self.get_bin_summary( - comparison_id=comparison_id, - regulator_column=regulator_column, - bin_size=bin_size, - regulators_filter=regulators_filter, - ) - elif summary_type == "regulator": - return self.get_regulator_summary( - comparison_id=comparison_id, - regulator_column=regulator_column, - max_bin_label=max_bin_label, - ) - else: - raise ValueError(f"Unknown summary type: {summary_type}") - - def query(self, sql: str) -> pd.DataFrame: - """ - Execute custom SQL query on the database. - - :param sql: SQL query to execute - :return: DataFrame with query results - - """ - return self.db.query(sql) - - def get_comparison_data( - self, - comparison_id: str, - regulator_filter: list[str] | None = None, - limit: int | None = None, - ) -> pd.DataFrame: - """ - Get raw data for a specific comparison. - - :param comparison_id: Identifier for the comparison - :param regulator_filter: Optional list of regulators to filter - :param limit: Optional limit on number of records - :return: DataFrame with raw comparison data - - """ - table_name = f"rank_response_{comparison_id}" - - if not self.db.table_exists(table_name): - raise ValueError(f"No results found for comparison '{comparison_id}'") - - filters = {} - if regulator_filter: - filters["regulator_locus_tag"] = regulator_filter - - return self.db.get_results(table_name, filters=filters, limit=limit) - - def compare_across_datasets( - self, - comparison_ids: list[str], - regulator_column: str = "regulator_locus_tag", - metric_columns: list[str] = ["overall_response_rate", "top10_response_rate"], - ) -> pd.DataFrame: - """ - Compare regulator performance across multiple dataset comparisons. - - :param comparison_ids: List of comparison identifiers to compare - :param regulator_column: Column name for regulator identifiers - :param metric_columns: Performance metrics to compare - :return: DataFrame with cross-comparison results - - """ - comparison_data = [] - - for comp_id in comparison_ids: - summary = self.summarize(comp_id, summary_type="regulator") - summary["comparison_id"] = comp_id - comparison_data.append( - summary[[regulator_column, "comparison_id"] + metric_columns] - ) - - if not comparison_data: - return pd.DataFrame() - - # Combine all comparisons - combined = pd.concat(comparison_data, ignore_index=True) - - # Pivot to have comparisons as columns - result_dfs = [] - for metric in metric_columns: - pivot = combined.pivot( - index=regulator_column, columns="comparison_id", values=metric - ) - pivot.columns = [f"{metric}_{col}" for col in pivot.columns] - result_dfs.append(pivot) - - if len(result_dfs) == 1: - return result_dfs[0].reset_index() - else: - final_result = result_dfs[0] - for df in result_dfs[1:]: - final_result = final_result.join(df) - return final_result.reset_index() diff --git a/tfbpapi/IncrementalAnalysisDB.py b/tfbpapi/IncrementalAnalysisDB.py deleted file mode 100644 index 9e8d53e..0000000 --- a/tfbpapi/IncrementalAnalysisDB.py +++ /dev/null @@ -1,340 +0,0 @@ -import logging -from pathlib import Path -from typing import Any - -import duckdb -import pandas as pd - - -class IncrementalAnalysisDB: - """ - Class for managing incremental analysis results in DuckDB. - - Supports appending new results, updating existing ones, and maintaining analysis - metadata for tracking what's been computed. - - """ - - def __init__(self, db_path: str): - """ - Initialize connection to persistent DuckDB database. - - :param db_path: Path to the DuckDB database file - - """ - self.db_path = db_path - Path(db_path).parent.mkdir(parents=True, exist_ok=True) - self.conn = duckdb.connect(db_path) - self.logger = logging.getLogger(__name__) - - # Create metadata table to track analyses - self._ensure_metadata_table() - - def _ensure_metadata_table(self): - """Create metadata table if it doesn't exist.""" - self.conn.execute( - """ - CREATE TABLE IF NOT EXISTS analysis_metadata ( - table_name VARCHAR PRIMARY KEY, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - total_records INTEGER, - analysis_type VARCHAR, - parameters JSON, - description TEXT - ) - """ - ) - - def append_results( - self, - new_results: pd.DataFrame, - table_name: str, - analysis_type: str = "response_rate", - parameters: dict | None = None, - description: str | None = None, - deduplicate_on: list[str] | None = None, - ) -> int: - """ - Append new analysis results to an existing table. - - :param new_results: DataFrame with new results to append - :param table_name: Name of the target table - :param analysis_type: Type of analysis for metadata - :param parameters: Parameters used in the analysis - :param description: Description of the analysis - :param deduplicate_on: Column names to deduplicate on - :return: Number of records added - - """ - if new_results.empty: - self._update_metadata(table_name, 0, analysis_type, parameters, description) - return 0 - - # Handle deduplication if specified - if deduplicate_on and self.table_exists(table_name): - existing_data = self.get_results(table_name) - if not existing_data.empty: - # Remove duplicates based on specified columns - merged = pd.merge( - new_results, - existing_data[deduplicate_on], - on=deduplicate_on, - how="left", - indicator=True, - ) - new_results = merged[merged["_merge"] == "left_only"].drop( - "_merge", axis=1 - ) - - # Insert new data - if not new_results.empty: - self.conn.register("new_data", new_results) - if self.table_exists(table_name): - self.conn.execute(f"INSERT INTO {table_name} SELECT * FROM new_data") - else: - self.conn.execute( - f"CREATE TABLE {table_name} AS SELECT * FROM new_data" - ) - self.conn.unregister("new_data") - - records_added = len(new_results) - self._update_metadata( - table_name, records_added, analysis_type, parameters, description - ) - - return records_added - - def update_results( - self, updated_data: pd.DataFrame, table_name: str, key_columns: list[str] - ) -> int: - """ - Update existing records in a table. - - :param updated_data: DataFrame with updated values - :param table_name: Name of the target table - :param key_columns: Columns to match records on - :return: Number of records updated - - """ - if not self.table_exists(table_name) or updated_data.empty: - return 0 - - records_updated = 0 - self.conn.register("update_data", updated_data) - - # Build SET clause for non-key columns - non_key_columns = [ - col for col in updated_data.columns if col not in key_columns - ] - set_clause = ", ".join( - [f"{col} = update_data.{col}" for col in non_key_columns] - ) - - # Build WHERE clause for key columns - where_clause = " AND ".join( - [f"{table_name}.{col} = update_data.{col}" for col in key_columns] - ) - - update_query = f""" - UPDATE {table_name} - SET {set_clause} - FROM update_data - WHERE {where_clause} - """ - - self.conn.execute(update_query) - records_updated = len(updated_data) - - self.conn.unregister("update_data") - self._update_metadata_timestamp(table_name) - - return records_updated - - def query(self, sql: str) -> pd.DataFrame: - """ - Execute a SQL query and return results as DataFrame. - - :param sql: SQL query to execute - :return: DataFrame with query results - - """ - return self.conn.execute(sql).fetchdf() - - def get_results( - self, - table_name: str, - filters: dict[str, Any] | None = None, - limit: int | None = None, - ) -> pd.DataFrame: - """ - Retrieve results from a table. - - :param table_name: Name of the table to query - :param filters: Optional filters to apply - :param limit: Optional limit on number of records - :return: DataFrame with results - - """ - if not self.table_exists(table_name): - raise ValueError(f"Table {table_name} does not exist") - - query = f"SELECT * FROM {table_name}" - - if filters: - where_conditions = [] - for column, values in filters.items(): - if isinstance(values, list): - values_str = ", ".join( - [f"'{v}'" if isinstance(v, str) else str(v) for v in values] - ) - where_conditions.append(f"{column} IN ({values_str})") - else: - if isinstance(values, str): - where_conditions.append(f"{column} = '{values}'") - else: - where_conditions.append(f"{column} = {values}") - - if where_conditions: - query += " WHERE " + " AND ".join(where_conditions) - - if limit: - query += f" LIMIT {limit}" - - return self.conn.execute(query).fetchdf() - - def table_exists(self, table_name: str) -> bool: - """Check if a table exists in the database.""" - result = self.conn.execute( - """ - SELECT table_name FROM information_schema.tables - WHERE table_name = ? AND table_schema = 'main' - """, - [table_name], - ).fetchall() - return len(result) > 0 - - def drop_table(self, table_name: str) -> None: - """Drop a table and its metadata.""" - if self.table_exists(table_name): - self.conn.execute(f"DROP TABLE {table_name}") - self.conn.execute( - "DELETE FROM analysis_metadata WHERE table_name = ?", [table_name] - ) - - def get_table_info(self, table_name: str) -> dict[str, Any]: - """Get metadata information about a table.""" - if not self.table_exists(table_name): - raise ValueError(f"Table {table_name} does not exist") - - result = self.conn.execute( - """ - SELECT * FROM analysis_metadata WHERE table_name = ? - """, - [table_name], - ).fetchdf() - - if result.empty: - raise ValueError(f"No metadata found for table {table_name}") - - return result.iloc[0].to_dict() - - def list_tables(self) -> list[str]: - """List all tables in the database.""" - result = self.conn.execute( - """ - SELECT table_name FROM information_schema.tables - WHERE table_schema = 'main' - """ - ).fetchall() - return [row[0] for row in result] - - def get_table_schema(self, table_name: str) -> list[dict[str, str]]: - """Get schema information for a table.""" - if not self.table_exists(table_name): - raise ValueError(f"Table {table_name} does not exist") - - result = self.conn.execute(f"DESCRIBE {table_name}").fetchall() - # TODO: fix the mypy ignore/typing - return [ - { - "column_name": row[0], - "column_type": row[1], - "null": row[2], - "key": row[3] if len(row) > 3 else None, # type: ignore - "default": row[4] if len(row) > 4 else None, # type: ignore - "extra": row[5] if len(row) > 5 else None, # type: ignore - } - for row in result - ] - - def close(self) -> None: - """Close the database connection.""" - if hasattr(self, "conn"): - self.conn.close() - - def __enter__(self): - """Context manager entry.""" - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - """Context manager exit.""" - self.close() - - def _update_metadata( - self, - table_name: str, - records_added: int, - analysis_type: str, - parameters: dict | None, - description: str | None, - ) -> None: - """Update or insert metadata for a table.""" - import json - - # Check if metadata exists - existing = self.conn.execute( - """ - SELECT total_records FROM analysis_metadata WHERE table_name = ? - """, - [table_name], - ).fetchall() - - if existing: - # Update existing metadata - new_total = existing[0][0] + records_added - self.conn.execute( - """ - UPDATE analysis_metadata - SET last_updated = CURRENT_TIMESTAMP, total_records = ? - WHERE table_name = ? - """, - [new_total, table_name], - ) - else: - # Insert new metadata - self.conn.execute( - """ - INSERT INTO analysis_metadata - (table_name, total_records, analysis_type, parameters, description) - VALUES (?, ?, ?, ?, ?) - """, - [ - table_name, - records_added, - analysis_type, - json.dumps(parameters) if parameters else None, - description, - ], - ) - - def _update_metadata_timestamp(self, table_name: str) -> None: - """Update the last_updated timestamp for a table.""" - self.conn.execute( - """ - UPDATE analysis_metadata - SET last_updated = CURRENT_TIMESTAMP - WHERE table_name = ? - """, - [table_name], - ) diff --git a/tfbpapi/RankResponseAnalysis.py b/tfbpapi/RankResponseAnalysis.py deleted file mode 100644 index 036e08a..0000000 --- a/tfbpapi/RankResponseAnalysis.py +++ /dev/null @@ -1,277 +0,0 @@ -""" -Simplified rank-response analysis for pre-arranged binding and perturbation data. - -This module provides a streamlined approach to rank-response analysis where: -1. Binding data is already ranked by strength (rank 1 = strongest binding) -2. Perturbation data provides a simple responsive TRUE/FALSE column -3. Analysis focuses on binning and statistical calculations - -""" - -import logging -from typing import Any, Dict, Optional, Tuple - -import numpy as np -import pandas as pd - - -class RankResponseAnalyzer: - """ - Simplified rank-response analyzer for pre-arranged data. - - Takes a DataFrame with target identifiers (pre-sorted by binding strength) and - responsive boolean values, then performs binning analysis. - - """ - - def __init__( - self, - data: pd.DataFrame, - target_col: str, - responsive_col: str, - bin_size: int = 100, - ): - """ - Initialize the rank-response analyzer. - - :param data: DataFrame with target identifiers and responsive booleans - :param target_col: Name of column containing target identifiers - :param responsive_col: Name of column containing TRUE/FALSE responsive values - :param bin_size: Number of targets per bin for analysis - :raises ValueError: If data validation fails - - """ - self.logger = logging.getLogger(self.__class__.__name__) - - # Store parameters - self.target_col = target_col - self.responsive_col = responsive_col - self.bin_size = bin_size - - # Validate and store data - self.data = self._validate_data(data) - self.n_targets = len(self.data) - self.n_bins = (self.n_targets + bin_size - 1) // bin_size # Ceiling division - - # Calculate overall statistics - self.total_responsive = self.data[responsive_col].sum() - self.overall_response_rate = self.total_responsive / self.n_targets - - self.logger.info( - f"Initialized RankResponseAnalyzer: {self.n_targets} targets, " - f"{self.total_responsive} responsive ({self.overall_response_rate:.1%}), " - f"{self.n_bins} bins of size {bin_size}" - ) - - def _validate_data(self, data: pd.DataFrame) -> pd.DataFrame: - """Validate input data and return cleaned version.""" - if not isinstance(data, pd.DataFrame): - raise ValueError("Data must be a pandas DataFrame") - - if len(data) == 0: - raise ValueError("Data cannot be empty") - - # Check required columns exist - if self.target_col not in data.columns: - raise ValueError(f"Target column '{self.target_col}' not found in data") - if self.responsive_col not in data.columns: - raise ValueError( - f"Responsive column '{self.responsive_col}' not found in data" - ) - - # Extract just the required columns - clean_data = data[[self.target_col, self.responsive_col]].copy() - - # Check for missing values - if clean_data[self.target_col].isna().any(): - raise ValueError( - f"Target column '{self.target_col}' contains missing values" - ) - if clean_data[self.responsive_col].isna().any(): - raise ValueError( - f"Responsive column '{self.responsive_col}' contains missing values" - ) - - # Validate responsive column is boolean-like - unique_values = set(clean_data[self.responsive_col].unique()) - valid_boolean_sets = [ - {True, False}, - {1, 0}, - {1.0, 0.0}, - {"TRUE", "FALSE"}, - {"True", "False"}, - {"true", "false"}, - {"T", "F"}, - ] - - # Allow subsets (e.g., only True values, only False values) - is_valid_boolean = any( - unique_values.issubset(valid_set) for valid_set in valid_boolean_sets - ) - - if not is_valid_boolean: - raise ValueError( - f"Responsive column '{self.responsive_col}' must contain boolean-like values. " - f"Found: {unique_values}" - ) - - # Convert to standard boolean - clean_data[self.responsive_col] = clean_data[self.responsive_col].astype(bool) - - # Reset index to ensure proper ranking (rank 1 = index 0) - clean_data = clean_data.reset_index(drop=True) - - self.logger.debug( - f"Validated data: {len(clean_data)} rows, {unique_values} -> boolean" - ) - - return clean_data - - def create_bins(self) -> pd.DataFrame: - """ - Create bins from the ranked data. - - :return: DataFrame with bin assignments for each target - - """ - bins_data = self.data.copy() - bins_data["rank"] = range(1, len(bins_data) + 1) - bins_data["bin"] = ((bins_data["rank"] - 1) // self.bin_size) + 1 - - return bins_data - - def calculate_bin_stats(self) -> pd.DataFrame: - """ - Calculate statistics for each bin. - - :return: DataFrame with bin-level statistics - - """ - bins_data = self.create_bins() - - bin_stats = [] - for bin_num in range(1, self.n_bins + 1): - bin_data = bins_data[bins_data["bin"] == bin_num] - - n_targets_in_bin = len(bin_data) - n_responsive_in_bin = bin_data[self.responsive_col].sum() - response_rate = ( - n_responsive_in_bin / n_targets_in_bin if n_targets_in_bin > 0 else 0 - ) - - # Calculate rank range for this bin - min_rank = bin_data["rank"].min() - max_rank = bin_data["rank"].max() - - bin_stats.append( - { - "bin": bin_num, - "min_rank": min_rank, - "max_rank": max_rank, - "n_targets": n_targets_in_bin, - "n_responsive": n_responsive_in_bin, - "response_rate": response_rate, - "enrichment_vs_overall": ( - response_rate / self.overall_response_rate - if self.overall_response_rate > 0 - else np.nan - ), - } - ) - - return pd.DataFrame(bin_stats) - - def get_bin_summary(self) -> pd.DataFrame: - """Get comprehensive bin-level summary statistics.""" - return self.calculate_bin_stats() - - def calculate_enrichment(self, reference_rate: float | None = None) -> pd.DataFrame: - """ - Calculate enrichment scores for each bin. - - :param reference_rate: Reference response rate for enrichment calculation. If - None, uses overall response rate. - :return: DataFrame with enrichment calculations - - """ - if reference_rate is None: - reference_rate = self.overall_response_rate - - if reference_rate <= 0: - raise ValueError( - "Reference rate must be greater than 0 for enrichment calculation" - ) - - bin_stats = self.calculate_bin_stats() - bin_stats["enrichment_vs_reference"] = ( - bin_stats["response_rate"] / reference_rate - ) - bin_stats["reference_rate"] = reference_rate - - return bin_stats - - def get_rank_response_curve(self, window_size: int | None = None) -> pd.DataFrame: - """ - Get data for plotting rank vs response rate curve with sliding window. - - :param window_size: Size of sliding window for smoothing. If None, uses - bin_size. - :return: DataFrame with rank positions and smoothed response rates - - """ - if window_size is None: - window_size = self.bin_size - - bins_data = self.create_bins() - curve_data = [] - - for i in range(len(bins_data)): - # Define window around current position - start_idx = max(0, i - window_size // 2) - end_idx = min(len(bins_data), i + window_size // 2 + 1) - - window_data = bins_data.iloc[start_idx:end_idx] - window_response_rate = window_data[self.responsive_col].mean() - - curve_data.append( - { - "rank": bins_data.iloc[i]["rank"], - "target": bins_data.iloc[i][self.target_col], - "responsive": bins_data.iloc[i][self.responsive_col], - "smoothed_response_rate": window_response_rate, - "window_size": len(window_data), - } - ) - - return pd.DataFrame(curve_data) - - def get_summary_stats(self) -> dict[str, Any]: - """Get overall summary statistics.""" - return { - "n_targets": self.n_targets, - "n_responsive": self.total_responsive, - "overall_response_rate": self.overall_response_rate, - "n_bins": self.n_bins, - "bin_size": self.bin_size, - "targets_per_bin_avg": self.n_targets / self.n_bins, - } - - def to_dataframe(self) -> pd.DataFrame: - """Export full results as a comprehensive DataFrame.""" - bins_data = self.create_bins() - bin_stats = self.calculate_bin_stats() - - # Merge bin statistics back to individual target data - result = bins_data.merge(bin_stats, on="bin", suffixes=("", "_bin")) - - return result - - def __repr__(self) -> str: - """String representation of the analyzer.""" - return ( - f"RankResponseAnalyzer(" - f"targets={self.n_targets}, " - f"responsive={self.total_responsive}, " - f"rate={self.overall_response_rate:.1%}, " - f"bins={self.n_bins})" - ) diff --git a/tfbpapi/__init__.py b/tfbpapi/__init__.py index e69de29..f9db664 100644 --- a/tfbpapi/__init__.py +++ b/tfbpapi/__init__.py @@ -0,0 +1,33 @@ +from .datacard import DataCard +from .fetchers import HfDataCardFetcher, HfRepoStructureFetcher, HfSizeInfoFetcher +from .hf_cache_manager import HfCacheManager +from .models import ( + DatasetCard, + DatasetConfig, + DatasetType, + ExtractedMetadata, + FeatureInfo, + MetadataConfig, + MetadataRelationship, + PropertyMapping, + RepositoryConfig, +) +from .virtual_db import VirtualDB + +__all__ = [ + "DataCard", + "HfCacheManager", + "HfDataCardFetcher", + "HfRepoStructureFetcher", + "HfSizeInfoFetcher", + "MetadataConfig", + "PropertyMapping", + "RepositoryConfig", + "VirtualDB", + "DatasetCard", + "DatasetConfig", + "DatasetType", + "ExtractedMetadata", + "FeatureInfo", + "MetadataRelationship", +] diff --git a/tfbpapi/constants.py b/tfbpapi/constants.py index 62a4227..749678f 100644 --- a/tfbpapi/constants.py +++ b/tfbpapi/constants.py @@ -9,47 +9,3 @@ def get_hf_token() -> str | None: """Get HuggingFace token from environment variable.""" return os.getenv("HF_TOKEN") - - -SQL_FILTER_KEYWORDS = sql_keywords = { - "AND", - "OR", - "NOT", - "IN", - "IS", - "NULL", - "TRUE", - "FALSE", - "LIKE", - "BETWEEN", - "EXISTS", - "ALL", - "ANY", - "SOME", - "CASE", - "WHEN", - "THEN", - "ELSE", - "END", - "CAST", - "AS", - "SELECT", - "FROM", - "WHERE", - "GROUP", - "ORDER", - "BY", - "HAVING", - "LIMIT", - "OFFSET", - "DISTINCT", - "COUNT", - "SUM", - "AVG", - "MIN", - "MAX", - "UPPER", - "LOWER", - "SUBSTR", - "LENGTH", -} diff --git a/tfbpapi/datainfo/datacard.py b/tfbpapi/datacard.py similarity index 59% rename from tfbpapi/datainfo/datacard.py rename to tfbpapi/datacard.py index 370a677..bc4cf72 100644 --- a/tfbpapi/datainfo/datacard.py +++ b/tfbpapi/datacard.py @@ -21,9 +21,9 @@ from pydantic import ValidationError -from ..errors import DataCardError, DataCardValidationError, HfDataFetchError -from .fetchers import HfDataCardFetcher, HfRepoStructureFetcher, HfSizeInfoFetcher -from .models import ( +from tfbpapi.errors import DataCardError, DataCardValidationError, HfDataFetchError +from tfbpapi.fetchers import HfDataCardFetcher, HfRepoStructureFetcher, HfSizeInfoFetcher +from tfbpapi.models import ( DatasetCard, DatasetConfig, DatasetType, @@ -159,49 +159,6 @@ def get_config(self, config_name: str) -> DatasetConfig | None: """Get a specific configuration by name.""" return self.dataset_card.get_config_by_name(config_name) - def get_configs_by_type( - self, dataset_type: DatasetType | str - ) -> list[DatasetConfig]: - """Get configurations by dataset type.""" - if isinstance(dataset_type, str): - dataset_type = DatasetType(dataset_type) - return self.dataset_card.get_configs_by_type(dataset_type) - - def get_card_metadata(self) -> dict[str, Any]: - """ - Get all top-level metadata fields from the dataset card. - - Returns all fields stored in model_extra (e.g., license, tags, pretty_name, - etc.) as a dict. This gives direct access to the raw YAML structure at the card - level. - - :return: Dict of all extra metadata fields - - """ - if self.dataset_card.model_extra: - return dict(self.dataset_card.model_extra) - return {} - - def get_config_metadata(self, config_name: str) -> dict[str, Any]: - """ - Get all extra metadata fields from a specific config. - - Returns all fields stored in the config's model_extra (e.g., - experimental_conditions, custom fields, etc.) as a dict. - - :param config_name: Configuration name - :return: Dict of all extra metadata fields for this config - :raises DataCardError: If config not found - - """ - config = self.get_config(config_name) - if not config: - raise DataCardError(f"Configuration '{config_name}' not found") - - if config.model_extra: - return dict(config.model_extra) - return {} - def get_features(self, config_name: str) -> list[FeatureInfo]: """ Get all feature definitions for a configuration. @@ -259,75 +216,6 @@ def get_features_by_role( return by_role - def get_field_values(self, config_name: str, field_name: str) -> set[str]: - """ - Get all unique values for a specific field in a configuration. - - :param config_name: Configuration name - :param field_name: Field name to extract values from - :return: Set of unique values - :raises DataCardError: If config or field not found - - """ - config = self.get_config(config_name) - if not config: - raise DataCardError(f"Configuration '{config_name}' not found") - - # Check if field exists in the config - field_names = [f.name for f in config.dataset_info.features] - if field_name not in field_names: - raise DataCardError( - f"Field '{field_name}' not found in config '{config_name}'" - ) - - return self._extract_field_values(config, field_name) - - def _extract_field_values(self, config: DatasetConfig, field_name: str) -> set[str]: - """Extract unique values for a field from partition structure only.""" - values = set() - - # Check cache first - cache_key = f"{config.config_name}:{field_name}" - if cache_key in self._metadata_cache: - cached_metadata = self._metadata_cache[cache_key] - for meta in cached_metadata: - if meta.field_name == field_name: - values.update(meta.values) - return values - - try: - # For partitioned datasets, extract from file structure - if ( - config.dataset_info.partitioning - and config.dataset_info.partitioning.enabled - ): - partition_values = self._extract_partition_values(config, field_name) - if partition_values: - values.update(partition_values) - # Cache the result - self._metadata_cache[cache_key] = [ - ExtractedMetadata( - config_name=config.config_name, - field_name=field_name, - values=values, - extraction_method="partition_structure", - ) - ] - return values - - # For non-partitioned fields, we can no longer query parquet files - self.logger.debug( - f"Cannot extract values for {field_name} in {config.config_name}: " - "field is not partitioned and parquet querying is not supported" - ) - - except Exception as e: - self.logger.warning(f"Failed to extract values for {field_name}: {e}") - # Return empty set on failure instead of raising - # This maintains backward compatibility - - return values - def _extract_partition_values( self, config: DatasetConfig, field_name: str ) -> set[str]: @@ -423,67 +311,6 @@ def get_repository_info(self) -> dict[str, Any]: "has_default_config": self.dataset_card.get_default_config() is not None, } - def explore_config( - self, config_name: str, include_extra: bool = True - ) -> dict[str, Any]: - """ - Get detailed information about a specific configuration. - - Returns a comprehensive dict with config structure including features, data - files, partitioning, and optionally all extra metadata fields. - - :param config_name: Configuration name - :param include_extra: If True, include all fields from model_extra - :return: Dict with config details - :raises DataCardError: If config not found - - """ - config = self.get_config(config_name) - if not config: - raise DataCardError(f"Configuration '{config_name}' not found") - - info: dict[str, Any] = { - "config_name": config.config_name, - "description": config.description, - "dataset_type": config.dataset_type.value, - "is_default": config.default, - "num_features": len(config.dataset_info.features), - "features": [ - { - "name": f.name, - "dtype": f.dtype, - "description": f.description, - "role": f.role, - "has_definitions": f.definitions is not None, - } - for f in config.dataset_info.features - ], - "data_files": [ - {"split": df.split, "path": df.path} for df in config.data_files - ], - } - - # Add partitioning info if present - if config.dataset_info.partitioning: - info["partitioning"] = { - "enabled": config.dataset_info.partitioning.enabled, - "partition_by": config.dataset_info.partitioning.partition_by, - "path_template": config.dataset_info.partitioning.path_template, - } - - # Add metadata-specific fields - if config.applies_to: - info["applies_to"] = config.applies_to - - if config.metadata_fields: - info["metadata_fields"] = config.metadata_fields - - # Add all extra fields from model_extra - if include_extra and config.model_extra: - info["extra_fields"] = dict(config.model_extra) - - return info - def extract_metadata_schema(self, config_name: str) -> dict[str, Any]: """ Extract complete metadata schema for planning metadata table structure. @@ -565,52 +392,6 @@ def extract_metadata_schema(self, config_name: str) -> dict[str, Any]: return schema - def get_condition_levels( - self, config_name: str, field_name: str - ) -> dict[str, Any] | list[str]: - """ - Get factor levels for an experimental condition field. - - Returns definitions if available (structured dict with descriptions), otherwise - queries distinct values from the parquet file. - - :param config_name: Configuration name - :param field_name: Experimental condition field name - :return: Dict of definitions if available, otherwise list of distinct values - :raises DataCardError: If config or field not found, or field is not an - experimental condition - - """ - config = self.get_config(config_name) - if not config: - raise DataCardError(f"Configuration '{config_name}' not found") - - # Find the feature and verify it's an experimental condition - feature = None - for f in config.dataset_info.features: - if f.name == field_name: - feature = f - break - - if not feature: - raise DataCardError( - f"Field '{field_name}' not found in config '{config_name}'" - ) - - if feature.role != "experimental_condition": - raise DataCardError( - f"Field '{field_name}' is not an experimental condition " - f"(role={feature.role})" - ) - - # If field has definitions, return those - if feature.definitions: - return feature.definitions - - # Otherwise, query distinct values from parquet file - values = self.get_field_values(config_name, field_name) - return sorted(list(values)) - def get_experimental_conditions( self, config_name: str | None = None ) -> dict[str, Any]: @@ -724,168 +505,6 @@ def get_field_definitions( # Return definitions if present, otherwise empty dict return feature.definitions if feature.definitions else {} - def get_field_attribute( - self, config_name: str, field_name: str, attribute: str - ) -> dict[str, Any]: - """ - Extract a specific attribute from field definitions. - - This is useful for exploring nested attributes in condition definitions, - such as media composition, temperature parameters, or growth phases. - - :param config_name: Configuration name - :param field_name: Field with definitions (e.g., 'condition') - :param attribute: Attribute to extract (e.g., 'media', 'temperature_celsius') - :return: Dict mapping field values to their attribute specifications. - Returns 'unspecified' if attribute doesn't exist for a value. - - Example: - >>> card = DataCard('BrentLab/harbison_2004') - >>> media = card.get_field_attribute('harbison_2004', 'condition', 'media') - >>> print(media['YPD']) - {'name': 'YPD', 'carbon_source': [...], 'nitrogen_source': [...]} - - """ - # Get all field definitions - definitions = self.get_field_definitions(config_name, field_name) - - # Extract attribute for each definition - result = {} - for field_value, definition in definitions.items(): - if attribute in definition: - result[field_value] = definition[attribute] - else: - result[field_value] = "unspecified" - - return result - - def list_experimental_condition_fields(self, config_name: str) -> list[str]: - """ - List all fields with role=experimental_condition in a config. - - These are fields that contain per-sample experimental condition variation. - They represent the field-level (third level) of the experimental conditions - hierarchy. - - Fields with this role typically have `definitions` that map each value to - its detailed experimental specification. Use `get_field_definitions()` to - access these definitions. - - :param config_name: Configuration name - :return: List of field names with experimental_condition role - :raises DataCardError: If config not found - - Example: - >>> # Find all condition fields - >>> cond_fields = card.list_experimental_condition_fields('config_name') - >>> # ['condition', 'treatment', 'time_point'] - >>> - >>> # Then get definitions for each - >>> for field in cond_fields: - ... defs = card.get_field_definitions('config_name', field) - ... print(f"{field}: {len(defs)} levels") - - """ - config = self.get_config(config_name) - if not config: - raise DataCardError(f"Configuration '{config_name}' not found") - - return [ - f.name - for f in config.dataset_info.features - if f.role == "experimental_condition" - ] - - def summarize_field_levels( - self, - config_name: str, - field_name: str, - properties: list[str] | None = None, - max_levels: int | None = None, - ) -> str: - """ - Get a human-readable summary of field levels and their key properties. - - Provides a formatted summary showing each level (value) and selected properties - from its definition. Useful for quickly exploring experimental condition - structures without writing loops. - - :param config_name: Configuration name - :param field_name: Field name to summarize - :param properties: Optional list of property paths to display (e.g., ["media.name", "temperature_celsius"]). - If None, shows all top-level keys. - :param max_levels: Optional limit on number of levels to show - :return: Formatted string summary - :raises DataCardError: If config or field not found - - Example: - >>> card = DataCard("BrentLab/harbison_2004") - >>> # Show summary with specific properties - >>> print(card.summarize_field_levels( - ... "harbison_2004", - ... "condition", - ... properties=["media.carbon_source.compound", "temperature_celsius"] - ... )) - Field: condition - Levels: 14 - - YPD: - media.carbon_source.compound: ['D-glucose'] - temperature_celsius: 30 - - GAL: - media.carbon_source.compound: ['D-galactose'] - temperature_celsius: 30 - ... - - """ - from tfbpapi.datainfo.metadata_builder import get_nested_value - - # Get definitions - definitions = self.get_field_definitions(config_name, field_name) - - if not definitions: - return f"Field '{field_name}' has no definitions" - - lines = [ - f"Field: {field_name}", - f"Levels: {len(definitions)}", - "", - ] - - # Determine how many levels to show - levels_to_show = list(definitions.keys()) - if max_levels: - levels_to_show = levels_to_show[:max_levels] - - for level_name in levels_to_show: - definition = definitions[level_name] - lines.append(f"{level_name}:") - - if properties: - # Show only specified properties - for prop_path in properties: - value = get_nested_value(definition, prop_path) - lines.append(f" {prop_path}: {value}") - else: - # Show all top-level keys - if isinstance(definition, dict): - for key in definition.keys(): - value = definition[key] - # Truncate long values - value_str = str(value) - if len(value_str) > 80: - value_str = value_str[:77] + "..." - lines.append(f" {key}: {value_str}") - - lines.append("") - - if max_levels and len(definitions) > max_levels: - remaining = len(definitions) - max_levels - lines.append(f"... and {remaining} more levels") - - return "\n".join(lines) - def summary(self) -> str: """Get a human-readable summary of the dataset.""" card = self.dataset_card diff --git a/tfbpapi/datainfo/__init__.py b/tfbpapi/datainfo/__init__.py deleted file mode 100644 index c1643f6..0000000 --- a/tfbpapi/datainfo/__init__.py +++ /dev/null @@ -1,31 +0,0 @@ -from .datacard import DataCard -from .fetchers import HfDataCardFetcher, HfRepoStructureFetcher, HfSizeInfoFetcher -from .metadata_builder import MetadataBuilder -from .metadata_config_models import MetadataConfig, PropertyMapping, RepositoryConfig -from .metadata_manager import MetadataManager -from .models import ( - DatasetCard, - DatasetConfig, - DatasetType, - ExtractedMetadata, - FeatureInfo, - MetadataRelationship, -) - -__all__ = [ - "DataCard", - "HfDataCardFetcher", - "HfRepoStructureFetcher", - "HfSizeInfoFetcher", - "MetadataBuilder", - "MetadataConfig", - "PropertyMapping", - "RepositoryConfig", - "MetadataManager", - "DatasetCard", - "DatasetConfig", - "DatasetType", - "ExtractedMetadata", - "FeatureInfo", - "MetadataRelationship", -] diff --git a/tfbpapi/datainfo/metadata_builder.py b/tfbpapi/datainfo/metadata_builder.py deleted file mode 100644 index 112c46a..0000000 --- a/tfbpapi/datainfo/metadata_builder.py +++ /dev/null @@ -1,619 +0,0 @@ -""" -Metadata builder for creating standardized tables across heterogeneous datasets. - -This module provides tools for building standardized metadata views by normalizing -factor levels across datasets with varying experimental condition structures. Users -specify optional alias mappings in external YAML files to standardize factor level -names (e.g., "D-glucose" -> "glucose"). - -Key Components: -- MetadataBuilder: Main class for building normalized metadata across datasets -- normalize_value(): Function for normalizing values using optional alias mappings -- Three output modes: conditions, samples, full_data -- External YAML configuration for aliases and property mappings - -Example Configuration: - ```yaml - factor_aliases: - carbon_source: - glucose: ["D-glucose", "dextrose"] - galactose: ["D-galactose", "Galactose"] - - BrentLab/harbison_2004: - dataset: - harbison_2004: - carbon_source: - field: condition - path: media.carbon_source - ``` - -Example Usage: - >>> builder = MetadataBuilder("metadata_config.yaml") - >>> results = builder.build_metadata( - ... repos=[("BrentLab/harbison_2004", "harbison_2004")], - ... mode="conditions" - ... ) - -""" - -from __future__ import annotations - -from pathlib import Path -from typing import Any, Optional - -import pandas as pd - -from tfbpapi.datainfo import DataCard -from tfbpapi.datainfo.metadata_config_models import MetadataConfig, PropertyMapping -from tfbpapi.errors import DataCardError -from tfbpapi.HfQueryAPI import HfQueryAPI - - -def get_nested_value(data: dict, path: str) -> Any: - """ - Navigate nested dict/list using dot notation. - - Handles missing intermediate keys gracefully by returning None. - Supports extracting properties from lists of dicts. - - :param data: Dictionary to navigate - :param path: Dot-separated path (e.g., "media.carbon_source.compound") - :return: Value at path or None if not found - - Examples: - # Simple nested dict - get_nested_value({"media": {"name": "YPD"}}, "media.name") - -> "YPD" - - # List of dicts - extract property from each item - get_nested_value( - {"media": {"carbon_source": [{"compound": "glucose"}, {"compound": "galactose"}]}}, - "media.carbon_source.compound" - ) - -> ["glucose", "galactose"] - - # List of dicts - get the list itself - get_nested_value( - {"media": {"carbon_source": [{"compound": "glucose"}]}}, - "media.carbon_source" - ) - -> [{"compound": "glucose"}] - - """ - if not isinstance(data, dict): - return None - - keys = path.split(".") - current = data - - for i, key in enumerate(keys): - if isinstance(current, dict): - if key not in current: - return None - current = current[key] - elif isinstance(current, list): - # If current is a list and we have more keys, extract property from each item - if i < len(keys): - # Extract the remaining path from each list item - remaining_path = ".".join(keys[i:]) - results = [] - for item in current: - if isinstance(item, dict): - val = get_nested_value(item, remaining_path) - if val is not None: - results.append(val) - return results if results else None - else: - return None - - return current - - - - -def normalize_value(actual_value: Any, aliases: dict[str, list[Any]] | None) -> str: - """ - Normalize a value using optional alias mappings (case-insensitive). - - Returns the alias name if a match is found, otherwise returns the - original value as a string. This enables standardizing factor level - names across heterogeneous datasets. - - :param actual_value: The value from the data to normalize - :param aliases: Optional dict mapping alias names to lists of actual values. - Example: {"glucose": ["D-glucose", "dextrose"]} - :return: Alias name if match found, otherwise str(actual_value) - - Examples: - # With aliases - exact match - normalize_value("D-glucose", {"glucose": ["D-glucose", "dextrose"]}) - -> "glucose" - - # With aliases - case-insensitive match - normalize_value("DEXTROSE", {"glucose": ["D-glucose", "dextrose"]}) - -> "glucose" - - # No alias match - pass through - normalize_value("maltose", {"glucose": ["D-glucose"]}) - -> "maltose" - - # No aliases provided - pass through - normalize_value("D-glucose", None) - -> "D-glucose" - - # Numeric value - normalize_value(30, {"thirty": [30, "30"]}) - -> "thirty" - - """ - if aliases is None: - return str(actual_value) - - # Convert to string for comparison (case-insensitive) - actual_str = str(actual_value).lower() - - # Check each alias mapping - for alias_name, actual_values in aliases.items(): - for val in actual_values: - if str(val).lower() == actual_str: - return alias_name - - # No match found - pass through original value - return str(actual_value) - - -class MetadataBuilder: - """ - Build standardized metadata tables across heterogeneous datasets. - - This class creates metadata views with normalized factor level names using - optional alias mappings. Unlike a filtering system, this includes ALL data - and simply normalizes the factor level names for standardization. - - Configuration specifies: - 1. Optional factor aliases (for normalizing factor level names) - 2. Repository and dataset-specific property paths (where to find each property) - - Configuration structure: - factor_aliases: # Optional - property_name: - alias_name: [actual_value1, actual_value2] - - BrentLab/repo_name: - # Repo-wide properties (apply to all datasets in this repository) - # Paths are relative to experimental_conditions at the repository level - property1: - path: media.name - - # Dataset-specific section - dataset: - dataset_name: - # Dataset-specific properties (apply only to this dataset) - # Paths are relative to experimental_conditions at the config level - property2: - path: temperature_celsius - - # Field-level properties (per-sample variation) - # Paths are relative to field definitions (NOT experimental_conditions) - property3: - field: condition - path: media.carbon_source - - Path Resolution: - - Repo-wide & dataset-specific: Paths automatically - prepended with "experimental_conditions." - Example: path "media.name" resolves to experimental_conditions.media.name - - - Field-level: Paths used directly on field definitions (no prepending) - Example: field "condition", path "media.carbon_source" - looks in condition field's definitions for media.carbon_source - - Three output modes: - - conditions: Extract normalized metadata (no data retrieval) - - samples: Sample-level metadata (one row per sample_id) - - full_data: All measurements with metadata - - Attributes: - config: Validated MetadataConfig instance - factor_aliases: Optional alias mappings for normalization - - """ - - def __init__(self, config_path: Path | str): - """ - Initialize metadata builder with external configuration. - - :param config_path: Path to YAML configuration file - :raises FileNotFoundError: If config file doesn't exist - :raises ValueError: If configuration is invalid - - """ - self.config = MetadataConfig.from_yaml(config_path) - self.factor_aliases = self.config.factor_aliases - - def _get_property_mappings( - self, repo_id: str, config_name: str - ) -> dict[str, PropertyMapping]: - """ - Get property mappings for a specific repo/dataset combination. - - Merges repo-wide and dataset-specific mappings, with dataset-specific taking - precedence. - - :param repo_id: Repository ID - :param config_name: Dataset/config name - :return: Dict mapping property names to PropertyMapping objects - - """ - return self.config.get_property_mappings(repo_id, config_name) - - def build_metadata( - self, - repos: list[tuple[str, str]], - mode: str = "conditions", - token: str | None = None, - ) -> dict[str, Any]: - """ - Build metadata tables across datasets with normalized factor levels. - - Note: ALL repositories are processed - no filtering/exclusion occurs. - Factor aliases are used to normalize factor level names, not to filter. - - :param repos: List of (repo_id, config_name) tuples to process - :param mode: Output mode - "conditions", "samples", or "full_data" - :param token: Optional HuggingFace token for private repos - :return: Dict mapping repo_id to metadata results - :raises ValueError: If mode is invalid - - """ - if mode not in ["conditions", "samples", "full_data"]: - raise ValueError( - f"Invalid mode: {mode}. Must be 'conditions', " - "'samples', or 'full_data'" - ) - - results = {} - - for repo_id, config_name in repos: - try: - # Load DataCard - card = DataCard(repo_id, token=token) - - # Check if this repository has configuration - if repo_id not in self.config.repositories: - results[repo_id] = { - "error": f"No property mappings defined for {repo_id}" - } - continue - - # Extract and normalize metadata for ALL data - metadata = self._extract_metadata(card, repo_id, config_name) - - # Build result based on mode - result = {"metadata": metadata} - - if mode == "conditions": - # Mode 0: Just metadata, no data - pass - elif mode == "samples": - # Mode 1: Sample-level metadata - result["data"] = self._get_sample_metadata( - repo_id, config_name, metadata, token - ) - elif mode == "full_data": - # Mode 2: Full data with measurements - result["data"] = self._get_full_data( - repo_id, config_name, metadata, token - ) - - results[repo_id] = result - - except Exception as e: - results[repo_id] = {"error": f"Error processing dataset: {str(e)}"} - - return results - - def _extract_metadata( - self, - card: DataCard, - repo_id: str, - config_name: str, - ) -> dict[str, Any]: - """ - Extract and normalize metadata from datacard. - - Extracts ALL metadata with normalized factor level names. - No filtering occurs - all data is included. - - :param card: DataCard instance - :param repo_id: Repository ID - :param config_name: Configuration name - :return: Dict with normalized metadata - - """ - metadata = {} - property_mappings = self._get_property_mappings(repo_id, config_name) - - # Extract repo/config level metadata - repo_metadata = self._extract_repo_level(card, config_name, property_mappings) - metadata.update(repo_metadata) - - # Extract field-level metadata - field_metadata = self._extract_field_level(card, config_name, property_mappings) - if field_metadata: - metadata["field_values"] = field_metadata - - return metadata - - def _extract_repo_level( - self, - card: DataCard, - config_name: str, - property_mappings: dict[str, PropertyMapping], - ) -> dict[str, list[str]]: - """ - Extract and normalize repo/config-level metadata. - - :param card: DataCard instance - :param config_name: Configuration name - :param property_mappings: Property mappings for this repo/dataset - :return: Dict mapping property names to normalized values - - """ - metadata = {} - - # Get repo and config level conditions - try: - conditions = card.get_experimental_conditions(config_name) - except DataCardError: - conditions = {} - - if not conditions: - return metadata - - # Extract each mapped property - for prop_name, mapping in property_mappings.items(): - # Skip field-level mappings (handled separately) - if mapping.field is not None: - continue - - # Get path for this property - path = mapping.path - full_path = f"experimental_conditions.{path}" - - # Get value at this path - value = get_nested_value(conditions, full_path) - - if value is None: - continue - - # Ensure value is a list for consistent processing - actual_values = [value] if not isinstance(value, list) else value - - # Normalize using aliases (if configured) - aliases = self.factor_aliases.get(prop_name) - normalized_values = [normalize_value(v, aliases) for v in actual_values] - - metadata[prop_name] = normalized_values - - return metadata - - def _extract_field_level( - self, - card: DataCard, - config_name: str, - property_mappings: dict[str, PropertyMapping], - ) -> dict[str, dict[str, Any]]: - """ - Extract and normalize field-level metadata. - - Returns metadata for ALL field values (no filtering). - - :param card: DataCard instance - :param config_name: Configuration name - :param property_mappings: Property mappings for this repo/dataset - :return: Dict mapping field values to their normalized metadata - - """ - field_metadata: dict[str, dict[str, Any]] = {} - - # Group property mappings by field - field_mappings: dict[str, dict[str, str]] = {} - for prop_name, mapping in property_mappings.items(): - if mapping.field is not None: - field_name = mapping.field - if field_name not in field_mappings: - field_mappings[field_name] = {} - field_mappings[field_name][prop_name] = mapping.path - - # Process each field that has mappings - for field_name, prop_paths in field_mappings.items(): - # Get field definitions - definitions = card.get_field_definitions(config_name, field_name) - if not definitions: - continue - - # Extract metadata for ALL field values (no filtering!) - for field_value, definition in definitions.items(): - if field_value not in field_metadata: - field_metadata[field_value] = {} - - for prop_name, path in prop_paths.items(): - # Get value at path - value = get_nested_value(definition, path) - - if value is None: - continue - - # Ensure value is a list for consistent processing - actual_values = [value] if not isinstance(value, list) else value - - # Normalize using aliases (if configured) - aliases = self.factor_aliases.get(prop_name) - normalized_values = [ - normalize_value(v, aliases) for v in actual_values - ] - - field_metadata[field_value][prop_name] = normalized_values - - return field_metadata - - def _get_sample_metadata( - self, - repo_id: str, - config_name: str, - metadata: dict[str, Any], - token: str | None = None, - ) -> pd.DataFrame: - """ - Get sample-level metadata (Mode 1). - - Returns one row per sample_id with metadata columns. Includes ALL samples (no - filtering). Selects base metadata_fields columns plus adds extracted columns - from field-level property mappings. - - :param repo_id: Repository ID - :param config_name: Configuration name - :param metadata: Extracted metadata dict with field_values - :param token: Optional HuggingFace token - :return: DataFrame with sample metadata including extracted columns - - """ - try: - # Load DataCard to get metadata_fields - card = DataCard(repo_id, token=token) - config = card.get_config(config_name) - - # Initialize query API - api = HfQueryAPI(repo_id, token=token) - - # Determine which columns to select - if config and hasattr(config, 'metadata_fields') and config.metadata_fields: - # Select only metadata fields - columns = ", ".join(config.metadata_fields) - # Add sample_id if not already in metadata_fields - if "sample_id" not in config.metadata_fields: - columns = f"sample_id, {columns}" - sql = f""" - SELECT DISTINCT {columns} - FROM {config_name} - """ - else: - # No metadata_fields specified, select all - sql = f""" - SELECT DISTINCT * - FROM {config_name} - """ - - df = api.query(sql, config_name) - - # For sample-level, we want one row per sample_id - if "sample_id" in df.columns: - df_samples = df.groupby("sample_id").first().reset_index() - else: - # No sample_id column, return distinct rows - df_samples = df - - # Add extracted columns from field-level metadata - if "field_values" in metadata: - df_samples = self._add_extracted_columns(df_samples, metadata["field_values"]) - - return df_samples - - except Exception as e: - return pd.DataFrame( - {"error": [str(e)], "note": ["Failed to retrieve sample metadata"]} - ) - - def _add_extracted_columns( - self, - df: pd.DataFrame, - field_values: dict[str, dict[str, Any]] - ) -> pd.DataFrame: - """ - Add columns extracted from field-level property mappings. - - For each field that has property mappings, creates new columns by looking up - the field value in field_values and extracting the mapped properties. - - :param df: DataFrame with base metadata - :param field_values: Dict mapping field values to their extracted properties - :return: DataFrame with additional extracted columns - - """ - # Group properties by source field - # field_values is like: {"YPD": {"growth_media": ["YPD"], "carbon_source": ["glucose"]}} - - # We need to determine which field each row's value comes from - # For harbison_2004, the field is "condition" - - # Find fields that have definitions (these are the ones we can map) - for field_value, properties in field_values.items(): - # Each property becomes a new column - for prop_name, prop_values in properties.items(): - # Initialize column if it doesn't exist - if prop_name not in df.columns: - df[prop_name] = None - - # For each row where the field matches this field_value, set the property - # We need to find which column contains field_value - for col in df.columns: - if col in [prop_name, "sample_id"]: - continue - # Check if this column contains field_value - mask = df[col] == field_value - if mask.any(): - # Set the property value for matching rows - # Take first value from list (normalized values are lists) - value = prop_values[0] if prop_values else None - df.loc[mask, prop_name] = value - - return df - - def _get_full_data( - self, - repo_id: str, - config_name: str, - metadata: dict[str, Any], - token: str | None = None, - ) -> pd.DataFrame: - """ - Get full data with all measurements (Mode 2). - - Returns many rows per sample_id (one per measured feature/target). Includes ALL - data (no filtering) and ALL columns (both metadata and measurements). - - Unlike samples mode which respects metadata_fields, this mode returns the complete - dataset including quantitative measurements. - - :param repo_id: Repository ID - :param config_name: Configuration name - :param metadata: Extracted metadata dict - :param token: Optional HuggingFace token - :return: DataFrame with full data (all columns) - - """ - # Initialize query API - api = HfQueryAPI(repo_id, token=token) - - # Query for all data (no WHERE clause filtering) - sql = f""" - SELECT * - FROM {config_name} - """ - - try: - return api.query(sql, config_name) - except Exception as e: - return pd.DataFrame( - {"error": [str(e)], "note": ["Failed to retrieve full data"]} - ) - - def __repr__(self) -> str: - """String representation.""" - n_props = len(self.factor_aliases) - n_repos = len(self.config.repositories) - return ( - f"MetadataBuilder({n_props} properties with aliases, " - f"{n_repos} repositories configured)" - ) diff --git a/tfbpapi/datainfo/metadata_config_models.py b/tfbpapi/datainfo/metadata_config_models.py deleted file mode 100644 index 2cd4ded..0000000 --- a/tfbpapi/datainfo/metadata_config_models.py +++ /dev/null @@ -1,312 +0,0 @@ -""" -Pydantic models for metadata normalization configuration. - -This module defines the schema for MetadataBuilder configuration files, providing -validation for factor alias mappings and repository configurations. - -""" - -from __future__ import annotations - -from pathlib import Path -from typing import Any, Dict, List, Optional - -import yaml # type: ignore[import-untyped] -from pydantic import BaseModel, Field, field_validator, model_validator - - -class PropertyMapping(BaseModel): - """ - Mapping specification for a single property. - - Attributes: - path: Dot-notation path to the property value. - For repo/config-level: relative to experimental_conditions - For field-level: relative to field definitions - field: Optional field name for field-level properties. - When specified, looks in this field's definitions. - When omitted, looks in repo/config-level experimental_conditions. - - Examples: - Field-level property: - PropertyMapping(field="condition", path="media.carbon_source") - - Repo/config-level property: - PropertyMapping(path="temperature_celsius") - - """ - - field: str | None = Field(None, description="Field name for field-level properties") - path: str = Field(..., min_length=1, description="Dot-notation path to property") - - @field_validator("path") - @classmethod - def validate_path(cls, v: str) -> str: - """Ensure path is not just whitespace.""" - if not v.strip(): - raise ValueError("path cannot be empty or whitespace") - return v.strip() - - @field_validator("field") - @classmethod - def validate_field(cls, v: str | None) -> str | None: - """Ensure field is not empty string if provided.""" - if v is not None and not v.strip(): - raise ValueError("field cannot be empty or whitespace") - return v.strip() if v else None - - -class RepositoryConfig(BaseModel): - """ - Configuration for a single repository. Eg BrentLab/harbison_2004. - - Attributes: - properties: Repo-wide property mappings that apply to all datasets - dataset: Dataset-specific property mappings (override repo-wide) - - Example: - ```python - config = RepositoryConfig( - properties={ - "temperature_celsius": PropertyMapping(path="temperature_celsius") - }, - dataset={ - "dataset_name": { - "carbon_source": PropertyMapping( - field="condition", - path="media.carbon_source" - ) - } - } - ) - ``` - - """ - - properties: dict[str, PropertyMapping] = Field( - default_factory=dict, description="Repo-wide property mappings" - ) - dataset: dict[str, dict[str, PropertyMapping]] | None = Field( - None, description="Dataset-specific property mappings" - ) - - @model_validator(mode="before") - @classmethod - def parse_structure(cls, data: Any) -> Any: - """Parse raw dict structure into typed PropertyMapping objects.""" - if not isinstance(data, dict): - return data - - # Extract and parse dataset section - dataset_section = data.get("dataset") - parsed_datasets: dict[str, dict[str, PropertyMapping]] | None = None - - if dataset_section: - if not isinstance(dataset_section, dict): - raise ValueError("'dataset' key must contain a dict") - - parsed_datasets = {} - for dataset_name, properties in dataset_section.items(): - if not isinstance(properties, dict): - raise ValueError( - f"Dataset '{dataset_name}' must contain a dict of properties" - ) - - # Parse each property mapping into PropertyMapping object - parsed_datasets[dataset_name] = {} - for prop_name, mapping in properties.items(): - try: - parsed_datasets[dataset_name][prop_name] = ( - PropertyMapping.model_validate(mapping) - ) - except Exception as e: - raise ValueError( - f"Invalid property '{prop_name}' in dataset " - f"'{dataset_name}': {e}" - ) from e - - # Parse repo-wide properties (all keys except 'dataset') - parsed_properties = {} - for key, value in data.items(): - if key == "dataset": - continue - - try: - parsed_properties[key] = PropertyMapping.model_validate(value) - except Exception as e: - raise ValueError(f"Invalid repo-wide property '{key}': {e}") from e - - return {"properties": parsed_properties, "dataset": parsed_datasets} - - -class MetadataConfig(BaseModel): - """ - Configuration for building standardized metadata tables. - - Specifies optional alias mappings for normalizing factor levels across - heterogeneous datasets, plus property path mappings for each repository. - - Attributes: - factor_aliases: Optional mappings of standardized names to actual values. - Example: {"carbon_source": {"glucose": ["D-glucose", "dextrose"]}} - repositories: Dict mapping repository IDs to their configurations - - Example: - ```yaml - factor_aliases: - carbon_source: - glucose: ["D-glucose", "dextrose"] - galactose: ["D-galactose", "Galactose"] - - BrentLab/harbison_2004: - dataset: - harbison_2004: - carbon_source: - field: condition - path: media.carbon_source - - BrentLab/kemmeren_2014: - temperature: - path: temperature_celsius - dataset: - kemmeren_2014: - carbon_source: - path: media.carbon_source - ``` - - """ - - factor_aliases: dict[str, dict[str, list[Any]]] = Field( - default_factory=dict, - description="Optional alias mappings for normalizing factor levels", - ) - repositories: dict[str, RepositoryConfig] = Field( - ..., description="Repository configurations keyed by repo ID" - ) - - @field_validator("factor_aliases") - @classmethod - def validate_factor_aliases( - cls, v: dict[str, dict[str, list[Any]]] - ) -> dict[str, dict[str, list[Any]]]: - """Validate factor alias structure.""" - # Empty is OK - aliases are optional - if not v: - return v - - for prop_name, aliases in v.items(): - if not isinstance(aliases, dict): - raise ValueError( - f"Property '{prop_name}' aliases must be a dict, " - f"got {type(aliases).__name__}" - ) - - # Validate each alias mapping - for alias_name, actual_values in aliases.items(): - if not isinstance(actual_values, list): - raise ValueError( - f"Alias '{alias_name}' for '{prop_name}' must map " - f"to a list of values" - ) - if not actual_values: - raise ValueError( - f"Alias '{alias_name}' for '{prop_name}' cannot " - f"have empty value list" - ) - for val in actual_values: - if not isinstance(val, (str, int, float, bool)): - raise ValueError( - f"Alias '{alias_name}' for '{prop_name}' contains " - f"invalid value type: {type(val).__name__}" - ) - - return v - - @model_validator(mode="before") - @classmethod - def parse_repositories(cls, data: Any) -> Any: - """Parse repository configurations from top-level keys.""" - if not isinstance(data, dict): - return data - - # Extract repositories (all keys except 'factor_aliases') - repositories = {} - for key, value in data.items(): - if key != "factor_aliases": - try: - repositories[key] = RepositoryConfig.model_validate(value) - except Exception as e: - raise ValueError( - f"Invalid configuration for repository '{key}': {e}" - ) from e - - if not repositories: - raise ValueError( - "Configuration must have at least one repository configuration" - ) - - return { - "factor_aliases": data.get("factor_aliases", {}), - "repositories": repositories, - } - - @classmethod - def from_yaml(cls, path: Path | str) -> MetadataConfig: - """ - Load and validate configuration from YAML file. - - :param path: Path to YAML configuration file - :return: Validated MetadataConfig instance - :raises FileNotFoundError: If file doesn't exist - :raises ValueError: If configuration is invalid - - """ - path = Path(path) - - if not path.exists(): - raise FileNotFoundError(f"Configuration file not found: {path}") - - with open(path) as f: - data = yaml.safe_load(f) - - if not isinstance(data, dict): - raise ValueError("Configuration must be a YAML dict") - - return cls.model_validate(data) - - def get_repository_config(self, repo_id: str) -> RepositoryConfig | None: - """ - Get configuration for a specific repository. - - :param repo_id: Repository ID (e.g., "BrentLab/harbison_2004") - :return: RepositoryConfig instance or None if not found - - """ - return self.repositories.get(repo_id) - - def get_property_mappings( - self, repo_id: str, config_name: str - ) -> dict[str, PropertyMapping]: - """ - Get merged property mappings for a repo/dataset combination. - - Merges repo-wide and dataset-specific mappings, with dataset-specific taking - precedence. - - :param repo_id: Repository ID - :param config_name: Dataset/config name - :return: Dict mapping property names to PropertyMapping objects - - """ - repo_config = self.get_repository_config(repo_id) - if not repo_config: - return {} - - # Start with repo-wide properties - mappings: dict[str, PropertyMapping] = dict(repo_config.properties) - - # Override with dataset-specific properties - if repo_config.dataset and config_name in repo_config.dataset: - mappings.update(repo_config.dataset[config_name]) - - return mappings diff --git a/tfbpapi/datainfo/metadata_manager.py b/tfbpapi/datainfo/metadata_manager.py deleted file mode 100644 index d20b05a..0000000 --- a/tfbpapi/datainfo/metadata_manager.py +++ /dev/null @@ -1,282 +0,0 @@ -""" -MetadataManager for creating metadata table views from DataCard information. - -This module provides the MetadataManager class for generating SQL commands that create -metadata views from HuggingFace dataset information extracted via DataCard. The manager -enables users to: - -- Specify which columns to include in metadata views -- Add constant columns from top-level or config-level experimental conditions -- Flatten nested condition definitions into queryable columns -- Generate SQL CREATE VIEW statements - -The design follows a user-driven workflow: -1. User explores DataCard to understand available fields and conditions -2. User downloads data via HfCacheManager/HfQueryAPI (creates base views) -3. User specifies column selection for metadata view -4. MetadataManager generates and executes SQL to create the view - -""" - -from pathlib import Path -from typing import Any - -import duckdb -import pandas as pd - -# Separator conventions for formatting compound metadata values -COMPONENT_SEPARATORS = { - "type_value": ":", # Separates type from value (e.g., "type:value") - "value_conc": "@", # Separates value from concentration (e.g., "compound@0.5%") - "components": ";", # Separates multiple components (e.g., "comp1;comp2;comp3") - "types": "|", # Separates component types (e.g., "type1|type2|type3") -} - - -class MetadataManager: - """ - Manager for creating metadata table views from DataCard information. - - MetadataManager provides a flexible interface for generating SQL views that combine - data fields with experimental condition metadata. Users specify which columns to - include and how to handle conditions at different hierarchy levels. - - Example: - >>> # Step 1: Explore schema with DataCard - >>> card = DataCard("BrentLab/harbison_2004") - >>> schema = card.extract_metadata_schema("harbison_2004") - >>> - >>> # Step 2: Create metadata view - >>> mgr = MetadataManager() - >>> view = mgr.create_metadata_view( - ... base_view="harbison_2004_train", - ... view_name="harbison_2004_metadata", - ... include_fields=["regulator_locus_tag", "target_locus_tag", "condition"], - ... constant_columns={ - ... "temperature_celsius": 30, - ... "strain_background": "BY4741" - ... } - ... ) - - """ - - def __init__( - self, - cache_dir: Path | None = None, - cache: bool = False, - duckdb_conn: duckdb.DuckDBPyConnection | None = None, - ): - """ - Initialize MetadataManager. - - :param cache_dir: Optional directory for caching metadata views - :param cache: Whether to enable persistent caching (not yet implemented) - :param duckdb_conn: Optional DuckDB connection to use. If None, creates new in- - memory connection. Pass a shared connection to work with views created by - HfQueryAPI or other tools. - - """ - self._conn = ( - duckdb_conn if duckdb_conn is not None else duckdb.connect(":memory:") - ) - self._cache_dir = cache_dir - self._cache_enabled = cache - self._registered_datasets: dict[str, Any] = {} - self._view_names: dict[tuple[str, str], str] = {} - - def _sanitize_view_name(self, repo_id: str, config_name: str) -> str: - """ - Convert repo_id and config_name to valid SQL identifier. - - Replaces special characters (/, -, spaces) with underscores and appends - '_metadata' suffix. - - :param repo_id: Repository ID (e.g., "BrentLab/harbison_2004") - :param config_name: Configuration name (e.g., "harbison_2004") - :return: Sanitized view name - (e.g., "BrentLab_harbison_2004_harbison_2004_metadata") - - Example: - >>> mgr = MetadataManager() - >>> mgr._sanitize_view_name("BrentLab/dataset-name", "config_name") - 'BrentLab_dataset_name_config_name_metadata' - - """ - sanitized = f"{repo_id}_{config_name}" - sanitized = sanitized.replace("/", "_").replace("-", "_").replace(" ", "_") - return f"{sanitized}_metadata" - - def _flatten_condition_definition(self, definition: dict) -> dict: - """ - Flatten nested condition definition dict into flat key-value pairs. - - Extracts common experimental condition fields: - - media.name -> growth_media - - temperature_celsius -> temperature_celsius - - cultivation_method -> cultivation_method - - strain_background -> strain_background - - :param definition: Nested definition dict from field.definitions[value] - :return: Flat dict with standardized keys - - Example: - >>> mgr = MetadataManager() - >>> definition = { - ... "media": {"name": "YPD"}, - ... "temperature_celsius": 30 - ... } - >>> mgr._flatten_condition_definition(definition) - {'growth_media': 'YPD', 'temperature_celsius': 30} - - """ - result: dict[str, Any] = {} - if not definition: - return result - - # Extract media name as growth_media - if "media" in definition and isinstance(definition["media"], dict): - if "name" in definition["media"]: - result["growth_media"] = definition["media"]["name"] - - # Extract other top-level condition fields - for key in ["temperature_celsius", "cultivation_method", "strain_background"]: - if key in definition: - result[key] = definition[key] - - return result - - def _flatten_experimental_conditions(self, exp_conds: Any) -> dict: - """ - Extract attributes from ExperimentalConditions object or dict. - - Handles both Pydantic model objects and plain dicts. Extracts: - - temperature_celsius - - cultivation_method - - strain_background - - media.name (as growth_media) - - :param exp_conds: ExperimentalConditions object or dict - :return: Flat dict with condition values - - Example: - >>> mgr = MetadataManager() - >>> class MockConditions: - ... temperature_celsius = 30 - ... strain_background = "BY4741" - >>> mgr._flatten_experimental_conditions(MockConditions()) - {'temperature_celsius': 30, 'strain_background': 'BY4741'} - - """ - result: dict[str, Any] = {} - if exp_conds is None: - return result - - # Handle both objects (with attributes) and dicts (with keys) - for attr in ["temperature_celsius", "cultivation_method", "strain_background"]: - if hasattr(exp_conds, attr): - val = getattr(exp_conds, attr, None) - else: - val = exp_conds.get(attr) if isinstance(exp_conds, dict) else None - - if val is not None: - result[attr] = val - - # Extract media.name if present - if hasattr(exp_conds, "media"): - media = getattr(exp_conds, "media", None) - else: - media = exp_conds.get("media") if isinstance(exp_conds, dict) else None - - if media: - if hasattr(media, "name"): - name = getattr(media, "name", None) - else: - name = media.get("name") if isinstance(media, dict) else None - - if name: - result["growth_media"] = name - - return result - - def create_metadata_view( - self, - base_view: str, - view_name: str, - include_fields: list[str], - constant_columns: dict[str, Any] | None = None, - ) -> str: - """ - Create metadata view from base view with user-specified columns. - - Generates SQL CREATE OR REPLACE VIEW statement that selects specified fields - from an existing base view and adds constant columns as literals. - - :param base_view: Name of existing DuckDB view (created by HfCacheManager) - :param view_name: Name for the new metadata view - :param include_fields: List of field names to select from base view - :param constant_columns: Optional dict of {column_name: value} - to add as constants - :return: Name of created view - - Example: - >>> mgr = MetadataManager() - >>> view = mgr.create_metadata_view( - ... base_view="harbison_2004_train", - ... view_name="harbison_2004_metadata", - ... include_fields=["regulator_locus_tag", "target_locus_tag"], - ... constant_columns={"temperature_celsius": 30} - ... ) - >>> view - 'harbison_2004_metadata' - - """ - # Build SELECT clause with included fields - select_parts = include_fields.copy() - - # Add constant columns as literals - if constant_columns: - for col_name, col_value in constant_columns.items(): - if isinstance(col_value, str): - select_parts.append(f"'{col_value}' AS {col_name}") - else: - select_parts.append(f"{col_value} AS {col_name}") - - select_clause = ",\n ".join(select_parts) - - # Generate SQL - sql = f"""\ - CREATE OR REPLACE VIEW {view_name} AS - SELECT {select_clause} - FROM {base_view} -""" - - # Execute - self._conn.execute(sql) - - return view_name - - def get_active_configs(self) -> list[tuple[str, str]]: - """ - Get list of registered (repo_id, config_name) tuples. - - :return: List of active config tuples (empty for minimal implementation) - - Note: - This is a placeholder method for future multi-dataset support. - Currently returns empty list. - - """ - return [] - - def get_summary(self) -> pd.DataFrame: - """ - Get summary DataFrame of registered datasets. - - :return: Empty DataFrame for minimal implementation - - Note: - This is a placeholder method for future multi-dataset support. - Currently returns empty DataFrame. - - """ - return pd.DataFrame() diff --git a/tfbpapi/datainfo/models.py b/tfbpapi/datainfo/models.py deleted file mode 100644 index 0e3230b..0000000 --- a/tfbpapi/datainfo/models.py +++ /dev/null @@ -1,222 +0,0 @@ -""" -Pydantic models for dataset card validation. - -These models provide minimal structure for parsing HuggingFace dataset cards while -remaining flexible enough to accommodate diverse experimental systems. Most fields use -extra="allow" to accept domain-specific additions without requiring code changes. - -""" - -from enum import Enum -from typing import Any - -from pydantic import BaseModel, ConfigDict, Field, field_validator - - -class DatasetType(str, Enum): - """Supported dataset types.""" - - GENOMIC_FEATURES = "genomic_features" - ANNOTATED_FEATURES = "annotated_features" - GENOME_MAP = "genome_map" - METADATA = "metadata" - QC_DATA = "qc_data" - - -class FeatureInfo(BaseModel): - """ - Information about a dataset feature/column. - - Minimal required fields with flexible dtype handling. - - """ - - name: str = Field(..., description="Column name in the data") - dtype: str | dict[str, Any] = Field( - ..., - description="Data type (string, int64, float64, etc.) or class_label dict", - ) - description: str = Field(..., description="Description of the field") - role: str | None = Field( - default=None, - description="Optional semantic role. 'experimental_condition' has special behavior.", - ) - definitions: dict[str, Any] | None = Field( - default=None, - description="For experimental_condition fields: definitions per value", - ) - - -class PartitioningInfo(BaseModel): - """Partitioning configuration for datasets.""" - - enabled: bool = Field(default=False, description="Whether partitioning is enabled") - partition_by: list[str] | None = Field( - default=None, description="Partition column names" - ) - path_template: str | None = Field( - default=None, description="Path template for partitioned files" - ) - - -class DatasetInfo(BaseModel): - """Dataset structure information.""" - - features: list[FeatureInfo] = Field(..., description="Feature definitions") - partitioning: PartitioningInfo | None = Field( - default=None, description="Partitioning configuration" - ) - - -class DataFileInfo(BaseModel): - """Information about data files.""" - - split: str = Field(default="train", description="Dataset split name") - path: str = Field(..., description="Path to data file(s)") - - -class DatasetConfig(BaseModel): - """ - Configuration for a dataset within a repository. - - Uses extra="allow" to accept arbitrary experimental_conditions and other fields. - - """ - - config_name: str = Field(..., description="Unique configuration identifier") - description: str = Field(..., description="Human-readable description") - dataset_type: DatasetType = Field(..., description="Type of dataset") - default: bool = Field( - default=False, description="Whether this is the default config" - ) - applies_to: list[str] | None = Field( - default=None, description="Configs this metadata applies to" - ) - metadata_fields: list[str] | None = Field( - default=None, description="Fields for embedded metadata extraction" - ) - data_files: list[DataFileInfo] = Field(..., description="Data file information") - dataset_info: DatasetInfo = Field(..., description="Dataset structure information") - - model_config = ConfigDict(extra="allow") - - @field_validator("applies_to") - @classmethod - def applies_to_only_for_metadata(cls, v, info): - """Validate that applies_to is only used for metadata or qc_data configs.""" - if v is not None: - dataset_type = info.data.get("dataset_type") - if dataset_type not in (DatasetType.METADATA, DatasetType.QC_DATA): - raise ValueError( - "applies_to field is only valid " - "for metadata and qc_data dataset types" - ) - return v - - @field_validator("metadata_fields") - @classmethod - def metadata_fields_validation(cls, v): - """Validate metadata_fields usage.""" - if v is not None and len(v) == 0: - raise ValueError("metadata_fields cannot be empty list, use None instead") - return v - - -class DatasetCard(BaseModel): - """ - Complete dataset card model. - - Uses extra="allow" to accept arbitrary top-level metadata and - experimental_conditions. - - """ - - configs: list[DatasetConfig] = Field(..., description="Dataset configurations") - - model_config = ConfigDict(extra="allow") - - @field_validator("configs") - @classmethod - def configs_not_empty(cls, v): - """Ensure at least one config is present.""" - if not v: - raise ValueError("At least one dataset configuration is required") - return v - - @field_validator("configs") - @classmethod - def unique_config_names(cls, v): - """Ensure config names are unique.""" - names = [config.config_name for config in v] - if len(names) != len(set(names)): - raise ValueError("Configuration names must be unique") - return v - - @field_validator("configs") - @classmethod - def at_most_one_default(cls, v): - """Ensure at most one config is marked as default.""" - defaults = [config for config in v if config.default] - if len(defaults) > 1: - raise ValueError("At most one configuration can be marked as default") - return v - - def get_config_by_name(self, name: str) -> DatasetConfig | None: - """Get a configuration by name.""" - for config in self.configs: - if config.config_name == name: - return config - return None - - def get_configs_by_type(self, dataset_type: DatasetType) -> list[DatasetConfig]: - """Get all configurations of a specific type.""" - return [ - config for config in self.configs if config.dataset_type == dataset_type - ] - - def get_default_config(self) -> DatasetConfig | None: - """Get the default configuration if one exists.""" - defaults = [config for config in self.configs if config.default] - return defaults[0] if defaults else None - - def get_data_configs(self) -> list[DatasetConfig]: - """Get all non-metadata configurations.""" - return [ - config - for config in self.configs - if config.dataset_type != DatasetType.METADATA - ] - - def get_metadata_configs(self) -> list[DatasetConfig]: - """Get all metadata configurations.""" - return [ - config - for config in self.configs - if config.dataset_type == DatasetType.METADATA - ] - - -class ExtractedMetadata(BaseModel): - """Metadata extracted from datasets.""" - - config_name: str = Field(..., description="Source configuration name") - field_name: str = Field( - ..., description="Field name the metadata was extracted from" - ) - values: set[str] = Field(..., description="Unique values found") - extraction_method: str = Field(..., description="How the metadata was extracted") - - model_config = ConfigDict( - # Allow sets in JSON serialization - json_encoders={set: list} - ) - - -class MetadataRelationship(BaseModel): - """Relationship between a data config and its metadata.""" - - data_config: str = Field(..., description="Data configuration name") - metadata_config: str = Field(..., description="Metadata configuration name") - relationship_type: str = Field( - ..., description="Type of relationship (explicit, embedded)" - ) diff --git a/tfbpapi/datainfo/sample_manager.py b/tfbpapi/datainfo/sample_manager.py deleted file mode 100644 index 271a0ee..0000000 --- a/tfbpapi/datainfo/sample_manager.py +++ /dev/null @@ -1,774 +0,0 @@ -""" -Node-based sample representation for flexible filtering across heterogeneous datasets. - -This module provides a lightweight, NoSQL-inspired approach to managing samples from -multiple datasets with varying experimental condition structures. Each sample is -represented as a node with flattened properties, enabling flexible filtering across -datasets with different metadata schemas. - -Key Components: -- SampleNode: Represents a single sample with flattened properties -- SampleNodeCollection: In-memory storage with efficient indexing -- ActiveSet: Filtered collection of samples supporting set operations -- SampleFilter: MongoDB-style query language for filtering -- ConditionFlattener: Handles heterogeneous experimental condition structures -- SampleManager: Main API for loading, filtering, and managing samples - -Example Usage: - >>> manager = SampleManager() - >>> manager.load_from_datacard("BrentLab/harbison_2004", "harbison_2004") - >>> active = manager.filter_all({"carbon_source": {"$contains": "glucose"}}) - >>> print(f"Found {len(active)} glucose-grown samples") -""" - -from __future__ import annotations - -from dataclasses import dataclass, field -from datetime import datetime -from pathlib import Path -from typing import Any, Iterator - -import duckdb -import pandas as pd - - -def get_nested_value(data: dict, path: str) -> Any: - """ - Navigate nested dict using dot notation. - - Handles missing intermediate keys gracefully by returning None. - - :param data: Dictionary to navigate - :param path: Dot-separated path (e.g., "environmental_conditions.media.name") - :return: Value at path or None if not found - """ - if not isinstance(data, dict): - return None - - keys = path.split(".") - current = data - - for key in keys: - if not isinstance(current, dict) or key not in current: - return None - current = current[key] - - return current - - -def flatten_compound_list(compounds: list[dict] | str | None) -> str: - """ - Flatten compound list to comma-separated string. - - Handles various representations: - - List of dicts: Extract compound names - - String "unspecified": Return as-is - - None or empty list: Return "unspecified" - - :param compounds: Compound list or string - :return: Comma-separated compound names or "unspecified" - """ - if compounds is None or compounds == "unspecified": - return "unspecified" - - if isinstance(compounds, str): - return compounds - - if isinstance(compounds, list): - if not compounds: - return "unspecified" - compound_names = [ - c.get("compound", "") for c in compounds if isinstance(c, dict) - ] - return ", ".join(compound_names) if compound_names else "unspecified" - - return "unspecified" - - -@dataclass -class SampleNode: - """ - Represents a single sample with flattened experimental condition metadata. - - A sample is uniquely identified by (repo_id, config_name, sample_id) and contains - flattened properties from the 3-level experimental conditions hierarchy plus - selected metadata field values. - - Attributes: - sample_id: Unique identifier within config - repo_id: Dataset repository (e.g., "BrentLab/harbison_2004") - config_name: Configuration name (e.g., "harbison_2004") - properties: Flattened experimental condition properties - metadata_fields: Selected data fields (regulator, target, etc.) - property_sources: Tracks which level each property came from (repo/config/field/missing) - """ - - sample_id: str - repo_id: str - config_name: str - properties: dict[str, Any] = field(default_factory=dict) - metadata_fields: dict[str, Any] = field(default_factory=dict) - property_sources: dict[str, str] = field(default_factory=dict) - - def global_id(self) -> str: - """ - Generate unique identifier across all datasets. - - Format: {repo_id}:{config_name}:{sample_id} - - :return: Global sample ID - """ - return f"{self.repo_id}:{self.config_name}:{self.sample_id}" - - def get_property(self, key: str, default: Any = None) -> Any: - """ - Get property value with default fallback. - - :param key: Property name - :param default: Default value if property not found - :return: Property value or default - """ - return self.properties.get(key, default) - - def __repr__(self) -> str: - """String representation showing global ID and property count.""" - return f"SampleNode({self.global_id()}, {len(self.properties)} properties)" - - -class ConditionFlattener: - """ - Flattens experimental conditions from 3-level hierarchy into node properties. - - Handles heterogeneity in experimental condition structures across datasets: - - Variable nesting depths - - Missing/optional fields - - Compound lists - - Multi-level hierarchical overrides - - Resolution order: repo-level -> config-level -> field-level (field overrides all) - - This flattener dynamically discovers all properties without hardcoded schemas. - It recursively flattens nested structures using dot notation for keys. - """ - - @staticmethod - def _flatten_dict(d: dict, parent_key: str = "", sep: str = ".") -> dict[str, Any]: - """ - Recursively flatten nested dict using dot notation. - - :param d: Dictionary to flatten - :param parent_key: Parent key for recursion - :param sep: Separator for nested keys - :return: Flattened dict with dot-notation keys - """ - items = [] - for k, v in d.items(): - new_key = f"{parent_key}{sep}{k}" if parent_key else k - - if isinstance(v, dict): - # Recursively flatten nested dicts - items.extend( - ConditionFlattener._flatten_dict(v, new_key, sep=sep).items() - ) - elif isinstance(v, list) and v and isinstance(v[0], dict): - # Handle list of dicts (like compound lists) - # Store both flattened string and structured version - items.append((new_key, flatten_compound_list(v))) - items.append((f"_{new_key}_structured", v)) - else: - # Store primitive values as-is - items.append((new_key, v)) - - return dict(items) - - @classmethod - def flatten_conditions( - cls, - repo_conditions: dict | None, - config_conditions: dict | None, - field_conditions: dict | None, - ) -> tuple[dict[str, Any], dict[str, str]]: - """ - Flatten 3-level hierarchy into properties dict. - - Dynamically discovers and flattens all properties without hardcoded schemas. - Nested structures are flattened using dot notation (e.g., "media.carbon_source"). - - :param repo_conditions: Top-level experimental_conditions - :param config_conditions: Config-level experimental_conditions - :param field_conditions: Field-level condition definition - :return: (properties, property_sources) tuple - """ - properties = {} - sources = {} - - # Flatten each level - levels = [ - (repo_conditions, "repo"), - (config_conditions, "config"), - (field_conditions, "field"), - ] - - for conditions, level_name in levels: - if conditions is None: - continue - - # Flatten this level - flattened = cls._flatten_dict(conditions) - - # Merge into properties (later levels override earlier) - for key, value in flattened.items(): - properties[key] = value - sources[key] = level_name - - return properties, sources - - -class SampleNodeCollection: - """ - In-memory collection of sample nodes with efficient indexing. - - Storage strategy: - - Primary index: {(repo_id, config_name): {sample_id: SampleNode}} - - Enables fast dataset-level and cross-dataset operations - - Memory-efficient for typical workloads (1K-100K samples) - - Attributes: - _nodes: Two-level dict storing all nodes - """ - - def __init__(self): - """Initialize empty collection.""" - self._nodes: dict[tuple[str, str], dict[str, SampleNode]] = {} - - def add_node(self, node: SampleNode): - """ - Add sample node to collection. - - :param node: SampleNode to add - """ - key = (node.repo_id, node.config_name) - if key not in self._nodes: - self._nodes[key] = {} - self._nodes[key][node.sample_id] = node - - def get_node( - self, repo_id: str, config_name: str, sample_id: str - ) -> SampleNode | None: - """ - Get specific node. - - :param repo_id: Repository ID - :param config_name: Config name - :param sample_id: Sample ID - :return: SampleNode or None if not found - """ - key = (repo_id, config_name) - if key not in self._nodes: - return None - return self._nodes[key].get(sample_id) - - def get_node_by_global_id(self, global_id: str) -> SampleNode | None: - """ - Get node by global ID. - - :param global_id: Global ID in format {repo_id}:{config_name}:{sample_id} - :return: SampleNode or None if not found - """ - parts = global_id.split(":", 2) - if len(parts) != 3: - return None - return self.get_node(parts[0], parts[1], parts[2]) - - def iter_dataset_nodes( - self, repo_id: str, config_name: str - ) -> Iterator[SampleNode]: - """ - Iterate over nodes in specific dataset. - - :param repo_id: Repository ID - :param config_name: Config name - :return: Iterator over SampleNodes - """ - key = (repo_id, config_name) - if key in self._nodes: - yield from self._nodes[key].values() - - def iter_all_nodes(self) -> Iterator[SampleNode]: - """ - Iterate over all nodes in collection. - - :return: Iterator over all SampleNodes - """ - for dataset_nodes in self._nodes.values(): - yield from dataset_nodes.values() - - def get_dataset_keys(self) -> list[tuple[str, str]]: - """ - Get list of loaded (repo_id, config_name) pairs. - - :return: List of dataset keys - """ - return list(self._nodes.keys()) - - def count_total_nodes(self) -> int: - """ - Count total nodes in collection. - - :return: Total number of nodes - """ - return sum(len(nodes) for nodes in self._nodes.values()) - - def count_dataset_nodes(self, repo_id: str, config_name: str) -> int: - """ - Count nodes in specific dataset. - - :param repo_id: Repository ID - :param config_name: Config name - :return: Number of nodes in dataset - """ - key = (repo_id, config_name) - return len(self._nodes.get(key, {})) - - -@dataclass -class ActiveSet: - """ - Represents a collection of active samples with provenance tracking. - - ActiveSet supports set operations (union, intersection, difference) and - maintains metadata about how the set was created. This enables building - complex filters incrementally and tracking analysis provenance. - - Attributes: - sample_ids: Set of global sample IDs - name: Optional name for this set - description: Optional description - source_filter: Filter query that created this set - created_at: Timestamp - parent_set: ID of parent set (for tracking lineage) - """ - - sample_ids: set[str] = field(default_factory=set) - name: str | None = None - description: str | None = None - source_filter: dict | None = None - created_at: datetime = field(default_factory=datetime.now) - parent_set: str | None = None - - def __len__(self) -> int: - """Return number of samples in set.""" - return len(self.sample_ids) - - def union(self, other: ActiveSet, name: str | None = None) -> ActiveSet: - """ - Create new set with samples from both sets. - - :param other: Another ActiveSet - :param name: Optional name for new set - :return: New ActiveSet with union - """ - return ActiveSet( - sample_ids=self.sample_ids | other.sample_ids, - name=name or f"{self.name}_union_{other.name}", - description=f"Union of {self.name} and {other.name}", - ) - - def intersection(self, other: ActiveSet, name: str | None = None) -> ActiveSet: - """ - Create new set with samples in both sets. - - :param other: Another ActiveSet - :param name: Optional name for new set - :return: New ActiveSet with intersection - """ - return ActiveSet( - sample_ids=self.sample_ids & other.sample_ids, - name=name or f"{self.name}_intersect_{other.name}", - description=f"Intersection of {self.name} and {other.name}", - ) - - def difference(self, other: ActiveSet, name: str | None = None) -> ActiveSet: - """ - Create new set with samples in this set but not other. - - :param other: Another ActiveSet - :param name: Optional name for new set - :return: New ActiveSet with difference - """ - return ActiveSet( - sample_ids=self.sample_ids - other.sample_ids, - name=name or f"{self.name}_minus_{other.name}", - description=f"Samples in {self.name} but not {other.name}", - ) - - def to_sample_ids(self) -> list[str]: - """ - Export as list of global sample IDs. - - :return: Sorted list of global IDs - """ - return sorted(self.sample_ids) - - def __repr__(self) -> str: - """String representation showing name and size.""" - return f"ActiveSet(name={self.name}, size={len(self)})" - - -class SampleFilter: - """ - Evaluate filter expressions against sample nodes. - - Implements MongoDB-style query language for filtering heterogeneous sample data. - - Supported operators: - - $eq, $ne: Equality/inequality (default is $eq) - - $gt, $gte, $lt, $lte: Numeric comparisons - - $in, $nin: List membership - - $contains: String/list containment (case-insensitive) - - $exists: Field presence check - - $and, $or: Logical operators - - Example queries: - {"temperature_celsius": 30} # Simple equality - {"carbon_source": {"$contains": "glucose"}} # Contains check - {"$and": [{"temp": {"$gte": 25}}, {"temp": {"$lte": 35}}]} # Range - """ - - @staticmethod - def _matches_operator(value: Any, operator: str, target: Any) -> bool: - """ - Check if value matches operator condition. - - :param value: Actual property value - :param operator: Operator string (e.g., "$eq", "$contains") - :param target: Target value to compare against - :return: True if condition matches - """ - # Handle None values - if value is None: - if operator == "$exists": - return not target # $exists: false matches None - return operator == "$eq" and target is None - - # Equality operators - if operator == "$eq": - return value == target - if operator == "$ne": - return value != target - - # Numeric comparisons - if operator in ["$gt", "$gte", "$lt", "$lte"]: - try: - value_num = ( - float(value) if not isinstance(value, (int, float)) else value - ) - target_num = ( - float(target) if not isinstance(target, (int, float)) else target - ) - if operator == "$gt": - return value_num > target_num - if operator == "$gte": - return value_num >= target_num - if operator == "$lt": - return value_num < target_num - if operator == "$lte": - return value_num <= target_num - except (ValueError, TypeError): - return False - - # List membership - if operator == "$in": - return value in target if isinstance(target, (list, set, tuple)) else False - if operator == "$nin": - return ( - value not in target if isinstance(target, (list, set, tuple)) else True - ) - - # Contains check (case-insensitive for strings) - if operator == "$contains": - if isinstance(value, str) and isinstance(target, str): - return target.lower() in value.lower() - if isinstance(value, (list, tuple)): - return target in value - return False - - # Existence check - if operator == "$exists": - return bool(target) # $exists: true matches any non-None value - - return False - - @classmethod - def _matches_condition(cls, node: SampleNode, field: str, condition: Any) -> bool: - """ - Check if node matches a single field condition. - - :param node: SampleNode to check - :param field: Property name - :param condition: Condition value or operator dict - :return: True if matches - """ - # Get value from node - value = node.get_property(field) - - # Simple equality check - if not isinstance(condition, dict): - return value == condition - - # Operator-based checks - for operator, target in condition.items(): - if not cls._matches_operator(value, operator, target): - return False - - return True - - @classmethod - def matches(cls, node: SampleNode, query: dict) -> bool: - """ - Check if node matches filter query. - - :param node: SampleNode to check - :param query: Filter query dict - :return: True if node matches all conditions - """ - # Handle logical operators - if "$and" in query: - return all(cls.matches(node, sub_query) for sub_query in query["$and"]) - - if "$or" in query: - return any(cls.matches(node, sub_query) for sub_query in query["$or"]) - - # Check all field conditions (implicit AND) - for field, condition in query.items(): - if field.startswith("$"): # Skip logical operators - continue - if not cls._matches_condition(node, field, condition): - return False - - return True - - @classmethod - def filter_nodes( - cls, nodes: Iterator[SampleNode], query: dict - ) -> Iterator[SampleNode]: - """ - Filter nodes matching query (generator for memory efficiency). - - :param nodes: Iterator of SampleNodes - :param query: Filter query dict - :return: Iterator of matching nodes - """ - for node in nodes: - if cls.matches(node, query): - yield node - - -class SampleManager: - """ - Main interface for node-based sample management. - - SampleManager provides a flexible, NoSQL-inspired approach to managing samples - from multiple heterogeneous datasets. It replaces the table-based MetadataManager - with a node-based system that handles varying experimental condition structures. - - Key Features: - - Load samples from DataCard or DuckDB - - Filter using MongoDB-style queries - - Create and manage ActiveSets - - Export to DuckDB for SQL analysis - - Handle multi-dataset scenarios - - Example: - >>> manager = SampleManager() - >>> manager.load_from_datacard("BrentLab/harbison_2004", "harbison_2004") - >>> active = manager.filter_all({"carbon_source": {"$contains": "glucose"}}) - >>> print(f"Found {len(active)} samples") - """ - - def __init__( - self, - duckdb_conn: duckdb.DuckDBPyConnection | None = None, - cache_dir: Path | None = None, - ): - """ - Initialize SampleManager. - - :param duckdb_conn: Optional shared DuckDB connection for integration with HfQueryAPI - :param cache_dir: Optional cache directory for DataCard fetches - """ - self._collection = SampleNodeCollection() - self._active_sets: dict[str, ActiveSet] = {} - self._duckdb_conn = ( - duckdb_conn if duckdb_conn is not None else duckdb.connect(":memory:") - ) - self._cache_dir = cache_dir - self._flattener = ConditionFlattener() - - def get_active_configs(self) -> list[tuple[str, str]]: - """ - Get list of loaded (repo_id, config_name) pairs. - - :return: List of dataset keys - """ - return self._collection.get_dataset_keys() - - def get_summary(self) -> pd.DataFrame: - """ - Get summary of loaded samples. - - Returns DataFrame with columns: - - repo_id: Repository ID - - config_name: Configuration name - - sample_count: Number of samples - - properties: Common properties available - - :return: Summary DataFrame - """ - rows = [] - for repo_id, config_name in self.get_active_configs(): - count = self._collection.count_dataset_nodes(repo_id, config_name) - # Sample first node to get property names - first_node = next( - self._collection.iter_dataset_nodes(repo_id, config_name), None - ) - properties = list(first_node.properties.keys()) if first_node else [] - - rows.append( - { - "repo_id": repo_id, - "config_name": config_name, - "sample_count": count, - "properties": properties, - } - ) - - return pd.DataFrame(rows) - - def filter_all(self, query: dict, name: str | None = None) -> ActiveSet: - """ - Filter across all loaded samples. - - :param query: Filter query dict (MongoDB-style) - :param name: Optional name for ActiveSet - :return: ActiveSet with matching samples - """ - matching_nodes = SampleFilter.filter_nodes( - self._collection.iter_all_nodes(), query - ) - sample_ids = {node.global_id() for node in matching_nodes} - - return ActiveSet( - sample_ids=sample_ids, - name=name or "filtered_samples", - source_filter=query, - ) - - def filter_dataset( - self, - repo_id: str, - config_name: str, - query: dict, - name: str | None = None, - ) -> ActiveSet: - """ - Filter samples within specific dataset. - - :param repo_id: Repository ID - :param config_name: Config name - :param query: Filter query dict - :param name: Optional name for ActiveSet - :return: ActiveSet with matching samples - """ - matching_nodes = SampleFilter.filter_nodes( - self._collection.iter_dataset_nodes(repo_id, config_name), query - ) - sample_ids = {node.global_id() for node in matching_nodes} - - return ActiveSet( - sample_ids=sample_ids, - name=name or f"{config_name}_filtered", - source_filter=query, - ) - - def get_sample(self, global_id: str) -> SampleNode | None: - """ - Retrieve specific sample by global ID. - - :param global_id: Global ID in format {repo_id}:{config_name}:{sample_id} - :return: SampleNode or None if not found - """ - return self._collection.get_node_by_global_id(global_id) - - def get_samples_by_ids(self, sample_ids: list[str]) -> list[SampleNode]: - """ - Batch retrieve samples. - - :param sample_ids: List of global IDs - :return: List of SampleNodes (may be shorter if some not found) - """ - nodes = [] - for global_id in sample_ids: - node = self.get_sample(global_id) - if node is not None: - nodes.append(node) - return nodes - - def save_active_set(self, name: str, active_set: ActiveSet): - """ - Save named active set for later use. - - :param name: Name to save under - :param active_set: ActiveSet to save - """ - self._active_sets[name] = active_set - - def get_active_set(self, name: str) -> ActiveSet | None: - """ - Retrieve saved active set. - - :param name: Name of saved set - :return: ActiveSet or None if not found - """ - return self._active_sets.get(name) - - def list_active_sets(self) -> list[str]: - """ - List all saved active set names. - - :return: List of set names - """ - return list(self._active_sets.keys()) - - def get_property_distribution( - self, - property_name: str, - dataset_filter: tuple[str, str] | None = None, - ) -> dict[Any, int]: - """ - Get value distribution for a property. - - :param property_name: Property name to analyze - :param dataset_filter: Optional (repo_id, config_name) to limit to specific dataset - :return: Dict mapping values to counts - """ - distribution: dict[Any, int] = {} - - if dataset_filter: - nodes = self._collection.iter_dataset_nodes( - dataset_filter[0], dataset_filter[1] - ) - else: - nodes = self._collection.iter_all_nodes() - - for node in nodes: - value = node.get_property(property_name, "missing") - distribution[value] = distribution.get(value, 0) + 1 - - return distribution - - def __repr__(self) -> str: - """String representation showing loaded datasets and sample count.""" - total = self._collection.count_total_nodes() - datasets = len(self.get_active_configs()) - return f"SampleManager({datasets} datasets, {total} samples)" diff --git a/tfbpapi/errors.py b/tfbpapi/errors.py index 4fc751e..cbacc92 100644 --- a/tfbpapi/errors.py +++ b/tfbpapi/errors.py @@ -3,65 +3,7 @@ from typing import Any -class DatasetError(Exception): - """Base exception for all dataset-related errors.""" - - def __init__(self, message: str, details: dict[str, Any] | None = None): - super().__init__(message) - self.details = details or {} - - def __str__(self) -> str: - base_msg = super().__str__() - if self.details: - detail_str = ", ".join(f"{k}={v}" for k, v in self.details.items()) - return f"{base_msg} (Details: {detail_str})" - return base_msg - - -class RepoTooLargeError(DatasetError): - """Raised when repository exceeds auto-download threshold.""" - - def __init__(self, repo_id: str, size_mb: float, threshold_mb: float): - message = f"Repository '{repo_id}' is too large for " - f"auto-download: {size_mb:.2f}MB exceeds {threshold_mb}MB threshold" - super().__init__( - message, - details={ - "repo_id": repo_id, - "actual_size_mb": size_mb, - "threshold_mb": threshold_mb, - }, - ) - self.repo_id = repo_id - self.size_mb = size_mb - self.threshold_mb = threshold_mb - - -class DataCardParsingError(DatasetError): - """Raised when dataset card parsing fails.""" - - def __init__( - self, - message: str, - repo_id: str | None = None, - config_name: str | None = None, - original_error: Exception | None = None, - ): - details: dict[str, Any] = {} - if repo_id: - details["repo_id"] = repo_id - if config_name: - details["config_name"] = config_name - if original_error: - details["original_error"] = str(original_error) - - super().__init__(message, details) - self.repo_id = repo_id - self.config_name = config_name - self.original_error = original_error - - -class HfDataFetchError(DatasetError): +class HfDataFetchError(Exception): """Raised when HuggingFace API requests fail.""" def __init__( @@ -71,106 +13,13 @@ def __init__( status_code: int | None = None, endpoint: str | None = None, ): - details: dict[str, Any] = {} - if repo_id: - details["repo_id"] = repo_id - if status_code: - details["status_code"] = status_code - if endpoint: - details["endpoint"] = endpoint - - super().__init__(message, details) + super().__init__(message) self.repo_id = repo_id self.status_code = status_code self.endpoint = endpoint -class TableNotFoundError(DatasetError): - """Raised when requested table doesn't exist.""" - - def __init__(self, table_name: str, available_tables: list | None = None): - available_str = ( - f"Available tables: {available_tables}" - if available_tables - else "No tables available" - ) - message = f"Table '{table_name}' not found. {available_str}" - - super().__init__( - message, - details={ - "requested_table": table_name, - "available_tables": available_tables or [], - }, - ) - self.table_name = table_name - self.available_tables = available_tables or [] - - -class MissingDatasetTypeError(DatasetError): - """Raised when dataset_type field is missing from config.""" - - def __init__(self, config_name: str, available_fields: list | None = None): - fields_str = f"Available fields: {available_fields}" if available_fields else "" - message = ( - f"Missing 'dataset_type' field in config '{config_name}'. {fields_str}" - ) - - super().__init__( - message, - details={ - "config_name": config_name, - "available_fields": available_fields or [], - }, - ) - self.config_name = config_name - self.available_fields = available_fields or [] - - -class InvalidDatasetTypeError(DatasetError): - """Raised when dataset_type value is not recognized.""" - - def __init__(self, invalid_type: str, valid_types: list | None = None): - valid_str = f"Valid types: {valid_types}" if valid_types else "" - message = f"Invalid dataset type '{invalid_type}'. {valid_str}" - - super().__init__( - message, - details={"invalid_type": invalid_type, "valid_types": valid_types or []}, - ) - self.invalid_type = invalid_type - self.valid_types = valid_types or [] - - -class ConfigNotFoundError(DatasetError): - """Raised when a requested config doesn't exist.""" - - def __init__( - self, - config_name: str, - repo_id: str | None = None, - available_configs: list | None = None, - ): - repo_str = f" in repository '{repo_id}'" if repo_id else "" - available_str = ( - f"Available configs: {available_configs}" if available_configs else "" - ) - message = f"Config '{config_name}' not found{repo_str}. {available_str}" - - super().__init__( - message, - details={ - "config_name": config_name, - "repo_id": repo_id, - "available_configs": available_configs or [], - }, - ) - self.config_name = config_name - self.repo_id = repo_id - self.available_configs = available_configs or [] - - -class DataCardError(DatasetError): +class DataCardError(Exception): """Base exception for DataCard operations.""" pass @@ -185,67 +34,6 @@ def __init__( repo_id: str | None = None, validation_errors: list | None = None, ): - details: dict[str, Any] = {} - if repo_id: - details["repo_id"] = repo_id - if validation_errors: - details["validation_errors"] = validation_errors - - super().__init__(message, details) + super().__init__(message) self.repo_id = repo_id self.validation_errors = validation_errors or [] - - -class DataCardMetadataError(DataCardError): - """Exception raised when metadata extraction fails.""" - - def __init__( - self, - message: str, - config_name: str | None = None, - field_name: str | None = None, - ): - details: dict[str, Any] = {} - if config_name: - details["config_name"] = config_name - if field_name: - details["field_name"] = field_name - - super().__init__(message, details) - self.config_name = config_name - self.field_name = field_name - - -class InvalidFilterFieldError(DatasetError): - """Raised when filter fields don't exist in metadata columns.""" - - def __init__( - self, - config_name: str, - invalid_fields: list[str], - available_fields: list[str] | None = None, - ): - invalid_str = ", ".join(f"'{field}'" for field in invalid_fields) - available_str = ( - f"Available fields: {sorted(available_fields)}" - if available_fields - else "No fields available" - ) - message = ( - f"Invalid filter field(s) {invalid_str} for config '{config_name}'. " - f"{available_str}" - ) - - super().__init__( - message, - details={ - "config_name": config_name, - "invalid_fields": invalid_fields, - "available_fields": ( - sorted(available_fields) if available_fields else [] - ), - }, - ) - self.config_name = config_name - self.invalid_fields = invalid_fields - self.available_fields = sorted(available_fields) if available_fields else [] diff --git a/tfbpapi/datainfo/fetchers.py b/tfbpapi/fetchers.py similarity index 98% rename from tfbpapi/datainfo/fetchers.py rename to tfbpapi/fetchers.py index 30cc72a..c8d978f 100644 --- a/tfbpapi/datainfo/fetchers.py +++ b/tfbpapi/fetchers.py @@ -8,8 +8,8 @@ from huggingface_hub import DatasetCard, repo_info from requests import HTTPError -from ..constants import get_hf_token -from ..errors import HfDataFetchError +from tfbpapi.constants import get_hf_token +from tfbpapi.errors import HfDataFetchError class HfDataCardFetcher: diff --git a/tfbpapi/HfCacheManager.py b/tfbpapi/hf_cache_manager.py similarity index 88% rename from tfbpapi/HfCacheManager.py rename to tfbpapi/hf_cache_manager.py index 4d2e38e..0a4378c 100644 --- a/tfbpapi/HfCacheManager.py +++ b/tfbpapi/hf_cache_manager.py @@ -7,7 +7,7 @@ from huggingface_hub import scan_cache_dir, try_to_load_from_cache from huggingface_hub.utils import DeleteCacheStrategy -from .datainfo.datacard import DataCard +from tfbpapi.datacard import DataCard class HfCacheManager(DataCard): @@ -518,3 +518,67 @@ def _format_bytes(self, bytes_size: int) -> str: return f"{size:.1f}{unit}" size /= 1024.0 return f"{size:.1f}TB" + + def query( + self, sql: str, config_name: str, refresh_cache: bool = False + ) -> Any: + """ + Execute SQL query against a specific dataset configuration. + + Loads the specified configuration and executes the SQL query. + Automatically replaces the config name in the SQL with the actual + table name for user convenience. + + :param sql: SQL query to execute + :param config_name: Configuration name to query (table will be loaded + if needed) + :param refresh_cache: If True, force refresh from remote instead of + using cache + :return: DataFrame with query results + :raises ValueError: If config_name not found or query fails + + Example: + mgr = HfCacheManager("BrentLab/harbison_2004", duckdb.connect()) + df = mgr.query( + "SELECT DISTINCT sample_id FROM harbison_2004", + "harbison_2004" + ) + """ + # Validate config exists + if config_name not in [c.config_name for c in self.configs]: + available_configs = [c.config_name for c in self.configs] + raise ValueError( + f"Config '{config_name}' not found. " + f"Available configs: {available_configs}" + ) + + # Load the configuration data + config = self.get_config(config_name) + if not config: + raise ValueError(f"Could not retrieve config '{config_name}'") + + config_result = self._get_metadata_for_config( + config, force_refresh=refresh_cache + ) + if not config_result.get("success", False): + raise ValueError( + f"Failed to load data for config '{config_name}': " + f"{config_result.get('message', 'Unknown error')}" + ) + + table_name = config_result.get("table_name") + if not table_name: + raise ValueError(f"No table available for config '{config_name}'") + + # Replace config name with actual table name in SQL for user convenience + modified_sql = sql.replace(config_name, table_name) + + # Execute query + try: + result = self.duckdb_conn.execute(modified_sql).fetchdf() + self.logger.debug(f"Query executed successfully on {config_name}") + return result + except Exception as e: + self.logger.error(f"Query execution failed: {e}") + self.logger.error(f"SQL: {modified_sql}") + raise ValueError(f"Query execution failed: {e}") from e diff --git a/tfbpapi/models.py b/tfbpapi/models.py new file mode 100644 index 0000000..a1489f7 --- /dev/null +++ b/tfbpapi/models.py @@ -0,0 +1,584 @@ +""" +Pydantic models for dataset card validation and metadata configuration. + +These models provide minimal structure for parsing HuggingFace dataset cards while +remaining flexible enough to accommodate diverse experimental systems. Most fields use +extra="allow" to accept domain-specific additions without requiring code changes. + +Also includes models for VirtualDB metadata normalization configuration. + +""" + +from enum import Enum +from pathlib import Path +from typing import Any + +import yaml # type: ignore[import-untyped] +from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator + + +class DatasetType(str, Enum): + """Supported dataset types.""" + + GENOMIC_FEATURES = "genomic_features" + ANNOTATED_FEATURES = "annotated_features" + GENOME_MAP = "genome_map" + METADATA = "metadata" + QC_DATA = "qc_data" + + +class FeatureInfo(BaseModel): + """ + Information about a dataset feature/column. + + Minimal required fields with flexible dtype handling. + + """ + + name: str = Field(..., description="Column name in the data") + dtype: str | dict[str, Any] = Field( + ..., + description="Data type (string, int64, float64, etc.) or class_label dict", + ) + description: str = Field(..., description="Description of the field") + role: str | None = Field( + default=None, + description="Optional semantic role. 'experimental_condition' has special behavior.", + ) + definitions: dict[str, Any] | None = Field( + default=None, + description="For experimental_condition fields: definitions per value", + ) + + +class PartitioningInfo(BaseModel): + """Partitioning configuration for datasets.""" + + enabled: bool = Field(default=False, description="Whether partitioning is enabled") + partition_by: list[str] | None = Field( + default=None, description="Partition column names" + ) + path_template: str | None = Field( + default=None, description="Path template for partitioned files" + ) + + +class DatasetInfo(BaseModel): + """Dataset structure information.""" + + features: list[FeatureInfo] = Field(..., description="Feature definitions") + partitioning: PartitioningInfo | None = Field( + default=None, description="Partitioning configuration" + ) + + +class DataFileInfo(BaseModel): + """Information about data files.""" + + split: str = Field(default="train", description="Dataset split name") + path: str = Field(..., description="Path to data file(s)") + + +class DatasetConfig(BaseModel): + """ + Configuration for a dataset within a repository. + + Uses extra="allow" to accept arbitrary experimental_conditions and other fields. + + """ + + config_name: str = Field(..., description="Unique configuration identifier") + description: str = Field(..., description="Human-readable description") + dataset_type: DatasetType = Field(..., description="Type of dataset") + default: bool = Field( + default=False, description="Whether this is the default config" + ) + applies_to: list[str] | None = Field( + default=None, description="Configs this metadata applies to" + ) + metadata_fields: list[str] | None = Field( + default=None, description="Fields for embedded metadata extraction" + ) + data_files: list[DataFileInfo] = Field(..., description="Data file information") + dataset_info: DatasetInfo = Field(..., description="Dataset structure information") + + model_config = ConfigDict(extra="allow") + + @field_validator("applies_to") + @classmethod + def applies_to_only_for_metadata(cls, v, info): + """Validate that applies_to is only used for metadata or qc_data configs.""" + if v is not None: + dataset_type = info.data.get("dataset_type") + if dataset_type not in (DatasetType.METADATA, DatasetType.QC_DATA): + raise ValueError( + "applies_to field is only valid " + "for metadata and qc_data dataset types" + ) + return v + + @field_validator("metadata_fields") + @classmethod + def metadata_fields_validation(cls, v): + """Validate metadata_fields usage.""" + if v is not None and len(v) == 0: + raise ValueError("metadata_fields cannot be empty list, use None instead") + return v + + +class DatasetCard(BaseModel): + """ + Complete dataset card model. + + Uses extra="allow" to accept arbitrary top-level metadata and + experimental_conditions. + + """ + + configs: list[DatasetConfig] = Field(..., description="Dataset configurations") + + model_config = ConfigDict(extra="allow") + + @field_validator("configs") + @classmethod + def configs_not_empty(cls, v): + """Ensure at least one config is present.""" + if not v: + raise ValueError("At least one dataset configuration is required") + return v + + @field_validator("configs") + @classmethod + def unique_config_names(cls, v): + """Ensure config names are unique.""" + names = [config.config_name for config in v] + if len(names) != len(set(names)): + raise ValueError("Configuration names must be unique") + return v + + @field_validator("configs") + @classmethod + def at_most_one_default(cls, v): + """Ensure at most one config is marked as default.""" + defaults = [config for config in v if config.default] + if len(defaults) > 1: + raise ValueError("At most one configuration can be marked as default") + return v + + def get_config_by_name(self, name: str) -> DatasetConfig | None: + """Get a configuration by name.""" + for config in self.configs: + if config.config_name == name: + return config + return None + + def get_configs_by_type(self, dataset_type: DatasetType) -> list[DatasetConfig]: + """Get all configurations of a specific type.""" + return [ + config for config in self.configs if config.dataset_type == dataset_type + ] + + def get_default_config(self) -> DatasetConfig | None: + """Get the default configuration if one exists.""" + defaults = [config for config in self.configs if config.default] + return defaults[0] if defaults else None + + def get_data_configs(self) -> list[DatasetConfig]: + """Get all non-metadata configurations.""" + return [ + config + for config in self.configs + if config.dataset_type != DatasetType.METADATA + ] + + def get_metadata_configs(self) -> list[DatasetConfig]: + """Get all metadata configurations.""" + return [ + config + for config in self.configs + if config.dataset_type == DatasetType.METADATA + ] + + +class ExtractedMetadata(BaseModel): + """Metadata extracted from datasets.""" + + config_name: str = Field(..., description="Source configuration name") + field_name: str = Field( + ..., description="Field name the metadata was extracted from" + ) + values: set[str] = Field(..., description="Unique values found") + extraction_method: str = Field(..., description="How the metadata was extracted") + + model_config = ConfigDict( + # Allow sets in JSON serialization + json_encoders={set: list} + ) + + +class MetadataRelationship(BaseModel): + """Relationship between a data config and its metadata.""" + + data_config: str = Field(..., description="Data configuration name") + metadata_config: str = Field(..., description="Metadata configuration name") + relationship_type: str = Field( + ..., description="Type of relationship (explicit, embedded)" + ) + + +# ============================================================================ +# VirtualDB Metadata Configuration Models +# ============================================================================ + + +class PropertyMapping(BaseModel): + """ + Mapping specification for a single property. + + Attributes: + path: Optional dot-notation path to the property value. + For repo/config-level: relative to experimental_conditions + For field-level: relative to field definitions + When omitted with field specified, creates a column alias. + field: Optional field name for field-level properties. + When specified, looks in this field's definitions. + When omitted, looks in repo/config-level experimental_conditions. + + Examples: + Field-level property with path: + PropertyMapping(field="condition", path="media.carbon_source") + + Repo/config-level property: + PropertyMapping(path="temperature_celsius") + + Field-level column alias (no path): + PropertyMapping(field="condition") + + """ + + field: str | None = Field(None, description="Field name for field-level properties") + path: str | None = Field(None, description="Dot-notation path to property") + + @field_validator("path") + @classmethod + def validate_path(cls, v: str | None) -> str | None: + """Ensure path is not just whitespace if provided.""" + if v is not None and not v.strip(): + raise ValueError("path cannot be empty or whitespace") + return v.strip() if v else None + + @field_validator("field") + @classmethod + def validate_field(cls, v: str | None) -> str | None: + """Ensure field is not empty string if provided.""" + if v is not None and not v.strip(): + raise ValueError("field cannot be empty or whitespace") + return v.strip() if v else None + + @model_validator(mode="after") + def validate_at_least_one_specified(self) -> "PropertyMapping": + """Ensure at least field or path is specified.""" + if self.field is None and self.path is None: + raise ValueError("At least one of 'field' or 'path' must be specified") + return self + + +class RepositoryConfig(BaseModel): + """ + Configuration for a single repository. Eg BrentLab/harbison_2004. + + Attributes: + properties: Repo-wide property mappings that apply to all datasets + dataset: Dataset-specific property mappings (override repo-wide) + + Example: + ```python + config = RepositoryConfig( + properties={ + "temperature_celsius": PropertyMapping(path="temperature_celsius") + }, + dataset={ + "dataset_name": { + "carbon_source": PropertyMapping( + field="condition", + path="media.carbon_source" + ) + } + } + ) + ``` + + """ + + properties: dict[str, PropertyMapping] = Field( + default_factory=dict, description="Repo-wide property mappings" + ) + dataset: dict[str, dict[str, PropertyMapping]] | None = Field( + None, description="Dataset-specific property mappings" + ) + + @model_validator(mode="before") + @classmethod + def parse_structure(cls, data: Any) -> Any: + """Parse raw dict structure into typed PropertyMapping objects.""" + if not isinstance(data, dict): + return data + + # Extract and parse dataset section + dataset_section = data.get("dataset") + parsed_datasets: dict[str, dict[str, PropertyMapping]] | None = None + + if dataset_section: + if not isinstance(dataset_section, dict): + raise ValueError("'dataset' key must contain a dict") + + parsed_datasets = {} + for dataset_name, properties in dataset_section.items(): + if not isinstance(properties, dict): + raise ValueError( + f"Dataset '{dataset_name}' must contain a dict of properties" + ) + + # Parse each property mapping into PropertyMapping object + parsed_datasets[dataset_name] = {} + for prop_name, mapping in properties.items(): + try: + parsed_datasets[dataset_name][prop_name] = ( + PropertyMapping.model_validate(mapping) + ) + except Exception as e: + raise ValueError( + f"Invalid property '{prop_name}' in dataset " + f"'{dataset_name}': {e}" + ) from e + + # Parse repo-wide properties (all keys except 'dataset') + parsed_properties = {} + for key, value in data.items(): + if key == "dataset": + continue + + try: + parsed_properties[key] = PropertyMapping.model_validate(value) + except Exception as e: + raise ValueError(f"Invalid repo-wide property '{key}': {e}") from e + + return {"properties": parsed_properties, "dataset": parsed_datasets} + + +class MetadataConfig(BaseModel): + """ + Configuration for building standardized metadata tables. + + Specifies optional alias mappings for normalizing factor levels across + heterogeneous datasets, plus property path mappings for each repository. + + Attributes: + factor_aliases: Optional mappings of standardized names to actual values. + Example: {"carbon_source": {"glucose": ["D-glucose", "dextrose"]}} + missing_value_labels: Labels for missing values by property name + description: Human-readable descriptions for each property + repositories: Dict mapping repository IDs to their configurations + + Example: + ```yaml + repositories: + BrentLab/harbison_2004: + dataset: + harbison_2004: + carbon_source: + field: condition + path: media.carbon_source + + BrentLab/kemmeren_2014: + temperature: + path: temperature_celsius + dataset: + kemmeren_2014: + carbon_source: + path: media.carbon_source + + factor_aliases: + carbon_source: + glucose: ["D-glucose", "dextrose"] + galactose: ["D-galactose", "Galactose"] + + missing_value_labels: + carbon_source: "unspecified" + + description: + carbon_source: "Carbon source in growth media" + ``` + + """ + + factor_aliases: dict[str, dict[str, list[Any]]] = Field( + default_factory=dict, + description="Optional alias mappings for normalizing factor levels", + ) + missing_value_labels: dict[str, str] = Field( + default_factory=dict, + description="Labels for missing values by property name", + ) + description: dict[str, str] = Field( + default_factory=dict, + description="Human-readable descriptions for each property", + ) + repositories: dict[str, RepositoryConfig] = Field( + ..., description="Repository configurations keyed by repo ID" + ) + + @field_validator("missing_value_labels", mode="before") + @classmethod + def validate_missing_value_labels(cls, v: Any) -> dict[str, str]: + """Validate missing value labels structure, filtering out None values.""" + if not v: + return {} + if not isinstance(v, dict): + raise ValueError("missing_value_labels must be a dict") + # Filter out None values that may come from empty YAML values + return {k: val for k, val in v.items() if val is not None} + + @field_validator("description", mode="before") + @classmethod + def validate_description(cls, v: Any) -> dict[str, str]: + """Validate description structure, filtering out None values.""" + if not v: + return {} + if not isinstance(v, dict): + raise ValueError("description must be a dict") + # Filter out None values that may come from empty YAML values + return {k: val for k, val in v.items() if val is not None} + + @field_validator("factor_aliases") + @classmethod + def validate_factor_aliases( + cls, v: dict[str, dict[str, list[Any]]] + ) -> dict[str, dict[str, list[Any]]]: + """Validate factor alias structure.""" + # Empty is OK - aliases are optional + if not v: + return v + + for prop_name, aliases in v.items(): + if not isinstance(aliases, dict): + raise ValueError( + f"Property '{prop_name}' aliases must be a dict, " + f"got {type(aliases).__name__}" + ) + + # Validate each alias mapping + for alias_name, actual_values in aliases.items(): + if not isinstance(actual_values, list): + raise ValueError( + f"Alias '{alias_name}' for '{prop_name}' must map " + f"to a list of values" + ) + if not actual_values: + raise ValueError( + f"Alias '{alias_name}' for '{prop_name}' cannot " + f"have empty value list" + ) + for val in actual_values: + if not isinstance(val, (str, int, float, bool)): + raise ValueError( + f"Alias '{alias_name}' for '{prop_name}' contains " + f"invalid value type: {type(val).__name__}" + ) + + return v + + @model_validator(mode="before") + @classmethod + def parse_repositories(cls, data: Any) -> Any: + """Parse repository configurations from 'repositories' key.""" + if not isinstance(data, dict): + return data + + # Extract repositories from 'repositories' key + repositories_data = data.get("repositories", {}) + + if not repositories_data: + raise ValueError( + "Configuration must have a 'repositories' key with at least one repository" + ) + + if not isinstance(repositories_data, dict): + raise ValueError("'repositories' key must contain a dict") + + repositories = {} + for repo_id, repo_config in repositories_data.items(): + try: + repositories[repo_id] = RepositoryConfig.model_validate(repo_config) + except Exception as e: + raise ValueError( + f"Invalid configuration for repository '{repo_id}': {e}" + ) from e + + return { + "factor_aliases": data.get("factor_aliases", {}), + "missing_value_labels": data.get("missing_value_labels", {}), + "description": data.get("description", {}), + "repositories": repositories, + } + + @classmethod + def from_yaml(cls, path: Path | str) -> "MetadataConfig": + """ + Load and validate configuration from YAML file. + + :param path: Path to YAML configuration file + :return: Validated MetadataConfig instance + :raises FileNotFoundError: If file doesn't exist + :raises ValueError: If configuration is invalid + + """ + path = Path(path) + + if not path.exists(): + raise FileNotFoundError(f"Configuration file not found: {path}") + + with open(path) as f: + data = yaml.safe_load(f) + + if not isinstance(data, dict): + raise ValueError("Configuration must be a YAML dict") + + return cls.model_validate(data) + + def get_repository_config(self, repo_id: str) -> RepositoryConfig | None: + """ + Get configuration for a specific repository. + + :param repo_id: Repository ID (e.g., "BrentLab/harbison_2004") + :return: RepositoryConfig instance or None if not found + + """ + return self.repositories.get(repo_id) + + def get_property_mappings( + self, repo_id: str, config_name: str + ) -> dict[str, PropertyMapping]: + """ + Get merged property mappings for a repo/dataset combination. + + Merges repo-wide and dataset-specific mappings, with dataset-specific taking + precedence. + + :param repo_id: Repository ID + :param config_name: Dataset/config name + :return: Dict mapping property names to PropertyMapping objects + + """ + repo_config = self.get_repository_config(repo_id) + if not repo_config: + return {} + + # Start with repo-wide properties + mappings: dict[str, PropertyMapping] = dict(repo_config.properties) + + # Override with dataset-specific properties + if repo_config.dataset and config_name in repo_config.dataset: + mappings.update(repo_config.dataset[config_name]) + + return mappings diff --git a/tfbpapi/tests/conftest.py b/tfbpapi/tests/conftest.py index 47b7b94..55c1082 100644 --- a/tfbpapi/tests/conftest.py +++ b/tfbpapi/tests/conftest.py @@ -24,3 +24,1442 @@ def mock_scan_cache_dir(mock_cache_info): """Mock scan_cache_dir to return our pickled cache data.""" with patch("huggingface_hub.scan_cache_dir", return_value=mock_cache_info): yield mock_cache_info + + +# ============================================================================ +# Datainfo Fixtures (merged from tests/datainfo/conftest.py) +# ============================================================================ + + +@pytest.fixture +def sample_dataset_card_data(): + """Sample dataset card data for testing.""" + return { + "license": "mit", + "language": ["en"], + "tags": ["biology", "genomics", "yeast"], + "pretty_name": "Test Genomics Dataset", + "size_categories": ["100K log2(1.7) & " + "pval < 0.05). Note that " + "there is a slight " + "difference when " + "calculating from the data " + "provided here, I believe " + "due to a difference in " + "the way the targets are " + "parsed and filtered (some " + "ORFs that have since been " + "removed from the " + "annotations are removed). " + "I didn't investigate this " + "closely, though.", + "role": "experimental_condition", + }, + { + "name": "profile_first_published", + "dtype": "string", + "description": "citation or reference " + "indicating where this " + "expression profile was " + "first published", + "role": "experimental_condition", + }, + { + "name": "chase_notes", + "dtype": "string", + "description": "notes added during data " + "curation and parsing", + }, + ] + }, + } + ], + } diff --git a/tfbpapi/tests/datainfo/__init__.py b/tfbpapi/tests/datainfo/__init__.py deleted file mode 100644 index e38de55..0000000 --- a/tfbpapi/tests/datainfo/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Tests for the datainfo package.""" diff --git a/tfbpapi/tests/datainfo/conftest.py b/tfbpapi/tests/datainfo/conftest.py deleted file mode 100644 index 2b459ff..0000000 --- a/tfbpapi/tests/datainfo/conftest.py +++ /dev/null @@ -1,1434 +0,0 @@ -"""Shared fixtures and test data for datainfo tests.""" - -from unittest.mock import Mock - -import pytest - - -@pytest.fixture -def sample_dataset_card_data(): - """Sample dataset card data for testing.""" - return { - "license": "mit", - "language": ["en"], - "tags": ["biology", "genomics", "yeast"], - "pretty_name": "Test Genomics Dataset", - "size_categories": ["100K log2(1.7) & " - "pval < 0.05). Note that " - "there is a slight " - "difference when " - "calculating from the data " - "provided here, I believe " - "due to a difference in " - "the way the targets are " - "parsed and filtered (some " - "ORFs that have since been " - "removed from the " - "annotations are removed). " - "I didn't investigate this " - "closely, though.", - "role": "experimental_condition", - }, - { - "name": "profile_first_published", - "dtype": "string", - "description": "citation or reference " - "indicating where this " - "expression profile was " - "first published", - "role": "experimental_condition", - }, - { - "name": "chase_notes", - "dtype": "string", - "description": "notes added during data " - "curation and parsing", - }, - ] - }, - } - ], - } diff --git a/tfbpapi/tests/datainfo/test_datacard.py b/tfbpapi/tests/datainfo/test_datacard.py deleted file mode 100644 index 1e061ee..0000000 --- a/tfbpapi/tests/datainfo/test_datacard.py +++ /dev/null @@ -1,775 +0,0 @@ -"""Tests for the DataCard class.""" - -from unittest.mock import Mock, patch - -import pytest - -from tfbpapi.datainfo import DataCard -from tfbpapi.datainfo.models import DatasetType -from tfbpapi.errors import DataCardError, DataCardValidationError, HfDataFetchError - - -class TestDataCard: - """Test suite for DataCard class.""" - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_init( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - test_token, - ): - """Test DataCard initialization.""" - datacard = DataCard(test_repo_id, token=test_token) - - assert datacard.repo_id == test_repo_id - assert datacard.token == test_token - assert datacard._dataset_card is None - assert datacard._metadata_cache == {} - - # Check that fetchers were initialized - mock_card_fetcher.assert_called_once_with(token=test_token) - mock_structure_fetcher.assert_called_once_with(token=test_token) - mock_size_fetcher.assert_called_once_with(token=test_token) - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_init_without_token( - self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id - ): - """Test DataCard initialization without token.""" - datacard = DataCard(test_repo_id) - - assert datacard.repo_id == test_repo_id - assert datacard.token is None - - # Check that fetchers were initialized without token - mock_card_fetcher.assert_called_once_with(token=None) - mock_structure_fetcher.assert_called_once_with(token=None) - mock_size_fetcher.assert_called_once_with(token=None) - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_load_and_validate_card_success( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - ): - """Test successful card loading and validation.""" - # Setup mock - mock_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_fetcher_instance - mock_fetcher_instance.fetch.return_value = sample_dataset_card_data - - datacard = DataCard(test_repo_id) - - # Access dataset_card property to trigger loading - card = datacard.dataset_card - - assert card is not None - assert len(card.configs) == 4 - assert card.pretty_name == "Test Genomics Dataset" - mock_fetcher_instance.fetch.assert_called_once_with(test_repo_id) - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_load_card_no_data( - self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id - ): - """Test handling when no dataset card is found.""" - mock_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_fetcher_instance - mock_fetcher_instance.fetch.return_value = {} - - datacard = DataCard(test_repo_id) - - with pytest.raises(DataCardValidationError, match="No dataset card found"): - _ = datacard.dataset_card - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_load_card_validation_error( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - invalid_dataset_card_data, - ): - """Test handling of validation errors.""" - mock_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_fetcher_instance - mock_fetcher_instance.fetch.return_value = invalid_dataset_card_data - - datacard = DataCard(test_repo_id) - - with pytest.raises( - DataCardValidationError, match="Dataset card validation failed" - ): - _ = datacard.dataset_card - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_load_card_fetch_error( - self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id - ): - """Test handling of fetch errors.""" - mock_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_fetcher_instance - mock_fetcher_instance.fetch.side_effect = HfDataFetchError("Fetch failed") - - datacard = DataCard(test_repo_id) - - with pytest.raises(DataCardError, match="Failed to fetch dataset card"): - _ = datacard.dataset_card - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_configs_property( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - ): - """Test getting all configurations via property.""" - mock_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_fetcher_instance - mock_fetcher_instance.fetch.return_value = sample_dataset_card_data - - datacard = DataCard(test_repo_id) - configs = datacard.configs - - assert len(configs) == 4 - config_names = [config.config_name for config in configs] - assert "genomic_features" in config_names - assert "binding_data" in config_names - assert "genome_map_data" in config_names - assert "experiment_metadata" in config_names - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_get_config_by_name( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - ): - """Test getting a specific configuration by name.""" - mock_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_fetcher_instance - mock_fetcher_instance.fetch.return_value = sample_dataset_card_data - - datacard = DataCard(test_repo_id) - - config = datacard.get_config("binding_data") - assert config is not None - assert config.config_name == "binding_data" - assert config.dataset_type == DatasetType.ANNOTATED_FEATURES - - # Test non-existent config - assert datacard.get_config("nonexistent") is None - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_get_configs_by_type( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - ): - """Test getting configurations by dataset type.""" - mock_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_fetcher_instance - mock_fetcher_instance.fetch.return_value = sample_dataset_card_data - - datacard = DataCard(test_repo_id) - - # Test with enum - genomic_configs = datacard.get_configs_by_type(DatasetType.GENOMIC_FEATURES) - assert len(genomic_configs) == 1 - assert genomic_configs[0].config_name == "genomic_features" - - # Test with string - metadata_configs = datacard.get_configs_by_type("metadata") - assert len(metadata_configs) == 1 - assert metadata_configs[0].config_name == "experiment_metadata" - - # Test with genome_map type - genome_map_configs = datacard.get_configs_by_type("genome_map") - assert len(genome_map_configs) == 1 - assert genome_map_configs[0].config_name == "genome_map_data" - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_get_field_values_success( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - ): - """Test getting field values for a specific config and field.""" - mock_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_fetcher_instance - mock_fetcher_instance.fetch.return_value = sample_dataset_card_data - - datacard = DataCard(test_repo_id) - - # Test existing field - values = datacard.get_field_values("binding_data", "regulator_symbol") - # Since _extract_field_values returns empty set by default, we expect empty set - assert isinstance(values, set) - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_get_field_values_config_not_found( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - ): - """Test error when config not found.""" - mock_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_fetcher_instance - mock_fetcher_instance.fetch.return_value = sample_dataset_card_data - - datacard = DataCard(test_repo_id) - - with pytest.raises( - DataCardError, match="Configuration 'nonexistent' not found" - ): - datacard.get_field_values("nonexistent", "some_field") - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_get_field_values_field_not_found( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - ): - """Test error when field not found.""" - mock_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_fetcher_instance - mock_fetcher_instance.fetch.return_value = sample_dataset_card_data - - datacard = DataCard(test_repo_id) - - with pytest.raises(DataCardError, match="Field 'nonexistent' not found"): - datacard.get_field_values("binding_data", "nonexistent") - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_get_metadata_relationships( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - ): - """Test getting metadata relationships.""" - mock_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_fetcher_instance - mock_fetcher_instance.fetch.return_value = sample_dataset_card_data - - datacard = DataCard(test_repo_id) - - relationships = datacard.get_metadata_relationships() - - # Should have explicit relationship between binding_data and experiment_metadata - explicit_rels = [r for r in relationships if r.relationship_type == "explicit"] - assert len(explicit_rels) == 1 - assert explicit_rels[0].data_config == "binding_data" - assert explicit_rels[0].metadata_config == "experiment_metadata" - - # Should have embedded relationship for binding_data (has metadata_fields) - embedded_rels = [r for r in relationships if r.relationship_type == "embedded"] - assert len(embedded_rels) == 1 - assert embedded_rels[0].data_config == "binding_data" - assert embedded_rels[0].metadata_config == "binding_data_embedded" - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_get_repository_info_success( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - sample_repo_structure, - ): - """Test getting repository information.""" - mock_card_fetcher_instance = Mock() - mock_structure_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_card_fetcher_instance - mock_structure_fetcher.return_value = mock_structure_fetcher_instance - - mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data - mock_structure_fetcher_instance.fetch.return_value = sample_repo_structure - - datacard = DataCard(test_repo_id) - - info = datacard.get_repository_info() - - assert info["repo_id"] == test_repo_id - assert info["pretty_name"] == "Test Genomics Dataset" - assert info["license"] == "mit" - assert info["num_configs"] == 4 - assert "genomic_features" in info["dataset_types"] - assert "annotated_features" in info["dataset_types"] - assert "genome_map" in info["dataset_types"] - assert "metadata" in info["dataset_types"] - assert info["total_files"] == 5 - assert info["last_modified"] == "2023-12-01T10:30:00Z" - assert info["has_default_config"] is True - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_get_repository_info_fetch_error( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - ): - """Test getting repository info when structure fetch fails.""" - mock_card_fetcher_instance = Mock() - mock_structure_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_card_fetcher_instance - mock_structure_fetcher.return_value = mock_structure_fetcher_instance - - mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data - mock_structure_fetcher_instance.fetch.side_effect = HfDataFetchError( - "Structure fetch failed" - ) - - datacard = DataCard(test_repo_id) - - info = datacard.get_repository_info() - - assert info["repo_id"] == test_repo_id - assert info["total_files"] is None - assert info["last_modified"] is None - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_explore_config( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - ): - """Test exploring a specific configuration.""" - mock_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_fetcher_instance - mock_fetcher_instance.fetch.return_value = sample_dataset_card_data - - datacard = DataCard(test_repo_id) - - # Test regular config - config_info = datacard.explore_config("binding_data") - - assert config_info["config_name"] == "binding_data" - assert config_info["description"] == "Transcription factor binding measurements" - assert config_info["dataset_type"] == "annotated_features" - assert config_info["is_default"] is False - assert config_info["num_features"] == 4 - assert len(config_info["features"]) == 4 - assert config_info["metadata_fields"] == [ - "regulator_symbol", - "experimental_condition", - ] - - # Test config with partitioning - partitioned_config_info = datacard.explore_config("genome_map_data") - assert "partitioning" in partitioned_config_info - assert partitioned_config_info["partitioning"]["enabled"] is True - assert partitioned_config_info["partitioning"]["partition_by"] == [ - "regulator", - "experiment", - ] - - # Test metadata config with applies_to - metadata_config_info = datacard.explore_config("experiment_metadata") - assert metadata_config_info["applies_to"] == ["binding_data"] - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_explore_config_not_found( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - ): - """Test exploring a non-existent configuration.""" - mock_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_fetcher_instance - mock_fetcher_instance.fetch.return_value = sample_dataset_card_data - - datacard = DataCard(test_repo_id) - - with pytest.raises( - DataCardError, match="Configuration 'nonexistent' not found" - ): - datacard.explore_config("nonexistent") - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_summary( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - sample_repo_structure, - ): - """Test getting a summary of the dataset.""" - mock_card_fetcher_instance = Mock() - mock_structure_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_card_fetcher_instance - mock_structure_fetcher.return_value = mock_structure_fetcher_instance - - mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data - mock_structure_fetcher_instance.fetch.return_value = sample_repo_structure - - datacard = DataCard(test_repo_id) - - summary = datacard.summary() - - assert "Dataset: Test Genomics Dataset" in summary - assert f"Repository: {test_repo_id}" in summary - assert "License: mit" in summary - assert "Configurations: 4" in summary - assert "genomic_features" in summary - assert "binding_data" in summary - assert "genome_map_data" in summary - assert "experiment_metadata" in summary - assert "(default)" in summary # genomic_features is marked as default - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_extract_partition_values( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - ): - """Test extracting partition values.""" - mock_card_fetcher_instance = Mock() - mock_structure_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_card_fetcher_instance - mock_structure_fetcher.return_value = mock_structure_fetcher_instance - - mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data - mock_structure_fetcher_instance.get_partition_values.return_value = [ - "TF1", - "TF2", - "TF3", - ] - - datacard = DataCard(test_repo_id) - - # Get the genome_map_data config which has partitioning enabled - config = datacard.get_config("genome_map_data") - assert config is not None - assert config.dataset_info.partitioning.enabled is True - - values = datacard._extract_partition_values(config, "regulator") - assert values == {"TF1", "TF2", "TF3"} - mock_structure_fetcher_instance.get_partition_values.assert_called_once_with( - test_repo_id, "regulator" - ) - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_extract_partition_values_no_partitioning( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - ): - """Test extracting partition values when partitioning is disabled.""" - mock_card_fetcher_instance = Mock() - mock_structure_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_card_fetcher_instance - mock_structure_fetcher.return_value = mock_structure_fetcher_instance - - mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data - - datacard = DataCard(test_repo_id) - - # Get a config without partitioning - config = datacard.get_config("genomic_features") - assert config is not None - assert config.dataset_info.partitioning is None - - values = datacard._extract_partition_values(config, "some_field") - assert values == set() - mock_structure_fetcher_instance.get_partition_values.assert_not_called() - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_extract_partition_values_field_not_in_partitions( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - ): - """Test extracting partition values when field is not a partition column.""" - mock_card_fetcher_instance = Mock() - mock_structure_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_card_fetcher_instance - mock_structure_fetcher.return_value = mock_structure_fetcher_instance - - mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data - - datacard = DataCard(test_repo_id) - - # Get the genome_map_data config which has partitioning enabled - config = datacard.get_config("genome_map_data") - assert config is not None - - # Try to extract values for a field that's not in partition_by - values = datacard._extract_partition_values(config, "not_a_partition_field") - assert values == set() - mock_structure_fetcher_instance.get_partition_values.assert_not_called() - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_extract_partition_values_fetch_error( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - sample_dataset_card_data, - ): - """Test extracting partition values when fetch fails.""" - mock_card_fetcher_instance = Mock() - mock_structure_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_card_fetcher_instance - mock_structure_fetcher.return_value = mock_structure_fetcher_instance - - mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data - mock_structure_fetcher_instance.get_partition_values.side_effect = ( - HfDataFetchError("Fetch failed") - ) - - datacard = DataCard(test_repo_id) - - config = datacard.get_config("genome_map_data") - values = datacard._extract_partition_values(config, "regulator") - - # Should return empty set on error - assert values == set() - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_get_field_attribute( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - ): - """Test extracting specific attributes from field definitions.""" - # Create sample card data with condition definitions - card_data = { - "configs": [ - { - "config_name": "test_config", - "description": "Test configuration", - "dataset_type": "annotated_features", - "data_files": [{"split": "train", "path": "test.parquet"}], - "dataset_info": { - "features": [ - { - "name": "condition", - "dtype": "string", - "description": "Experimental condition", - "role": "experimental_condition", - "definitions": { - "YPD": { - "media": { - "name": "YPD", - "carbon_source": [ - { - "compound": "D-glucose", - "concentration_percent": 2, - } - ], - "nitrogen_source": [ - { - "compound": "yeast_extract", - "concentration_percent": 1, - }, - { - "compound": "peptone", - "concentration_percent": 2, - }, - ], - }, - "temperature_celsius": 30, - }, - "HEAT": { - "media": { - "name": "YPD", - "carbon_source": [ - { - "compound": "D-glucose", - "concentration_percent": 2, - } - ], - }, - "temperature_celsius": 37, - }, - "SM": { - "media": { - "name": "synthetic_complete", - "carbon_source": "unspecified", - "nitrogen_source": "unspecified", - } - }, - }, - } - ] - }, - } - ] - } - - mock_card_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_card_fetcher_instance - mock_card_fetcher_instance.fetch.return_value = card_data - - datacard = DataCard(test_repo_id) - - # Test extracting media attribute - media_specs = datacard.get_field_attribute( - "test_config", "condition", "media" - ) - - assert "YPD" in media_specs - assert "HEAT" in media_specs - assert "SM" in media_specs - - # Check YPD media specification - assert media_specs["YPD"]["name"] == "YPD" - assert len(media_specs["YPD"]["carbon_source"]) == 1 - assert media_specs["YPD"]["carbon_source"][0]["compound"] == "D-glucose" - assert len(media_specs["YPD"]["nitrogen_source"]) == 2 - - # Check HEAT media specification - assert media_specs["HEAT"]["name"] == "YPD" - assert len(media_specs["HEAT"]["carbon_source"]) == 1 - - # Check SM media specification - assert media_specs["SM"]["name"] == "synthetic_complete" - assert media_specs["SM"]["carbon_source"] == "unspecified" - - # Test extracting temperature attribute - temp_specs = datacard.get_field_attribute( - "test_config", "condition", "temperature_celsius" - ) - - assert temp_specs["YPD"] == 30 - assert temp_specs["HEAT"] == 37 - assert temp_specs["SM"] == "unspecified" # SM doesn't have temperature - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_get_field_attribute_invalid_config( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - minimal_dataset_card_data, - ): - """Test get_field_attribute with invalid config name.""" - mock_card_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_card_fetcher_instance - mock_card_fetcher_instance.fetch.return_value = minimal_dataset_card_data - - datacard = DataCard(test_repo_id) - - with pytest.raises(DataCardError, match="Configuration 'invalid' not found"): - datacard.get_field_attribute("invalid", "condition", "media") - - @patch("tfbpapi.datainfo.datacard.HfDataCardFetcher") - @patch("tfbpapi.datainfo.datacard.HfRepoStructureFetcher") - @patch("tfbpapi.datainfo.datacard.HfSizeInfoFetcher") - def test_get_field_attribute_invalid_field( - self, - mock_size_fetcher, - mock_structure_fetcher, - mock_card_fetcher, - test_repo_id, - minimal_dataset_card_data, - ): - """Test get_field_attribute with invalid field name.""" - mock_card_fetcher_instance = Mock() - mock_card_fetcher.return_value = mock_card_fetcher_instance - mock_card_fetcher_instance.fetch.return_value = minimal_dataset_card_data - - datacard = DataCard(test_repo_id) - - with pytest.raises( - DataCardError, match="Field 'invalid_field' not found in config" - ): - datacard.get_field_attribute("test_config", "invalid_field", "media") diff --git a/tfbpapi/tests/datainfo/test_metadata_builder.py b/tfbpapi/tests/datainfo/test_metadata_builder.py deleted file mode 100644 index cee3b71..0000000 --- a/tfbpapi/tests/datainfo/test_metadata_builder.py +++ /dev/null @@ -1,375 +0,0 @@ -""" -Tests for MetadataBuilder. - -Tests normalization logic, metadata extraction, and builder functionality. - -""" - -from pathlib import Path - -import pytest -import yaml - -from tfbpapi.datainfo.metadata_builder import ( - MetadataBuilder, - get_nested_value, - normalize_value, -) - - -class TestGetNestedValue: - """Test get_nested_value function.""" - - def test_simple_path(self): - """Test simple one-level path.""" - data = {"temperature": 30} - assert get_nested_value(data, "temperature") == 30 - - def test_nested_path(self): - """Test multi-level nested path.""" - data = {"media": {"carbon_source": "D-glucose"}} - assert get_nested_value(data, "media.carbon_source") == "D-glucose" - - def test_deeply_nested_path(self): - """Test deeply nested path.""" - data = {"level1": {"level2": {"level3": {"value": 42}}}} - assert get_nested_value(data, "level1.level2.level3.value") == 42 - - def test_missing_key(self): - """Test that missing keys return None.""" - data = {"temperature": 30} - assert get_nested_value(data, "pressure") is None - - def test_missing_intermediate_key(self): - """Test that missing intermediate keys return None.""" - data = {"media": {"temperature": 30}} - assert get_nested_value(data, "media.carbon_source.compound") is None - - def test_non_dict_input(self): - """Test that non-dict input returns None.""" - assert get_nested_value("not a dict", "path") is None - assert get_nested_value(None, "path") is None - - -class TestGetNestedValueListExtraction: - """Test get_nested_value list extraction functionality.""" - - def test_extract_from_list_of_dicts(self): - """Test extracting property from list of dicts.""" - data = { - "media": { - "carbon_source": [ - {"compound": "D-glucose", "concentration_percent": 2}, - {"compound": "D-galactose", "concentration_percent": 1}, - ] - } - } - result = get_nested_value(data, "media.carbon_source.compound") - assert result == ["D-glucose", "D-galactose"] - - def test_extract_concentration_from_list(self): - """Test extracting numeric property from list of dicts.""" - data = { - "media": { - "carbon_source": [ - {"compound": "D-glucose", "concentration_percent": 2}, - {"compound": "D-galactose", "concentration_percent": 1}, - ] - } - } - result = get_nested_value(data, "media.carbon_source.concentration_percent") - assert result == [2, 1] - - def test_get_list_itself(self): - """Test getting the list without extracting a property.""" - data = { - "media": { - "carbon_source": [ - {"compound": "D-glucose"}, - {"compound": "D-galactose"}, - ] - } - } - result = get_nested_value(data, "media.carbon_source") - expected = [ - {"compound": "D-glucose"}, - {"compound": "D-galactose"}, - ] - assert result == expected - - def test_extract_from_single_item_list(self): - """Test extracting from list with single item.""" - data = { - "media": { - "carbon_source": [{"compound": "D-glucose"}] - } - } - result = get_nested_value(data, "media.carbon_source.compound") - assert result == ["D-glucose"] - - def test_extract_missing_property_from_list(self): - """Test extracting non-existent property from list items.""" - data = { - "media": { - "carbon_source": [ - {"compound": "D-glucose"}, - {"compound": "D-galactose"}, - ] - } - } - result = get_nested_value(data, "media.carbon_source.missing_key") - assert result is None - - def test_nested_list_extraction(self): - """Test extracting from nested structures with lists.""" - data = { - "level1": { - "level2": [ - {"level3": {"value": "a"}}, - {"level3": {"value": "b"}}, - ] - } - } - result = get_nested_value(data, "level1.level2.level3.value") - assert result == ["a", "b"] - - -class TestNormalizeValue: - """Test normalize_value function.""" - - def test_normalize_with_alias_match(self): - """Test normalization when value matches an alias.""" - aliases = {"glucose": ["D-glucose", "dextrose"]} - assert normalize_value("D-glucose", aliases) == "glucose" - assert normalize_value("dextrose", aliases) == "glucose" - - def test_normalize_case_insensitive(self): - """Test that matching is case-insensitive.""" - aliases = {"glucose": ["D-glucose"]} - assert normalize_value("d-glucose", aliases) == "glucose" - assert normalize_value("D-GLUCOSE", aliases) == "glucose" - assert normalize_value("D-Glucose", aliases) == "glucose" - - def test_normalize_no_match_passthrough(self): - """Test pass-through when no alias matches.""" - aliases = {"glucose": ["D-glucose"]} - assert normalize_value("maltose", aliases) == "maltose" - assert normalize_value("galactose", aliases) == "galactose" - - def test_normalize_no_aliases(self): - """Test pass-through when no aliases provided.""" - assert normalize_value("D-glucose", None) == "D-glucose" - assert normalize_value("maltose", None) == "maltose" - - def test_normalize_empty_aliases(self): - """Test pass-through when empty aliases dict.""" - assert normalize_value("D-glucose", {}) == "D-glucose" - - def test_normalize_numeric_values(self): - """Test normalization with numeric actual values.""" - aliases = {"thirty": [30, "30"]} - assert normalize_value(30, aliases) == "thirty" - assert normalize_value("30", aliases) == "thirty" - - def test_normalize_numeric_passthrough(self): - """Test that unmatched numeric values pass through as strings.""" - aliases = {"thirty": [30]} - assert normalize_value(37, aliases) == "37" - - def test_normalize_boolean_values(self): - """Test normalization with boolean values.""" - aliases = {"present": [True, "true", "True"]} - assert normalize_value(True, aliases) == "present" - # Note: bool to str conversion makes this "True" - assert normalize_value("true", aliases) == "present" - - def test_normalize_multiple_aliases(self): - """Test with multiple alias mappings.""" - aliases = { - "glucose": ["D-glucose", "dextrose"], - "galactose": ["D-galactose", "Galactose"], - } - assert normalize_value("D-glucose", aliases) == "glucose" - assert normalize_value("DEXTROSE", aliases) == "glucose" - assert normalize_value("d-galactose", aliases) == "galactose" - assert normalize_value("Galactose", aliases) == "galactose" - # No match - assert normalize_value("maltose", aliases) == "maltose" - - -@pytest.fixture -def write_config(tmp_path): - """Fixture to write YAML config files.""" - - def _write(config_data): - config_path = tmp_path / "config.yaml" - with open(config_path, "w") as f: - yaml.dump(config_data, f) - return config_path - - return _write - - -class TestMetadataBuilder: - """Test MetadataBuilder class.""" - - def test_init_with_aliases(self, write_config): - """Test initialization with factor aliases.""" - config = { - "factor_aliases": {"carbon_source": {"glucose": ["D-glucose", "dextrose"]}}, - "BrentLab/test": { - "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} - }, - } - config_file = write_config(config) - builder = MetadataBuilder(config_file) - - assert len(builder.factor_aliases) == 1 - assert "carbon_source" in builder.factor_aliases - assert "glucose" in builder.factor_aliases["carbon_source"] - - def test_init_without_aliases(self, write_config): - """Test initialization without factor aliases.""" - config = { - "BrentLab/test": { - "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} - } - } - config_file = write_config(config) - builder = MetadataBuilder(config_file) - - assert builder.factor_aliases == {} - - def test_invalid_mode(self, write_config): - """Test that invalid mode raises ValueError.""" - config = { - "BrentLab/test": { - "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} - } - } - config_file = write_config(config) - builder = MetadataBuilder(config_file) - - with pytest.raises(ValueError) as exc_info: - builder.build_metadata( - repos=[("BrentLab/test", "test")], mode="invalid_mode" - ) - assert "Invalid mode" in str(exc_info.value) - - def test_build_metadata_missing_repo_config(self, write_config): - """Test handling of missing repository configuration.""" - config = { - "BrentLab/test": { - "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} - } - } - config_file = write_config(config) - builder = MetadataBuilder(config_file) - - results = builder.build_metadata( - repos=[("BrentLab/missing", "dataset")], mode="conditions" - ) - - assert "BrentLab/missing" in results - assert "error" in results["BrentLab/missing"] - assert "No property mappings" in results["BrentLab/missing"]["error"] - - def test_repr(self, write_config): - """Test string representation.""" - config = { - "factor_aliases": { - "carbon_source": {"glucose": ["D-glucose"]}, - "temperature": {"thirty": [30]}, - }, - "BrentLab/test1": { - "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} - }, - "BrentLab/test2": { - "dataset": {"test": {"temperature": {"path": "temperature_celsius"}}} - }, - } - config_file = write_config(config) - builder = MetadataBuilder(config_file) - - repr_str = repr(builder) - assert "MetadataBuilder" in repr_str - assert "2 properties" in repr_str - assert "2 repositories" in repr_str - - def test_repr_no_aliases(self, write_config): - """Test string representation with no aliases.""" - config = { - "BrentLab/test": { - "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} - } - } - config_file = write_config(config) - builder = MetadataBuilder(config_file) - - repr_str = repr(builder) - assert "MetadataBuilder" in repr_str - assert "0 properties" in repr_str - assert "1 repositories" in repr_str - - -class TestMetadataBuilderIntegration: - """Integration tests with real datacards (if available).""" - - def test_build_metadata_conditions_mode(self, write_config): - """Test building metadata in conditions mode.""" - # This is a minimal test that doesn't require actual datacards - # In practice, you'd use real datacards from HuggingFace - config = { - "factor_aliases": {"carbon_source": {"glucose": ["D-glucose"]}}, - "BrentLab/test": { - "dataset": { - "test": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source", - } - } - } - }, - } - config_file = write_config(config) - builder = MetadataBuilder(config_file) - - # This would fail without a real datacard, but tests the structure - # In actual usage, you'd mock DataCard or use real repos - # For now, just verify the method exists and can be called - assert hasattr(builder, "build_metadata") - assert callable(builder.build_metadata) - - def test_get_property_mappings(self, write_config): - """Test _get_property_mappings method.""" - config = { - "BrentLab/test": { - "temperature": {"path": "temperature_celsius"}, - "dataset": { - "test_dataset": {"carbon_source": {"path": "media.carbon_source"}} - }, - } - } - config_file = write_config(config) - builder = MetadataBuilder(config_file) - - mappings = builder._get_property_mappings("BrentLab/test", "test_dataset") - - assert "temperature" in mappings - assert "carbon_source" in mappings - assert mappings["temperature"].path == "temperature_celsius" - assert mappings["carbon_source"].path == "media.carbon_source" - - def test_get_property_mappings_missing_repo(self, write_config): - """Test _get_property_mappings with missing repository.""" - config = { - "BrentLab/test": { - "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} - } - } - config_file = write_config(config) - builder = MetadataBuilder(config_file) - - mappings = builder._get_property_mappings("BrentLab/missing", "dataset") - assert mappings == {} diff --git a/tfbpapi/tests/datainfo/test_metadata_manager.py b/tfbpapi/tests/datainfo/test_metadata_manager.py deleted file mode 100644 index ad03c5a..0000000 --- a/tfbpapi/tests/datainfo/test_metadata_manager.py +++ /dev/null @@ -1,243 +0,0 @@ -"""Tests for MetadataManager.""" - -import pandas as pd -import pytest - -from tfbpapi.datainfo import DataCard, MetadataManager - - -class TestMetadataManagerBasic: - """Basic tests for MetadataManager instantiation and structure.""" - - def test_instantiation(self): - """Test MetadataManager can be instantiated.""" - mgr = MetadataManager() - assert mgr is not None - assert hasattr(mgr, "_conn") - assert hasattr(mgr, "_registered_datasets") - assert hasattr(mgr, "_view_names") - - def test_instantiation_with_cache(self): - """Test MetadataManager with cache enabled.""" - from pathlib import Path - - cache_dir = Path("/tmp/metadata_cache") - mgr = MetadataManager(cache_dir=cache_dir, cache=True) - assert mgr._cache_dir == cache_dir - assert mgr._cache_enabled is True - - def test_get_active_configs_empty(self): - """Test get_active_configs with no registered datasets.""" - mgr = MetadataManager() - configs = mgr.get_active_configs() - assert configs == [] - - def test_get_summary_empty(self): - """Test get_summary with no registered datasets.""" - mgr = MetadataManager() - summary = mgr.get_summary() - assert isinstance(summary, pd.DataFrame) - assert len(summary) == 0 - - -class TestDataCardExtractMetadataSchema: - """Tests for DataCard.extract_metadata_schema method.""" - - def test_extract_metadata_schema_structure(self): - """Test that extract_metadata_schema returns correct structure.""" - # Note: This test uses a mock since we need a real datacard - # In actual testing, we'd use a real HuggingFace dataset - # For now, we just test the structure - - # Mock test - verify the method exists - from tfbpapi.datainfo.datacard import DataCard - - assert hasattr(DataCard, "extract_metadata_schema") - - -class TestMetadataManagerHelpers: - """Tests for MetadataManager helper methods.""" - - def test_sanitize_view_name(self): - """Test view name sanitization.""" - mgr = MetadataManager() - view_name = mgr._sanitize_view_name("BrentLab/dataset-name", "config_name") - assert view_name == "BrentLab_dataset_name_config_name_metadata" - assert " " not in view_name - assert "/" not in view_name - assert "-" not in view_name - - def test_flatten_condition_definition_empty(self): - """Test flattening empty definition.""" - mgr = MetadataManager() - result = mgr._flatten_condition_definition({}) - assert isinstance(result, dict) - assert len(result) == 0 - - def test_flatten_condition_definition_with_media(self): - """Test flattening definition with media name.""" - mgr = MetadataManager() - definition = { - "media": { - "name": "YPD", - } - } - result = mgr._flatten_condition_definition(definition) - assert result["growth_media"] == "YPD" - - -class TestComponentSeparators: - """Tests for separator conventions.""" - - def test_separator_constants(self): - """Test that separator constants are defined.""" - from tfbpapi.datainfo.metadata_manager import COMPONENT_SEPARATORS - - assert COMPONENT_SEPARATORS["type_value"] == ":" - assert COMPONENT_SEPARATORS["value_conc"] == "@" - assert COMPONENT_SEPARATORS["components"] == ";" - assert COMPONENT_SEPARATORS["types"] == "|" - - -class TestThreeLevelConditionHierarchy: - """Tests for three-level experimental condition hierarchy support.""" - - def test_flatten_experimental_conditions_empty(self): - """Test flattening empty ExperimentalConditions.""" - mgr = MetadataManager() - - # Create a simple mock object with no conditions - class MockExpConditions: - strain_background = None - - result = mgr._flatten_experimental_conditions(MockExpConditions()) - assert isinstance(result, dict) - assert len(result) == 0 - - def test_flatten_experimental_conditions_with_temperature(self): - """Test flattening conditions with temperature.""" - mgr = MetadataManager() - - class MockExpConditions: - temperature_celsius = 30 - strain_background = None - - result = mgr._flatten_experimental_conditions(MockExpConditions()) - assert result["temperature_celsius"] == 30 - - def test_flatten_experimental_conditions_with_cultivation_method(self): - """Test flattening conditions with cultivation method.""" - mgr = MetadataManager() - - class MockExpConditions: - temperature_celsius = None - cultivation_method = "chemostat" - strain_background = None - - result = mgr._flatten_experimental_conditions(MockExpConditions()) - assert result["cultivation_method"] == "chemostat" - - def test_flatten_experimental_conditions_with_media(self): - """Test flattening conditions with media information.""" - mgr = MetadataManager() - - class MockMedia: - name = "minimal" - - class MockExpConditions: - media = MockMedia() - strain_background = None - - result = mgr._flatten_experimental_conditions(MockExpConditions()) - assert result["growth_media"] == "minimal" - - def test_flatten_experimental_conditions_with_strain_background(self): - """Test flattening conditions with strain background.""" - mgr = MetadataManager() - - class MockExpConditions: - strain_background = "BY4741" - - result = mgr._flatten_experimental_conditions(MockExpConditions()) - assert result["strain_background"] == "BY4741" - - def test_datacard_extract_schema_includes_top_level_conditions(self): - """Test that extract_metadata_schema includes top_level_conditions.""" - # This test needs to use real datacards with experimental conditions - # For now, we verify the keys exist in the schema - from unittest.mock import MagicMock, Mock - - # Create a mock DataCard with experimental conditions - mock_card = Mock() - mock_card.repo_id = "test/repo" - - # Mock dataset card with experimental conditions - mock_dataset_card = Mock() - mock_dataset_card.experimental_conditions = Mock() # Non-None - mock_card.dataset_card = mock_dataset_card - - # Mock config with no config-level conditions - mock_config = Mock() - mock_config.experimental_conditions = None - mock_config.dataset_info = Mock() - mock_config.dataset_info.features = [] - - mock_card.get_config = Mock(return_value=mock_config) - - # Call the method via the actual DataCard class - from tfbpapi.datainfo.datacard import DataCard - - schema = DataCard.extract_metadata_schema(mock_card, "test_config") - - # Verify top_level_conditions is in schema - assert "top_level_conditions" in schema - assert "config_level_conditions" in schema - - def test_datacard_extract_schema_includes_config_level_conditions(self): - """Test that extract_metadata_schema includes config_level_conditions.""" - from unittest.mock import Mock - - # Create a mock DataCard - mock_card = Mock() - mock_card.repo_id = "test/repo" - - # Mock dataset card with NO repo-level conditions - mock_dataset_card = Mock() - mock_dataset_card.experimental_conditions = None - mock_card.dataset_card = mock_dataset_card - - # Mock config WITH config-level conditions - mock_config = Mock() - mock_config.experimental_conditions = Mock() # Non-None - mock_config.dataset_info = Mock() - mock_config.dataset_info.features = [] - - mock_card.get_config = Mock(return_value=mock_config) - - # Call the method - from tfbpapi.datainfo.datacard import DataCard - - schema = DataCard.extract_metadata_schema(mock_card, "test_config") - - # Verify config_level_conditions is populated - assert schema["config_level_conditions"] is not None - - -# Integration test placeholder -class TestMetadataManagerIntegration: - """Integration tests for MetadataManager (require real HF datasets).""" - - @pytest.mark.skip(reason="Requires real HuggingFace dataset access") - def test_register_real_dataset(self): - """Test registering a real HuggingFace dataset.""" - # This would test with a real dataset like: - # mgr = MetadataManager() - # mgr.register("BrentLab/some_real_dataset") - # assert len(mgr.get_active_configs()) > 0 - pass - - @pytest.mark.skip(reason="Requires real HuggingFace dataset access") - def test_query_across_datasets(self): - """Test querying across multiple datasets.""" - # This would test cross-dataset queries - pass diff --git a/tfbpapi/tests/datainfo/example_datacards.py b/tfbpapi/tests/example_datacards.py similarity index 100% rename from tfbpapi/tests/datainfo/example_datacards.py rename to tfbpapi/tests/example_datacards.py diff --git a/tfbpapi/tests/datainfo/huggingface_collection_datacards.txt b/tfbpapi/tests/huggingface_collection_datacards.txt similarity index 100% rename from tfbpapi/tests/datainfo/huggingface_collection_datacards.txt rename to tfbpapi/tests/huggingface_collection_datacards.txt diff --git a/tfbpapi/tests/test_HfQueryAPI.py b/tfbpapi/tests/test_HfQueryAPI.py deleted file mode 100644 index 02ad783..0000000 --- a/tfbpapi/tests/test_HfQueryAPI.py +++ /dev/null @@ -1,859 +0,0 @@ -"""Comprehensive tests for HfQueryAPI class.""" - -import logging -from unittest.mock import MagicMock, Mock, patch - -import duckdb -import pandas as pd -import pytest - -from tfbpapi.datainfo.models import MetadataRelationship -from tfbpapi.errors import InvalidFilterFieldError -from tfbpapi.HfQueryAPI import HfQueryAPI - - -class TestHfQueryAPIInit: - """Test HfQueryAPI initialization.""" - - def test_init_basic(self): - """Test basic initialization.""" - conn = duckdb.connect(":memory:") - repo_id = "test/repo" - - with patch("tfbpapi.HfQueryAPI.HfCacheManager.__init__", return_value=None): - api = HfQueryAPI(repo_id, duckdb_conn=conn) - # Manually set properties that would be set by parent - api.repo_id = repo_id - api.duckdb_conn = conn - api._duckdb_conn = conn - api.logger = logging.getLogger("HfQueryAPI") - - assert api.repo_id == repo_id - assert api.duckdb_conn == conn - assert api._duckdb_conn == conn - - def test_init_with_all_params(self): - """Test initialization with all parameters.""" - import tempfile - - conn = duckdb.connect(":memory:") - repo_id = "test/repo" - token = "test_token" - - with tempfile.TemporaryDirectory() as cache_dir: - with patch("tfbpapi.HfQueryAPI.HfCacheManager.__init__", return_value=None): - api = HfQueryAPI( - repo_id=repo_id, - repo_type="model", - token=token, - cache_dir=cache_dir, - duckdb_conn=conn, - ) - # Manually set properties that would be set by parent - api.repo_id = repo_id - api.duckdb_conn = conn - api._duckdb_conn = conn - - assert api.repo_id == repo_id - assert api.duckdb_conn == conn - assert api._duckdb_conn == conn - assert api.repo_type == "model" - - -class TestHfQueryAPIHelpers: - """Test HfQueryAPI helper methods.""" - - @pytest.fixture - def mock_api(self): - """Create a mock HfQueryAPI instance.""" - conn = duckdb.connect(":memory:") - with patch("tfbpapi.HfQueryAPI.HfCacheManager.__init__", return_value=None): - api = HfQueryAPI("test/repo", duckdb_conn=conn) - api.duckdb_conn = conn - api._duckdb_conn = conn - api.logger = logging.getLogger("test") - return api - - def test_get_explicit_metadata(self, mock_api): - """Test _get_explicit_metadata helper.""" - # Mock config and return value - mock_config = Mock() - mock_config.config_name = "test_config" - - # Create mock DataFrame - expected_df = pd.DataFrame({"field1": [1, 2], "field2": ["a", "b"]}) - - # Replace the duckdb_conn with a mock - mock_conn = Mock() - mock_result = Mock() - mock_result.fetchdf.return_value = expected_df - mock_conn.execute.return_value = mock_result - mock_api.duckdb_conn = mock_conn - - result = mock_api._get_explicit_metadata(mock_config, "test_table") - - # Verify SQL was executed correctly - mock_conn.execute.assert_called_once_with("SELECT * FROM test_table") - pd.testing.assert_frame_equal(result, expected_df) - - def test_get_embedded_metadata(self, mock_api): - """Test _get_embedded_metadata helper.""" - # Mock config with metadata fields - mock_config = Mock() - mock_config.config_name = "test_config" - mock_config.metadata_fields = ["time", "mechanism"] - - expected_df = pd.DataFrame( - {"time": [15, 30], "mechanism": ["ZEV", "ZREV"], "count": [100, 50]} - ) - - # Replace the duckdb_conn with a mock - mock_conn = Mock() - mock_result = Mock() - mock_result.fetchdf.return_value = expected_df - mock_conn.execute.return_value = mock_result - mock_api.duckdb_conn = mock_conn - - result = mock_api._get_embedded_metadata(mock_config, "test_table") - - # Verify correct SQL was generated - expected_sql = """ - SELECT DISTINCT time, mechanism, COUNT(*) as count - FROM test_table - WHERE time IS NOT NULL AND mechanism IS NOT NULL - GROUP BY time, mechanism - ORDER BY count DESC - """ - mock_conn.execute.assert_called_once() - actual_sql = mock_conn.execute.call_args[0][0] - # Normalize whitespace for comparison - assert " ".join(actual_sql.split()) == " ".join(expected_sql.split()) - pd.testing.assert_frame_equal(result, expected_df) - - def test_get_embedded_metadata_no_fields(self, mock_api): - """Test _get_embedded_metadata with no metadata fields.""" - mock_config = Mock() - mock_config.config_name = "test_config" - mock_config.metadata_fields = None - - with pytest.raises(ValueError, match="has no metadata fields"): - mock_api._get_embedded_metadata(mock_config, "test_table") - - def test_extract_fields_from_sql_simple(self, mock_api): - """Test _extract_fields_from_sql with simple SQL.""" - sql = "time = 15 AND mechanism = 'ZEV'" - fields = mock_api._extract_fields_from_sql(sql) - - assert "time" in fields - assert "mechanism" in fields - # Note: The current regex may pick up quoted strings as identifiers - # This is a limitation we accept for simplicity - assert "15" not in fields # Should not include numeric literals - - def test_extract_fields_from_sql_complex(self, mock_api): - """Test _extract_fields_from_sql with complex SQL.""" - sql = "field1 IN (1, 2, 3) AND field2 IS NOT NULL AND field3 LIKE '%test%'" - fields = mock_api._extract_fields_from_sql(sql) - - assert "field1" in fields - assert "field2" in fields - assert "field3" in fields - assert "NULL" not in fields # SQL keyword should be excluded - assert "LIKE" not in fields # SQL keyword should be excluded - - def test_extract_fields_from_sql_quoted(self, mock_api): - """Test _extract_fields_from_sql with quoted identifiers.""" - sql = "\"quoted_field\" = 1 AND 'another_field' > 5" - fields = mock_api._extract_fields_from_sql(sql) - - assert "quoted_field" in fields - assert "another_field" in fields - - def test_validate_metadata_fields_success(self, mock_api): - """Test _validate_metadata_fields with valid fields.""" - # Mock get_metadata to return DataFrame with expected columns - metadata_df = pd.DataFrame( - {"time": [15, 30], "mechanism": ["ZEV", "ZREV"], "restriction": ["P", "A"]} - ) - - with patch.object(mock_api, "get_metadata", return_value=metadata_df): - # Should not raise any exception - mock_api._validate_metadata_fields("test_config", ["time", "mechanism"]) - - def test_validate_metadata_fields_invalid(self, mock_api): - """Test _validate_metadata_fields with invalid fields.""" - metadata_df = pd.DataFrame({"time": [15, 30], "mechanism": ["ZEV", "ZREV"]}) - - with patch.object(mock_api, "get_metadata", return_value=metadata_df): - with pytest.raises(InvalidFilterFieldError) as exc_info: - mock_api._validate_metadata_fields( - "test_config", ["invalid_field", "time"] - ) - - error = exc_info.value - assert "invalid_field" in error.invalid_fields - assert "time" not in error.invalid_fields - assert "time" in error.available_fields - assert error.config_name == "test_config" - - def test_validate_metadata_fields_empty_metadata(self, mock_api): - """Test _validate_metadata_fields with empty metadata.""" - empty_df = pd.DataFrame() - - with patch.object(mock_api, "get_metadata", return_value=empty_df): - with pytest.raises(InvalidFilterFieldError) as exc_info: - mock_api._validate_metadata_fields("test_config", ["any_field"]) - - error = exc_info.value - assert error.invalid_fields == ["any_field"] - assert error.available_fields == [] - - def test_validate_metadata_fields_empty_list(self, mock_api): - """Test _validate_metadata_fields with empty field list.""" - # Should not call get_metadata or raise any exception - with patch.object(mock_api, "get_metadata") as mock_get_metadata: - mock_api._validate_metadata_fields("test_config", []) - mock_get_metadata.assert_not_called() - - -class TestHfQueryAPIMainMethods: - """Test HfQueryAPI main methods.""" - - @pytest.fixture - def mock_api(self): - """Create a mock HfQueryAPI instance.""" - conn = duckdb.connect(":memory:") - with patch("tfbpapi.HfQueryAPI.HfCacheManager.__init__", return_value=None): - api = HfQueryAPI("test/repo", duckdb_conn=conn) - # Set up all necessary attributes - api.repo_id = "test/repo" - api.duckdb_conn = conn - api._duckdb_conn = conn - api.logger = logging.getLogger("test") - api._table_filters = {} - - # Set up the internal dataset card attribute - api._dataset_card = Mock() - return api - - def test_get_metadata_explicit_config(self, mock_api): - """Test get_metadata with explicit metadata config.""" - # Setup mock configurations - explicit_config = Mock() - explicit_config.config_name = "metadata_config" - explicit_config.applies_to = None # This config doesn't apply to others - - # Mock the metadata relationship - relationship = MetadataRelationship( - data_config="some_data", - metadata_config="metadata_config", - relationship_type="explicit", - ) - - # Mock the config loading and table setup - mock_config_result = {"success": True, "table_name": "test_metadata_table"} - - expected_df = pd.DataFrame( - {"sample_id": ["sample1", "sample2"], "condition": ["ctrl", "treatment"]} - ) - - with ( - patch.object( - mock_api, "get_metadata_relationships", return_value=[relationship] - ), - patch.object(mock_api, "get_config", return_value=explicit_config), - patch.object( - mock_api, "_get_metadata_for_config", return_value=mock_config_result - ), - patch.object(mock_api, "_get_explicit_metadata", return_value=expected_df), - ): - - result = mock_api.get_metadata("metadata_config") - - mock_api._get_explicit_metadata.assert_called_once_with( - explicit_config, "test_metadata_table" - ) - pd.testing.assert_frame_equal(result, expected_df) - - def test_get_metadata_embedded_config(self, mock_api): - """Test get_metadata with embedded metadata config.""" - # Setup mock configurations - embedded_config = Mock() - embedded_config.config_name = "data_config" - embedded_config.metadata_fields = ["time", "mechanism"] - - # Mock the metadata relationship - relationship = MetadataRelationship( - data_config="data_config", - metadata_config="data_config_embedded", - relationship_type="embedded", - ) - mock_config_result = {"success": True, "table_name": "test_data_table"} - - expected_df = pd.DataFrame( - {"time": [15, 30], "mechanism": ["ZEV", "ZREV"], "count": [100, 50]} - ) - - with ( - patch.object( - mock_api, "get_metadata_relationships", return_value=[relationship] - ), - patch.object(mock_api, "get_config", return_value=embedded_config), - patch.object( - mock_api, "_get_metadata_for_config", return_value=mock_config_result - ), - patch.object(mock_api, "_get_embedded_metadata", return_value=expected_df), - ): - - result = mock_api.get_metadata("data_config") - - mock_api._get_embedded_metadata.assert_called_once_with( - embedded_config, "test_data_table" - ) - pd.testing.assert_frame_equal(result, expected_df) - - def test_get_metadata_applied_config(self, mock_api): - """Test get_metadata with config that has metadata applied to it.""" - # Setup a metadata config that applies to another config - metadata_config = Mock() - metadata_config.config_name = "experiment_metadata" - metadata_config.applies_to = ["data_config", "other_data_config"] - - # Mock the metadata relationship - relationship = MetadataRelationship( - data_config="data_config", - metadata_config="experiment_metadata", - relationship_type="explicit", - ) - mock_config_result = {"success": True, "table_name": "test_metadata_table"} - - expected_df = pd.DataFrame( - {"experiment_id": ["exp1", "exp2"], "condition": ["ctrl", "treatment"]} - ) - - with ( - patch.object( - mock_api, "get_metadata_relationships", return_value=[relationship] - ), - patch.object(mock_api, "get_config", return_value=metadata_config), - patch.object( - mock_api, "_get_metadata_for_config", return_value=mock_config_result - ), - patch.object(mock_api, "_get_explicit_metadata", return_value=expected_df), - ): - - # Request metadata for a config that appears in applies_to - result = mock_api.get_metadata("data_config") - - # Should return the metadata from the config that applies to it - mock_api._get_explicit_metadata.assert_called_once_with( - metadata_config, "test_metadata_table" - ) - pd.testing.assert_frame_equal(result, expected_df) - - def test_get_metadata_config_not_found(self, mock_api): - """Test get_metadata with non-existent config when other configs exist.""" - # Setup a relationship for a different config - relationship = MetadataRelationship( - data_config="other_data", - metadata_config="other_config", - relationship_type="explicit", - ) - with patch.object( - mock_api, "get_metadata_relationships", return_value=[relationship] - ): - with pytest.raises(ValueError, match="Config 'nonexistent' not found"): - mock_api.get_metadata("nonexistent") - - def test_get_metadata_no_metadata_sources(self, mock_api): - """Test get_metadata when no metadata sources are available.""" - with patch.object(mock_api, "get_metadata_relationships", return_value=[]): - result = mock_api.get_metadata("any_config") - assert result.empty - - def test_get_metadata_load_failure(self, mock_api): - """Test get_metadata when config loading fails.""" - config = Mock() - config.config_name = "test_config" - config.applies_to = None - - # Mock the metadata relationship - relationship = MetadataRelationship( - data_config="some_data", - metadata_config="test_config", - relationship_type="explicit", - ) - mock_config_result = {"success": False} - - with ( - patch.object( - mock_api, "get_metadata_relationships", return_value=[relationship] - ), - patch.object(mock_api, "get_config", return_value=config), - patch.object( - mock_api, "_get_metadata_for_config", return_value=mock_config_result - ), - ): - with pytest.raises(RuntimeError, match="Failed to load data for config"): - mock_api.get_metadata("test_config") - - def test_set_filter_valid_fields(self, mock_api): - """Test set_filter with valid field names.""" - with patch.object(mock_api, "_validate_metadata_fields") as mock_validate: - mock_api.set_filter("test_config", time=15, mechanism="ZEV") - - mock_validate.assert_called_once_with("test_config", ["time", "mechanism"]) - assert ( - mock_api._table_filters["test_config"] - == "time = 15 AND mechanism = 'ZEV'" - ) - - def test_set_filter_clear_on_empty(self, mock_api): - """Test set_filter clears filter when no kwargs provided.""" - # Set an initial filter - mock_api._table_filters["test_config"] = "existing_filter" - - with patch.object(mock_api, "clear_filter") as mock_clear: - mock_api.set_filter("test_config") - mock_clear.assert_called_once_with("test_config") - - def test_set_filter_various_types(self, mock_api): - """Test set_filter with different value types.""" - with patch.object(mock_api, "_validate_metadata_fields"): - mock_api.set_filter( - "test_config", - string_field="text", - numeric_field=42, - null_field=None, - bool_field=True, - ) - - expected = ( - "string_field = 'text' AND numeric_field = 42 AND " - "null_field IS NULL AND bool_field = True" - ) - assert mock_api._table_filters["test_config"] == expected - - def test_set_sql_filter_with_validation(self, mock_api): - """Test set_sql_filter with field validation enabled.""" - sql_where = "time IN (15, 30) AND mechanism = 'ZEV'" - - with ( - patch.object( - mock_api, "_extract_fields_from_sql", return_value=["time", "mechanism"] - ) as mock_extract, - patch.object(mock_api, "_validate_metadata_fields") as mock_validate, - ): - - mock_api.set_sql_filter("test_config", sql_where) - - mock_extract.assert_called_once_with(sql_where) - mock_validate.assert_called_once_with("test_config", ["time", "mechanism"]) - assert mock_api._table_filters["test_config"] == sql_where - - def test_set_sql_filter_without_validation(self, mock_api): - """Test set_sql_filter with field validation disabled.""" - sql_where = "complex_function(field1, field2) > 0" - - with ( - patch.object(mock_api, "_extract_fields_from_sql") as mock_extract, - patch.object(mock_api, "_validate_metadata_fields") as mock_validate, - ): - - mock_api.set_sql_filter("test_config", sql_where, validate_fields=False) - - mock_extract.assert_not_called() - mock_validate.assert_not_called() - assert mock_api._table_filters["test_config"] == sql_where - - def test_set_sql_filter_clear_on_empty(self, mock_api): - """Test set_sql_filter clears filter when empty SQL provided.""" - mock_api._table_filters["test_config"] = "existing_filter" - - with patch.object(mock_api, "clear_filter") as mock_clear: - mock_api.set_sql_filter("test_config", "") - mock_clear.assert_called_once_with("test_config") - - def test_clear_filter(self, mock_api): - """Test clear_filter removes stored filter.""" - mock_api._table_filters["test_config"] = "some_filter" - - mock_api.clear_filter("test_config") - - assert "test_config" not in mock_api._table_filters - - def test_clear_filter_nonexistent(self, mock_api): - """Test clear_filter with non-existent config.""" - # Should not raise an error - mock_api.clear_filter("nonexistent_config") - - def test_get_current_filter_exists(self, mock_api): - """Test get_current_filter returns existing filter.""" - expected_filter = "time = 15" - mock_api._table_filters["test_config"] = expected_filter - - result = mock_api.get_current_filter("test_config") - assert result == expected_filter - - def test_get_current_filter_not_exists(self, mock_api): - """Test get_current_filter returns None for non-existent filter.""" - result = mock_api.get_current_filter("nonexistent_config") - assert result is None - - -class TestHfQueryAPIErrorHandling: - """Test HfQueryAPI error handling and edge cases.""" - - @pytest.fixture - def mock_api(self): - """Create a mock HfQueryAPI instance.""" - conn = duckdb.connect(":memory:") - with patch("tfbpapi.HfQueryAPI.HfCacheManager.__init__", return_value=None): - api = HfQueryAPI("test/repo", duckdb_conn=conn) - # Set up all necessary attributes - api.repo_id = "test/repo" - api.duckdb_conn = conn - api._duckdb_conn = conn - api.logger = logging.getLogger("test") - api._table_filters = {} - - # Set up the internal dataset card attribute - api._dataset_card = Mock() - return api - - def test_set_filter_validation_error_propagates(self, mock_api): - """Test that InvalidFilterFieldError from validation propagates.""" - error = InvalidFilterFieldError( - config_name="test_config", - invalid_fields=["invalid_field"], - available_fields=["valid_field"], - ) - - with patch.object(mock_api, "_validate_metadata_fields", side_effect=error): - with pytest.raises(InvalidFilterFieldError) as exc_info: - mock_api.set_filter("test_config", invalid_field="value") - - assert exc_info.value.config_name == "test_config" - assert "invalid_field" in exc_info.value.invalid_fields - - def test_set_sql_filter_validation_error_propagates(self, mock_api): - """Test that InvalidFilterFieldError from SQL validation propagates.""" - error = InvalidFilterFieldError( - config_name="test_config", - invalid_fields=["nonexistent"], - available_fields=["time", "mechanism"], - ) - - with ( - patch.object( - mock_api, "_extract_fields_from_sql", return_value=["nonexistent"] - ), - patch.object(mock_api, "_validate_metadata_fields", side_effect=error), - ): - - with pytest.raises(InvalidFilterFieldError) as exc_info: - mock_api.set_sql_filter("test_config", "nonexistent = 1") - - assert exc_info.value.config_name == "test_config" - - def test_get_metadata_query_error_propagates(self, mock_api): - """Test that query errors in get_metadata propagate.""" - config = Mock() - config.config_name = "test_config" - config.applies_to = None - - # Mock the metadata relationship - relationship = MetadataRelationship( - data_config="some_data", - metadata_config="test_config", - relationship_type="explicit", - ) - - mock_config_result = {"success": True, "table_name": "test_table"} - - with ( - patch.object( - mock_api, "get_metadata_relationships", return_value=[relationship] - ), - patch.object(mock_api, "get_config", return_value=config), - patch.object( - mock_api, "_get_metadata_for_config", return_value=mock_config_result - ), - patch.object( - mock_api, - "_get_explicit_metadata", - side_effect=Exception("Query failed"), - ), - ): - - with pytest.raises(Exception, match="Query failed"): - mock_api.get_metadata("test_config") - - def test_validate_metadata_fields_get_metadata_error_logged(self, mock_api): - """Test that non-InvalidFilterFieldError exceptions in validation are logged.""" - with ( - patch.object( - mock_api, "get_metadata", side_effect=Exception("Network error") - ), - patch.object(mock_api.logger, "warning") as mock_warning, - ): - - # Should not raise, but should log warning - mock_api._validate_metadata_fields("test_config", ["field1"]) - - mock_warning.assert_called_once() - assert "Could not validate filter fields" in mock_warning.call_args[0][0] - - def test_extract_fields_edge_cases(self, mock_api): - """Test _extract_fields_from_sql with various edge cases.""" - # Empty string - assert mock_api._extract_fields_from_sql("") == [] - - # Only SQL keywords - fields = mock_api._extract_fields_from_sql("AND OR NOT NULL") - assert len(fields) == 0 - - # Mixed quotes and operators - fields = mock_api._extract_fields_from_sql( - '"field1" >= "field2" AND field3 <= field4' - ) - assert "field1" in fields - assert "field2" in fields - assert "field3" in fields - assert "field4" in fields - - # Function calls should be excluded - fields = mock_api._extract_fields_from_sql( - "UPPER(field1) = 'VALUE' AND field2 > MAX(field3)" - ) - assert "field1" in fields - assert "field2" in fields - assert "field3" in fields - assert "UPPER" not in fields - assert "MAX" not in fields - - # Numeric literals should be excluded - fields = mock_api._extract_fields_from_sql("field1 = 123.45 AND field2 = -67") - assert "field1" in fields - assert "field2" in fields - assert "123" not in fields - assert "45" not in fields - assert "67" not in fields - - def test_extract_fields_complex_sql(self, mock_api): - """Test _extract_fields_from_sql with complex SQL patterns.""" - complex_sql = """ - (field1 IN (1, 2, 3) OR field2 IS NOT NULL) - AND field3 LIKE '%pattern%' - AND "quoted field" BETWEEN 'start' AND 'end' - AND field4 > COALESCE(field5, 0) - """ - - fields = mock_api._extract_fields_from_sql(complex_sql) - - expected_fields = [ - "field1", - "field2", - "field3", - "quoted field", - "field4", - "field5", - ] - for field in expected_fields: - assert field in fields, f"Field '{field}' should be extracted from SQL" - - # These should not be extracted (SQL keywords and function names) - unwanted = ["COALESCE", "LIKE", "BETWEEN", "NULL"] - for unwanted_item in unwanted: - assert ( - unwanted_item not in fields - ), f"'{unwanted_item}' should not be extracted" - - # String literals should not be extracted - string_literals = ["start", "end", "pattern"] - for literal in string_literals: - assert ( - literal not in fields - ), f"String literal '{literal}' should not be extracted" - - def test_extract_fields_in_clause_with_quoted_values(self, mock_api): - """Test _extract_fields_from_sql with IN clause containing quoted values.""" - # This is the exact pattern from the user's error case - gene_ids = ["YNL199C", "YDL106C", "YLR098C", "YNR009W", "YLR176C"] - regulator_clause = "(" + ", ".join(f"'{gene_id}'" for gene_id in gene_ids) + ")" - - sql = f""" - time = 15 - AND mechanism = 'ZEV' - AND restriction = 'P' - AND regulator_locus_tag IN {regulator_clause} - """ - - fields = mock_api._extract_fields_from_sql(sql) - - # Should extract field names - expected_fields = ["time", "mechanism", "restriction", "regulator_locus_tag"] - for field in expected_fields: - assert field in fields, f"Field '{field}' should be extracted from SQL" - - # Should NOT extract string literals or gene IDs - unwanted_values = ["ZEV", "P"] + gene_ids - for value in unwanted_values: - assert ( - value not in fields - ), f"String literal '{value}' should not be extracted as field" - - # Should NOT extract numeric literals - assert "15" not in fields - - def test_extract_fields_various_comparison_operators(self, mock_api): - """Test _extract_fields_from_sql with various comparison operators and string - values.""" - sql = """ - field1 = 'value1' AND field2 != 'value2' - AND field3 <> 'value3' AND field4 > 'value4' - AND field5 <= 'value5' AND field6 >= 'value6' - AND field7 LIKE 'pattern%' AND field8 NOT LIKE '%other%' - """ - - fields = mock_api._extract_fields_from_sql(sql) - - # Should extract field names - expected_fields = [ - "field1", - "field2", - "field3", - "field4", - "field5", - "field6", - "field7", - "field8", - ] - for field in expected_fields: - assert field in fields, f"Field '{field}' should be extracted from SQL" - - # Should NOT extract string values - unwanted_values = [ - "value1", - "value2", - "value3", - "value4", - "value5", - "value6", - "pattern", - "other", - ] - for value in unwanted_values: - assert ( - value not in fields - ), f"String literal '{value}' should not be extracted as field" - - def test_extract_fields_between_clause(self, mock_api): - """Test _extract_fields_from_sql with BETWEEN clause containing string - values.""" - sql = ( - "field1 BETWEEN 'start_value' AND 'end_value' AND field2 BETWEEN 10 AND 20" - ) - - fields = mock_api._extract_fields_from_sql(sql) - - # Should extract field names - assert "field1" in fields - assert "field2" in fields - - # Should NOT extract BETWEEN values - assert "start_value" not in fields - assert "end_value" not in fields - assert "10" not in fields - assert "20" not in fields - - def test_get_metadata_table_name_missing(self, mock_api): - """Test get_metadata when table_name is missing from config result.""" - config = Mock() - config.config_name = "test_config" - config.applies_to = None - - # Mock the metadata relationship - relationship = MetadataRelationship( - data_config="some_data", - metadata_config="test_config", - relationship_type="explicit", - ) - # Success but no table name - mock_config_result = {"success": True, "table_name": None} - - with ( - patch.object( - mock_api, "get_metadata_relationships", return_value=[relationship] - ), - patch.object(mock_api, "get_config", return_value=config), - patch.object( - mock_api, "_get_metadata_for_config", return_value=mock_config_result - ), - ): - with pytest.raises(RuntimeError, match="No table name for config"): - mock_api.get_metadata("test_config") - - def test_filter_methods_whitespace_handling(self, mock_api): - """Test that filter methods handle whitespace correctly.""" - # set_sql_filter should strip whitespace - with ( - patch.object(mock_api, "_extract_fields_from_sql", return_value=[]), - patch.object(mock_api, "_validate_metadata_fields"), - ): - - mock_api.set_sql_filter("test_config", " field = 1 ") - assert mock_api._table_filters["test_config"] == "field = 1" - - # Empty/whitespace-only SQL should clear filter - with patch.object(mock_api, "clear_filter") as mock_clear: - mock_api.set_sql_filter("test_config", " ") - mock_clear.assert_called_once_with("test_config") - - -class TestInvalidFilterFieldError: - """Test the InvalidFilterFieldError exception.""" - - def test_error_message_formatting(self): - """Test that error message is formatted correctly.""" - error = InvalidFilterFieldError( - config_name="test_config", - invalid_fields=["field1", "field2"], - available_fields=["valid1", "valid2", "valid3"], - ) - - message = str(error) - assert "test_config" in message - assert "'field1'" in message - assert "'field2'" in message - assert "Available fields:" in message - assert "valid1" in message - - def test_error_with_no_available_fields(self): - """Test error message when no fields are available.""" - error = InvalidFilterFieldError( - config_name="empty_config", - invalid_fields=["any_field"], - available_fields=[], - ) - - message = str(error) - assert "No fields available" in message - - def test_error_attributes(self): - """Test that error attributes are set correctly.""" - invalid_fields = ["bad1", "bad2"] - available_fields = ["good1", "good2"] - - error = InvalidFilterFieldError( - config_name="test_config", - invalid_fields=invalid_fields, - available_fields=available_fields, - ) - - assert error.config_name == "test_config" - assert error.invalid_fields == invalid_fields - assert error.available_fields == sorted(available_fields) - assert error.details["config_name"] == "test_config" - assert error.details["invalid_fields"] == invalid_fields - assert error.details["available_fields"] == sorted(available_fields) diff --git a/tfbpapi/tests/test_HfRankResponse.py b/tfbpapi/tests/test_HfRankResponse.py deleted file mode 100644 index 922af97..0000000 --- a/tfbpapi/tests/test_HfRankResponse.py +++ /dev/null @@ -1,505 +0,0 @@ -import tempfile -from pathlib import Path -from unittest.mock import MagicMock, patch - -import pandas as pd -import pytest - -from tfbpapi.HfRankResponse import HfRankResponse -from tfbpapi.IncrementalAnalysisDB import IncrementalAnalysisDB - - -@pytest.fixture -def temp_db_path(): - """Create temporary database path for testing.""" - with tempfile.TemporaryDirectory() as temp_dir: - db_path = Path(temp_dir) / "test.db" - yield str(db_path) - - -@pytest.fixture -def analysis_db(temp_db_path): - """Create IncrementalAnalysisDB instance for testing.""" - return IncrementalAnalysisDB(temp_db_path) - - -@pytest.fixture -def rank_response(analysis_db): - """Create HfRankResponse instance for testing.""" - return HfRankResponse(analysis_db) - - -@pytest.fixture -def mock_ranking_api(): - """Create mock ranking API.""" - api = MagicMock() - api.get_table_filter.return_value = None - api.query.return_value = pd.DataFrame( - { - "regulator_locus_tag": ["REG1", "REG2", "REG3"], - } - ) - api._duckdb_conn.execute.return_value.fetchdf.return_value = pd.DataFrame( - { - "regulator_locus_tag": ["REG1", "REG1", "REG2", "REG2", "REG3"], - "target_locus_tag": ["TGT1", "TGT2", "TGT1", "TGT3", "TGT1"], - "binding_score": [10.5, 8.2, 9.1, 7.8, 6.5], - } - ) - return api - - -@pytest.fixture -def mock_response_api(): - """Create mock response API.""" - api = MagicMock() - api.get_table_filter.return_value = None - api._duckdb_conn.execute.return_value.fetchdf.return_value = pd.DataFrame( - { - "regulator_locus_tag": ["REG1", "REG1", "REG2"], - "target_locus_tag": ["TGT1", "TGT2", "TGT1"], - "log2fc": [2.1, -1.5, 1.8], - } - ) - return api - - -@pytest.fixture -def sample_computed_data(): - """Create sample computed rank response data.""" - return pd.DataFrame( - { - "regulator_locus_tag": ["REG1", "REG1", "REG1", "REG2", "REG2", "REG3"], - "target_locus_tag": ["TGT1", "TGT2", "TGT3", "TGT1", "TGT2", "TGT1"], - "binding_score": [10.5, 8.2, 7.1, 9.1, 7.8, 6.5], - "log2fc": [2.1, -1.5, None, 1.8, None, None], - "responsive": [1, 1, 0, 1, 0, 0], - "bin_label": [5, 5, 10, 5, 10, 5], - "cumulative_responsive": [2, 2, 2, 1, 1, 0], - } - ) - - -class TestHfRankResponse: - - def test_init(self, analysis_db): - """Test HfRankResponse initialization.""" - rr = HfRankResponse(analysis_db) - assert rr.db == analysis_db - assert rr.logger is not None - - def test_get_comparisons_empty(self, rank_response): - """Test getting comparisons when none exist.""" - comparisons = rank_response.get_comparisons() - assert comparisons == [] - - def test_get_comparisons_with_data(self, rank_response, sample_computed_data): - """Test getting comparisons with existing data.""" - # Add some test data - rank_response.db.append_results( - sample_computed_data, "rank_response_test_comparison_1" - ) - rank_response.db.append_results( - sample_computed_data, "rank_response_test_comparison_2" - ) - - comparisons = rank_response.get_comparisons() - assert "test_comparison_1" in comparisons - assert "test_comparison_2" in comparisons - assert len(comparisons) == 2 - - @patch("tfbpapi.HfRankResponse.duckdb.connect") - def test_compute_new_comparison( - self, - mock_duckdb_connect, - rank_response, - mock_ranking_api, - mock_response_api, - sample_computed_data, - ): - """Test computing a new comparison.""" - # Mock the temporary connection - mock_temp_conn = MagicMock() - mock_duckdb_connect.return_value = mock_temp_conn - mock_temp_conn.execute.return_value.fetchdf.return_value = sample_computed_data - - result = rank_response.compute( - ranking_api=mock_ranking_api, - response_api=mock_response_api, - ranking_table="binding_data", - response_table="expression_data", - ranking_score_column="binding_score", - response_column="log2fc", - comparison_id="test_comp", - bin_size=5, - ) - - # Verify APIs were called correctly - mock_ranking_api._ensure_dataset_loaded.assert_called() - mock_response_api._ensure_dataset_loaded.assert_called() - - # Verify data was stored - assert rank_response.db.table_exists("rank_response_test_comp") - - # Verify result - assert not result.empty - assert "regulator_locus_tag" in result.columns - - def test_compute_auto_generated_comparison_id( - self, rank_response, mock_ranking_api, mock_response_api - ): - """Test compute with auto-generated comparison ID.""" - with patch("tfbpapi.HfRankResponse.duckdb.connect") as mock_duckdb: - mock_temp_conn = MagicMock() - mock_duckdb.return_value = mock_temp_conn - mock_temp_conn.execute.return_value.fetchdf.return_value = pd.DataFrame( - { - "regulator_locus_tag": ["REG1"], - "target_locus_tag": ["TGT1"], - "binding_score": [10.0], - "log2fc": [2.0], - "responsive": [1], - "bin_label": [5], - "cumulative_responsive": [1], - } - ) - - rank_response.compute( - ranking_api=mock_ranking_api, - response_api=mock_response_api, - ranking_table="binding_table", - response_table="expression_table", - ranking_score_column="binding_score", - response_column="log2fc", - ) - - # Should create table with auto-generated ID - assert rank_response.db.table_exists( - "rank_response_binding_table_vs_expression_table" - ) - - def test_compute_incremental_update( - self, rank_response, mock_ranking_api, mock_response_api, sample_computed_data - ): - """Test incremental computation with existing data.""" - # First, add some existing data - rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") - - # Mock APIs to return new regulators - mock_ranking_api.query.return_value = pd.DataFrame( - { - "regulator_locus_tag": ["REG1", "REG2", "REG3", "REG4"], # REG4 is new - } - ) - - with patch("tfbpapi.HfRankResponse.duckdb.connect") as mock_duckdb: - mock_temp_conn = MagicMock() - mock_duckdb.return_value = mock_temp_conn - # Return data for new regulator only - mock_temp_conn.execute.return_value.fetchdf.return_value = pd.DataFrame( - { - "regulator_locus_tag": ["REG4"], - "target_locus_tag": ["TGT1"], - "binding_score": [5.0], - "log2fc": [1.0], - "responsive": [1], - "bin_label": [5], - "cumulative_responsive": [1], - } - ) - - result = rank_response.compute( - ranking_api=mock_ranking_api, - response_api=mock_response_api, - ranking_table="binding_data", - response_table="expression_data", - ranking_score_column="binding_score", - response_column="log2fc", - comparison_id="test_comp", - ) - - # Should have data for all regulators now - regulators = set(result["regulator_locus_tag"].unique()) - assert "REG1" in regulators - assert "REG4" in regulators - - def test_get_bin_summary(self, rank_response, sample_computed_data): - """Test generating bin-level summary.""" - # Add test data - rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") - - summary = rank_response.get_bin_summary("test_comp") - - assert not summary.empty - assert "regulator_locus_tag" in summary.columns - assert "bin_label" in summary.columns - assert "records_in_bin" in summary.columns - assert "responsive_in_bin" in summary.columns - assert "cumulative_responsive" in summary.columns - assert "response_rate" in summary.columns - - def test_get_bin_summary_with_filter(self, rank_response, sample_computed_data): - """Test bin summary with regulator filter.""" - rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") - - summary = rank_response.get_bin_summary( - "test_comp", regulators_filter=["REG1", "REG2"] - ) - - assert not summary.empty - regulators = set(summary["regulator_locus_tag"].unique()) - assert regulators.issubset({"REG1", "REG2"}) - assert "REG3" not in regulators - - def test_get_regulator_summary(self, rank_response, sample_computed_data): - """Test generating regulator-level summary.""" - rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") - - summary = rank_response.get_regulator_summary("test_comp") - - assert not summary.empty - assert "regulator_locus_tag" in summary.columns - assert "total_targets" in summary.columns - assert "total_responsive" in summary.columns - assert "overall_response_rate" in summary.columns - assert "top5_response_rate" in summary.columns - assert "top10_response_rate" in summary.columns - assert "top20_response_rate" in summary.columns - - def test_get_regulator_summary_with_max_bin( - self, rank_response, sample_computed_data - ): - """Test regulator summary with max bin limit.""" - rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") - - summary = rank_response.get_regulator_summary("test_comp", max_bin_label=5) - - assert not summary.empty - # Should only include data from bins <= 5 - - def test_summarize_bin_type(self, rank_response, sample_computed_data): - """Test summarize method with bin type.""" - rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") - - summary = rank_response.summarize("test_comp", summary_type="bin") - - assert not summary.empty - assert "bin_label" in summary.columns - - def test_summarize_regulator_type(self, rank_response, sample_computed_data): - """Test summarize method with regulator type.""" - rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") - - summary = rank_response.summarize("test_comp", summary_type="regulator") - - assert not summary.empty - assert "overall_response_rate" in summary.columns - - def test_summarize_invalid_type(self, rank_response, sample_computed_data): - """Test summarize with invalid summary type.""" - rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") - - with pytest.raises(ValueError, match="Unknown summary type"): - rank_response.summarize("test_comp", summary_type="invalid") - - def test_query_method(self, rank_response, sample_computed_data): - """Test direct SQL query method.""" - rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") - - result = rank_response.query( - "SELECT COUNT(*) as count FROM rank_response_test_comp" - ) - - assert not result.empty - assert result.iloc[0]["count"] == len(sample_computed_data) - - def test_get_comparison_data(self, rank_response, sample_computed_data): - """Test getting raw comparison data.""" - rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") - - data = rank_response.get_comparison_data("test_comp") - - assert not data.empty - assert len(data) == len(sample_computed_data) - - def test_get_comparison_data_with_filter(self, rank_response, sample_computed_data): - """Test getting comparison data with regulator filter.""" - rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") - - data = rank_response.get_comparison_data("test_comp", regulator_filter=["REG1"]) - - assert not data.empty - assert all(data["regulator_locus_tag"] == "REG1") - - def test_get_comparison_data_with_limit(self, rank_response, sample_computed_data): - """Test getting comparison data with limit.""" - rank_response.db.append_results(sample_computed_data, "rank_response_test_comp") - - data = rank_response.get_comparison_data("test_comp", limit=3) - - assert len(data) == 3 - - def test_compare_across_datasets(self, rank_response, sample_computed_data): - """Test comparing across multiple datasets.""" - # Add data for multiple comparisons - rank_response.db.append_results(sample_computed_data, "rank_response_comp1") - rank_response.db.append_results(sample_computed_data, "rank_response_comp2") - - comparison = rank_response.compare_across_datasets(["comp1", "comp2"]) - - assert not comparison.empty - assert "regulator_locus_tag" in comparison.columns - # Should have columns for each comparison and metric - assert any("overall_response_rate_" in col for col in comparison.columns) - - def test_compare_across_datasets_empty(self, rank_response): - """Test comparing across datasets with no data.""" - comparison = rank_response.compare_across_datasets([]) - - assert comparison.empty - - def test_compare_across_datasets_custom_metrics( - self, rank_response, sample_computed_data - ): - """Test comparing with custom metric columns.""" - rank_response.db.append_results(sample_computed_data, "rank_response_comp1") - - comparison = rank_response.compare_across_datasets( - ["comp1"], metric_columns=["top5_response_rate"] - ) - - assert not comparison.empty - assert any("top5_response_rate_" in col for col in comparison.columns) - - def test_nonexistent_comparison_error(self, rank_response): - """Test error handling for nonexistent comparisons.""" - with pytest.raises(ValueError, match="No results found"): - rank_response.get_comparison_data("nonexistent") - - with pytest.raises(ValueError, match="does not exist"): - rank_response.get_bin_summary("nonexistent") - - with pytest.raises(ValueError, match="does not exist"): - rank_response.get_regulator_summary("nonexistent") - - def test_compute_with_existing_filters( - self, rank_response, mock_ranking_api, mock_response_api - ): - """Test compute when APIs already have filters set.""" - # Set existing filters - mock_ranking_api.get_table_filter.return_value = "existing_filter = 'value'" - mock_response_api.get_table_filter.return_value = "another_filter = 'value'" - - with patch("tfbpapi.HfRankResponse.duckdb.connect") as mock_duckdb: - mock_temp_conn = MagicMock() - mock_duckdb.return_value = mock_temp_conn - mock_temp_conn.execute.return_value.fetchdf.return_value = pd.DataFrame( - { - "regulator_locus_tag": ["REG1"], - "target_locus_tag": ["TGT1"], - "binding_score": [10.0], - "log2fc": [2.0], - "responsive": [1], - "bin_label": [5], - "cumulative_responsive": [1], - } - ) - - rank_response.compute( - ranking_api=mock_ranking_api, - response_api=mock_response_api, - ranking_table="binding_data", - response_table="expression_data", - ranking_score_column="binding_score", - response_column="log2fc", - comparison_id="with_filters", - ) - - # Verify filters were combined with AND - calls = mock_ranking_api.set_table_filter.call_args_list - assert len(calls) > 0 - combined_filter = calls[0][0][1] # Second argument of first call - assert "existing_filter = 'value'" in combined_filter - assert "AND" in combined_filter - - @patch("tfbpapi.HfRankResponse.logging.getLogger") - def test_logging_setup(self, mock_get_logger, analysis_db): - """Test that logging is properly configured.""" - mock_logger = MagicMock() - mock_get_logger.return_value = mock_logger - - rr = HfRankResponse(analysis_db) - - mock_get_logger.assert_called_once_with("tfbpapi.HfRankResponse") - assert rr.logger == mock_logger - - def test_compute_with_custom_responsive_condition( - self, rank_response, mock_ranking_api, mock_response_api - ): - """Test compute with custom responsive condition.""" - with patch("tfbpapi.HfRankResponse.duckdb.connect") as mock_duckdb: - mock_temp_conn = MagicMock() - mock_duckdb.return_value = mock_temp_conn - mock_temp_conn.execute.return_value.fetchdf.return_value = pd.DataFrame( - { - "regulator_locus_tag": ["REG1"], - "target_locus_tag": ["TGT1"], - "binding_score": [10.0], - "log2fc": [2.0], - "responsive": [1], - "bin_label": [5], - "cumulative_responsive": [1], - } - ) - - rank_response.compute( - ranking_api=mock_ranking_api, - response_api=mock_response_api, - ranking_table="binding_data", - response_table="expression_data", - ranking_score_column="binding_score", - response_column="log2fc", - comparison_id="custom_responsive", - responsive_condition="log2fc IS NOT NULL AND log2fc != 0", - ) - - # Verify the SQL contains the custom responsive condition - sql_calls = mock_temp_conn.execute.call_args_list - assert len(sql_calls) > 0 - executed_sql = sql_calls[0][0][0] - assert "b.log2fc IS NOT NULL AND b.log2fc != 0" in executed_sql - - def test_compute_with_default_responsive_condition( - self, rank_response, mock_ranking_api, mock_response_api - ): - """Test compute with default responsive condition (IS NOT NULL).""" - with patch("tfbpapi.HfRankResponse.duckdb.connect") as mock_duckdb: - mock_temp_conn = MagicMock() - mock_duckdb.return_value = mock_temp_conn - mock_temp_conn.execute.return_value.fetchdf.return_value = pd.DataFrame( - { - "regulator_locus_tag": ["REG1"], - "target_locus_tag": ["TGT1"], - "binding_score": [10.0], - "log2fc": [2.0], - "responsive": [1], - "bin_label": [5], - "cumulative_responsive": [1], - } - ) - - rank_response.compute( - ranking_api=mock_ranking_api, - response_api=mock_response_api, - ranking_table="binding_data", - response_table="expression_data", - ranking_score_column="binding_score", - response_column="log2fc", - comparison_id="default_responsive", - ) - - # Verify the SQL contains the default responsive condition - sql_calls = mock_temp_conn.execute.call_args_list - assert len(sql_calls) > 0 - executed_sql = sql_calls[0][0][0] - assert "b.log2fc IS NOT NULL" in executed_sql diff --git a/tfbpapi/tests/test_IncrementalAnalysisDB.py b/tfbpapi/tests/test_IncrementalAnalysisDB.py deleted file mode 100644 index 0fbb1e1..0000000 --- a/tfbpapi/tests/test_IncrementalAnalysisDB.py +++ /dev/null @@ -1,341 +0,0 @@ -import json -import tempfile -from pathlib import Path -from unittest.mock import MagicMock, patch - -import pandas as pd -import pytest - -from tfbpapi.IncrementalAnalysisDB import IncrementalAnalysisDB - - -@pytest.fixture -def temp_db_path(): - """Create temporary database path for testing.""" - with tempfile.TemporaryDirectory() as temp_dir: - db_path = Path(temp_dir) / "test.db" - yield str(db_path) - - -@pytest.fixture -def sample_dataframe(): - """Create sample DataFrame for testing.""" - return pd.DataFrame( - {"id": [1, 2, 3], "name": ["A", "B", "C"], "value": [10.5, 20.3, 15.7]} - ) - - -@pytest.fixture -def analysis_db(temp_db_path): - """Create IncrementalAnalysisDB instance for testing.""" - return IncrementalAnalysisDB(temp_db_path) - - -class TestIncrementalAnalysisDB: - - def test_init_creates_database_and_metadata_table(self, temp_db_path): - """Test that initialization creates the database file and metadata table.""" - db = IncrementalAnalysisDB(temp_db_path) - - # Check database file exists - assert Path(temp_db_path).exists() - - # Check metadata table exists - result = db.conn.execute( - """ - SELECT table_name FROM information_schema.tables - WHERE table_name='analysis_metadata' AND table_schema='main' - """ - ).fetchall() - assert len(result) == 1 - - db.conn.close() - - def test_init_creates_parent_directories(self): - """Test that initialization creates parent directories if they don't exist.""" - with tempfile.TemporaryDirectory() as temp_dir: - nested_path = Path(temp_dir) / "nested" / "path" / "test.db" - db = IncrementalAnalysisDB(str(nested_path)) - - assert nested_path.parent.exists() - assert nested_path.exists() - db.conn.close() - - def test_append_results_new_table(self, analysis_db, sample_dataframe): - """Test appending results to a new table.""" - records_added = analysis_db.append_results( - new_results=sample_dataframe, - table_name="test_table", - analysis_type="test_analysis", - parameters={"param1": "value1"}, - description="Test description", - ) - - assert records_added == 3 - - # Check data was inserted - result = analysis_db.conn.execute("SELECT * FROM test_table").fetchdf() - pd.testing.assert_frame_equal(result, sample_dataframe) - - # Check metadata was inserted - metadata = analysis_db.conn.execute( - """ - SELECT * FROM analysis_metadata WHERE table_name = 'test_table' - """ - ).fetchdf() - - assert len(metadata) == 1 - assert metadata.iloc[0]["analysis_type"] == "test_analysis" - assert metadata.iloc[0]["total_records"] == 3 - assert json.loads(metadata.iloc[0]["parameters"]) == {"param1": "value1"} - assert metadata.iloc[0]["description"] == "Test description" - - def test_append_results_existing_table(self, analysis_db, sample_dataframe): - """Test appending results to an existing table.""" - # First append - analysis_db.append_results(sample_dataframe, "test_table") - - # Second append with new data - new_data = pd.DataFrame( - {"id": [4, 5], "name": ["D", "E"], "value": [25.1, 30.9]} - ) - - records_added = analysis_db.append_results(new_data, "test_table") - assert records_added == 2 - - # Check total records - result = analysis_db.conn.execute( - "SELECT COUNT(*) as count FROM test_table" - ).fetchdf() - assert result.iloc[0]["count"] == 5 - - # Check metadata updated - metadata = analysis_db.conn.execute( - """ - SELECT total_records FROM analysis_metadata WHERE table_name = 'test_table' - """ - ).fetchdf() - assert metadata.iloc[0]["total_records"] == 5 - - def test_append_results_with_deduplication(self, analysis_db): - """Test appending results with deduplication.""" - initial_data = pd.DataFrame( - {"id": [1, 2, 3], "name": ["A", "B", "C"], "value": [10.5, 20.3, 15.7]} - ) - - analysis_db.append_results(initial_data, "test_table", deduplicate_on=["id"]) - - # Append data with some duplicates - new_data = pd.DataFrame( - { - "id": [2, 3, 4], # 2 and 3 are duplicates - "name": ["B2", "C2", "D"], - "value": [20.3, 15.7, 25.1], - } - ) - - records_added = analysis_db.append_results( - new_data, "test_table", deduplicate_on=["id"] - ) - - # Only record with id=4 should be added - assert records_added == 1 - - # Check total records - result = analysis_db.conn.execute( - "SELECT COUNT(*) as count FROM test_table" - ).fetchdf() - assert result.iloc[0]["count"] == 4 - - def test_update_results(self, analysis_db, sample_dataframe): - """Test updating existing results.""" - # First insert data - analysis_db.append_results(sample_dataframe, "test_table") - - # Update data - updated_data = pd.DataFrame( - {"id": [1, 2], "name": ["A_updated", "B_updated"], "value": [100.5, 200.3]} - ) - - records_updated = analysis_db.update_results( - updated_data, "test_table", key_columns=["id"] - ) - - assert records_updated == 2 - - # Check data was updated - result = analysis_db.conn.execute( - """ - SELECT * FROM test_table WHERE id IN (1, 2) ORDER BY id - """ - ).fetchdf() - - assert result.iloc[0]["name"] == "A_updated" - assert result.iloc[1]["name"] == "B_updated" - - def test_get_results(self, analysis_db, sample_dataframe): - """Test retrieving results.""" - analysis_db.append_results(sample_dataframe, "test_table") - - # Get all results - result = analysis_db.get_results("test_table") - pd.testing.assert_frame_equal(result, sample_dataframe) - - # Get results with filter - filtered_result = analysis_db.get_results("test_table", filters={"id": [1, 2]}) - - expected = sample_dataframe[sample_dataframe["id"].isin([1, 2])] - pd.testing.assert_frame_equal(filtered_result.reset_index(drop=True), expected) - - def test_get_results_with_limit(self, analysis_db, sample_dataframe): - """Test retrieving results with limit.""" - analysis_db.append_results(sample_dataframe, "test_table") - - result = analysis_db.get_results("test_table", limit=2) - assert len(result) == 2 - - def test_query_method(self, analysis_db, sample_dataframe): - """Test direct SQL query execution.""" - analysis_db.append_results(sample_dataframe, "test_table") - - # Test basic query - result = analysis_db.query("SELECT * FROM test_table") - assert len(result) == len(sample_dataframe) - pd.testing.assert_frame_equal(result, sample_dataframe) - - # Test query with WHERE clause - result = analysis_db.query("SELECT * FROM test_table WHERE id = 1") - assert len(result) == 1 - assert result.iloc[0]["id"] == 1 - - # Test query with aggregation - result = analysis_db.query("SELECT COUNT(*) as count FROM test_table") - assert result.iloc[0]["count"] == len(sample_dataframe) - - # Test query with complex SQL - result = analysis_db.query( - """ - SELECT name, AVG(value) as avg_value - FROM test_table - GROUP BY name - ORDER BY name - """ - ) - assert len(result) == 3 # Should have 3 distinct names - assert "avg_value" in result.columns - - def test_table_exists(self, analysis_db, sample_dataframe): - """Test checking if table exists.""" - assert not analysis_db.table_exists("test_table") - - analysis_db.append_results(sample_dataframe, "test_table") - assert analysis_db.table_exists("test_table") - - def test_drop_table(self, analysis_db, sample_dataframe): - """Test dropping a table.""" - analysis_db.append_results(sample_dataframe, "test_table") - assert analysis_db.table_exists("test_table") - - analysis_db.drop_table("test_table") - assert not analysis_db.table_exists("test_table") - - # Check metadata was also removed - metadata = analysis_db.conn.execute( - """ - SELECT * FROM analysis_metadata WHERE table_name = 'test_table' - """ - ).fetchdf() - assert len(metadata) == 0 - - def test_get_table_info(self, analysis_db, sample_dataframe): - """Test getting table information.""" - analysis_db.append_results( - sample_dataframe, - "test_table", - analysis_type="test_analysis", - parameters={"param1": "value1"}, - description="Test description", - ) - - info = analysis_db.get_table_info("test_table") - - assert info["table_name"] == "test_table" - assert info["total_records"] == 3 - assert info["analysis_type"] == "test_analysis" - assert json.loads(info["parameters"]) == {"param1": "value1"} - assert info["description"] == "Test description" - - def test_list_tables(self, analysis_db, sample_dataframe): - """Test listing all tables.""" - # Initially should be empty (except metadata table) - tables = analysis_db.list_tables() - assert "analysis_metadata" in tables - - # Add some tables - analysis_db.append_results(sample_dataframe, "table1") - analysis_db.append_results(sample_dataframe, "table2") - - tables = analysis_db.list_tables() - assert "table1" in tables - assert "table2" in tables - assert "analysis_metadata" in tables - - def test_get_table_schema(self, analysis_db, sample_dataframe): - """Test getting table schema.""" - analysis_db.append_results(sample_dataframe, "test_table") - - schema = analysis_db.get_table_schema("test_table") - - # Should have columns from sample dataframe - column_names = [col["column_name"] for col in schema] - assert "id" in column_names - assert "name" in column_names - assert "value" in column_names - - def test_close_connection(self, analysis_db): - """Test closing database connection.""" - analysis_db.close() - - # Connection should be closed - with pytest.raises(Exception): - analysis_db.conn.execute("SELECT 1") - - def test_context_manager(self, temp_db_path, sample_dataframe): - """Test using IncrementalAnalysisDB as context manager.""" - with IncrementalAnalysisDB(temp_db_path) as db: - db.append_results(sample_dataframe, "test_table") - assert db.table_exists("test_table") - - # Connection should be closed after context exit - with pytest.raises(Exception): - db.conn.execute("SELECT 1") - - @patch("tfbpapi.IncrementalAnalysisDB.logging.getLogger") - def test_logging_setup(self, mock_get_logger, temp_db_path): - """Test that logging is properly configured.""" - mock_logger = MagicMock() - mock_get_logger.return_value = mock_logger - - db = IncrementalAnalysisDB(temp_db_path) - - mock_get_logger.assert_called_once_with("tfbpapi.IncrementalAnalysisDB") - assert db.logger == mock_logger - db.conn.close() - - def test_error_handling_nonexistent_table(self, analysis_db): - """Test error handling for operations on nonexistent tables.""" - with pytest.raises(Exception): - analysis_db.get_results("nonexistent_table") - - with pytest.raises(Exception): - analysis_db.get_table_info("nonexistent_table") - - def test_empty_dataframe_append(self, analysis_db): - """Test appending empty DataFrame.""" - empty_df = pd.DataFrame() - - records_added = analysis_db.append_results(empty_df, "empty_table") - assert records_added == 0 - - # Table should not be created for empty DataFrame - assert not analysis_db.table_exists("empty_table") diff --git a/tfbpapi/tests/test_datacard.py b/tfbpapi/tests/test_datacard.py new file mode 100644 index 0000000..01b2c0b --- /dev/null +++ b/tfbpapi/tests/test_datacard.py @@ -0,0 +1,449 @@ +"""Tests for the DataCard class.""" + +from unittest.mock import Mock, patch + +import pytest + +from tfbpapi import DataCard +from tfbpapi.errors import DataCardError, DataCardValidationError, HfDataFetchError +from tfbpapi.models import DatasetType + + +class TestDataCard: + """Test suite for DataCard class.""" + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_init( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + test_token, + ): + """Test DataCard initialization.""" + datacard = DataCard(test_repo_id, token=test_token) + + assert datacard.repo_id == test_repo_id + assert datacard.token == test_token + assert datacard._dataset_card is None + assert datacard._metadata_cache == {} + + # Check that fetchers were initialized + mock_card_fetcher.assert_called_once_with(token=test_token) + mock_structure_fetcher.assert_called_once_with(token=test_token) + mock_size_fetcher.assert_called_once_with(token=test_token) + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_init_without_token( + self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id + ): + """Test DataCard initialization without token.""" + datacard = DataCard(test_repo_id) + + assert datacard.repo_id == test_repo_id + assert datacard.token is None + + # Check that fetchers were initialized without token + mock_card_fetcher.assert_called_once_with(token=None) + mock_structure_fetcher.assert_called_once_with(token=None) + mock_size_fetcher.assert_called_once_with(token=None) + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_load_and_validate_card_success( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): + """Test successful card loading and validation.""" + # Setup mock + mock_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_fetcher_instance + mock_fetcher_instance.fetch.return_value = sample_dataset_card_data + + datacard = DataCard(test_repo_id) + + # Access dataset_card property to trigger loading + card = datacard.dataset_card + + assert card is not None + assert len(card.configs) == 4 + assert card.pretty_name == "Test Genomics Dataset" + mock_fetcher_instance.fetch.assert_called_once_with(test_repo_id) + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_load_card_no_data( + self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id + ): + """Test handling when no dataset card is found.""" + mock_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_fetcher_instance + mock_fetcher_instance.fetch.return_value = {} + + datacard = DataCard(test_repo_id) + + with pytest.raises(DataCardValidationError, match="No dataset card found"): + _ = datacard.dataset_card + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_load_card_validation_error( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + invalid_dataset_card_data, + ): + """Test handling of validation errors.""" + mock_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_fetcher_instance + mock_fetcher_instance.fetch.return_value = invalid_dataset_card_data + + datacard = DataCard(test_repo_id) + + with pytest.raises( + DataCardValidationError, match="Dataset card validation failed" + ): + _ = datacard.dataset_card + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_load_card_fetch_error( + self, mock_size_fetcher, mock_structure_fetcher, mock_card_fetcher, test_repo_id + ): + """Test handling of fetch errors.""" + mock_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_fetcher_instance + mock_fetcher_instance.fetch.side_effect = HfDataFetchError("Fetch failed") + + datacard = DataCard(test_repo_id) + + with pytest.raises(DataCardError, match="Failed to fetch dataset card"): + _ = datacard.dataset_card + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_configs_property( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): + """Test getting all configurations via property.""" + mock_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_fetcher_instance + mock_fetcher_instance.fetch.return_value = sample_dataset_card_data + + datacard = DataCard(test_repo_id) + configs = datacard.configs + + assert len(configs) == 4 + config_names = [config.config_name for config in configs] + assert "genomic_features" in config_names + assert "binding_data" in config_names + assert "genome_map_data" in config_names + assert "experiment_metadata" in config_names + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_get_config_by_name( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): + """Test getting a specific configuration by name.""" + mock_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_fetcher_instance + mock_fetcher_instance.fetch.return_value = sample_dataset_card_data + + datacard = DataCard(test_repo_id) + + config = datacard.get_config("binding_data") + assert config is not None + assert config.config_name == "binding_data" + assert config.dataset_type == DatasetType.ANNOTATED_FEATURES + + # Test non-existent config + assert datacard.get_config("nonexistent") is None + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_get_metadata_relationships( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): + """Test getting metadata relationships.""" + mock_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_fetcher_instance + mock_fetcher_instance.fetch.return_value = sample_dataset_card_data + + datacard = DataCard(test_repo_id) + + relationships = datacard.get_metadata_relationships() + + # Should have explicit relationship between binding_data and experiment_metadata + explicit_rels = [r for r in relationships if r.relationship_type == "explicit"] + assert len(explicit_rels) == 1 + assert explicit_rels[0].data_config == "binding_data" + assert explicit_rels[0].metadata_config == "experiment_metadata" + + # Should have embedded relationship for binding_data (has metadata_fields) + embedded_rels = [r for r in relationships if r.relationship_type == "embedded"] + assert len(embedded_rels) == 1 + assert embedded_rels[0].data_config == "binding_data" + assert embedded_rels[0].metadata_config == "binding_data_embedded" + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_get_repository_info_success( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + sample_repo_structure, + ): + """Test getting repository information.""" + mock_card_fetcher_instance = Mock() + mock_structure_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_card_fetcher_instance + mock_structure_fetcher.return_value = mock_structure_fetcher_instance + + mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data + mock_structure_fetcher_instance.fetch.return_value = sample_repo_structure + + datacard = DataCard(test_repo_id) + + info = datacard.get_repository_info() + + assert info["repo_id"] == test_repo_id + assert info["pretty_name"] == "Test Genomics Dataset" + assert info["license"] == "mit" + assert info["num_configs"] == 4 + assert "genomic_features" in info["dataset_types"] + assert "annotated_features" in info["dataset_types"] + assert "genome_map" in info["dataset_types"] + assert "metadata" in info["dataset_types"] + assert info["total_files"] == 5 + assert info["last_modified"] == "2023-12-01T10:30:00Z" + assert info["has_default_config"] is True + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_get_repository_info_fetch_error( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): + """Test getting repository info when structure fetch fails.""" + mock_card_fetcher_instance = Mock() + mock_structure_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_card_fetcher_instance + mock_structure_fetcher.return_value = mock_structure_fetcher_instance + + mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data + mock_structure_fetcher_instance.fetch.side_effect = HfDataFetchError( + "Structure fetch failed" + ) + + datacard = DataCard(test_repo_id) + + info = datacard.get_repository_info() + + assert info["repo_id"] == test_repo_id + assert info["total_files"] is None + assert info["last_modified"] is None + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_summary( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + sample_repo_structure, + ): + """Test getting a summary of the dataset.""" + mock_card_fetcher_instance = Mock() + mock_structure_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_card_fetcher_instance + mock_structure_fetcher.return_value = mock_structure_fetcher_instance + + mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data + mock_structure_fetcher_instance.fetch.return_value = sample_repo_structure + + datacard = DataCard(test_repo_id) + + summary = datacard.summary() + + assert "Dataset: Test Genomics Dataset" in summary + assert f"Repository: {test_repo_id}" in summary + assert "License: mit" in summary + assert "Configurations: 4" in summary + assert "genomic_features" in summary + assert "binding_data" in summary + assert "genome_map_data" in summary + assert "experiment_metadata" in summary + assert "(default)" in summary # genomic_features is marked as default + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_extract_partition_values( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): + """Test extracting partition values.""" + mock_card_fetcher_instance = Mock() + mock_structure_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_card_fetcher_instance + mock_structure_fetcher.return_value = mock_structure_fetcher_instance + + mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data + mock_structure_fetcher_instance.get_partition_values.return_value = [ + "TF1", + "TF2", + "TF3", + ] + + datacard = DataCard(test_repo_id) + + # Get the genome_map_data config which has partitioning enabled + config = datacard.get_config("genome_map_data") + assert config is not None + assert config.dataset_info.partitioning.enabled is True + + values = datacard._extract_partition_values(config, "regulator") + assert values == {"TF1", "TF2", "TF3"} + mock_structure_fetcher_instance.get_partition_values.assert_called_once_with( + test_repo_id, "regulator" + ) + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_extract_partition_values_no_partitioning( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): + """Test extracting partition values when partitioning is disabled.""" + mock_card_fetcher_instance = Mock() + mock_structure_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_card_fetcher_instance + mock_structure_fetcher.return_value = mock_structure_fetcher_instance + + mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data + + datacard = DataCard(test_repo_id) + + # Get a config without partitioning + config = datacard.get_config("genomic_features") + assert config is not None + assert config.dataset_info.partitioning is None + + values = datacard._extract_partition_values(config, "some_field") + assert values == set() + mock_structure_fetcher_instance.get_partition_values.assert_not_called() + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_extract_partition_values_field_not_in_partitions( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): + """Test extracting partition values when field is not a partition column.""" + mock_card_fetcher_instance = Mock() + mock_structure_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_card_fetcher_instance + mock_structure_fetcher.return_value = mock_structure_fetcher_instance + + mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data + + datacard = DataCard(test_repo_id) + + # Get the genome_map_data config which has partitioning enabled + config = datacard.get_config("genome_map_data") + assert config is not None + + # Try to extract values for a field that's not in partition_by + values = datacard._extract_partition_values(config, "not_a_partition_field") + assert values == set() + mock_structure_fetcher_instance.get_partition_values.assert_not_called() + + @patch("tfbpapi.datacard.HfDataCardFetcher") + @patch("tfbpapi.datacard.HfRepoStructureFetcher") + @patch("tfbpapi.datacard.HfSizeInfoFetcher") + def test_extract_partition_values_fetch_error( + self, + mock_size_fetcher, + mock_structure_fetcher, + mock_card_fetcher, + test_repo_id, + sample_dataset_card_data, + ): + """Test extracting partition values when fetch fails.""" + mock_card_fetcher_instance = Mock() + mock_structure_fetcher_instance = Mock() + mock_card_fetcher.return_value = mock_card_fetcher_instance + mock_structure_fetcher.return_value = mock_structure_fetcher_instance + + mock_card_fetcher_instance.fetch.return_value = sample_dataset_card_data + mock_structure_fetcher_instance.get_partition_values.side_effect = ( + HfDataFetchError("Fetch failed") + ) + + datacard = DataCard(test_repo_id) + + config = datacard.get_config("genome_map_data") + values = datacard._extract_partition_values(config, "regulator") + + # Should return empty set on error + assert values == set() diff --git a/tfbpapi/tests/datainfo/test_datacard_parsing.py b/tfbpapi/tests/test_datacard_parsing.py similarity index 98% rename from tfbpapi/tests/datainfo/test_datacard_parsing.py rename to tfbpapi/tests/test_datacard_parsing.py index c7a35d0..5d2210e 100644 --- a/tfbpapi/tests/datainfo/test_datacard_parsing.py +++ b/tfbpapi/tests/test_datacard_parsing.py @@ -2,8 +2,8 @@ import yaml -from tfbpapi.datainfo.models import DatasetCard -from tfbpapi.tests.datainfo.example_datacards import ( +from tfbpapi.models import DatasetCard +from tfbpapi.tests.example_datacards import ( EXAMPLE_1_SIMPLE_TOPLEVEL, EXAMPLE_2_COMPLEX_FIELD_DEFINITIONS, EXAMPLE_3_PARTITIONED_WITH_METADATA, diff --git a/tfbpapi/tests/datainfo/test_fetchers.py b/tfbpapi/tests/test_fetchers.py similarity index 93% rename from tfbpapi/tests/datainfo/test_fetchers.py rename to tfbpapi/tests/test_fetchers.py index a0a04fe..ac350f5 100644 --- a/tfbpapi/tests/datainfo/test_fetchers.py +++ b/tfbpapi/tests/test_fetchers.py @@ -6,7 +6,7 @@ import requests from requests import HTTPError -from tfbpapi.datainfo.fetchers import ( +from tfbpapi.fetchers import ( HfDataCardFetcher, HfRepoStructureFetcher, HfSizeInfoFetcher, @@ -34,7 +34,7 @@ def test_init_with_env_token(self, test_token): fetcher = HfDataCardFetcher() assert fetcher.token == test_token - @patch("tfbpapi.datainfo.fetchers.DatasetCard") + @patch("tfbpapi.fetchers.DatasetCard") def test_fetch_success( self, mock_dataset_card, test_repo_id, sample_dataset_card_data ): @@ -52,7 +52,7 @@ def test_fetch_success( test_repo_id, repo_type="dataset", token="test_token" ) - @patch("tfbpapi.datainfo.fetchers.DatasetCard") + @patch("tfbpapi.fetchers.DatasetCard") def test_fetch_no_data_section(self, mock_dataset_card, test_repo_id): """Test fetch when dataset card has no data section.""" # Setup mock with no data @@ -65,7 +65,7 @@ def test_fetch_no_data_section(self, mock_dataset_card, test_repo_id): assert result == {} - @patch("tfbpapi.datainfo.fetchers.DatasetCard") + @patch("tfbpapi.fetchers.DatasetCard") def test_fetch_exception(self, mock_dataset_card, test_repo_id): """Test fetch when DatasetCard.load raises exception.""" mock_dataset_card.load.side_effect = Exception("API Error") @@ -77,7 +77,7 @@ def test_fetch_exception(self, mock_dataset_card, test_repo_id): def test_fetch_different_repo_types(self, sample_dataset_card_data): """Test fetch with different repository types.""" - with patch("tfbpapi.datainfo.fetchers.DatasetCard") as mock_dataset_card: + with patch("tfbpapi.fetchers.DatasetCard") as mock_dataset_card: mock_card = Mock() mock_card.data.to_dict.return_value = sample_dataset_card_data mock_dataset_card.load.return_value = mock_card @@ -122,7 +122,7 @@ def test_build_headers_without_token(self): assert headers["User-Agent"] == "TFBP-API/1.0" assert "Authorization" not in headers - @patch("tfbpapi.datainfo.fetchers.requests.get") + @patch("tfbpapi.fetchers.requests.get") def test_fetch_success(self, mock_get, test_repo_id, sample_size_info): """Test successful size info fetch.""" # Setup mock response @@ -142,7 +142,7 @@ def test_fetch_success(self, mock_get, test_repo_id, sample_size_info): assert call_args[1]["headers"]["Authorization"] == "Bearer test_token" assert call_args[1]["timeout"] == 30 - @patch("tfbpapi.datainfo.fetchers.requests.get") + @patch("tfbpapi.fetchers.requests.get") def test_fetch_404_error(self, mock_get, test_repo_id): """Test fetch with 404 error.""" # Setup mock 404 response @@ -156,7 +156,7 @@ def test_fetch_404_error(self, mock_get, test_repo_id): with pytest.raises(HfDataFetchError, match="Dataset .* not found"): fetcher.fetch(test_repo_id) - @patch("tfbpapi.datainfo.fetchers.requests.get") + @patch("tfbpapi.fetchers.requests.get") def test_fetch_403_error(self, mock_get, test_repo_id): """Test fetch with 403 error.""" # Setup mock 403 response @@ -172,7 +172,7 @@ def test_fetch_403_error(self, mock_get, test_repo_id): ): fetcher.fetch(test_repo_id) - @patch("tfbpapi.datainfo.fetchers.requests.get") + @patch("tfbpapi.fetchers.requests.get") def test_fetch_other_http_error(self, mock_get, test_repo_id): """Test fetch with other HTTP error.""" # Setup mock 500 response @@ -186,7 +186,7 @@ def test_fetch_other_http_error(self, mock_get, test_repo_id): with pytest.raises(HfDataFetchError, match="HTTP error fetching size"): fetcher.fetch(test_repo_id) - @patch("tfbpapi.datainfo.fetchers.requests.get") + @patch("tfbpapi.fetchers.requests.get") def test_fetch_request_exception(self, mock_get, test_repo_id): """Test fetch with request exception.""" mock_get.side_effect = requests.RequestException("Network error") @@ -196,7 +196,7 @@ def test_fetch_request_exception(self, mock_get, test_repo_id): with pytest.raises(HfDataFetchError, match="Request failed fetching size"): fetcher.fetch(test_repo_id) - @patch("tfbpapi.datainfo.fetchers.requests.get") + @patch("tfbpapi.fetchers.requests.get") def test_fetch_json_decode_error(self, mock_get, test_repo_id): """Test fetch with JSON decode error.""" # Setup mock response with invalid JSON @@ -219,7 +219,7 @@ def test_init(self, test_token): assert fetcher.token == test_token assert fetcher._cached_structure == {} - @patch("tfbpapi.datainfo.fetchers.repo_info") + @patch("tfbpapi.fetchers.repo_info") def test_fetch_success(self, mock_repo_info, test_repo_id, sample_repo_structure): """Test successful repository structure fetch.""" # Setup mock repo info @@ -249,7 +249,7 @@ def test_fetch_success(self, mock_repo_info, test_repo_id, sample_repo_structure repo_id=test_repo_id, repo_type="dataset", token="test_token" ) - @patch("tfbpapi.datainfo.fetchers.repo_info") + @patch("tfbpapi.fetchers.repo_info") def test_fetch_with_caching(self, mock_repo_info, test_repo_id): """Test fetch with caching behavior.""" # Setup mock @@ -273,7 +273,7 @@ def test_fetch_with_caching(self, mock_repo_info, test_repo_id): result3 = fetcher.fetch(test_repo_id, force_refresh=True) assert mock_repo_info.call_count == 2 - @patch("tfbpapi.datainfo.fetchers.repo_info") + @patch("tfbpapi.fetchers.repo_info") def test_fetch_siblings_none(self, mock_repo_info, test_repo_id): """Test fetch when siblings is None.""" # Setup mock with None siblings @@ -289,7 +289,7 @@ def test_fetch_siblings_none(self, mock_repo_info, test_repo_id): assert result["files"] == [] assert result["partitions"] == {} - @patch("tfbpapi.datainfo.fetchers.repo_info") + @patch("tfbpapi.fetchers.repo_info") def test_fetch_exception(self, mock_repo_info, test_repo_id): """Test fetch when repo_info raises exception.""" mock_repo_info.side_effect = Exception("API Error") @@ -326,7 +326,7 @@ def test_extract_partition_info(self): # partitions dict should remain unchanged assert len(partitions) == 2 - @patch("tfbpapi.datainfo.fetchers.repo_info") + @patch("tfbpapi.fetchers.repo_info") def test_get_partition_values_success(self, mock_repo_info, test_repo_id): """Test getting partition values for a specific column.""" # Setup mock with partitioned files @@ -344,7 +344,7 @@ def test_get_partition_values_success(self, mock_repo_info, test_repo_id): assert values == ["TF1", "TF2", "TF3"] # Should be sorted - @patch("tfbpapi.datainfo.fetchers.repo_info") + @patch("tfbpapi.fetchers.repo_info") def test_get_partition_values_no_partitions(self, mock_repo_info, test_repo_id): """Test getting partition values when no partitions exist.""" # Setup mock with no partitioned files @@ -360,7 +360,7 @@ def test_get_partition_values_no_partitions(self, mock_repo_info, test_repo_id): assert values == [] - @patch("tfbpapi.datainfo.fetchers.repo_info") + @patch("tfbpapi.fetchers.repo_info") def test_get_dataset_files_all(self, mock_repo_info, test_repo_id): """Test getting all dataset files.""" # Setup mock @@ -384,7 +384,7 @@ def test_get_dataset_files_all(self, mock_repo_info, test_repo_id): assert files[1]["size"] == 2000 assert files[1]["is_lfs"] is True - @patch("tfbpapi.datainfo.fetchers.repo_info") + @patch("tfbpapi.fetchers.repo_info") def test_get_dataset_files_with_pattern(self, mock_repo_info, test_repo_id): """Test getting dataset files with path pattern filter.""" # Setup mock diff --git a/tfbpapi/tests/test_HfCacheManager.py b/tfbpapi/tests/test_hf_cache_manager.py similarity index 94% rename from tfbpapi/tests/test_HfCacheManager.py rename to tfbpapi/tests/test_hf_cache_manager.py index e044150..94fb568 100644 --- a/tfbpapi/tests/test_HfCacheManager.py +++ b/tfbpapi/tests/test_hf_cache_manager.py @@ -7,8 +7,8 @@ import duckdb import pytest -from tfbpapi.datainfo.models import DatasetType -from tfbpapi.HfCacheManager import HfCacheManager +from tfbpapi.hf_cache_manager import HfCacheManager +from tfbpapi.models import DatasetType class TestHfCacheManagerInit: @@ -20,7 +20,7 @@ def test_init_basic(self): repo_id = "test/repo" with patch( - "tfbpapi.HfCacheManager.DataCard.__init__", return_value=None + "tfbpapi.hf_cache_manager.DataCard.__init__", return_value=None ) as mock_datacard_init: cache_manager = HfCacheManager(repo_id, conn) # Manually set the properties that would normally @@ -43,7 +43,7 @@ def test_init_with_token_and_logger(self): logger = logging.getLogger("test_logger") with patch( - "tfbpapi.HfCacheManager.DataCard.__init__", return_value=None + "tfbpapi.hf_cache_manager.DataCard.__init__", return_value=None ) as mock_datacard_init: cache_manager = HfCacheManager(repo_id, conn, token=token, logger=logger) # Manually set the properties that would @@ -69,7 +69,7 @@ def test_datacard_inheritance(self): token = "test_token" with patch( - "tfbpapi.HfCacheManager.DataCard.__init__", return_value=None + "tfbpapi.hf_cache_manager.DataCard.__init__", return_value=None ) as mock_datacard_init: cache_manager = HfCacheManager(repo_id, conn, token=token) @@ -78,13 +78,12 @@ def test_datacard_inheritance(self): # Should have DataCard methods available (they exist on the class) assert hasattr(cache_manager, "get_config") - assert hasattr(cache_manager, "get_configs_by_type") class TestHfCacheManagerDuckDBOperations: """Test DuckDB operations that are still part of HfCacheManager.""" - @patch("tfbpapi.HfCacheManager.DataCard.__init__", return_value=None) + @patch("tfbpapi.hf_cache_manager.DataCard.__init__", return_value=None) def test_create_duckdb_table_from_files_single_file( self, mock_datacard_init, tmpdir ): @@ -106,7 +105,7 @@ def test_create_duckdb_table_from_files_single_file( assert "CREATE OR REPLACE VIEW test_table" in sql_call assert str(parquet_file) in sql_call - @patch("tfbpapi.HfCacheManager.DataCard.__init__", return_value=None) + @patch("tfbpapi.hf_cache_manager.DataCard.__init__", return_value=None) def test_create_duckdb_table_from_files_multiple_files( self, mock_datacard_init, tmpdir ): @@ -137,7 +136,7 @@ class TestHfCacheManagerCacheManagement: def setup_method(self): """Set up test fixtures.""" - with patch("tfbpapi.HfCacheManager.DataCard.__init__", return_value=None): + with patch("tfbpapi.hf_cache_manager.DataCard.__init__", return_value=None): self.conn = duckdb.connect(":memory:") self.repo_id = "test/repo" self.cache_manager = HfCacheManager(self.repo_id, self.conn) @@ -160,7 +159,7 @@ def test_format_bytes(self): assert self.cache_manager._format_bytes(1024**3) == "1.0GB" assert self.cache_manager._format_bytes(1024**4) == "1.0TB" - @patch("tfbpapi.HfCacheManager.scan_cache_dir") + @patch("tfbpapi.hf_cache_manager.scan_cache_dir") def test_clean_cache_by_age(self, mock_scan_cache_dir): """Test age-based cache cleaning.""" # Setup mock cache info @@ -184,7 +183,7 @@ def test_clean_cache_by_age(self, mock_scan_cache_dir): assert result == mock_delete_strategy mock_cache_info.delete_revisions.assert_called_once_with("abc123") - @patch("tfbpapi.HfCacheManager.scan_cache_dir") + @patch("tfbpapi.hf_cache_manager.scan_cache_dir") def test_clean_cache_by_age_no_old_revisions(self, mock_scan_cache_dir): """Test age-based cleaning when no old revisions exist.""" mock_cache_info = Mock() @@ -208,7 +207,7 @@ def test_clean_cache_by_age_no_old_revisions(self, mock_scan_cache_dir): assert result == mock_delete_strategy mock_cache_info.delete_revisions.assert_called_once_with() - @patch("tfbpapi.HfCacheManager.scan_cache_dir") + @patch("tfbpapi.hf_cache_manager.scan_cache_dir") def test_clean_cache_by_size(self, mock_scan_cache_dir): """Test size-based cache cleaning.""" # Setup mock cache info @@ -238,7 +237,7 @@ def test_clean_cache_by_size(self, mock_scan_cache_dir): assert result == mock_delete_strategy mock_cache_info.delete_revisions.assert_called_once() - @patch("tfbpapi.HfCacheManager.scan_cache_dir") + @patch("tfbpapi.hf_cache_manager.scan_cache_dir") def test_clean_cache_by_size_already_under_target(self, mock_scan_cache_dir): """Test size-based cleaning when already under target.""" mock_cache_info = Mock() @@ -258,7 +257,7 @@ def test_clean_cache_by_size_already_under_target(self, mock_scan_cache_dir): assert result == mock_delete_strategy - @patch("tfbpapi.HfCacheManager.scan_cache_dir") + @patch("tfbpapi.hf_cache_manager.scan_cache_dir") def test_clean_unused_revisions(self, mock_scan_cache_dir): """Test cleaning unused revisions.""" # Setup mock with multiple revisions @@ -292,7 +291,7 @@ def test_clean_unused_revisions(self, mock_scan_cache_dir): # Should delete oldest revision (ghi789) mock_cache_info.delete_revisions.assert_called_once_with("ghi789") - @patch("tfbpapi.HfCacheManager.scan_cache_dir") + @patch("tfbpapi.hf_cache_manager.scan_cache_dir") def test_auto_clean_cache(self, mock_scan_cache_dir): """Test automated cache cleaning.""" mock_cache_info = Mock() @@ -336,7 +335,7 @@ class TestHfCacheManagerErrorHandling: def setup_method(self): """Set up test fixtures.""" - with patch("tfbpapi.HfCacheManager.DataCard.__init__", return_value=None): + with patch("tfbpapi.hf_cache_manager.DataCard.__init__", return_value=None): self.conn = duckdb.connect(":memory:") self.repo_id = "test/repo" self.cache_manager = HfCacheManager(self.repo_id, self.conn) @@ -346,7 +345,7 @@ def test_parse_size_string_invalid_input(self): with pytest.raises(ValueError): self.cache_manager._parse_size_string("invalid") - @patch("tfbpapi.HfCacheManager.scan_cache_dir") + @patch("tfbpapi.hf_cache_manager.scan_cache_dir") def test_clean_cache_invalid_strategy(self, mock_scan_cache_dir): """Test error handling for invalid cleanup strategy.""" mock_cache_info = Mock() @@ -367,7 +366,7 @@ class TestHfCacheManagerIntegration: def setup_method(self): """Set up test fixtures.""" - with patch("tfbpapi.HfCacheManager.DataCard.__init__", return_value=None): + with patch("tfbpapi.hf_cache_manager.DataCard.__init__", return_value=None): self.conn = duckdb.connect(":memory:") self.repo_id = "test/repo" self.cache_manager = HfCacheManager(self.repo_id, self.conn) diff --git a/tfbpapi/tests/datainfo/test_metadata_config_models.py b/tfbpapi/tests/test_metadata_config_models.py similarity index 75% rename from tfbpapi/tests/datainfo/test_metadata_config_models.py rename to tfbpapi/tests/test_metadata_config_models.py index 9a82598..09d6523 100644 --- a/tfbpapi/tests/datainfo/test_metadata_config_models.py +++ b/tfbpapi/tests/test_metadata_config_models.py @@ -11,7 +11,7 @@ import yaml from pydantic import ValidationError -from tfbpapi.datainfo.metadata_config_models import ( +from tfbpapi.models import ( MetadataConfig, PropertyMapping, RepositoryConfig, @@ -37,8 +37,7 @@ def test_invalid_empty_path(self): """Test that empty path is rejected.""" with pytest.raises(ValidationError) as exc_info: PropertyMapping(path="") - # Pydantic catches this with min_length=1 before our custom validator - assert "at least 1 character" in str(exc_info.value) + assert "path cannot be empty" in str(exc_info.value) def test_invalid_whitespace_path(self): """Test that whitespace-only path is rejected.""" @@ -57,6 +56,18 @@ def test_path_whitespace_stripped(self): mapping = PropertyMapping(path=" media.carbon_source ") assert mapping.path == "media.carbon_source" + def test_valid_field_only_mapping(self): + """Test valid field-only mapping (column alias).""" + mapping = PropertyMapping(field="condition") + assert mapping.field == "condition" + assert mapping.path is None + + def test_invalid_neither_field_nor_path(self): + """Test that at least one of field or path is required.""" + with pytest.raises(ValidationError) as exc_info: + PropertyMapping() + assert "At least one of 'field' or 'path' must be specified" in str(exc_info.value) + class TestRepositoryConfig: """Tests for RepositoryConfig model.""" @@ -91,27 +102,24 @@ def test_invalid_dataset_not_dict(self): RepositoryConfig.model_validate(config_data) assert "'dataset' key must contain a dict" in str(exc_info.value) - def test_invalid_property_missing_path(self): - """Test that properties must have 'path' field.""" + def test_valid_field_only_property(self): + """Test that field-only properties are valid (column aliases).""" config_data = { "dataset": {"dataset1": {"carbon_source": {"field": "condition"}}} } - with pytest.raises(ValidationError) as exc_info: - RepositoryConfig.model_validate(config_data) - # Pydantic's Field required error comes before our custom validation - assert "Field required" in str( - exc_info.value - ) or "missing required 'path' field" in str(exc_info.value) + config = RepositoryConfig.model_validate(config_data) + assert config.dataset is not None + assert "dataset1" in config.dataset + assert config.dataset["dataset1"]["carbon_source"].field == "condition" + assert config.dataset["dataset1"]["carbon_source"].path is None - def test_invalid_repo_wide_property_missing_path(self): - """Test that repo-wide properties must have 'path' field.""" - config_data = {"temperature_celsius": {"field": "something"}} - with pytest.raises(ValidationError) as exc_info: - RepositoryConfig.model_validate(config_data) - # Pydantic's Field required error comes before our custom validation - assert "Field required" in str( - exc_info.value - ) or "missing required 'path' field" in str(exc_info.value) + def test_valid_repo_wide_field_only_property(self): + """Test that repo-wide field-only properties are valid.""" + config_data = {"environmental_condition": {"field": "condition"}} + config = RepositoryConfig.model_validate(config_data) + assert "environmental_condition" in config.properties + assert config.properties["environmental_condition"].field == "condition" + assert config.properties["environmental_condition"].path is None class TestMetadataConfig: @@ -126,8 +134,10 @@ def test_valid_config_with_aliases(self, tmp_path): "galactose": ["D-galactose", "Galactose"], } }, - "BrentLab/test": { - "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + "repositories": { + "BrentLab/test": { + "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + } }, } @@ -146,8 +156,10 @@ def test_valid_config_with_aliases(self, tmp_path): def test_valid_config_without_aliases(self, tmp_path): """Test that factor_aliases is optional.""" config_data = { - "BrentLab/test": { - "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + "repositories": { + "BrentLab/test": { + "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + } } } @@ -162,8 +174,10 @@ def test_valid_config_empty_aliases(self, tmp_path): """Test that empty factor_aliases dict is allowed.""" config_data = { "factor_aliases": {}, - "BrentLab/test": { - "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + "repositories": { + "BrentLab/test": { + "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + } }, } @@ -180,7 +194,9 @@ def test_invalid_alias_not_dict(self): "factor_aliases": { "carbon_source": ["D-glucose"] # Should be dict, not list }, - "BrentLab/test": {"dataset": {"test": {"prop": {"path": "path"}}}}, + "repositories": { + "BrentLab/test": {"dataset": {"test": {"prop": {"path": "path"}}}} + }, } with pytest.raises(ValidationError) as exc_info: @@ -196,7 +212,9 @@ def test_invalid_alias_value_not_list(self): "factor_aliases": { "carbon_source": {"glucose": "D-glucose"} # Should be list, not string }, - "BrentLab/test": {"dataset": {"test": {"prop": {"path": "path"}}}}, + "repositories": { + "BrentLab/test": {"dataset": {"test": {"prop": {"path": "path"}}}} + }, } with pytest.raises(ValidationError) as exc_info: @@ -210,7 +228,9 @@ def test_invalid_alias_empty_list(self): """Test that alias value lists cannot be empty.""" config_data = { "factor_aliases": {"carbon_source": {"glucose": []}}, - "BrentLab/test": {"dataset": {"test": {"prop": {"path": "path"}}}}, + "repositories": { + "BrentLab/test": {"dataset": {"test": {"prop": {"path": "path"}}}} + }, } with pytest.raises(ValidationError) as exc_info: @@ -226,8 +246,10 @@ def test_aliases_allow_numeric_values(self): "thirty_seven": [37, 37.0], # Integer and float } }, - "BrentLab/test": { - "dataset": {"test": {"temperature": {"path": "temperature_celsius"}}} + "repositories": { + "BrentLab/test": { + "dataset": {"test": {"temperature": {"path": "temperature_celsius"}}} + } }, } @@ -243,18 +265,20 @@ def test_invalid_no_repositories(self): config_data = {"factor_aliases": {"carbon_source": {"glucose": ["D-glucose"]}}} with pytest.raises(ValidationError) as exc_info: MetadataConfig.model_validate(config_data) - assert "at least one repository configuration" in str(exc_info.value) + assert "at least one repository" in str(exc_info.value) def test_get_repository_config(self, tmp_path): """Test get_repository_config method.""" config_data = { "factor_aliases": {"carbon_source": {"glucose": ["D-glucose"]}}, - "BrentLab/harbison_2004": { - "dataset": { - "harbison_2004": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source", + "repositories": { + "BrentLab/harbison_2004": { + "dataset": { + "harbison_2004": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source", + } } } } @@ -282,11 +306,13 @@ def test_get_property_mappings(self, tmp_path): "carbon_source": {"glucose": ["D-glucose"]}, "temperature": {"thirty": [30]}, }, - "BrentLab/kemmeren_2014": { - "temperature": {"path": "temperature_celsius"}, # Repo-wide - "dataset": { - "kemmeren_2014": {"carbon_source": {"path": "media.carbon_source"}} - }, + "repositories": { + "BrentLab/kemmeren_2014": { + "temperature": {"path": "temperature_celsius"}, # Repo-wide + "dataset": { + "kemmeren_2014": {"carbon_source": {"path": "media.carbon_source"}} + }, + } }, } @@ -310,13 +336,15 @@ def test_get_property_mappings(self, tmp_path): def test_dataset_specific_overrides_repo_wide(self, tmp_path): """Test that dataset-specific mappings override repo-wide.""" config_data = { - "BrentLab/test": { - "carbon_source": {"path": "repo.level.path"}, # Repo-wide - "dataset": { - "test_dataset": { - "carbon_source": {"path": "dataset.level.path"} # Override - } - }, + "repositories": { + "BrentLab/test": { + "carbon_source": {"path": "repo.level.path"}, # Repo-wide + "dataset": { + "test_dataset": { + "carbon_source": {"path": "dataset.level.path"} # Override + } + }, + } }, } @@ -353,12 +381,14 @@ def test_nested_alias_property_names(self, tmp_path): "carbon_source.concentration_percent": {"two_percent": [2]}, "carbon_source.specifications": {"no_aa": ["without_amino_acids"]}, }, - "BrentLab/test": { - "dataset": { - "test": { - "carbon_source": { - "field": "condition", - "path": "media.carbon_source", + "repositories": { + "BrentLab/test": { + "dataset": { + "test": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source", + } } } } diff --git a/tfbpapi/tests/datainfo/test_models.py b/tfbpapi/tests/test_models.py similarity index 99% rename from tfbpapi/tests/datainfo/test_models.py rename to tfbpapi/tests/test_models.py index d4701e4..bf52f75 100644 --- a/tfbpapi/tests/datainfo/test_models.py +++ b/tfbpapi/tests/test_models.py @@ -8,7 +8,7 @@ import pytest from pydantic import ValidationError -from tfbpapi.datainfo.models import ( +from tfbpapi.models import ( DataFileInfo, DatasetCard, DatasetConfig, diff --git a/tfbpapi/tests/datainfo/test_real_datacards.py b/tfbpapi/tests/test_real_datacards.py similarity index 99% rename from tfbpapi/tests/datainfo/test_real_datacards.py rename to tfbpapi/tests/test_real_datacards.py index 0220797..cd07626 100644 --- a/tfbpapi/tests/datainfo/test_real_datacards.py +++ b/tfbpapi/tests/test_real_datacards.py @@ -11,7 +11,7 @@ import pytest import yaml -from tfbpapi.datainfo.models import DatasetCard +from tfbpapi.models import DatasetCard # Real datacard YAML strings from the collection BARKAI_COMPENDIUM = """ diff --git a/tfbpapi/tests/test_virtual_db.py b/tfbpapi/tests/test_virtual_db.py new file mode 100644 index 0000000..3728e3e --- /dev/null +++ b/tfbpapi/tests/test_virtual_db.py @@ -0,0 +1,507 @@ +""" +Tests for VirtualDB unified query interface. + +Tests configuration loading, schema discovery, querying, filtering, and caching. + +""" + +from pathlib import Path +import tempfile +import pytest +import pandas as pd +import yaml + +from tfbpapi.virtual_db import VirtualDB, get_nested_value, normalize_value + + +class TestHelperFunctions: + """Tests for helper functions.""" + + def test_get_nested_value_simple(self): + """Test simple nested dict navigation.""" + data = {"media": {"name": "YPD"}} + result = get_nested_value(data, "media.name") + assert result == "YPD" + + def test_get_nested_value_missing_key(self): + """Test that missing keys return None.""" + data = {"media": {"name": "YPD"}} + result = get_nested_value(data, "media.carbon_source") + assert result is None + + def test_get_nested_value_list_extraction(self): + """Test extracting property from list of dicts.""" + data = { + "media": { + "carbon_source": [ + {"compound": "glucose"}, + {"compound": "galactose"} + ] + } + } + result = get_nested_value(data, "media.carbon_source.compound") + assert result == ["glucose", "galactose"] + + def test_get_nested_value_non_dict(self): + """Test that non-dict input returns None.""" + result = get_nested_value("not a dict", "path") + assert result is None + + def test_normalize_value_exact_match(self): + """Test exact alias match.""" + aliases = {"glucose": ["D-glucose", "dextrose"]} + result = normalize_value("D-glucose", aliases) + assert result == "glucose" + + def test_normalize_value_case_insensitive(self): + """Test case-insensitive matching.""" + aliases = {"glucose": ["D-glucose", "dextrose"]} + result = normalize_value("DEXTROSE", aliases) + assert result == "glucose" + + def test_normalize_value_no_match(self): + """Test pass-through when no alias matches.""" + aliases = {"glucose": ["D-glucose"]} + result = normalize_value("maltose", aliases) + assert result == "maltose" + + def test_normalize_value_no_aliases(self): + """Test pass-through when no aliases provided.""" + result = normalize_value("D-glucose", None) + assert result == "D-glucose" + + def test_normalize_value_missing_value_label(self): + """Test missing value handling.""" + result = normalize_value(None, None, "unspecified") + assert result == "unspecified" + + def test_normalize_value_missing_value_no_label(self): + """Test missing value without label.""" + result = normalize_value(None, None) + assert result == "None" + + +class TestVirtualDBConfig: + """Tests for VirtualDB configuration loading.""" + + def create_test_config(self, **overrides): + """Helper to create test configuration file.""" + config = { + "factor_aliases": { + "carbon_source": { + "glucose": ["D-glucose", "dextrose"], + "galactose": ["D-galactose", "Galactose"] + } + }, + "missing_value_labels": { + "carbon_source": "unspecified" + }, + "description": { + "carbon_source": "Carbon source in growth media" + }, + "repositories": { + "BrentLab/test_repo": { + "temperature_celsius": {"path": "temperature_celsius"}, + "dataset": { + "test_dataset": { + "carbon_source": { + "field": "condition", + "path": "media.carbon_source.compound" + } + } + } + } + } + } + config.update(overrides) + return config + + def test_init_with_valid_config(self): + """Test VirtualDB initialization with valid config.""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + yaml.dump(self.create_test_config(), f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + assert vdb.config is not None + assert vdb.token is None + assert len(vdb.cache) == 0 + finally: + Path(config_path).unlink() + + def test_init_with_token(self): + """Test VirtualDB initialization with HF token.""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + yaml.dump(self.create_test_config(), f) + config_path = f.name + + try: + vdb = VirtualDB(config_path, token="test_token") + assert vdb.token == "test_token" + finally: + Path(config_path).unlink() + + def test_init_missing_config_file(self): + """Test error when config file doesn't exist.""" + with pytest.raises(FileNotFoundError): + VirtualDB("/nonexistent/path.yaml") + + def test_repr(self): + """Test string representation.""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + yaml.dump(self.create_test_config(), f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + repr_str = repr(vdb) + assert "VirtualDB" in repr_str + assert "1 repositories" in repr_str + assert "1 datasets configured" in repr_str + assert "0 views cached" in repr_str + finally: + Path(config_path).unlink() + + +class TestSchemaDiscovery: + """Tests for schema discovery methods.""" + + def create_multi_dataset_config(self): + """Create config with multiple datasets.""" + return { + "factor_aliases": {}, + "repositories": { + "BrentLab/repo1": { + "temperature_celsius": {"path": "temperature_celsius"}, + "dataset": { + "dataset1": { + "carbon_source": {"field": "condition", "path": "media.carbon_source"} + } + } + }, + "BrentLab/repo2": { + "nitrogen_source": {"path": "media.nitrogen_source"}, + "dataset": { + "dataset2": { + "carbon_source": {"path": "media.carbon_source"}, + "temperature_celsius": {"path": "temperature_celsius"} + } + } + } + } + } + + def test_get_fields_all_datasets(self): + """Test getting all fields across all datasets.""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + yaml.dump(self.create_multi_dataset_config(), f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + fields = vdb.get_fields() + assert "carbon_source" in fields + assert "temperature_celsius" in fields + assert "nitrogen_source" in fields + assert fields == sorted(fields) # Should be sorted + finally: + Path(config_path).unlink() + + def test_get_fields_specific_dataset(self): + """Test getting fields for specific dataset.""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + yaml.dump(self.create_multi_dataset_config(), f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + fields = vdb.get_fields("BrentLab/repo1", "dataset1") + assert "carbon_source" in fields + assert "temperature_celsius" in fields + # nitrogen_source is in repo2, not repo1 + assert "nitrogen_source" not in fields + finally: + Path(config_path).unlink() + + def test_get_fields_invalid_partial_args(self): + """Test error when only one of repo_id/config_name provided.""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + yaml.dump(self.create_multi_dataset_config(), f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + with pytest.raises(ValueError, match="Both repo_id and config_name"): + vdb.get_fields(repo_id="BrentLab/repo1") + finally: + Path(config_path).unlink() + + def test_get_common_fields(self): + """Test getting fields common to all datasets.""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + yaml.dump(self.create_multi_dataset_config(), f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + common = vdb.get_common_fields() + # Both datasets have carbon_source and temperature_celsius + assert "carbon_source" in common + assert "temperature_celsius" in common + # nitrogen_source is only in repo2 + assert "nitrogen_source" not in common + finally: + Path(config_path).unlink() + + def test_get_common_fields_empty_config(self): + """Test getting common fields with no repositories.""" + config = {"factor_aliases": {}} + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + # This will fail validation (needs at least one repo) + # So we skip this test + pass + + +class TestCaching: + """Tests for view materialization and caching.""" + + def create_simple_config(self): + """Create simple config for testing.""" + return { + "factor_aliases": {}, + "repositories": { + "BrentLab/test_repo": { + "dataset": { + "test_dataset": { + "carbon_source": {"path": "media.carbon_source"} + } + } + } + } + } + + def test_invalidate_cache_all(self): + """Test invalidating all cache.""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + yaml.dump(self.create_simple_config(), f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + # Manually add to cache + vdb.cache[("BrentLab/test_repo", "test_dataset")] = pd.DataFrame() + assert len(vdb.cache) == 1 + + vdb.invalidate_cache() + assert len(vdb.cache) == 0 + finally: + Path(config_path).unlink() + + def test_invalidate_cache_specific(self): + """Test invalidating specific dataset cache.""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + yaml.dump(self.create_simple_config(), f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + # Add multiple entries to cache + vdb.cache[("BrentLab/test_repo", "test_dataset")] = pd.DataFrame() + vdb.cache[("BrentLab/other_repo", "other_dataset")] = pd.DataFrame() + assert len(vdb.cache) == 2 + + vdb.invalidate_cache([("BrentLab/test_repo", "test_dataset")]) + assert len(vdb.cache) == 1 + assert ("BrentLab/other_repo", "other_dataset") in vdb.cache + finally: + Path(config_path).unlink() + + +class TestFiltering: + """Tests for filter application logic.""" + + def test_apply_filters_exact_match(self): + """Test exact value matching in filters.""" + df = pd.DataFrame({ + "sample_id": ["s1", "s2", "s3"], + "carbon_source": ["glucose", "galactose", "glucose"] + }) + + # Create minimal VirtualDB instance + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + config = { + "repositories": { + "BrentLab/test": { + "dataset": { + "test": {"carbon_source": {"path": "media.carbon_source"}} + } + } + } + } + yaml.dump(config, f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + filtered = vdb._apply_filters( + df, {"carbon_source": "glucose"}, "BrentLab/test", "test" + ) + assert len(filtered) == 2 + assert all(filtered["carbon_source"] == "glucose") + finally: + Path(config_path).unlink() + + def test_apply_filters_numeric_range(self): + """Test numeric range filtering.""" + df = pd.DataFrame({ + "sample_id": ["s1", "s2", "s3"], + "temperature_celsius": [25, 30, 37] + }) + + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + config = { + "repositories": { + "BrentLab/test": { + "dataset": { + "test": {"temperature_celsius": {"path": "temperature_celsius"}} + } + } + } + } + yaml.dump(config, f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + + # Test >= operator + filtered = vdb._apply_filters( + df, {"temperature_celsius": (">=", 30)}, "BrentLab/test", "test" + ) + assert len(filtered) == 2 + assert all(filtered["temperature_celsius"] >= 30) + + # Test between operator + filtered = vdb._apply_filters( + df, {"temperature_celsius": ("between", 28, 32)}, "BrentLab/test", "test" + ) + assert len(filtered) == 1 + assert filtered.iloc[0]["temperature_celsius"] == 30 + finally: + Path(config_path).unlink() + + def test_apply_filters_with_alias_expansion(self): + """Test filter with alias expansion.""" + df = pd.DataFrame({ + "sample_id": ["s1", "s2", "s3"], + "carbon_source": ["glucose", "D-glucose", "galactose"] + }) + + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + config = { + "factor_aliases": { + "carbon_source": { + "glucose": ["D-glucose", "dextrose", "glucose"] + } + }, + "repositories": { + "BrentLab/test": { + "dataset": { + "test": {"carbon_source": {"path": "media.carbon_source"}} + } + } + } + } + yaml.dump(config, f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + filtered = vdb._apply_filters( + df, {"carbon_source": "glucose"}, "BrentLab/test", "test" + ) + # Should match both "glucose" and "D-glucose" due to alias expansion + assert len(filtered) == 2 + finally: + Path(config_path).unlink() + + +class TestExtraction: + """Tests for metadata extraction methods.""" + + def test_add_field_metadata(self): + """Test adding field-level metadata to DataFrame.""" + df = pd.DataFrame({ + "sample_id": ["s1", "s2"], + "condition": ["YPD", "YPG"] + }) + + field_metadata = { + "YPD": { + "carbon_source": ["glucose"], + "growth_media": ["YPD"] + }, + "YPG": { + "carbon_source": ["glycerol"], + "growth_media": ["YPG"] + } + } + + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + config = { + "repositories": { + "BrentLab/test": { + "dataset": { + "test": {"carbon_source": {"path": "media.carbon_source"}} + } + } + } + } + yaml.dump(config, f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + result = vdb._add_field_metadata(df, field_metadata) + + assert "carbon_source" in result.columns + assert "growth_media" in result.columns + assert result.loc[result["condition"] == "YPD", "carbon_source"].iloc[0] == "glucose" + assert result.loc[result["condition"] == "YPG", "carbon_source"].iloc[0] == "glycerol" + finally: + Path(config_path).unlink() + + +class TestQuery: + """Tests for query method - requires mocking HfQueryAPI.""" + + def test_query_empty_result(self): + """Test query with no matching datasets.""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + config = { + "repositories": { + "BrentLab/test": { + "dataset": { + "test": {"carbon_source": {"path": "media.carbon_source"}} + } + } + } + } + yaml.dump(config, f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + # Query with non-configured dataset should return empty + result = vdb.query(datasets=[("BrentLab/other", "other")]) + assert isinstance(result, pd.DataFrame) + assert result.empty + finally: + Path(config_path).unlink() + + +# Note: Full integration tests with real HuggingFace datasets would go here +# but are excluded as they require network access and specific test datasets. +# These tests cover the core logic and would be supplemented with integration +# tests using the actual sample config and real datasets like harbison_2004. diff --git a/tfbpapi/virtual_db.py b/tfbpapi/virtual_db.py new file mode 100644 index 0000000..245bf18 --- /dev/null +++ b/tfbpapi/virtual_db.py @@ -0,0 +1,796 @@ +""" +VirtualDB provides a unified query interface across heterogeneous datasets. + +This module enables cross-dataset queries with standardized field names and values, +mapping varying experimental condition structures to a common schema through external +YAML configuration. + +Key Components: +- VirtualDB: Main interface for unified cross-dataset queries +- Helper functions: get_nested_value(), normalize_value() for metadata extraction +- Configuration-driven schema via models.MetadataConfig + +Example Usage: + >>> from tfbpapi.datainfo import VirtualDB + >>> vdb = VirtualDB("config.yaml") + >>> + >>> # Discover available fields + >>> fields = vdb.get_fields() + >>> print(fields) # ["carbon_source", "temperature_celsius", ...] + >>> + >>> # Query across datasets + >>> df = vdb.query( + ... filters={"carbon_source": "glucose", "temperature_celsius": 30}, + ... fields=["sample_id", "carbon_source", "temperature_celsius"] + ... ) + >>> + >>> # Get complete data with measurements + >>> df = vdb.query( + ... filters={"carbon_source": "glucose"}, + ... complete=True + ... ) + +""" + +from __future__ import annotations + +from pathlib import Path +from typing import Any + +import duckdb +import pandas as pd + +from tfbpapi.datacard import DataCard +from tfbpapi.errors import DataCardError +from tfbpapi.hf_cache_manager import HfCacheManager +from tfbpapi.models import MetadataConfig, PropertyMapping + + +def get_nested_value(data: dict, path: str) -> Any: + """ + Navigate nested dict/list using dot notation. + + Handles missing intermediate keys gracefully by returning None. + Supports extracting properties from lists of dicts. + + :param data: Dictionary to navigate + :param path: Dot-separated path (e.g., "media.carbon_source.compound") + :return: Value at path or None if not found + + Examples: + Simple nested dict: + get_nested_value({"media": {"name": "YPD"}}, "media.name") + Returns: "YPD" + + List of dicts - extract property from each item: + get_nested_value( + {"media": {"carbon_source": [{"compound": "glucose"}, {"compound": "galactose"}]}}, + "media.carbon_source.compound" + ) + Returns: ["glucose", "galactose"] + + """ + if not isinstance(data, dict): + return None + + keys = path.split(".") + current = data + + for i, key in enumerate(keys): + if isinstance(current, dict): + if key not in current: + return None + current = current[key] + elif isinstance(current, list): + # If current is a list and we have more keys, extract property from each item + if i < len(keys): + # Extract the remaining path from each list item + remaining_path = ".".join(keys[i:]) + results = [] + for item in current: + if isinstance(item, dict): + val = get_nested_value(item, remaining_path) + if val is not None: + results.append(val) + return results if results else None + else: + return None + + return current + + +def normalize_value( + actual_value: Any, + aliases: dict[str, list[Any]] | None, + missing_value_label: str | None = None, +) -> str: + """ + Normalize a value using optional alias mappings (case-insensitive). + + Returns the alias name if a match is found, otherwise returns the + original value as a string. Handles missing values by returning + the configured missing_value_label. + + :param actual_value: The value from the data to normalize + :param aliases: Optional dict mapping alias names to lists of actual values. + Example: {"glucose": ["D-glucose", "dextrose"]} + :param missing_value_label: Label to use for None/missing values + :return: Alias name if match found, missing_value_label if None, + otherwise str(actual_value) + + Examples: + With aliases - exact match: + normalize_value("D-glucose", {"glucose": ["D-glucose", "dextrose"]}) + Returns: "glucose" + + With aliases - case-insensitive match: + normalize_value("DEXTROSE", {"glucose": ["D-glucose", "dextrose"]}) + Returns: "glucose" + + Missing value: + normalize_value(None, None, "unspecified") + Returns: "unspecified" + + No alias match - pass through: + normalize_value("maltose", {"glucose": ["D-glucose"]}) + Returns: "maltose" + + """ + # Handle None/missing values + if actual_value is None: + return missing_value_label if missing_value_label else "None" + + if aliases is None: + return str(actual_value) + + # Convert to string for comparison (case-insensitive) + actual_str = str(actual_value).lower() + + # Check each alias mapping + for alias_name, actual_values in aliases.items(): + for val in actual_values: + if str(val).lower() == actual_str: + return alias_name + + # No match found - pass through original value + return str(actual_value) + + +class VirtualDB: + """ + Unified query interface across heterogeneous datasets. + + VirtualDB provides a virtual database layer over multiple HuggingFace datasets, + allowing cross-dataset queries with standardized field names and normalized values. + Each configured dataset becomes a view with a common schema defined by external + YAML configuration. + + The YAML configuration specifies: + 1. Property mappings: How to extract each field from dataset structures + 2. Factor aliases: Normalize varying terminologies to standard values + 3. Missing value labels: Handle missing data consistently + 4. Descriptions: Document each field's semantics + + Attributes: + config: MetadataConfig instance with all configuration + token: Optional HuggingFace token for private datasets + cache: Dict mapping (repo_id, config_name) to cached DataFrame views + + """ + + def __init__(self, config_path: Path | str, token: str | None = None): + """ + Initialize VirtualDB with configuration and optional auth token. + + :param config_path: Path to YAML configuration file + :param token: Optional HuggingFace token for private datasets + :raises FileNotFoundError: If config file doesn't exist + :raises ValueError: If configuration is invalid + + """ + self.config = MetadataConfig.from_yaml(config_path) + self.token = token + self.cache: dict[tuple[str, str], pd.DataFrame] = {} + + def get_fields( + self, repo_id: str | None = None, config_name: str | None = None + ) -> list[str]: + """ + Get list of queryable fields. + + :param repo_id: Optional repository ID to filter to specific dataset + :param config_name: Optional config name (required if repo_id provided) + :return: List of field names + + Examples: + All fields across all datasets: + fields = vdb.get_fields() + + Fields for specific dataset: + fields = vdb.get_fields("BrentLab/harbison_2004", "harbison_2004") + + """ + if repo_id is not None and config_name is not None: + # Get fields for specific dataset + mappings = self.config.get_property_mappings(repo_id, config_name) + return sorted(mappings.keys()) + + if repo_id is not None or config_name is not None: + raise ValueError("Both repo_id and config_name must be provided, or neither") + + # Get all fields across all datasets + all_fields: set[str] = set() + for repo_id, repo_config in self.config.repositories.items(): + # Add repo-wide fields + all_fields.update(repo_config.properties.keys()) + # Add dataset-specific fields + if repo_config.dataset: + for dataset_props in repo_config.dataset.values(): + all_fields.update(dataset_props.keys()) + + return sorted(all_fields) + + def get_common_fields(self) -> list[str]: + """ + Get fields present in ALL configured datasets. + + :return: List of field names common to all datasets + + Example: + common = vdb.get_common_fields() + # ["carbon_source", "temperature_celsius"] + + """ + if not self.config.repositories: + return [] + + # Get field sets for each dataset + dataset_fields: list[set[str]] = [] + for repo_id, repo_config in self.config.repositories.items(): + if repo_config.dataset: + for config_name in repo_config.dataset.keys(): + mappings = self.config.get_property_mappings(repo_id, config_name) + dataset_fields.append(set(mappings.keys())) + + if not dataset_fields: + return [] + + # Return intersection + common = set.intersection(*dataset_fields) + return sorted(common) + + def get_unique_values( + self, field: str, by_dataset: bool = False + ) -> list[str] | dict[str, list[str]]: + """ + Get unique values for a field across datasets (with normalization). + + :param field: Field name to get values for + :param by_dataset: If True, return dict keyed by dataset identifier + :return: List of unique normalized values, or dict if by_dataset=True + + Examples: + All unique values: + values = vdb.get_unique_values("carbon_source") + # ["glucose", "galactose", "raffinose"] + + Values by dataset: + values = vdb.get_unique_values("carbon_source", by_dataset=True) + # {"BrentLab/harbison_2004": ["glucose", "galactose"], + # "BrentLab/kemmeren_2014": ["glucose", "raffinose"]} + + """ + if by_dataset: + result: dict[str, list[str]] = {} + else: + all_values: set[str] = set() + + # Query each dataset that has this field + for repo_id, repo_config in self.config.repositories.items(): + if repo_config.dataset: + for config_name in repo_config.dataset.keys(): + mappings = self.config.get_property_mappings(repo_id, config_name) + if field not in mappings: + continue + + # Build metadata table for this dataset + metadata_df = self._build_metadata_table(repo_id, config_name) + if metadata_df.empty or field not in metadata_df.columns: + continue + + # Get unique values (already normalized) + unique_vals = metadata_df[field].dropna().unique().tolist() + + if by_dataset: + dataset_key = f"{repo_id}/{config_name}" + result[dataset_key] = sorted(unique_vals) + else: + all_values.update(unique_vals) + + if by_dataset: + return result + else: + return sorted(all_values) + + def query( + self, + filters: dict[str, Any] | None = None, + datasets: list[tuple[str, str]] | None = None, + fields: list[str] | None = None, + complete: bool = False, + ) -> pd.DataFrame: + """ + Query VirtualDB with optional filters and field selection. + + :param filters: Dict of field:value pairs to filter on + :param datasets: List of (repo_id, config_name) tuples to query (None = all) + :param fields: List of field names to return (None = all) + :param complete: If True, return measurement-level data; if False, sample-level + :return: DataFrame with query results + + Examples: + Basic query across all datasets: + df = vdb.query(filters={"carbon_source": "glucose"}) + + Query specific datasets with field selection: + df = vdb.query( + filters={"carbon_source": "glucose", "temperature_celsius": 30}, + datasets=[("BrentLab/harbison_2004", "harbison_2004")], + fields=["sample_id", "carbon_source", "temperature_celsius"] + ) + + Complete data with measurements: + df = vdb.query( + filters={"carbon_source": "glucose"}, + complete=True + ) + + """ + # Determine which datasets to query + if datasets is None: + # Query all configured datasets + datasets = [] + for repo_id, repo_config in self.config.repositories.items(): + if repo_config.dataset: + for config_name in repo_config.dataset.keys(): + datasets.append((repo_id, config_name)) + + if not datasets: + return pd.DataFrame() + + # Query each dataset + results: list[pd.DataFrame] = [] + for repo_id, config_name in datasets: + # Build metadata table + metadata_df = self._build_metadata_table(repo_id, config_name) + if metadata_df.empty: + continue + + # Apply filters + if filters: + metadata_df = self._apply_filters(metadata_df, filters, repo_id, config_name) + + # If complete=True, join with full data + if complete: + sample_ids = metadata_df["sample_id"].tolist() + if sample_ids: + full_df = self._get_complete_data( + repo_id, config_name, sample_ids, metadata_df + ) + if not full_df.empty: + metadata_df = full_df + + # Select requested fields + if fields: + # Keep sample_id and any dataset identifier columns + keep_cols = ["sample_id"] + if "dataset_id" in metadata_df.columns: + keep_cols.append("dataset_id") + # Add requested fields that exist + for field in fields: + if field in metadata_df.columns and field not in keep_cols: + keep_cols.append(field) + metadata_df = metadata_df[keep_cols] + + # Add dataset identifier + if "dataset_id" not in metadata_df.columns: + metadata_df["dataset_id"] = f"{repo_id}/{config_name}" + + results.append(metadata_df) + + if not results: + return pd.DataFrame() + + # Concatenate results, filling NaN for missing columns + return pd.concat(results, ignore_index=True, sort=False) + + def materialize_views( + self, datasets: list[tuple[str, str]] | None = None + ) -> None: + """ + Build and cache metadata DataFrames for faster subsequent queries. + + :param datasets: List of (repo_id, config_name) tuples to materialize + (None = materialize all) + + Example: + vdb.materialize_views() # Cache all datasets + vdb.materialize_views([("BrentLab/harbison_2004", "harbison_2004")]) + + """ + if datasets is None: + # Materialize all configured datasets + datasets = [] + for repo_id, repo_config in self.config.repositories.items(): + if repo_config.dataset: + for config_name in repo_config.dataset.keys(): + datasets.append((repo_id, config_name)) + + for repo_id, config_name in datasets: + # Build and cache + self._build_metadata_table(repo_id, config_name, use_cache=False) + + def invalidate_cache( + self, datasets: list[tuple[str, str]] | None = None + ) -> None: + """ + Clear cached metadata DataFrames. + + :param datasets: List of (repo_id, config_name) tuples to invalidate + (None = invalidate all) + + Example: + vdb.invalidate_cache() # Clear all cache + vdb.invalidate_cache([("BrentLab/harbison_2004", "harbison_2004")]) + + """ + if datasets is None: + self.cache.clear() + else: + for dataset_key in datasets: + if dataset_key in self.cache: + del self.cache[dataset_key] + + def _build_metadata_table( + self, repo_id: str, config_name: str, use_cache: bool = True + ) -> pd.DataFrame: + """ + Build metadata table for a single dataset. + + Extracts sample-level metadata from experimental conditions hierarchy + and field definitions, with normalization and missing value handling. + + :param repo_id: Repository ID + :param config_name: Configuration name + :param use_cache: Whether to use/update cache + :return: DataFrame with one row per sample_id + + """ + cache_key = (repo_id, config_name) + + # Check cache + if use_cache and cache_key in self.cache: + return self.cache[cache_key] + + try: + # Load DataCard and CacheManager + card = DataCard(repo_id, token=self.token) + cache_mgr = HfCacheManager( + repo_id, duckdb_conn=duckdb.connect(":memory:"), token=self.token + ) + + # Get property mappings + property_mappings = self.config.get_property_mappings(repo_id, config_name) + if not property_mappings: + return pd.DataFrame() + + # Extract repo/config-level metadata + repo_metadata = self._extract_repo_level(card, config_name, property_mappings) + + # Extract field-level metadata + field_metadata = self._extract_field_level(card, config_name, property_mappings) + + # Get sample-level data from HuggingFace + config = card.get_config(config_name) + if config and hasattr(config, "metadata_fields") and config.metadata_fields: + # Select only metadata fields + columns = ", ".join(config.metadata_fields) + if "sample_id" not in config.metadata_fields: + columns = f"sample_id, {columns}" + sql = f"SELECT DISTINCT {columns} FROM {config_name}" + else: + # No metadata_fields specified, select all + sql = f"SELECT DISTINCT * FROM {config_name}" + + df = cache_mgr.query(sql, config_name) + + # One row per sample_id + if "sample_id" in df.columns: + df = df.groupby("sample_id").first().reset_index() + + # Add repo-level metadata as columns + for prop_name, values in repo_metadata.items(): + # Use first value (repo-level properties are constant) + df[prop_name] = values[0] if values else None + + # Add field-level metadata + if field_metadata: + df = self._add_field_metadata(df, field_metadata) + + # Cache result + if use_cache: + self.cache[cache_key] = df + + return df + + except Exception as e: + # Return empty DataFrame on error + return pd.DataFrame() + + def _extract_repo_level( + self, + card: DataCard, + config_name: str, + property_mappings: dict[str, PropertyMapping], + ) -> dict[str, list[str]]: + """ + Extract and normalize repo/config-level metadata. + + :param card: DataCard instance + :param config_name: Configuration name + :param property_mappings: Property mappings for this dataset + :return: Dict mapping property names to normalized values + + """ + metadata: dict[str, list[str]] = {} + + # Get experimental conditions + try: + conditions = card.get_experimental_conditions(config_name) + except DataCardError: + conditions = {} + + if not conditions: + return metadata + + # Extract each mapped property + for prop_name, mapping in property_mappings.items(): + # Skip field-level mappings + if mapping.field is not None: + continue + + # Build full path + full_path = f"experimental_conditions.{mapping.path}" + + # Get value at path + value = get_nested_value(conditions, full_path) + + # Handle missing values + missing_label = self.config.missing_value_labels.get(prop_name) + if value is None: + if missing_label: + metadata[prop_name] = [missing_label] + continue + + # Ensure value is a list + actual_values = [value] if not isinstance(value, list) else value + + # Normalize using aliases + aliases = self.config.factor_aliases.get(prop_name) + normalized_values = [ + normalize_value(v, aliases, missing_label) for v in actual_values + ] + + metadata[prop_name] = normalized_values + + return metadata + + def _extract_field_level( + self, + card: DataCard, + config_name: str, + property_mappings: dict[str, PropertyMapping], + ) -> dict[str, dict[str, Any]]: + """ + Extract and normalize field-level metadata. + + :param card: DataCard instance + :param config_name: Configuration name + :param property_mappings: Property mappings for this dataset + :return: Dict mapping field values to their normalized metadata + + """ + field_metadata: dict[str, dict[str, Any]] = {} + + # Group property mappings by field + field_mappings: dict[str, dict[str, str]] = {} + for prop_name, mapping in property_mappings.items(): + if mapping.field is not None: + field_name = mapping.field + if field_name not in field_mappings: + field_mappings[field_name] = {} + field_mappings[field_name][prop_name] = mapping.path + + # Process each field that has mappings + for field_name, prop_paths in field_mappings.items(): + # Get field definitions + definitions = card.get_field_definitions(config_name, field_name) + if not definitions: + continue + + # Extract metadata for each field value + for field_value, definition in definitions.items(): + if field_value not in field_metadata: + field_metadata[field_value] = {} + + for prop_name, path in prop_paths.items(): + # Get value at path + value = get_nested_value(definition, path) + + # Handle missing values + missing_label = self.config.missing_value_labels.get(prop_name) + if value is None: + if missing_label: + field_metadata[field_value][prop_name] = [missing_label] + continue + + # Ensure value is a list + actual_values = [value] if not isinstance(value, list) else value + + # Normalize using aliases + aliases = self.config.factor_aliases.get(prop_name) + normalized_values = [ + normalize_value(v, aliases, missing_label) for v in actual_values + ] + + field_metadata[field_value][prop_name] = normalized_values + + return field_metadata + + def _add_field_metadata( + self, df: pd.DataFrame, field_metadata: dict[str, dict[str, Any]] + ) -> pd.DataFrame: + """ + Add columns from field-level metadata to DataFrame. + + :param df: DataFrame with base sample metadata + :param field_metadata: Dict mapping field values to their properties + :return: DataFrame with additional property columns + + """ + # For each field value, add its properties as columns + for field_value, properties in field_metadata.items(): + for prop_name, prop_values in properties.items(): + # Initialize column if needed + if prop_name not in df.columns: + df[prop_name] = None + + # Find rows where any column matches field_value + for col in df.columns: + if col in [prop_name, "sample_id", "dataset_id"]: + continue + mask = df[col] == field_value + if mask.any(): + # Set property value (take first from list) + value = prop_values[0] if prop_values else None + df.loc[mask, prop_name] = value + + return df + + def _apply_filters( + self, + df: pd.DataFrame, + filters: dict[str, Any], + repo_id: str, + config_name: str, + ) -> pd.DataFrame: + """ + Apply filters to DataFrame with alias expansion and numeric handling. + + :param df: DataFrame to filter + :param filters: Dict of field:value pairs + :param repo_id: Repository ID (for alias lookup) + :param config_name: Config name (for alias lookup) + :return: Filtered DataFrame + + """ + for field, filter_value in filters.items(): + if field not in df.columns: + continue + + # Handle numeric range filters + if isinstance(filter_value, tuple): + operator = filter_value[0] + if operator == "between" and len(filter_value) == 3: + df = df[(df[field] >= filter_value[1]) & (df[field] <= filter_value[2])] + elif operator in (">=", ">", "<=", "<", "==", "!="): + if operator == ">=": + df = df[df[field] >= filter_value[1]] + elif operator == ">": + df = df[df[field] > filter_value[1]] + elif operator == "<=": + df = df[df[field] <= filter_value[1]] + elif operator == "<": + df = df[df[field] < filter_value[1]] + elif operator == "==": + df = df[df[field] == filter_value[1]] + elif operator == "!=": + df = df[df[field] != filter_value[1]] + else: + # Exact match with alias expansion + aliases = self.config.factor_aliases.get(field) + if aliases: + # Expand filter value to all aliases + expanded_values = [filter_value] + for alias_name, actual_values in aliases.items(): + if alias_name == filter_value: + # Add all actual values for this alias + expanded_values.extend([str(v) for v in actual_values]) + df = df[df[field].isin(expanded_values)] + else: + # No aliases, exact match + df = df[df[field] == filter_value] + + return df + + def _get_complete_data( + self, + repo_id: str, + config_name: str, + sample_ids: list[str], + metadata_df: pd.DataFrame, + ) -> pd.DataFrame: + """ + Get complete data (with measurements) for sample_ids. + + Uses WHERE sample_id IN (...) approach for efficient retrieval. + + :param repo_id: Repository ID + :param config_name: Configuration name + :param sample_ids: List of sample IDs to retrieve + :param metadata_df: Metadata DataFrame to merge with + :return: DataFrame with measurements and metadata + + """ + try: + cache_mgr = HfCacheManager( + repo_id, duckdb_conn=duckdb.connect(":memory:"), token=self.token + ) + + # Build IN clause + sample_id_list = ", ".join([f"'{sid}'" for sid in sample_ids]) + sql = f""" + SELECT * + FROM {config_name} + WHERE sample_id IN ({sample_id_list}) + """ + + full_df = cache_mgr.query(sql, config_name) + + # Merge with metadata (metadata_df has normalized fields) + # Drop metadata columns from full_df to avoid duplicates + metadata_cols = [ + col for col in metadata_df.columns if col not in ["sample_id", "dataset_id"] + ] + full_df = full_df.drop(columns=[c for c in metadata_cols if c in full_df.columns], errors="ignore") + + # Merge on sample_id + result = full_df.merge(metadata_df, on="sample_id", how="left") + + return result + + except Exception: + return pd.DataFrame() + + def __repr__(self) -> str: + """String representation.""" + n_repos = len(self.config.repositories) + n_datasets = sum( + len(rc.dataset) if rc.dataset else 0 + for rc in self.config.repositories.values() + ) + n_cached = len(self.cache) + return ( + f"VirtualDB({n_repos} repositories, {n_datasets} datasets configured, " + f"{n_cached} views cached)" + ) From b1fcb1fd9301dbd478119e1ed1c9d51af3dc45eb Mon Sep 17 00:00:00 2001 From: chasem Date: Thu, 18 Dec 2025 12:04:46 -0600 Subject: [PATCH 48/49] tmp --- docs/tutorials/virtual_db_tutorial.ipynb | 626 +++++++++++++++++++++-- 1 file changed, 586 insertions(+), 40 deletions(-) diff --git a/docs/tutorials/virtual_db_tutorial.ipynb b/docs/tutorials/virtual_db_tutorial.ipynb index 14c613e..2ea0f72 100644 --- a/docs/tutorials/virtual_db_tutorial.ipynb +++ b/docs/tutorials/virtual_db_tutorial.ipynb @@ -67,7 +67,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Configuration saved to: /tmp/tmpf7f_eml_/vdb_config.yaml\n" + "Configuration saved to: /tmp/tmpi90ps0mq/vdb_config.yaml\n" ] } ], @@ -130,17 +130,11 @@ "metadata": {}, "outputs": [ { - "ename": "ValidationError", - "evalue": "1 validation error for MetadataConfig\n Value error, Invalid configuration for repository 'repositories': 1 validation error for RepositoryConfig\n Value error, Invalid repo-wide property 'BrentLab/harbison_2004': 1 validation error for PropertyMapping\npath\n Field required [type=missing, input_value={'dataset': {'harbison_20...'field': 'condition'}}}}, input_type=dict]\n For further information visit https://errors.pydantic.dev/2.12/v/missing [type=value_error, input_value={'BrentLab/harbison_2004'...emperature_celsius'}}}}}, input_type=dict]\n For further information visit https://errors.pydantic.dev/2.12/v/value_error [type=value_error, input_value={'repositories': {'BrentL...vironmental condition'}}, input_type=dict]\n For further information visit https://errors.pydantic.dev/2.12/v/value_error", - "output_type": "error", - "traceback": [ - "\u001b[31m---------------------------------------------------------------------------\u001b[39m", - "\u001b[31mValidationError\u001b[39m Traceback (most recent call last)", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[2]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;66;03m# Initialize VirtualDB with the configuration\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m vdb = \u001b[43mVirtualDB\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mstr\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mtemp_config\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 4\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33mVirtualDB initialized successfully!\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 5\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mConfigured repositories: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mlen\u001b[39m(vdb.config.repositories)\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/code/tfbp/tfbpapi/tfbpapi/virtual_db.py:191\u001b[39m, in \u001b[36mVirtualDB.__init__\u001b[39m\u001b[34m(self, config_path, token)\u001b[39m\n\u001b[32m 181\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__init__\u001b[39m(\u001b[38;5;28mself\u001b[39m, config_path: Path | \u001b[38;5;28mstr\u001b[39m, token: \u001b[38;5;28mstr\u001b[39m | \u001b[38;5;28;01mNone\u001b[39;00m = \u001b[38;5;28;01mNone\u001b[39;00m):\n\u001b[32m 182\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m 183\u001b[39m \u001b[33;03m Initialize VirtualDB with configuration and optional auth token.\u001b[39;00m\n\u001b[32m 184\u001b[39m \n\u001b[32m (...)\u001b[39m\u001b[32m 189\u001b[39m \n\u001b[32m 190\u001b[39m \u001b[33;03m \"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m191\u001b[39m \u001b[38;5;28mself\u001b[39m.config = \u001b[43mMetadataConfig\u001b[49m\u001b[43m.\u001b[49m\u001b[43mfrom_yaml\u001b[49m\u001b[43m(\u001b[49m\u001b[43mconfig_path\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 192\u001b[39m \u001b[38;5;28mself\u001b[39m.token = token\n\u001b[32m 193\u001b[39m \u001b[38;5;28mself\u001b[39m.cache: \u001b[38;5;28mdict\u001b[39m[\u001b[38;5;28mtuple\u001b[39m[\u001b[38;5;28mstr\u001b[39m, \u001b[38;5;28mstr\u001b[39m], pd.DataFrame] = {}\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/code/tfbp/tfbpapi/tfbpapi/models.py:531\u001b[39m, in \u001b[36mMetadataConfig.from_yaml\u001b[39m\u001b[34m(cls, path)\u001b[39m\n\u001b[32m 528\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(data, \u001b[38;5;28mdict\u001b[39m):\n\u001b[32m 529\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[33m\"\u001b[39m\u001b[33mConfiguration must be a YAML dict\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m--> \u001b[39m\u001b[32m531\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mcls\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mmodel_validate\u001b[49m\u001b[43m(\u001b[49m\u001b[43mdata\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/pydantic/main.py:716\u001b[39m, in \u001b[36mBaseModel.model_validate\u001b[39m\u001b[34m(cls, obj, strict, extra, from_attributes, context, by_alias, by_name)\u001b[39m\n\u001b[32m 710\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m by_alias \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mFalse\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m by_name \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mTrue\u001b[39;00m:\n\u001b[32m 711\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m PydanticUserError(\n\u001b[32m 712\u001b[39m \u001b[33m'\u001b[39m\u001b[33mAt least one of `by_alias` or `by_name` must be set to True.\u001b[39m\u001b[33m'\u001b[39m,\n\u001b[32m 713\u001b[39m code=\u001b[33m'\u001b[39m\u001b[33mvalidate-by-alias-and-name-false\u001b[39m\u001b[33m'\u001b[39m,\n\u001b[32m 714\u001b[39m )\n\u001b[32m--> \u001b[39m\u001b[32m716\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mcls\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m__pydantic_validator__\u001b[49m\u001b[43m.\u001b[49m\u001b[43mvalidate_python\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 717\u001b[39m \u001b[43m \u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 718\u001b[39m \u001b[43m \u001b[49m\u001b[43mstrict\u001b[49m\u001b[43m=\u001b[49m\u001b[43mstrict\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 719\u001b[39m \u001b[43m \u001b[49m\u001b[43mextra\u001b[49m\u001b[43m=\u001b[49m\u001b[43mextra\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 720\u001b[39m \u001b[43m \u001b[49m\u001b[43mfrom_attributes\u001b[49m\u001b[43m=\u001b[49m\u001b[43mfrom_attributes\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 721\u001b[39m \u001b[43m \u001b[49m\u001b[43mcontext\u001b[49m\u001b[43m=\u001b[49m\u001b[43mcontext\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 722\u001b[39m \u001b[43m \u001b[49m\u001b[43mby_alias\u001b[49m\u001b[43m=\u001b[49m\u001b[43mby_alias\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 723\u001b[39m \u001b[43m \u001b[49m\u001b[43mby_name\u001b[49m\u001b[43m=\u001b[49m\u001b[43mby_name\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 724\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[31mValidationError\u001b[39m: 1 validation error for MetadataConfig\n Value error, Invalid configuration for repository 'repositories': 1 validation error for RepositoryConfig\n Value error, Invalid repo-wide property 'BrentLab/harbison_2004': 1 validation error for PropertyMapping\npath\n Field required [type=missing, input_value={'dataset': {'harbison_20...'field': 'condition'}}}}, input_type=dict]\n For further information visit https://errors.pydantic.dev/2.12/v/missing [type=value_error, input_value={'BrentLab/harbison_2004'...emperature_celsius'}}}}}, input_type=dict]\n For further information visit https://errors.pydantic.dev/2.12/v/value_error [type=value_error, input_value={'repositories': {'BrentL...vironmental condition'}}, input_type=dict]\n For further information visit https://errors.pydantic.dev/2.12/v/value_error" + "name": "stdout", + "output_type": "stream", + "text": [ + "VirtualDB initialized successfully!\n", + "Configured repositories: 2\n" ] } ], @@ -163,9 +157,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "All available fields:\n", + " - carbon_source\n", + " - environmental_condition\n", + " - temperature_celsius\n" + ] + } + ], "source": [ "# Get all fields defined in any dataset\n", "all_fields = vdb.get_fields()\n", @@ -177,9 +182,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Common fields (present in all datasets):\n", + " - carbon_source\n", + " - temperature_celsius\n" + ] + } + ], "source": [ "# Get fields present in ALL datasets (common fields)\n", "common_fields = vdb.get_common_fields()\n", @@ -191,9 +206,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Fields in harbison_2004:\n", + " - carbon_source\n", + " - environmental_condition\n", + " - temperature_celsius\n" + ] + } + ], "source": [ "# Get fields for a specific dataset\n", "harbison_fields = vdb.get_fields(\"BrentLab/harbison_2004\", \"harbison_2004\")\n", @@ -214,9 +240,32 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Fetching 1 files: 100%|██████████| 1/1 [00:09<00:00, 9.60s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Unique carbon sources (normalized):\n", + " - unspecified\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], "source": [ "# Get all unique values for a field (normalized)\n", "carbon_sources = vdb.get_unique_values(\"carbon_source\")\n", @@ -228,9 +277,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Carbon sources by dataset:\n", + "\n", + "BrentLab/kemmeren_2014/kemmeren_2014:\n", + " - unspecified\n" + ] + } + ], "source": [ "# Get values broken down by dataset\n", "carbon_by_dataset = vdb.get_unique_values(\"carbon_source\", by_dataset=True)\n", @@ -262,9 +322,104 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 0 samples with glucose\n", + "\n", + "Columns: ['sample_id', 'regulator_locus_tag', 'regulator_symbol', 'carbon_source', 'dataset_id']\n", + "\n", + "First few rows:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "sample_id", + "rawType": "int32", + "type": "integer" + }, + { + "name": "regulator_locus_tag", + "rawType": "object", + "type": "string" + }, + { + "name": "regulator_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "carbon_source", + "rawType": "object", + "type": "string" + }, + { + "name": "dataset_id", + "rawType": "object", + "type": "string" + } + ], + "ref": "57bb029a-d940-4e06-ae20-6eeae41e836a", + "rows": [], + "shape": { + "columns": 5, + "rows": 0 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sample_idregulator_locus_tagregulator_symbolcarbon_sourcedataset_id
\n", + "
" + ], + "text/plain": [ + "Empty DataFrame\n", + "Columns: [sample_id, regulator_locus_tag, regulator_symbol, carbon_source, dataset_id]\n", + "Index: []" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Query all datasets for samples grown on glucose\n", "glucose_samples = vdb.query(filters={\"carbon_source\": \"glucose\"})\n", @@ -286,9 +441,70 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 0 samples from harbison_2004\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + } + ], + "ref": "a0d7c9a2-a3e0-4146-86ec-12e51a2e6968", + "rows": [], + "shape": { + "columns": 0, + "rows": 0 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
" + ], + "text/plain": [ + "Empty DataFrame\n", + "Columns: []\n", + "Index: []" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Query only harbison_2004\n", "harbison_glucose = vdb.query(\n", @@ -311,9 +527,88 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Columns: ['sample_id', 'carbon_source', 'dataset_id']\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "sample_id", + "rawType": "int32", + "type": "integer" + }, + { + "name": "carbon_source", + "rawType": "object", + "type": "string" + }, + { + "name": "dataset_id", + "rawType": "object", + "type": "string" + } + ], + "ref": "6dffa2f6-a87e-4ee2-ba81-22829fb14d26", + "rows": [], + "shape": { + "columns": 3, + "rows": 0 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sample_idcarbon_sourcedataset_id
\n", + "
" + ], + "text/plain": [ + "Empty DataFrame\n", + "Columns: [sample_id, carbon_source, dataset_id]\n", + "Index: []" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Get just sample_id, carbon_source, and temperature\n", "minimal_data = vdb.query(\n", @@ -343,9 +638,100 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 0 samples with glucose at 30C\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "sample_id", + "rawType": "int32", + "type": "integer" + }, + { + "name": "regulator_locus_tag", + "rawType": "object", + "type": "string" + }, + { + "name": "regulator_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "carbon_source", + "rawType": "object", + "type": "string" + }, + { + "name": "dataset_id", + "rawType": "object", + "type": "string" + } + ], + "ref": "8309daf0-f866-44e1-8ca4-3b5fa071dc9b", + "rows": [], + "shape": { + "columns": 5, + "rows": 0 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sample_idregulator_locus_tagregulator_symbolcarbon_sourcedataset_id
\n", + "
" + ], + "text/plain": [ + "Empty DataFrame\n", + "Columns: [sample_id, regulator_locus_tag, regulator_symbol, carbon_source, dataset_id]\n", + "Index: []" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Samples with glucose at 30C\n", "glucose_30c = vdb.query(\n", @@ -368,9 +754,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 1487 samples at >= 30C\n", + "Found 1487 samples between 28-32C\n" + ] + } + ], "source": [ "# Samples at temperature >= 30C\n", "warm_samples = vdb.query(\n", @@ -398,9 +793,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 0 galactose samples\n", + "\n", + "This query internally expanded to:\n", + " WHERE carbon_source IN ('D-galactose', 'gal', 'galactose')\n" + ] + } + ], "source": [ "# Query for \"galactose\" matches \"D-galactose\", \"gal\", and \"galactose\"\n", "galactose_samples = vdb.query(filters={\"carbon_source\": \"galactose\"})\n", @@ -422,9 +828,73 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Complete data: 0 rows\n", + "Columns: []\n", + "\n", + "First few measurements:\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + } + ], + "ref": "a3896349-b6ba-4827-b7be-e4cfa318f2b2", + "rows": [], + "shape": { + "columns": 0, + "rows": 0 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
" + ], + "text/plain": [ + "Empty DataFrame\n", + "Columns: []\n", + "Index: []" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Get complete data with measurements\n", "complete_data = vdb.query(\n", @@ -441,9 +911,70 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Binding data: 0 measurements\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + } + ], + "ref": "fc59efba-e365-4b1c-b9ff-31828897cde2", + "rows": [], + "shape": { + "columns": 0, + "rows": 0 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
" + ], + "text/plain": [ + "Empty DataFrame\n", + "Columns: []\n", + "Index: []" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# You can combine complete=True with field selection\n", "# Get just the binding data columns\n", @@ -548,9 +1079,24 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "KeyError", + "evalue": "'_repo_id'", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mKeyError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[16]\u001b[39m\u001b[32m, line 7\u001b[39m\n\u001b[32m 4\u001b[39m all_samples = vdb.query()\n\u001b[32m 6\u001b[39m \u001b[38;5;66;03m# Count by dataset and carbon source\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m7\u001b[39m summary = \u001b[43mall_samples\u001b[49m\u001b[43m.\u001b[49m\u001b[43mgroupby\u001b[49m\u001b[43m(\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43m_repo_id\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43m_config_name\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43mcarbon_source\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m.size()\n\u001b[32m 8\u001b[39m summary = summary.reset_index(name=\u001b[33m'\u001b[39m\u001b[33mnum_samples\u001b[39m\u001b[33m'\u001b[39m)\n\u001b[32m 10\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33mSample counts by dataset and carbon source:\u001b[39m\u001b[33m\"\u001b[39m)\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/pandas/core/frame.py:9210\u001b[39m, in \u001b[36mDataFrame.groupby\u001b[39m\u001b[34m(self, by, axis, level, as_index, sort, group_keys, observed, dropna)\u001b[39m\n\u001b[32m 9207\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m level \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m by \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m 9208\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[33m\"\u001b[39m\u001b[33mYou have to supply one of \u001b[39m\u001b[33m'\u001b[39m\u001b[33mby\u001b[39m\u001b[33m'\u001b[39m\u001b[33m and \u001b[39m\u001b[33m'\u001b[39m\u001b[33mlevel\u001b[39m\u001b[33m'\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m-> \u001b[39m\u001b[32m9210\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mDataFrameGroupBy\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 9211\u001b[39m \u001b[43m \u001b[49m\u001b[43mobj\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 9212\u001b[39m \u001b[43m \u001b[49m\u001b[43mkeys\u001b[49m\u001b[43m=\u001b[49m\u001b[43mby\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9213\u001b[39m \u001b[43m \u001b[49m\u001b[43maxis\u001b[49m\u001b[43m=\u001b[49m\u001b[43maxis\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9214\u001b[39m \u001b[43m \u001b[49m\u001b[43mlevel\u001b[49m\u001b[43m=\u001b[49m\u001b[43mlevel\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9215\u001b[39m \u001b[43m \u001b[49m\u001b[43mas_index\u001b[49m\u001b[43m=\u001b[49m\u001b[43mas_index\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9216\u001b[39m \u001b[43m \u001b[49m\u001b[43msort\u001b[49m\u001b[43m=\u001b[49m\u001b[43msort\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9217\u001b[39m \u001b[43m \u001b[49m\u001b[43mgroup_keys\u001b[49m\u001b[43m=\u001b[49m\u001b[43mgroup_keys\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9218\u001b[39m \u001b[43m \u001b[49m\u001b[43mobserved\u001b[49m\u001b[43m=\u001b[49m\u001b[43mobserved\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9219\u001b[39m \u001b[43m \u001b[49m\u001b[43mdropna\u001b[49m\u001b[43m=\u001b[49m\u001b[43mdropna\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9220\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/pandas/core/groupby/groupby.py:1331\u001b[39m, in \u001b[36mGroupBy.__init__\u001b[39m\u001b[34m(self, obj, keys, axis, level, grouper, exclusions, selection, as_index, sort, group_keys, observed, dropna)\u001b[39m\n\u001b[32m 1328\u001b[39m \u001b[38;5;28mself\u001b[39m.dropna = dropna\n\u001b[32m 1330\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m grouper \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m-> \u001b[39m\u001b[32m1331\u001b[39m grouper, exclusions, obj = \u001b[43mget_grouper\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 1332\u001b[39m \u001b[43m \u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1333\u001b[39m \u001b[43m \u001b[49m\u001b[43mkeys\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1334\u001b[39m \u001b[43m \u001b[49m\u001b[43maxis\u001b[49m\u001b[43m=\u001b[49m\u001b[43maxis\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1335\u001b[39m \u001b[43m \u001b[49m\u001b[43mlevel\u001b[49m\u001b[43m=\u001b[49m\u001b[43mlevel\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1336\u001b[39m \u001b[43m \u001b[49m\u001b[43msort\u001b[49m\u001b[43m=\u001b[49m\u001b[43msort\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1337\u001b[39m \u001b[43m \u001b[49m\u001b[43mobserved\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mobserved\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mis\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mlib\u001b[49m\u001b[43m.\u001b[49m\u001b[43mno_default\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01melse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mobserved\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1338\u001b[39m \u001b[43m \u001b[49m\u001b[43mdropna\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mdropna\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1339\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1341\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m observed \u001b[38;5;129;01mis\u001b[39;00m lib.no_default:\n\u001b[32m 1342\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28many\u001b[39m(ping._passed_categorical \u001b[38;5;28;01mfor\u001b[39;00m ping \u001b[38;5;129;01min\u001b[39;00m grouper.groupings):\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/pandas/core/groupby/grouper.py:1043\u001b[39m, in \u001b[36mget_grouper\u001b[39m\u001b[34m(obj, key, axis, level, sort, observed, validate, dropna)\u001b[39m\n\u001b[32m 1041\u001b[39m in_axis, level, gpr = \u001b[38;5;28;01mFalse\u001b[39;00m, gpr, \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m 1042\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m-> \u001b[39m\u001b[32m1043\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m(gpr)\n\u001b[32m 1044\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(gpr, Grouper) \u001b[38;5;129;01mand\u001b[39;00m gpr.key \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m 1045\u001b[39m \u001b[38;5;66;03m# Add key to exclusions\u001b[39;00m\n\u001b[32m 1046\u001b[39m exclusions.add(gpr.key)\n", + "\u001b[31mKeyError\u001b[39m: '_repo_id'" + ] + } + ], "source": [ "# Compare number of samples by carbon source across datasets\n", "\n", From ab02d282721ad874d50b143166408aa448a54433 Mon Sep 17 00:00:00 2001 From: chasem Date: Wed, 7 Jan 2026 17:11:11 -0600 Subject: [PATCH 49/49] tfbpapi virtual_db is working --- docs/brentlab_yeastresources_collection.md | 24 +- docs/huggingface_datacard.md | 42 +- docs/tutorials/cache_manager_tutorial.ipynb | 224 +- docs/tutorials/datacard_tutorial.ipynb | 89 +- docs/tutorials/virtual_db_tutorial.ipynb | 4202 +++++++++++++----- docs/virtual_database_concepts.md | 284 +- docs/virtual_db.md | 5 + pyproject.toml | 10 - tfbpapi/datacard.py | 67 +- tfbpapi/hf_cache_manager.py | 59 +- tfbpapi/models.py | 224 +- tfbpapi/tests/example_datacards.py | 5 +- tfbpapi/tests/test_hf_cache_manager.py | 271 +- tfbpapi/tests/test_metadata_config_models.py | 122 +- tfbpapi/tests/test_models.py | 4 +- tfbpapi/tests/test_virtual_db.py | 346 +- tfbpapi/virtual_db.py | 615 ++- 17 files changed, 4910 insertions(+), 1683 deletions(-) diff --git a/docs/brentlab_yeastresources_collection.md b/docs/brentlab_yeastresources_collection.md index 29d3561..4f49a02 100644 --- a/docs/brentlab_yeastresources_collection.md +++ b/docs/brentlab_yeastresources_collection.md @@ -4,7 +4,7 @@ This document describes the BrentLab yeast resources collection on HuggingFace a ## Collection Overview -The BrentLab yeast resources collection contains 10 datasets related to yeast transcription factor binding and gene expression regulation: +The BrentLab yeast resources collection contains 11 datasets related to yeast transcription factor binding and gene expression regulation: 1. **barkai_compendium** - ChEC-seq binding data across multiple GEO series 2. **callingcards** - Calling Cards transposon-based binding data @@ -15,7 +15,8 @@ The BrentLab yeast resources collection contains 10 datasets related to yeast tr 7. **kemmeren_2014** - TF deletion expression profiling 8. **mahendrawada_2025** - ChEC-seq and nascent RNA-seq data 9. **rossi_2021** - ChIP-exo binding data -10. **yeast_genome_resources** - Reference genomic features +10. **yeast_comparative_analysis** - Cross-dataset comparative analyses +11. **yeast_genome_resources** - Reference genomic features ## Standardized Media Names @@ -234,6 +235,25 @@ Separate metadata configs or embedded metadata via `metadata_fields`: metadata_fields: ["regulator_locus_tag", "regulator_symbol", "condition"] ``` +### comparative + +**yeast_comparative_analysis** provides cross-dataset analysis results: + +- **dto config**: Direct Target Overlap analysis comparing binding and perturbation experiments +- Uses `source_sample` role for composite identifiers +- Format: `"repo_id;config_name;sample_id"` (semicolon-separated) +- Contains 8 quantitative measures: rank thresholds, set sizes, FDR, p-values +- Partitioned by binding_repo_dataset and perturbation_repo_dataset + +**Composite Sample Identifiers**: +Comparative datasets use composite identifiers to reference samples from other datasets: +- `binding_id`: Points to a binding experiment (e.g., `BrentLab/callingcards;annotated_features;1`) +- `perturbation_id`: Points to a perturbation experiment (e.g., `BrentLab/hackett_2020;hackett_2020;200`) + +**Typical structure**: source_sample_1 x source_sample_2 x ... x measurements + +**Use case**: Answer questions like "Which binding experiments show significant overlap with perturbation effects?" + ## Categorical Condition Definitions Many datasets define categorical experimental conditions using the `definitions` field. diff --git a/docs/huggingface_datacard.md b/docs/huggingface_datacard.md index 5cb30d7..d56c771 100644 --- a/docs/huggingface_datacard.md +++ b/docs/huggingface_datacard.md @@ -50,20 +50,42 @@ Position-level data across genomic coordinates ### 4. `metadata` Experimental metadata and sample descriptions -- **Use case**: Sample information, experimental conditions, protocol details +- **Use case**: Sample information, experimental conditions, protocol details. Note + that this can also include per-sample QC metrics. For cross-sample QC or analysis, + see [comparative](#5-comparative) below. - **Structure**: One row per sample - **Common fields**: Sample identifiers, experimental conditions, publication info - **Special field**: `applies_to` - Optional list of config names this metadata applies to -### 5. `qc_data` -Quality control metrics and assessments -- **Use case**: QC metrics derived from raw or processed data, cross-dataset quality - assessments, validation metrics -- **Structure**: One row per sample, measurement, or QC evaluation -- **Common fields**: QC metrics, quality flags, threshold references, possibly - references to source datasets -- **Note**: QC datasets can be derived from single or multiple source configs within - a repository or across repositories +### 5. `comparative` + +Quality control metrics, validation results, and cross-dataset analysis outputs. + +**Use cases**: +- Cross-dataset quality assessments and validation metrics +- Analysis results relating samples across datasets or repositories +- Comparative analyses (e.g., binding vs expression correlation) + +**Structure**: One row represents an observation on 2 or more samples. Note that the + name of the column containing the sample references isn't specified. However, the + role and format of the sample references are strictly defined. See + [Defining Sample References](#defining-sample-references) below. + +#### Defining Sample References + +The name of the field which contains the sample reference is user-defined. However, +the contents of that field, and its role, must be as follows: + +- **`source_sample`**: Fields containing composite sample identifiers. This must be in + the format `"repo_id;config_name;sample_id"`. + +``` +"repo_id;config_name;sample_id" +``` + +Examples: +- `"BrentLab/harbison_2004;harbison_2004;CBF1_YPD"` +- `"BrentLab/kemmeren_2014;kemmeren_2014;sample_42"` ## Experimental Conditions diff --git a/docs/tutorials/cache_manager_tutorial.ipynb b/docs/tutorials/cache_manager_tutorial.ipynb index a905a8b..899b45f 100644 --- a/docs/tutorials/cache_manager_tutorial.ipynb +++ b/docs/tutorials/cache_manager_tutorial.ipynb @@ -48,8 +48,8 @@ "HfCacheManager initialized for: BrentLab/mahendrawada_2025\n", "DuckDB connection: Active\n", "Logger configured: Yes\n", - "Current HF cache size: 5.2G\n", - "Cached repositories: 10\n" + "Current HF cache size: 5.5G\n", + "Cached repositories: 11\n" ] } ], @@ -92,7 +92,7 @@ { "data": { "text/plain": [ - "HFCacheInfo(size_on_disk=5198144267, repos=frozenset({CachedRepoInfo(repo_id='BrentLab/barkai_compendium', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium'), size_on_disk=3579068970, nb_files=504, revisions=frozenset({CachedRevisionInfo(commit_hash='a987ef37c72fcd07b18828d320bc4305480daade', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade'), size_on_disk=3579068970, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417639/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/43c5fb0a4f049fa5895f8167928d047cc4954aa37db1fc8a9892fe9f99c401bf'), size_on_disk=782319, blob_last_accessed=1756926308.7694294, blob_last_modified=1756926309.6284256), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417694/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/058b507f75cb01bb5912e14e7bbb4e25a1de945e578323bce9f920f31a9ced82'), size_on_disk=11980395, blob_last_accessed=1756926320.09838, blob_last_modified=1756926321.5623736), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417643/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/66dfa3feea4410ba34f5d5d37c7b01e1fa2cdd4ff38f9dfb10d20d55e40e449f'), size_on_disk=1012185, blob_last_accessed=1756926309.326427, blob_last_modified=1756926310.0784237), CachedFileInfo(file_name='genome_map.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b8f0ed936cb43c4649126e9b7596cef0ab626a2d'), size_on_disk=142786, blob_last_accessed=1756926300.410466, blob_last_modified=1756926300.4954655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417701/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a0dd9587c7525a610a3794cf41f6db24a6ba22a12ed852dc27381a68db3e8fca'), size_on_disk=8781514, blob_last_accessed=1756926321.6273735, blob_last_modified=1756926323.1263669), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417706/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4f1ea5592a855f3bf5739b0caae1c69c7606276d125c3dfe8e98379d12bedf1b'), size_on_disk=1154142, blob_last_accessed=1756926322.9053679, blob_last_modified=1756926323.5173652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381054/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1a9f9c650973937d852d2de54c0d73336d1516184d29e203563fd9bf8d7fefa5'), size_on_disk=3346485, blob_last_accessed=1756926778.1077476, blob_last_modified=1756926778.8637433), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417619/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0a4d4159941caa34a5e079821d49047d1434077926b2933c27d384e04ed2e5b5'), size_on_disk=9949802, blob_last_accessed=1756926302.5744565, blob_last_modified=1756926304.05045), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380996/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/09e663ce2a60df5635f4e4ea77d6350e7752a9bb5e81fbdfd818869ac53ca805'), size_on_disk=13309208, blob_last_accessed=1756926375.0511415, blob_last_modified=1756926376.0221374), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380977/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/74237b16fd38090785d203d96bae72e1f5ce919d423f793cfb9d3b86623358e6'), size_on_disk=11114601, blob_last_accessed=1756926371.6651561, blob_last_modified=1756926373.12615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417906/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/34d3381dd45e579dcc298e91cc95174c4219cf548401b8f2392cf746fd508c5b'), size_on_disk=13795318, blob_last_accessed=1756926356.3642225, blob_last_modified=1756926357.640217), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417920/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/00a869703f03e3df3f0f51a7d93d52dd7eac5174ed2a1b752207460e71c7b41a'), size_on_disk=14880236, blob_last_accessed=1756926359.1242106, blob_last_modified=1756926360.409205), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381060/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1bb05aa71fe55db65fc3868b131854c1e6666dca8cc370874f517aa0756d21ac'), size_on_disk=9137992, blob_last_accessed=1756926778.9637427, blob_last_modified=1756926780.2307358), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417890/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3eca80a3220fd0294fdfca83740ba70a20af764d28833494948705c4d542ded3'), size_on_disk=9018889, blob_last_accessed=1756926353.7652338, blob_last_modified=1756926354.7252295), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380943/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/987771cb978dbc949eff5044071149a00223c6771efcc870850e3c8cbe0648c3'), size_on_disk=13397144, blob_last_accessed=1756926367.295175, blob_last_modified=1756926368.7601688), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417662/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/90008c75533e5a395bf92b2a4382cfb8d6c27af4921615cac22bc1965375efb5'), size_on_disk=12701661, blob_last_accessed=1756926313.0204108, blob_last_modified=1756926314.158406), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417752/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/efc9f1ecf4a7b06b23da46fb8c7d1879b67a9d6603ed753d09e2028396ec05c5'), size_on_disk=3420764, blob_last_accessed=1756926329.26534, blob_last_modified=1756926330.0613368), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417848/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eec4ffff5d145c0ee0850d5e68787ab90e6510cffa5803579db44e3a575822dc'), size_on_disk=13556826, blob_last_accessed=1756926346.1882668, blob_last_modified=1756926347.2852619), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380950/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f508a5da82832b920577592b853da2c2d76ebf0adca587e10b63507f46dc5066'), size_on_disk=3452513, blob_last_accessed=1756926368.7781687, blob_last_modified=1756926369.7081647), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417940/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/270559ca1db4605de241e8642461b4d7304d124e4fc624f7d8a8a59867301a4c'), size_on_disk=2328150, blob_last_accessed=1756926363.182193, blob_last_modified=1756926363.9021897), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417730/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f2554716083cfa1bb54c2098315b92e1b3537eb265da884c00ba7e7d4c050da'), size_on_disk=14980864, blob_last_accessed=1756926325.5143564, blob_last_modified=1756926326.803351), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417632/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/23c561fde2e6c2973ec1913373c343a5c49d6cc0dbb44ff21eb5aff87cd77d3a'), size_on_disk=18481828, blob_last_accessed=1756926306.2764404, blob_last_modified=1756926308.5744302), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417736/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/671a67d7275810840cafc9db32e43385c4ba61b007d702e7eb5cb244a0e4afa9'), size_on_disk=13855124, blob_last_accessed=1756926326.1693537, blob_last_modified=1756926327.261349), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417684/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eee25a9a91c54d0404db884532d1b85555f2e1de0c664abcbda4343f9d729842'), size_on_disk=10310520, blob_last_accessed=1756926317.7173905, blob_last_modified=1756926319.3473833), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417761/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ba86b2041414034d38b95ccbf3c65957642c56b56914240999986ebc76b9e193'), size_on_disk=1635118, blob_last_accessed=1756926330.478335, blob_last_modified=1756926331.0933323), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417813/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f7d5239d0d39e171aad1e84964f8cdc3c7a325fe8c38b6829e114cb3997e658'), size_on_disk=10238322, blob_last_accessed=1756926339.4142962, blob_last_modified=1756926340.8722897), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417823/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1887333f71d11fe7c43748662f58d9ba3fb12f3f6ca30a61d7e1c7a0c95f7064'), size_on_disk=9444476, blob_last_accessed=1756926341.6372864, blob_last_modified=1756926342.8442812), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417791/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8d2eb1bdd1a5cae023abb271e5858cf111879953cb0d6267f4ebf21a3d30c4ad'), size_on_disk=5232904, blob_last_accessed=1756926335.2033143, blob_last_modified=1756926336.417309), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417915/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9fbfc50c3e1f756169d25442880ba33e5cf237e3c83a0d674b64ace47e8d3322'), size_on_disk=13801295, blob_last_accessed=1756926358.318214, blob_last_modified=1756926359.4592092), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417614/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dac699f09872b0c58b742c692b341f0b00b7d83284cec5ef838c276a6f48908c'), size_on_disk=1759475, blob_last_accessed=1756926301.7274601, blob_last_modified=1756926302.4774568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417648/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fb706eb700e8fc003822b897accd3501360d737fd1a9ffcf26c3e9d9b25a5e35'), size_on_disk=1285054, blob_last_accessed=1756926310.3104227, blob_last_modified=1756926311.0174196), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381013/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c831ca7cc9cfd2cc2ccd92a0168b2e14dedf2bde9e29780dfaf6d281b31e9462'), size_on_disk=4648281, blob_last_accessed=1756926377.4421313, blob_last_modified=1756926378.3451273), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381040/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a7c622a8ae1f5083eef26ecbcc2444055a9265b674ae80002e5468c94c0eb070'), size_on_disk=9682671, blob_last_accessed=1756926774.782766, blob_last_modified=1756926776.7967548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417797/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/12ee278356fa75b0ad555fe06376c78b71630324b7b89aa28abe960c187d256f'), size_on_disk=14391201, blob_last_accessed=1756926336.3743093, blob_last_modified=1756926338.322301), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417665/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7c9b3d448e53fcac9334df092cc003f438cbc462e6785ac3d61202bd65a7125f'), size_on_disk=7481605, blob_last_accessed=1756926313.490409, blob_last_modified=1756926314.7744033), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417914/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d062dbf660e1ef240ff8ef99ff2d486d9caaabda14575e6365ce6f725fa91ccf'), size_on_disk=8599250, blob_last_accessed=1756926358.2422144, blob_last_modified=1756926360.3412054), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381018/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/878bf028aca9daeebe7ed0f8ee522580d512771ec4a5e35bfc62ac675176ad15'), size_on_disk=701951, blob_last_accessed=1756926378.2281277, blob_last_modified=1756926378.643126), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8168980e59c50149bfb9866a1e8855c235f2882b988df99331c9247f65e9813e'), size_on_disk=2289686, blob_last_accessed=1756926376.4721353, blob_last_modified=1756926377.2151322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417725/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4195b9ed6506437a735fd8061cd1b79211c99169c4319c4f72b801a501f2b0f9'), size_on_disk=2233032, blob_last_accessed=1756926324.958359, blob_last_modified=1756926325.594356), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417863/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4aabb9d1a4b105c7669deb0f13cd635ab7433ed1ae02f5785aad014d604f1504'), size_on_disk=6900487, blob_last_accessed=1756926348.4532568, blob_last_modified=1756926349.2742534), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417617/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9ad746ec09ed51ed497d87f3dd64b11916b70e6833ce66141bc8feb6ba73d1d3'), size_on_disk=23082279, blob_last_accessed=1756926302.1754582, blob_last_modified=1756926303.6944516), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417943/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0e4191a403f5070636bcb281bd80d39e11c70fb502fa2b18150032d58f2e0740'), size_on_disk=5396199, blob_last_accessed=1756926363.7271905, blob_last_modified=1756926364.542187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417790/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f88bed27521de8285bbb92cf95242820116e6fa2c23ab578e6608a6a25e0c04e'), size_on_disk=10836858, blob_last_accessed=1756926335.078315, blob_last_modified=1756926336.1843102), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417794/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f15be566c029c52cf46f6258e589529afb3a002f010710a54d04dd6151f8b684'), size_on_disk=10205023, blob_last_accessed=1756926335.9063113, blob_last_modified=1756926337.151306), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380952/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3811d6e6194d8b88c84fc0931cba844da23677e76e1e9a25e2890df968a7e529'), size_on_disk=1003172, blob_last_accessed=1756926368.8351684, blob_last_modified=1756926369.4641657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417847/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4dd39704bc9c1d60a8271f16206887171ee92abde023c53501c1560533a688c1'), size_on_disk=4755120, blob_last_accessed=1756926346.125267, blob_last_modified=1756926346.8372638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381023/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/22e3e0f4cd1f2c755ec1ac13b99d2d6c98d0158319478f2deca4e89aefc5e88e'), size_on_disk=3860195, blob_last_accessed=1756926378.5651264, blob_last_modified=1756926379.6101217), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417827/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8bfd1221f930e86ffd6150c82aae85f00a23fff33c672e40f47d671f5a2e4d17'), size_on_disk=7291599, blob_last_accessed=1756926342.156284, blob_last_modified=1756926343.1502798), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417886/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62fe87e7519370c4ac46e6d9d0abf314a8f92d9a5d6804ebddae0ed095641219'), size_on_disk=7968977, blob_last_accessed=1756926352.9472373, blob_last_modified=1756926353.7992337), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380934/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d264f2a91c87696f6a07f573f1d921c91e1d53e3afca59610ba4703b3683b617'), size_on_disk=4601537, blob_last_accessed=1756926366.1091802, blob_last_modified=1756926366.9191768), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417609/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e3d8451242fe9ecf9f82e2ac1a406144840a6182178228e6e1fa67e6a04a318a'), size_on_disk=3176651, blob_last_accessed=1756926300.651465, blob_last_modified=1756926302.0864584), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417849/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/23b6606ffcf116cf066b103bf1b6b4fb33c40e544e20e4565fa44c5bbcea4bdd'), size_on_disk=5689527, blob_last_accessed=1756926346.584265, blob_last_modified=1756926347.3892615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417808/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0251ae27bd961d6c962253c555963b42148092cdaa2616a78f6872cf1a4e5e4d'), size_on_disk=12900784, blob_last_accessed=1756926338.3973005, blob_last_modified=1756926339.439296), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417628/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/291f0e4d2d0fa1656de1583bd9e131fd4d2fc1934d0bb70a6774f7e7f90e62ae'), size_on_disk=15444530, blob_last_accessed=1756926305.1314452, blob_last_modified=1756926307.1334364), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417671/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bced5a56092fea7de5754bebc33caaab643f27236f8d9020e7bc6b30b9045d6e'), size_on_disk=5347180, blob_last_accessed=1756926314.7704034, blob_last_modified=1756926315.8293986), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381062/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/76151e17f242c5108010debea961b0b13f3821be2fcb77fd923a36cc0e672695'), size_on_disk=4880117, blob_last_accessed=1756926779.3357406, blob_last_modified=1756926780.5167341), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417843/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6569e23f0eb660e9f7111b9cdd69e7ae797ba265647ee44e4cbf2d8babdeca9e'), size_on_disk=2706587, blob_last_accessed=1756926345.3402703, blob_last_modified=1756926346.125267), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c103eef2a9834296fb1328b7012b5b9184c5a3f131817dfca257843e863d34b2'), size_on_disk=9829417, blob_last_accessed=1756926362.1531975, blob_last_modified=1756926363.3531923), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bbfeb8571ee8915d793698c2116cef9f2ea61573c66b4ae7066078271c6172d6'), size_on_disk=5132301, blob_last_accessed=1756926362.6441953, blob_last_modified=1756926363.7271905), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417756/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ede027f2c13a1e6669becf6194ace85af5800d5fe77db16ec47bfb3abaa735d3'), size_on_disk=8620421, blob_last_accessed=1756926329.968337, blob_last_modified=1756926331.0303326), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417700/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1240fbb2497074bb7ff98b1bc6399b8a8ad5d320246285abfb6738e27e2bbdeb'), size_on_disk=19312447, blob_last_accessed=1756926321.6763732, blob_last_modified=1756926322.8383682), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380951/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b9b68679dd2da94e4aa4852ae3de3ee5b823c1fb1483f4ece3eee5d947014785'), size_on_disk=1154119, blob_last_accessed=1756926368.8281684, blob_last_modified=1756926369.410166), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417687/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fa4f79173f5a5ed278c7ef80d9fb220aa9c615de97bae7963969e27ecf0e587c'), size_on_disk=13352339, blob_last_accessed=1756926318.3003879, blob_last_modified=1756926320.3913789), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3fe2828766efd936f02efae2da3e068ef9b28ea374e594e09850e3e8ff0d8164'), size_on_disk=4421540, blob_last_accessed=1756926375.7961383, blob_last_modified=1756926376.7581341), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417642/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/591f688d0d9f98350a1d636e6f93ed37563f3ea9765ca6b633490533b0cf32d4'), size_on_disk=11498542, blob_last_accessed=1756926309.2804272, blob_last_modified=1756926310.8714201), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417817/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ccf81aa26021a009a212ef8f8bee71c12f5c8d0a2b97ffdb2c4d86d13c8b8133'), size_on_disk=7521086, blob_last_accessed=1756926340.0802932, blob_last_modified=1756926341.3222878), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381047/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dda6f6ca669a0e5c9d34b548305cbeb6b82f1b70086e06d3604e7e096fd7fb25'), size_on_disk=6799004, blob_last_accessed=1756926776.8777544, blob_last_modified=1756926778.4717455), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417689/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e27d1d153e5b135a09ec5db80bb05ace721fd60ac90f0693ac34ca3160fdfbdd'), size_on_disk=5015020, blob_last_accessed=1756926319.2973835, blob_last_modified=1756926321.220375), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417909/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c5de46a74c16aee32dfb2be89a50dceaa5704c0c550076dcec483d1c21d983af'), size_on_disk=5969725, blob_last_accessed=1756926357.3362184, blob_last_modified=1756926358.5162132), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417634/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/529d984782684991b2eb7be2d345b5a4e8a6e08d3dbe30ad34495d8380464e6e'), size_on_disk=16306189, blob_last_accessed=1756926306.810438, blob_last_modified=1756926310.239423), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417668/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b426513aab4e071a24b7d27100a4da4da8c71c8e9b3e82163808b0ec6a42b8d0'), size_on_disk=6011682, blob_last_accessed=1756926314.5374043, blob_last_modified=1756926315.4354005), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417947/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/50bb9cc74904c9d3b6cc3ec8bc12c7ad1fb89204be6f89a863e703a94d224512'), size_on_disk=4023877, blob_last_accessed=1756926364.0161893, blob_last_modified=1756926364.9561853), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417895/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2daca9b311da1afa40f578c6df7dc4b0039dd4857663c98cd87c665a547b7144'), size_on_disk=11915424, blob_last_accessed=1756926354.7232296, blob_last_modified=1756926355.8262248), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380933/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/07c22e11c56ce5ecb6c22c910c3d3a0d9b54fd341bef0513b4bf42a9c53ec6a0'), size_on_disk=10138164, blob_last_accessed=1756926365.8761814, blob_last_modified=1756926366.7471776), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417789/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6bd6339b995a5730b6b65ec65f680b57195469ebee9f2e15de6e604b069d4b0e'), size_on_disk=9612750, blob_last_accessed=1756926334.6493168, blob_last_modified=1756926335.8183117), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4c942b91ac018330aeb4da512a3e47ae4531a434b6e9933cf3e2bb476a77f9ca'), size_on_disk=6312758, blob_last_accessed=1756926366.1041803, blob_last_modified=1756926367.0141764), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381043/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f6ded8e6496dcfdd499edc382d19da2071f25daebbb250b375b9a2706e9d8211'), size_on_disk=9139336, blob_last_accessed=1756926775.6057615, blob_last_modified=1756926777.1057532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417683/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6131abcd72bd4c6a8627685c4df8a295af50e824f52a5cde156adae7cd694bba'), size_on_disk=15545733, blob_last_accessed=1756926317.382392, blob_last_modified=1756926319.0483847), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417766/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/39da4f16b82cc0b5019d8526f2e3eed49de9760e319f8e839db621bf4c1ab021'), size_on_disk=6482957, blob_last_accessed=1756926331.2123318, blob_last_modified=1756926332.1133277), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417613/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/695d3095619f3a313d25c506b9d0493f171896f1439bcf3bd1cd932d1f6fed5d'), size_on_disk=2812565, blob_last_accessed=1756926301.6434605, blob_last_modified=1756926302.4814568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380936/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5a931bc1181001314c3822051c493ba7a8b87b5bb26b8866783612f2bcf49c62'), size_on_disk=3467608, blob_last_accessed=1756926366.19118, blob_last_modified=1756926367.2261755), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381015/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/345c83c5ab13d230542683014d3d6073ba5a348e3063fea325843981c6c6e7c1'), size_on_disk=3807314, blob_last_accessed=1756926377.67913, blob_last_modified=1756926378.4971266), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381036/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ff76dabe83d2948240dd846fcc2a969380b7bd9f8d2eff2c71c299f0b14134cd'), size_on_disk=2593393, blob_last_accessed=1756926379.8661208, blob_last_modified=1756926380.5611176), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417675/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/79072181ebce12769f2f0a1971977d6691dde1841b53d7b54cf9d4f47f53b3dc'), size_on_disk=6478433, blob_last_accessed=1756926315.763399, blob_last_modified=1756926317.2643924), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417806/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2befcad73b2aa8170ceaf8c249ceba45e87037771e86c9bb4bd7addc5815bbec'), size_on_disk=5276603, blob_last_accessed=1756926337.9863024, blob_last_modified=1756926338.8112986), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380979/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef743a2a6a8fe44b62963b981ef0429676e7854243ab07d450d9c3ab569c7e8e'), size_on_disk=3735979, blob_last_accessed=1756926372.2201538, blob_last_modified=1756926373.6791475), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417656/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/03f8707a323ef7563c8ec2d6352d24c6454d043c4bbbca417680df5c7db208f8'), size_on_disk=10029839, blob_last_accessed=1756926311.588417, blob_last_modified=1756926312.8104117), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380928/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/251ffcb8e06f2ac0db0791065248327a9d1eeef33a07f5c6b25946e04b6c46d8'), size_on_disk=2289693, blob_last_accessed=1756926365.037185, blob_last_modified=1756926366.1961799), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417674/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5970db11c3efe2bba4eb50d07480cb0b46ed74dfc3ddf12d611ecd61f1d5fb68'), size_on_disk=4619941, blob_last_accessed=1756926315.5114, blob_last_modified=1756926317.630391), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417667/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef18f0ec136ef4b0facd217b32604556a7c9f514d75e26d2be17eb8da9b74a14'), size_on_disk=7979245, blob_last_accessed=1756926314.2284057, blob_last_modified=1756926315.734399), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fb5958ec4e10f5c7e19ebc55b24f26012e6d8ccf'), size_on_disk=8004, blob_last_accessed=1756926300.2594666, blob_last_modified=1756926300.3214662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417931/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7e70415ed0dfb1eb42e46789c905ccf2b593dc192d907d939d87acd59cd963e1'), size_on_disk=4991063, blob_last_accessed=1756926361.306201, blob_last_modified=1756926363.1161933), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417825/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/efc4971b63b21d6aee96e17d9e9b2cc2e3fd34608cc3722e36032c594fc03e48'), size_on_disk=8550531, blob_last_accessed=1756926341.8732853, blob_last_modified=1756926345.0782714), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417907/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b24926bd36cf33ea9107043b9a47db8239ad6208745c56002513225e8cb80a6e'), size_on_disk=7594380, blob_last_accessed=1756926356.7332208, blob_last_modified=1756926357.882216), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381068/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/16e4412527449a3cb9d2da129c3309a71b2ec9b33a7a39cb9bb31d41ce5d2e04'), size_on_disk=14909067, blob_last_accessed=1756926780.5687337, blob_last_modified=1756926782.3187242), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417753/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a8969c75211310735ad33d5af57327f2babc86276adc7cefcda254cc9fc9baa5'), size_on_disk=6433095, blob_last_accessed=1756926329.577339, blob_last_modified=1756926330.4023352), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417750/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aedd94fe4b42547108a807242052397b6dfbfda0eb4e56a9f2e267b312251147'), size_on_disk=1531584, blob_last_accessed=1756926329.1973405, blob_last_modified=1756926329.9403372), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381020/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c85f953439e9f6918c7f7c8194bcbbebe0ef8a96070119813025a42bde8c08fe'), size_on_disk=897644, blob_last_accessed=1756926378.3451273, blob_last_modified=1756926378.7061257), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381045/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdc8f9e6447280a7fc713c2a8f2157eb9076a5c01a7eb8437176e9ee30d6d4ab'), size_on_disk=4939421, blob_last_accessed=1756926776.3437574, blob_last_modified=1756926777.2507522), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417844/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ae034bb207abc0c649fb60aae78e798d46647f53b4e0352c6510fb7c8339a234'), size_on_disk=5408445, blob_last_accessed=1756926345.5212696, blob_last_modified=1756926346.4342656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417830/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/21e2dbb49f07cb7c2d7277f5bf1a442e216a2991677c3c4ece5c51316795ea77'), size_on_disk=12760016, blob_last_accessed=1756926342.9432807, blob_last_modified=1756926344.1602755), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417928/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/97e74e01fd641bfef68b6206a3c62ab41b5c28d8ea8369ce4198df1b3387b266'), size_on_disk=2449472, blob_last_accessed=1756926360.9072027, blob_last_modified=1756926361.3542008), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417760/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0500e5835116a61477f2d98554d0dd97e8d932a6b564f709c40c4884f2b49052'), size_on_disk=2234621, blob_last_accessed=1756926330.2873356, blob_last_modified=1756926330.8273335), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417708/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b58038866c334daaf757a086f99c3a5c5962b8563769ee0a3205bcdcbb0144ec'), size_on_disk=250863, blob_last_accessed=1756926323.0003674, blob_last_modified=1756926323.366366), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417884/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ff7d3efa4a231f5130be299d8d0ff67fe4973856e4db18e8a6c53d47ba6b28a7'), size_on_disk=11684836, blob_last_accessed=1756926352.791238, blob_last_modified=1756926354.0932324), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417918/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9f1cf3832969364b6cee1c817d3583a554b765bdbc1ff3f5b29692b7ea98ee7e'), size_on_disk=6936136, blob_last_accessed=1756926358.5802128, blob_last_modified=1756926360.9132028), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417901/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/69432386b347f5b364c02d270b38891358fa2068465354d1ada84d5450ea5bf0'), size_on_disk=9484337, blob_last_accessed=1756926355.6292257, blob_last_modified=1756926357.0582194), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381070/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/32c8f4c4efdbe6e137add61b0e31c27ae6a9eb5e8b463e9e4410a27cc51a57d3'), size_on_disk=29864184, blob_last_accessed=1756926780.7487328, blob_last_modified=1756926783.3167186), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417670/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5e36b63ef1fbb55f821ed2c3d61f0d0bf86cf00846e86ff9dbe6f4d9597f9dc8'), size_on_disk=10881106, blob_last_accessed=1756926314.7704034, blob_last_modified=1756926315.980398), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380973/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/343eb57057b5af866386fb4c02d0c41b888e206f5473ed7b4041200cfb93dbd5'), size_on_disk=1511721, blob_last_accessed=1756926371.3611574, blob_last_modified=1756926372.4931526), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381017/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1d9b2dbb5079eca871182c2dce1d681b943f1362072c18e255a4bd6d1068b840'), size_on_disk=2436549, blob_last_accessed=1756926378.161128, blob_last_modified=1756926378.7931254), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417660/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c394d7b0cfca3f3782dca86380e1a55903111d32efde98173af8a0b726ef5e7d'), size_on_disk=6441490, blob_last_accessed=1756926312.4744132, blob_last_modified=1756926313.4024093), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417840/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7e32bef3c0f7f7520382325aba12f5cf9405c22ebce8554b1d877fe24d2da52b'), size_on_disk=9679209, blob_last_accessed=1756926345.0092719, blob_last_modified=1756926346.0542672), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417882/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ac2bd9710196abc4469d4d78ccbde6ddc67e9ef39ade7d50c32b0d951866e653'), size_on_disk=10076041, blob_last_accessed=1756926352.116241, blob_last_modified=1756926353.223236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417746/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/432442dbdd5578acedcbf6eddc14cfe0d460facc7b0dea857225fbd4e6a4242c'), size_on_disk=2726090, blob_last_accessed=1756926328.6353428, blob_last_modified=1756926329.1983404), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417625/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/96b04f28e74e25bd1502a4ea9aca8047bfa8462e61cced0d726af4a336e1a585'), size_on_disk=17384720, blob_last_accessed=1756926304.4014485, blob_last_modified=1756926306.2074406), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417720/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/961772a081bde1795af5caea9d7562e2b1f889981bd3c3f1c792ad9093a758d7'), size_on_disk=659095, blob_last_accessed=1756926324.1233625, blob_last_modified=1756926324.6243603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380947/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3d6062f97d18f4945c36590355a312df2a16206c26626392cfb44d9613aea49d'), size_on_disk=9201129, blob_last_accessed=1756926368.5321698, blob_last_modified=1756926369.383166), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381063/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1e4361a7d20f8e92e8efc238365e0ed06eb021243864321c93d4638fa21634c4'), size_on_disk=10916323, blob_last_accessed=1756926779.3497405, blob_last_modified=1756926780.685733), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417768/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8bb8e088eb5123049e418c868ab42d94fb48946f934a8313f4cf283e3bfa09ba'), size_on_disk=4124375, blob_last_accessed=1756926331.3153312, blob_last_modified=1756926332.2313273), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417845/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ca2b89837de406b10d72533a3e3d5457b3512b704f7cea395fb0e1b88828393e'), size_on_disk=4648586, blob_last_accessed=1756926345.6932688, blob_last_modified=1756926346.5862648), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417861/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef65c45e0e86b7d04e274c39408b332cc94879c0804f16acf7c537b14bb6498c'), size_on_disk=10436399, blob_last_accessed=1756926348.3832572, blob_last_modified=1756926349.6192517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417894/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/12f262a12783317d135ffc5e1d281a3d43b746a82ed8053c15de824b94bd5ef8'), size_on_disk=6449687, blob_last_accessed=1756926354.4772308, blob_last_modified=1756926355.4742265), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417677/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cfcdac65879c0c26771c1cd1c4a656d4db20e6adb2ed3ad833cf23cd35a9d848'), size_on_disk=13135194, blob_last_accessed=1756926315.9083984, blob_last_modified=1756926318.1283886), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380942/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a958d997c1aff07ec07304485ee818280bc7714dd9c485ea73ef7fda81f2fe63'), size_on_disk=11407010, blob_last_accessed=1756926367.109176, blob_last_modified=1756926368.6211693), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380925/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdf190d30101829153acfd62edd4d689ac65a7d737f0e80978c7d460819b5caa'), size_on_disk=9220100, blob_last_accessed=1756926364.6101868, blob_last_modified=1756926366.1331801), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417854/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d52c8b14e197b179a9b43fbc89bb3205d09d458161ee49ac15a62b7c1f7a39fe'), size_on_disk=6203801, blob_last_accessed=1756926347.311262, blob_last_modified=1756926348.387257), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417717/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/689842dffacf7efa4ba6cd9b4314d195e89f9105eb71675f27019a9e25be63db'), size_on_disk=102777, blob_last_accessed=1756926323.9603631, blob_last_modified=1756926324.5793605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380966/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f27a802d18bf78781f3d168370e0c0e241a130d04a5871d7416442be3ce3e40f'), size_on_disk=4241927, blob_last_accessed=1756926370.3881617, blob_last_modified=1756926371.272158), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380986/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f23d3060df5cb96778fbdf10fff0155c931e1a1e8cc077c7f1582542d54827f5'), size_on_disk=5858046, blob_last_accessed=1756926373.285149, blob_last_modified=1756926374.5971434), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417896/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/71e393229e9071bad31abe5dc3d41bd5d719a5dcb68077827861b4b4e9a49a85'), size_on_disk=6055777, blob_last_accessed=1756926354.7882292, blob_last_modified=1756926355.8442247), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381022/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/980ed07a51dfad581fc2f7bce44031c92445822ba7e994cbfc9b5508cdf97c85'), size_on_disk=2103246, blob_last_accessed=1756926378.5151265, blob_last_modified=1756926378.9441247), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417949/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6123bc7bd312e6e37fabadee561c7dacad32d26bada92c9785c783f94fc50a37'), size_on_disk=1320707, blob_last_accessed=1756926364.4151876, blob_last_modified=1756926365.0891848), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417740/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e91586589c967e7bcbd521989874b21c024da79365d0d9916ffc66b774043e01'), size_on_disk=11875677, blob_last_accessed=1756926326.8903506, blob_last_modified=1756926327.6843472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380963/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c0a30240a0a99347e114928a15a24268495fd37480fde6822f503438bdf3ed48'), size_on_disk=579777, blob_last_accessed=1756926370.2681623, blob_last_modified=1756926370.81716), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417912/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4451877e599fa65e1f00f67b5fcd41fe74719d8bcb19ddacac419fb347c77cf5'), size_on_disk=4943909, blob_last_accessed=1756926357.7152166, blob_last_modified=1756926358.6302128), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417757/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6c9c5dbd0513759ace8599b2542a1f99fe0c30feb318665ebdb55bfb111c8c67'), size_on_disk=5834465, blob_last_accessed=1756926330.011337, blob_last_modified=1756926330.7623336), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381011/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/839fea471c67e08e0b68ae6344432c76da559e67c505c3f6120b20db2f1f753e'), size_on_disk=4378212, blob_last_accessed=1756926377.2971318, blob_last_modified=1756926378.0891285), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381049/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c571bcc5f87a79c7cd38b9614ee83b0d5cc742094e17a7990c829394fca193be'), size_on_disk=3753119, blob_last_accessed=1756926777.145753, blob_last_modified=1756926778.2097468), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417828/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eb4987a8ef94e1691c74c1d828d7d9313da4691b577118a73524def2a90c6d0b'), size_on_disk=4827634, blob_last_accessed=1756926342.3472834, blob_last_modified=1756926343.2782793), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417604/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e6dd3abdf75462f1f5d16f19383b98a8a4c817dc3b13c7cea2b4bea611c40c62'), size_on_disk=961320, blob_last_accessed=1756926300.390466, blob_last_modified=1756926301.3534617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417922/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fba17c65fa210922e57c533f5146b4237677149a5022fb836c1cd52dc89855b2'), size_on_disk=10194446, blob_last_accessed=1756926359.5812085, blob_last_modified=1756926360.610204), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417631/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b4b8c985528935ec46c6c7e78482af1ed26238137fc63b2196c6631e2d3796ab'), size_on_disk=27651513, blob_last_accessed=1756926306.2814403, blob_last_modified=1756926309.1994276), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417692/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/82405dc0a21bd225cc6a9e813e62d4dc0522719abf1e677c954a6ec38763b306'), size_on_disk=4873514, blob_last_accessed=1756926319.6253822, blob_last_modified=1756926320.6443777), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417898/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ced2c80d4f9d2aa28260b885a20b1c90b41d8eb54ee92f418ed592b97b954a9e'), size_on_disk=6422129, blob_last_accessed=1756926355.1262279, blob_last_modified=1756926356.0962236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417864/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/15d259c52e80a2d2d9c20274ef940237c90001d32f2aecb80779335a708222ff'), size_on_disk=3706383, blob_last_accessed=1756926348.5722563, blob_last_modified=1756926350.0962496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417826/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3532f474125bbdc0445e46fe8be83801ca9c079afb1dedd7cd95e25994c50d17'), size_on_disk=11492099, blob_last_accessed=1756926341.9132853, blob_last_modified=1756926343.2502794), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380949/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/65090ef93158269c98bc2e747e505cea9d460f0959d0b2957e58543cd1944163'), size_on_disk=2110872, blob_last_accessed=1756926368.705169, blob_last_modified=1756926369.142167), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417818/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3b30217b127ff88f165762c5054c392d9489b9007ba7ca8b86f18b44f9988e4d'), size_on_disk=8388230, blob_last_accessed=1756926340.2372925, blob_last_modified=1756926341.5632868), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417620/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b973f1a83f111c4e114a40d3731ff24779cafcb7b71523fe8571cf2ed849b316'), size_on_disk=20780203, blob_last_accessed=1756926302.5494566, blob_last_modified=1756926306.7334383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380946/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62fe0084bf7a8f9f484e76366f203e1c68862a211d5d501c08e19b8f28678d6b'), size_on_disk=8090103, blob_last_accessed=1756926367.6871734, blob_last_modified=1756926368.7681687), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417698/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d240b652ebca64784e037cc8ffbda424e2c92f368c5bb9c730930f48f29facc2'), size_on_disk=1855826, blob_last_accessed=1756926321.2993748, blob_last_modified=1756926322.0703714), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380929/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aeddf7cf3aee11ee08c90d305e66fc682e675959c171ecfaa591bc1d9015ebbc'), size_on_disk=6656759, blob_last_accessed=1756926365.1011846, blob_last_modified=1756926366.0181806), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417807/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ebc732f5d06d745bb63af3c7f5966722972fa722ef9d0194d71cf6d9485fda83'), size_on_disk=14049230, blob_last_accessed=1756926338.268301, blob_last_modified=1756926339.5272956), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417637/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ca1a954c7b2aca56529daf9a89249ad4e6ec2373e29ac7c19d2af1533afc4263'), size_on_disk=2776397, blob_last_accessed=1756926307.8804333, blob_last_modified=1756926309.1794276), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417821/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c29db68a3430cf8cb01716d90a92105387777ed1361992eb9501271c35359cfb'), size_on_disk=6597050, blob_last_accessed=1756926341.1692884, blob_last_modified=1756926342.2702837), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417929/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/85b194132215311994e48908799e31357d1094663a232e8170a4edcfa5a11eff'), size_on_disk=9155231, blob_last_accessed=1756926360.9862025, blob_last_modified=1756926361.9231985), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380955/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/369e0f1de51ea197e928618e562613eeed2ed59ce5c1389ce07ff6dd96d391b4'), size_on_disk=3596907, blob_last_accessed=1756926369.4561658, blob_last_modified=1756926370.2661622), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417743/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d4eb93049648498d2fdc1f125574429a4dfdec65e626ad9fa16bdf0d0cf599d8'), size_on_disk=2280534, blob_last_accessed=1756926327.462348, blob_last_modified=1756926329.1343408), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417788/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fd6f44471f1b86568bc059f25e5ccec7bda2b85b9f8be536aeaf2305fe015f5d'), size_on_disk=9112879, blob_last_accessed=1756926334.546317, blob_last_modified=1756926335.971311), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380945/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/51efeee4439e81d920c4d9e860de321d90c72703600044e52215c774e1540650'), size_on_disk=6902367, blob_last_accessed=1756926367.356175, blob_last_modified=1756926368.6011696), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381059/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/59daa71f55c9c0da6f8d56fc2497491477312fbdbcb9e92d4951874f2ed72686'), size_on_disk=13124154, blob_last_accessed=1756926778.926743, blob_last_modified=1756926781.8157268), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380974/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ea9eb19e91fedadd9e46d5d7dc9169995065af5b6b8e823f1667117be9d737d0'), size_on_disk=8889742, blob_last_accessed=1756926371.5481567, blob_last_modified=1756926372.2801535), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417934/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5816750a355a277e532d46ffa5159f8a2ec1ce66686fb12f07ccab4b4f7b3c98'), size_on_disk=7324082, blob_last_accessed=1756926361.995198, blob_last_modified=1756926363.89619), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380985/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2ec986412ef2a9e6e59f4ea3dc278b27ab3975b5025d00bd1cd38fe1867b0fea'), size_on_disk=7208882, blob_last_accessed=1756926373.2251494, blob_last_modified=1756926373.9591463), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417876/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0713329434bfbd55f1998400d9d44c1d8c7fec6cb1b646c0fdb926f1046f9b47'), size_on_disk=11246388, blob_last_accessed=1756926350.971246, blob_last_modified=1756926352.0472412), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380980/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e36f58da696f0827c72fed8647c2a45a0499fdc5d1859386dbf025388f07e16d'), size_on_disk=2288305, blob_last_accessed=1756926372.371153, blob_last_modified=1756926373.2051497), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417869/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8dd08ab28007f90a664a6fbde23298ae2bb0ded917d5d75b1275c41e3eb5f7dd'), size_on_disk=16604892, blob_last_accessed=1756926349.6872516, blob_last_modified=1756926352.7202382), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417649/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6baf4bcd82d9fe55b8dcdc7602c0d39d20954675ef4c5484331ff25dac8ad3f4'), size_on_disk=2672976, blob_last_accessed=1756926310.8234205, blob_last_modified=1756926311.4704177), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417764/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8a30e54d96d5b83981e37a0d8f6cae9e6584819a76a200f2730d9de32e282d02'), size_on_disk=2641764, blob_last_accessed=1756926331.099332, blob_last_modified=1756926332.088328), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417652/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d38c028233981eb275c44995eafa73172388a9e0e704fe278b0cedbab49d429f'), size_on_disk=12947754, blob_last_accessed=1756926311.0344195, blob_last_modified=1756926312.1414146), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417855/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ac2218ccb9f80664ce377351ea1dc84bf3f70b36006a86cffdb761a40ddab791'), size_on_disk=1981158, blob_last_accessed=1756926347.3992615, blob_last_modified=1756926348.3062575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417719/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cc0a365d1d79f0469571216394f0cb211b9096f2a567e0a347a18f0c496bac06'), size_on_disk=2076369, blob_last_accessed=1756926324.010363, blob_last_modified=1756926324.951359), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417903/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3ddfaef9dc57d0f63b8613408aba3af803dabd5ee87a1584edd9391b2b41bec3'), size_on_disk=11218813, blob_last_accessed=1756926355.9432244, blob_last_modified=1756926357.2232187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417709/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/395b1dd171ab9759e06a22580672bef65aac2acd7d548e38098d207c80071d89'), size_on_disk=717131, blob_last_accessed=1756926322.9983675, blob_last_modified=1756926323.6373646), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417870/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ca1bc9825b47ee9ac59e53bc6ada3b1a9e8aba1b57c57c57947afae7926054b'), size_on_disk=10710319, blob_last_accessed=1756926349.7052515, blob_last_modified=1756926350.7552469), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417742/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1a87c492aab479dd0c013fbe80e006f18ba80de751da600b95babcb3f6e88dad'), size_on_disk=2054520, blob_last_accessed=1756926327.3293486, blob_last_modified=1756926328.4213438), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380990/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4e04122d2830db35f2b82b29efaca846fe10d7638c88c3e4d3cf78bf10cf245a'), size_on_disk=2093813, blob_last_accessed=1756926373.9261465, blob_last_modified=1756926375.1611412), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f00bc431128183affc8964f9610f3e9486a043bcea5b31b3ebbcbd9b0070a448'), size_on_disk=6096918, blob_last_accessed=1756926377.3431315, blob_last_modified=1756926378.2571278), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381025/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aea8c4d9f3783191d3e89f4566811f01089f3b0b76b736c5167bda54693b0bc4'), size_on_disk=339954, blob_last_accessed=1756926378.7781255, blob_last_modified=1756926379.125124), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380958/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ef3849004b6c72205ea1b298cf6a586f1114061ca94925229eda191d4666885'), size_on_disk=3288900, blob_last_accessed=1756926369.617165, blob_last_modified=1756926370.303162), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417899/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b4349f94cb0679468e9c28a9439c1a71c88d7288adc3bdad0ca31a2590f1d4e0'), size_on_disk=7910137, blob_last_accessed=1756926355.3832269, blob_last_modified=1756926356.3022227), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380931/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/be287270314fed3dded81e0c24a66d8bd991b219fd6a157f1aeaa1690262e563'), size_on_disk=4324179, blob_last_accessed=1756926365.1611843, blob_last_modified=1756926366.1241803), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417776/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/01c4a97f92cd5e2919759d52d083eb824408a2fd7ade9413a060c6b20d233963'), size_on_disk=2759569, blob_last_accessed=1756926332.8593245, blob_last_modified=1756926333.629321), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417618/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d2ce91297551bc97b4fca46007f80c8846e5f9e9c78d9c1d7ded02fc31b3ef38'), size_on_disk=9239111, blob_last_accessed=1756926302.3654573, blob_last_modified=1756926304.3714485), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417862/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cea0cfbd611f8c6482ec33f8ea32f304a481d1c1225d3dacea432dd359fc976b'), size_on_disk=5244392, blob_last_accessed=1756926348.381257, blob_last_modified=1756926349.6162517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381026/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e41f2d7a1b084a404e7f5e9e003b27f88665a983ec73859f7eb42cd8bfa2bdd3'), size_on_disk=2857045, blob_last_accessed=1756926378.898125, blob_last_modified=1756926379.7281213), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417872/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7d71ca12594ab85797fca8c1b88947cb0f3ece93596f1a4c5adc04c11c0abf68'), size_on_disk=8522283, blob_last_accessed=1756926350.1742494, blob_last_modified=1756926351.180245), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417941/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/30188c510d2d7041e1525e304edc8f25b569a1771cf00844cb27300a052441e8'), size_on_disk=7032333, blob_last_accessed=1756926363.2551925, blob_last_modified=1756926364.2891881), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417680/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aa9e972e7427b1db5556008756651933c8792891339607dee2ee231733072d1c'), size_on_disk=18443288, blob_last_accessed=1756926316.5853953, blob_last_modified=1756926321.4263742), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417738/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c3d82a351044aa8cd5d43979369088ccf2f563022d4f7aa3683ef20923202770'), size_on_disk=19772535, blob_last_accessed=1756926326.3913527, blob_last_modified=1756926328.6913426), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417654/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/67d658902a2b91d0d36345be06dc00c85af1b88ba40c37deaa091705d97b3ffc'), size_on_disk=6323092, blob_last_accessed=1756926311.166419, blob_last_modified=1756926314.4344046), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417946/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef43c84affe53bbdefcd70cb97a4a4d1e483824b8b6f91e3360a11094609f815'), size_on_disk=4853893, blob_last_accessed=1756926364.0141892, blob_last_modified=1756926365.0591848), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417641/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/355d8669b1d4c5576d8aa900582800fdf1819360f0db91d5e0299342b49884fb'), size_on_disk=6322689, blob_last_accessed=1756926309.3414268, blob_last_modified=1756926310.7974205), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417796/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/52d8262071c9c9de8cd8fb52be9e67acf125b8ce67033749fb92989f0d1f9345'), size_on_disk=10184069, blob_last_accessed=1756926336.2803097, blob_last_modified=1756926337.4333048), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417627/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4027ab6ecd21364ca365bbe7192797ed924c817e6edc841d750111b2e3f6f45a'), size_on_disk=21089243, blob_last_accessed=1756926305.0144458, blob_last_modified=1756926309.6264257), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417651/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b95b6be3a7a6a5b4af3bbf15c5a4a6b3893f3030ef6d1dcfe79e6c6554e3d3cb'), size_on_disk=13551797, blob_last_accessed=1756926310.9544199, blob_last_modified=1756926314.6814036), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417630/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/26fdbadf7c1252a8fd73ac85ea7430d942d2d11ddc43ea1b2590aaa008e08337'), size_on_disk=3015575, blob_last_accessed=1756926305.7104428, blob_last_modified=1756926306.7214384), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417802/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ea68aebb727f77d846a145d43ec16fa2496e9c05d49a8791aa131b8e3e536452'), size_on_disk=4881005, blob_last_accessed=1756926337.2223055, blob_last_modified=1756926338.1273017), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381028/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8d40caa48e7b9d32d2a0548732ef91c01ede9d0f4f6d74f045817e818c52c808'), size_on_disk=18141950, blob_last_accessed=1756926378.9691246, blob_last_modified=1756926778.2687466), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417858/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2d7539fcb6297592fe3755ccc88e122441925fbaf9f5600d4daa88639630fd8b'), size_on_disk=1021831, blob_last_accessed=1756926347.8042595, blob_last_modified=1756926348.5072565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417815/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ee8a37cd03dfe154008718f2a0f7fd95b3fdf468f99b1fc73ca515433fa45930'), size_on_disk=6563669, blob_last_accessed=1756926339.6062953, blob_last_modified=1756926341.8062856), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381061/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d44d57e9c03bb6e6e750809eea88657c2960ed251b8acaa1352a07f4cb346a82'), size_on_disk=3789431, blob_last_accessed=1756926779.0527422, blob_last_modified=1756926779.8977375), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380997/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/add73ab9723ef88a47192fa169b89aaf17c16a0841fc9e53c1c5d73cfdc2ad50'), size_on_disk=15536494, blob_last_accessed=1756926375.1291413, blob_last_modified=1756926377.6141305), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380932/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f214422d02adb2b0a1037eb424d8574123cf27d8a5cd1418feb5733b0249f60c'), size_on_disk=6251591, blob_last_accessed=1756926365.6181824, blob_last_modified=1756926366.5551784), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380971/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0147090dd5a74f558b67151503ff635423a8af661b56d1faf06bb50a59adfb3a'), size_on_disk=1276600, blob_last_accessed=1756926370.9411592, blob_last_modified=1756926371.462157), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417836/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4fb964c54f4a37387c7844902d6171011a9a81efff244f365efcb156f630158f'), size_on_disk=3312893, blob_last_accessed=1756926344.2532752, blob_last_modified=1756926345.0702715), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417846/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a3dc440aac6a69d75ec73242a30e59d8183023a2677f1576b5e73d037d6e6654'), size_on_disk=2680468, blob_last_accessed=1756926346.0562673, blob_last_modified=1756926346.7392642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381044/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/89a79f2c5f6258380ff4776af49db6119ac0c04342686705cfde94feebd7c9ca'), size_on_disk=3576881, blob_last_accessed=1756926776.065759, blob_last_modified=1756926778.8987432), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381024/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5182750a0c8af8253cc98ecad8715519463d5c18adbe067488dfef91198c0d80'), size_on_disk=603678, blob_last_accessed=1756926378.7151258, blob_last_modified=1756926379.128124), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417638/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c8cface311cd55cd1542ed94d609da72cd2b3087422c28ad26c60e1d81a3e6bd'), size_on_disk=993699, blob_last_accessed=1756926308.4724307, blob_last_modified=1756926309.2634273), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417663/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cc7010c3ed70c3083d49d88c0f95dbe8f6eff873d4b5848eeae06a22df71a479'), size_on_disk=8938116, blob_last_accessed=1756926313.18441, blob_last_modified=1756926314.5674043), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380962/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/72be01aa42db736ffac8236c23c16edea00bdf5da88c3114fc94d9eb3daa4ee9'), size_on_disk=1460258, blob_last_accessed=1756926370.2361624, blob_last_modified=1756926370.6811604), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417814/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9f70b1469a6d0ad8758aa7a44e754e07ccc401906f8af403a72075d763919535'), size_on_disk=7618977, blob_last_accessed=1756926339.5142956, blob_last_modified=1756926341.0872889), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417676/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/53c35af59283469a22148b2841cb8574faf80d59162a33d269560283c9228af2'), size_on_disk=12006227, blob_last_accessed=1756926315.8083987, blob_last_modified=1756926316.8563943), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417666/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/38008595447ab722c1889d0e19babf74299822ac7733504a4f09e1af1e4ad1ac'), size_on_disk=7712422, blob_last_accessed=1756926313.945407, blob_last_modified=1756926315.073402), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417856/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3dc4e868f31759c9ac3d43f40097f5ed2f0efb6307e3bc1ccbed70c50a16ee4e'), size_on_disk=6126482, blob_last_accessed=1756926347.3832614, blob_last_modified=1756926348.3162575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417718/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3ba23a0e7c5b3a455e95fbf6a22b51687d5330c19207241bdfd189e3776f5ba3'), size_on_disk=3304954, blob_last_accessed=1756926324.0233629, blob_last_modified=1756926324.986359), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417942/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f3a5b1eb0f954ddf66a8dfa1a38465726d2251730d3898865535269a43c7fa4a'), size_on_disk=6911573, blob_last_accessed=1756926363.415192, blob_last_modified=1756926364.3521879), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417695/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d6ae8d39c2949235dd4d82e7a0e56d9f20c20ba5f1b7a445fbba1f0df29c88b7'), size_on_disk=7710772, blob_last_accessed=1756926320.4913783, blob_last_modified=1756926321.959372), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417678/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/825d9bcd10c008b68cc84e92f20608c0d52770ac028bbc4a9219f1cd210ab8c4'), size_on_disk=29320273, blob_last_accessed=1756926316.0503976, blob_last_modified=1756926318.3993874), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380972/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/91a8106ca7ac2babcabd1549072b2b730909e1386f827e732428bff2bb5251cb'), size_on_disk=6712599, blob_last_accessed=1756926371.1721582, blob_last_modified=1756926372.0711544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417888/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/961a35161b04a0e892f46a0a90b6d4776d7fe07ef82fc7016c95b1a885c4274f'), size_on_disk=9651505, blob_last_accessed=1756926353.298236, blob_last_modified=1756926354.65023), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417921/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/05fb4ff0001b88b711eee9f29ae30642d940667f7166121c9782f464057880f5'), size_on_disk=7891276, blob_last_accessed=1756926359.3782094, blob_last_modified=1756926361.2442014), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417799/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cba2fb579c7706a7ff38fad3c7c649000560c1052e681adea986c5cc02c02cfb'), size_on_disk=5688906, blob_last_accessed=1756926336.7133079, blob_last_modified=1756926337.7073035), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417867/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f4783b00d4674df92d84f9a304fb9d143c7b9e4754e3efcb9c072a49a9b9298b'), size_on_disk=8536709, blob_last_accessed=1756926349.3882527, blob_last_modified=1756926350.2882488), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417887/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/58707813ec63af2eb1a72b0c8317b4701b4777df8f828f2e08a0cbb1ccfa534f'), size_on_disk=9080847, blob_last_accessed=1756926353.0712368, blob_last_modified=1756926354.412231), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bd149efcdaa899981b965dc424d16fb66668a7666a059c6bc5043572331394c2'), size_on_disk=4513644, blob_last_accessed=1756926377.1751323, blob_last_modified=1756926378.1161282), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417820/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9d36f1c17151e3203186142abff734c0182377d5287b3f27d00917608501abb3'), size_on_disk=8116588, blob_last_accessed=1756926340.9562893, blob_last_modified=1756926342.0902843), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381033/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5fff567b96a4649d787e93ef8cdca2d8ce977b7f2eb25d6f78e50bc30c798bce'), size_on_disk=1712156, blob_last_accessed=1756926379.7391212, blob_last_modified=1756926380.3661187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417710/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9837ecd3769eaaf407d939ff09d6ae74d832630b27353251942bd262cd70fc35'), size_on_disk=330457, blob_last_accessed=1756926323.098367, blob_last_modified=1756926323.797364), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417767/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8d0afed33cd85298c709656da4b210e3fe87aa4d6822208d744200816d59b95b'), size_on_disk=3739401, blob_last_accessed=1756926331.2333317, blob_last_modified=1756926331.8623288), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380964/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3a63df07de544662fb04d5388f34b0c8068efbc19f464b673d8a860a57b5b422'), size_on_disk=956844, blob_last_accessed=1756926370.333162, blob_last_modified=1756926370.8741596), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417801/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4bbb1b3faac1a4c15eaac8c81260f4f18493d2a082915cbe4c480ce76a94140e'), size_on_disk=4600965, blob_last_accessed=1756926337.090306, blob_last_modified=1756926337.9153025), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417711/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e30dbc09cd2dfb4722d60d0833123542e9c5096f70b97867cd15d466aa7b927a'), size_on_disk=96859, blob_last_accessed=1756926323.1863666, blob_last_modified=1756926323.7823641), CachedFileInfo(file_name='GSE222268_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE222268_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e0f1d5b82fd97798d0c02d992c49c0e83d8dcf82e07e001284e78d9656355481'), size_on_disk=4411, blob_last_accessed=1756926300.1784668, blob_last_modified=1756926300.5344653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417737/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2e1d5b2306dd24257d4ba261baa42b324c9f4f8ca3a1d30e0f920c4f4a5f99ba'), size_on_disk=1342511, blob_last_accessed=1756926326.2713532, blob_last_modified=1756926326.9043505), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380927/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/38e67ea2fef57610ff63c0c298a6db65ae1c0a159f2b1e61940f2e65306b8fa9'), size_on_disk=7297320, blob_last_accessed=1756926364.969185, blob_last_modified=1756926365.7781818), CachedFileInfo(file_name='GSE178430_metadata.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE178430_metadata.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9673758f13ec096ec4a89642bbe34d60975f5f05'), size_on_disk=61, blob_last_accessed=1756926300.2374666, blob_last_modified=1756926300.2994664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417702/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/41123195fa339a4ffabf6d686a284c343d0519089a18b828818c92ec43a26eae'), size_on_disk=6213776, blob_last_accessed=1756926322.0263717, blob_last_modified=1756926323.0323672), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381032/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/335a75b123ae38273f1b1a10f16e9547eec356f68a1d3078926f011d3332e2b5'), size_on_disk=656385, blob_last_accessed=1756926379.6781216, blob_last_modified=1756926775.4077625), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417771/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8fe5513ae86b28ecca3d4c70bdb73097f578dd31b4d08a072960b61550f582fa'), size_on_disk=3970195, blob_last_accessed=1756926331.9313285, blob_last_modified=1756926332.8643246), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417716/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cd98cf97346b62e4f1efcace598c9c273d9ad9b8d8692e35f50b11d46f7ed8a6'), size_on_disk=5100666, blob_last_accessed=1756926323.8513637, blob_last_modified=1756926324.8793592), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417833/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/48792db399d7f0aab6765109e8c01abd61a2da93e5d0bdf7c325bcffe01fe113'), size_on_disk=8139822, blob_last_accessed=1756926343.3262792, blob_last_modified=1756926344.480274), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417786/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdd4770c9b3e18e86f9a46ff924e3a562e37b632d6f0e8ad11627956a3416e09'), size_on_disk=11354740, blob_last_accessed=1756926334.3243182, blob_last_modified=1756926335.4083135), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417900/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/45d5a0bbf39e542f22aa4e36186673d1246533dc513f006312a26286496f6ead'), size_on_disk=11235417, blob_last_accessed=1756926355.535226, blob_last_modified=1756926356.6632211), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4331f53e52f628f99a9695fa70339b08eb69a011cc8a3cbdb08dfa8e6c5d3ddd'), size_on_disk=10779086, blob_last_accessed=1756926376.8381338, blob_last_modified=1756926377.9141293), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417686/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6a42f24b250baf0d88696bc123f01494a400b3f03f2ff2a1fc828f9295721a16'), size_on_disk=12070748, blob_last_accessed=1756926318.2063882, blob_last_modified=1756926319.627382), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380926/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fd708dfb8084d767fde32c52acb58e785083ce7becbd2e160277f31460719346'), size_on_disk=6579161, blob_last_accessed=1756926364.8451858, blob_last_modified=1756926365.5441828), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380981/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/198a0b08846a11e72de25e0e9c44f495ef88c9260de2e036d240715004c04953'), size_on_disk=8535317, blob_last_accessed=1756926372.5771523, blob_last_modified=1756926373.8291469), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417602/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/26ae91a914e9ad095615c88ac3c62c99e28ae2687ccd66007f13ac8fbbffa3fe'), size_on_disk=3717181, blob_last_accessed=1756926448.0367467, blob_last_modified=1756926301.5714607), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417889/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/295f9efb0d78e3a116ce4022a7830297570446e2017a3fcd3b3d423c0c20664b'), size_on_disk=9617979, blob_last_accessed=1756926353.3102357, blob_last_modified=1756926354.7322295), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ad0e80a28e4cd2027893adee5afceab851576ed2637c1a8e94d0af2adae6ace1'), size_on_disk=5088900, blob_last_accessed=1756926375.41114, blob_last_modified=1756926376.3821359), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417804/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/16ee23ae4dbe8a4d968a614f5e6de65028b6edce012231a50eb776d65f949d34'), size_on_disk=2107502, blob_last_accessed=1756926337.7883031, blob_last_modified=1756926338.5083), CachedFileInfo(file_name='GSE209631_metadata.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE209631_metadata.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fe31b19357a717d3d51d1d201620d37fca79b1c8'), size_on_disk=61, blob_last_accessed=1756926300.2214668, blob_last_modified=1756926300.2814665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417735/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/634551506800bc76183ce179c8c8f324cb6f0f577f6cf0b629b74ce4f1e995d5'), size_on_disk=19171195, blob_last_accessed=1756926326.0393543, blob_last_modified=1756926329.4873393), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417793/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8a51bd4d953e697022a85f9b527afd45126988ba4bbb796d71c9e5f6c8fffc44'), size_on_disk=10797388, blob_last_accessed=1756926335.4883132, blob_last_modified=1756926336.7013078), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417650/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c4d103888f768ff3cb0c68f4b747cb8d8aad8058b75174160cfc42b2504407f1'), size_on_disk=1337049, blob_last_accessed=1756926310.89842, blob_last_modified=1756926311.4924176), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417865/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/348bfc1685f79e9ec7f559e96b61b8c43628d611378b6c4ead8bffea44415f1d'), size_on_disk=9233047, blob_last_accessed=1756926348.8982549, blob_last_modified=1756926349.9182506), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417868/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/52e3f224a0147264d4ce477d26f4e11ff0745c328eb3e2ac44209dba1c8c55eb'), size_on_disk=5039768, blob_last_accessed=1756926349.6852515, blob_last_modified=1756926350.6082475), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381046/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a96c1819b1010c40fc4d92418e407cc5b5f48f9e24883d4b4cdf3e02d23b688a'), size_on_disk=3071378, blob_last_accessed=1756926776.6277559, blob_last_modified=1756926777.855749), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381037/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5cef560daffa3a5b1e841a312937eca595ced0bbdb3e6c641b13359a0340fea1'), size_on_disk=2090047, blob_last_accessed=1756926379.8821206, blob_last_modified=1756926380.4171183), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417732/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f99cdb10c12a75e1d020125d68b5c5302263eaef050b5c6b0032e27a0ac9a23c'), size_on_disk=685742, blob_last_accessed=1756926325.7463555, blob_last_modified=1756926326.326353), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380987/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2eb045d9f88b952eed8f8354e77c682bc3003bd2e9eacb0eb0c2df5ca44a34cc'), size_on_disk=4463796, blob_last_accessed=1756926373.559148, blob_last_modified=1756926374.4241443), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381065/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/99cc73bc4c0908da04c01c7c59eff995a478ffc023c31fb970c7bb9789b2d70b'), size_on_disk=14673323, blob_last_accessed=1756926780.0087368, blob_last_modified=1756926781.5847282), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417640/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/411d9146359847b5805450f67cbde4f1cbd51e3c2fae4bbcd6295db7426cb2ca'), size_on_disk=7000528, blob_last_accessed=1756926308.9124289, blob_last_modified=1756926310.0344238), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/64a119c8028083521d3872e2daca1545f2f8753eb8acfc751bf0c6d25803a3f8'), size_on_disk=2003211, blob_last_accessed=1756926376.794134, blob_last_modified=1756926377.2761319), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417696/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2e1bce702520ca07d9037bd4de4d21bb30ef6b7fdf7de9d75ea232acc036a72e'), size_on_disk=31303382, blob_last_accessed=1756926320.7203774, blob_last_modified=1756926777.0657532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417810/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b807197acb33acdd1b70eddedd59dc4d831e271d231d6c9bb6f1281fb4d5813f'), size_on_disk=9808564, blob_last_accessed=1756926338.8082986, blob_last_modified=1756926339.7202947), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417853/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5995033a04f017892b30c025a1527f9b6420bf1e7636c466cdd8d1462cf228c8'), size_on_disk=2186899, blob_last_accessed=1756926346.9012635, blob_last_modified=1756926347.8172596), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417893/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f3b9f2c711cb806350320b93dba528efd6433d0ca10c39e5e2dd6aa89a408bd'), size_on_disk=9896637, blob_last_accessed=1756926354.2372317, blob_last_modified=1756926355.535226), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417800/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8e563a7d64bf06e7fe26c2817271f626273eed4c7dd19db0b9fd8e14c25f73b3'), size_on_disk=9904697, blob_last_accessed=1756926336.7953074, blob_last_modified=1756926338.958298), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417713/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c5d75f4a32cadceee844da0c5ce703483f575fb927990a1615ee37205137d986'), size_on_disk=524444, blob_last_accessed=1756926323.533365, blob_last_modified=1756926324.045363), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381048/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/53cd017f084781da16e69ae01b406ac0d8824a4d991e39b22c428e664a3808f2'), size_on_disk=2181901, blob_last_accessed=1756926776.9907537, blob_last_modified=1756926777.7067497), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380976/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d27d3efdccd104ca208d69fe9d94fe9011cb6212b34739679b1c8f550c5e9df2'), size_on_disk=12889551, blob_last_accessed=1756926371.6141565, blob_last_modified=1756926372.8021512), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417774/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ad00f794fb29d2a609375d63b09170f396f055ae392cd05a31d5f8fb5ede7a33'), size_on_disk=2914273, blob_last_accessed=1756926332.1763275, blob_last_modified=1756926333.217323), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380983/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eec33f1158aa017b073fcb16d6400cd800eb79e8c0b78b5e2f831d7413e4a736'), size_on_disk=4478939, blob_last_accessed=1756926372.869151, blob_last_modified=1756926373.6721475), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417834/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a69c65a4d32399355863fa14d4a1e92ecff2c92498e122e8e77796de16d42948'), size_on_disk=8537233, blob_last_accessed=1756926343.3672788, blob_last_modified=1756926345.2462707), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417926/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/303d44a38fbb646cf211ce1bd03fe2d24bb004f385c63d05ab8e707c5b283f0c'), size_on_disk=10766909, blob_last_accessed=1756926360.6072042, blob_last_modified=1756926361.6751995), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417734/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/10d4c6d9acc5b56370fc32062262ae3871008e20e960c6b352ac27a4e229c905'), size_on_disk=1495194, blob_last_accessed=1756926325.9453547, blob_last_modified=1756926326.745351), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417910/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/694d86a1e469836a53e1ce5dba0d64b77b8f68590a2034e2a1ef5310457dc7fb'), size_on_disk=4494010, blob_last_accessed=1756926357.3112185, blob_last_modified=1756926358.3942137), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417722/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f29055ed874a3a40735526808a6739884efecd8d7c5e7e9eb1fcaa8e495ade3'), size_on_disk=345818, blob_last_accessed=1756926324.5753605, blob_last_modified=1756926325.2483575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417945/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6689ba375c993d00d1f0e716d3bc39604d68df654a67b2518c56f4d54c7f4ca6'), size_on_disk=11289854, blob_last_accessed=1756926364.0191894, blob_last_modified=1756926364.9011855), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417839/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a04fc093d2a4fe873de135165bd62a38a1cb9b29860881ed973022ff128dc6d8'), size_on_disk=6789775, blob_last_accessed=1756926344.5502737, blob_last_modified=1756926346.4242656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417944/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/01e0e606e4e8b2203e6497ffc8c231bce8672a43d83098ba838c8edfdb052c27'), size_on_disk=4563261, blob_last_accessed=1756926363.89319, blob_last_modified=1756926364.6951864), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417681/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6e4ea424e9eab4dc3ea304621992e1db158565a8ce30374980b1084a653a0132'), size_on_disk=7689435, blob_last_accessed=1756926316.934394, blob_last_modified=1756926318.231388), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417779/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/918376b5cb0b63e3e531ac990854e5a7b461e7251f5ed562c5a92cbf6a5a0141'), size_on_disk=9459105, blob_last_accessed=1756926333.1593232, blob_last_modified=1756926334.2033186), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417917/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2118491b5ba6b4af1c0d394eec3139b0258cf3a4e42b4d1a02ab177247852081'), size_on_disk=14019020, blob_last_accessed=1756926358.4622135, blob_last_modified=1756926359.8632073), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381064/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8569620c4d2efb8dd163aa69d8edf8f855b96eba637e754a126a88e6f0c73192'), size_on_disk=20598404, blob_last_accessed=1756926779.800738, blob_last_modified=1756926781.1587305), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417871/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e44cb883893b5e30baa752dfbd773b31795f2072a7ec4f473371f435989cad42'), size_on_disk=6264896, blob_last_accessed=1756926349.9852502, blob_last_modified=1756926350.937246), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/059cc8a8b1f7918ab1b6499d3d9843100b3b37fe6dcd57dfe9ae512c6030cad6'), size_on_disk=2109635, blob_last_accessed=1756926376.7021344, blob_last_modified=1756926377.469131), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417930/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62f17648a07c7675d6a101e3d12efeae247c742e37eaacf96f95e03dceedefde'), size_on_disk=20574235, blob_last_accessed=1756926361.2242014, blob_last_modified=1756926362.5701957), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417731/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/39ec5969bc8f3acceb3aa30b72f0c0101785a36da340f3983ad00d8cc80160a4'), size_on_disk=1557745, blob_last_accessed=1756926325.658356, blob_last_modified=1756926326.2043536), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381055/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0aad0d3724742845da7f0afae0c18ab5c992bec76c02e0ffda057f00d0894d35'), size_on_disk=3355008, blob_last_accessed=1756926778.3377461, blob_last_modified=1756926779.263741), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381058/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9dc97b5892fcc8af593c03fad2040396b9b10626a655319ad40b38acd75e1d7b'), size_on_disk=11534270, blob_last_accessed=1756926778.543745, blob_last_modified=1756926779.7317386), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417880/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b08db0a2399fc4e5b6f59c19b7af05b1294d0fac10b2c336b627234fa828f1be'), size_on_disk=9818886, blob_last_accessed=1756926351.5782433, blob_last_modified=1756926353.246236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380982/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d1e44b0b82ea22fb6c513ce02b49facf5f38b64b2baa91b24942fc6858125e37'), size_on_disk=6977161, blob_last_accessed=1756926372.8241513, blob_last_modified=1756926373.4951482), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417704/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/477a83e4a5eace957a036c5ec65ef4539442c078058c1a44a877763cb850c86d'), size_on_disk=459034, blob_last_accessed=1756926322.1393712, blob_last_modified=1756926322.8993678), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417724/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e5beee12039dfe6b4dae180a31ac0897dea725754554db43e2df9f99da94db58'), size_on_disk=4535402, blob_last_accessed=1756926324.69836, blob_last_modified=1756926325.9703546), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381027/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ba9aba2cc5b9bd9e83599ccba5920f028ac9c6936436dac50f45c606f3abd841'), size_on_disk=6537548, blob_last_accessed=1756926379.0151243, blob_last_modified=1756926775.5467618), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417688/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f21ccaca0f6782b6ce2b70b50c9dc93487b6aaec03db6c30d9cb0c9fc01dda42'), size_on_disk=16023102, blob_last_accessed=1756926318.5463867, blob_last_modified=1756926320.0283804), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417608/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d225f561536fca4d073d2418a32efc3df68e9666a3403c7d8b1ee0a520861322'), size_on_disk=873305, blob_last_accessed=1756926300.603465, blob_last_modified=1756926301.521461), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417897/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/309aab637d037638082524649ef7b0828885a98e786c64af01072d68968b7d66'), size_on_disk=7857749, blob_last_accessed=1756926354.8022292, blob_last_modified=1756926355.9902241), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381051/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ba017d941936d755013757711f5de4661acd187ef79524662d85485ba6588f34'), size_on_disk=8199472, blob_last_accessed=1756926777.322752, blob_last_modified=1756926778.4757454), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417824/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/65d997db1339fc5a8380a61fcf1ab1a6d28fb9142fc7d78c89236dc218b91ea3'), size_on_disk=8218856, blob_last_accessed=1756926341.7612858, blob_last_modified=1756926343.0512803), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417659/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/20f21bb1ba86fdbee1918776793b1ff2275be49933c72eaba90b8be8582f7692'), size_on_disk=15311232, blob_last_accessed=1756926312.2364142, blob_last_modified=1756926313.8474073), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380937/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e31f54581c4a4a47033065033f3f9266913edad654543dd07b20d0ffe54847dd'), size_on_disk=2691574, blob_last_accessed=1756926366.2071798, blob_last_modified=1756926367.1951756), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417644/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0c788a7ba70689a796af823df5cc14504dfdc854617952459367364c4498cdeb'), size_on_disk=949200, blob_last_accessed=1756926309.758425, blob_last_modified=1756926310.6444213), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417745/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ab84f2e8e76311cfe45a4b525b239186e835cab37f1e5222fdc35c286064e5e'), size_on_disk=1270105, blob_last_accessed=1756926328.596343, blob_last_modified=1756926329.1233408), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380989/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3c8b646955209386a1d97882163b7c9816463d2dea68fbed2cc960817ffeb332'), size_on_disk=12022144, blob_last_accessed=1756926373.7761471, blob_last_modified=1756926375.1511412), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/43f12fb4826be4c7a3dd64800fc72cbcab2a9ce5bed7f7b5b9aee974deb7c06d'), size_on_disk=1116877, blob_last_accessed=1756926376.094137, blob_last_modified=1756926376.6491346), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417913/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/35c1a160b767f9f5870216ed33a1dc24804690772a5baee58f623c6dc0632127'), size_on_disk=9135350, blob_last_accessed=1756926357.9462156, blob_last_modified=1756926359.3142097), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380944/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7331bfc92dc902a721083ac4ea027481c90ce204d7dca7c493fec8f284db0155'), size_on_disk=8432479, blob_last_accessed=1756926367.299175, blob_last_modified=1756926368.6141694), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417690/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/283af9b17d776b75f6ff6ad1f426d6d0e0e5aa1f2089a20910edcb6763191b69'), size_on_disk=12265448, blob_last_accessed=1756926319.171384, blob_last_modified=1756926321.4153743), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417739/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1c9e3fcff1dc467673efe70177cb4d678d7eff7b5b2bb3ded247d7a458b466dc'), size_on_disk=21974051, blob_last_accessed=1756926326.8623507, blob_last_modified=1756926328.7393425), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417727/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/414c702fdf3dd97c536f3237d2c38e0ef5ad8d08bb7c991ee252e16d57b201b6'), size_on_disk=2636905, blob_last_accessed=1756926325.0683584, blob_last_modified=1756926325.8093553), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380960/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/60771297311bf96c3213c2a96321062a06067c91d2bba2fce69a7a6d7a0c980e'), size_on_disk=560607, blob_last_accessed=1756926369.7811644, blob_last_modified=1756926370.3121622), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417612/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ee40f7af39e729ee79ef18d6777bbd632b7cc7811041a32d54f4a02b42638464'), size_on_disk=3509045, blob_last_accessed=1756926301.5874608, blob_last_modified=1756926302.6274562), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417881/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bf380975f31e362e8964000688bff364809bbe2e8e3efa9ceaa71253ca6beefb'), size_on_disk=8717095, blob_last_accessed=1756926351.848242, blob_last_modified=1756926352.9732373), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380988/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/27b6e71986cf2b7da20b64dbda0ba47e08753f7184ddd57ed288f11df88d21e1'), size_on_disk=16063760, blob_last_accessed=1756926373.7421472, blob_last_modified=1756926374.932142), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381030/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e5baa8386faa327b26d27d6c8e6b265dddac1d55565e81d724a1980b93d871a3'), size_on_disk=1867415, blob_last_accessed=1756926379.2021236, blob_last_modified=1756926379.6681216), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417714/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d6abeb724a8c8b23217f3465284564da5baffee59ee4e3af582e0a3705ae32c6'), size_on_disk=262443, blob_last_accessed=1756926323.756364, blob_last_modified=1756926324.256362), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417744/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/faf407f0b251cf5f58c8ac7becee4b995327fd9a7d6599c3045fde8a78e1e811'), size_on_disk=2548979, blob_last_accessed=1756926327.7843466, blob_last_modified=1756926328.7443423), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380968/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/549ef4e39a437adb99efecdc9b05fee3ca4b31c878b99f7a4cf1398f405ca049'), size_on_disk=3575926, blob_last_accessed=1756926370.7441602, blob_last_modified=1756926371.5941565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417832/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/268a3b7a91ccc3f3909042a711454c336b1887705de99442bda022bcbc26a01e'), size_on_disk=7655304, blob_last_accessed=1756926343.2332795, blob_last_modified=1756926344.3042748), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380948/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6ac219aa7f5be77c21bde60c80ccad59edf825e2a009d6a4b7fc8bd4d8be83d4'), size_on_disk=6046323, blob_last_accessed=1756926368.6691692, blob_last_modified=1756926369.5471654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380941/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9a23f03fee8d60d752bd0bc1e59e9205abea4f3a78e3272b9a03447f076383d5'), size_on_disk=3514837, blob_last_accessed=1756926366.9851766, blob_last_modified=1756926368.7591689), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417851/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2d8a571fae50598143744a1f13d40cec2ea8c2198e87aae17f96d7c8a00460d0'), size_on_disk=11051527, blob_last_accessed=1756926346.6472647, blob_last_modified=1756926347.6582603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417783/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9603c95097c451e616c54d52d3474b866453409905ea16365d019e77c6ca31b3'), size_on_disk=4176778, blob_last_accessed=1756926333.6983209, blob_last_modified=1756926334.4773176), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417835/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6245fc8d220aad556115d4b831f72ae6a9beac48ed6e2aba811284aa0ccab4f7'), size_on_disk=7888215, blob_last_accessed=1756926343.795277, blob_last_modified=1756926344.942272), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380970/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bdec4a51127eaf09abb5d25d7f3a732afc3b561105fd18ba1df44d04e6dd90a2'), size_on_disk=3545790, blob_last_accessed=1756926370.9301593, blob_last_modified=1756926371.5451567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417811/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/da92b1c1579a2295da085050305367bd02a47fd52a74d1ec2a9f227b5e808b11'), size_on_disk=6127159, blob_last_accessed=1756926338.8782983, blob_last_modified=1756926340.1172931), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380992/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dbc6b1de202e7fb25701c02607facc3e8c332550907ca0b70ba177533a311d6d'), size_on_disk=2551903, blob_last_accessed=1756926374.2281451, blob_last_modified=1756926375.0341415), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417773/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d7980069a740f581dbcfd9416372452203d636d8ad4c9352c3b2d6483cd27379'), size_on_disk=5178664, blob_last_accessed=1756926332.1683276, blob_last_modified=1756926333.411322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380969/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/10410488c74f35a493cef2ffc4a64b28b336be1fb32a2b6dac692e0d7fbd4271'), size_on_disk=1973405, blob_last_accessed=1756926371.041159, blob_last_modified=1756926372.1191542), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381053/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6ca1dc55841bbb6d8d8a83d1a87d00570c1eea7cf1963db8cb04ddd427711fb6'), size_on_disk=10277654, blob_last_accessed=1756926777.9197485, blob_last_modified=1756926779.9187374), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380965/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6b384b53be751cec9056e74c2c8ce9fe2e532cfbf6a38e43bb09ce74ec817f3a'), size_on_disk=12974487, blob_last_accessed=1756926370.3761618, blob_last_modified=1756926371.4361572), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417838/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0dcd4dc268902c8e0845a1ee81938a81a00412014d5b198a41a523cc3450dfc4'), size_on_disk=10178872, blob_last_accessed=1756926344.5452738, blob_last_modified=1756926345.3902702), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381042/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/80a0c18e8d146043e9c95c6a86552d28e28267a489b3e5c6f2a11319cf47b877'), size_on_disk=4477936, blob_last_accessed=1756926775.660761, blob_last_modified=1756926776.9177542), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417765/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bd2b5a1b1593db002ea9c159d4c40d1d8addddbf124207a7b32b0bb43e2e4d72'), size_on_disk=2696437, blob_last_accessed=1756926331.2103317, blob_last_modified=1756926332.046328), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417647/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8c01c928abe00b5afa6a2292820695797391b3671cc110b9026f5fc5cb8564ae'), size_on_disk=2891716, blob_last_accessed=1756926310.1614234, blob_last_modified=1756926310.93942), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417747/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7011e7ff4b64ca9c57bdab93cfcfc52938e7b0574b48417feb4f6185f6c118b9'), size_on_disk=4791771, blob_last_accessed=1756926328.7593424, blob_last_modified=1756926329.9543371), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381069/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aa8f6186814bf973f8b4448a0c4b692c31594e03905c75d5691b982d6ccde97d'), size_on_disk=13705106, blob_last_accessed=1756926780.5807338, blob_last_modified=1756926782.319724), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380940/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4f59a7f9287c427bed27910f08a041e460eff2490c21e2fbdaa5cc9348f5921e'), size_on_disk=6062382, blob_last_accessed=1756926366.8141773, blob_last_modified=1756926368.3051708), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417809/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/486100d34df42ce97476d9753f8d57edb5f53596eaeca2d0cf81f5f95a4f4e12'), size_on_disk=7376702, blob_last_accessed=1756926338.5872996, blob_last_modified=1756926339.9172938), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417891/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cabef83ee373e37d528b127d813f1535faede4b98c5524d83e9906df83558b07'), size_on_disk=12596609, blob_last_accessed=1756926353.9052331, blob_last_modified=1756926355.2992272), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417904/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cf9194917da2f4c6c3f0411256b2604e8c14959373d7431047de2e23cffbb9bf'), size_on_disk=7269877, blob_last_accessed=1756926356.062224, blob_last_modified=1756926358.1762147), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417908/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/128e54326b088977000f8caaba7d09e324281ed7fe0e3e5f2779783f0a82f293'), size_on_disk=3614805, blob_last_accessed=1756926357.1272192, blob_last_modified=1756926358.2542143), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381034/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/112a8e705f7536fa06c630165d3c409f87a5784ad626b891cc2b3919e24bdd14'), size_on_disk=250813, blob_last_accessed=1756926379.795121, blob_last_modified=1756926380.1791193), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417866/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b0424cd7567723443472cb266e090a30e2b64ee6b09bb1b94d8dcfdb9f0b0c43'), size_on_disk=9685580, blob_last_accessed=1756926349.109254, blob_last_modified=1756926350.493248), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417860/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/72e751d246e9b938ce9ab4380e2a1114ecd3c36d5829e3298f391c1edfefae24'), size_on_disk=3996315, blob_last_accessed=1756926347.9312592, blob_last_modified=1756926348.819255), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417729/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8383a0e86c2bfe15c46045980d6768eb4e7e3c4c157ff1ae7ce20144e245ca80'), size_on_disk=3058876, blob_last_accessed=1756926325.3113573, blob_last_modified=1756926326.103354), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417805/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/661f37ed294c22f6c3b02d5c1ea25794f5e33090c246185e891ee82153e2e5f2'), size_on_disk=3071166, blob_last_accessed=1756926337.8643029, blob_last_modified=1756926339.3292964), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417877/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b170370a220db5677b9c6193707d32d1d14b9082ca574b9562c4ac8ea4c72c26'), size_on_disk=10449263, blob_last_accessed=1756926351.0392456, blob_last_modified=1756926352.32624), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417924/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f2053fcaa7f21b1703e3491e167b457c29759b2fe9535f0cb2bfa257a1b86cea'), size_on_disk=6988370, blob_last_accessed=1756926359.940207, blob_last_modified=1756926360.8152032), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417905/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b9d291469b7a414bab157f45bfd3a1f7ffaaf931de25fec9eb18012a18b70939'), size_on_disk=4418829, blob_last_accessed=1756926356.1612234, blob_last_modified=1756926357.2652185), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380998/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6808b8e1fa74b807321826b2c54182a3b6e257f0a223be0a6a94caf9463959e9'), size_on_disk=16847111, blob_last_accessed=1756926375.2231407, blob_last_modified=1756926376.572135), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417932/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f040d4f594fe69143a7c3f04f0498be2a30fb9b5e78d42ec464fee65efcc42dd'), size_on_disk=9231914, blob_last_accessed=1756926361.4262006, blob_last_modified=1756926362.5771956), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417636/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4a3b2c7076107dcc47a87179cecab9e2051350c94e951a7aadc2ff0cf6fdbaca'), size_on_disk=43479381, blob_last_accessed=1756926307.6494343, blob_last_modified=1756926311.4484177), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381067/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/da9c5ab3c0f970f9c2af4c0f5f0e9a95d47769e8096ff6b9d76092becbc8517e'), size_on_disk=9837433, blob_last_accessed=1756926780.2977352, blob_last_modified=1756926781.5777283), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417754/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0d840353f1148a08928dfb3177a0abfe635c406fc77d6d9958743585ade6b872'), size_on_disk=1101891, blob_last_accessed=1756926329.758338, blob_last_modified=1756926330.215336), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381039/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5f95b6de8d0d73589c612313eaed6ce1e6adf734e795719f23df6f54cbbedc69'), size_on_disk=1656495, blob_last_accessed=1756926379.8751206, blob_last_modified=1756926380.283119), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380984/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a15ab5e5a0ead07dc2a530a1e4abb714027a95ce090c7a0eda5ec2bb714d903f'), size_on_disk=8070704, blob_last_accessed=1756926373.2251494, blob_last_modified=1756926374.1601455), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417624/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/28c1de2a4bd6b421cd05c3bb211ffde83e4b830127f106ec7be42ad288e26ef6'), size_on_disk=16758887, blob_last_accessed=1756926303.7884512, blob_last_modified=1756926305.6184433), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417703/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e82b21d919b84770e2d141eaea09aff82ef57c3ff3dbd15c588c1b8cff212c9e'), size_on_disk=1462841, blob_last_accessed=1756926322.0533717, blob_last_modified=1756926322.898368), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380995/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e0cae334f1cb71be0be1e8ceb5fc6ad23d22e4db20cb64b1314c3d080f2bb5f7'), size_on_disk=6315860, blob_last_accessed=1756926374.931142, blob_last_modified=1756926375.7271385), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417787/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/17f33e06b6bd77c0bbd3141b53fc2d9550b0c54e99b18fe79ddad931e3a283a9'), size_on_disk=12355842, blob_last_accessed=1756926334.5003173, blob_last_modified=1756926336.2953095), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417672/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f0741c46f1460c30d4d9b88ca1f7c0ca77a040cf25a34d82fab4c777a921ac26'), size_on_disk=18268758, blob_last_accessed=1756926314.9764023, blob_last_modified=1756926316.5143957), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417936/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8ba89583b8c822b64968df9eca8273d12270429f0f820d6027f13c66504c383d'), size_on_disk=4552381, blob_last_accessed=1756926362.205197, blob_last_modified=1756926363.171193), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380956/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/15047f69259f2cbc2cb08949d07ac6fde505bade6a0e3bb3912b183a48a4b288'), size_on_disk=2282680, blob_last_accessed=1756926369.4771657, blob_last_modified=1756926370.1431627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/225bf60f7c9796682210dc48d07a30d3dabab8ebc13abb7a84834279fa131020'), size_on_disk=5307353, blob_last_accessed=1756926376.2401364, blob_last_modified=1756926377.3751316), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417645/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/761eba472193b23cbf7735946e08aa1c4db5b1abd5125f1aad22ddabbf872f04'), size_on_disk=2080044, blob_last_accessed=1756926309.765425, blob_last_modified=1756926311.5084174), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417712/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/59cbfa83b9abe13f60d6a80b35afe857e3ffd761366133f60505312a2b5a72e2'), size_on_disk=641243, blob_last_accessed=1756926323.2163665, blob_last_modified=1756926323.7173643), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417923/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a32971eb62fe5c4c08298b1bdd97a916d8ef8f5898aee42aaf3fbaa8069e92d1'), size_on_disk=10004280, blob_last_accessed=1756926359.919207, blob_last_modified=1756926362.0851977), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417693/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/98a5478f5bd4feb033ac69510f2bf785c87777377b7b8b6dce24b8f89dbd6a03'), size_on_disk=18580692, blob_last_accessed=1756926319.7013817, blob_last_modified=1756926321.9783719), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417812/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9e8445f413d26051897f0fe3e7566b116a9a55062b78201990de334f17aecb31'), size_on_disk=19905089, blob_last_accessed=1756926339.0272977, blob_last_modified=1756926340.6232908), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417755/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f4ffef985aed69a80417b5179ef9923bd73378a93a97f2dff128b1f7716b4599'), size_on_disk=8437424, blob_last_accessed=1756926329.777338, blob_last_modified=1756926331.2423315), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417772/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a21b204a2da7654dd5bf4e7965a3c1e19159c16052232030787175a830ff0233'), size_on_disk=8204405, blob_last_accessed=1756926332.1323278, blob_last_modified=1756926333.453322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381052/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/652334151cb539b888d9cfcb60d9711d80bd74ec65cfa3799de10b2970ab9c09'), size_on_disk=5788579, blob_last_accessed=1756926777.7767494, blob_last_modified=1756926779.283741), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380999/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/acb2ee4711dab5800afb7ccbea72c8c80268b0a848723e7bd8ecf197decb982d'), size_on_disk=2605563, blob_last_accessed=1756926375.2281408, blob_last_modified=1756926376.1321368), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417751/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9a5733130de69115a731511dac8bf577fc5609a7f9649106ae9a12ffae7011ba'), size_on_disk=1660869, blob_last_accessed=1756926329.2023404, blob_last_modified=1756926329.9043374), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380959/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4693c1876e880fee1ec277fd5ae9127aafa9f40d41a3d5ed482dfcfa8cef55f1'), size_on_disk=873902, blob_last_accessed=1756926369.7061646, blob_last_modified=1756926370.1731627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417749/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8460c24102627d611c8918a1620078ee1bdf9929c44cdca863cd0dbb23f9cf1f'), size_on_disk=4938551, blob_last_accessed=1756926328.814342, blob_last_modified=1756926329.7143383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380957/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5a2362d35ac90fb1926051e6313886a649074b0022b128741a22a5ee24bb095d'), size_on_disk=5388789, blob_last_accessed=1756926369.5311654, blob_last_modified=1756926370.8571596), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417874/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6674c316eab3b03bbbe3502565efff7650445f5e1453933336d74938e42a14bf'), size_on_disk=6201897, blob_last_accessed=1756926350.5772476, blob_last_modified=1756926351.5142436), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417792/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/196cd0cf279f6bc85ca0dbeb1f089721f63eb167644d999b45d8cfe9a1f52acf'), size_on_disk=11982413, blob_last_accessed=1756926335.4593132, blob_last_modified=1756926336.651308), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417778/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/54e8ea03e214967eb4a1c7b50a086c4d7083d2c9c95dd0419e545fb0e8f18422'), size_on_disk=9345257, blob_last_accessed=1756926333.1513233, blob_last_modified=1756926334.0903192), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417933/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4fe347d88754646d39d93dd8a78e857d04d1903f7163a21495038b087d5f6b8a'), size_on_disk=12389872, blob_last_accessed=1756926361.7481992, blob_last_modified=1756926362.9061942), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417726/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/739e474fc54ffee0c7e6300d3abb1f83d33d48c78d137ed848f837fa186b9308'), size_on_disk=2430088, blob_last_accessed=1756926325.0663583, blob_last_modified=1756926325.6803558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380978/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a1d811dc3a4bfd44683ddf8bd0401a71d4941497c5cac8d8040a597fc5d26587'), size_on_disk=5902739, blob_last_accessed=1756926372.160154, blob_last_modified=1756926373.09715), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417878/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d3d105cc45280a5ea88c2bba754c0c5849d0cec7112a0926e9ed0c946e7f5cfd'), size_on_disk=8838404, blob_last_accessed=1756926351.2492447, blob_last_modified=1756926352.7352383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380993/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d568d29486ad3ec6f67f87b78cacb8b7102c591f979ec2bdb17ff75bde789670'), size_on_disk=3031778, blob_last_accessed=1756926374.496144, blob_last_modified=1756926375.2661407), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417892/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7ac6b85018422c74c894467b7b53b1c2dc7c5298fc31654bdea5ca70493e0a87'), size_on_disk=8194725, blob_last_accessed=1756926354.176232, blob_last_modified=1756926354.9712286), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417798/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0ac62fa7573f4b3c756fd9c280be4b97ec0c7c756e8c76307d4fab56d29e1c08'), size_on_disk=10509245, blob_last_accessed=1756926336.5193086, blob_last_modified=1756926337.800303), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417873/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d282d59c3c191bd7b7fa7251108224d0ac3929bc98f96a2c43f283f8a5cd046a'), size_on_disk=7609540, blob_last_accessed=1756926350.3852484, blob_last_modified=1756926351.373244), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417770/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0e7437c90c616b78ccc0963e0388385028b70d66fbe73e0ceb4e30e2d0239816'), size_on_disk=8252889, blob_last_accessed=1756926331.6563299, blob_last_modified=1756926333.0863235), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417603/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdfb1fd9b21341104e6423c684b0ae8a52ed1701eb3e232babb98340f953ea5b'), size_on_disk=4460026, blob_last_accessed=1756926300.366466, blob_last_modified=1756926301.2574623), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417795/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b995a48f28ae3baa145438a39c9fed73fa8689eb31e07ff02a3bc6bc827eb76e'), size_on_disk=8032882, blob_last_accessed=1756926336.1053104, blob_last_modified=1756926336.9993064), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381014/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5c5106dfb24bcfb1436aeb87b8218df873417ca2809aed71ca58e9b48ffacd29'), size_on_disk=1425306, blob_last_accessed=1756926377.5811305, blob_last_modified=1756926378.2571278), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417837/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/04d099809ca78bd87dcb9921398681ac64e827439049a37e97a71ba0828debfb'), size_on_disk=8348990, blob_last_accessed=1756926344.4012744, blob_last_modified=1756926345.6152692), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417785/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/209799965b03585e1143e881d87c5cea80d37e06637d2fc3f7e1ad2eaadfd3bc'), size_on_disk=11557515, blob_last_accessed=1756926334.1813188, blob_last_modified=1756926335.1353147), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417669/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8eaf08a45dbd72787c5250403f3d3079db9902e77527c7ca438abc740fdfb753'), size_on_disk=7376929, blob_last_accessed=1756926314.6724036, blob_last_modified=1756926316.0383978), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417733/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/542261ed8bf189205b611485f1a878f28cae5c90e452ba9cad687eacfdfc2814'), size_on_disk=2645461, blob_last_accessed=1756926325.8883548, blob_last_modified=1756926327.3873484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417819/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a1ba6808d672dbc9c58c26364c5cd9ed0b8a3041e6714844846648d3cd1aa050'), size_on_disk=3244517, blob_last_accessed=1756926340.7022905, blob_last_modified=1756926341.6952863), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417857/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/64f710e0b8a62bb6eddf3508f133b6b82af8bd21dd07f2319af6d07981085b1e'), size_on_disk=11109477, blob_last_accessed=1756926347.471261, blob_last_modified=1756926349.6202517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417841/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cb829be078dd8b533a90c2c548dfb2ce0455026be54c36572337e0a1ddeeb2cf'), size_on_disk=5791736, blob_last_accessed=1756926345.138271, blob_last_modified=1756926345.9902675), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417925/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8cdb783613764a35fd3a73db89463db6876951d443f5651a9a46877a53917f3f'), size_on_disk=912848, blob_last_accessed=1756926360.409205, blob_last_modified=1756926361.1532018), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417916/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2abad8555b89fbb5009e8f2a95e553a5ce579d1841ae4e71eec7b137f3282ef1'), size_on_disk=9236466, blob_last_accessed=1756926358.371214, blob_last_modified=1756926359.0582108), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417850/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c08fca103aa3dd1944dcf5c73b67397263842dc687b4101c8ab2c1a63d50205a'), size_on_disk=1182282, blob_last_accessed=1756926346.5122652, blob_last_modified=1756926347.3082619), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417635/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d2d1c241467bd5c55cf059f793b4236c99d48e59bec853d1052ff6c644e50d1b'), size_on_disk=3044373, blob_last_accessed=1756926307.2014363, blob_last_modified=1756926308.391431), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417606/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f76c81d98337c9e127bf5516af0dd135458fd622c01f2b5c9fd85ce6edfa4c7f'), size_on_disk=9502783, blob_last_accessed=1756926300.5794652, blob_last_modified=1756926301.6394606), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417653/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/59233342d3c3b71fce95d5295cf7c1e5ef3f4254dbc120229cb83b6c8115c759'), size_on_disk=12208357, blob_last_accessed=1756926311.136419, blob_last_modified=1756926312.3904135), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417629/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/29c9c86be94792d797005b106ab00d066634bea44fbf17e81738a00cbffa264b'), size_on_disk=22750780, blob_last_accessed=1756926305.5594435, blob_last_modified=1756926307.5804346), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381066/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ae017c72cf3b91ea3fdd40f63a126f640e7049340baca502ff3228c6ee40a7b4'), size_on_disk=13139272, blob_last_accessed=1756926780.000737, blob_last_modified=1756926782.6217225), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417902/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2d72fc510cd348af7611ca170b7804b228b74bd38e7ff5e437375c1cc926fa95'), size_on_disk=8952511, blob_last_accessed=1756926355.9322243, blob_last_modified=1756926357.2422187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380991/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dd86db1779581d5853d4d6a0fa2aeacdb00f92c832683ad788743f17db625db4'), size_on_disk=2640507, blob_last_accessed=1756926374.030146, blob_last_modified=1756926374.8661423), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381035/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/410edf5a6097a77bcec6880ec2618b4aec4bc10c44607163fe2b27aca0a05117'), size_on_disk=1451911, blob_last_accessed=1756926379.8691208, blob_last_modified=1756926380.5191178), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417657/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62398c518588ad11a939f95736cf0ed01a39890567cd4a8f05e9c9ba1155f9c1'), size_on_disk=10144285, blob_last_accessed=1756926311.5634172, blob_last_modified=1756926313.1174104), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417762/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c8b34d14be6dd44af03432d758f705cf81e86a46831c6deb4b0944248709630a'), size_on_disk=3024339, blob_last_accessed=1756926330.943333, blob_last_modified=1756926331.59933), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417633/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0b38824aeb2fac57eca256e3652539deb4bc221eeb5afc3701a997885f9ad9d0'), size_on_disk=19311587, blob_last_accessed=1756926306.8324378, blob_last_modified=1756926308.847429), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381041/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a53caa1961d9245d82db10989b8a8d601760ccf26f67666d5558937830c51e31'), size_on_disk=6621865, blob_last_accessed=1756926774.784766, blob_last_modified=1756926775.9747593), CachedFileInfo(file_name='GSE209631_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE209631_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/be49eef90682fbd55a5e60ada8b4c1c554e1b5a2ec1209996269db020d3bb074'), size_on_disk=4258, blob_last_accessed=1756926300.173467, blob_last_modified=1756926300.594465), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417697/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/198a3cd31740f347f0308db2914df4fc0fc303b4a9f040fbaf79fb5ca0170e7f'), size_on_disk=5521438, blob_last_accessed=1756926321.4153743, blob_last_modified=1756926322.4743698), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417611/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c82d882f6538d65316dd01e26a82af13e41717952c79e20dae6c3f1d9724ab60'), size_on_disk=3720891, blob_last_accessed=1756926301.4614613, blob_last_modified=1756926302.2704577), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417605/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f689b77eccd8d95d70fb616f4e2aa03a41201aec2a8c3c86849b7b145cb0f796'), size_on_disk=1787545, blob_last_accessed=1756926300.5184655, blob_last_modified=1756926301.74346), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417673/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/535d074f70f4553e24f3171fe5cfbd1821635ce7ca6ef93f8665876de620d6c9'), size_on_disk=6559693, blob_last_accessed=1756926315.1844015, blob_last_modified=1756926317.0733933), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381031/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b4705ac4577c8e7b116d1a1b677967f69412b0e4716e0de7fa7ee97bc9be4d74'), size_on_disk=4551217, blob_last_accessed=1756926379.2931232, blob_last_modified=1756926776.2707577), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417780/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4f1b7c3ded6441c8e906c3494ae63750d25303ee6f8d8e05120dc11ce8d79080'), size_on_disk=9025914, blob_last_accessed=1756926333.2923226, blob_last_modified=1756926334.387318), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417911/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3ca8b1b14564fbc90b44995a61a34d5a67482eb5fd867ce4f9f2196de2b73539'), size_on_disk=3051177, blob_last_accessed=1756926357.404218, blob_last_modified=1756926358.3022141), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417883/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8e23cc97e00adfd244a989f83702f4ce7a998ba7998dd95908a1b4ec256bc054'), size_on_disk=10709227, blob_last_accessed=1756926352.4542394, blob_last_modified=1756926353.675234), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417664/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/91fc5dce8497d1fc5466fb5ac31e44a20b295d8f97825500f256f6830ea2f983'), size_on_disk=8203399, blob_last_accessed=1756926313.1894102, blob_last_modified=1756926314.6624038), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381021/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3e80794742d10b6d120e70c4e4f1d7f77123ddf8aa18499f686b4abc8eadd325'), size_on_disk=1524611, blob_last_accessed=1756926378.410127, blob_last_modified=1756926378.897125), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417621/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/354c654ace9bc123bcd9009c56af518b98f1a547a7ccd79f348d0e533607a41e'), size_on_disk=20457307, blob_last_accessed=1756926302.5744565, blob_last_modified=1756926307.7874336), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380975/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ea018f4882a60f5fb07a4bbae51bd494e44e6df9b215d9da5e405687a567dcbf'), size_on_disk=8369150, blob_last_accessed=1756926371.6051564, blob_last_modified=1756926372.7471516), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380939/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dc6382d9dba7855504a6c7fa2a4adc44f564e25ff5c54eedd082c3cbe0a520a0'), size_on_disk=9235430, blob_last_accessed=1756926366.630178, blob_last_modified=1756926367.5861738), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417781/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9dd7989f90f8762246868ccd7d11c9ce876b2b33099efc3a4c5bf1bc7cbcca80'), size_on_disk=6923944, blob_last_accessed=1756926333.4783218, blob_last_modified=1756926335.3743136), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417784/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fd3dd53cf34f48c235e4f771378f42a5605446bbc62d108a5637a042513fd75d'), size_on_disk=4826642, blob_last_accessed=1756926333.7233207, blob_last_modified=1756926334.576317), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417919/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f5504428a85399cb212c74235abf9cec068dba03136a87ea79eae6d63a3cf430'), size_on_disk=7011211, blob_last_accessed=1756926358.6892123, blob_last_modified=1756926359.8312075), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417927/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8e639025209cff8d34b7e35a5fa371282bb725173225097a39f8f2101a49deed'), size_on_disk=7241057, blob_last_accessed=1756926360.6762037, blob_last_modified=1756926362.1171975), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381000/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/503f854145f3a220aa936072c3e66410d34914fef948992399a4addbfc977186'), size_on_disk=3904727, blob_last_accessed=1756926375.3401403, blob_last_modified=1756926376.5601351), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417748/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/db4c67c001a1f688f70918f0e25d3ddd996392b33b9f1144a14551bc0026d036'), size_on_disk=3246481, blob_last_accessed=1756926328.8123422, blob_last_modified=1756926329.6943383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381056/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/382194ca9758ddc18f059a649b13c5a21ead3a7f1fa5e012065315047a037a58'), size_on_disk=2351268, blob_last_accessed=1756926778.362746, blob_last_modified=1756926778.9827425), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381057/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4e6c617d22a655d32847df8b5334f4fb7236a15b8bf6e30aaabcbb0a4c55782f'), size_on_disk=17494867, blob_last_accessed=1756926778.5417452, blob_last_modified=1756926780.4997342), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417885/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6d5faeda491268a8ec88393883d743aac6cd14d4837698164ca32ca1052da36f'), size_on_disk=7668243, blob_last_accessed=1756926352.806238, blob_last_modified=1756926354.1422322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417661/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c6effb515a20066646b7863cb86e9dc012e14187e432ba7458cd6af18662bfd6'), size_on_disk=22122748, blob_last_accessed=1756926312.9054115, blob_last_modified=1756926315.6813993), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417699/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0c64ecbe6b245299fe04c04239d792fbae0f1b288562fe0c7912a24de56f0ad5'), size_on_disk=5707915, blob_last_accessed=1756926321.6203735, blob_last_modified=1756926322.8483682), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417816/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/86256a1b98c6d44750fd21ac9ea099262f1f0ec03d182b65741d669fb80f289e'), size_on_disk=10618044, blob_last_accessed=1756926339.7852945, blob_last_modified=1756926341.8372855), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417679/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bc48687cfd5c03c1a71787981180295dd3a27cf4d2c46bd26bb68cdf8b35925a'), size_on_disk=17346576, blob_last_accessed=1756926316.1203973, blob_last_modified=1756926317.84439), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417685/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/32edfbf9226b7ef1060ebe3e6986a1803d60408ccda969528cf4a59c5785c722'), size_on_disk=16967633, blob_last_accessed=1756926317.9163895, blob_last_modified=1756926319.5263824), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417852/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/43431a444b2761ed9b6fa4c7e6edfc8da31b64ff933408c517bc771e9f3e94dc'), size_on_disk=6967416, blob_last_accessed=1756926346.8272638, blob_last_modified=1756926347.8462594), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756926300.2174668, blob_last_modified=1756926300.2804666), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417842/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c763016dcbf1b53df840e3ea3c1b45234e2d9e797114000efaf692e2c394d487'), size_on_disk=9967694, blob_last_accessed=1756926345.1472712, blob_last_modified=1756926347.1812623), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417610/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a93c708ade83c1c4521589a51835a1dcc98972ab900e0eefdb8ce34e64394bf4'), size_on_disk=2566580, blob_last_accessed=1756926301.328462, blob_last_modified=1756926302.4814568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417831/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/53d810292e5a513a8ab543fe18783f8d493cedbadc7c97400dc103a4675954df'), size_on_disk=6767934, blob_last_accessed=1756926343.12528, blob_last_modified=1756926344.4612741), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417723/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f397cef2d9c491fb034826727c63b3c224f6ed791c9b4dcf884c63c55c9961c'), size_on_disk=2800893, blob_last_accessed=1756926324.70836, blob_last_modified=1756926325.4453568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417769/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e640018be0b2de04e0ed77c7586c413990876e91348531f43511c20da5fe279a'), size_on_disk=11568261, blob_last_accessed=1756926331.59533, blob_last_modified=1756926333.0913236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417705/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/92b4f5af6793c98b854bcca83c86ac0cb41ec4e40ec23d3ba9c4ff287852c63c'), size_on_disk=332184, blob_last_accessed=1756926322.5923693, blob_last_modified=1756926323.1503668), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417691/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/48de5a2968610e5ab855522d27f535d40e1973c1835ec32fcfa5bcd91a0795dc'), size_on_disk=14481663, blob_last_accessed=1756926319.447383, blob_last_modified=1756926321.2093751), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417948/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d43ea802a452aa9d57004381e1688222e61bd313b107077c1c663555547d1d44'), size_on_disk=1748124, blob_last_accessed=1756926364.3661878, blob_last_modified=1756926365.038185), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417646/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2b3d78fc052de8647b1ba6e83245b5f375b4e6c10952feff795855160458a1d5'), size_on_disk=396843, blob_last_accessed=1756926310.1544235, blob_last_modified=1756926311.0694194), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381050/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f3c3bac37a1d4ec633edf53ba305c970eb3de412fd54dfbaf0cdf89f39574334'), size_on_disk=1518412, blob_last_accessed=1756926777.1687527, blob_last_modified=1756926778.028748), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417728/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2be09708238c50a31b4e9b334c6202c26eaa6a950ebf3de4287651999d45097e'), size_on_disk=3385901, blob_last_accessed=1756926325.0603585, blob_last_modified=1756926325.869355), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4c4a265f9e3e8d373550ea58ca1cde6094c823f090336974fde3e7cee117fd99'), size_on_disk=1770007, blob_last_accessed=1756926376.6771345, blob_last_modified=1756926377.1011326), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417875/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8dc3634dae23cad7f562d15903341a67d3048929bd2198e53f71529b00a3508a'), size_on_disk=11490812, blob_last_accessed=1756926350.6762471, blob_last_modified=1756926351.7832425), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417775/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2ca9ba5e1394191206945f41168d555519fe3963d4c39f251c1ead57f2a323f4'), size_on_disk=1059891, blob_last_accessed=1756926332.3723266, blob_last_modified=1756926332.773325), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417803/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1fd8b542fb1b0e18bc5346c50ad85da440ff6190a987467ec6876ed3ec51a4e8'), size_on_disk=4928668, blob_last_accessed=1756926337.5433042, blob_last_modified=1756926338.7182992), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417741/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/734d5947cb26e877b6ae9e9adaf84575974a4fe2fb464ba11c4320906f17a42a'), size_on_disk=4566889, blob_last_accessed=1756926326.96635, blob_last_modified=1756926328.5653431), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381038/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/415f62b8333a02df8a87ba85e1d0de30a6cc9fdb07f7c8dc02d30f81ed4c14ef'), size_on_disk=2424198, blob_last_accessed=1756926379.8751206, blob_last_modified=1756926380.4921181), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380967/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3abcaaa4db306d580dc27882172ced53d591b8aa69a1805bd2a9eea0448ff941'), size_on_disk=1245882, blob_last_accessed=1756926370.6731606, blob_last_modified=1756926371.1021585), CachedFileInfo(file_name='GSE178430_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE178430_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/be2c4af55a502d7fa22abb28afe6db347950327bde1bc7e97dbb68fdb0c59f3d'), size_on_disk=9398, blob_last_accessed=1756926861.9192877, blob_last_modified=1756926300.5344653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417682/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/83b3100cfd1dc6918855c4a711ff607b36f40971655733855a7df353b4d5fca5'), size_on_disk=14037100, blob_last_accessed=1756926317.1663928, blob_last_modified=1756926319.1013844), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417822/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62cbbb0172158a2c0776000136424032a794757ca9e9f971e5f445deb20e8579'), size_on_disk=6040431, blob_last_accessed=1756926341.3882875, blob_last_modified=1756926342.8302813), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417626/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1981a2a1e321acc5c35bbeb9b61a12636c27f28d6e216de20093afdbee9f8689'), size_on_disk=12046611, blob_last_accessed=1756926304.4464483, blob_last_modified=1756926306.2084405), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380961/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fc4356aaa2a30217145b4aecc4f3fdd2575fcd696a2603fea3d8112998f770c7'), size_on_disk=798599, blob_last_accessed=1756926370.1791627, blob_last_modified=1756926370.589161), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380953/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5b3410430d33f3123b935982be90037f2d0a8e657f6b0b70a7042846d5ddd1b0'), size_on_disk=1192961, blob_last_accessed=1756926368.8401685, blob_last_modified=1756926369.627165), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381071/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cf15be48b47b6c369c6a4290b783b9b22b01d6cb89d04c738c275241548b811b'), size_on_disk=2543373, blob_last_accessed=1756926781.2297301, blob_last_modified=1756926782.309724), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417623/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/562a43ff381a2ca177fb1cc3dbe47fc2cf7cade495bad511ca7ed75aefca7a98'), size_on_disk=19049552, blob_last_accessed=1756926302.696456, blob_last_modified=1756926305.4934437), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417715/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ac5effc2ec07a8fb49bac4d77b7d4129a18ae2180807874ea02d1c252a3b68bd'), size_on_disk=520286, blob_last_accessed=1756926323.8693635, blob_last_modified=1756926324.477361), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417859/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e4c5915df6cb5bb450e1948d8c43bbe031d09ec345688ab51f2d8ea60f3be09f'), size_on_disk=12709203, blob_last_accessed=1756926347.8872592, blob_last_modified=1756926348.9972544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417937/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e432985b09ba72eab54fc008b87b20a718dbae96d920a61b47de7e16d214227c'), size_on_disk=6445266, blob_last_accessed=1756926362.6341953, blob_last_modified=1756926363.6641908), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417763/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/91d8a58ead35fc52f7c2649ce9509a92236a20ca8c26f5f2ef4ba1a38e64afb1'), size_on_disk=230495, blob_last_accessed=1756926330.8943331, blob_last_modified=1756926331.4653306), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380954/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/338886924ec42501a99a4b842e18fdbed28200d52daaf43e00ee601a147e0188'), size_on_disk=4903852, blob_last_accessed=1756926369.2841666, blob_last_modified=1756926370.101163), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417707/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/89192f5007f730a90c823219ea107c799935f5e824963090abad813603160fa8'), size_on_disk=2615872, blob_last_accessed=1756926322.9183679, blob_last_modified=1756926323.9473634), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417622/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/28eed3b67a6206b453d8adb775edf2bea32cfd54ba5b37d51787f67280e0317f'), size_on_disk=20411775, blob_last_accessed=1756926302.674456, blob_last_modified=1756926304.8564465), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417758/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/54f8e8d84e5b8b9457aaa143829fcfd5a4715d0fb184478ce659cd6c6efe7be1'), size_on_disk=4176901, blob_last_accessed=1756926330.0203369, blob_last_modified=1756926331.1383321), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417939/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a0fc7804b026f95809f33087deb75f00beeb9635475b62864d3e58e948361bfa'), size_on_disk=9099632, blob_last_accessed=1756926362.9701939, blob_last_modified=1756926363.9411898), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417658/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b6cf4c024743be14fcd5730f0ddbb960dbcd83d07adf8668639f9a1cdf195368'), size_on_disk=8754108, blob_last_accessed=1756926311.5764172, blob_last_modified=1756926313.1134105), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417829/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/63a9af789279180a07d8e8afe07bb0e9b8aa32c4340e01347a6f7ee809198308'), size_on_disk=5677879, blob_last_accessed=1756926342.904281, blob_last_modified=1756926343.7332773), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417721/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5457a24eab864fb369ae0e6fbdb302876e2a6c65ae83444f18bc01ad08ecfc0c'), size_on_disk=2073045, blob_last_accessed=1756926324.3243616, blob_last_modified=1756926324.9983587), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417759/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b2dad4cf4b03c846adf9d5229aeab340e58034c751bc68b37bb0c8402a7c071b'), size_on_disk=1372312, blob_last_accessed=1756926330.1243365, blob_last_modified=1756926331.1623318), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417782/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d6624acaa7c4ec21ae02d6d1322b8669a148308095e714b41bff868483fe2fa9'), size_on_disk=11742567, blob_last_accessed=1756926333.5273216, blob_last_modified=1756926334.824316), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381029/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6431cec7b7404a0773d8715761e2b161f01ebd8471950e71e17dad8976d83f3c'), size_on_disk=7229691, blob_last_accessed=1756926379.0071244, blob_last_modified=1756926776.565756), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417879/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6714b3f5155dd540962a7541625f45e14e6a93b4c01e913790b70818e50c821b'), size_on_disk=8601872, blob_last_accessed=1756926351.4612439, blob_last_modified=1756926352.8182378), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380994/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7c5f237d9c32233a005e39386360f02d9e1932f9ac267ac3458f96aaf59e01ef'), size_on_disk=2496053, blob_last_accessed=1756926374.8051426, blob_last_modified=1756926375.3471403), CachedFileInfo(file_name='GSE222268_metadata.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE222268_metadata.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4459325dc2deeb9f00d803b89cd8fdc823ed383f'), size_on_disk=61, blob_last_accessed=1756926300.2714665, blob_last_modified=1756926300.3444662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417607/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/623d085888c300fba1d05a9018ec3d4c31912f2b91a872ef34feda3322889b70'), size_on_disk=6809356, blob_last_accessed=1756926300.611465, blob_last_modified=1756926301.75846), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417615/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3b8b3c4c686238c7c642151bbdcb2ba7796de56c1922e558a1e7f08507dc5abb'), size_on_disk=2866472, blob_last_accessed=1756926301.8314598, blob_last_modified=1756926302.5744565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381016/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0e597280417f8f8b5b476bc9d66033afc9412759f5e2f9bd9f3367932cc6f03f'), size_on_disk=820782, blob_last_accessed=1756926377.9791288, blob_last_modified=1756926378.4041271), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417777/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bd953fd8a3ee2a5c90382c1c9adc8091fbca801aa87f07a409b6dd5a9704421f'), size_on_disk=1267333, blob_last_accessed=1756926332.9623241, blob_last_modified=1756926333.6353211), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380930/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/734e9bc8119c7a507e9cf84f9926b1f9d06ccc8b0062a9b4dc36631f6f1bc6d1'), size_on_disk=4604638, blob_last_accessed=1756926365.1301844, blob_last_modified=1756926365.9251812), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417616/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0b0f3c9481fd0e254abd544ba803de0c9da9b3a686451975f981d02fc481148d'), size_on_disk=15058562, blob_last_accessed=1756926301.8254597, blob_last_modified=1756926305.0234458), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417655/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/25d98ba17be9b354b0ec7a0e513a6ff9d4593dd1cadf269f28bcea1ca9c71565'), size_on_disk=5461224, blob_last_accessed=1756926311.5504172, blob_last_modified=1756926312.9474113), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381019/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3023e878b223b00b77d3411a6d20195b85aa7e61a8445fe725ff694c77816053'), size_on_disk=1022843, blob_last_accessed=1756926378.3191273, blob_last_modified=1756926378.864125), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fc6783c3340c869bc3838a9a9d5abb8bca67361feeef12c42b6cf478d73bfd66'), size_on_disk=9020794, blob_last_accessed=1756926366.2571797, blob_last_modified=1756926367.2561753)}), refs=frozenset({'main'}), last_modified=1756926783.3167186)}), last_accessed=1756926861.9192877, last_modified=1756926783.3167186), CachedRepoInfo(repo_id='BrentLab/mahendrawada_2025', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025'), size_on_disk=94317969, nb_files=8, revisions=frozenset({CachedRevisionInfo(commit_hash='3b912489743a1797199beb023a49b14f7e3ba19d', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/3b912489743a1797199beb023a49b14f7e3ba19d'), size_on_disk=1957331, files=frozenset({CachedFileInfo(file_name='chec_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/3b912489743a1797199beb023a49b14f7e3ba19d/chec_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/dd3e967d8c54c6056dc9fd6bdb921c3eed9d10df3a449273bd08faeb1b936765'), size_on_disk=1220601, blob_last_accessed=1759345153.538238, blob_last_modified=1758569549.9902885), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/3b912489743a1797199beb023a49b14f7e3ba19d/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/c8cbc56ef9fad47193120cce2affd35374b8373d'), size_on_disk=18689, blob_last_accessed=1759345153.4962385, blob_last_modified=1758569549.1302917), CachedFileInfo(file_name='features_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/3b912489743a1797199beb023a49b14f7e3ba19d/features_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/618ea89d880c8a1b4c6d05cc3c13a70cbb05784d10bf2b6c4fc954705203096c'), size_on_disk=431436, blob_last_accessed=1759345156.897212, blob_last_modified=1758652945.6395862), CachedFileInfo(file_name='rnaseq_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/3b912489743a1797199beb023a49b14f7e3ba19d/rnaseq_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/4411789f50aab449658c100f2758ceb410022a0a30f956fd5fe41152915580f9'), size_on_disk=286605, blob_last_accessed=1759345153.5952375, blob_last_modified=1758569588.8621461)}), refs=frozenset(), last_modified=1758652945.6395862), CachedRevisionInfo(commit_hash='8bea431be57e21633be4174df291540b1fae212a', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/8bea431be57e21633be4174df291540b1fae212a'), size_on_disk=18603, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/8bea431be57e21633be4174df291540b1fae212a/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/6a2a5a6a8006e386c0e47a2a98151442046d3d41'), size_on_disk=18603, blob_last_accessed=1758568089.249362, blob_last_modified=1758568089.246362)}), refs=frozenset(), last_modified=1758568089.246362), CachedRevisionInfo(commit_hash='874a5dfe4052a1e71b6544a2e5b2c99ad4286c04', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/874a5dfe4052a1e71b6544a2e5b2c99ad4286c04'), size_on_disk=18603, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/874a5dfe4052a1e71b6544a2e5b2c99ad4286c04/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/685c0e56e0c68e20e3c2af8e38267095d686c03c'), size_on_disk=18603, blob_last_accessed=1758569041.2889845, blob_last_modified=1758569041.2879846)}), refs=frozenset(), last_modified=1758569041.2879846), CachedRevisionInfo(commit_hash='af5ac9dc922b7fbd14f460c2fe94b727db1e1245', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/af5ac9dc922b7fbd14f460c2fe94b727db1e1245'), size_on_disk=92323432, files=frozenset({CachedFileInfo(file_name='reprocess_diffcontrol_5prime.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/af5ac9dc922b7fbd14f460c2fe94b727db1e1245/reprocess_diffcontrol_5prime.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/eba528776f8fe8da63c39ee0660527bc35f989ac6a976daf09270860cad6d735'), size_on_disk=92302997, blob_last_accessed=1763665270.5143101, blob_last_modified=1763578870.280984), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/af5ac9dc922b7fbd14f460c2fe94b727db1e1245/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/8f0cf5e3dd2bb5bdc2f8fde970e31a2404481fec'), size_on_disk=20435, blob_last_accessed=1764298314.9501038, blob_last_modified=1763578711.307058)}), refs=frozenset({'main'}), last_modified=1763578870.280984)}), last_accessed=1764298314.9501038, last_modified=1763578870.280984), CachedRepoInfo(repo_id='BrentLab/hughes_2006', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006'), size_on_disk=11969274, nb_files=11, revisions=frozenset({CachedRevisionInfo(commit_hash='51ae7addf429c955a97934a15b242ff8a2ccd653', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/51ae7addf429c955a97934a15b242ff8a2ccd653'), size_on_disk=5701, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/51ae7addf429c955a97934a15b242ff8a2ccd653/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/f799807a7bf81229b0d063e6c939339c1f5c90f4'), size_on_disk=5701, blob_last_accessed=1756855057.8437808, blob_last_modified=1756855057.8417807)}), refs=frozenset(), last_modified=1756855057.8417807), CachedRevisionInfo(commit_hash='0de73d0932e423cfbbfbc1b21d029c96490dc200', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/0de73d0932e423cfbbfbc1b21d029c96490dc200'), size_on_disk=8791, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/0de73d0932e423cfbbfbc1b21d029c96490dc200/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/76efc047ec3c08e362cfcaac776b7e05c02f8a6b'), size_on_disk=8791, blob_last_accessed=1764712744.7980804, blob_last_modified=1764712744.7960804)}), refs=frozenset(), last_modified=1764712744.7960804), CachedRevisionInfo(commit_hash='1009546961df1cf9743a03383826d8a5e010bb38', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/1009546961df1cf9743a03383826d8a5e010bb38'), size_on_disk=17152, files=frozenset({CachedFileInfo(file_name='metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/1009546961df1cf9743a03383826d8a5e010bb38/metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/54173aa9fff98e5b077e2b24e139ad600a6d7cea74a47b0305cb0fbdf1c14d9b'), size_on_disk=8362, blob_last_accessed=1764713263.750364, blob_last_modified=1764713263.741364), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/1009546961df1cf9743a03383826d8a5e010bb38/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/572115c6e031637a3ac876795a6f91d3653bf5e2'), size_on_disk=8790, blob_last_accessed=1764713262.7733643, blob_last_modified=1764713223.5823674)}), refs=frozenset({'main'}), last_modified=1764713263.741364), CachedRevisionInfo(commit_hash='cc20720c4fe7de9151a051aafbd41e6de2b4cd84', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84'), size_on_disk=11937630, files=frozenset({CachedFileInfo(file_name='knockout.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/knockout.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/5e000e2e9401011c9ecc81655c94d55b7f9469ed856216fa21cef2883a904c93'), size_on_disk=5571518, blob_last_accessed=1756856029.2493467, blob_last_modified=1756856029.2143471), CachedFileInfo(file_name='overexpression.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/overexpression.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/9b8cc1feede780efe0e8537555ed1cbbf067e7fa9aa7a7edc74e5a343c64a443'), size_on_disk=6337781, blob_last_accessed=1756856028.536351, blob_last_modified=1756856029.230347), CachedFileInfo(file_name='metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/e27a67137876123c68c229705cfbb8e06ed3ee76a3a868b66d80d624f8d03671'), size_on_disk=7942, blob_last_accessed=1756856028.533351, blob_last_modified=1756856028.9083488), CachedFileInfo(file_name='checksum.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/checksum.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/973b686313e32c96b6e0ef1dbc84020857c073ef'), size_on_disk=159, blob_last_accessed=1756856028.5883508, blob_last_modified=1756856028.6513503), CachedFileInfo(file_name='parse_hughes_2006.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/scripts/parse_hughes_2006.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/0c2645bdc0ab7f6be38a428a0a92752e9d3f88d5'), size_on_disk=9255, blob_last_accessed=1756856028.5803509, blob_last_modified=1756856028.6393504), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/842b551fb3d1ba1e6f1daa35157a0c7c616f7655'), size_on_disk=8514, blob_last_accessed=1756855276.4871445, blob_last_modified=1756855276.4861445), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756856028.69735, blob_last_modified=1756856028.7793496)}), refs=frozenset(), last_modified=1756856029.230347)}), last_accessed=1764713263.750364, last_modified=1764713263.741364), CachedRepoInfo(repo_id='BrentLab/callingcards', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards'), size_on_disk=49606009, nb_files=137, revisions=frozenset({CachedRevisionInfo(commit_hash='9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0'), size_on_disk=49588983, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6140/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/66c2da17957d7e6a8b159d3573879ac779d0094e35150b51e2643f7ddee36e1f'), size_on_disk=332958, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4236548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=MAG001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/30b63d014686a103b5e7fc6571e902fa9c08630a96557cc12ec9dfcf60871306'), size_on_disk=347333, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.559651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR007B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6f0620b102e7cbfadced7d5530545a185036b53118d3e2b7f074c56403d80f4b'), size_on_disk=263185, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7616518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7295/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/19bd07f913e0af3460e6b4b5091971ceed8f4aa510cf18b82ec36dabb67c68b8'), size_on_disk=272619, blob_last_accessed=1763591393.1151721, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6863/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec534c91cae4413d30477014131d39b51c9c236dfbf150874bd21ec3de821613'), size_on_disk=389025, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648269.6376605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7118/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/be8bd409b83526d5671501de6ef2980177955fdcc3a13d5e153d2593493b2a1e'), size_on_disk=346308, blob_last_accessed=1763591392.9281735, blob_last_modified=1758648270.1336627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7297/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4bd4088cbfcccdf96def916bb8fd9d673327d3e57c2a4a3e57299743ce4aca7'), size_on_disk=261344, blob_last_accessed=1763591393.588169, blob_last_modified=1758648270.6516652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5301_5088/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/65ef775934339b80eda59a141cf791a74e1c6f12513d8143307cebe2f11c3738'), size_on_disk=410106, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.9516528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5617/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bc01241e8363373c995a0fa7439aa1e432a60eb8a7805124fff302311f638361'), size_on_disk=351950, blob_last_accessed=1763591393.6541686, blob_last_modified=1758648268.1026535), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7367/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a7778bb24506047963575fa96126157895c7f329996c729c3e7d6070595955c3'), size_on_disk=324361, blob_last_accessed=1763591392.1631787, blob_last_modified=1758648270.628665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4d098c33564f99ec6aed77d585d1f6e871806f441f099790eef4d6478e322af'), size_on_disk=264459, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7486472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6073/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d68ca6411ba47eb63adad81e88fa1341a0ee269897268957ae936dcc9ba7788'), size_on_disk=450031, blob_last_accessed=1763591393.1341722, blob_last_modified=1758648268.3886547), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6061/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73e03845d0fbff1a5931e4f792e2c1965ade02fc622ffc08e05c267ac6cd3231'), size_on_disk=394930, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.447655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7211/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73d7747698fa1168c89f12c105bbe900d5969d27a82ae2302d0720d761fbfdab'), size_on_disk=298482, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.1276627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6770/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/082f14f7b0d5fc47e088efc40ae85a8ec41f99437c66da96ca69af210bce61cc'), size_on_disk=318118, blob_last_accessed=1763591392.8081744, blob_last_modified=1758648269.3776593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/62e9a627f20607bc4887ebc9277131f974ec80ff39ac386775f4b8f2f5cb9d1d'), size_on_disk=346782, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7476518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/11485fd7bb94d0093ce2508d541fea3ce64f934197d8a5f7bb26ecbdb59f8dbb'), size_on_disk=124642, blob_last_accessed=1763591392.3321776, blob_last_modified=1758648267.0666487), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5961/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/49cac5c2df1adf790a9ac0209db6c0945de50b8191d403e8b1ad386ee2dcb35e'), size_on_disk=321368, blob_last_accessed=1763591392.560176, blob_last_modified=1758648268.215654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6532/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3041f19cf0dcb3e0bd9b1178994351a22f3297ccd43c5e291df8bfe0ad8d5d02'), size_on_disk=516437, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.8236568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6506/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/986d9d3b4ed064d66ebaa0f2502b933f1650a20ac4bbbba804e2cde03b9454c0'), size_on_disk=378407, blob_last_accessed=1763591391.97318, blob_last_modified=1758648268.7656565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5614/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1aa702ff57d34ebed961043e0efae064bfdb62f589cbb515311ae8ea37ba8c76'), size_on_disk=429423, blob_last_accessed=1763591393.4671698, blob_last_modified=1758648268.0406532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6764/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec87eef9e2e8cdf3c3a10ead28dcf1bfa76e94e12a42422a46c8559fb18207c2'), size_on_disk=75684, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.4036593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02313d4d85859a7353e56d89b6e14bcec5160849b5c0089ccbecaa1cda5012cf'), size_on_disk=301213, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4356549), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6635/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6c5b7f1fda3a7f5679870f438d13a45558f7f8da0f479c5144d7751bc8f7fbbc'), size_on_disk=420658, blob_last_accessed=1763591391.95018, blob_last_modified=1758648269.3956594), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ef036874e53ee98dfe1b5d84c42749fc40da9279cd573174321ac83201c1651'), size_on_disk=349762, blob_last_accessed=1763591393.0791724, blob_last_modified=1758648267.4966507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=KB001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38c939d54d3ed77a09650d2ccf00a6ed0e20390b8be722f1c3ed7881a5904a60'), size_on_disk=351677, blob_last_accessed=1763591392.9151735, blob_last_modified=1758648267.5566509), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e5b3174998823af40d055f46ca06e7bb44601cbe97b133062cd3e0491dd30ac2'), size_on_disk=323308, blob_last_accessed=1763591393.153172, blob_last_modified=1758648267.2686496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fc9311b65abb851fbbfd8bb0c0765af59069659f5a715aee546a8644b0af4ec2'), size_on_disk=210948, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648267.6766515), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7283/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0dc91bfacca82c5ed23de7757d59f9f808d541d96dd79aab56f0c4252e7aabf3'), size_on_disk=265146, blob_last_accessed=1763591392.7731745, blob_last_modified=1758648270.401664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6374/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e410602f56bc94d9831a2bc8bdb865ee4ebe9862a65b603165b655fbcc4b9914'), size_on_disk=441531, blob_last_accessed=1763591393.1891718, blob_last_modified=1758648268.5356555), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/33d7303ed077f489ee28021daca0bbc859feb1cfd383dfc9a856ea13994801a9'), size_on_disk=331877, blob_last_accessed=1763591393.292171, blob_last_modified=1758648268.2226539), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6545/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/260c4a280f52024cb12d44559a6189641ee02efbaf20168435196efbfc69d741'), size_on_disk=404812, blob_last_accessed=1763591392.3761773, blob_last_modified=1758648268.8256567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR006B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fcc5e886ec7c6363a5ab51b03d3da189ac3e0cde0a0f79d4f0c46043a6b61838'), size_on_disk=253857, blob_last_accessed=1763591392.0501795, blob_last_modified=1758648267.785652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS011/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ea7cb0559b342dd0ec278e7d5846a5ba0c0d867ea04d545f72e0d1c9472a0cc'), size_on_disk=363979, blob_last_accessed=1763591392.9791732, blob_last_modified=1758648267.0116484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6872/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02fabe6e32f52fe6fca923beb0a6cc2898d991304e23a3e75f3ae8a6bcf361c6'), size_on_disk=274362, blob_last_accessed=1763591393.6231687, blob_last_modified=1758648269.6966608), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a76f0849e518faedfe50e6f0c76908a3f6beed34fd0b607603db1859493595d0'), size_on_disk=292312, blob_last_accessed=1763587658.6168654, blob_last_modified=1758648266.7676473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5399/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b65d0aa7ddc98940f91f2ae5f07e1d3f81dda688792552b91fcc39fb4f9ededa'), size_on_disk=256729, blob_last_accessed=1763591392.5471761, blob_last_modified=1758648267.972653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6437/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/556084dbbc174db5450872d6f5e66cc72091380ea32006977dd176e23c105a7f'), size_on_disk=389840, blob_last_accessed=1763591392.714175, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6567/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0e9713a94594dfcbc80134af52449e12ef207ca9149f5a96c43b6cb457b2431c'), size_on_disk=478809, blob_last_accessed=1763591392.6241755, blob_last_modified=1758648268.893657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7370/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ff4dce26afb34b291220c17625be4a28078f9721a8b000ea177c4add3bf1a039'), size_on_disk=241010, blob_last_accessed=1763591393.7701678, blob_last_modified=1758648270.6726654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5a70fa6f22aeda3246d17646cc4ec7c5a46e1993f9981b19d9fd5a8cc99bf11f'), size_on_disk=308858, blob_last_accessed=1763591391.9411802, blob_last_modified=1758648267.0186484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6177/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b91ffef1dfcdf7952df46956d76a4b364aa0d8342453636cf09e6a343607184f'), size_on_disk=387618, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.424655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6959/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c0cb6c2ec2bda19b0c1da5d5fc2229d0b6d6987d75646fa230c0c14d591cf1f1'), size_on_disk=537588, blob_last_accessed=1763591392.851174, blob_last_modified=1758648269.8686616), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6390/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f324cdf73148c8b0fa23456bc4f51cff3032d08ed42e44dd9fcc4815a6d969c'), size_on_disk=560601, blob_last_accessed=1763591392.7391748, blob_last_modified=1758648268.6206558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7151/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9969e4607d6bf42424f5d2ce8f0d16e3d2d89d069cae6d75997e99c88a68dc77'), size_on_disk=275572, blob_last_accessed=1763591393.7231681, blob_last_modified=1758648270.1476629), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4e8332a5db6260f6357f940e62e3e7b622785f386e81cdb9c75b538d318f4a6'), size_on_disk=158978, blob_last_accessed=1763591393.5471692, blob_last_modified=1758648266.7626472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6421/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1e3bacd3bcf1fcb46cfc5f9ab66c2af57e190f3d006031e7ef37864fbd36aaca'), size_on_disk=495452, blob_last_accessed=1763591392.2161784, blob_last_modified=1758648268.6366558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6708/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/35520976736159652e3be8066e77b1371522f63a8b3564d9b0d9f5b1598a144d'), size_on_disk=253955, blob_last_accessed=1763591393.6881683, blob_last_modified=1758648269.1996584), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6551/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c3729692991cdcafe9baa3d2805b3866f72fe9bc89ba22c6d2cdae4d5b5bbf5f'), size_on_disk=545477, blob_last_accessed=1763591393.0611725, blob_last_modified=1758648268.8376567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1fa09a5a6e6e69198501d994050adfcad7ff39a3cc2fe94e7ea177414e1bc968'), size_on_disk=303532, blob_last_accessed=1763591393.7091682, blob_last_modified=1758648267.2776496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ac8c590c19e7b95b9aac571b1394976039f25fdd66e62a68ecb99925433f8e54'), size_on_disk=355706, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0226483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d3e36418bd46acf9537a8f864b1a1361a062d72fec2b093789b88ed655ec117'), size_on_disk=377393, blob_last_accessed=1763591393.0451727, blob_last_modified=1758648267.0136483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dcb33135a9d854d17a6b80348f6bee8becea9ca3808583a034f89c5e4f460c54'), size_on_disk=218643, blob_last_accessed=1763591393.4831698, blob_last_modified=1758648267.9636528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/00e5602b87ac84670a98c081e6520ca744038f4e9b3a72a78cb61f5926be56b1'), size_on_disk=251087, blob_last_accessed=1763591392.5781758, blob_last_modified=1758648267.8286521), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e91fd764a83947c14f35d7a2d8c0d156c0a61a32a1da96273718ef4be720787'), size_on_disk=361855, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.7196517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6527/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/10081f44b1c2f5ad32489da51fc9ac5b5289b54688a7e78725db247a644b7e4d'), size_on_disk=494640, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.882657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6861/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6955b1a1746fe41162ea1d7a098482bbfdb3d0e411c809b52d698e285627ce03'), size_on_disk=385103, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6146603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6689/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/90ea249116dc6a9be86974e1ec0acf79558dfd56a5666be2e05d2107b8c4ee60'), size_on_disk=366787, blob_last_accessed=1763591393.6661685, blob_last_modified=1758648269.1406581), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6021/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eea0d101dd58c9619b41a94009fc0dff8156d2d36d7f2b88bb653e96123a032e'), size_on_disk=439545, blob_last_accessed=1763591393.5531693, blob_last_modified=1758648268.258654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6920/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/45122fcfb3abc3a43631023712c224dc6e00dba566fb2e15deb4ab4501ad2538'), size_on_disk=446998, blob_last_accessed=1763591392.8881738, blob_last_modified=1758648269.6416605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6448/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/182dc134914a5ddb02992ab6a4f1c12d2450aa331820bf7bd95693dc999bcfae'), size_on_disk=390210, blob_last_accessed=1763591392.9591732, blob_last_modified=1758648268.672656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6954/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/aeed3c55d81a4eb0c568486e476211c599ce2bf37f9dff22c7dfca23f5d80495'), size_on_disk=476054, blob_last_accessed=1763591393.011173, blob_last_modified=1758648269.943662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6853/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ed47ecb2aa870fdbf12920fd4486933262c0a24318c9c8c5531e359a3c416c90'), size_on_disk=515114, blob_last_accessed=1763591393.2661712, blob_last_modified=1758648269.4276595), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1c8a8cb23b1ec073827d6f5343a3bf03a9d7a04cf2ae3fd3ea4cc5ac37673b38'), size_on_disk=342987, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.6226559), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6572/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3488eaac5e4b9b3418b909138e6b50e3f9ebaf3e7345a4a10b6ced18b66152e4'), size_on_disk=440579, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9506574), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6055/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/31bb70b4c90654ecb276664f5c914679322b24985ec66d5b396a99f253ebfa17'), size_on_disk=377015, blob_last_accessed=1763591393.5701692, blob_last_modified=1758648268.3076544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6513/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c8f9c31704c2f8732b4668e1e6410034b8adf2ada39ff55935aa0199e920d055'), size_on_disk=507367, blob_last_accessed=1763591392.1451788, blob_last_modified=1758648268.8406568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6965/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9ae4546d58672f0632757c94b595af8b7c40da215611783fdc1a65000075ec98'), size_on_disk=509677, blob_last_accessed=1763591392.2731779, blob_last_modified=1758648269.933662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6354/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/80b129a46defc7b06e472a35935bfaf410bc5e456475b2885feb2c8a4934a81b'), size_on_disk=173225, blob_last_accessed=1763591393.4981697, blob_last_modified=1758648268.4986553), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bd2017e75947ead2c06f9d1936347f7fdcfac7fbff620e0b39077208a02c3bd9'), size_on_disk=303094, blob_last_accessed=1763591393.1271722, blob_last_modified=1758648269.9296618), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6731/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/23ecd71c4e3c1c547295bb756d4654eb413ff288d10118ab286cd21ccd95b666'), size_on_disk=346187, blob_last_accessed=1763591392.1871786, blob_last_modified=1758648269.1906583), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ac08e026f6d48c034f13a49f3531c90b6c070907'), size_on_disk=9519, blob_last_accessed=1763588875.9720669, blob_last_modified=1763588875.9700668), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/530b5650d91dae3bf169f967af8e800611e3e01daeb1811d6fd21c2612ac4ad9'), size_on_disk=315170, blob_last_accessed=1763591393.6121688, blob_last_modified=1758648266.7956474), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5690/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4831e3e037d421a14efbe1da8c369015ec7d01df351bb27f0ef0400c8b94c693'), size_on_disk=311754, blob_last_accessed=1763591392.9021738, blob_last_modified=1758648268.211654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d4c0cbbf6075a8b5732e3cef2ba154d95d21aa0efc7e49d947cceebd8742b30'), size_on_disk=269007, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4106548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7258/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7b1860ba3be7dbf053203fa5690da2002f7841186940231acea6064e70f0bdc8'), size_on_disk=171331, blob_last_accessed=1763591392.6171756, blob_last_modified=1758648270.3746638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6651/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7644af4cc956ea08809ab6c80c65095e3fdbd36e056040c0eaded3773d546359'), size_on_disk=559951, blob_last_accessed=1763591393.6361687, blob_last_modified=1758648269.1856585), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7155/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/521fc1bea6963640645c21cbdb8d1de07793f11213c23ac71d249f3252b32f54'), size_on_disk=304554, blob_last_accessed=1763591393.5841691, blob_last_modified=1758648270.1366627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6924/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5e9b3a94d73c1720e6b05ea55000054905a8672edea7f950736ce3fe191f8faf'), size_on_disk=412939, blob_last_accessed=1763591393.748168, blob_last_modified=1758648269.7066607), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6923/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/454ca6a52d4b305eddc4b6967ef9604be4861269fdcfff419883daed9b2fe48b'), size_on_disk=337423, blob_last_accessed=1763591393.5031695, blob_last_modified=1758648269.6476605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5801/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/12e91a415cc2a149af4c34c3c8b032821ff301e2828dfb9f4636bd6b2913ea3d'), size_on_disk=360485, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.1636536), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7063/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9427d8f8c54e3c0cc9d720958ef15d3859f00cdfb678559e0268387a36a8ef37'), size_on_disk=404051, blob_last_accessed=1763591392.5301762, blob_last_modified=1758648269.8976617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS006b/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7eedae811a5b3dd4389e7e814d35b022bfaf01fc4d2cfb47be6e6786d6d58c64'), size_on_disk=315912, blob_last_accessed=1763591393.2571712, blob_last_modified=1758648266.7546473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bbe701ffd546a2f4359f821262f7b44232bdc328d9d4719bfdaa8f2aedc6d2e9'), size_on_disk=346079, blob_last_accessed=1763591393.0861723, blob_last_modified=1758648267.589651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5986/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f1179c75ac6e91de03b67a73882f3b9c07c8e435d938d3059de489403adde7a'), size_on_disk=328749, blob_last_accessed=1763591392.691175, blob_last_modified=1758648268.228654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7331/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f159a9811b8ac1b5e252b52427979a3bddce2e7131b13f84046b31e47f7fa399'), size_on_disk=242821, blob_last_accessed=1763591393.2501714, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eb61968b5ec5eadd9087e42ead29333f91195dd5b3f9c5a616b41e94ca23c494'), size_on_disk=330245, blob_last_accessed=1763591393.6761684, blob_last_modified=1758648269.710661), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6808/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c71f5371e8c803cfc7fd507d0b3c11f7548db5d7a0fa42c68977169369ea036e'), size_on_disk=422880, blob_last_accessed=1763591392.9491735, blob_last_modified=1758648269.3916593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ab338795b8bbf334650386a123f6e0df41363cb5dcbb778d485ce58804e96b29'), size_on_disk=375506, blob_last_accessed=1763591392.9711733, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7237/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/110a743162ace7792a44dfe48d7afd40bb67c45c722038c981548477ebc5d8fe'), size_on_disk=313207, blob_last_accessed=1763591393.143172, blob_last_modified=1758648270.1426628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7137/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/703fe9f8552adcbdfb5e640509cb62df2fa734b43fe1e98fc1fd342415f823d7'), size_on_disk=515398, blob_last_accessed=1763589053.7698598, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f427ea0f4ded3b6ff841153d8ad87d72a26a964c189246fb6fd5c439bd8b0c1e'), size_on_disk=353623, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.812652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7115/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/205443fd87ce5c793dab86eb75c21dab7dc2ae2edac41d7e6314cfe491bdb01d'), size_on_disk=321850, blob_last_accessed=1763591393.2331715, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6855/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48e48378d24404e926bc1c44b10af60d4b12f82124ff2f80bc0b69b4a0f98740'), size_on_disk=609248, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6556606), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6798/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/899cde55032cd593be0013e1f6ff33fe9a12f4616477b1410d86fae1b2008a09'), size_on_disk=342376, blob_last_accessed=1763591392.7951744, blob_last_modified=1758648269.4006593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6677/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4ee6e1d3e2b9a9bbcc8851048d2b16f4aea6a1e2f752afb9e0bf75a56adf7042'), size_on_disk=322147, blob_last_accessed=1763591392.393177, blob_last_modified=1758648269.0826578), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7243/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cb59e645fff2683db692b4d2f514602ba210271b632cd113853febe941c6485f'), size_on_disk=281696, blob_last_accessed=1763591392.701175, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7269/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5bae329d2318610f627fc562a5163253c7d61e0ef83c53808b220133b2299264'), size_on_disk=203263, blob_last_accessed=1763591392.541176, blob_last_modified=1758648270.4096642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e20f616f6e340b76f6446d2cdffb6cc9feb81297bd0307733f835acf4142cfd'), size_on_disk=269523, blob_last_accessed=1763591392.3461773, blob_last_modified=1758648267.2696495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6443/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4deb4634e8b4bf141a2410e94b2d99e2b31a0e8f037de9a1c6e14696025bffae'), size_on_disk=494775, blob_last_accessed=1763591392.991173, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6640/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48339729112d22aa5aabf411674bfb90a567ec4c7a07ea7930738645a96823ef'), size_on_disk=329042, blob_last_accessed=1763591392.6391754, blob_last_modified=1758648269.090658), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6573/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e435989051765cf2955d8cd733904e8b60e5f3121e1c2fdc987f4e6f71fe3970'), size_on_disk=265022, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9586573), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/d1cd38451bc41ef14dbc1566c4704b004b2c0661f667210c81dba09bec90540f'), size_on_disk=244487, blob_last_accessed=1763591393.6301687, blob_last_modified=1758648267.3246498), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/74cbce50c258bdddb7e42c8fd5b0b2a9da74fe3cf89a9ab5a53ee4fde7c1dee6'), size_on_disk=186142, blob_last_accessed=1763591392.842174, blob_last_modified=1758648266.9966483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7351/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cc708adf186f28db09dfdd4ac43865a90482b2192306949eab01492598cf23e7'), size_on_disk=198070, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.610665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7185/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/14f67668aca8d8f8aa2b8af98835da683a7fc3f22aa09cdcd7f943a37632391e'), size_on_disk=201650, blob_last_accessed=1763591393.2191715, blob_last_modified=1758648270.1076627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7256/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1af21cbdd7f6f6effff15a1b061a48f85132d582f08c8fd42e04ea58bde7ec00'), size_on_disk=206710, blob_last_accessed=1763591392.586176, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5610/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a86512ae1000a994c86e9aebaf30cf9ce1a59160155f3dc1afa6cbde0a1fc47e'), size_on_disk=307382, blob_last_accessed=1763591393.5291693, blob_last_modified=1758648267.994653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6739/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/58b5ed63f2197f406dd3df6a569975d776722193007912718979eaed4d4e5f9c'), size_on_disk=399160, blob_last_accessed=1763591392.6551754, blob_last_modified=1758648269.331659), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/128015caa722289251a45aa3d56cabac6b5946eb4e1be16eb950555fb24ac413'), size_on_disk=172328, blob_last_accessed=1763591393.735168, blob_last_modified=1758648267.2826495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9f7663ccf72b0681ae5866c13500fbde98f35877fd677ac0d49c17447d17d6ce'), size_on_disk=332767, blob_last_accessed=1763591392.1731787, blob_last_modified=1758648266.744647), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5654/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38961219eb66dec31505ebcc5088f16684be9891b96fffa8c9a13cd3f13254d0'), size_on_disk=447782, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.1876538), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6738/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e6d612d16538a06c985f1134f3fdfd367944b2a85cedfb8b0ddad1679004c940'), size_on_disk=333560, blob_last_accessed=1763591392.8651738, blob_last_modified=1758648269.3676593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6b92129c2fb99028b6cab9509cf1a4c40b142109cde34c9a130d375001dcd211'), size_on_disk=267273, blob_last_accessed=1763591392.7811744, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6454/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c35b60861d2fc7a3f4256869e14e629aed07cd2ccce76608578ceb8c6ec423e1'), size_on_disk=498326, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.7226562), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d50648ed5c9f593f830aff661d13b3d34bd14f0bf93b16169df4765c9f47fc8'), size_on_disk=295924, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7426472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7266/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9aea2d6f5606d24e1999d450379b1f8c8b9d80eded9e784fb729faa516a27026'), size_on_disk=324480, blob_last_accessed=1763591392.0401795, blob_last_modified=1758648270.379664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=MAG002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f7f35257d4c1f7fa5812ea31c1de791e08ab5e3a4e7ca42b2bb12b2c64f7ed7'), size_on_disk=490174, blob_last_accessed=1763591392.829174, blob_last_modified=1758648267.583651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dc74862f02ff12b8c906046f1f03953d33f5516091d58397b0f9a0fc05bb4b5c'), size_on_disk=315435, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.5006506), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7276/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4e0d576cc4a6a85be64f2198e5001a9f5b02b506072df6f695cfdfc719cb041'), size_on_disk=204036, blob_last_accessed=1763591392.987173, blob_last_modified=1758648270.4646642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=composite/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1f8adf8fb4992283714df8587126211d54e6a0580b8be88c1d5e911196aed783'), size_on_disk=4814573, blob_last_accessed=1763591392.0561793, blob_last_modified=1758648268.0256531), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7cb5cca74f1bda2f8f1c8ef962e54d34228361eaa9dd8b21e0d1dd7092ff8164'), size_on_disk=229703, blob_last_accessed=1763591392.555176, blob_last_modified=1758648267.0126483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6657/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/95ad2225152f51e2e31c030ff6708aeebccee5e548035d6f43c9cc3554bdfe5e'), size_on_disk=507730, blob_last_accessed=1763591393.163172, blob_last_modified=1758648269.1676583), CachedFileInfo(file_name='annotated_features_meta.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features_meta.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/2be4b693d333698f7ada7ef40ffaf927c0160d7975ebcf6e215cbc4ba1be1604'), size_on_disk=11813, blob_last_accessed=1763587594.724117, blob_last_modified=1758648264.795638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4b2ff192aa4808cb50b1c1b1667060f98505dd63c1aa58c7d8354e41194957e4'), size_on_disk=289767, blob_last_accessed=1763591393.4871697, blob_last_modified=1758648266.7706473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ddd58117dbedfe7e9dc3fd946187e7bd5d88fe8b0f38999c6e4b5660938bb787'), size_on_disk=359739, blob_last_accessed=1763591393.5981688, blob_last_modified=1758648267.2726495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6983/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4ee29f84ab279c520521a6c5a5eed9e5e5581b2e39378bdd898eec67d13654f'), size_on_disk=410123, blob_last_accessed=1763591392.6751752, blob_last_modified=1758648269.8636615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/646c843611bf88c7f5dcd5b1a664d724c3b24b6434cc52d9a54de62ccaf6c542'), size_on_disk=255949, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.4506505), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fa1a44bd33df22ec6b5a8baf3be17e51a11522b5fdd545a38e6e163cc5ae8207'), size_on_disk=241911, blob_last_accessed=1763591392.572176, blob_last_modified=1758648267.5226507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/71a33d11c3ff714ce0730145c740eec4fe88812c6136b8d10b86fa8d4054991f'), size_on_disk=186500, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7286518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7332/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f7f62ee008fa2b5cc688cbd80c888f1bf9da538caa245b63581520d485338a99'), size_on_disk=309595, blob_last_accessed=1763591393.1021724, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c9b19726dc45896e4f86a5acc52d07cc422f895bfaaaddb332aaceedff36560c'), size_on_disk=178793, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0266485), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0230df4f1d3748337e5315bb1d22a0373fdab95f9bc900041b0e34966d868718'), size_on_disk=311116, blob_last_accessed=1763591392.9241736, blob_last_modified=1758648267.2506495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7240/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4460d188b4cd3b362f33acf0f0b5312a267b37f8fb2dab4b2577c6c3bbb1231'), size_on_disk=307039, blob_last_accessed=1763591392.6101756, blob_last_modified=1758648270.403664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e871cf06f596bfe74896b62aa28d6b1e6f2f0b8408630fb146c7e29cfd88fbf0'), size_on_disk=232721, blob_last_accessed=1763591392.7871745, blob_last_modified=1758648267.991653)}), refs=frozenset(), last_modified=1763588875.9700668), CachedRevisionInfo(commit_hash='27becd0ab489579a50c38ba896dd03680c5fd2da', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da'), size_on_disk=49585603, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ab338795b8bbf334650386a123f6e0df41363cb5dcbb778d485ce58804e96b29'), size_on_disk=375506, blob_last_accessed=1763591392.9711733, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6965/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9ae4546d58672f0632757c94b595af8b7c40da215611783fdc1a65000075ec98'), size_on_disk=509677, blob_last_accessed=1763591392.2731779, blob_last_modified=1758648269.933662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e871cf06f596bfe74896b62aa28d6b1e6f2f0b8408630fb146c7e29cfd88fbf0'), size_on_disk=232721, blob_last_accessed=1763591392.7871745, blob_last_modified=1758648267.991653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7331/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f159a9811b8ac1b5e252b52427979a3bddce2e7131b13f84046b31e47f7fa399'), size_on_disk=242821, blob_last_accessed=1763591393.2501714, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6532/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3041f19cf0dcb3e0bd9b1178994351a22f3297ccd43c5e291df8bfe0ad8d5d02'), size_on_disk=516437, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.8236568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6177/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b91ffef1dfcdf7952df46956d76a4b364aa0d8342453636cf09e6a343607184f'), size_on_disk=387618, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.424655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6390/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f324cdf73148c8b0fa23456bc4f51cff3032d08ed42e44dd9fcc4815a6d969c'), size_on_disk=560601, blob_last_accessed=1763591392.7391748, blob_last_modified=1758648268.6206558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6954/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/aeed3c55d81a4eb0c568486e476211c599ce2bf37f9dff22c7dfca23f5d80495'), size_on_disk=476054, blob_last_accessed=1763591393.011173, blob_last_modified=1758648269.943662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ef036874e53ee98dfe1b5d84c42749fc40da9279cd573174321ac83201c1651'), size_on_disk=349762, blob_last_accessed=1763591393.0791724, blob_last_modified=1758648267.4966507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7258/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7b1860ba3be7dbf053203fa5690da2002f7841186940231acea6064e70f0bdc8'), size_on_disk=171331, blob_last_accessed=1763591392.6171756, blob_last_modified=1758648270.3746638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS011/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ea7cb0559b342dd0ec278e7d5846a5ba0c0d867ea04d545f72e0d1c9472a0cc'), size_on_disk=363979, blob_last_accessed=1763591392.9791732, blob_last_modified=1758648267.0116484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6513/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c8f9c31704c2f8732b4668e1e6410034b8adf2ada39ff55935aa0199e920d055'), size_on_disk=507367, blob_last_accessed=1763591392.1451788, blob_last_modified=1758648268.8406568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6506/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/986d9d3b4ed064d66ebaa0f2502b933f1650a20ac4bbbba804e2cde03b9454c0'), size_on_disk=378407, blob_last_accessed=1763591391.97318, blob_last_modified=1758648268.7656565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6573/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e435989051765cf2955d8cd733904e8b60e5f3121e1c2fdc987f4e6f71fe3970'), size_on_disk=265022, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9586573), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6454/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c35b60861d2fc7a3f4256869e14e629aed07cd2ccce76608578ceb8c6ec423e1'), size_on_disk=498326, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.7226562), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6443/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4deb4634e8b4bf141a2410e94b2d99e2b31a0e8f037de9a1c6e14696025bffae'), size_on_disk=494775, blob_last_accessed=1763591392.991173, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7240/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4460d188b4cd3b362f33acf0f0b5312a267b37f8fb2dab4b2577c6c3bbb1231'), size_on_disk=307039, blob_last_accessed=1763591392.6101756, blob_last_modified=1758648270.403664), CachedFileInfo(file_name='annotated_features_meta.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features_meta.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/2be4b693d333698f7ada7ef40ffaf927c0160d7975ebcf6e215cbc4ba1be1604'), size_on_disk=11813, blob_last_accessed=1763587594.724117, blob_last_modified=1758648264.795638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6421/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1e3bacd3bcf1fcb46cfc5f9ab66c2af57e190f3d006031e7ef37864fbd36aaca'), size_on_disk=495452, blob_last_accessed=1763591392.2161784, blob_last_modified=1758648268.6366558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7118/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/be8bd409b83526d5671501de6ef2980177955fdcc3a13d5e153d2593493b2a1e'), size_on_disk=346308, blob_last_accessed=1763591392.9281735, blob_last_modified=1758648270.1336627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4d098c33564f99ec6aed77d585d1f6e871806f441f099790eef4d6478e322af'), size_on_disk=264459, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7486472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6055/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/31bb70b4c90654ecb276664f5c914679322b24985ec66d5b396a99f253ebfa17'), size_on_disk=377015, blob_last_accessed=1763591393.5701692, blob_last_modified=1758648268.3076544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5690/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4831e3e037d421a14efbe1da8c369015ec7d01df351bb27f0ef0400c8b94c693'), size_on_disk=311754, blob_last_accessed=1763591392.9021738, blob_last_modified=1758648268.211654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6731/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/23ecd71c4e3c1c547295bb756d4654eb413ff288d10118ab286cd21ccd95b666'), size_on_disk=346187, blob_last_accessed=1763591392.1871786, blob_last_modified=1758648269.1906583), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7211/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73d7747698fa1168c89f12c105bbe900d5969d27a82ae2302d0720d761fbfdab'), size_on_disk=298482, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.1276627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6708/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/35520976736159652e3be8066e77b1371522f63a8b3564d9b0d9f5b1598a144d'), size_on_disk=253955, blob_last_accessed=1763591393.6881683, blob_last_modified=1758648269.1996584), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6b92129c2fb99028b6cab9509cf1a4c40b142109cde34c9a130d375001dcd211'), size_on_disk=267273, blob_last_accessed=1763591392.7811744, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6551/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c3729692991cdcafe9baa3d2805b3866f72fe9bc89ba22c6d2cdae4d5b5bbf5f'), size_on_disk=545477, blob_last_accessed=1763591393.0611725, blob_last_modified=1758648268.8376567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5617/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bc01241e8363373c995a0fa7439aa1e432a60eb8a7805124fff302311f638361'), size_on_disk=351950, blob_last_accessed=1763591393.6541686, blob_last_modified=1758648268.1026535), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/33d7303ed077f489ee28021daca0bbc859feb1cfd383dfc9a856ea13994801a9'), size_on_disk=331877, blob_last_accessed=1763591393.292171, blob_last_modified=1758648268.2226539), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6657/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/95ad2225152f51e2e31c030ff6708aeebccee5e548035d6f43c9cc3554bdfe5e'), size_on_disk=507730, blob_last_accessed=1763591393.163172, blob_last_modified=1758648269.1676583), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dc74862f02ff12b8c906046f1f03953d33f5516091d58397b0f9a0fc05bb4b5c'), size_on_disk=315435, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.5006506), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/00e5602b87ac84670a98c081e6520ca744038f4e9b3a72a78cb61f5926be56b1'), size_on_disk=251087, blob_last_accessed=1763591392.5781758, blob_last_modified=1758648267.8286521), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e91fd764a83947c14f35d7a2d8c0d156c0a61a32a1da96273718ef4be720787'), size_on_disk=361855, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.7196517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7063/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9427d8f8c54e3c0cc9d720958ef15d3859f00cdfb678559e0268387a36a8ef37'), size_on_disk=404051, blob_last_accessed=1763591392.5301762, blob_last_modified=1758648269.8976617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/11485fd7bb94d0093ce2508d541fea3ce64f934197d8a5f7bb26ecbdb59f8dbb'), size_on_disk=124642, blob_last_accessed=1763591392.3321776, blob_last_modified=1758648267.0666487), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c9b19726dc45896e4f86a5acc52d07cc422f895bfaaaddb332aaceedff36560c'), size_on_disk=178793, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0266485), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9f7663ccf72b0681ae5866c13500fbde98f35877fd677ac0d49c17447d17d6ce'), size_on_disk=332767, blob_last_accessed=1763591392.1731787, blob_last_modified=1758648266.744647), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6923/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/454ca6a52d4b305eddc4b6967ef9604be4861269fdcfff419883daed9b2fe48b'), size_on_disk=337423, blob_last_accessed=1763591393.5031695, blob_last_modified=1758648269.6476605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02313d4d85859a7353e56d89b6e14bcec5160849b5c0089ccbecaa1cda5012cf'), size_on_disk=301213, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4356549), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6527/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/10081f44b1c2f5ad32489da51fc9ac5b5289b54688a7e78725db247a644b7e4d'), size_on_disk=494640, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.882657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7269/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5bae329d2318610f627fc562a5163253c7d61e0ef83c53808b220133b2299264'), size_on_disk=203263, blob_last_accessed=1763591392.541176, blob_last_modified=1758648270.4096642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1c8a8cb23b1ec073827d6f5343a3bf03a9d7a04cf2ae3fd3ea4cc5ac37673b38'), size_on_disk=342987, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.6226559), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6738/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e6d612d16538a06c985f1134f3fdfd367944b2a85cedfb8b0ddad1679004c940'), size_on_disk=333560, blob_last_accessed=1763591392.8651738, blob_last_modified=1758648269.3676593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7370/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ff4dce26afb34b291220c17625be4a28078f9721a8b000ea177c4add3bf1a039'), size_on_disk=241010, blob_last_accessed=1763591393.7701678, blob_last_modified=1758648270.6726654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d3e36418bd46acf9537a8f864b1a1361a062d72fec2b093789b88ed655ec117'), size_on_disk=377393, blob_last_accessed=1763591393.0451727, blob_last_modified=1758648267.0136483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dcb33135a9d854d17a6b80348f6bee8becea9ca3808583a034f89c5e4f460c54'), size_on_disk=218643, blob_last_accessed=1763591393.4831698, blob_last_modified=1758648267.9636528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ddd58117dbedfe7e9dc3fd946187e7bd5d88fe8b0f38999c6e4b5660938bb787'), size_on_disk=359739, blob_last_accessed=1763591393.5981688, blob_last_modified=1758648267.2726495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6448/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/182dc134914a5ddb02992ab6a4f1c12d2450aa331820bf7bd95693dc999bcfae'), size_on_disk=390210, blob_last_accessed=1763591392.9591732, blob_last_modified=1758648268.672656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6739/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/58b5ed63f2197f406dd3df6a569975d776722193007912718979eaed4d4e5f9c'), size_on_disk=399160, blob_last_accessed=1763591392.6551754, blob_last_modified=1758648269.331659), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=KB001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38c939d54d3ed77a09650d2ccf00a6ed0e20390b8be722f1c3ed7881a5904a60'), size_on_disk=351677, blob_last_accessed=1763591392.9151735, blob_last_modified=1758648267.5566509), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5801/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/12e91a415cc2a149af4c34c3c8b032821ff301e2828dfb9f4636bd6b2913ea3d'), size_on_disk=360485, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.1636536), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR006B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fcc5e886ec7c6363a5ab51b03d3da189ac3e0cde0a0f79d4f0c46043a6b61838'), size_on_disk=253857, blob_last_accessed=1763591392.0501795, blob_last_modified=1758648267.785652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6677/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4ee6e1d3e2b9a9bbcc8851048d2b16f4aea6a1e2f752afb9e0bf75a56adf7042'), size_on_disk=322147, blob_last_accessed=1763591392.393177, blob_last_modified=1758648269.0826578), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6140/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/66c2da17957d7e6a8b159d3573879ac779d0094e35150b51e2643f7ddee36e1f'), size_on_disk=332958, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4236548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7237/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/110a743162ace7792a44dfe48d7afd40bb67c45c722038c981548477ebc5d8fe'), size_on_disk=313207, blob_last_accessed=1763591393.143172, blob_last_modified=1758648270.1426628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6798/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/899cde55032cd593be0013e1f6ff33fe9a12f4616477b1410d86fae1b2008a09'), size_on_disk=342376, blob_last_accessed=1763591392.7951744, blob_last_modified=1758648269.4006593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6861/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6955b1a1746fe41162ea1d7a098482bbfdb3d0e411c809b52d698e285627ce03'), size_on_disk=385103, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6146603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6920/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/45122fcfb3abc3a43631023712c224dc6e00dba566fb2e15deb4ab4501ad2538'), size_on_disk=446998, blob_last_accessed=1763591392.8881738, blob_last_modified=1758648269.6416605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/74cbce50c258bdddb7e42c8fd5b0b2a9da74fe3cf89a9ab5a53ee4fde7c1dee6'), size_on_disk=186142, blob_last_accessed=1763591392.842174, blob_last_modified=1758648266.9966483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/128015caa722289251a45aa3d56cabac6b5946eb4e1be16eb950555fb24ac413'), size_on_disk=172328, blob_last_accessed=1763591393.735168, blob_last_modified=1758648267.2826495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0230df4f1d3748337e5315bb1d22a0373fdab95f9bc900041b0e34966d868718'), size_on_disk=311116, blob_last_accessed=1763591392.9241736, blob_last_modified=1758648267.2506495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6635/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6c5b7f1fda3a7f5679870f438d13a45558f7f8da0f479c5144d7751bc8f7fbbc'), size_on_disk=420658, blob_last_accessed=1763591391.95018, blob_last_modified=1758648269.3956594), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6073/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d68ca6411ba47eb63adad81e88fa1341a0ee269897268957ae936dcc9ba7788'), size_on_disk=450031, blob_last_accessed=1763591393.1341722, blob_last_modified=1758648268.3886547), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7332/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f7f62ee008fa2b5cc688cbd80c888f1bf9da538caa245b63581520d485338a99'), size_on_disk=309595, blob_last_accessed=1763591393.1021724, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS006b/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7eedae811a5b3dd4389e7e814d35b022bfaf01fc4d2cfb47be6e6786d6d58c64'), size_on_disk=315912, blob_last_accessed=1763591393.2571712, blob_last_modified=1758648266.7546473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6651/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7644af4cc956ea08809ab6c80c65095e3fdbd36e056040c0eaded3773d546359'), size_on_disk=559951, blob_last_accessed=1763591393.6361687, blob_last_modified=1758648269.1856585), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4e8332a5db6260f6357f940e62e3e7b622785f386e81cdb9c75b538d318f4a6'), size_on_disk=158978, blob_last_accessed=1763591393.5471692, blob_last_modified=1758648266.7626472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6764/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec87eef9e2e8cdf3c3a10ead28dcf1bfa76e94e12a42422a46c8559fb18207c2'), size_on_disk=75684, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.4036593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7295/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/19bd07f913e0af3460e6b4b5091971ceed8f4aa510cf18b82ec36dabb67c68b8'), size_on_disk=272619, blob_last_accessed=1763591393.1151721, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6872/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02fabe6e32f52fe6fca923beb0a6cc2898d991304e23a3e75f3ae8a6bcf361c6'), size_on_disk=274362, blob_last_accessed=1763591393.6231687, blob_last_modified=1758648269.6966608), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=MAG001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/30b63d014686a103b5e7fc6571e902fa9c08630a96557cc12ec9dfcf60871306'), size_on_disk=347333, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.559651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d50648ed5c9f593f830aff661d13b3d34bd14f0bf93b16169df4765c9f47fc8'), size_on_disk=295924, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7426472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6863/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec534c91cae4413d30477014131d39b51c9c236dfbf150874bd21ec3de821613'), size_on_disk=389025, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648269.6376605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR007B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6f0620b102e7cbfadced7d5530545a185036b53118d3e2b7f074c56403d80f4b'), size_on_disk=263185, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7616518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bd2017e75947ead2c06f9d1936347f7fdcfac7fbff620e0b39077208a02c3bd9'), size_on_disk=303094, blob_last_accessed=1763591393.1271722, blob_last_modified=1758648269.9296618), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/530b5650d91dae3bf169f967af8e800611e3e01daeb1811d6fd21c2612ac4ad9'), size_on_disk=315170, blob_last_accessed=1763591393.6121688, blob_last_modified=1758648266.7956474), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bbe701ffd546a2f4359f821262f7b44232bdc328d9d4719bfdaa8f2aedc6d2e9'), size_on_disk=346079, blob_last_accessed=1763591393.0861723, blob_last_modified=1758648267.589651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6640/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48339729112d22aa5aabf411674bfb90a567ec4c7a07ea7930738645a96823ef'), size_on_disk=329042, blob_last_accessed=1763591392.6391754, blob_last_modified=1758648269.090658), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5654/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38961219eb66dec31505ebcc5088f16684be9891b96fffa8c9a13cd3f13254d0'), size_on_disk=447782, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.1876538), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=composite/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1f8adf8fb4992283714df8587126211d54e6a0580b8be88c1d5e911196aed783'), size_on_disk=4814573, blob_last_accessed=1763591392.0561793, blob_last_modified=1758648268.0256531), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/71a33d11c3ff714ce0730145c740eec4fe88812c6136b8d10b86fa8d4054991f'), size_on_disk=186500, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7286518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7243/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cb59e645fff2683db692b4d2f514602ba210271b632cd113853febe941c6485f'), size_on_disk=281696, blob_last_accessed=1763591392.701175, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7185/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/14f67668aca8d8f8aa2b8af98835da683a7fc3f22aa09cdcd7f943a37632391e'), size_on_disk=201650, blob_last_accessed=1763591393.2191715, blob_last_modified=1758648270.1076627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7276/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4e0d576cc4a6a85be64f2198e5001a9f5b02b506072df6f695cfdfc719cb041'), size_on_disk=204036, blob_last_accessed=1763591392.987173, blob_last_modified=1758648270.4646642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7151/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9969e4607d6bf42424f5d2ce8f0d16e3d2d89d069cae6d75997e99c88a68dc77'), size_on_disk=275572, blob_last_accessed=1763591393.7231681, blob_last_modified=1758648270.1476629), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7266/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9aea2d6f5606d24e1999d450379b1f8c8b9d80eded9e784fb729faa516a27026'), size_on_disk=324480, blob_last_accessed=1763591392.0401795, blob_last_modified=1758648270.379664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5399/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b65d0aa7ddc98940f91f2ae5f07e1d3f81dda688792552b91fcc39fb4f9ededa'), size_on_disk=256729, blob_last_accessed=1763591392.5471761, blob_last_modified=1758648267.972653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6354/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/80b129a46defc7b06e472a35935bfaf410bc5e456475b2885feb2c8a4934a81b'), size_on_disk=173225, blob_last_accessed=1763591393.4981697, blob_last_modified=1758648268.4986553), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/d1cd38451bc41ef14dbc1566c4704b004b2c0661f667210c81dba09bec90540f'), size_on_disk=244487, blob_last_accessed=1763591393.6301687, blob_last_modified=1758648267.3246498), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5961/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/49cac5c2df1adf790a9ac0209db6c0945de50b8191d403e8b1ad386ee2dcb35e'), size_on_disk=321368, blob_last_accessed=1763591392.560176, blob_last_modified=1758648268.215654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5986/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f1179c75ac6e91de03b67a73882f3b9c07c8e435d938d3059de489403adde7a'), size_on_disk=328749, blob_last_accessed=1763591392.691175, blob_last_modified=1758648268.228654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7297/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4bd4088cbfcccdf96def916bb8fd9d673327d3e57c2a4a3e57299743ce4aca7'), size_on_disk=261344, blob_last_accessed=1763591393.588169, blob_last_modified=1758648270.6516652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/62e9a627f20607bc4887ebc9277131f974ec80ff39ac386775f4b8f2f5cb9d1d'), size_on_disk=346782, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7476518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7256/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1af21cbdd7f6f6effff15a1b061a48f85132d582f08c8fd42e04ea58bde7ec00'), size_on_disk=206710, blob_last_accessed=1763591392.586176, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7115/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/205443fd87ce5c793dab86eb75c21dab7dc2ae2edac41d7e6314cfe491bdb01d'), size_on_disk=321850, blob_last_accessed=1763591393.2331715, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7cb5cca74f1bda2f8f1c8ef962e54d34228361eaa9dd8b21e0d1dd7092ff8164'), size_on_disk=229703, blob_last_accessed=1763591392.555176, blob_last_modified=1758648267.0126483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5610/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a86512ae1000a994c86e9aebaf30cf9ce1a59160155f3dc1afa6cbde0a1fc47e'), size_on_disk=307382, blob_last_accessed=1763591393.5291693, blob_last_modified=1758648267.994653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1fa09a5a6e6e69198501d994050adfcad7ff39a3cc2fe94e7ea177414e1bc968'), size_on_disk=303532, blob_last_accessed=1763591393.7091682, blob_last_modified=1758648267.2776496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5614/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1aa702ff57d34ebed961043e0efae064bfdb62f589cbb515311ae8ea37ba8c76'), size_on_disk=429423, blob_last_accessed=1763591393.4671698, blob_last_modified=1758648268.0406532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=MAG002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f7f35257d4c1f7fa5812ea31c1de791e08ab5e3a4e7ca42b2bb12b2c64f7ed7'), size_on_disk=490174, blob_last_accessed=1763591392.829174, blob_last_modified=1758648267.583651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fa1a44bd33df22ec6b5a8baf3be17e51a11522b5fdd545a38e6e163cc5ae8207'), size_on_disk=241911, blob_last_accessed=1763591392.572176, blob_last_modified=1758648267.5226507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7137/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/703fe9f8552adcbdfb5e640509cb62df2fa734b43fe1e98fc1fd342415f823d7'), size_on_disk=515398, blob_last_accessed=1763589053.7698598, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6924/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5e9b3a94d73c1720e6b05ea55000054905a8672edea7f950736ce3fe191f8faf'), size_on_disk=412939, blob_last_accessed=1763591393.748168, blob_last_modified=1758648269.7066607), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6061/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73e03845d0fbff1a5931e4f792e2c1965ade02fc622ffc08e05c267ac6cd3231'), size_on_disk=394930, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.447655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6689/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/90ea249116dc6a9be86974e1ec0acf79558dfd56a5666be2e05d2107b8c4ee60'), size_on_disk=366787, blob_last_accessed=1763591393.6661685, blob_last_modified=1758648269.1406581), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6021/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eea0d101dd58c9619b41a94009fc0dff8156d2d36d7f2b88bb653e96123a032e'), size_on_disk=439545, blob_last_accessed=1763591393.5531693, blob_last_modified=1758648268.258654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6545/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/260c4a280f52024cb12d44559a6189641ee02efbaf20168435196efbfc69d741'), size_on_disk=404812, blob_last_accessed=1763591392.3761773, blob_last_modified=1758648268.8256567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e5b3174998823af40d055f46ca06e7bb44601cbe97b133062cd3e0491dd30ac2'), size_on_disk=323308, blob_last_accessed=1763591393.153172, blob_last_modified=1758648267.2686496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6567/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0e9713a94594dfcbc80134af52449e12ef207ca9149f5a96c43b6cb457b2431c'), size_on_disk=478809, blob_last_accessed=1763591392.6241755, blob_last_modified=1758648268.893657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ac8c590c19e7b95b9aac571b1394976039f25fdd66e62a68ecb99925433f8e54'), size_on_disk=355706, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0226483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f427ea0f4ded3b6ff841153d8ad87d72a26a964c189246fb6fd5c439bd8b0c1e'), size_on_disk=353623, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.812652), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a88564c359f73b5ac3f70a4635f33a1ec57f527f'), size_on_disk=6139, blob_last_accessed=1763587733.4135532, blob_last_modified=1758648264.1986353), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6853/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ed47ecb2aa870fdbf12920fd4486933262c0a24318c9c8c5531e359a3c416c90'), size_on_disk=515114, blob_last_accessed=1763591393.2661712, blob_last_modified=1758648269.4276595), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d4c0cbbf6075a8b5732e3cef2ba154d95d21aa0efc7e49d947cceebd8742b30'), size_on_disk=269007, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4106548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6437/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/556084dbbc174db5450872d6f5e66cc72091380ea32006977dd176e23c105a7f'), size_on_disk=389840, blob_last_accessed=1763591392.714175, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7155/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/521fc1bea6963640645c21cbdb8d1de07793f11213c23ac71d249f3252b32f54'), size_on_disk=304554, blob_last_accessed=1763591393.5841691, blob_last_modified=1758648270.1366627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4b2ff192aa4808cb50b1c1b1667060f98505dd63c1aa58c7d8354e41194957e4'), size_on_disk=289767, blob_last_accessed=1763591393.4871697, blob_last_modified=1758648266.7706473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fc9311b65abb851fbbfd8bb0c0765af59069659f5a715aee546a8644b0af4ec2'), size_on_disk=210948, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648267.6766515), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a76f0849e518faedfe50e6f0c76908a3f6beed34fd0b607603db1859493595d0'), size_on_disk=292312, blob_last_accessed=1763587658.6168654, blob_last_modified=1758648266.7676473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7351/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cc708adf186f28db09dfdd4ac43865a90482b2192306949eab01492598cf23e7'), size_on_disk=198070, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.610665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6770/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/082f14f7b0d5fc47e088efc40ae85a8ec41f99437c66da96ca69af210bce61cc'), size_on_disk=318118, blob_last_accessed=1763591392.8081744, blob_last_modified=1758648269.3776593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6983/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4ee29f84ab279c520521a6c5a5eed9e5e5581b2e39378bdd898eec67d13654f'), size_on_disk=410123, blob_last_accessed=1763591392.6751752, blob_last_modified=1758648269.8636615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6959/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c0cb6c2ec2bda19b0c1da5d5fc2229d0b6d6987d75646fa230c0c14d591cf1f1'), size_on_disk=537588, blob_last_accessed=1763591392.851174, blob_last_modified=1758648269.8686616), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6808/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c71f5371e8c803cfc7fd507d0b3c11f7548db5d7a0fa42c68977169369ea036e'), size_on_disk=422880, blob_last_accessed=1763591392.9491735, blob_last_modified=1758648269.3916593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/646c843611bf88c7f5dcd5b1a664d724c3b24b6434cc52d9a54de62ccaf6c542'), size_on_disk=255949, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.4506505), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5301_5088/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/65ef775934339b80eda59a141cf791a74e1c6f12513d8143307cebe2f11c3738'), size_on_disk=410106, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.9516528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e20f616f6e340b76f6446d2cdffb6cc9feb81297bd0307733f835acf4142cfd'), size_on_disk=269523, blob_last_accessed=1763591392.3461773, blob_last_modified=1758648267.2696495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6572/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3488eaac5e4b9b3418b909138e6b50e3f9ebaf3e7345a4a10b6ced18b66152e4'), size_on_disk=440579, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9506574), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eb61968b5ec5eadd9087e42ead29333f91195dd5b3f9c5a616b41e94ca23c494'), size_on_disk=330245, blob_last_accessed=1763591393.6761684, blob_last_modified=1758648269.710661), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6374/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e410602f56bc94d9831a2bc8bdb865ee4ebe9862a65b603165b655fbcc4b9914'), size_on_disk=441531, blob_last_accessed=1763591393.1891718, blob_last_modified=1758648268.5356555), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6855/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48e48378d24404e926bc1c44b10af60d4b12f82124ff2f80bc0b69b4a0f98740'), size_on_disk=609248, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6556606), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7367/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a7778bb24506047963575fa96126157895c7f329996c729c3e7d6070595955c3'), size_on_disk=324361, blob_last_accessed=1763591392.1631787, blob_last_modified=1758648270.628665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7283/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0dc91bfacca82c5ed23de7757d59f9f808d541d96dd79aab56f0c4252e7aabf3'), size_on_disk=265146, blob_last_accessed=1763591392.7731745, blob_last_modified=1758648270.401664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5a70fa6f22aeda3246d17646cc4ec7c5a46e1993f9981b19d9fd5a8cc99bf11f'), size_on_disk=308858, blob_last_accessed=1763591391.9411802, blob_last_modified=1758648267.0186484)}), refs=frozenset(), last_modified=1758648270.6726654), CachedRevisionInfo(commit_hash='6864b2582ce35f92a8dde59851786319c2c494bc', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc'), size_on_disk=49590351, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6443/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4deb4634e8b4bf141a2410e94b2d99e2b31a0e8f037de9a1c6e14696025bffae'), size_on_disk=494775, blob_last_accessed=1763591392.991173, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6863/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec534c91cae4413d30477014131d39b51c9c236dfbf150874bd21ec3de821613'), size_on_disk=389025, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648269.6376605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7295/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/19bd07f913e0af3460e6b4b5091971ceed8f4aa510cf18b82ec36dabb67c68b8'), size_on_disk=272619, blob_last_accessed=1763591393.1151721, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6513/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c8f9c31704c2f8732b4668e1e6410034b8adf2ada39ff55935aa0199e920d055'), size_on_disk=507367, blob_last_accessed=1763591392.1451788, blob_last_modified=1758648268.8406568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7269/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5bae329d2318610f627fc562a5163253c7d61e0ef83c53808b220133b2299264'), size_on_disk=203263, blob_last_accessed=1763591392.541176, blob_last_modified=1758648270.4096642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6965/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9ae4546d58672f0632757c94b595af8b7c40da215611783fdc1a65000075ec98'), size_on_disk=509677, blob_last_accessed=1763591392.2731779, blob_last_modified=1758648269.933662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/71a33d11c3ff714ce0730145c740eec4fe88812c6136b8d10b86fa8d4054991f'), size_on_disk=186500, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7286518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6640/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48339729112d22aa5aabf411674bfb90a567ec4c7a07ea7930738645a96823ef'), size_on_disk=329042, blob_last_accessed=1763591392.6391754, blob_last_modified=1758648269.090658), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7211/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73d7747698fa1168c89f12c105bbe900d5969d27a82ae2302d0720d761fbfdab'), size_on_disk=298482, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.1276627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7237/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/110a743162ace7792a44dfe48d7afd40bb67c45c722038c981548477ebc5d8fe'), size_on_disk=313207, blob_last_accessed=1763591393.143172, blob_last_modified=1758648270.1426628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1c8a8cb23b1ec073827d6f5343a3bf03a9d7a04cf2ae3fd3ea4cc5ac37673b38'), size_on_disk=342987, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.6226559), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9f7663ccf72b0681ae5866c13500fbde98f35877fd677ac0d49c17447d17d6ce'), size_on_disk=332767, blob_last_accessed=1763591392.1731787, blob_last_modified=1758648266.744647), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6545/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/260c4a280f52024cb12d44559a6189641ee02efbaf20168435196efbfc69d741'), size_on_disk=404812, blob_last_accessed=1763591392.3761773, blob_last_modified=1758648268.8256567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6437/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/556084dbbc174db5450872d6f5e66cc72091380ea32006977dd176e23c105a7f'), size_on_disk=389840, blob_last_accessed=1763591392.714175, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6923/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/454ca6a52d4b305eddc4b6967ef9604be4861269fdcfff419883daed9b2fe48b'), size_on_disk=337423, blob_last_accessed=1763591393.5031695, blob_last_modified=1758648269.6476605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5986/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f1179c75ac6e91de03b67a73882f3b9c07c8e435d938d3059de489403adde7a'), size_on_disk=328749, blob_last_accessed=1763591392.691175, blob_last_modified=1758648268.228654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6506/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/986d9d3b4ed064d66ebaa0f2502b933f1650a20ac4bbbba804e2cde03b9454c0'), size_on_disk=378407, blob_last_accessed=1763591391.97318, blob_last_modified=1758648268.7656565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6983/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4ee29f84ab279c520521a6c5a5eed9e5e5581b2e39378bdd898eec67d13654f'), size_on_disk=410123, blob_last_accessed=1763591392.6751752, blob_last_modified=1758648269.8636615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6532/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3041f19cf0dcb3e0bd9b1178994351a22f3297ccd43c5e291df8bfe0ad8d5d02'), size_on_disk=516437, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.8236568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6738/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e6d612d16538a06c985f1134f3fdfd367944b2a85cedfb8b0ddad1679004c940'), size_on_disk=333560, blob_last_accessed=1763591392.8651738, blob_last_modified=1758648269.3676593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS006b/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7eedae811a5b3dd4389e7e814d35b022bfaf01fc4d2cfb47be6e6786d6d58c64'), size_on_disk=315912, blob_last_accessed=1763591393.2571712, blob_last_modified=1758648266.7546473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/128015caa722289251a45aa3d56cabac6b5946eb4e1be16eb950555fb24ac413'), size_on_disk=172328, blob_last_accessed=1763591393.735168, blob_last_modified=1758648267.2826495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0230df4f1d3748337e5315bb1d22a0373fdab95f9bc900041b0e34966d868718'), size_on_disk=311116, blob_last_accessed=1763591392.9241736, blob_last_modified=1758648267.2506495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5a70fa6f22aeda3246d17646cc4ec7c5a46e1993f9981b19d9fd5a8cc99bf11f'), size_on_disk=308858, blob_last_accessed=1763591391.9411802, blob_last_modified=1758648267.0186484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7240/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4460d188b4cd3b362f33acf0f0b5312a267b37f8fb2dab4b2577c6c3bbb1231'), size_on_disk=307039, blob_last_accessed=1763591392.6101756, blob_last_modified=1758648270.403664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6374/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e410602f56bc94d9831a2bc8bdb865ee4ebe9862a65b603165b655fbcc4b9914'), size_on_disk=441531, blob_last_accessed=1763591393.1891718, blob_last_modified=1758648268.5356555), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6770/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/082f14f7b0d5fc47e088efc40ae85a8ec41f99437c66da96ca69af210bce61cc'), size_on_disk=318118, blob_last_accessed=1763591392.8081744, blob_last_modified=1758648269.3776593), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fc266bb938fa146c46d1a7bd5446d5d27b3bf202'), size_on_disk=10887, blob_last_accessed=1763649507.5438576, blob_last_modified=1763649507.5418575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/00e5602b87ac84670a98c081e6520ca744038f4e9b3a72a78cb61f5926be56b1'), size_on_disk=251087, blob_last_accessed=1763591392.5781758, blob_last_modified=1758648267.8286521), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6021/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eea0d101dd58c9619b41a94009fc0dff8156d2d36d7f2b88bb653e96123a032e'), size_on_disk=439545, blob_last_accessed=1763591393.5531693, blob_last_modified=1758648268.258654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6551/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c3729692991cdcafe9baa3d2805b3866f72fe9bc89ba22c6d2cdae4d5b5bbf5f'), size_on_disk=545477, blob_last_accessed=1763591393.0611725, blob_last_modified=1758648268.8376567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5614/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1aa702ff57d34ebed961043e0efae064bfdb62f589cbb515311ae8ea37ba8c76'), size_on_disk=429423, blob_last_accessed=1763591393.4671698, blob_last_modified=1758648268.0406532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02313d4d85859a7353e56d89b6e14bcec5160849b5c0089ccbecaa1cda5012cf'), size_on_disk=301213, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4356549), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6572/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3488eaac5e4b9b3418b909138e6b50e3f9ebaf3e7345a4a10b6ced18b66152e4'), size_on_disk=440579, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9506574), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ddd58117dbedfe7e9dc3fd946187e7bd5d88fe8b0f38999c6e4b5660938bb787'), size_on_disk=359739, blob_last_accessed=1763591393.5981688, blob_last_modified=1758648267.2726495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6073/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d68ca6411ba47eb63adad81e88fa1341a0ee269897268957ae936dcc9ba7788'), size_on_disk=450031, blob_last_accessed=1763591393.1341722, blob_last_modified=1758648268.3886547), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6421/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1e3bacd3bcf1fcb46cfc5f9ab66c2af57e190f3d006031e7ef37864fbd36aaca'), size_on_disk=495452, blob_last_accessed=1763591392.2161784, blob_last_modified=1758648268.6366558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/530b5650d91dae3bf169f967af8e800611e3e01daeb1811d6fd21c2612ac4ad9'), size_on_disk=315170, blob_last_accessed=1763591393.6121688, blob_last_modified=1758648266.7956474), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bd2017e75947ead2c06f9d1936347f7fdcfac7fbff620e0b39077208a02c3bd9'), size_on_disk=303094, blob_last_accessed=1763591393.1271722, blob_last_modified=1758648269.9296618), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c9b19726dc45896e4f86a5acc52d07cc422f895bfaaaddb332aaceedff36560c'), size_on_disk=178793, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0266485), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7063/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9427d8f8c54e3c0cc9d720958ef15d3859f00cdfb678559e0268387a36a8ef37'), size_on_disk=404051, blob_last_accessed=1763591392.5301762, blob_last_modified=1758648269.8976617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6635/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6c5b7f1fda3a7f5679870f438d13a45558f7f8da0f479c5144d7751bc8f7fbbc'), size_on_disk=420658, blob_last_accessed=1763591391.95018, blob_last_modified=1758648269.3956594), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ef036874e53ee98dfe1b5d84c42749fc40da9279cd573174321ac83201c1651'), size_on_disk=349762, blob_last_accessed=1763591393.0791724, blob_last_modified=1758648267.4966507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4e8332a5db6260f6357f940e62e3e7b622785f386e81cdb9c75b538d318f4a6'), size_on_disk=158978, blob_last_accessed=1763591393.5471692, blob_last_modified=1758648266.7626472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6177/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b91ffef1dfcdf7952df46956d76a4b364aa0d8342453636cf09e6a343607184f'), size_on_disk=387618, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.424655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eb61968b5ec5eadd9087e42ead29333f91195dd5b3f9c5a616b41e94ca23c494'), size_on_disk=330245, blob_last_accessed=1763591393.6761684, blob_last_modified=1758648269.710661), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6689/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/90ea249116dc6a9be86974e1ec0acf79558dfd56a5666be2e05d2107b8c4ee60'), size_on_disk=366787, blob_last_accessed=1763591393.6661685, blob_last_modified=1758648269.1406581), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7185/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/14f67668aca8d8f8aa2b8af98835da683a7fc3f22aa09cdcd7f943a37632391e'), size_on_disk=201650, blob_last_accessed=1763591393.2191715, blob_last_modified=1758648270.1076627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7370/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ff4dce26afb34b291220c17625be4a28078f9721a8b000ea177c4add3bf1a039'), size_on_disk=241010, blob_last_accessed=1763591393.7701678, blob_last_modified=1758648270.6726654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5399/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b65d0aa7ddc98940f91f2ae5f07e1d3f81dda688792552b91fcc39fb4f9ededa'), size_on_disk=256729, blob_last_accessed=1763591392.5471761, blob_last_modified=1758648267.972653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6567/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0e9713a94594dfcbc80134af52449e12ef207ca9149f5a96c43b6cb457b2431c'), size_on_disk=478809, blob_last_accessed=1763591392.6241755, blob_last_modified=1758648268.893657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6390/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f324cdf73148c8b0fa23456bc4f51cff3032d08ed42e44dd9fcc4815a6d969c'), size_on_disk=560601, blob_last_accessed=1763591392.7391748, blob_last_modified=1758648268.6206558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6808/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c71f5371e8c803cfc7fd507d0b3c11f7548db5d7a0fa42c68977169369ea036e'), size_on_disk=422880, blob_last_accessed=1763591392.9491735, blob_last_modified=1758648269.3916593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fc9311b65abb851fbbfd8bb0c0765af59069659f5a715aee546a8644b0af4ec2'), size_on_disk=210948, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648267.6766515), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7367/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a7778bb24506047963575fa96126157895c7f329996c729c3e7d6070595955c3'), size_on_disk=324361, blob_last_accessed=1763591392.1631787, blob_last_modified=1758648270.628665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6448/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/182dc134914a5ddb02992ab6a4f1c12d2450aa331820bf7bd95693dc999bcfae'), size_on_disk=390210, blob_last_accessed=1763591392.9591732, blob_last_modified=1758648268.672656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5654/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38961219eb66dec31505ebcc5088f16684be9891b96fffa8c9a13cd3f13254d0'), size_on_disk=447782, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.1876538), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6055/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/31bb70b4c90654ecb276664f5c914679322b24985ec66d5b396a99f253ebfa17'), size_on_disk=377015, blob_last_accessed=1763591393.5701692, blob_last_modified=1758648268.3076544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/74cbce50c258bdddb7e42c8fd5b0b2a9da74fe3cf89a9ab5a53ee4fde7c1dee6'), size_on_disk=186142, blob_last_accessed=1763591392.842174, blob_last_modified=1758648266.9966483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e91fd764a83947c14f35d7a2d8c0d156c0a61a32a1da96273718ef4be720787'), size_on_disk=361855, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.7196517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7cb5cca74f1bda2f8f1c8ef962e54d34228361eaa9dd8b21e0d1dd7092ff8164'), size_on_disk=229703, blob_last_accessed=1763591392.555176, blob_last_modified=1758648267.0126483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ac8c590c19e7b95b9aac571b1394976039f25fdd66e62a68ecb99925433f8e54'), size_on_disk=355706, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0226483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7332/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f7f62ee008fa2b5cc688cbd80c888f1bf9da538caa245b63581520d485338a99'), size_on_disk=309595, blob_last_accessed=1763591393.1021724, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7151/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9969e4607d6bf42424f5d2ce8f0d16e3d2d89d069cae6d75997e99c88a68dc77'), size_on_disk=275572, blob_last_accessed=1763591393.7231681, blob_last_modified=1758648270.1476629), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4d098c33564f99ec6aed77d585d1f6e871806f441f099790eef4d6478e322af'), size_on_disk=264459, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7486472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d3e36418bd46acf9537a8f864b1a1361a062d72fec2b093789b88ed655ec117'), size_on_disk=377393, blob_last_accessed=1763591393.0451727, blob_last_modified=1758648267.0136483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dcb33135a9d854d17a6b80348f6bee8becea9ca3808583a034f89c5e4f460c54'), size_on_disk=218643, blob_last_accessed=1763591393.4831698, blob_last_modified=1758648267.9636528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6140/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/66c2da17957d7e6a8b159d3573879ac779d0094e35150b51e2643f7ddee36e1f'), size_on_disk=332958, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4236548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6573/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e435989051765cf2955d8cd733904e8b60e5f3121e1c2fdc987f4e6f71fe3970'), size_on_disk=265022, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9586573), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6354/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/80b129a46defc7b06e472a35935bfaf410bc5e456475b2885feb2c8a4934a81b'), size_on_disk=173225, blob_last_accessed=1763591393.4981697, blob_last_modified=1758648268.4986553), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f427ea0f4ded3b6ff841153d8ad87d72a26a964c189246fb6fd5c439bd8b0c1e'), size_on_disk=353623, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.812652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6954/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/aeed3c55d81a4eb0c568486e476211c599ce2bf37f9dff22c7dfca23f5d80495'), size_on_disk=476054, blob_last_accessed=1763591393.011173, blob_last_modified=1758648269.943662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6739/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/58b5ed63f2197f406dd3df6a569975d776722193007912718979eaed4d4e5f9c'), size_on_disk=399160, blob_last_accessed=1763591392.6551754, blob_last_modified=1758648269.331659), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6924/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5e9b3a94d73c1720e6b05ea55000054905a8672edea7f950736ce3fe191f8faf'), size_on_disk=412939, blob_last_accessed=1763591393.748168, blob_last_modified=1758648269.7066607), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=composite/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1f8adf8fb4992283714df8587126211d54e6a0580b8be88c1d5e911196aed783'), size_on_disk=4814573, blob_last_accessed=1763591392.0561793, blob_last_modified=1758648268.0256531), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR007B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6f0620b102e7cbfadced7d5530545a185036b53118d3e2b7f074c56403d80f4b'), size_on_disk=263185, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7616518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6764/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec87eef9e2e8cdf3c3a10ead28dcf1bfa76e94e12a42422a46c8559fb18207c2'), size_on_disk=75684, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.4036593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4b2ff192aa4808cb50b1c1b1667060f98505dd63c1aa58c7d8354e41194957e4'), size_on_disk=289767, blob_last_accessed=1763591393.4871697, blob_last_modified=1758648266.7706473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d50648ed5c9f593f830aff661d13b3d34bd14f0bf93b16169df4765c9f47fc8'), size_on_disk=295924, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7426472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7256/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1af21cbdd7f6f6effff15a1b061a48f85132d582f08c8fd42e04ea58bde7ec00'), size_on_disk=206710, blob_last_accessed=1763591392.586176, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5801/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/12e91a415cc2a149af4c34c3c8b032821ff301e2828dfb9f4636bd6b2913ea3d'), size_on_disk=360485, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.1636536), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=MAG002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f7f35257d4c1f7fa5812ea31c1de791e08ab5e3a4e7ca42b2bb12b2c64f7ed7'), size_on_disk=490174, blob_last_accessed=1763591392.829174, blob_last_modified=1758648267.583651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fa1a44bd33df22ec6b5a8baf3be17e51a11522b5fdd545a38e6e163cc5ae8207'), size_on_disk=241911, blob_last_accessed=1763591392.572176, blob_last_modified=1758648267.5226507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dc74862f02ff12b8c906046f1f03953d33f5516091d58397b0f9a0fc05bb4b5c'), size_on_disk=315435, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.5006506), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6731/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/23ecd71c4e3c1c547295bb756d4654eb413ff288d10118ab286cd21ccd95b666'), size_on_disk=346187, blob_last_accessed=1763591392.1871786, blob_last_modified=1758648269.1906583), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6920/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/45122fcfb3abc3a43631023712c224dc6e00dba566fb2e15deb4ab4501ad2538'), size_on_disk=446998, blob_last_accessed=1763591392.8881738, blob_last_modified=1758648269.6416605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6657/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/95ad2225152f51e2e31c030ff6708aeebccee5e548035d6f43c9cc3554bdfe5e'), size_on_disk=507730, blob_last_accessed=1763591393.163172, blob_last_modified=1758648269.1676583), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR006B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fcc5e886ec7c6363a5ab51b03d3da189ac3e0cde0a0f79d4f0c46043a6b61838'), size_on_disk=253857, blob_last_accessed=1763591392.0501795, blob_last_modified=1758648267.785652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7283/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0dc91bfacca82c5ed23de7757d59f9f808d541d96dd79aab56f0c4252e7aabf3'), size_on_disk=265146, blob_last_accessed=1763591392.7731745, blob_last_modified=1758648270.401664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e871cf06f596bfe74896b62aa28d6b1e6f2f0b8408630fb146c7e29cfd88fbf0'), size_on_disk=232721, blob_last_accessed=1763591392.7871745, blob_last_modified=1758648267.991653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS011/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ea7cb0559b342dd0ec278e7d5846a5ba0c0d867ea04d545f72e0d1c9472a0cc'), size_on_disk=363979, blob_last_accessed=1763591392.9791732, blob_last_modified=1758648267.0116484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5301_5088/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/65ef775934339b80eda59a141cf791a74e1c6f12513d8143307cebe2f11c3738'), size_on_disk=410106, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.9516528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7118/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/be8bd409b83526d5671501de6ef2980177955fdcc3a13d5e153d2593493b2a1e'), size_on_disk=346308, blob_last_accessed=1763591392.9281735, blob_last_modified=1758648270.1336627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e20f616f6e340b76f6446d2cdffb6cc9feb81297bd0307733f835acf4142cfd'), size_on_disk=269523, blob_last_accessed=1763591392.3461773, blob_last_modified=1758648267.2696495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7266/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9aea2d6f5606d24e1999d450379b1f8c8b9d80eded9e784fb729faa516a27026'), size_on_disk=324480, blob_last_accessed=1763591392.0401795, blob_last_modified=1758648270.379664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6527/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/10081f44b1c2f5ad32489da51fc9ac5b5289b54688a7e78725db247a644b7e4d'), size_on_disk=494640, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.882657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7297/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4bd4088cbfcccdf96def916bb8fd9d673327d3e57c2a4a3e57299743ce4aca7'), size_on_disk=261344, blob_last_accessed=1763591393.588169, blob_last_modified=1758648270.6516652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/d1cd38451bc41ef14dbc1566c4704b004b2c0661f667210c81dba09bec90540f'), size_on_disk=244487, blob_last_accessed=1763591393.6301687, blob_last_modified=1758648267.3246498), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5610/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a86512ae1000a994c86e9aebaf30cf9ce1a59160155f3dc1afa6cbde0a1fc47e'), size_on_disk=307382, blob_last_accessed=1763591393.5291693, blob_last_modified=1758648267.994653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6061/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73e03845d0fbff1a5931e4f792e2c1965ade02fc622ffc08e05c267ac6cd3231'), size_on_disk=394930, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.447655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5617/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bc01241e8363373c995a0fa7439aa1e432a60eb8a7805124fff302311f638361'), size_on_disk=351950, blob_last_accessed=1763591393.6541686, blob_last_modified=1758648268.1026535), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6651/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7644af4cc956ea08809ab6c80c65095e3fdbd36e056040c0eaded3773d546359'), size_on_disk=559951, blob_last_accessed=1763591393.6361687, blob_last_modified=1758648269.1856585), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/11485fd7bb94d0093ce2508d541fea3ce64f934197d8a5f7bb26ecbdb59f8dbb'), size_on_disk=124642, blob_last_accessed=1763591392.3321776, blob_last_modified=1758648267.0666487), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7115/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/205443fd87ce5c793dab86eb75c21dab7dc2ae2edac41d7e6314cfe491bdb01d'), size_on_disk=321850, blob_last_accessed=1763591393.2331715, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6872/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02fabe6e32f52fe6fca923beb0a6cc2898d991304e23a3e75f3ae8a6bcf361c6'), size_on_disk=274362, blob_last_accessed=1763591393.6231687, blob_last_modified=1758648269.6966608), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7243/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cb59e645fff2683db692b4d2f514602ba210271b632cd113853febe941c6485f'), size_on_disk=281696, blob_last_accessed=1763591392.701175, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/62e9a627f20607bc4887ebc9277131f974ec80ff39ac386775f4b8f2f5cb9d1d'), size_on_disk=346782, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7476518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6855/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48e48378d24404e926bc1c44b10af60d4b12f82124ff2f80bc0b69b4a0f98740'), size_on_disk=609248, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6556606), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e5b3174998823af40d055f46ca06e7bb44601cbe97b133062cd3e0491dd30ac2'), size_on_disk=323308, blob_last_accessed=1763591393.153172, blob_last_modified=1758648267.2686496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=KB001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38c939d54d3ed77a09650d2ccf00a6ed0e20390b8be722f1c3ed7881a5904a60'), size_on_disk=351677, blob_last_accessed=1763591392.9151735, blob_last_modified=1758648267.5566509), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5961/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/49cac5c2df1adf790a9ac0209db6c0945de50b8191d403e8b1ad386ee2dcb35e'), size_on_disk=321368, blob_last_accessed=1763591392.560176, blob_last_modified=1758648268.215654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7258/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7b1860ba3be7dbf053203fa5690da2002f7841186940231acea6064e70f0bdc8'), size_on_disk=171331, blob_last_accessed=1763591392.6171756, blob_last_modified=1758648270.3746638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7331/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f159a9811b8ac1b5e252b52427979a3bddce2e7131b13f84046b31e47f7fa399'), size_on_disk=242821, blob_last_accessed=1763591393.2501714, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a76f0849e518faedfe50e6f0c76908a3f6beed34fd0b607603db1859493595d0'), size_on_disk=292312, blob_last_accessed=1763587658.6168654, blob_last_modified=1758648266.7676473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7137/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/703fe9f8552adcbdfb5e640509cb62df2fa734b43fe1e98fc1fd342415f823d7'), size_on_disk=515398, blob_last_accessed=1763589053.7698598, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bbe701ffd546a2f4359f821262f7b44232bdc328d9d4719bfdaa8f2aedc6d2e9'), size_on_disk=346079, blob_last_accessed=1763591393.0861723, blob_last_modified=1758648267.589651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7276/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4e0d576cc4a6a85be64f2198e5001a9f5b02b506072df6f695cfdfc719cb041'), size_on_disk=204036, blob_last_accessed=1763591392.987173, blob_last_modified=1758648270.4646642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1fa09a5a6e6e69198501d994050adfcad7ff39a3cc2fe94e7ea177414e1bc968'), size_on_disk=303532, blob_last_accessed=1763591393.7091682, blob_last_modified=1758648267.2776496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6708/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/35520976736159652e3be8066e77b1371522f63a8b3564d9b0d9f5b1598a144d'), size_on_disk=253955, blob_last_accessed=1763591393.6881683, blob_last_modified=1758648269.1996584), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ab338795b8bbf334650386a123f6e0df41363cb5dcbb778d485ce58804e96b29'), size_on_disk=375506, blob_last_accessed=1763591392.9711733, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6959/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c0cb6c2ec2bda19b0c1da5d5fc2229d0b6d6987d75646fa230c0c14d591cf1f1'), size_on_disk=537588, blob_last_accessed=1763591392.851174, blob_last_modified=1758648269.8686616), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/33d7303ed077f489ee28021daca0bbc859feb1cfd383dfc9a856ea13994801a9'), size_on_disk=331877, blob_last_accessed=1763591393.292171, blob_last_modified=1758648268.2226539), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7155/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/521fc1bea6963640645c21cbdb8d1de07793f11213c23ac71d249f3252b32f54'), size_on_disk=304554, blob_last_accessed=1763591393.5841691, blob_last_modified=1758648270.1366627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6853/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ed47ecb2aa870fdbf12920fd4486933262c0a24318c9c8c5531e359a3c416c90'), size_on_disk=515114, blob_last_accessed=1763591393.2661712, blob_last_modified=1758648269.4276595), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6454/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c35b60861d2fc7a3f4256869e14e629aed07cd2ccce76608578ceb8c6ec423e1'), size_on_disk=498326, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.7226562), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6798/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/899cde55032cd593be0013e1f6ff33fe9a12f4616477b1410d86fae1b2008a09'), size_on_disk=342376, blob_last_accessed=1763591392.7951744, blob_last_modified=1758648269.4006593), CachedFileInfo(file_name='annotated_features_meta.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features_meta.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/2be4b693d333698f7ada7ef40ffaf927c0160d7975ebcf6e215cbc4ba1be1604'), size_on_disk=11813, blob_last_accessed=1763587594.724117, blob_last_modified=1758648264.795638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=MAG001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/30b63d014686a103b5e7fc6571e902fa9c08630a96557cc12ec9dfcf60871306'), size_on_disk=347333, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.559651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6677/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4ee6e1d3e2b9a9bbcc8851048d2b16f4aea6a1e2f752afb9e0bf75a56adf7042'), size_on_disk=322147, blob_last_accessed=1763591392.393177, blob_last_modified=1758648269.0826578), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5690/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4831e3e037d421a14efbe1da8c369015ec7d01df351bb27f0ef0400c8b94c693'), size_on_disk=311754, blob_last_accessed=1763591392.9021738, blob_last_modified=1758648268.211654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d4c0cbbf6075a8b5732e3cef2ba154d95d21aa0efc7e49d947cceebd8742b30'), size_on_disk=269007, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4106548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/646c843611bf88c7f5dcd5b1a664d724c3b24b6434cc52d9a54de62ccaf6c542'), size_on_disk=255949, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.4506505), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6861/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6955b1a1746fe41162ea1d7a098482bbfdb3d0e411c809b52d698e285627ce03'), size_on_disk=385103, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6146603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7351/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cc708adf186f28db09dfdd4ac43865a90482b2192306949eab01492598cf23e7'), size_on_disk=198070, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.610665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6b92129c2fb99028b6cab9509cf1a4c40b142109cde34c9a130d375001dcd211'), size_on_disk=267273, blob_last_accessed=1763591392.7811744, blob_last_modified=1758648269.9106617)}), refs=frozenset({'main'}), last_modified=1763649507.5418575)}), last_accessed=1763649507.5438576, last_modified=1763649507.5418575), CachedRepoInfo(repo_id='BrentLab/kemmeren_2014', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014'), size_on_disk=319245924, nb_files=5, revisions=frozenset({CachedRevisionInfo(commit_hash='b76f0dfe8477b300000faecf16b7d315fbf59344', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/b76f0dfe8477b300000faecf16b7d315fbf59344'), size_on_disk=319228170, files=frozenset({CachedFileInfo(file_name='kemmeren_2014.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/b76f0dfe8477b300000faecf16b7d315fbf59344/kemmeren_2014.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/3c463017444bd7690959c0d6fff0d0ebe2faa7b916dd9c44e305156f6c98b863'), size_on_disk=319218929, blob_last_accessed=1760552530.221978, blob_last_modified=1756832530.9644527), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/b76f0dfe8477b300000faecf16b7d315fbf59344/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/199dce9d01d3884716dd3a785a85751cb9b1de3e'), size_on_disk=9241, blob_last_accessed=1760552530.0869784, blob_last_modified=1758654056.5908144)}), refs=frozenset(), last_modified=1758654056.5908144), CachedRevisionInfo(commit_hash='95a5f915ad49dfe4af75632861d9da69de2b525f', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/95a5f915ad49dfe4af75632861d9da69de2b525f'), size_on_disk=10286, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/95a5f915ad49dfe4af75632861d9da69de2b525f/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/05a15ab9764763e5d1957023162e13b0068a5697'), size_on_disk=10286, blob_last_accessed=1765901283.9662895, blob_last_modified=1765415815.7076464)}), refs=frozenset({'main'}), last_modified=1765415815.7076464), CachedRevisionInfo(commit_hash='a6c9a954a1def1774b68582f117ad1abc5864a07', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07'), size_on_disk=319226397, files=frozenset({CachedFileInfo(file_name='kemmeren_2014.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07/kemmeren_2014.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/3c463017444bd7690959c0d6fff0d0ebe2faa7b916dd9c44e305156f6c98b863'), size_on_disk=319218929, blob_last_accessed=1760552530.221978, blob_last_modified=1756832530.9644527), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/a29a64128c10ef9691ee773131972c61f720ba07'), size_on_disk=5007, blob_last_accessed=1756832529.5094638, blob_last_modified=1756832529.5404634), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756832529.5094638, blob_last_modified=1756832529.5434635)}), refs=frozenset(), last_modified=1756832530.9644527)}), last_accessed=1765901283.9662895, last_modified=1765415815.7076464), CachedRepoInfo(repo_id='BrentLab/hu_2007_reimand_2010', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010'), size_on_disk=42682732, nb_files=4, revisions=frozenset({CachedRevisionInfo(commit_hash='31ccd35daf785420014a1346a7db064dd306d4f8', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8'), size_on_disk=42682732, files=frozenset({CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756844741.8097973, blob_last_modified=1756844741.8767972), CachedFileInfo(file_name='hu_2007_reimand_2010.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/hu_2007_reimand_2010.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/eb9be5a1f01907a81eb2ab4fbbee36ff8c50186f'), size_on_disk=63, blob_last_accessed=1756844741.7997973, blob_last_modified=1756844741.8557973), CachedFileInfo(file_name='hu_2007_reimand_2010.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/hu_2007_reimand_2010.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/47a15f3e23f8cb50af3217d00c6439d29a7422b5190116e4453a5f78cb22540d'), size_on_disk=42677516, blob_last_accessed=1756844742.1787965, blob_last_modified=1756844742.1477966), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/886f4614dfd1ef33709d462908ae18608c13e8ba'), size_on_disk=2692, blob_last_accessed=1756844742.0937967, blob_last_modified=1756844742.1537964)}), refs=frozenset({'main'}), last_modified=1756844742.1537964)}), last_accessed=1756844742.1787965, last_modified=1756844742.1537964), CachedRepoInfo(repo_id='BrentLab/rossi_2021', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021'), size_on_disk=213734041, nb_files=48, revisions=frozenset({CachedRevisionInfo(commit_hash='dcf529428b66f5efc8c44ea06fb4733c41952e03', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/dcf529428b66f5efc8c44ea06fb4733c41952e03'), size_on_disk=15570962, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/dcf529428b66f5efc8c44ea06fb4733c41952e03/genome_map/accession=SRR11466106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/04bacc0f8c03ee000ba3b69320522d0b1be909686474b6e8a126173a0ceae8c4'), size_on_disk=15547870, blob_last_accessed=1762805142.7999256, blob_last_modified=1756944357.1972551), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/dcf529428b66f5efc8c44ea06fb4733c41952e03/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/192e51c15075f65f15da70558261e4c95bb1392a'), size_on_disk=8396, blob_last_accessed=1762805108.5211468, blob_last_modified=1762805108.5201466), CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/dcf529428b66f5efc8c44ea06fb4733c41952e03/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1762805126.6580298, blob_last_modified=1756944310.9395182)}), refs=frozenset({'main'}), last_modified=1762805108.5201466), CachedRevisionInfo(commit_hash='6b29baae54286d5fbdd1c70f499c03319badad51', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51'), size_on_disk=213707016, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466115/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/c08ce1194dd98cb028a6038944317e70ac1545e32b21dd323eafa57b16611e6e'), size_on_disk=3503431, blob_last_accessed=1757598690.9468212, blob_last_modified=1757104405.9427147), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466143/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/54bd4af6ed8caf52a5810598d96c9b235266542586a692112cb247a209c2649f'), size_on_disk=10801408, blob_last_accessed=1757598691.2688208, blob_last_modified=1757104413.3726768), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466107/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/2f924bc58d82a21b621b051addd1913c77369afaa738171ef0962548fc1a1021'), size_on_disk=5670198, blob_last_accessed=1757598690.8608212, blob_last_modified=1757104405.0417192), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466124/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/39ef32412cae257b724c3b797ea482eeffb5b29afef4246d997ea525a2124054'), size_on_disk=7162800, blob_last_accessed=1757598691.075821, blob_last_modified=1757104409.5966961), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466108/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/56875f81a3cc7c1d40b3042b2c443058eb0f9a2b7e7ebc2e1704c098be612628'), size_on_disk=1765819, blob_last_accessed=1757598690.8238213, blob_last_modified=1757104407.1947083), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466127/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/1565c5175e05c322f160529cb4bb74d5e8468458f150f228d6aadf2b670c3b4a'), size_on_disk=11670434, blob_last_accessed=1757598691.092821, blob_last_modified=1757104411.0776885), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466125/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/0bba21490ea0a6297031b0ff3219fc0158d9bbcf15099906c86a770b1a1312fc'), size_on_disk=2624759, blob_last_accessed=1757598691.081821, blob_last_modified=1757104409.4426968), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466129/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/b290d4c9436756b444d0da1af1229b0a2a6e877fd968c5d01b73efa96e62cff8'), size_on_disk=6955953, blob_last_accessed=1757598691.196821, blob_last_modified=1757104410.78769), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466126/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/34292b90d99b5a5be542c7dd10b11336605330639d92e93b092e567a56fe7d93'), size_on_disk=2850164, blob_last_accessed=1757598691.086821, blob_last_modified=1757104410.0976934), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466144/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/57b3d2c7917bfa29312cc51c921beff914858c61762e28a658eb29d8eaf3348b'), size_on_disk=918271, blob_last_accessed=1757598691.2758207, blob_last_modified=1757104413.554676), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466112/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8f3441964a046d3c148b41f335948a085d26884e1a890e515711a2d1b48a40be'), size_on_disk=4883601, blob_last_accessed=1757598690.9128213, blob_last_modified=1757104408.5027015), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466142/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/3dc044b5cc1b7bc8070d8de4b7b870524b296f26b4f43b22dce2dcbf161d4c74'), size_on_disk=1739520, blob_last_accessed=1757598691.2588208, blob_last_modified=1757104415.5896657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466149/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/ae4d5237fde214a92d3267fbe11f6f1f88569cc7f3f7ccb40fed200871bf33b1'), size_on_disk=10933582, blob_last_accessed=1757598691.3518207, blob_last_modified=1757104415.1316679), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466109/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/6588704c47423e671054df9cb2318c045138b0018f2eefff61ef91a25848eb58'), size_on_disk=5259413, blob_last_accessed=1757598690.8298213, blob_last_modified=1757104404.9987195), CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1762805126.6580298, blob_last_modified=1756944310.9395182), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466141/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/e9efb807ce35fc3e43b1ff0d27b80646c7d137c587abd9d6247393e44d4f30a0'), size_on_disk=986450, blob_last_accessed=1757598691.2628207, blob_last_modified=1757104413.129678), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/04bacc0f8c03ee000ba3b69320522d0b1be909686474b6e8a126173a0ceae8c4'), size_on_disk=15547870, blob_last_accessed=1762805142.7999256, blob_last_modified=1756944357.1972551), CachedFileInfo(file_name='genome_map.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/51978dc2125afb40a41a29672f8ae750308d9a63'), size_on_disk=5311747, blob_last_accessed=1757598690.9468212, blob_last_modified=1757104406.477712), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466118/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/192057e6004dd5cd303e8b1abf54cb72005e211c315665e28eec7b617a84d95a'), size_on_disk=728586, blob_last_accessed=1757598691.0068212, blob_last_modified=1757104407.0917087), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466122/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/6362da9e07f02d4c9dc05f15f638ca1b1bf0de5989da4ee8c6f0768f2453f98f'), size_on_disk=1249990, blob_last_accessed=1757598691.0398211, blob_last_modified=1757104411.9016843), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466150/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/80cd2d325e5d7a50c6ecbf79a052f45c54a93b9423e90735bf8989aada9bb9eb'), size_on_disk=3234303, blob_last_accessed=1757598691.3798206, blob_last_modified=1757104417.837654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466139/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/25c7d3ffbacca76fd998edffe0c762e220f448140019aa2d389facff5117ce48'), size_on_disk=176811, blob_last_accessed=1757598691.2478209, blob_last_modified=1757104412.6266806), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466110/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/d5283e08e898b4f59837f7ff2064f79c8a828994f868c637894f4e07df36daa5'), size_on_disk=13123795, blob_last_accessed=1757598690.8318212, blob_last_modified=1757104407.6787057), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466132/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/cc41509e63473d565341ed36fd6881fecfd537e59c003f4779d4c4ec7b49cb54'), size_on_disk=1906184, blob_last_accessed=1757598691.161821, blob_last_modified=1757104413.4366765), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466131/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/bdce0842b5568e406a799dbe36b4b9645a30fa58eb0a707f24ed3bb3900fbf67'), size_on_disk=11759994, blob_last_accessed=1757598691.157821, blob_last_modified=1757104411.1146884), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466111/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/ba431fc9a05f40581e450429d467226a8e3c49195de355437f9044bb0fdfe524'), size_on_disk=5749460, blob_last_accessed=1757598690.9028213, blob_last_modified=1757104404.9707196), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466114/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/2c07f671cca471d6ad3e10e071cd1b976a89a45ff32920913d8fc36dbeaa1678'), size_on_disk=10992797, blob_last_accessed=1757598690.9498212, blob_last_modified=1757104406.3287127), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466123/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/57b7ca5d419676590538573f295b99db05a952e20cb4c6b2a1618974611d5051'), size_on_disk=3672899, blob_last_accessed=1757598691.073821, blob_last_modified=1757104409.590696), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466135/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/d44d381db0b8c0cb4960796e48d3d16e3d766aff87cbc686789a0c5799ba0a98'), size_on_disk=5204256, blob_last_accessed=1757598691.180821, blob_last_modified=1757104412.4376817), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466113/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/acec3b041a74ab45dbeb03e7f183c2b3d16479aadd64a81f80a74aa42d56368d'), size_on_disk=5866774, blob_last_accessed=1757598690.921821, blob_last_modified=1757104409.0226989), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466134/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/0b23434181eb1fc864e858cfca82d2d8fed1524a8f7dbea3bf576f6f452cf08b'), size_on_disk=12398132, blob_last_accessed=1757598691.169821, blob_last_modified=1757104412.552681), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466140/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/25f98a891820b5a91c11629ed5615d40ffe1fe4a6cba1b2c6642067d76b5be87'), size_on_disk=68174, blob_last_accessed=1757598691.2588208, blob_last_modified=1757104415.6456654), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/09f6dce90ec0e64d8f18614091dd18ad2e86213a'), size_on_disk=4403, blob_last_accessed=1757598690.987821, blob_last_modified=1757104409.9196944), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466136/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/f5e7fd644d30ae0c8e2c72bc341155bfe5df2ce102b68b08ff4d243470026fcc'), size_on_disk=343033, blob_last_accessed=1757598691.1958208, blob_last_modified=1757104411.7116852), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466119/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/0157d9136c2c2bb5640866449adf4bab8c62b3d7f7fb4262981908c77986f89e'), size_on_disk=12516152, blob_last_accessed=1757598691.0068212, blob_last_modified=1757104412.3166823), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466120/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/4220a31e6a7be79341828d41da77ef77fb5a8026980d4445fec18c57b5229c1f'), size_on_disk=3644577, blob_last_accessed=1757598691.034821, blob_last_modified=1757104408.3467023), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466121/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/78d0f377f8a756b28f2f5296932368c9c6b764d2972dc93aa6dd354591d29860'), size_on_disk=7053621, blob_last_accessed=1757598691.0328212, blob_last_modified=1757104408.5097015), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1757598690.9228213, blob_last_modified=1757104403.6017265), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466116/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/587a7b86dffe3d7ade9adeeb94029ea1de54e61fb2c2445dc527979b7f1c24ac'), size_on_disk=5596311, blob_last_accessed=1757598690.988821, blob_last_modified=1757104407.3507075), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466130/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/73db821736e3e47c09da250e369fa06812e02e5abe7a4a3ceab5d1020422b66c'), size_on_disk=1920920, blob_last_accessed=1757598691.1328208, blob_last_modified=1757104410.9306893), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466117/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/49065d4cee74c20f34303405e34e284e29f19082b76fa883a38aeab24cadf674'), size_on_disk=1742958, blob_last_accessed=1757598690.995821, blob_last_modified=1757104411.8276846), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466128/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/f631797d2bc13a580f570637d94a0b9ea37deb2fd7ef92e32c66fd9014260525'), size_on_disk=1059587, blob_last_accessed=1757598691.118821, blob_last_modified=1757104410.6996903), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466133/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/b6d6913e14b52d5d51dab2f254815da89e03cebe75e80356b27cf18e31eb048f'), size_on_disk=5090722, blob_last_accessed=1757598691.169821, blob_last_modified=1757104412.180683)}), refs=frozenset(), last_modified=1757104417.837654), CachedRevisionInfo(commit_hash='6aac54b4b3fbb414561a8b6cc7acf87880c3c3c2', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6aac54b4b3fbb414561a8b6cc7acf87880c3c3c2'), size_on_disk=4665, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6aac54b4b3fbb414561a8b6cc7acf87880c3c3c2/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/14e1e02ad3804c0a4146a52509460e9cbb759d93'), size_on_disk=4665, blob_last_accessed=1758306843.4906857, blob_last_modified=1758156479.1516545)}), refs=frozenset(), last_modified=1758156479.1516545), CachedRevisionInfo(commit_hash='824bf517ff0722a085c86ac7924df0ab1278c8bf', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/824bf517ff0722a085c86ac7924df0ab1278c8bf'), size_on_disk=14696, files=frozenset({CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/824bf517ff0722a085c86ac7924df0ab1278c8bf/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1762805126.6580298, blob_last_modified=1756944310.9395182)}), refs=frozenset(), last_modified=1756944310.9395182), CachedRevisionInfo(commit_hash='3ce77eb2070d7bb43049ba26bb462fded0ff7863', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/3ce77eb2070d7bb43049ba26bb462fded0ff7863'), size_on_disk=15562566, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/3ce77eb2070d7bb43049ba26bb462fded0ff7863/genome_map/accession=SRR11466106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/04bacc0f8c03ee000ba3b69320522d0b1be909686474b6e8a126173a0ceae8c4'), size_on_disk=15547870, blob_last_accessed=1762805142.7999256, blob_last_modified=1756944357.1972551), CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/3ce77eb2070d7bb43049ba26bb462fded0ff7863/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1762805126.6580298, blob_last_modified=1756944310.9395182)}), refs=frozenset(), last_modified=1756944357.1972551), CachedRevisionInfo(commit_hash='1acaa5629922414e8efe23a5d023bd44965824c8', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1acaa5629922414e8efe23a5d023bd44965824c8'), size_on_disk=4683, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1acaa5629922414e8efe23a5d023bd44965824c8/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/bb690d71edd44885609ed660d926f3cf2c4c473e'), size_on_disk=4683, blob_last_accessed=1758306349.7238934, blob_last_modified=1758156991.1235294)}), refs=frozenset(), last_modified=1758156991.1235294), CachedRevisionInfo(commit_hash='22ebbfcf62a7ed665fb8eceaea53b58b45a1709e', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/22ebbfcf62a7ed665fb8eceaea53b58b45a1709e'), size_on_disk=4602, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/22ebbfcf62a7ed665fb8eceaea53b58b45a1709e/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/e03b8d10ba59c549907a309ad8343133b7b902fc'), size_on_disk=4602, blob_last_accessed=1757613581.062093, blob_last_modified=1757613581.0610929)}), refs=frozenset(), last_modified=1757613581.0610929), CachedRevisionInfo(commit_hash='1e07e46fd0b5348243487ac4a573b570a2a3946b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1e07e46fd0b5348243487ac4a573b570a2a3946b'), size_on_disk=4683, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1e07e46fd0b5348243487ac4a573b570a2a3946b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/bb690d71edd44885609ed660d926f3cf2c4c473e'), size_on_disk=4683, blob_last_accessed=1758306349.7238934, blob_last_modified=1758156991.1235294)}), refs=frozenset(), last_modified=1758156991.1235294), CachedRevisionInfo(commit_hash='664d95cdd482d753bc139d26119061c2fa6eaa76', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/664d95cdd482d753bc139d26119061c2fa6eaa76'), size_on_disk=4679, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/664d95cdd482d753bc139d26119061c2fa6eaa76/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/ff6f75db42c0690f0e0285e56c1aa95bc5cf1300'), size_on_disk=4679, blob_last_accessed=1758157110.1705706, blob_last_modified=1758157110.1695707)}), refs=frozenset(), last_modified=1758157110.1695707)}), last_accessed=1762805142.7999256, last_modified=1762805108.5201466), CachedRepoInfo(repo_id='BrentLab/harbison_2004', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004'), size_on_disk=70377892, nb_files=10, revisions=frozenset({CachedRevisionInfo(commit_hash='2916ad316cc0d8a1ce2e7f35d3707b831b203e87', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/2916ad316cc0d8a1ce2e7f35d3707b831b203e87'), size_on_disk=7160, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/2916ad316cc0d8a1ce2e7f35d3707b831b203e87/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/7e3ec66f5479346ca9d082545ff0b6a960fb60d5'), size_on_disk=7160, blob_last_accessed=1758649475.233073, blob_last_modified=1758155946.7669883)}), refs=frozenset(), last_modified=1758155946.7669883), CachedRevisionInfo(commit_hash='073cdeb1b541c28b1a97df9dbc6be6af1d9c23b6', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/073cdeb1b541c28b1a97df9dbc6be6af1d9c23b6'), size_on_disk=5145, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/073cdeb1b541c28b1a97df9dbc6be6af1d9c23b6/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/f7a6cf3dd4477f08e508f38665350e8d11589963'), size_on_disk=5145, blob_last_accessed=1755876214.0380862, blob_last_modified=1755876188.1642818)}), refs=frozenset(), last_modified=1755876188.1642818), CachedRevisionInfo(commit_hash='a0f6f1b23505c4e6a471e65a43a33ff5cced0733', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a0f6f1b23505c4e6a471e65a43a33ff5cced0733'), size_on_disk=25423545, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a0f6f1b23505c4e6a471e65a43a33ff5cced0733/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b5fbd9e98fd8ddadeeb5631e3b6f5055e917c98d'), size_on_disk=7679, blob_last_accessed=1759345152.2162483, blob_last_modified=1758649637.161202), CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a0f6f1b23505c4e6a471e65a43a33ff5cced0733/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/91904b3e84754bd6011cd4b385bbc99d69a899e5c7562f039a121a744ba95a0a'), size_on_disk=25415866, blob_last_accessed=1763588775.480192, blob_last_modified=1758649664.1292322)}), refs=frozenset(), last_modified=1758649664.1292322), CachedRevisionInfo(commit_hash='5eeb06e389a648a36087e20eabad1f961e22dc5a', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/5eeb06e389a648a36087e20eabad1f961e22dc5a'), size_on_disk=44894945, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/5eeb06e389a648a36087e20eabad1f961e22dc5a/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/aa43ea37112a84a60d21ce254c1cc1e433def6dc'), size_on_disk=8550, blob_last_accessed=1765406568.606891, blob_last_modified=1765292615.6690626), CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/5eeb06e389a648a36087e20eabad1f961e22dc5a/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b89f865a471fbf3a8054871f8fe79507f6f4be5c2291dcd19030ec8fd4a5325c'), size_on_disk=44886395, blob_last_accessed=1765901285.6763043, blob_last_modified=1765314558.8410568)}), refs=frozenset(), last_modified=1765314558.8410568), CachedRevisionInfo(commit_hash='4e54b7fafd79829bcd179b508efd11a3dc7182cc', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/4e54b7fafd79829bcd179b508efd11a3dc7182cc'), size_on_disk=44900498, files=frozenset({CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/4e54b7fafd79829bcd179b508efd11a3dc7182cc/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b89f865a471fbf3a8054871f8fe79507f6f4be5c2291dcd19030ec8fd4a5325c'), size_on_disk=44886395, blob_last_accessed=1765901285.6763043, blob_last_modified=1765314558.8410568), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/4e54b7fafd79829bcd179b508efd11a3dc7182cc/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/ef196364c79e1a0ec01b3ab6db8dae798f0f8fd5'), size_on_disk=14103, blob_last_accessed=1765911961.5656407, blob_last_modified=1765819563.7734888)}), refs=frozenset(), last_modified=1765819563.7734888), CachedRevisionInfo(commit_hash='95dace55563820030e6bcac2d4b2a9a386a2369b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/95dace55563820030e6bcac2d4b2a9a386a2369b'), size_on_disk=44900425, files=frozenset({CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/95dace55563820030e6bcac2d4b2a9a386a2369b/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b89f865a471fbf3a8054871f8fe79507f6f4be5c2291dcd19030ec8fd4a5325c'), size_on_disk=44886395, blob_last_accessed=1765901285.6763043, blob_last_modified=1765314558.8410568), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/95dace55563820030e6bcac2d4b2a9a386a2369b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/1cfc9a691644fd13ba4e3c4d141caf8bc8dadc92'), size_on_disk=14030, blob_last_accessed=1765809787.9431984, blob_last_modified=1765415607.8811436)}), refs=frozenset(), last_modified=1765415607.8811436), CachedRevisionInfo(commit_hash='a33c34b373e379dfa9bd4922d281790180bb1217', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a33c34b373e379dfa9bd4922d281790180bb1217'), size_on_disk=44899466, files=frozenset({CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a33c34b373e379dfa9bd4922d281790180bb1217/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b89f865a471fbf3a8054871f8fe79507f6f4be5c2291dcd19030ec8fd4a5325c'), size_on_disk=44886395, blob_last_accessed=1765901285.6763043, blob_last_modified=1765314558.8410568), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a33c34b373e379dfa9bd4922d281790180bb1217/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/3cb3997d01aa1c91d0719f954e1cf207976c8a7d'), size_on_disk=13071, blob_last_accessed=1765916907.5030358, blob_last_modified=1765916907.339037)}), refs=frozenset({'main'}), last_modified=1765916907.339037), CachedRevisionInfo(commit_hash='3ff5f32dfeef3c0b2b7d6cc860b259b0ad095829', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/3ff5f32dfeef3c0b2b7d6cc860b259b0ad095829'), size_on_disk=25421759, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/3ff5f32dfeef3c0b2b7d6cc860b259b0ad095829/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/342343ab9f71e7ce0394c540bed737de39f2cf08'), size_on_disk=5893, blob_last_accessed=1764345374.74204, blob_last_modified=1763587990.060371), CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/3ff5f32dfeef3c0b2b7d6cc860b259b0ad095829/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/91904b3e84754bd6011cd4b385bbc99d69a899e5c7562f039a121a744ba95a0a'), size_on_disk=25415866, blob_last_accessed=1763588775.480192, blob_last_modified=1758649664.1292322)}), refs=frozenset(), last_modified=1763587990.060371)}), last_accessed=1765916907.5030358, last_modified=1765916907.339037), CachedRepoInfo(repo_id='BrentLab/yeast_genome_resources', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources'), size_on_disk=114475, nb_files=7, revisions=frozenset({CachedRevisionInfo(commit_hash='6607f7be76546563db0a68a9365c0a858bb5f3a1', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/6607f7be76546563db0a68a9365c0a858bb5f3a1'), size_on_disk=4495, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/6607f7be76546563db0a68a9365c0a858bb5f3a1/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/a31c4647c502767c22b5fe725ec8de58686a02d4'), size_on_disk=4495, blob_last_accessed=1755813454.8008156, blob_last_modified=1755813454.7998157)}), refs=frozenset(), last_modified=1755813454.7998157), CachedRevisionInfo(commit_hash='7441b9a8c858332e730e7ddb9d255460ae698626', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/7441b9a8c858332e730e7ddb9d255460ae698626'), size_on_disk=87714, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/7441b9a8c858332e730e7ddb9d255460ae698626/features/chr=chrII/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/d64638adb2290412a962abffe0fbd0546ff139b29064bbf313b0a768449454a5'), size_on_disk=82276, blob_last_accessed=1755812630.656903, blob_last_modified=1755812631.0999012), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/7441b9a8c858332e730e7ddb9d255460ae698626/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/e63c2087b2e56dc15e8ac6cf5693c2391127b732'), size_on_disk=5438, blob_last_accessed=1755816785.70087, blob_last_modified=1755816785.6988702)}), refs=frozenset(), last_modified=1755816785.6988702), CachedRevisionInfo(commit_hash='25b47a191e40728efc2437d906e84317f922b36b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/25b47a191e40728efc2437d906e84317f922b36b'), size_on_disk=5426, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/25b47a191e40728efc2437d906e84317f922b36b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/9cdf7b342b249f25f2c2cbe46a2e0a26ded4f692'), size_on_disk=5426, blob_last_accessed=1755815405.3308983, blob_last_modified=1755815405.3298984)}), refs=frozenset(), last_modified=1755815405.3298984), CachedRevisionInfo(commit_hash='aae6e5ea8139e23940d11b4d7e77684c78bb7f6b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/aae6e5ea8139e23940d11b4d7e77684c78bb7f6b'), size_on_disk=4585, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/aae6e5ea8139e23940d11b4d7e77684c78bb7f6b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/4320f8f18620d988a5fc32872a671ace406fd0a2'), size_on_disk=4585, blob_last_accessed=1755814342.1923234, blob_last_modified=1755814342.1913235)}), refs=frozenset(), last_modified=1755814342.1913235), CachedRevisionInfo(commit_hash='a5ecb243ba27ca4b4d6ad1b60cc160c033ca2be6', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/a5ecb243ba27ca4b4d6ad1b60cc160c033ca2be6'), size_on_disk=82276, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/a5ecb243ba27ca4b4d6ad1b60cc160c033ca2be6/features/chr=chrII/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/d64638adb2290412a962abffe0fbd0546ff139b29064bbf313b0a768449454a5'), size_on_disk=82276, blob_last_accessed=1755812630.656903, blob_last_modified=1755812631.0999012)}), refs=frozenset(), last_modified=1755812631.0999012), CachedRevisionInfo(commit_hash='42beb28478e27c5d4c5bb2308b12100e6489ef07', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/42beb28478e27c5d4c5bb2308b12100e6489ef07'), size_on_disk=6125, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/42beb28478e27c5d4c5bb2308b12100e6489ef07/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/ab6cc475062a2767a41b2aaa006faf4d9d2d60d6'), size_on_disk=6125, blob_last_accessed=1759787821.8450553, blob_last_modified=1758155946.5549896)}), refs=frozenset({'main'}), last_modified=1758155946.5549896), CachedRevisionInfo(commit_hash='15fdb72f8c31ae58f3e3ce7c90be279383743a2f', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/15fdb72f8c31ae58f3e3ce7c90be279383743a2f'), size_on_disk=88406, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/15fdb72f8c31ae58f3e3ce7c90be279383743a2f/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/645da8028fd6abe86ee305f76eab78cdf29be364'), size_on_disk=6130, blob_last_accessed=1755819093.2316637, blob_last_modified=1755819093.2306638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/15fdb72f8c31ae58f3e3ce7c90be279383743a2f/features/chr=chrII/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/d64638adb2290412a962abffe0fbd0546ff139b29064bbf313b0a768449454a5'), size_on_disk=82276, blob_last_accessed=1755812630.656903, blob_last_modified=1755812631.0999012)}), refs=frozenset(), last_modified=1755819093.2306638)}), last_accessed=1759787821.8450553, last_modified=1758155946.5549896), CachedRepoInfo(repo_id='BrentLab/hackett_2020', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020'), size_on_disk=817026981, nb_files=22, revisions=frozenset({CachedRevisionInfo(commit_hash='4fef197cd055065207d66ea42aec9746b23f38c8', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/4fef197cd055065207d66ea42aec9746b23f38c8'), size_on_disk=9431, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/4fef197cd055065207d66ea42aec9746b23f38c8/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/0a3330d889673abc635b58a78c221ba5b3e36200'), size_on_disk=9431, blob_last_accessed=1765809790.786152, blob_last_modified=1765650535.7166286)}), refs=frozenset({'main'}), last_modified=1765650535.7166286), CachedRevisionInfo(commit_hash='2c196866d4f057cb5cf0adf1fe35f2c712c61025', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025'), size_on_disk=404565376, files=frozenset({CachedFileInfo(file_name='hackett_2020.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/hackett_2020.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4f47951c65c799b5c74ee3782b49c16cbb038d50'), size_on_disk=55, blob_last_accessed=1757105553.3228161, blob_last_modified=1756843395.5719483), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c3e72ccb1b8deba4bbfd18abe6081de7ec3914d9'), size_on_disk=8879, blob_last_accessed=1757105552.290822, blob_last_modified=1757104206.2667632), CachedFileInfo(file_name='parse_mcisaac_data.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/scripts/parse_mcisaac_data.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0'), size_on_disk=4691, blob_last_accessed=1756843395.7579472, blob_last_modified=1756843395.820947), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1760552530.4189773, blob_last_modified=1756843405.225893), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756843395.5059488, blob_last_modified=1756843395.5679483)}), refs=frozenset(), last_modified=1757104206.2667632), CachedRevisionInfo(commit_hash='1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43'), size_on_disk=2678425, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=45/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/db8595cd20dd7440c890c46025cf0643da8ec6be3e15bc18a8759b3fecdc02af'), size_on_disk=349256, blob_last_accessed=1756145253.7795417, blob_last_modified=1756145254.6225288), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=5/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/ed331d9d2b27a0958b12368c7b5d2d3383b111d3d1c39ed0e77b78561dc78b6a'), size_on_disk=348626, blob_last_accessed=1756145253.7845416, blob_last_modified=1756145254.6395288), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=90/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/ebd06af83cc386d9094ce6b8d795b155c76c47c96da1a458743348264f1638ae'), size_on_disk=349917, blob_last_accessed=1756145253.7845416, blob_last_modified=1756145254.7235274), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=30/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/a85bd6b418d9644d9adaa1269c27f97469a4aaee51af63cf1aa041f62cd8ba2c'), size_on_disk=350044, blob_last_accessed=1756145253.7915416, blob_last_modified=1756145254.6385286), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/45cb0687c3ea72e7b32ad17dd799bc38ad02f80b'), size_on_disk=16034, blob_last_accessed=1756138543.6766906, blob_last_modified=1756138543.6746907), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=10/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4a2eb1051cad96c5b39a014f19b092f042b03700cc1b1a40c2de9e7edd73edf9'), size_on_disk=352201, blob_last_accessed=1756145253.7915416, blob_last_modified=1756145254.6375287), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=15/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/51dfb68ed2518f0dd8ab4d4070d4896b6eca912ecb7e4aa773d857e3096020b5'), size_on_disk=351273, blob_last_accessed=1756145217.972108, blob_last_modified=1756142799.7054222), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=20/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/97adc0bb2b436c325555d82bb7f0ffb0e250d73092fcbf03ac0c00fa2b762e51'), size_on_disk=351379, blob_last_accessed=1756145253.7895415, blob_last_modified=1756145254.6265287), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=0/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/409962e1914ac9e9d631e4ae73e04b167822d0915d8d8d4a87703d4c7281ffb9'), size_on_disk=209695, blob_last_accessed=1756145254.7305272, blob_last_modified=1756145254.608529)}), refs=frozenset(), last_modified=1756145254.7235274), CachedRevisionInfo(commit_hash='60b3ecf6a06e709f0397b327ece5c31016303b5c', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/60b3ecf6a06e709f0397b327ece5c31016303b5c'), size_on_disk=404558833, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/60b3ecf6a06e709f0397b327ece5c31016303b5c/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/6f54100c2bb84a6bc059c17e0954dad0ea451666'), size_on_disk=9543, blob_last_accessed=1760552529.9219792, blob_last_modified=1758155372.1328666), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/60b3ecf6a06e709f0397b327ece5c31016303b5c/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1760552530.4189773, blob_last_modified=1756843405.225893)}), refs=frozenset(), last_modified=1758155372.1328666), CachedRevisionInfo(commit_hash='ca2500351e8aa9fc5b996516cd25a60e8298734d', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/ca2500351e8aa9fc5b996516cd25a60e8298734d'), size_on_disk=6294, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/ca2500351e8aa9fc5b996516cd25a60e8298734d/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/ebb31f16ba92f3c0a9eec2aaf4897b3a7662cfad'), size_on_disk=6294, blob_last_accessed=1764169619.6288424, blob_last_modified=1764169578.8420532)}), refs=frozenset(), last_modified=1764169578.8420532), CachedRevisionInfo(commit_hash='6bea8e8497a12fec83927a941ba912a0ec781822', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/6bea8e8497a12fec83927a941ba912a0ec781822'), size_on_disk=8027, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/6bea8e8497a12fec83927a941ba912a0ec781822/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/159b3e518310d0bf928d14a691e41a77d5540aaa'), size_on_disk=8027, blob_last_accessed=1765299157.242701, blob_last_modified=1765299156.9627051)}), refs=frozenset(), last_modified=1765299156.9627051), CachedRevisionInfo(commit_hash='5b09fb31bac250b0bfc099c1bdb32bc2673e754b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/5b09fb31bac250b0bfc099c1bdb32bc2673e754b'), size_on_disk=409731660, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/5b09fb31bac250b0bfc099c1bdb32bc2673e754b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/dacac6a8023596c4c46dce14e80f861d4e4a7401'), size_on_disk=9583, blob_last_accessed=1765309535.4347303, blob_last_modified=1765308942.1208022), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/5b09fb31bac250b0bfc099c1bdb32bc2673e754b/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/10f544e6c4e4da0b411f1084cbe7f9188a842bc6578b699bdf02f3507aff6592'), size_on_disk=409722077, blob_last_accessed=1765311990.5457778, blob_last_modified=1765311990.540778)}), refs=frozenset(), last_modified=1765311990.540778), CachedRevisionInfo(commit_hash='7192b0e2275ba5aa3ffb3752b87f1c3595f2331a', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a'), size_on_disk=404565656, files=frozenset({CachedFileInfo(file_name='parse_mcisaac_data.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/scripts/parse_mcisaac_data.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0'), size_on_disk=4691, blob_last_accessed=1756843395.7579472, blob_last_modified=1756843395.820947), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/f3899cd97da03a3543db10d76c15ee0442bb136e'), size_on_disk=9159, blob_last_accessed=1758145252.5929751, blob_last_modified=1757541416.40237), CachedFileInfo(file_name='hackett_2020.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/hackett_2020.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4f47951c65c799b5c74ee3782b49c16cbb038d50'), size_on_disk=55, blob_last_accessed=1757105553.3228161, blob_last_modified=1756843395.5719483), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756843395.5059488, blob_last_modified=1756843395.5679483), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1760552530.4189773, blob_last_modified=1756843405.225893)}), refs=frozenset(), last_modified=1757541416.40237), CachedRevisionInfo(commit_hash='b9d39535e175295d9cba23489a49fafebbac5ca0', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0'), size_on_disk=404565563, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/03e579e00c632cdb393c0ffafd9d15b069390e5c'), size_on_disk=9066, blob_last_accessed=1757439136.0598524, blob_last_modified=1757105945.6476595), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756843395.5059488, blob_last_modified=1756843395.5679483), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1760552530.4189773, blob_last_modified=1756843405.225893), CachedFileInfo(file_name='hackett_2020.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/hackett_2020.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4f47951c65c799b5c74ee3782b49c16cbb038d50'), size_on_disk=55, blob_last_accessed=1757105553.3228161, blob_last_modified=1756843395.5719483), CachedFileInfo(file_name='parse_mcisaac_data.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/scripts/parse_mcisaac_data.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0'), size_on_disk=4691, blob_last_accessed=1756843395.7579472, blob_last_modified=1756843395.820947)}), refs=frozenset(), last_modified=1757105945.6476595)}), last_accessed=1765809790.786152, last_modified=1765650535.7166286)}), warnings=[])" + "HFCacheInfo(size_on_disk=5525278711, repos=frozenset({CachedRepoInfo(repo_id='BrentLab/yeast_comparative_analysis', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis'), size_on_disk=166072, nb_files=7, revisions=frozenset({CachedRevisionInfo(commit_hash='ac03d065bb493bc9dd7a77460fdaf6f954968b0b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis/snapshots/ac03d065bb493bc9dd7a77460fdaf6f954968b0b'), size_on_disk=166072, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis/snapshots/ac03d065bb493bc9dd7a77460fdaf6f954968b0b/dto/binding_repo_dataset=harbison_2004-harbison_2004/perturbation_repo_dataset=hu_2007_reimand_2010-hu_2007_reimand_2010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis/blobs/30024adb7ad73354c2c962d168dee64c9601c5ee3dcfea72c24178a5638dc32b'), size_on_disk=4498, blob_last_accessed=1767824941.5591376, blob_last_modified=1767824941.5531375), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis/snapshots/ac03d065bb493bc9dd7a77460fdaf6f954968b0b/dto/binding_repo_dataset=harbison_2004-harbison_2004/perturbation_repo_dataset=Hackett_2020-hackett_2020/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis/blobs/0372fb07783a76efc38492cd738f4bf3eef044a6069f00a1d447cf2dae3e243c'), size_on_disk=20942, blob_last_accessed=1767824941.5571375, blob_last_modified=1767824940.9771397), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis/snapshots/ac03d065bb493bc9dd7a77460fdaf6f954968b0b/dto/binding_repo_dataset=callingcards-annotated_features/perturbation_repo_dataset=Hackett_2020-hackett_2020/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis/blobs/fd88d84c5e45f81dd6432d5e5a6f9056dd38cfb03c162442d1d5928151bcb2aa'), size_on_disk=100988, blob_last_accessed=1767824941.5591376, blob_last_modified=1767824941.0061395), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis/snapshots/ac03d065bb493bc9dd7a77460fdaf6f954968b0b/dto/binding_repo_dataset=callingcards-annotated_features/perturbation_repo_dataset=kemmeren_2014-kemmeren_2014/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis/blobs/8454d4bba35420099ec175dd81eeaacec4b1ec43adf8110b278974716c46c25c'), size_on_disk=19150, blob_last_accessed=1767824941.5591376, blob_last_modified=1767824940.9881396), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis/snapshots/ac03d065bb493bc9dd7a77460fdaf6f954968b0b/dto/binding_repo_dataset=harbison_2004-harbison_2004/perturbation_repo_dataset=kemmeren_2014-kemmeren_2014/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis/blobs/cbacede910cd43bf24ffdde99be52f8ec211c9b54ed1f0865034b67101ffcda7'), size_on_disk=10728, blob_last_accessed=1767824941.5591376, blob_last_modified=1767824940.9741397), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis/snapshots/ac03d065bb493bc9dd7a77460fdaf6f954968b0b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis/blobs/5c68356d43f7f19567f17bea53a021de93e97e3f'), size_on_disk=3660, blob_last_accessed=1767808305.8494713, blob_last_modified=1767808305.8474715), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis/snapshots/ac03d065bb493bc9dd7a77460fdaf6f954968b0b/dto/binding_repo_dataset=callingcards-annotated_features/perturbation_repo_dataset=hu_2007_reimand_2010-hu_2007_reimand_2010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_comparative_analysis/blobs/872b0b76a4eaa911c4319fc5103864b7f3d22ec5b25faeb551694985f69a8697'), size_on_disk=6106, blob_last_accessed=1767824941.5591376, blob_last_modified=1767824940.9561398)}), refs=frozenset({'main'}), last_modified=1767824941.5531375)}), last_accessed=1767824941.5591376, last_modified=1767824941.5531375), CachedRepoInfo(repo_id='BrentLab/yeast_genome_resources', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources'), size_on_disk=114475, nb_files=7, revisions=frozenset({CachedRevisionInfo(commit_hash='15fdb72f8c31ae58f3e3ce7c90be279383743a2f', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/15fdb72f8c31ae58f3e3ce7c90be279383743a2f'), size_on_disk=88406, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/15fdb72f8c31ae58f3e3ce7c90be279383743a2f/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/645da8028fd6abe86ee305f76eab78cdf29be364'), size_on_disk=6130, blob_last_accessed=1755819093.2316637, blob_last_modified=1755819093.2306638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/15fdb72f8c31ae58f3e3ce7c90be279383743a2f/features/chr=chrII/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/d64638adb2290412a962abffe0fbd0546ff139b29064bbf313b0a768449454a5'), size_on_disk=82276, blob_last_accessed=1755812630.656903, blob_last_modified=1755812631.0999012)}), refs=frozenset(), last_modified=1755819093.2306638), CachedRevisionInfo(commit_hash='42beb28478e27c5d4c5bb2308b12100e6489ef07', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/42beb28478e27c5d4c5bb2308b12100e6489ef07'), size_on_disk=6125, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/42beb28478e27c5d4c5bb2308b12100e6489ef07/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/ab6cc475062a2767a41b2aaa006faf4d9d2d60d6'), size_on_disk=6125, blob_last_accessed=1759787821.8450553, blob_last_modified=1758155946.5549896)}), refs=frozenset({'main'}), last_modified=1758155946.5549896), CachedRevisionInfo(commit_hash='25b47a191e40728efc2437d906e84317f922b36b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/25b47a191e40728efc2437d906e84317f922b36b'), size_on_disk=5426, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/25b47a191e40728efc2437d906e84317f922b36b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/9cdf7b342b249f25f2c2cbe46a2e0a26ded4f692'), size_on_disk=5426, blob_last_accessed=1755815405.3308983, blob_last_modified=1755815405.3298984)}), refs=frozenset(), last_modified=1755815405.3298984), CachedRevisionInfo(commit_hash='7441b9a8c858332e730e7ddb9d255460ae698626', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/7441b9a8c858332e730e7ddb9d255460ae698626'), size_on_disk=87714, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/7441b9a8c858332e730e7ddb9d255460ae698626/features/chr=chrII/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/d64638adb2290412a962abffe0fbd0546ff139b29064bbf313b0a768449454a5'), size_on_disk=82276, blob_last_accessed=1755812630.656903, blob_last_modified=1755812631.0999012), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/7441b9a8c858332e730e7ddb9d255460ae698626/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/e63c2087b2e56dc15e8ac6cf5693c2391127b732'), size_on_disk=5438, blob_last_accessed=1755816785.70087, blob_last_modified=1755816785.6988702)}), refs=frozenset(), last_modified=1755816785.6988702), CachedRevisionInfo(commit_hash='a5ecb243ba27ca4b4d6ad1b60cc160c033ca2be6', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/a5ecb243ba27ca4b4d6ad1b60cc160c033ca2be6'), size_on_disk=82276, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/a5ecb243ba27ca4b4d6ad1b60cc160c033ca2be6/features/chr=chrII/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/d64638adb2290412a962abffe0fbd0546ff139b29064bbf313b0a768449454a5'), size_on_disk=82276, blob_last_accessed=1755812630.656903, blob_last_modified=1755812631.0999012)}), refs=frozenset(), last_modified=1755812631.0999012), CachedRevisionInfo(commit_hash='aae6e5ea8139e23940d11b4d7e77684c78bb7f6b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/aae6e5ea8139e23940d11b4d7e77684c78bb7f6b'), size_on_disk=4585, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/aae6e5ea8139e23940d11b4d7e77684c78bb7f6b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/4320f8f18620d988a5fc32872a671ace406fd0a2'), size_on_disk=4585, blob_last_accessed=1755814342.1923234, blob_last_modified=1755814342.1913235)}), refs=frozenset(), last_modified=1755814342.1913235), CachedRevisionInfo(commit_hash='6607f7be76546563db0a68a9365c0a858bb5f3a1', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/6607f7be76546563db0a68a9365c0a858bb5f3a1'), size_on_disk=4495, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/snapshots/6607f7be76546563db0a68a9365c0a858bb5f3a1/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_genome_resources/blobs/a31c4647c502767c22b5fe725ec8de58686a02d4'), size_on_disk=4495, blob_last_accessed=1755813454.8008156, blob_last_modified=1755813454.7998157)}), refs=frozenset(), last_modified=1755813454.7998157)}), last_accessed=1759787821.8450553, last_modified=1758155946.5549896), CachedRepoInfo(repo_id='BrentLab/barkai_compendium', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium'), size_on_disk=3579068970, nb_files=504, revisions=frozenset({CachedRevisionInfo(commit_hash='a987ef37c72fcd07b18828d320bc4305480daade', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade'), size_on_disk=3579068970, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417941/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/30188c510d2d7041e1525e304edc8f25b569a1771cf00844cb27300a052441e8'), size_on_disk=7032333, blob_last_accessed=1756926363.2551925, blob_last_modified=1756926364.2891881), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417914/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d062dbf660e1ef240ff8ef99ff2d486d9caaabda14575e6365ce6f725fa91ccf'), size_on_disk=8599250, blob_last_accessed=1756926358.2422144, blob_last_modified=1756926360.3412054), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381017/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1d9b2dbb5079eca871182c2dce1d681b943f1362072c18e255a4bd6d1068b840'), size_on_disk=2436549, blob_last_accessed=1756926378.161128, blob_last_modified=1756926378.7931254), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381028/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8d40caa48e7b9d32d2a0548732ef91c01ede9d0f4f6d74f045817e818c52c808'), size_on_disk=18141950, blob_last_accessed=1756926378.9691246, blob_last_modified=1756926778.2687466), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417928/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/97e74e01fd641bfef68b6206a3c62ab41b5c28d8ea8369ce4198df1b3387b266'), size_on_disk=2449472, blob_last_accessed=1756926360.9072027, blob_last_modified=1756926361.3542008), CachedFileInfo(file_name='GSE178430_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE178430_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/be2c4af55a502d7fa22abb28afe6db347950327bde1bc7e97dbb68fdb0c59f3d'), size_on_disk=9398, blob_last_accessed=1756926861.9192877, blob_last_modified=1756926300.5344653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417837/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/04d099809ca78bd87dcb9921398681ac64e827439049a37e97a71ba0828debfb'), size_on_disk=8348990, blob_last_accessed=1756926344.4012744, blob_last_modified=1756926345.6152692), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417625/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/96b04f28e74e25bd1502a4ea9aca8047bfa8462e61cced0d726af4a336e1a585'), size_on_disk=17384720, blob_last_accessed=1756926304.4014485, blob_last_modified=1756926306.2074406), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417735/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/634551506800bc76183ce179c8c8f324cb6f0f577f6cf0b629b74ce4f1e995d5'), size_on_disk=19171195, blob_last_accessed=1756926326.0393543, blob_last_modified=1756926329.4873393), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417627/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4027ab6ecd21364ca365bbe7192797ed924c817e6edc841d750111b2e3f6f45a'), size_on_disk=21089243, blob_last_accessed=1756926305.0144458, blob_last_modified=1756926309.6264257), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417943/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0e4191a403f5070636bcb281bd80d39e11c70fb502fa2b18150032d58f2e0740'), size_on_disk=5396199, blob_last_accessed=1756926363.7271905, blob_last_modified=1756926364.542187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417673/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/535d074f70f4553e24f3171fe5cfbd1821635ce7ca6ef93f8665876de620d6c9'), size_on_disk=6559693, blob_last_accessed=1756926315.1844015, blob_last_modified=1756926317.0733933), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417720/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/961772a081bde1795af5caea9d7562e2b1f889981bd3c3f1c792ad9093a758d7'), size_on_disk=659095, blob_last_accessed=1756926324.1233625, blob_last_modified=1756926324.6243603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417698/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d240b652ebca64784e037cc8ffbda424e2c92f368c5bb9c730930f48f29facc2'), size_on_disk=1855826, blob_last_accessed=1756926321.2993748, blob_last_modified=1756926322.0703714), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417777/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bd953fd8a3ee2a5c90382c1c9adc8091fbca801aa87f07a409b6dd5a9704421f'), size_on_disk=1267333, blob_last_accessed=1756926332.9623241, blob_last_modified=1756926333.6353211), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381019/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3023e878b223b00b77d3411a6d20195b85aa7e61a8445fe725ff694c77816053'), size_on_disk=1022843, blob_last_accessed=1756926378.3191273, blob_last_modified=1756926378.864125), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417844/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ae034bb207abc0c649fb60aae78e798d46647f53b4e0352c6510fb7c8339a234'), size_on_disk=5408445, blob_last_accessed=1756926345.5212696, blob_last_modified=1756926346.4342656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417835/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6245fc8d220aad556115d4b831f72ae6a9beac48ed6e2aba811284aa0ccab4f7'), size_on_disk=7888215, blob_last_accessed=1756926343.795277, blob_last_modified=1756926344.942272), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381023/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/22e3e0f4cd1f2c755ec1ac13b99d2d6c98d0158319478f2deca4e89aefc5e88e'), size_on_disk=3860195, blob_last_accessed=1756926378.5651264, blob_last_modified=1756926379.6101217), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417757/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6c9c5dbd0513759ace8599b2542a1f99fe0c30feb318665ebdb55bfb111c8c67'), size_on_disk=5834465, blob_last_accessed=1756926330.011337, blob_last_modified=1756926330.7623336), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417778/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/54e8ea03e214967eb4a1c7b50a086c4d7083d2c9c95dd0419e545fb0e8f18422'), size_on_disk=9345257, blob_last_accessed=1756926333.1513233, blob_last_modified=1756926334.0903192), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417647/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8c01c928abe00b5afa6a2292820695797391b3671cc110b9026f5fc5cb8564ae'), size_on_disk=2891716, blob_last_accessed=1756926310.1614234, blob_last_modified=1756926310.93942), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381060/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1bb05aa71fe55db65fc3868b131854c1e6666dca8cc370874f517aa0756d21ac'), size_on_disk=9137992, blob_last_accessed=1756926778.9637427, blob_last_modified=1756926780.2307358), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417688/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f21ccaca0f6782b6ce2b70b50c9dc93487b6aaec03db6c30d9cb0c9fc01dda42'), size_on_disk=16023102, blob_last_accessed=1756926318.5463867, blob_last_modified=1756926320.0283804), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380972/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/91a8106ca7ac2babcabd1549072b2b730909e1386f827e732428bff2bb5251cb'), size_on_disk=6712599, blob_last_accessed=1756926371.1721582, blob_last_modified=1756926372.0711544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381041/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a53caa1961d9245d82db10989b8a8d601760ccf26f67666d5558937830c51e31'), size_on_disk=6621865, blob_last_accessed=1756926774.784766, blob_last_modified=1756926775.9747593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380925/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdf190d30101829153acfd62edd4d689ac65a7d737f0e80978c7d460819b5caa'), size_on_disk=9220100, blob_last_accessed=1756926364.6101868, blob_last_modified=1756926366.1331801), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417945/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6689ba375c993d00d1f0e716d3bc39604d68df654a67b2518c56f4d54c7f4ca6'), size_on_disk=11289854, blob_last_accessed=1756926364.0191894, blob_last_modified=1756926364.9011855), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381016/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0e597280417f8f8b5b476bc9d66033afc9412759f5e2f9bd9f3367932cc6f03f'), size_on_disk=820782, blob_last_accessed=1756926377.9791288, blob_last_modified=1756926378.4041271), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417748/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/db4c67c001a1f688f70918f0e25d3ddd996392b33b9f1144a14551bc0026d036'), size_on_disk=3246481, blob_last_accessed=1756926328.8123422, blob_last_modified=1756926329.6943383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380949/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/65090ef93158269c98bc2e747e505cea9d460f0959d0b2957e58543cd1944163'), size_on_disk=2110872, blob_last_accessed=1756926368.705169, blob_last_modified=1756926369.142167), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417636/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4a3b2c7076107dcc47a87179cecab9e2051350c94e951a7aadc2ff0cf6fdbaca'), size_on_disk=43479381, blob_last_accessed=1756926307.6494343, blob_last_modified=1756926311.4484177), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417747/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7011e7ff4b64ca9c57bdab93cfcfc52938e7b0574b48417feb4f6185f6c118b9'), size_on_disk=4791771, blob_last_accessed=1756926328.7593424, blob_last_modified=1756926329.9543371), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381064/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8569620c4d2efb8dd163aa69d8edf8f855b96eba637e754a126a88e6f0c73192'), size_on_disk=20598404, blob_last_accessed=1756926779.800738, blob_last_modified=1756926781.1587305), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417870/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ca1bc9825b47ee9ac59e53bc6ada3b1a9e8aba1b57c57c57947afae7926054b'), size_on_disk=10710319, blob_last_accessed=1756926349.7052515, blob_last_modified=1756926350.7552469), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417710/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9837ecd3769eaaf407d939ff09d6ae74d832630b27353251942bd262cd70fc35'), size_on_disk=330457, blob_last_accessed=1756926323.098367, blob_last_modified=1756926323.797364), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417658/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b6cf4c024743be14fcd5730f0ddbb960dbcd83d07adf8668639f9a1cdf195368'), size_on_disk=8754108, blob_last_accessed=1756926311.5764172, blob_last_modified=1756926313.1134105), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417663/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cc7010c3ed70c3083d49d88c0f95dbe8f6eff873d4b5848eeae06a22df71a479'), size_on_disk=8938116, blob_last_accessed=1756926313.18441, blob_last_modified=1756926314.5674043), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417640/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/411d9146359847b5805450f67cbde4f1cbd51e3c2fae4bbcd6295db7426cb2ca'), size_on_disk=7000528, blob_last_accessed=1756926308.9124289, blob_last_modified=1756926310.0344238), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417779/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/918376b5cb0b63e3e531ac990854e5a7b461e7251f5ed562c5a92cbf6a5a0141'), size_on_disk=9459105, blob_last_accessed=1756926333.1593232, blob_last_modified=1756926334.2033186), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417946/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef43c84affe53bbdefcd70cb97a4a4d1e483824b8b6f91e3360a11094609f815'), size_on_disk=4853893, blob_last_accessed=1756926364.0141892, blob_last_modified=1756926365.0591848), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4c4a265f9e3e8d373550ea58ca1cde6094c823f090336974fde3e7cee117fd99'), size_on_disk=1770007, blob_last_accessed=1756926376.6771345, blob_last_modified=1756926377.1011326), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417869/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8dd08ab28007f90a664a6fbde23298ae2bb0ded917d5d75b1275c41e3eb5f7dd'), size_on_disk=16604892, blob_last_accessed=1756926349.6872516, blob_last_modified=1756926352.7202382), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417933/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4fe347d88754646d39d93dd8a78e857d04d1903f7163a21495038b087d5f6b8a'), size_on_disk=12389872, blob_last_accessed=1756926361.7481992, blob_last_modified=1756926362.9061942), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417881/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bf380975f31e362e8964000688bff364809bbe2e8e3efa9ceaa71253ca6beefb'), size_on_disk=8717095, blob_last_accessed=1756926351.848242, blob_last_modified=1756926352.9732373), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381063/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1e4361a7d20f8e92e8efc238365e0ed06eb021243864321c93d4638fa21634c4'), size_on_disk=10916323, blob_last_accessed=1756926779.3497405, blob_last_modified=1756926780.685733), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417632/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/23c561fde2e6c2973ec1913373c343a5c49d6cc0dbb44ff21eb5aff87cd77d3a'), size_on_disk=18481828, blob_last_accessed=1756926306.2764404, blob_last_modified=1756926308.5744302), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381020/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c85f953439e9f6918c7f7c8194bcbbebe0ef8a96070119813025a42bde8c08fe'), size_on_disk=897644, blob_last_accessed=1756926378.3451273, blob_last_modified=1756926378.7061257), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417732/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f99cdb10c12a75e1d020125d68b5c5302263eaef050b5c6b0032e27a0ac9a23c'), size_on_disk=685742, blob_last_accessed=1756926325.7463555, blob_last_modified=1756926326.326353), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380958/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ef3849004b6c72205ea1b298cf6a586f1114061ca94925229eda191d4666885'), size_on_disk=3288900, blob_last_accessed=1756926369.617165, blob_last_modified=1756926370.303162), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417736/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/671a67d7275810840cafc9db32e43385c4ba61b007d702e7eb5cb244a0e4afa9'), size_on_disk=13855124, blob_last_accessed=1756926326.1693537, blob_last_modified=1756926327.261349), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381013/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c831ca7cc9cfd2cc2ccd92a0168b2e14dedf2bde9e29780dfaf6d281b31e9462'), size_on_disk=4648281, blob_last_accessed=1756926377.4421313, blob_last_modified=1756926378.3451273), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417637/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ca1a954c7b2aca56529daf9a89249ad4e6ec2373e29ac7c19d2af1533afc4263'), size_on_disk=2776397, blob_last_accessed=1756926307.8804333, blob_last_modified=1756926309.1794276), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417898/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ced2c80d4f9d2aa28260b885a20b1c90b41d8eb54ee92f418ed592b97b954a9e'), size_on_disk=6422129, blob_last_accessed=1756926355.1262279, blob_last_modified=1756926356.0962236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417786/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdd4770c9b3e18e86f9a46ff924e3a562e37b632d6f0e8ad11627956a3416e09'), size_on_disk=11354740, blob_last_accessed=1756926334.3243182, blob_last_modified=1756926335.4083135), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417680/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aa9e972e7427b1db5556008756651933c8792891339607dee2ee231733072d1c'), size_on_disk=18443288, blob_last_accessed=1756926316.5853953, blob_last_modified=1756926321.4263742), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380988/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/27b6e71986cf2b7da20b64dbda0ba47e08753f7184ddd57ed288f11df88d21e1'), size_on_disk=16063760, blob_last_accessed=1756926373.7421472, blob_last_modified=1756926374.932142), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417784/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fd3dd53cf34f48c235e4f771378f42a5605446bbc62d108a5637a042513fd75d'), size_on_disk=4826642, blob_last_accessed=1756926333.7233207, blob_last_modified=1756926334.576317), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417708/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b58038866c334daaf757a086f99c3a5c5962b8563769ee0a3205bcdcbb0144ec'), size_on_disk=250863, blob_last_accessed=1756926323.0003674, blob_last_modified=1756926323.366366), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417893/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f3b9f2c711cb806350320b93dba528efd6433d0ca10c39e5e2dd6aa89a408bd'), size_on_disk=9896637, blob_last_accessed=1756926354.2372317, blob_last_modified=1756926355.535226), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417766/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/39da4f16b82cc0b5019d8526f2e3eed49de9760e319f8e839db621bf4c1ab021'), size_on_disk=6482957, blob_last_accessed=1756926331.2123318, blob_last_modified=1756926332.1133277), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417761/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ba86b2041414034d38b95ccbf3c65957642c56b56914240999986ebc76b9e193'), size_on_disk=1635118, blob_last_accessed=1756926330.478335, blob_last_modified=1756926331.0933323), CachedFileInfo(file_name='GSE178430_metadata.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE178430_metadata.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9673758f13ec096ec4a89642bbe34d60975f5f05'), size_on_disk=61, blob_last_accessed=1756926300.2374666, blob_last_modified=1756926300.2994664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417775/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2ca9ba5e1394191206945f41168d555519fe3963d4c39f251c1ead57f2a323f4'), size_on_disk=1059891, blob_last_accessed=1756926332.3723266, blob_last_modified=1756926332.773325), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417612/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ee40f7af39e729ee79ef18d6777bbd632b7cc7811041a32d54f4a02b42638464'), size_on_disk=3509045, blob_last_accessed=1756926301.5874608, blob_last_modified=1756926302.6274562), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417715/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ac5effc2ec07a8fb49bac4d77b7d4129a18ae2180807874ea02d1c252a3b68bd'), size_on_disk=520286, blob_last_accessed=1756926323.8693635, blob_last_modified=1756926324.477361), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417641/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/355d8669b1d4c5576d8aa900582800fdf1819360f0db91d5e0299342b49884fb'), size_on_disk=6322689, blob_last_accessed=1756926309.3414268, blob_last_modified=1756926310.7974205), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417740/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e91586589c967e7bcbd521989874b21c024da79365d0d9916ffc66b774043e01'), size_on_disk=11875677, blob_last_accessed=1756926326.8903506, blob_last_modified=1756926327.6843472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380970/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bdec4a51127eaf09abb5d25d7f3a732afc3b561105fd18ba1df44d04e6dd90a2'), size_on_disk=3545790, blob_last_accessed=1756926370.9301593, blob_last_modified=1756926371.5451567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417852/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/43431a444b2761ed9b6fa4c7e6edfc8da31b64ff933408c517bc771e9f3e94dc'), size_on_disk=6967416, blob_last_accessed=1756926346.8272638, blob_last_modified=1756926347.8462594), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380999/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/acb2ee4711dab5800afb7ccbea72c8c80268b0a848723e7bd8ecf197decb982d'), size_on_disk=2605563, blob_last_accessed=1756926375.2281408, blob_last_modified=1756926376.1321368), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417723/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f397cef2d9c491fb034826727c63b3c224f6ed791c9b4dcf884c63c55c9961c'), size_on_disk=2800893, blob_last_accessed=1756926324.70836, blob_last_modified=1756926325.4453568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381031/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b4705ac4577c8e7b116d1a1b677967f69412b0e4716e0de7fa7ee97bc9be4d74'), size_on_disk=4551217, blob_last_accessed=1756926379.2931232, blob_last_modified=1756926776.2707577), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/225bf60f7c9796682210dc48d07a30d3dabab8ebc13abb7a84834279fa131020'), size_on_disk=5307353, blob_last_accessed=1756926376.2401364, blob_last_modified=1756926377.3751316), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417697/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/198a3cd31740f347f0308db2914df4fc0fc303b4a9f040fbaf79fb5ca0170e7f'), size_on_disk=5521438, blob_last_accessed=1756926321.4153743, blob_last_modified=1756926322.4743698), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417892/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7ac6b85018422c74c894467b7b53b1c2dc7c5298fc31654bdea5ca70493e0a87'), size_on_disk=8194725, blob_last_accessed=1756926354.176232, blob_last_modified=1756926354.9712286), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380976/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d27d3efdccd104ca208d69fe9d94fe9011cb6212b34739679b1c8f550c5e9df2'), size_on_disk=12889551, blob_last_accessed=1756926371.6141565, blob_last_modified=1756926372.8021512), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417772/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a21b204a2da7654dd5bf4e7965a3c1e19159c16052232030787175a830ff0233'), size_on_disk=8204405, blob_last_accessed=1756926332.1323278, blob_last_modified=1756926333.453322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417915/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9fbfc50c3e1f756169d25442880ba33e5cf237e3c83a0d674b64ace47e8d3322'), size_on_disk=13801295, blob_last_accessed=1756926358.318214, blob_last_modified=1756926359.4592092), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417861/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef65c45e0e86b7d04e274c39408b332cc94879c0804f16acf7c537b14bb6498c'), size_on_disk=10436399, blob_last_accessed=1756926348.3832572, blob_last_modified=1756926349.6192517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417841/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cb829be078dd8b533a90c2c548dfb2ce0455026be54c36572337e0a1ddeeb2cf'), size_on_disk=5791736, blob_last_accessed=1756926345.138271, blob_last_modified=1756926345.9902675), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417742/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1a87c492aab479dd0c013fbe80e006f18ba80de751da600b95babcb3f6e88dad'), size_on_disk=2054520, blob_last_accessed=1756926327.3293486, blob_last_modified=1756926328.4213438), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381045/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdc8f9e6447280a7fc713c2a8f2157eb9076a5c01a7eb8437176e9ee30d6d4ab'), size_on_disk=4939421, blob_last_accessed=1756926776.3437574, blob_last_modified=1756926777.2507522), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417849/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/23b6606ffcf116cf066b103bf1b6b4fb33c40e544e20e4565fa44c5bbcea4bdd'), size_on_disk=5689527, blob_last_accessed=1756926346.584265, blob_last_modified=1756926347.3892615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380961/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fc4356aaa2a30217145b4aecc4f3fdd2575fcd696a2603fea3d8112998f770c7'), size_on_disk=798599, blob_last_accessed=1756926370.1791627, blob_last_modified=1756926370.589161), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417793/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8a51bd4d953e697022a85f9b527afd45126988ba4bbb796d71c9e5f6c8fffc44'), size_on_disk=10797388, blob_last_accessed=1756926335.4883132, blob_last_modified=1756926336.7013078), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417620/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b973f1a83f111c4e114a40d3731ff24779cafcb7b71523fe8571cf2ed849b316'), size_on_disk=20780203, blob_last_accessed=1756926302.5494566, blob_last_modified=1756926306.7334383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381029/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6431cec7b7404a0773d8715761e2b161f01ebd8471950e71e17dad8976d83f3c'), size_on_disk=7229691, blob_last_accessed=1756926379.0071244, blob_last_modified=1756926776.565756), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417902/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2d72fc510cd348af7611ca170b7804b228b74bd38e7ff5e437375c1cc926fa95'), size_on_disk=8952511, blob_last_accessed=1756926355.9322243, blob_last_modified=1756926357.2422187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380973/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/343eb57057b5af866386fb4c02d0c41b888e206f5473ed7b4041200cfb93dbd5'), size_on_disk=1511721, blob_last_accessed=1756926371.3611574, blob_last_modified=1756926372.4931526), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417739/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1c9e3fcff1dc467673efe70177cb4d678d7eff7b5b2bb3ded247d7a458b466dc'), size_on_disk=21974051, blob_last_accessed=1756926326.8623507, blob_last_modified=1756926328.7393425), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381038/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/415f62b8333a02df8a87ba85e1d0de30a6cc9fdb07f7c8dc02d30f81ed4c14ef'), size_on_disk=2424198, blob_last_accessed=1756926379.8751206, blob_last_modified=1756926380.4921181), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417654/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/67d658902a2b91d0d36345be06dc00c85af1b88ba40c37deaa091705d97b3ffc'), size_on_disk=6323092, blob_last_accessed=1756926311.166419, blob_last_modified=1756926314.4344046), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417614/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dac699f09872b0c58b742c692b341f0b00b7d83284cec5ef838c276a6f48908c'), size_on_disk=1759475, blob_last_accessed=1756926301.7274601, blob_last_modified=1756926302.4774568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380944/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7331bfc92dc902a721083ac4ea027481c90ce204d7dca7c493fec8f284db0155'), size_on_disk=8432479, blob_last_accessed=1756926367.299175, blob_last_modified=1756926368.6141694), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417896/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/71e393229e9071bad31abe5dc3d41bd5d719a5dcb68077827861b4b4e9a49a85'), size_on_disk=6055777, blob_last_accessed=1756926354.7882292, blob_last_modified=1756926355.8442247), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417704/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/477a83e4a5eace957a036c5ec65ef4539442c078058c1a44a877763cb850c86d'), size_on_disk=459034, blob_last_accessed=1756926322.1393712, blob_last_modified=1756926322.8993678), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417764/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8a30e54d96d5b83981e37a0d8f6cae9e6584819a76a200f2730d9de32e282d02'), size_on_disk=2641764, blob_last_accessed=1756926331.099332, blob_last_modified=1756926332.088328), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417634/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/529d984782684991b2eb7be2d345b5a4e8a6e08d3dbe30ad34495d8380464e6e'), size_on_disk=16306189, blob_last_accessed=1756926306.810438, blob_last_modified=1756926310.239423), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417642/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/591f688d0d9f98350a1d636e6f93ed37563f3ea9765ca6b633490533b0cf32d4'), size_on_disk=11498542, blob_last_accessed=1756926309.2804272, blob_last_modified=1756926310.8714201), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417781/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9dd7989f90f8762246868ccd7d11c9ce876b2b33099efc3a4c5bf1bc7cbcca80'), size_on_disk=6923944, blob_last_accessed=1756926333.4783218, blob_last_modified=1756926335.3743136), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417884/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ff7d3efa4a231f5130be299d8d0ff67fe4973856e4db18e8a6c53d47ba6b28a7'), size_on_disk=11684836, blob_last_accessed=1756926352.791238, blob_last_modified=1756926354.0932324), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417646/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2b3d78fc052de8647b1ba6e83245b5f375b4e6c10952feff795855160458a1d5'), size_on_disk=396843, blob_last_accessed=1756926310.1544235, blob_last_modified=1756926311.0694194), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417699/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0c64ecbe6b245299fe04c04239d792fbae0f1b288562fe0c7912a24de56f0ad5'), size_on_disk=5707915, blob_last_accessed=1756926321.6203735, blob_last_modified=1756926322.8483682), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417606/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f76c81d98337c9e127bf5516af0dd135458fd622c01f2b5c9fd85ce6edfa4c7f'), size_on_disk=9502783, blob_last_accessed=1756926300.5794652, blob_last_modified=1756926301.6394606), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380987/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2eb045d9f88b952eed8f8354e77c682bc3003bd2e9eacb0eb0c2df5ca44a34cc'), size_on_disk=4463796, blob_last_accessed=1756926373.559148, blob_last_modified=1756926374.4241443), CachedFileInfo(file_name='GSE222268_metadata.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE222268_metadata.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4459325dc2deeb9f00d803b89cd8fdc823ed383f'), size_on_disk=61, blob_last_accessed=1756926300.2714665, blob_last_modified=1756926300.3444662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417621/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/354c654ace9bc123bcd9009c56af518b98f1a547a7ccd79f348d0e533607a41e'), size_on_disk=20457307, blob_last_accessed=1756926302.5744565, blob_last_modified=1756926307.7874336), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417613/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/695d3095619f3a313d25c506b9d0493f171896f1439bcf3bd1cd932d1f6fed5d'), size_on_disk=2812565, blob_last_accessed=1756926301.6434605, blob_last_modified=1756926302.4814568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bd149efcdaa899981b965dc424d16fb66668a7666a059c6bc5043572331394c2'), size_on_disk=4513644, blob_last_accessed=1756926377.1751323, blob_last_modified=1756926378.1161282), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417714/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d6abeb724a8c8b23217f3465284564da5baffee59ee4e3af582e0a3705ae32c6'), size_on_disk=262443, blob_last_accessed=1756926323.756364, blob_last_modified=1756926324.256362), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417652/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d38c028233981eb275c44995eafa73172388a9e0e704fe278b0cedbab49d429f'), size_on_disk=12947754, blob_last_accessed=1756926311.0344195, blob_last_modified=1756926312.1414146), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381051/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ba017d941936d755013757711f5de4661acd187ef79524662d85485ba6588f34'), size_on_disk=8199472, blob_last_accessed=1756926777.322752, blob_last_modified=1756926778.4757454), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417905/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b9d291469b7a414bab157f45bfd3a1f7ffaaf931de25fec9eb18012a18b70939'), size_on_disk=4418829, blob_last_accessed=1756926356.1612234, blob_last_modified=1756926357.2652185), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417655/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/25d98ba17be9b354b0ec7a0e513a6ff9d4593dd1cadf269f28bcea1ca9c71565'), size_on_disk=5461224, blob_last_accessed=1756926311.5504172, blob_last_modified=1756926312.9474113), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381068/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/16e4412527449a3cb9d2da129c3309a71b2ec9b33a7a39cb9bb31d41ce5d2e04'), size_on_disk=14909067, blob_last_accessed=1756926780.5687337, blob_last_modified=1756926782.3187242), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417927/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8e639025209cff8d34b7e35a5fa371282bb725173225097a39f8f2101a49deed'), size_on_disk=7241057, blob_last_accessed=1756926360.6762037, blob_last_modified=1756926362.1171975), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381037/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5cef560daffa3a5b1e841a312937eca595ced0bbdb3e6c641b13359a0340fea1'), size_on_disk=2090047, blob_last_accessed=1756926379.8821206, blob_last_modified=1756926380.4171183), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417737/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2e1d5b2306dd24257d4ba261baa42b324c9f4f8ca3a1d30e0f920c4f4a5f99ba'), size_on_disk=1342511, blob_last_accessed=1756926326.2713532, blob_last_modified=1756926326.9043505), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417882/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ac2bd9710196abc4469d4d78ccbde6ddc67e9ef39ade7d50c32b0d951866e653'), size_on_disk=10076041, blob_last_accessed=1756926352.116241, blob_last_modified=1756926353.223236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417798/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0ac62fa7573f4b3c756fd9c280be4b97ec0c7c756e8c76307d4fab56d29e1c08'), size_on_disk=10509245, blob_last_accessed=1756926336.5193086, blob_last_modified=1756926337.800303), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417926/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/303d44a38fbb646cf211ce1bd03fe2d24bb004f385c63d05ab8e707c5b283f0c'), size_on_disk=10766909, blob_last_accessed=1756926360.6072042, blob_last_modified=1756926361.6751995), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417838/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0dcd4dc268902c8e0845a1ee81938a81a00412014d5b198a41a523cc3450dfc4'), size_on_disk=10178872, blob_last_accessed=1756926344.5452738, blob_last_modified=1756926345.3902702), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417879/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6714b3f5155dd540962a7541625f45e14e6a93b4c01e913790b70818e50c821b'), size_on_disk=8601872, blob_last_accessed=1756926351.4612439, blob_last_modified=1756926352.8182378), CachedFileInfo(file_name='GSE209631_metadata.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE209631_metadata.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fe31b19357a717d3d51d1d201620d37fca79b1c8'), size_on_disk=61, blob_last_accessed=1756926300.2214668, blob_last_modified=1756926300.2814665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417773/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d7980069a740f581dbcfd9416372452203d636d8ad4c9352c3b2d6483cd27379'), size_on_disk=5178664, blob_last_accessed=1756926332.1683276, blob_last_modified=1756926333.411322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380984/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a15ab5e5a0ead07dc2a530a1e4abb714027a95ce090c7a0eda5ec2bb714d903f'), size_on_disk=8070704, blob_last_accessed=1756926373.2251494, blob_last_modified=1756926374.1601455), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417794/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f15be566c029c52cf46f6258e589529afb3a002f010710a54d04dd6151f8b684'), size_on_disk=10205023, blob_last_accessed=1756926335.9063113, blob_last_modified=1756926337.151306), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417851/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2d8a571fae50598143744a1f13d40cec2ea8c2198e87aae17f96d7c8a00460d0'), size_on_disk=11051527, blob_last_accessed=1756926346.6472647, blob_last_modified=1756926347.6582603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417635/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d2d1c241467bd5c55cf059f793b4236c99d48e59bec853d1052ff6c644e50d1b'), size_on_disk=3044373, blob_last_accessed=1756926307.2014363, blob_last_modified=1756926308.391431), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417676/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/53c35af59283469a22148b2841cb8574faf80d59162a33d269560283c9228af2'), size_on_disk=12006227, blob_last_accessed=1756926315.8083987, blob_last_modified=1756926316.8563943), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381024/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5182750a0c8af8253cc98ecad8715519463d5c18adbe067488dfef91198c0d80'), size_on_disk=603678, blob_last_accessed=1756926378.7151258, blob_last_modified=1756926379.128124), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381043/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f6ded8e6496dcfdd499edc382d19da2071f25daebbb250b375b9a2706e9d8211'), size_on_disk=9139336, blob_last_accessed=1756926775.6057615, blob_last_modified=1756926777.1057532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417782/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d6624acaa7c4ec21ae02d6d1322b8669a148308095e714b41bff868483fe2fa9'), size_on_disk=11742567, blob_last_accessed=1756926333.5273216, blob_last_modified=1756926334.824316), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417724/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e5beee12039dfe6b4dae180a31ac0897dea725754554db43e2df9f99da94db58'), size_on_disk=4535402, blob_last_accessed=1756926324.69836, blob_last_modified=1756926325.9703546), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381069/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aa8f6186814bf973f8b4448a0c4b692c31594e03905c75d5691b982d6ccde97d'), size_on_disk=13705106, blob_last_accessed=1756926780.5807338, blob_last_modified=1756926782.319724), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417756/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ede027f2c13a1e6669becf6194ace85af5800d5fe77db16ec47bfb3abaa735d3'), size_on_disk=8620421, blob_last_accessed=1756926329.968337, blob_last_modified=1756926331.0303326), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381014/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5c5106dfb24bcfb1436aeb87b8218df873417ca2809aed71ca58e9b48ffacd29'), size_on_disk=1425306, blob_last_accessed=1756926377.5811305, blob_last_modified=1756926378.2571278), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380962/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/72be01aa42db736ffac8236c23c16edea00bdf5da88c3114fc94d9eb3daa4ee9'), size_on_disk=1460258, blob_last_accessed=1756926370.2361624, blob_last_modified=1756926370.6811604), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fb5958ec4e10f5c7e19ebc55b24f26012e6d8ccf'), size_on_disk=8004, blob_last_accessed=1756926300.2594666, blob_last_modified=1756926300.3214662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417805/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/661f37ed294c22f6c3b02d5c1ea25794f5e33090c246185e891ee82153e2e5f2'), size_on_disk=3071166, blob_last_accessed=1756926337.8643029, blob_last_modified=1756926339.3292964), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417752/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/efc9f1ecf4a7b06b23da46fb8c7d1879b67a9d6603ed753d09e2028396ec05c5'), size_on_disk=3420764, blob_last_accessed=1756926329.26534, blob_last_modified=1756926330.0613368), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417917/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2118491b5ba6b4af1c0d394eec3139b0258cf3a4e42b4d1a02ab177247852081'), size_on_disk=14019020, blob_last_accessed=1756926358.4622135, blob_last_modified=1756926359.8632073), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417694/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/058b507f75cb01bb5912e14e7bbb4e25a1de945e578323bce9f920f31a9ced82'), size_on_disk=11980395, blob_last_accessed=1756926320.09838, blob_last_modified=1756926321.5623736), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417631/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b4b8c985528935ec46c6c7e78482af1ed26238137fc63b2196c6631e2d3796ab'), size_on_disk=27651513, blob_last_accessed=1756926306.2814403, blob_last_modified=1756926309.1994276), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380995/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e0cae334f1cb71be0be1e8ceb5fc6ad23d22e4db20cb64b1314c3d080f2bb5f7'), size_on_disk=6315860, blob_last_accessed=1756926374.931142, blob_last_modified=1756926375.7271385), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417909/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c5de46a74c16aee32dfb2be89a50dceaa5704c0c550076dcec483d1c21d983af'), size_on_disk=5969725, blob_last_accessed=1756926357.3362184, blob_last_modified=1756926358.5162132), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417936/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8ba89583b8c822b64968df9eca8273d12270429f0f820d6027f13c66504c383d'), size_on_disk=4552381, blob_last_accessed=1756926362.205197, blob_last_modified=1756926363.171193), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417944/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/01e0e606e4e8b2203e6497ffc8c231bce8672a43d83098ba838c8edfdb052c27'), size_on_disk=4563261, blob_last_accessed=1756926363.89319, blob_last_modified=1756926364.6951864), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417672/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f0741c46f1460c30d4d9b88ca1f7c0ca77a040cf25a34d82fab4c777a921ac26'), size_on_disk=18268758, blob_last_accessed=1756926314.9764023, blob_last_modified=1756926316.5143957), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f00bc431128183affc8964f9610f3e9486a043bcea5b31b3ebbcbd9b0070a448'), size_on_disk=6096918, blob_last_accessed=1756926377.3431315, blob_last_modified=1756926378.2571278), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417659/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/20f21bb1ba86fdbee1918776793b1ff2275be49933c72eaba90b8be8582f7692'), size_on_disk=15311232, blob_last_accessed=1756926312.2364142, blob_last_modified=1756926313.8474073), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417722/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f29055ed874a3a40735526808a6739884efecd8d7c5e7e9eb1fcaa8e495ade3'), size_on_disk=345818, blob_last_accessed=1756926324.5753605, blob_last_modified=1756926325.2483575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417759/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b2dad4cf4b03c846adf9d5229aeab340e58034c751bc68b37bb0c8402a7c071b'), size_on_disk=1372312, blob_last_accessed=1756926330.1243365, blob_last_modified=1756926331.1623318), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417609/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e3d8451242fe9ecf9f82e2ac1a406144840a6182178228e6e1fa67e6a04a318a'), size_on_disk=3176651, blob_last_accessed=1756926300.651465, blob_last_modified=1756926302.0864584), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417876/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0713329434bfbd55f1998400d9d44c1d8c7fec6cb1b646c0fdb926f1046f9b47'), size_on_disk=11246388, blob_last_accessed=1756926350.971246, blob_last_modified=1756926352.0472412), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417922/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fba17c65fa210922e57c533f5146b4237677149a5022fb836c1cd52dc89855b2'), size_on_disk=10194446, blob_last_accessed=1756926359.5812085, blob_last_modified=1756926360.610204), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380953/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5b3410430d33f3123b935982be90037f2d0a8e657f6b0b70a7042846d5ddd1b0'), size_on_disk=1192961, blob_last_accessed=1756926368.8401685, blob_last_modified=1756926369.627165), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380974/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ea9eb19e91fedadd9e46d5d7dc9169995065af5b6b8e823f1667117be9d737d0'), size_on_disk=8889742, blob_last_accessed=1756926371.5481567, blob_last_modified=1756926372.2801535), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380978/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a1d811dc3a4bfd44683ddf8bd0401a71d4941497c5cac8d8040a597fc5d26587'), size_on_disk=5902739, blob_last_accessed=1756926372.160154, blob_last_modified=1756926373.09715), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381065/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/99cc73bc4c0908da04c01c7c59eff995a478ffc023c31fb970c7bb9789b2d70b'), size_on_disk=14673323, blob_last_accessed=1756926780.0087368, blob_last_modified=1756926781.5847282), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417932/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f040d4f594fe69143a7c3f04f0498be2a30fb9b5e78d42ec464fee65efcc42dd'), size_on_disk=9231914, blob_last_accessed=1756926361.4262006, blob_last_modified=1756926362.5771956), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417623/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/562a43ff381a2ca177fb1cc3dbe47fc2cf7cade495bad511ca7ed75aefca7a98'), size_on_disk=19049552, blob_last_accessed=1756926302.696456, blob_last_modified=1756926305.4934437), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417815/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ee8a37cd03dfe154008718f2a0f7fd95b3fdf468f99b1fc73ca515433fa45930'), size_on_disk=6563669, blob_last_accessed=1756926339.6062953, blob_last_modified=1756926341.8062856), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417901/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/69432386b347f5b364c02d270b38891358fa2068465354d1ada84d5450ea5bf0'), size_on_disk=9484337, blob_last_accessed=1756926355.6292257, blob_last_modified=1756926357.0582194), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417921/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/05fb4ff0001b88b711eee9f29ae30642d940667f7166121c9782f464057880f5'), size_on_disk=7891276, blob_last_accessed=1756926359.3782094, blob_last_modified=1756926361.2442014), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417762/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c8b34d14be6dd44af03432d758f705cf81e86a46831c6deb4b0944248709630a'), size_on_disk=3024339, blob_last_accessed=1756926330.943333, blob_last_modified=1756926331.59933), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380966/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f27a802d18bf78781f3d168370e0c0e241a130d04a5871d7416442be3ce3e40f'), size_on_disk=4241927, blob_last_accessed=1756926370.3881617, blob_last_modified=1756926371.272158), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417685/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/32edfbf9226b7ef1060ebe3e6986a1803d60408ccda969528cf4a59c5785c722'), size_on_disk=16967633, blob_last_accessed=1756926317.9163895, blob_last_modified=1756926319.5263824), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417768/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8bb8e088eb5123049e418c868ab42d94fb48946f934a8313f4cf283e3bfa09ba'), size_on_disk=4124375, blob_last_accessed=1756926331.3153312, blob_last_modified=1756926332.2313273), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380934/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d264f2a91c87696f6a07f573f1d921c91e1d53e3afca59610ba4703b3683b617'), size_on_disk=4601537, blob_last_accessed=1756926366.1091802, blob_last_modified=1756926366.9191768), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417920/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/00a869703f03e3df3f0f51a7d93d52dd7eac5174ed2a1b752207460e71c7b41a'), size_on_disk=14880236, blob_last_accessed=1756926359.1242106, blob_last_modified=1756926360.409205), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380930/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/734e9bc8119c7a507e9cf84f9926b1f9d06ccc8b0062a9b4dc36631f6f1bc6d1'), size_on_disk=4604638, blob_last_accessed=1756926365.1301844, blob_last_modified=1756926365.9251812), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417745/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ab84f2e8e76311cfe45a4b525b239186e835cab37f1e5222fdc35c286064e5e'), size_on_disk=1270105, blob_last_accessed=1756926328.596343, blob_last_modified=1756926329.1233408), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417716/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cd98cf97346b62e4f1efcace598c9c273d9ad9b8d8692e35f50b11d46f7ed8a6'), size_on_disk=5100666, blob_last_accessed=1756926323.8513637, blob_last_modified=1756926324.8793592), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417820/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9d36f1c17151e3203186142abff734c0182377d5287b3f27d00917608501abb3'), size_on_disk=8116588, blob_last_accessed=1756926340.9562893, blob_last_modified=1756926342.0902843), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380939/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dc6382d9dba7855504a6c7fa2a4adc44f564e25ff5c54eedd082c3cbe0a520a0'), size_on_disk=9235430, blob_last_accessed=1756926366.630178, blob_last_modified=1756926367.5861738), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417774/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ad00f794fb29d2a609375d63b09170f396f055ae392cd05a31d5f8fb5ede7a33'), size_on_disk=2914273, blob_last_accessed=1756926332.1763275, blob_last_modified=1756926333.217323), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381070/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/32c8f4c4efdbe6e137add61b0e31c27ae6a9eb5e8b463e9e4410a27cc51a57d3'), size_on_disk=29864184, blob_last_accessed=1756926780.7487328, blob_last_modified=1756926783.3167186), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417949/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6123bc7bd312e6e37fabadee561c7dacad32d26bada92c9785c783f94fc50a37'), size_on_disk=1320707, blob_last_accessed=1756926364.4151876, blob_last_modified=1756926365.0891848), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380956/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/15047f69259f2cbc2cb08949d07ac6fde505bade6a0e3bb3912b183a48a4b288'), size_on_disk=2282680, blob_last_accessed=1756926369.4771657, blob_last_modified=1756926370.1431627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417910/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/694d86a1e469836a53e1ce5dba0d64b77b8f68590a2034e2a1ef5310457dc7fb'), size_on_disk=4494010, blob_last_accessed=1756926357.3112185, blob_last_modified=1756926358.3942137), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417863/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4aabb9d1a4b105c7669deb0f13cd635ab7433ed1ae02f5785aad014d604f1504'), size_on_disk=6900487, blob_last_accessed=1756926348.4532568, blob_last_modified=1756926349.2742534), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417681/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6e4ea424e9eab4dc3ea304621992e1db158565a8ce30374980b1084a653a0132'), size_on_disk=7689435, blob_last_accessed=1756926316.934394, blob_last_modified=1756926318.231388), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381025/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aea8c4d9f3783191d3e89f4566811f01089f3b0b76b736c5167bda54693b0bc4'), size_on_disk=339954, blob_last_accessed=1756926378.7781255, blob_last_modified=1756926379.125124), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417692/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/82405dc0a21bd225cc6a9e813e62d4dc0522719abf1e677c954a6ec38763b306'), size_on_disk=4873514, blob_last_accessed=1756926319.6253822, blob_last_modified=1756926320.6443777), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380926/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fd708dfb8084d767fde32c52acb58e785083ce7becbd2e160277f31460719346'), size_on_disk=6579161, blob_last_accessed=1756926364.8451858, blob_last_modified=1756926365.5441828), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381015/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/345c83c5ab13d230542683014d3d6073ba5a348e3063fea325843981c6c6e7c1'), size_on_disk=3807314, blob_last_accessed=1756926377.67913, blob_last_modified=1756926378.4971266), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417894/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/12f262a12783317d135ffc5e1d281a3d43b746a82ed8053c15de824b94bd5ef8'), size_on_disk=6449687, blob_last_accessed=1756926354.4772308, blob_last_modified=1756926355.4742265), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417919/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f5504428a85399cb212c74235abf9cec068dba03136a87ea79eae6d63a3cf430'), size_on_disk=7011211, blob_last_accessed=1756926358.6892123, blob_last_modified=1756926359.8312075), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417810/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b807197acb33acdd1b70eddedd59dc4d831e271d231d6c9bb6f1281fb4d5813f'), size_on_disk=9808564, blob_last_accessed=1756926338.8082986, blob_last_modified=1756926339.7202947), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417726/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/739e474fc54ffee0c7e6300d3abb1f83d33d48c78d137ed848f837fa186b9308'), size_on_disk=2430088, blob_last_accessed=1756926325.0663583, blob_last_modified=1756926325.6803558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381056/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/382194ca9758ddc18f059a649b13c5a21ead3a7f1fa5e012065315047a037a58'), size_on_disk=2351268, blob_last_accessed=1756926778.362746, blob_last_modified=1756926778.9827425), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417738/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c3d82a351044aa8cd5d43979369088ccf2f563022d4f7aa3683ef20923202770'), size_on_disk=19772535, blob_last_accessed=1756926326.3913527, blob_last_modified=1756926328.6913426), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417859/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e4c5915df6cb5bb450e1948d8c43bbe031d09ec345688ab51f2d8ea60f3be09f'), size_on_disk=12709203, blob_last_accessed=1756926347.8872592, blob_last_modified=1756926348.9972544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380967/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3abcaaa4db306d580dc27882172ced53d591b8aa69a1805bd2a9eea0448ff941'), size_on_disk=1245882, blob_last_accessed=1756926370.6731606, blob_last_modified=1756926371.1021585), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417839/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a04fc093d2a4fe873de135165bd62a38a1cb9b29860881ed973022ff128dc6d8'), size_on_disk=6789775, blob_last_accessed=1756926344.5502737, blob_last_modified=1756926346.4242656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417931/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7e70415ed0dfb1eb42e46789c905ccf2b593dc192d907d939d87acd59cd963e1'), size_on_disk=4991063, blob_last_accessed=1756926361.306201, blob_last_modified=1756926363.1161933), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4c942b91ac018330aeb4da512a3e47ae4531a434b6e9933cf3e2bb476a77f9ca'), size_on_disk=6312758, blob_last_accessed=1756926366.1041803, blob_last_modified=1756926367.0141764), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380940/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4f59a7f9287c427bed27910f08a041e460eff2490c21e2fbdaa5cc9348f5921e'), size_on_disk=6062382, blob_last_accessed=1756926366.8141773, blob_last_modified=1756926368.3051708), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417767/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8d0afed33cd85298c709656da4b210e3fe87aa4d6822208d744200816d59b95b'), size_on_disk=3739401, blob_last_accessed=1756926331.2333317, blob_last_modified=1756926331.8623288), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ad0e80a28e4cd2027893adee5afceab851576ed2637c1a8e94d0af2adae6ace1'), size_on_disk=5088900, blob_last_accessed=1756926375.41114, blob_last_modified=1756926376.3821359), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417628/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/291f0e4d2d0fa1656de1583bd9e131fd4d2fc1934d0bb70a6774f7e7f90e62ae'), size_on_disk=15444530, blob_last_accessed=1756926305.1314452, blob_last_modified=1756926307.1334364), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4331f53e52f628f99a9695fa70339b08eb69a011cc8a3cbdb08dfa8e6c5d3ddd'), size_on_disk=10779086, blob_last_accessed=1756926376.8381338, blob_last_modified=1756926377.9141293), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381021/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3e80794742d10b6d120e70c4e4f1d7f77123ddf8aa18499f686b4abc8eadd325'), size_on_disk=1524611, blob_last_accessed=1756926378.410127, blob_last_modified=1756926378.897125), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417923/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a32971eb62fe5c4c08298b1bdd97a916d8ef8f5898aee42aaf3fbaa8069e92d1'), size_on_disk=10004280, blob_last_accessed=1756926359.919207, blob_last_modified=1756926362.0851977), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417891/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cabef83ee373e37d528b127d813f1535faede4b98c5524d83e9906df83558b07'), size_on_disk=12596609, blob_last_accessed=1756926353.9052331, blob_last_modified=1756926355.2992272), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417644/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0c788a7ba70689a796af823df5cc14504dfdc854617952459367364c4498cdeb'), size_on_disk=949200, blob_last_accessed=1756926309.758425, blob_last_modified=1756926310.6444213), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417885/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6d5faeda491268a8ec88393883d743aac6cd14d4837698164ca32ca1052da36f'), size_on_disk=7668243, blob_last_accessed=1756926352.806238, blob_last_modified=1756926354.1422322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417828/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eb4987a8ef94e1691c74c1d828d7d9313da4691b577118a73524def2a90c6d0b'), size_on_disk=4827634, blob_last_accessed=1756926342.3472834, blob_last_modified=1756926343.2782793), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380937/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e31f54581c4a4a47033065033f3f9266913edad654543dd07b20d0ffe54847dd'), size_on_disk=2691574, blob_last_accessed=1756926366.2071798, blob_last_modified=1756926367.1951756), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417916/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2abad8555b89fbb5009e8f2a95e553a5ce579d1841ae4e71eec7b137f3282ef1'), size_on_disk=9236466, blob_last_accessed=1756926358.371214, blob_last_modified=1756926359.0582108), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417712/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/59cbfa83b9abe13f60d6a80b35afe857e3ffd761366133f60505312a2b5a72e2'), size_on_disk=641243, blob_last_accessed=1756926323.2163665, blob_last_modified=1756926323.7173643), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380979/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef743a2a6a8fe44b62963b981ef0429676e7854243ab07d450d9c3ab569c7e8e'), size_on_disk=3735979, blob_last_accessed=1756926372.2201538, blob_last_modified=1756926373.6791475), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417682/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/83b3100cfd1dc6918855c4a711ff607b36f40971655733855a7df353b4d5fca5'), size_on_disk=14037100, blob_last_accessed=1756926317.1663928, blob_last_modified=1756926319.1013844), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417713/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c5d75f4a32cadceee844da0c5ce703483f575fb927990a1615ee37205137d986'), size_on_disk=524444, blob_last_accessed=1756926323.533365, blob_last_modified=1756926324.045363), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381053/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6ca1dc55841bbb6d8d8a83d1a87d00570c1eea7cf1963db8cb04ddd427711fb6'), size_on_disk=10277654, blob_last_accessed=1756926777.9197485, blob_last_modified=1756926779.9187374), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417711/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e30dbc09cd2dfb4722d60d0833123542e9c5096f70b97867cd15d466aa7b927a'), size_on_disk=96859, blob_last_accessed=1756926323.1863666, blob_last_modified=1756926323.7823641), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417686/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6a42f24b250baf0d88696bc123f01494a400b3f03f2ff2a1fc828f9295721a16'), size_on_disk=12070748, blob_last_accessed=1756926318.2063882, blob_last_modified=1756926319.627382), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381042/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/80a0c18e8d146043e9c95c6a86552d28e28267a489b3e5c6f2a11319cf47b877'), size_on_disk=4477936, blob_last_accessed=1756926775.660761, blob_last_modified=1756926776.9177542), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417854/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d52c8b14e197b179a9b43fbc89bb3205d09d458161ee49ac15a62b7c1f7a39fe'), size_on_disk=6203801, blob_last_accessed=1756926347.311262, blob_last_modified=1756926348.387257), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c103eef2a9834296fb1328b7012b5b9184c5a3f131817dfca257843e863d34b2'), size_on_disk=9829417, blob_last_accessed=1756926362.1531975, blob_last_modified=1756926363.3531923), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417633/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0b38824aeb2fac57eca256e3652539deb4bc221eeb5afc3701a997885f9ad9d0'), size_on_disk=19311587, blob_last_accessed=1756926306.8324378, blob_last_modified=1756926308.847429), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417895/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2daca9b311da1afa40f578c6df7dc4b0039dd4857663c98cd87c665a547b7144'), size_on_disk=11915424, blob_last_accessed=1756926354.7232296, blob_last_modified=1756926355.8262248), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417832/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/268a3b7a91ccc3f3909042a711454c336b1887705de99442bda022bcbc26a01e'), size_on_disk=7655304, blob_last_accessed=1756926343.2332795, blob_last_modified=1756926344.3042748), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417862/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cea0cfbd611f8c6482ec33f8ea32f304a481d1c1225d3dacea432dd359fc976b'), size_on_disk=5244392, blob_last_accessed=1756926348.381257, blob_last_modified=1756926349.6162517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380933/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/07c22e11c56ce5ecb6c22c910c3d3a0d9b54fd341bef0513b4bf42a9c53ec6a0'), size_on_disk=10138164, blob_last_accessed=1756926365.8761814, blob_last_modified=1756926366.7471776), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380963/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c0a30240a0a99347e114928a15a24268495fd37480fde6822f503438bdf3ed48'), size_on_disk=579777, blob_last_accessed=1756926370.2681623, blob_last_modified=1756926370.81716), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417679/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bc48687cfd5c03c1a71787981180295dd3a27cf4d2c46bd26bb68cdf8b35925a'), size_on_disk=17346576, blob_last_accessed=1756926316.1203973, blob_last_modified=1756926317.84439), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381046/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a96c1819b1010c40fc4d92418e407cc5b5f48f9e24883d4b4cdf3e02d23b688a'), size_on_disk=3071378, blob_last_accessed=1756926776.6277559, blob_last_modified=1756926777.855749), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417721/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5457a24eab864fb369ae0e6fbdb302876e2a6c65ae83444f18bc01ad08ecfc0c'), size_on_disk=2073045, blob_last_accessed=1756926324.3243616, blob_last_modified=1756926324.9983587), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3fe2828766efd936f02efae2da3e068ef9b28ea374e594e09850e3e8ff0d8164'), size_on_disk=4421540, blob_last_accessed=1756926375.7961383, blob_last_modified=1756926376.7581341), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381061/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d44d57e9c03bb6e6e750809eea88657c2960ed251b8acaa1352a07f4cb346a82'), size_on_disk=3789431, blob_last_accessed=1756926779.0527422, blob_last_modified=1756926779.8977375), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417866/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b0424cd7567723443472cb266e090a30e2b64ee6b09bb1b94d8dcfdb9f0b0c43'), size_on_disk=9685580, blob_last_accessed=1756926349.109254, blob_last_modified=1756926350.493248), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417629/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/29c9c86be94792d797005b106ab00d066634bea44fbf17e81738a00cbffa264b'), size_on_disk=22750780, blob_last_accessed=1756926305.5594435, blob_last_modified=1756926307.5804346), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380952/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3811d6e6194d8b88c84fc0931cba844da23677e76e1e9a25e2890df968a7e529'), size_on_disk=1003172, blob_last_accessed=1756926368.8351684, blob_last_modified=1756926369.4641657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417948/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d43ea802a452aa9d57004381e1688222e61bd313b107077c1c663555547d1d44'), size_on_disk=1748124, blob_last_accessed=1756926364.3661878, blob_last_modified=1756926365.038185), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417709/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/395b1dd171ab9759e06a22580672bef65aac2acd7d548e38098d207c80071d89'), size_on_disk=717131, blob_last_accessed=1756926322.9983675, blob_last_modified=1756926323.6373646), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381022/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/980ed07a51dfad581fc2f7bce44031c92445822ba7e994cbfc9b5508cdf97c85'), size_on_disk=2103246, blob_last_accessed=1756926378.5151265, blob_last_modified=1756926378.9441247), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417803/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1fd8b542fb1b0e18bc5346c50ad85da440ff6190a987467ec6876ed3ec51a4e8'), size_on_disk=4928668, blob_last_accessed=1756926337.5433042, blob_last_modified=1756926338.7182992), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417814/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9f70b1469a6d0ad8758aa7a44e754e07ccc401906f8af403a72075d763919535'), size_on_disk=7618977, blob_last_accessed=1756926339.5142956, blob_last_modified=1756926341.0872889), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417842/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c763016dcbf1b53df840e3ea3c1b45234e2d9e797114000efaf692e2c394d487'), size_on_disk=9967694, blob_last_accessed=1756926345.1472712, blob_last_modified=1756926347.1812623), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380983/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eec33f1158aa017b073fcb16d6400cd800eb79e8c0b78b5e2f831d7413e4a736'), size_on_disk=4478939, blob_last_accessed=1756926372.869151, blob_last_modified=1756926373.6721475), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381052/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/652334151cb539b888d9cfcb60d9711d80bd74ec65cfa3799de10b2970ab9c09'), size_on_disk=5788579, blob_last_accessed=1756926777.7767494, blob_last_modified=1756926779.283741), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/059cc8a8b1f7918ab1b6499d3d9843100b3b37fe6dcd57dfe9ae512c6030cad6'), size_on_disk=2109635, blob_last_accessed=1756926376.7021344, blob_last_modified=1756926377.469131), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417858/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2d7539fcb6297592fe3755ccc88e122441925fbaf9f5600d4daa88639630fd8b'), size_on_disk=1021831, blob_last_accessed=1756926347.8042595, blob_last_modified=1756926348.5072565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381055/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0aad0d3724742845da7f0afae0c18ab5c992bec76c02e0ffda057f00d0894d35'), size_on_disk=3355008, blob_last_accessed=1756926778.3377461, blob_last_modified=1756926779.263741), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380957/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5a2362d35ac90fb1926051e6313886a649074b0022b128741a22a5ee24bb095d'), size_on_disk=5388789, blob_last_accessed=1756926369.5311654, blob_last_modified=1756926370.8571596), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417661/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c6effb515a20066646b7863cb86e9dc012e14187e432ba7458cd6af18662bfd6'), size_on_disk=22122748, blob_last_accessed=1756926312.9054115, blob_last_modified=1756926315.6813993), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380936/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5a931bc1181001314c3822051c493ba7a8b87b5bb26b8866783612f2bcf49c62'), size_on_disk=3467608, blob_last_accessed=1756926366.19118, blob_last_modified=1756926367.2261755), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417670/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5e36b63ef1fbb55f821ed2c3d61f0d0bf86cf00846e86ff9dbe6f4d9597f9dc8'), size_on_disk=10881106, blob_last_accessed=1756926314.7704034, blob_last_modified=1756926315.980398), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417750/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aedd94fe4b42547108a807242052397b6dfbfda0eb4e56a9f2e267b312251147'), size_on_disk=1531584, blob_last_accessed=1756926329.1973405, blob_last_modified=1756926329.9403372), CachedFileInfo(file_name='genome_map.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b8f0ed936cb43c4649126e9b7596cef0ab626a2d'), size_on_disk=142786, blob_last_accessed=1756926300.410466, blob_last_modified=1756926300.4954655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417843/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6569e23f0eb660e9f7111b9cdd69e7ae797ba265647ee44e4cbf2d8babdeca9e'), size_on_disk=2706587, blob_last_accessed=1756926345.3402703, blob_last_modified=1756926346.125267), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417918/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9f1cf3832969364b6cee1c817d3583a554b765bdbc1ff3f5b29692b7ea98ee7e'), size_on_disk=6936136, blob_last_accessed=1756926358.5802128, blob_last_modified=1756926360.9132028), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417765/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bd2b5a1b1593db002ea9c159d4c40d1d8addddbf124207a7b32b0bb43e2e4d72'), size_on_disk=2696437, blob_last_accessed=1756926331.2103317, blob_last_modified=1756926332.046328), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380985/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2ec986412ef2a9e6e59f4ea3dc278b27ab3975b5025d00bd1cd38fe1867b0fea'), size_on_disk=7208882, blob_last_accessed=1756926373.2251494, blob_last_modified=1756926373.9591463), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417826/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3532f474125bbdc0445e46fe8be83801ca9c079afb1dedd7cd95e25994c50d17'), size_on_disk=11492099, blob_last_accessed=1756926341.9132853, blob_last_modified=1756926343.2502794), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417818/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3b30217b127ff88f165762c5054c392d9489b9007ba7ca8b86f18b44f9988e4d'), size_on_disk=8388230, blob_last_accessed=1756926340.2372925, blob_last_modified=1756926341.5632868), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417618/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d2ce91297551bc97b4fca46007f80c8846e5f9e9c78d9c1d7ded02fc31b3ef38'), size_on_disk=9239111, blob_last_accessed=1756926302.3654573, blob_last_modified=1756926304.3714485), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417822/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62cbbb0172158a2c0776000136424032a794757ca9e9f971e5f445deb20e8579'), size_on_disk=6040431, blob_last_accessed=1756926341.3882875, blob_last_modified=1756926342.8302813), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417649/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6baf4bcd82d9fe55b8dcdc7602c0d39d20954675ef4c5484331ff25dac8ad3f4'), size_on_disk=2672976, blob_last_accessed=1756926310.8234205, blob_last_modified=1756926311.4704177), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417700/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1240fbb2497074bb7ff98b1bc6399b8a8ad5d320246285abfb6738e27e2bbdeb'), size_on_disk=19312447, blob_last_accessed=1756926321.6763732, blob_last_modified=1756926322.8383682), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417611/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c82d882f6538d65316dd01e26a82af13e41717952c79e20dae6c3f1d9724ab60'), size_on_disk=3720891, blob_last_accessed=1756926301.4614613, blob_last_modified=1756926302.2704577), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417845/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ca2b89837de406b10d72533a3e3d5457b3512b704f7cea395fb0e1b88828393e'), size_on_disk=4648586, blob_last_accessed=1756926345.6932688, blob_last_modified=1756926346.5862648), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381030/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e5baa8386faa327b26d27d6c8e6b265dddac1d55565e81d724a1980b93d871a3'), size_on_disk=1867415, blob_last_accessed=1756926379.2021236, blob_last_modified=1756926379.6681216), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417769/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e640018be0b2de04e0ed77c7586c413990876e91348531f43511c20da5fe279a'), size_on_disk=11568261, blob_last_accessed=1756926331.59533, blob_last_modified=1756926333.0913236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417703/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e82b21d919b84770e2d141eaea09aff82ef57c3ff3dbd15c588c1b8cff212c9e'), size_on_disk=1462841, blob_last_accessed=1756926322.0533717, blob_last_modified=1756926322.898368), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417678/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/825d9bcd10c008b68cc84e92f20608c0d52770ac028bbc4a9219f1cd210ab8c4'), size_on_disk=29320273, blob_last_accessed=1756926316.0503976, blob_last_modified=1756926318.3993874), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417746/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/432442dbdd5578acedcbf6eddc14cfe0d460facc7b0dea857225fbd4e6a4242c'), size_on_disk=2726090, blob_last_accessed=1756926328.6353428, blob_last_modified=1756926329.1983404), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417648/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fb706eb700e8fc003822b897accd3501360d737fd1a9ffcf26c3e9d9b25a5e35'), size_on_disk=1285054, blob_last_accessed=1756926310.3104227, blob_last_modified=1756926311.0174196), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417857/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/64f710e0b8a62bb6eddf3508f133b6b82af8bd21dd07f2319af6d07981085b1e'), size_on_disk=11109477, blob_last_accessed=1756926347.471261, blob_last_modified=1756926349.6202517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381071/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cf15be48b47b6c369c6a4290b783b9b22b01d6cb89d04c738c275241548b811b'), size_on_disk=2543373, blob_last_accessed=1756926781.2297301, blob_last_modified=1756926782.309724), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380991/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dd86db1779581d5853d4d6a0fa2aeacdb00f92c832683ad788743f17db625db4'), size_on_disk=2640507, blob_last_accessed=1756926374.030146, blob_last_modified=1756926374.8661423), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417607/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/623d085888c300fba1d05a9018ec3d4c31912f2b91a872ef34feda3322889b70'), size_on_disk=6809356, blob_last_accessed=1756926300.611465, blob_last_modified=1756926301.75846), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417831/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/53d810292e5a513a8ab543fe18783f8d493cedbadc7c97400dc103a4675954df'), size_on_disk=6767934, blob_last_accessed=1756926343.12528, blob_last_modified=1756926344.4612741), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381000/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/503f854145f3a220aa936072c3e66410d34914fef948992399a4addbfc977186'), size_on_disk=3904727, blob_last_accessed=1756926375.3401403, blob_last_modified=1756926376.5601351), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417912/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4451877e599fa65e1f00f67b5fcd41fe74719d8bcb19ddacac419fb347c77cf5'), size_on_disk=4943909, blob_last_accessed=1756926357.7152166, blob_last_modified=1756926358.6302128), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417645/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/761eba472193b23cbf7735946e08aa1c4db5b1abd5125f1aad22ddabbf872f04'), size_on_disk=2080044, blob_last_accessed=1756926309.765425, blob_last_modified=1756926311.5084174), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417913/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/35c1a160b767f9f5870216ed33a1dc24804690772a5baee58f623c6dc0632127'), size_on_disk=9135350, blob_last_accessed=1756926357.9462156, blob_last_modified=1756926359.3142097), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8168980e59c50149bfb9866a1e8855c235f2882b988df99331c9247f65e9813e'), size_on_disk=2289686, blob_last_accessed=1756926376.4721353, blob_last_modified=1756926377.2151322), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380990/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4e04122d2830db35f2b82b29efaca846fe10d7638c88c3e4d3cf78bf10cf245a'), size_on_disk=2093813, blob_last_accessed=1756926373.9261465, blob_last_modified=1756926375.1611412), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417797/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/12ee278356fa75b0ad555fe06376c78b71630324b7b89aa28abe960c187d256f'), size_on_disk=14391201, blob_last_accessed=1756926336.3743093, blob_last_modified=1756926338.322301), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417816/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/86256a1b98c6d44750fd21ac9ea099262f1f0ec03d182b65741d669fb80f289e'), size_on_disk=10618044, blob_last_accessed=1756926339.7852945, blob_last_modified=1756926341.8372855), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417701/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a0dd9587c7525a610a3794cf41f6db24a6ba22a12ed852dc27381a68db3e8fca'), size_on_disk=8781514, blob_last_accessed=1756926321.6273735, blob_last_modified=1756926323.1263669), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417662/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/90008c75533e5a395bf92b2a4382cfb8d6c27af4921615cac22bc1965375efb5'), size_on_disk=12701661, blob_last_accessed=1756926313.0204108, blob_last_modified=1756926314.158406), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417813/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f7d5239d0d39e171aad1e84964f8cdc3c7a325fe8c38b6829e114cb3997e658'), size_on_disk=10238322, blob_last_accessed=1756926339.4142962, blob_last_modified=1756926340.8722897), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380982/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d1e44b0b82ea22fb6c513ce02b49facf5f38b64b2baa91b24942fc6858125e37'), size_on_disk=6977161, blob_last_accessed=1756926372.8241513, blob_last_modified=1756926373.4951482), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417865/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/348bfc1685f79e9ec7f559e96b61b8c43628d611378b6c4ead8bffea44415f1d'), size_on_disk=9233047, blob_last_accessed=1756926348.8982549, blob_last_modified=1756926349.9182506), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417696/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2e1bce702520ca07d9037bd4de4d21bb30ef6b7fdf7de9d75ea232acc036a72e'), size_on_disk=31303382, blob_last_accessed=1756926320.7203774, blob_last_modified=1756926777.0657532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417940/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/270559ca1db4605de241e8642461b4d7304d124e4fc624f7d8a8a59867301a4c'), size_on_disk=2328150, blob_last_accessed=1756926363.182193, blob_last_modified=1756926363.9021897), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417689/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e27d1d153e5b135a09ec5db80bb05ace721fd60ac90f0693ac34ca3160fdfbdd'), size_on_disk=5015020, blob_last_accessed=1756926319.2973835, blob_last_modified=1756926321.220375), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381027/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ba9aba2cc5b9bd9e83599ccba5920f028ac9c6936436dac50f45c606f3abd841'), size_on_disk=6537548, blob_last_accessed=1756926379.0151243, blob_last_modified=1756926775.5467618), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380928/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/251ffcb8e06f2ac0db0791065248327a9d1eeef33a07f5c6b25946e04b6c46d8'), size_on_disk=2289693, blob_last_accessed=1756926365.037185, blob_last_modified=1756926366.1961799), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417605/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f689b77eccd8d95d70fb616f4e2aa03a41201aec2a8c3c86849b7b145cb0f796'), size_on_disk=1787545, blob_last_accessed=1756926300.5184655, blob_last_modified=1756926301.74346), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417706/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4f1ea5592a855f3bf5739b0caae1c69c7606276d125c3dfe8e98379d12bedf1b'), size_on_disk=1154142, blob_last_accessed=1756926322.9053679, blob_last_modified=1756926323.5173652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417886/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62fe87e7519370c4ac46e6d9d0abf314a8f92d9a5d6804ebddae0ed095641219'), size_on_disk=7968977, blob_last_accessed=1756926352.9472373, blob_last_modified=1756926353.7992337), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417651/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b95b6be3a7a6a5b4af3bbf15c5a4a6b3893f3030ef6d1dcfe79e6c6554e3d3cb'), size_on_disk=13551797, blob_last_accessed=1756926310.9544199, blob_last_modified=1756926314.6814036), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417864/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/15d259c52e80a2d2d9c20274ef940237c90001d32f2aecb80779335a708222ff'), size_on_disk=3706383, blob_last_accessed=1756926348.5722563, blob_last_modified=1756926350.0962496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417834/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a69c65a4d32399355863fa14d4a1e92ecff2c92498e122e8e77796de16d42948'), size_on_disk=8537233, blob_last_accessed=1756926343.3672788, blob_last_modified=1756926345.2462707), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380964/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3a63df07de544662fb04d5388f34b0c8068efbc19f464b673d8a860a57b5b422'), size_on_disk=956844, blob_last_accessed=1756926370.333162, blob_last_modified=1756926370.8741596), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417650/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c4d103888f768ff3cb0c68f4b747cb8d8aad8058b75174160cfc42b2504407f1'), size_on_disk=1337049, blob_last_accessed=1756926310.89842, blob_last_modified=1756926311.4924176), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417877/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b170370a220db5677b9c6193707d32d1d14b9082ca574b9562c4ac8ea4c72c26'), size_on_disk=10449263, blob_last_accessed=1756926351.0392456, blob_last_modified=1756926352.32624), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417830/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/21e2dbb49f07cb7c2d7277f5bf1a442e216a2991677c3c4ece5c51316795ea77'), size_on_disk=12760016, blob_last_accessed=1756926342.9432807, blob_last_modified=1756926344.1602755), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380954/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/338886924ec42501a99a4b842e18fdbed28200d52daaf43e00ee601a147e0188'), size_on_disk=4903852, blob_last_accessed=1756926369.2841666, blob_last_modified=1756926370.101163), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417925/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8cdb783613764a35fd3a73db89463db6876951d443f5651a9a46877a53917f3f'), size_on_disk=912848, blob_last_accessed=1756926360.409205, blob_last_modified=1756926361.1532018), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417872/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7d71ca12594ab85797fca8c1b88947cb0f3ece93596f1a4c5adc04c11c0abf68'), size_on_disk=8522283, blob_last_accessed=1756926350.1742494, blob_last_modified=1756926351.180245), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417770/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0e7437c90c616b78ccc0963e0388385028b70d66fbe73e0ceb4e30e2d0239816'), size_on_disk=8252889, blob_last_accessed=1756926331.6563299, blob_last_modified=1756926333.0863235), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417868/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/52e3f224a0147264d4ce477d26f4e11ff0745c328eb3e2ac44209dba1c8c55eb'), size_on_disk=5039768, blob_last_accessed=1756926349.6852515, blob_last_modified=1756926350.6082475), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417900/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/45d5a0bbf39e542f22aa4e36186673d1246533dc513f006312a26286496f6ead'), size_on_disk=11235417, blob_last_accessed=1756926355.535226, blob_last_modified=1756926356.6632211), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417848/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eec4ffff5d145c0ee0850d5e68787ab90e6510cffa5803579db44e3a575822dc'), size_on_disk=13556826, blob_last_accessed=1756926346.1882668, blob_last_modified=1756926347.2852619), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417853/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5995033a04f017892b30c025a1527f9b6420bf1e7636c466cdd8d1462cf228c8'), size_on_disk=2186899, blob_last_accessed=1756926346.9012635, blob_last_modified=1756926347.8172596), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380942/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a958d997c1aff07ec07304485ee818280bc7714dd9c485ea73ef7fda81f2fe63'), size_on_disk=11407010, blob_last_accessed=1756926367.109176, blob_last_modified=1756926368.6211693), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417725/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4195b9ed6506437a735fd8061cd1b79211c99169c4319c4f72b801a501f2b0f9'), size_on_disk=2233032, blob_last_accessed=1756926324.958359, blob_last_modified=1756926325.594356), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380932/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f214422d02adb2b0a1037eb424d8574123cf27d8a5cd1418feb5733b0249f60c'), size_on_disk=6251591, blob_last_accessed=1756926365.6181824, blob_last_modified=1756926366.5551784), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417624/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/28c1de2a4bd6b421cd05c3bb211ffde83e4b830127f106ec7be42ad288e26ef6'), size_on_disk=16758887, blob_last_accessed=1756926303.7884512, blob_last_modified=1756926305.6184433), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417753/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a8969c75211310735ad33d5af57327f2babc86276adc7cefcda254cc9fc9baa5'), size_on_disk=6433095, blob_last_accessed=1756926329.577339, blob_last_modified=1756926330.4023352), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417787/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/17f33e06b6bd77c0bbd3141b53fc2d9550b0c54e99b18fe79ddad931e3a283a9'), size_on_disk=12355842, blob_last_accessed=1756926334.5003173, blob_last_modified=1756926336.2953095), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417897/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/309aab637d037638082524649ef7b0828885a98e786c64af01072d68968b7d66'), size_on_disk=7857749, blob_last_accessed=1756926354.8022292, blob_last_modified=1756926355.9902241), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417731/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/39ec5969bc8f3acceb3aa30b72f0c0101785a36da340f3983ad00d8cc80160a4'), size_on_disk=1557745, blob_last_accessed=1756926325.658356, blob_last_modified=1756926326.2043536), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380950/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f508a5da82832b920577592b853da2c2d76ebf0adca587e10b63507f46dc5066'), size_on_disk=3452513, blob_last_accessed=1756926368.7781687, blob_last_modified=1756926369.7081647), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417934/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5816750a355a277e532d46ffa5159f8a2ec1ce66686fb12f07ccab4b4f7b3c98'), size_on_disk=7324082, blob_last_accessed=1756926361.995198, blob_last_modified=1756926363.89619), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417878/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d3d105cc45280a5ea88c2bba754c0c5849d0cec7112a0926e9ed0c946e7f5cfd'), size_on_disk=8838404, blob_last_accessed=1756926351.2492447, blob_last_modified=1756926352.7352383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417806/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2befcad73b2aa8170ceaf8c249ceba45e87037771e86c9bb4bd7addc5815bbec'), size_on_disk=5276603, blob_last_accessed=1756926337.9863024, blob_last_modified=1756926338.8112986), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417702/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/41123195fa339a4ffabf6d686a284c343d0519089a18b828818c92ec43a26eae'), size_on_disk=6213776, blob_last_accessed=1756926322.0263717, blob_last_modified=1756926323.0323672), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417860/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/72e751d246e9b938ce9ab4380e2a1114ecd3c36d5829e3298f391c1edfefae24'), size_on_disk=3996315, blob_last_accessed=1756926347.9312592, blob_last_modified=1756926348.819255), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417615/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3b8b3c4c686238c7c642151bbdcb2ba7796de56c1922e558a1e7f08507dc5abb'), size_on_disk=2866472, blob_last_accessed=1756926301.8314598, blob_last_modified=1756926302.5744565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417856/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3dc4e868f31759c9ac3d43f40097f5ed2f0efb6307e3bc1ccbed70c50a16ee4e'), size_on_disk=6126482, blob_last_accessed=1756926347.3832614, blob_last_modified=1756926348.3162575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417907/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b24926bd36cf33ea9107043b9a47db8239ad6208745c56002513225e8cb80a6e'), size_on_disk=7594380, blob_last_accessed=1756926356.7332208, blob_last_modified=1756926357.882216), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417719/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cc0a365d1d79f0469571216394f0cb211b9096f2a567e0a347a18f0c496bac06'), size_on_disk=2076369, blob_last_accessed=1756926324.010363, blob_last_modified=1756926324.951359), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417792/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/196cd0cf279f6bc85ca0dbeb1f089721f63eb167644d999b45d8cfe9a1f52acf'), size_on_disk=11982413, blob_last_accessed=1756926335.4593132, blob_last_modified=1756926336.651308), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417617/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9ad746ec09ed51ed497d87f3dd64b11916b70e6833ce66141bc8feb6ba73d1d3'), size_on_disk=23082279, blob_last_accessed=1756926302.1754582, blob_last_modified=1756926303.6944516), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417755/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f4ffef985aed69a80417b5179ef9923bd73378a93a97f2dff128b1f7716b4599'), size_on_disk=8437424, blob_last_accessed=1756926329.777338, blob_last_modified=1756926331.2423315), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381057/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4e6c617d22a655d32847df8b5334f4fb7236a15b8bf6e30aaabcbb0a4c55782f'), size_on_disk=17494867, blob_last_accessed=1756926778.5417452, blob_last_modified=1756926780.4997342), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417817/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ccf81aa26021a009a212ef8f8bee71c12f5c8d0a2b97ffdb2c4d86d13c8b8133'), size_on_disk=7521086, blob_last_accessed=1756926340.0802932, blob_last_modified=1756926341.3222878), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417695/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d6ae8d39c2949235dd4d82e7a0e56d9f20c20ba5f1b7a445fbba1f0df29c88b7'), size_on_disk=7710772, blob_last_accessed=1756926320.4913783, blob_last_modified=1756926321.959372), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380971/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0147090dd5a74f558b67151503ff635423a8af661b56d1faf06bb50a59adfb3a'), size_on_disk=1276600, blob_last_accessed=1756926370.9411592, blob_last_modified=1756926371.462157), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417811/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/da92b1c1579a2295da085050305367bd02a47fd52a74d1ec2a9f227b5e808b11'), size_on_disk=6127159, blob_last_accessed=1756926338.8782983, blob_last_modified=1756926340.1172931), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417603/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fdfb1fd9b21341104e6423c684b0ae8a52ed1701eb3e232babb98340f953ea5b'), size_on_disk=4460026, blob_last_accessed=1756926300.366466, blob_last_modified=1756926301.2574623), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380959/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4693c1876e880fee1ec277fd5ae9127aafa9f40d41a3d5ed482dfcfa8cef55f1'), size_on_disk=873902, blob_last_accessed=1756926369.7061646, blob_last_modified=1756926370.1731627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381054/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1a9f9c650973937d852d2de54c0d73336d1516184d29e203563fd9bf8d7fefa5'), size_on_disk=3346485, blob_last_accessed=1756926778.1077476, blob_last_modified=1756926778.8637433), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417819/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a1ba6808d672dbc9c58c26364c5cd9ed0b8a3041e6714844846648d3cd1aa050'), size_on_disk=3244517, blob_last_accessed=1756926340.7022905, blob_last_modified=1756926341.6952863), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380927/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/38e67ea2fef57610ff63c0c298a6db65ae1c0a159f2b1e61940f2e65306b8fa9'), size_on_disk=7297320, blob_last_accessed=1756926364.969185, blob_last_modified=1756926365.7781818), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417749/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8460c24102627d611c8918a1620078ee1bdf9929c44cdca863cd0dbb23f9cf1f'), size_on_disk=4938551, blob_last_accessed=1756926328.814342, blob_last_modified=1756926329.7143383), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380996/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/09e663ce2a60df5635f4e4ea77d6350e7752a9bb5e81fbdfd818869ac53ca805'), size_on_disk=13309208, blob_last_accessed=1756926375.0511415, blob_last_modified=1756926376.0221374), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417626/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1981a2a1e321acc5c35bbeb9b61a12636c27f28d6e216de20093afdbee9f8689'), size_on_disk=12046611, blob_last_accessed=1756926304.4464483, blob_last_modified=1756926306.2084405), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381049/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c571bcc5f87a79c7cd38b9614ee83b0d5cc742094e17a7990c829394fca193be'), size_on_disk=3753119, blob_last_accessed=1756926777.145753, blob_last_modified=1756926778.2097468), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417604/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e6dd3abdf75462f1f5d16f19383b98a8a4c817dc3b13c7cea2b4bea611c40c62'), size_on_disk=961320, blob_last_accessed=1756926300.390466, blob_last_modified=1756926301.3534617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380977/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/74237b16fd38090785d203d96bae72e1f5ce919d423f793cfb9d3b86623358e6'), size_on_disk=11114601, blob_last_accessed=1756926371.6651561, blob_last_modified=1756926373.12615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417771/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8fe5513ae86b28ecca3d4c70bdb73097f578dd31b4d08a072960b61550f582fa'), size_on_disk=3970195, blob_last_accessed=1756926331.9313285, blob_last_modified=1756926332.8643246), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417733/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/542261ed8bf189205b611485f1a878f28cae5c90e452ba9cad687eacfdfc2814'), size_on_disk=2645461, blob_last_accessed=1756926325.8883548, blob_last_modified=1756926327.3873484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417683/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6131abcd72bd4c6a8627685c4df8a295af50e824f52a5cde156adae7cd694bba'), size_on_disk=15545733, blob_last_accessed=1756926317.382392, blob_last_modified=1756926319.0483847), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417730/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8f2554716083cfa1bb54c2098315b92e1b3537eb265da884c00ba7e7d4c050da'), size_on_disk=14980864, blob_last_accessed=1756926325.5143564, blob_last_modified=1756926326.803351), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417760/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0500e5835116a61477f2d98554d0dd97e8d932a6b564f709c40c4884f2b49052'), size_on_disk=2234621, blob_last_accessed=1756926330.2873356, blob_last_modified=1756926330.8273335), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417639/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/43c5fb0a4f049fa5895f8167928d047cc4954aa37db1fc8a9892fe9f99c401bf'), size_on_disk=782319, blob_last_accessed=1756926308.7694294, blob_last_modified=1756926309.6284256), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380943/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/987771cb978dbc949eff5044071149a00223c6771efcc870850e3c8cbe0648c3'), size_on_disk=13397144, blob_last_accessed=1756926367.295175, blob_last_modified=1756926368.7601688), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417727/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/414c702fdf3dd97c536f3237d2c38e0ef5ad8d08bb7c991ee252e16d57b201b6'), size_on_disk=2636905, blob_last_accessed=1756926325.0683584, blob_last_modified=1756926325.8093553), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417616/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0b0f3c9481fd0e254abd544ba803de0c9da9b3a686451975f981d02fc481148d'), size_on_disk=15058562, blob_last_accessed=1756926301.8254597, blob_last_modified=1756926305.0234458), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380948/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6ac219aa7f5be77c21bde60c80ccad59edf825e2a009d6a4b7fc8bd4d8be83d4'), size_on_disk=6046323, blob_last_accessed=1756926368.6691692, blob_last_modified=1756926369.5471654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417790/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f88bed27521de8285bbb92cf95242820116e6fa2c23ab578e6608a6a25e0c04e'), size_on_disk=10836858, blob_last_accessed=1756926335.078315, blob_last_modified=1756926336.1843102), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417889/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/295f9efb0d78e3a116ce4022a7830297570446e2017a3fcd3b3d423c0c20664b'), size_on_disk=9617979, blob_last_accessed=1756926353.3102357, blob_last_modified=1756926354.7322295), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417622/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/28eed3b67a6206b453d8adb775edf2bea32cfd54ba5b37d51787f67280e0317f'), size_on_disk=20411775, blob_last_accessed=1756926302.674456, blob_last_modified=1756926304.8564465), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417840/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7e32bef3c0f7f7520382325aba12f5cf9405c22ebce8554b1d877fe24d2da52b'), size_on_disk=9679209, blob_last_accessed=1756926345.0092719, blob_last_modified=1756926346.0542672), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381026/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e41f2d7a1b084a404e7f5e9e003b27f88665a983ec73859f7eb42cd8bfa2bdd3'), size_on_disk=2857045, blob_last_accessed=1756926378.898125, blob_last_modified=1756926379.7281213), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381011/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/839fea471c67e08e0b68ae6344432c76da559e67c505c3f6120b20db2f1f753e'), size_on_disk=4378212, blob_last_accessed=1756926377.2971318, blob_last_modified=1756926378.0891285), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380941/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9a23f03fee8d60d752bd0bc1e59e9205abea4f3a78e3272b9a03447f076383d5'), size_on_disk=3514837, blob_last_accessed=1756926366.9851766, blob_last_modified=1756926368.7591689), CachedFileInfo(file_name='GSE209631_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE209631_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/be49eef90682fbd55a5e60ada8b4c1c554e1b5a2ec1209996269db020d3bb074'), size_on_disk=4258, blob_last_accessed=1756926300.173467, blob_last_modified=1756926300.594465), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417930/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62f17648a07c7675d6a101e3d12efeae247c742e37eaacf96f95e03dceedefde'), size_on_disk=20574235, blob_last_accessed=1756926361.2242014, blob_last_modified=1756926362.5701957), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417801/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4bbb1b3faac1a4c15eaac8c81260f4f18493d2a082915cbe4c480ce76a94140e'), size_on_disk=4600965, blob_last_accessed=1756926337.090306, blob_last_modified=1756926337.9153025), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380986/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f23d3060df5cb96778fbdf10fff0155c931e1a1e8cc077c7f1582542d54827f5'), size_on_disk=5858046, blob_last_accessed=1756926373.285149, blob_last_modified=1756926374.5971434), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417821/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c29db68a3430cf8cb01716d90a92105387777ed1361992eb9501271c35359cfb'), size_on_disk=6597050, blob_last_accessed=1756926341.1692884, blob_last_modified=1756926342.2702837), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417887/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/58707813ec63af2eb1a72b0c8317b4701b4777df8f828f2e08a0cbb1ccfa534f'), size_on_disk=9080847, blob_last_accessed=1756926353.0712368, blob_last_modified=1756926354.412231), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417871/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e44cb883893b5e30baa752dfbd773b31795f2072a7ec4f473371f435989cad42'), size_on_disk=6264896, blob_last_accessed=1756926349.9852502, blob_last_modified=1756926350.937246), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380968/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/549ef4e39a437adb99efecdc9b05fee3ca4b31c878b99f7a4cf1398f405ca049'), size_on_disk=3575926, blob_last_accessed=1756926370.7441602, blob_last_modified=1756926371.5941565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381067/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/da9c5ab3c0f970f9c2af4c0f5f0e9a95d47769e8096ff6b9d76092becbc8517e'), size_on_disk=9837433, blob_last_accessed=1756926780.2977352, blob_last_modified=1756926781.5777283), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381032/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/335a75b123ae38273f1b1a10f16e9547eec356f68a1d3078926f011d3332e2b5'), size_on_disk=656385, blob_last_accessed=1756926379.6781216, blob_last_modified=1756926775.4077625), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417904/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cf9194917da2f4c6c3f0411256b2604e8c14959373d7431047de2e23cffbb9bf'), size_on_disk=7269877, blob_last_accessed=1756926356.062224, blob_last_modified=1756926358.1762147), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417691/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/48de5a2968610e5ab855522d27f535d40e1973c1835ec32fcfa5bcd91a0795dc'), size_on_disk=14481663, blob_last_accessed=1756926319.447383, blob_last_modified=1756926321.2093751), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417808/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0251ae27bd961d6c962253c555963b42148092cdaa2616a78f6872cf1a4e5e4d'), size_on_disk=12900784, blob_last_accessed=1756926338.3973005, blob_last_modified=1756926339.439296), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417795/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b995a48f28ae3baa145438a39c9fed73fa8689eb31e07ff02a3bc6bc827eb76e'), size_on_disk=8032882, blob_last_accessed=1756926336.1053104, blob_last_modified=1756926336.9993064), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417669/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8eaf08a45dbd72787c5250403f3d3079db9902e77527c7ca438abc740fdfb753'), size_on_disk=7376929, blob_last_accessed=1756926314.6724036, blob_last_modified=1756926316.0383978), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417883/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8e23cc97e00adfd244a989f83702f4ce7a998ba7998dd95908a1b4ec256bc054'), size_on_disk=10709227, blob_last_accessed=1756926352.4542394, blob_last_modified=1756926353.675234), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417827/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8bfd1221f930e86ffd6150c82aae85f00a23fff33c672e40f47d671f5a2e4d17'), size_on_disk=7291599, blob_last_accessed=1756926342.156284, blob_last_modified=1756926343.1502798), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417873/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d282d59c3c191bd7b7fa7251108224d0ac3929bc98f96a2c43f283f8a5cd046a'), size_on_disk=7609540, blob_last_accessed=1756926350.3852484, blob_last_modified=1756926351.373244), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381039/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5f95b6de8d0d73589c612313eaed6ce1e6adf734e795719f23df6f54cbbedc69'), size_on_disk=1656495, blob_last_accessed=1756926379.8751206, blob_last_modified=1756926380.283119), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417789/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6bd6339b995a5730b6b65ec65f680b57195469ebee9f2e15de6e604b069d4b0e'), size_on_disk=9612750, blob_last_accessed=1756926334.6493168, blob_last_modified=1756926335.8183117), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417867/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f4783b00d4674df92d84f9a304fb9d143c7b9e4754e3efcb9c072a49a9b9298b'), size_on_disk=8536709, blob_last_accessed=1756926349.3882527, blob_last_modified=1756926350.2882488), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381035/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/410edf5a6097a77bcec6880ec2618b4aec4bc10c44607163fe2b27aca0a05117'), size_on_disk=1451911, blob_last_accessed=1756926379.8691208, blob_last_modified=1756926380.5191178), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381036/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ff76dabe83d2948240dd846fcc2a969380b7bd9f8d2eff2c71c299f0b14134cd'), size_on_disk=2593393, blob_last_accessed=1756926379.8661208, blob_last_modified=1756926380.5611176), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417707/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/89192f5007f730a90c823219ea107c799935f5e824963090abad813603160fa8'), size_on_disk=2615872, blob_last_accessed=1756926322.9183679, blob_last_modified=1756926323.9473634), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417666/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/38008595447ab722c1889d0e19babf74299822ac7733504a4f09e1af1e4ad1ac'), size_on_disk=7712422, blob_last_accessed=1756926313.945407, blob_last_modified=1756926315.073402), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417855/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ac2218ccb9f80664ce377351ea1dc84bf3f70b36006a86cffdb761a40ddab791'), size_on_disk=1981158, blob_last_accessed=1756926347.3992615, blob_last_modified=1756926348.3062575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380993/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d568d29486ad3ec6f67f87b78cacb8b7102c591f979ec2bdb17ff75bde789670'), size_on_disk=3031778, blob_last_accessed=1756926374.496144, blob_last_modified=1756926375.2661407), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bbfeb8571ee8915d793698c2116cef9f2ea61573c66b4ae7066078271c6172d6'), size_on_disk=5132301, blob_last_accessed=1756926362.6441953, blob_last_modified=1756926363.7271905), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417847/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4dd39704bc9c1d60a8271f16206887171ee92abde023c53501c1560533a688c1'), size_on_disk=4755120, blob_last_accessed=1756926346.125267, blob_last_modified=1756926346.8372638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417796/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/52d8262071c9c9de8cd8fb52be9e67acf125b8ce67033749fb92989f0d1f9345'), size_on_disk=10184069, blob_last_accessed=1756926336.2803097, blob_last_modified=1756926337.4333048), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417705/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/92b4f5af6793c98b854bcca83c86ac0cb41ec4e40ec23d3ba9c4ff287852c63c'), size_on_disk=332184, blob_last_accessed=1756926322.5923693, blob_last_modified=1756926323.1503668), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381018/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/878bf028aca9daeebe7ed0f8ee522580d512771ec4a5e35bfc62ac675176ad15'), size_on_disk=701951, blob_last_accessed=1756926378.2281277, blob_last_modified=1756926378.643126), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417903/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3ddfaef9dc57d0f63b8613408aba3af803dabd5ee87a1584edd9391b2b41bec3'), size_on_disk=11218813, blob_last_accessed=1756926355.9432244, blob_last_modified=1756926357.2232187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417684/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/eee25a9a91c54d0404db884532d1b85555f2e1de0c664abcbda4343f9d729842'), size_on_disk=10310520, blob_last_accessed=1756926317.7173905, blob_last_modified=1756926319.3473833), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417780/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4f1b7c3ded6441c8e906c3494ae63750d25303ee6f8d8e05120dc11ce8d79080'), size_on_disk=9025914, blob_last_accessed=1756926333.2923226, blob_last_modified=1756926334.387318), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417734/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/10d4c6d9acc5b56370fc32062262ae3871008e20e960c6b352ac27a4e229c905'), size_on_disk=1495194, blob_last_accessed=1756926325.9453547, blob_last_modified=1756926326.745351), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417804/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/16ee23ae4dbe8a4d968a614f5e6de65028b6edce012231a50eb776d65f949d34'), size_on_disk=2107502, blob_last_accessed=1756926337.7883031, blob_last_modified=1756926338.5083), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417653/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/59233342d3c3b71fce95d5295cf7c1e5ef3f4254dbc120229cb83b6c8115c759'), size_on_disk=12208357, blob_last_accessed=1756926311.136419, blob_last_modified=1756926312.3904135), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417850/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c08fca103aa3dd1944dcf5c73b67397263842dc687b4101c8ab2c1a63d50205a'), size_on_disk=1182282, blob_last_accessed=1756926346.5122652, blob_last_modified=1756926347.3082619), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381059/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/59daa71f55c9c0da6f8d56fc2497491477312fbdbcb9e92d4951874f2ed72686'), size_on_disk=13124154, blob_last_accessed=1756926778.926743, blob_last_modified=1756926781.8157268), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417602/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/26ae91a914e9ad095615c88ac3c62c99e28ae2687ccd66007f13ac8fbbffa3fe'), size_on_disk=3717181, blob_last_accessed=1756926448.0367467, blob_last_modified=1756926301.5714607), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417823/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1887333f71d11fe7c43748662f58d9ba3fb12f3f6ca30a61d7e1c7a0c95f7064'), size_on_disk=9444476, blob_last_accessed=1756926341.6372864, blob_last_modified=1756926342.8442812), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417908/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/128e54326b088977000f8caaba7d09e324281ed7fe0e3e5f2779783f0a82f293'), size_on_disk=3614805, blob_last_accessed=1756926357.1272192, blob_last_modified=1756926358.2542143), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417677/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cfcdac65879c0c26771c1cd1c4a656d4db20e6adb2ed3ad833cf23cd35a9d848'), size_on_disk=13135194, blob_last_accessed=1756926315.9083984, blob_last_modified=1756926318.1283886), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380975/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ea018f4882a60f5fb07a4bbae51bd494e44e6df9b215d9da5e405687a567dcbf'), size_on_disk=8369150, blob_last_accessed=1756926371.6051564, blob_last_modified=1756926372.7471516), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417825/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/efc4971b63b21d6aee96e17d9e9b2cc2e3fd34608cc3722e36032c594fc03e48'), size_on_disk=8550531, blob_last_accessed=1756926341.8732853, blob_last_modified=1756926345.0782714), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380992/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dbc6b1de202e7fb25701c02607facc3e8c332550907ca0b70ba177533a311d6d'), size_on_disk=2551903, blob_last_accessed=1756926374.2281451, blob_last_modified=1756926375.0341415), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417728/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/2be09708238c50a31b4e9b334c6202c26eaa6a950ebf3de4287651999d45097e'), size_on_disk=3385901, blob_last_accessed=1756926325.0603585, blob_last_modified=1756926325.869355), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381062/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/76151e17f242c5108010debea961b0b13f3821be2fcb77fd923a36cc0e672695'), size_on_disk=4880117, blob_last_accessed=1756926779.3357406, blob_last_modified=1756926780.5167341), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380997/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/add73ab9723ef88a47192fa169b89aaf17c16a0841fc9e53c1c5d73cfdc2ad50'), size_on_disk=15536494, blob_last_accessed=1756926375.1291413, blob_last_modified=1756926377.6141305), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417754/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0d840353f1148a08928dfb3177a0abfe635c406fc77d6d9958743585ade6b872'), size_on_disk=1101891, blob_last_accessed=1756926329.758338, blob_last_modified=1756926330.215336), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380969/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/10410488c74f35a493cef2ffc4a64b28b336be1fb32a2b6dac692e0d7fbd4271'), size_on_disk=1973405, blob_last_accessed=1756926371.041159, blob_last_modified=1756926372.1191542), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380965/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6b384b53be751cec9056e74c2c8ce9fe2e532cfbf6a38e43bb09ce74ec817f3a'), size_on_disk=12974487, blob_last_accessed=1756926370.3761618, blob_last_modified=1756926371.4361572), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417899/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b4349f94cb0679468e9c28a9439c1a71c88d7288adc3bdad0ca31a2590f1d4e0'), size_on_disk=7910137, blob_last_accessed=1756926355.3832269, blob_last_modified=1756926356.3022227), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380929/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/aeddf7cf3aee11ee08c90d305e66fc682e675959c171ecfaa591bc1d9015ebbc'), size_on_disk=6656759, blob_last_accessed=1756926365.1011846, blob_last_modified=1756926366.0181806), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417690/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/283af9b17d776b75f6ff6ad1f426d6d0e0e5aa1f2089a20910edcb6763191b69'), size_on_disk=12265448, blob_last_accessed=1756926319.171384, blob_last_modified=1756926321.4153743), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417807/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ebc732f5d06d745bb63af3c7f5966722972fa722ef9d0194d71cf6d9485fda83'), size_on_disk=14049230, blob_last_accessed=1756926338.268301, blob_last_modified=1756926339.5272956), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756926300.2174668, blob_last_modified=1756926300.2804666), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417687/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fa4f79173f5a5ed278c7ef80d9fb220aa9c615de97bae7963969e27ecf0e587c'), size_on_disk=13352339, blob_last_accessed=1756926318.3003879, blob_last_modified=1756926320.3913789), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417619/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/0a4d4159941caa34a5e079821d49047d1434077926b2933c27d384e04ed2e5b5'), size_on_disk=9949802, blob_last_accessed=1756926302.5744565, blob_last_modified=1756926304.05045), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417718/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3ba23a0e7c5b3a455e95fbf6a22b51687d5330c19207241bdfd189e3776f5ba3'), size_on_disk=3304954, blob_last_accessed=1756926324.0233629, blob_last_modified=1756926324.986359), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417799/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/cba2fb579c7706a7ff38fad3c7c649000560c1052e681adea986c5cc02c02cfb'), size_on_disk=5688906, blob_last_accessed=1756926336.7133079, blob_last_modified=1756926337.7073035), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417924/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f2053fcaa7f21b1703e3491e167b457c29759b2fe9535f0cb2bfa257a1b86cea'), size_on_disk=6988370, blob_last_accessed=1756926359.940207, blob_last_modified=1756926360.8152032), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417667/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ef18f0ec136ef4b0facd217b32604556a7c9f514d75e26d2be17eb8da9b74a14'), size_on_disk=7979245, blob_last_accessed=1756926314.2284057, blob_last_modified=1756926315.734399), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417824/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/65d997db1339fc5a8380a61fcf1ab1a6d28fb9142fc7d78c89236dc218b91ea3'), size_on_disk=8218856, blob_last_accessed=1756926341.7612858, blob_last_modified=1756926343.0512803), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417833/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/48792db399d7f0aab6765109e8c01abd61a2da93e5d0bdf7c325bcffe01fe113'), size_on_disk=8139822, blob_last_accessed=1756926343.3262792, blob_last_modified=1756926344.480274), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417809/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/486100d34df42ce97476d9753f8d57edb5f53596eaeca2d0cf81f5f95a4f4e12'), size_on_disk=7376702, blob_last_accessed=1756926338.5872996, blob_last_modified=1756926339.9172938), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417942/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f3a5b1eb0f954ddf66a8dfa1a38465726d2251730d3898865535269a43c7fa4a'), size_on_disk=6911573, blob_last_accessed=1756926363.415192, blob_last_modified=1756926364.3521879), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380998/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6808b8e1fa74b807321826b2c54182a3b6e257f0a223be0a6a94caf9463959e9'), size_on_disk=16847111, blob_last_accessed=1756926375.2231407, blob_last_modified=1756926376.572135), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417911/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3ca8b1b14564fbc90b44995a61a34d5a67482eb5fd867ce4f9f2196de2b73539'), size_on_disk=3051177, blob_last_accessed=1756926357.404218, blob_last_modified=1756926358.3022141), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417665/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7c9b3d448e53fcac9334df092cc003f438cbc462e6785ac3d61202bd65a7125f'), size_on_disk=7481605, blob_last_accessed=1756926313.490409, blob_last_modified=1756926314.7744033), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417657/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62398c518588ad11a939f95736cf0ed01a39890567cd4a8f05e9c9ba1155f9c1'), size_on_disk=10144285, blob_last_accessed=1756926311.5634172, blob_last_modified=1756926313.1174104), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380981/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/198a0b08846a11e72de25e0e9c44f495ef88c9260de2e036d240715004c04953'), size_on_disk=8535317, blob_last_accessed=1756926372.5771523, blob_last_modified=1756926373.8291469), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417874/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/6674c316eab3b03bbbe3502565efff7650445f5e1453933336d74938e42a14bf'), size_on_disk=6201897, blob_last_accessed=1756926350.5772476, blob_last_modified=1756926351.5142436), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417668/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b426513aab4e071a24b7d27100a4da4da8c71c8e9b3e82163808b0ec6a42b8d0'), size_on_disk=6011682, blob_last_accessed=1756926314.5374043, blob_last_modified=1756926315.4354005), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417783/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9603c95097c451e616c54d52d3474b866453409905ea16365d019e77c6ca31b3'), size_on_disk=4176778, blob_last_accessed=1756926333.6983209, blob_last_modified=1756926334.4773176), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380960/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/60771297311bf96c3213c2a96321062a06067c91d2bba2fce69a7a6d7a0c980e'), size_on_disk=560607, blob_last_accessed=1756926369.7811644, blob_last_modified=1756926370.3121622), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417610/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a93c708ade83c1c4521589a51835a1dcc98972ab900e0eefdb8ce34e64394bf4'), size_on_disk=2566580, blob_last_accessed=1756926301.328462, blob_last_modified=1756926302.4814568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380947/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3d6062f97d18f4945c36590355a312df2a16206c26626392cfb44d9613aea49d'), size_on_disk=9201129, blob_last_accessed=1756926368.5321698, blob_last_modified=1756926369.383166), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fc6783c3340c869bc3838a9a9d5abb8bca67361feeef12c42b6cf478d73bfd66'), size_on_disk=9020794, blob_last_accessed=1756926366.2571797, blob_last_modified=1756926367.2561753), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417937/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e432985b09ba72eab54fc008b87b20a718dbae96d920a61b47de7e16d214227c'), size_on_disk=6445266, blob_last_accessed=1756926362.6341953, blob_last_modified=1756926363.6641908), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417890/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3eca80a3220fd0294fdfca83740ba70a20af764d28833494948705c4d542ded3'), size_on_disk=9018889, blob_last_accessed=1756926353.7652338, blob_last_modified=1756926354.7252295), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417788/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/fd6f44471f1b86568bc059f25e5ccec7bda2b85b9f8be536aeaf2305fe015f5d'), size_on_disk=9112879, blob_last_accessed=1756926334.546317, blob_last_modified=1756926335.971311), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/43f12fb4826be4c7a3dd64800fc72cbcab2a9ce5bed7f7b5b9aee974deb7c06d'), size_on_disk=1116877, blob_last_accessed=1756926376.094137, blob_last_modified=1756926376.6491346), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381034/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/112a8e705f7536fa06c630165d3c409f87a5784ad626b891cc2b3919e24bdd14'), size_on_disk=250813, blob_last_accessed=1756926379.795121, blob_last_modified=1756926380.1791193), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381033/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5fff567b96a4649d787e93ef8cdca2d8ce977b7f2eb25d6f78e50bc30c798bce'), size_on_disk=1712156, blob_last_accessed=1756926379.7391212, blob_last_modified=1756926380.3661187), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381048/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/53cd017f084781da16e69ae01b406ac0d8824a4d991e39b22c428e664a3808f2'), size_on_disk=2181901, blob_last_accessed=1756926776.9907537, blob_last_modified=1756926777.7067497), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417693/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/98a5478f5bd4feb033ac69510f2bf785c87777377b7b8b6dce24b8f89dbd6a03'), size_on_disk=18580692, blob_last_accessed=1756926319.7013817, blob_last_modified=1756926321.9783719), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417758/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/54f8e8d84e5b8b9457aaa143829fcfd5a4715d0fb184478ce659cd6c6efe7be1'), size_on_disk=4176901, blob_last_accessed=1756926330.0203369, blob_last_modified=1756926331.1383321), CachedFileInfo(file_name='GSE222268_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/GSE222268_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e0f1d5b82fd97798d0c02d992c49c0e83d8dcf82e07e001284e78d9656355481'), size_on_disk=4411, blob_last_accessed=1756926300.1784668, blob_last_modified=1756926300.5344653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380994/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/7c5f237d9c32233a005e39386360f02d9e1932f9ac267ac3458f96aaf59e01ef'), size_on_disk=2496053, blob_last_accessed=1756926374.8051426, blob_last_modified=1756926375.3471403), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381044/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/89a79f2c5f6258380ff4776af49db6119ac0c04342686705cfde94feebd7c9ca'), size_on_disk=3576881, blob_last_accessed=1756926776.065759, blob_last_modified=1756926778.8987432), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381066/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ae017c72cf3b91ea3fdd40f63a126f640e7049340baca502ff3228c6ee40a7b4'), size_on_disk=13139272, blob_last_accessed=1756926780.000737, blob_last_modified=1756926782.6217225), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417671/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/bced5a56092fea7de5754bebc33caaab643f27236f8d9020e7bc6b30b9045d6e'), size_on_disk=5347180, blob_last_accessed=1756926314.7704034, blob_last_modified=1756926315.8293986), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417846/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a3dc440aac6a69d75ec73242a30e59d8183023a2677f1576b5e73d037d6e6654'), size_on_disk=2680468, blob_last_accessed=1756926346.0562673, blob_last_modified=1756926346.7392642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417608/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d225f561536fca4d073d2418a32efc3df68e9666a3403c7d8b1ee0a520861322'), size_on_disk=873305, blob_last_accessed=1756926300.603465, blob_last_modified=1756926301.521461), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417729/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8383a0e86c2bfe15c46045980d6768eb4e7e3c4c157ff1ae7ce20144e245ca80'), size_on_disk=3058876, blob_last_accessed=1756926325.3113573, blob_last_modified=1756926326.103354), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381047/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/dda6f6ca669a0e5c9d34b548305cbeb6b82f1b70086e06d3604e7e096fd7fb25'), size_on_disk=6799004, blob_last_accessed=1756926776.8777544, blob_last_modified=1756926778.4717455), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417791/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8d2eb1bdd1a5cae023abb271e5858cf111879953cb0d6267f4ebf21a3d30c4ad'), size_on_disk=5232904, blob_last_accessed=1756926335.2033143, blob_last_modified=1756926336.417309), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417785/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/209799965b03585e1143e881d87c5cea80d37e06637d2fc3f7e1ad2eaadfd3bc'), size_on_disk=11557515, blob_last_accessed=1756926334.1813188, blob_last_modified=1756926335.1353147), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417829/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/63a9af789279180a07d8e8afe07bb0e9b8aa32c4340e01347a6f7ee809198308'), size_on_disk=5677879, blob_last_accessed=1756926342.904281, blob_last_modified=1756926343.7332773), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417664/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/91fc5dce8497d1fc5466fb5ac31e44a20b295d8f97825500f256f6830ea2f983'), size_on_disk=8203399, blob_last_accessed=1756926313.1894102, blob_last_modified=1756926314.6624038), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380946/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/62fe0084bf7a8f9f484e76366f203e1c68862a211d5d501c08e19b8f28678d6b'), size_on_disk=8090103, blob_last_accessed=1756926367.6871734, blob_last_modified=1756926368.7681687), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380955/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/369e0f1de51ea197e928618e562613eeed2ed59ce5c1389ce07ff6dd96d391b4'), size_on_disk=3596907, blob_last_accessed=1756926369.4561658, blob_last_modified=1756926370.2661622), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417638/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c8cface311cd55cd1542ed94d609da72cd2b3087422c28ad26c60e1d81a3e6bd'), size_on_disk=993699, blob_last_accessed=1756926308.4724307, blob_last_modified=1756926309.2634273), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417656/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/03f8707a323ef7563c8ec2d6352d24c6454d043c4bbbca417680df5c7db208f8'), size_on_disk=10029839, blob_last_accessed=1756926311.588417, blob_last_modified=1756926312.8104117), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380931/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/be287270314fed3dded81e0c24a66d8bd991b219fd6a157f1aeaa1690262e563'), size_on_disk=4324179, blob_last_accessed=1756926365.1611843, blob_last_modified=1756926366.1241803), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417717/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/689842dffacf7efa4ba6cd9b4314d195e89f9105eb71675f27019a9e25be63db'), size_on_disk=102777, blob_last_accessed=1756926323.9603631, blob_last_modified=1756926324.5793605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417906/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/34d3381dd45e579dcc298e91cc95174c4219cf548401b8f2392cf746fd508c5b'), size_on_disk=13795318, blob_last_accessed=1756926356.3642225, blob_last_modified=1756926357.640217), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417800/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8e563a7d64bf06e7fe26c2817271f626273eed4c7dd19db0b9fd8e14c25f73b3'), size_on_disk=9904697, blob_last_accessed=1756926336.7953074, blob_last_modified=1756926338.958298), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381058/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9dc97b5892fcc8af593c03fad2040396b9b10626a655319ad40b38acd75e1d7b'), size_on_disk=11534270, blob_last_accessed=1756926778.543745, blob_last_modified=1756926779.7317386), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381040/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a7c622a8ae1f5083eef26ecbcc2444055a9265b674ae80002e5468c94c0eb070'), size_on_disk=9682671, blob_last_accessed=1756926774.782766, blob_last_modified=1756926776.7967548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380951/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b9b68679dd2da94e4aa4852ae3de3ee5b823c1fb1483f4ece3eee5d947014785'), size_on_disk=1154119, blob_last_accessed=1756926368.8281684, blob_last_modified=1756926369.410166), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417929/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/85b194132215311994e48908799e31357d1094663a232e8170a4edcfa5a11eff'), size_on_disk=9155231, blob_last_accessed=1756926360.9862025, blob_last_modified=1756926361.9231985), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417751/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9a5733130de69115a731511dac8bf577fc5609a7f9649106ae9a12ffae7011ba'), size_on_disk=1660869, blob_last_accessed=1756926329.2023404, blob_last_modified=1756926329.9043374), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/64a119c8028083521d3872e2daca1545f2f8753eb8acfc751bf0c6d25803a3f8'), size_on_disk=2003211, blob_last_accessed=1756926376.794134, blob_last_modified=1756926377.2761319), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417675/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/79072181ebce12769f2f0a1971977d6691dde1841b53d7b54cf9d4f47f53b3dc'), size_on_disk=6478433, blob_last_accessed=1756926315.763399, blob_last_modified=1756926317.2643924), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380945/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/51efeee4439e81d920c4d9e860de321d90c72703600044e52215c774e1540650'), size_on_disk=6902367, blob_last_accessed=1756926367.356175, blob_last_modified=1756926368.6011696), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380980/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/e36f58da696f0827c72fed8647c2a45a0499fdc5d1859386dbf025388f07e16d'), size_on_disk=2288305, blob_last_accessed=1756926372.371153, blob_last_modified=1756926373.2051497), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417947/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/50bb9cc74904c9d3b6cc3ec8bc12c7ad1fb89204be6f89a863e703a94d224512'), size_on_disk=4023877, blob_last_accessed=1756926364.0161893, blob_last_modified=1756926364.9561853), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417875/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/8dc3634dae23cad7f562d15903341a67d3048929bd2198e53f71529b00a3508a'), size_on_disk=11490812, blob_last_accessed=1756926350.6762471, blob_last_modified=1756926351.7832425), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417776/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/01c4a97f92cd5e2919759d52d083eb824408a2fd7ade9413a060c6b20d233963'), size_on_disk=2759569, blob_last_accessed=1756926332.8593245, blob_last_modified=1756926333.629321), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417939/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/a0fc7804b026f95809f33087deb75f00beeb9635475b62864d3e58e948361bfa'), size_on_disk=9099632, blob_last_accessed=1756926362.9701939, blob_last_modified=1756926363.9411898), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417836/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/4fb964c54f4a37387c7844902d6171011a9a81efff244f365efcb156f630158f'), size_on_disk=3312893, blob_last_accessed=1756926344.2532752, blob_last_modified=1756926345.0702715), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417744/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/faf407f0b251cf5f58c8ac7becee4b995327fd9a7d6599c3045fde8a78e1e811'), size_on_disk=2548979, blob_last_accessed=1756926327.7843466, blob_last_modified=1756926328.7443423), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417802/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/ea68aebb727f77d846a145d43ec16fa2496e9c05d49a8791aa131b8e3e536452'), size_on_disk=4881005, blob_last_accessed=1756926337.2223055, blob_last_modified=1756926338.1273017), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417630/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/26fdbadf7c1252a8fd73ac85ea7430d942d2d11ddc43ea1b2590aaa008e08337'), size_on_disk=3015575, blob_last_accessed=1756926305.7104428, blob_last_modified=1756926306.7214384), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417812/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/9e8445f413d26051897f0fe3e7566b116a9a55062b78201990de334f17aecb31'), size_on_disk=19905089, blob_last_accessed=1756926339.0272977, blob_last_modified=1756926340.6232908), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6381050/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/f3c3bac37a1d4ec633edf53ba305c970eb3de412fd54dfbaf0cdf89f39574334'), size_on_disk=1518412, blob_last_accessed=1756926777.1687527, blob_last_modified=1756926778.028748), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417660/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/c394d7b0cfca3f3782dca86380e1a55903111d32efde98173af8a0b726ef5e7d'), size_on_disk=6441490, blob_last_accessed=1756926312.4744132, blob_last_modified=1756926313.4024093), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417763/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/91d8a58ead35fc52f7c2649ce9509a92236a20ca8c26f5f2ef4ba1a38e64afb1'), size_on_disk=230495, blob_last_accessed=1756926330.8943331, blob_last_modified=1756926331.4653306), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417880/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/b08db0a2399fc4e5b6f59c19b7af05b1294d0fac10b2c336b627234fa828f1be'), size_on_disk=9818886, blob_last_accessed=1756926351.5782433, blob_last_modified=1756926353.246236), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417741/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/734d5947cb26e877b6ae9e9adaf84575974a4fe2fb464ba11c4320906f17a42a'), size_on_disk=4566889, blob_last_accessed=1756926326.96635, blob_last_modified=1756926328.5653431), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417743/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/d4eb93049648498d2fdc1f125574429a4dfdec65e626ad9fa16bdf0d0cf599d8'), size_on_disk=2280534, blob_last_accessed=1756926327.462348, blob_last_modified=1756926329.1343408), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE209631/accession=GSM6380989/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/3c8b646955209386a1d97882163b7c9816463d2dea68fbed2cc960817ffeb332'), size_on_disk=12022144, blob_last_accessed=1756926373.7761471, blob_last_modified=1756926375.1511412), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417643/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/66dfa3feea4410ba34f5d5d37c7b01e1fa2cdd4ff38f9dfb10d20d55e40e449f'), size_on_disk=1012185, blob_last_accessed=1756926309.326427, blob_last_modified=1756926310.0784237), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417888/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/961a35161b04a0e892f46a0a90b6d4776d7fe07ef82fc7016c95b1a885c4274f'), size_on_disk=9651505, blob_last_accessed=1756926353.298236, blob_last_modified=1756926354.65023), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/snapshots/a987ef37c72fcd07b18828d320bc4305480daade/genome_map/series=GSE179430/accession=GSM5417674/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--barkai_compendium/blobs/5970db11c3efe2bba4eb50d07480cb0b46ed74dfc3ddf12d611ecd61f1d5fb68'), size_on_disk=4619941, blob_last_accessed=1756926315.5114, blob_last_modified=1756926317.630391)}), refs=frozenset({'main'}), last_modified=1756926783.3167186)}), last_accessed=1756926861.9192877, last_modified=1756926783.3167186), CachedRepoInfo(repo_id='BrentLab/kemmeren_2014', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014'), size_on_disk=646214296, nb_files=6, revisions=frozenset({CachedRevisionInfo(commit_hash='95a5f915ad49dfe4af75632861d9da69de2b525f', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/95a5f915ad49dfe4af75632861d9da69de2b525f'), size_on_disk=326978658, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/95a5f915ad49dfe4af75632861d9da69de2b525f/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/05a15ab9764763e5d1957023162e13b0068a5697'), size_on_disk=10286, blob_last_accessed=1767811670.0019596, blob_last_modified=1765415815.7076464), CachedFileInfo(file_name='kemmeren_2014.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/95a5f915ad49dfe4af75632861d9da69de2b525f/kemmeren_2014.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/12a63d4ccf5936c4a7ef7dc11f71287e1eb82b92901da87dd5b48e06f726c709'), size_on_disk=326968372, blob_last_accessed=1767811670.1439586, blob_last_modified=1766022970.1360188)}), refs=frozenset({'main'}), last_modified=1766022970.1360188), CachedRevisionInfo(commit_hash='a6c9a954a1def1774b68582f117ad1abc5864a07', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07'), size_on_disk=319226397, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/a29a64128c10ef9691ee773131972c61f720ba07'), size_on_disk=5007, blob_last_accessed=1756832529.5094638, blob_last_modified=1756832529.5404634), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756832529.5094638, blob_last_modified=1756832529.5434635), CachedFileInfo(file_name='kemmeren_2014.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/a6c9a954a1def1774b68582f117ad1abc5864a07/kemmeren_2014.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/3c463017444bd7690959c0d6fff0d0ebe2faa7b916dd9c44e305156f6c98b863'), size_on_disk=319218929, blob_last_accessed=1760552530.221978, blob_last_modified=1756832530.9644527)}), refs=frozenset(), last_modified=1756832530.9644527), CachedRevisionInfo(commit_hash='b76f0dfe8477b300000faecf16b7d315fbf59344', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/b76f0dfe8477b300000faecf16b7d315fbf59344'), size_on_disk=319228170, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/b76f0dfe8477b300000faecf16b7d315fbf59344/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/199dce9d01d3884716dd3a785a85751cb9b1de3e'), size_on_disk=9241, blob_last_accessed=1760552530.0869784, blob_last_modified=1758654056.5908144), CachedFileInfo(file_name='kemmeren_2014.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/snapshots/b76f0dfe8477b300000faecf16b7d315fbf59344/kemmeren_2014.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--kemmeren_2014/blobs/3c463017444bd7690959c0d6fff0d0ebe2faa7b916dd9c44e305156f6c98b863'), size_on_disk=319218929, blob_last_accessed=1760552530.221978, blob_last_modified=1756832530.9644527)}), refs=frozenset(), last_modified=1758654056.5908144)}), last_accessed=1767811670.1439586, last_modified=1766022970.1360188), CachedRepoInfo(repo_id='BrentLab/hu_2007_reimand_2010', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010'), size_on_disk=42682732, nb_files=4, revisions=frozenset({CachedRevisionInfo(commit_hash='31ccd35daf785420014a1346a7db064dd306d4f8', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8'), size_on_disk=42682732, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/886f4614dfd1ef33709d462908ae18608c13e8ba'), size_on_disk=2692, blob_last_accessed=1756844742.0937967, blob_last_modified=1756844742.1537964), CachedFileInfo(file_name='hu_2007_reimand_2010.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/hu_2007_reimand_2010.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/eb9be5a1f01907a81eb2ab4fbbee36ff8c50186f'), size_on_disk=63, blob_last_accessed=1756844741.7997973, blob_last_modified=1756844741.8557973), CachedFileInfo(file_name='hu_2007_reimand_2010.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/hu_2007_reimand_2010.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/47a15f3e23f8cb50af3217d00c6439d29a7422b5190116e4453a5f78cb22540d'), size_on_disk=42677516, blob_last_accessed=1756844742.1787965, blob_last_modified=1756844742.1477966), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/snapshots/31ccd35daf785420014a1346a7db064dd306d4f8/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hu_2007_reimand_2010/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756844741.8097973, blob_last_modified=1756844741.8767972)}), refs=frozenset({'main'}), last_modified=1756844742.1537964)}), last_accessed=1756844742.1787965, last_modified=1756844742.1537964), CachedRepoInfo(repo_id='BrentLab/callingcards', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards'), size_on_disk=49606009, nb_files=137, revisions=frozenset({CachedRevisionInfo(commit_hash='9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0'), size_on_disk=49588983, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/00e5602b87ac84670a98c081e6520ca744038f4e9b3a72a78cb61f5926be56b1'), size_on_disk=251087, blob_last_accessed=1763591392.5781758, blob_last_modified=1758648267.8286521), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6657/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/95ad2225152f51e2e31c030ff6708aeebccee5e548035d6f43c9cc3554bdfe5e'), size_on_disk=507730, blob_last_accessed=1763591393.163172, blob_last_modified=1758648269.1676583), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6764/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec87eef9e2e8cdf3c3a10ead28dcf1bfa76e94e12a42422a46c8559fb18207c2'), size_on_disk=75684, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.4036593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7295/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/19bd07f913e0af3460e6b4b5091971ceed8f4aa510cf18b82ec36dabb67c68b8'), size_on_disk=272619, blob_last_accessed=1763591393.1151721, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6567/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0e9713a94594dfcbc80134af52449e12ef207ca9149f5a96c43b6cb457b2431c'), size_on_disk=478809, blob_last_accessed=1763591392.6241755, blob_last_modified=1758648268.893657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6527/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/10081f44b1c2f5ad32489da51fc9ac5b5289b54688a7e78725db247a644b7e4d'), size_on_disk=494640, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.882657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7118/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/be8bd409b83526d5671501de6ef2980177955fdcc3a13d5e153d2593493b2a1e'), size_on_disk=346308, blob_last_accessed=1763591392.9281735, blob_last_modified=1758648270.1336627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5399/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b65d0aa7ddc98940f91f2ae5f07e1d3f81dda688792552b91fcc39fb4f9ededa'), size_on_disk=256729, blob_last_accessed=1763591392.5471761, blob_last_modified=1758648267.972653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6739/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/58b5ed63f2197f406dd3df6a569975d776722193007912718979eaed4d4e5f9c'), size_on_disk=399160, blob_last_accessed=1763591392.6551754, blob_last_modified=1758648269.331659), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7185/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/14f67668aca8d8f8aa2b8af98835da683a7fc3f22aa09cdcd7f943a37632391e'), size_on_disk=201650, blob_last_accessed=1763591393.2191715, blob_last_modified=1758648270.1076627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7211/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73d7747698fa1168c89f12c105bbe900d5969d27a82ae2302d0720d761fbfdab'), size_on_disk=298482, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.1276627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6861/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6955b1a1746fe41162ea1d7a098482bbfdb3d0e411c809b52d698e285627ce03'), size_on_disk=385103, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6146603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6808/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c71f5371e8c803cfc7fd507d0b3c11f7548db5d7a0fa42c68977169369ea036e'), size_on_disk=422880, blob_last_accessed=1763591392.9491735, blob_last_modified=1758648269.3916593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6872/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02fabe6e32f52fe6fca923beb0a6cc2898d991304e23a3e75f3ae8a6bcf361c6'), size_on_disk=274362, blob_last_accessed=1763591393.6231687, blob_last_modified=1758648269.6966608), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6073/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d68ca6411ba47eb63adad81e88fa1341a0ee269897268957ae936dcc9ba7788'), size_on_disk=450031, blob_last_accessed=1763591393.1341722, blob_last_modified=1758648268.3886547), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ac08e026f6d48c034f13a49f3531c90b6c070907'), size_on_disk=9519, blob_last_accessed=1763588875.9720669, blob_last_modified=1763588875.9700668), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=MAG001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/30b63d014686a103b5e7fc6571e902fa9c08630a96557cc12ec9dfcf60871306'), size_on_disk=347333, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.559651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7297/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4bd4088cbfcccdf96def916bb8fd9d673327d3e57c2a4a3e57299743ce4aca7'), size_on_disk=261344, blob_last_accessed=1763591393.588169, blob_last_modified=1758648270.6516652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7367/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a7778bb24506047963575fa96126157895c7f329996c729c3e7d6070595955c3'), size_on_disk=324361, blob_last_accessed=1763591392.1631787, blob_last_modified=1758648270.628665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6708/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/35520976736159652e3be8066e77b1371522f63a8b3564d9b0d9f5b1598a144d'), size_on_disk=253955, blob_last_accessed=1763591393.6881683, blob_last_modified=1758648269.1996584), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d4c0cbbf6075a8b5732e3cef2ba154d95d21aa0efc7e49d947cceebd8742b30'), size_on_disk=269007, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4106548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6689/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/90ea249116dc6a9be86974e1ec0acf79558dfd56a5666be2e05d2107b8c4ee60'), size_on_disk=366787, blob_last_accessed=1763591393.6661685, blob_last_modified=1758648269.1406581), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/11485fd7bb94d0093ce2508d541fea3ce64f934197d8a5f7bb26ecbdb59f8dbb'), size_on_disk=124642, blob_last_accessed=1763591392.3321776, blob_last_modified=1758648267.0666487), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6983/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4ee29f84ab279c520521a6c5a5eed9e5e5581b2e39378bdd898eec67d13654f'), size_on_disk=410123, blob_last_accessed=1763591392.6751752, blob_last_modified=1758648269.8636615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e20f616f6e340b76f6446d2cdffb6cc9feb81297bd0307733f835acf4142cfd'), size_on_disk=269523, blob_last_accessed=1763591392.3461773, blob_last_modified=1758648267.2696495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7266/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9aea2d6f5606d24e1999d450379b1f8c8b9d80eded9e784fb729faa516a27026'), size_on_disk=324480, blob_last_accessed=1763591392.0401795, blob_last_modified=1758648270.379664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7151/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9969e4607d6bf42424f5d2ce8f0d16e3d2d89d069cae6d75997e99c88a68dc77'), size_on_disk=275572, blob_last_accessed=1763591393.7231681, blob_last_modified=1758648270.1476629), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6532/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3041f19cf0dcb3e0bd9b1178994351a22f3297ccd43c5e291df8bfe0ad8d5d02'), size_on_disk=516437, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.8236568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6055/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/31bb70b4c90654ecb276664f5c914679322b24985ec66d5b396a99f253ebfa17'), size_on_disk=377015, blob_last_accessed=1763591393.5701692, blob_last_modified=1758648268.3076544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/74cbce50c258bdddb7e42c8fd5b0b2a9da74fe3cf89a9ab5a53ee4fde7c1dee6'), size_on_disk=186142, blob_last_accessed=1763591392.842174, blob_last_modified=1758648266.9966483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/71a33d11c3ff714ce0730145c740eec4fe88812c6136b8d10b86fa8d4054991f'), size_on_disk=186500, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7286518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5690/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4831e3e037d421a14efbe1da8c369015ec7d01df351bb27f0ef0400c8b94c693'), size_on_disk=311754, blob_last_accessed=1763591392.9021738, blob_last_modified=1758648268.211654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6545/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/260c4a280f52024cb12d44559a6189641ee02efbaf20168435196efbfc69d741'), size_on_disk=404812, blob_last_accessed=1763591392.3761773, blob_last_modified=1758648268.8256567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6573/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e435989051765cf2955d8cd733904e8b60e5f3121e1c2fdc987f4e6f71fe3970'), size_on_disk=265022, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9586573), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7351/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cc708adf186f28db09dfdd4ac43865a90482b2192306949eab01492598cf23e7'), size_on_disk=198070, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.610665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a76f0849e518faedfe50e6f0c76908a3f6beed34fd0b607603db1859493595d0'), size_on_disk=292312, blob_last_accessed=1763587658.6168654, blob_last_modified=1758648266.7676473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=composite/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1f8adf8fb4992283714df8587126211d54e6a0580b8be88c1d5e911196aed783'), size_on_disk=4814573, blob_last_accessed=1763591392.0561793, blob_last_modified=1758648268.0256531), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e5b3174998823af40d055f46ca06e7bb44601cbe97b133062cd3e0491dd30ac2'), size_on_disk=323308, blob_last_accessed=1763591393.153172, blob_last_modified=1758648267.2686496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6437/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/556084dbbc174db5450872d6f5e66cc72091380ea32006977dd176e23c105a7f'), size_on_disk=389840, blob_last_accessed=1763591392.714175, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0230df4f1d3748337e5315bb1d22a0373fdab95f9bc900041b0e34966d868718'), size_on_disk=311116, blob_last_accessed=1763591392.9241736, blob_last_modified=1758648267.2506495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6454/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c35b60861d2fc7a3f4256869e14e629aed07cd2ccce76608578ceb8c6ec423e1'), size_on_disk=498326, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.7226562), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=KB001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38c939d54d3ed77a09650d2ccf00a6ed0e20390b8be722f1c3ed7881a5904a60'), size_on_disk=351677, blob_last_accessed=1763591392.9151735, blob_last_modified=1758648267.5566509), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ef036874e53ee98dfe1b5d84c42749fc40da9279cd573174321ac83201c1651'), size_on_disk=349762, blob_last_accessed=1763591393.0791724, blob_last_modified=1758648267.4966507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6923/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/454ca6a52d4b305eddc4b6967ef9604be4861269fdcfff419883daed9b2fe48b'), size_on_disk=337423, blob_last_accessed=1763591393.5031695, blob_last_modified=1758648269.6476605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5614/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1aa702ff57d34ebed961043e0efae064bfdb62f589cbb515311ae8ea37ba8c76'), size_on_disk=429423, blob_last_accessed=1763591393.4671698, blob_last_modified=1758648268.0406532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6853/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ed47ecb2aa870fdbf12920fd4486933262c0a24318c9c8c5531e359a3c416c90'), size_on_disk=515114, blob_last_accessed=1763591393.2661712, blob_last_modified=1758648269.4276595), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6572/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3488eaac5e4b9b3418b909138e6b50e3f9ebaf3e7345a4a10b6ced18b66152e4'), size_on_disk=440579, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9506574), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6731/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/23ecd71c4e3c1c547295bb756d4654eb413ff288d10118ab286cd21ccd95b666'), size_on_disk=346187, blob_last_accessed=1763591392.1871786, blob_last_modified=1758648269.1906583), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d50648ed5c9f593f830aff661d13b3d34bd14f0bf93b16169df4765c9f47fc8'), size_on_disk=295924, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7426472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR007B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6f0620b102e7cbfadced7d5530545a185036b53118d3e2b7f074c56403d80f4b'), size_on_disk=263185, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7616518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6924/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5e9b3a94d73c1720e6b05ea55000054905a8672edea7f950736ce3fe191f8faf'), size_on_disk=412939, blob_last_accessed=1763591393.748168, blob_last_modified=1758648269.7066607), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7063/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9427d8f8c54e3c0cc9d720958ef15d3859f00cdfb678559e0268387a36a8ef37'), size_on_disk=404051, blob_last_accessed=1763591392.5301762, blob_last_modified=1758648269.8976617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e871cf06f596bfe74896b62aa28d6b1e6f2f0b8408630fb146c7e29cfd88fbf0'), size_on_disk=232721, blob_last_accessed=1763591392.7871745, blob_last_modified=1758648267.991653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eb61968b5ec5eadd9087e42ead29333f91195dd5b3f9c5a616b41e94ca23c494'), size_on_disk=330245, blob_last_accessed=1763591393.6761684, blob_last_modified=1758648269.710661), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6b92129c2fb99028b6cab9509cf1a4c40b142109cde34c9a130d375001dcd211'), size_on_disk=267273, blob_last_accessed=1763591392.7811744, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6374/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e410602f56bc94d9831a2bc8bdb865ee4ebe9862a65b603165b655fbcc4b9914'), size_on_disk=441531, blob_last_accessed=1763591393.1891718, blob_last_modified=1758648268.5356555), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5a70fa6f22aeda3246d17646cc4ec7c5a46e1993f9981b19d9fd5a8cc99bf11f'), size_on_disk=308858, blob_last_accessed=1763591391.9411802, blob_last_modified=1758648267.0186484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6738/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e6d612d16538a06c985f1134f3fdfd367944b2a85cedfb8b0ddad1679004c940'), size_on_disk=333560, blob_last_accessed=1763591392.8651738, blob_last_modified=1758648269.3676593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5986/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f1179c75ac6e91de03b67a73882f3b9c07c8e435d938d3059de489403adde7a'), size_on_disk=328749, blob_last_accessed=1763591392.691175, blob_last_modified=1758648268.228654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d3e36418bd46acf9537a8f864b1a1361a062d72fec2b093789b88ed655ec117'), size_on_disk=377393, blob_last_accessed=1763591393.0451727, blob_last_modified=1758648267.0136483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6640/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48339729112d22aa5aabf411674bfb90a567ec4c7a07ea7930738645a96823ef'), size_on_disk=329042, blob_last_accessed=1763591392.6391754, blob_last_modified=1758648269.090658), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c9b19726dc45896e4f86a5acc52d07cc422f895bfaaaddb332aaceedff36560c'), size_on_disk=178793, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0266485), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6770/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/082f14f7b0d5fc47e088efc40ae85a8ec41f99437c66da96ca69af210bce61cc'), size_on_disk=318118, blob_last_accessed=1763591392.8081744, blob_last_modified=1758648269.3776593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/33d7303ed077f489ee28021daca0bbc859feb1cfd383dfc9a856ea13994801a9'), size_on_disk=331877, blob_last_accessed=1763591393.292171, blob_last_modified=1758648268.2226539), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5654/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38961219eb66dec31505ebcc5088f16684be9891b96fffa8c9a13cd3f13254d0'), size_on_disk=447782, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.1876538), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7240/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4460d188b4cd3b362f33acf0f0b5312a267b37f8fb2dab4b2577c6c3bbb1231'), size_on_disk=307039, blob_last_accessed=1763591392.6101756, blob_last_modified=1758648270.403664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bd2017e75947ead2c06f9d1936347f7fdcfac7fbff620e0b39077208a02c3bd9'), size_on_disk=303094, blob_last_accessed=1763591393.1271722, blob_last_modified=1758648269.9296618), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7155/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/521fc1bea6963640645c21cbdb8d1de07793f11213c23ac71d249f3252b32f54'), size_on_disk=304554, blob_last_accessed=1763591393.5841691, blob_last_modified=1758648270.1366627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6021/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eea0d101dd58c9619b41a94009fc0dff8156d2d36d7f2b88bb653e96123a032e'), size_on_disk=439545, blob_last_accessed=1763591393.5531693, blob_last_modified=1758648268.258654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/62e9a627f20607bc4887ebc9277131f974ec80ff39ac386775f4b8f2f5cb9d1d'), size_on_disk=346782, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7476518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fc9311b65abb851fbbfd8bb0c0765af59069659f5a715aee546a8644b0af4ec2'), size_on_disk=210948, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648267.6766515), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dcb33135a9d854d17a6b80348f6bee8becea9ca3808583a034f89c5e4f460c54'), size_on_disk=218643, blob_last_accessed=1763591393.4831698, blob_last_modified=1758648267.9636528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7269/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5bae329d2318610f627fc562a5163253c7d61e0ef83c53808b220133b2299264'), size_on_disk=203263, blob_last_accessed=1763591392.541176, blob_last_modified=1758648270.4096642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR006B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fcc5e886ec7c6363a5ab51b03d3da189ac3e0cde0a0f79d4f0c46043a6b61838'), size_on_disk=253857, blob_last_accessed=1763591392.0501795, blob_last_modified=1758648267.785652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/646c843611bf88c7f5dcd5b1a664d724c3b24b6434cc52d9a54de62ccaf6c542'), size_on_disk=255949, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.4506505), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7243/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cb59e645fff2683db692b4d2f514602ba210271b632cd113853febe941c6485f'), size_on_disk=281696, blob_last_accessed=1763591392.701175, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/530b5650d91dae3bf169f967af8e800611e3e01daeb1811d6fd21c2612ac4ad9'), size_on_disk=315170, blob_last_accessed=1763591393.6121688, blob_last_modified=1758648266.7956474), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7331/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f159a9811b8ac1b5e252b52427979a3bddce2e7131b13f84046b31e47f7fa399'), size_on_disk=242821, blob_last_accessed=1763591393.2501714, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5301_5088/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/65ef775934339b80eda59a141cf791a74e1c6f12513d8143307cebe2f11c3738'), size_on_disk=410106, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.9516528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7332/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f7f62ee008fa2b5cc688cbd80c888f1bf9da538caa245b63581520d485338a99'), size_on_disk=309595, blob_last_accessed=1763591393.1021724, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5610/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a86512ae1000a994c86e9aebaf30cf9ce1a59160155f3dc1afa6cbde0a1fc47e'), size_on_disk=307382, blob_last_accessed=1763591393.5291693, blob_last_modified=1758648267.994653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7370/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ff4dce26afb34b291220c17625be4a28078f9721a8b000ea177c4add3bf1a039'), size_on_disk=241010, blob_last_accessed=1763591393.7701678, blob_last_modified=1758648270.6726654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ac8c590c19e7b95b9aac571b1394976039f25fdd66e62a68ecb99925433f8e54'), size_on_disk=355706, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0226483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6863/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec534c91cae4413d30477014131d39b51c9c236dfbf150874bd21ec3de821613'), size_on_disk=389025, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648269.6376605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=MAG002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f7f35257d4c1f7fa5812ea31c1de791e08ab5e3a4e7ca42b2bb12b2c64f7ed7'), size_on_disk=490174, blob_last_accessed=1763591392.829174, blob_last_modified=1758648267.583651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f427ea0f4ded3b6ff841153d8ad87d72a26a964c189246fb6fd5c439bd8b0c1e'), size_on_disk=353623, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.812652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5801/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/12e91a415cc2a149af4c34c3c8b032821ff301e2828dfb9f4636bd6b2913ea3d'), size_on_disk=360485, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.1636536), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4b2ff192aa4808cb50b1c1b1667060f98505dd63c1aa58c7d8354e41194957e4'), size_on_disk=289767, blob_last_accessed=1763591393.4871697, blob_last_modified=1758648266.7706473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dc74862f02ff12b8c906046f1f03953d33f5516091d58397b0f9a0fc05bb4b5c'), size_on_disk=315435, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.5006506), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7258/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7b1860ba3be7dbf053203fa5690da2002f7841186940231acea6064e70f0bdc8'), size_on_disk=171331, blob_last_accessed=1763591392.6171756, blob_last_modified=1758648270.3746638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7283/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0dc91bfacca82c5ed23de7757d59f9f808d541d96dd79aab56f0c4252e7aabf3'), size_on_disk=265146, blob_last_accessed=1763591392.7731745, blob_last_modified=1758648270.401664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6651/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7644af4cc956ea08809ab6c80c65095e3fdbd36e056040c0eaded3773d546359'), size_on_disk=559951, blob_last_accessed=1763591393.6361687, blob_last_modified=1758648269.1856585), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6855/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48e48378d24404e926bc1c44b10af60d4b12f82124ff2f80bc0b69b4a0f98740'), size_on_disk=609248, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6556606), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6390/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f324cdf73148c8b0fa23456bc4f51cff3032d08ed42e44dd9fcc4815a6d969c'), size_on_disk=560601, blob_last_accessed=1763591392.7391748, blob_last_modified=1758648268.6206558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6061/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73e03845d0fbff1a5931e4f792e2c1965ade02fc622ffc08e05c267ac6cd3231'), size_on_disk=394930, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.447655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5961/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/49cac5c2df1adf790a9ac0209db6c0945de50b8191d403e8b1ad386ee2dcb35e'), size_on_disk=321368, blob_last_accessed=1763591392.560176, blob_last_modified=1758648268.215654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6635/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6c5b7f1fda3a7f5679870f438d13a45558f7f8da0f479c5144d7751bc8f7fbbc'), size_on_disk=420658, blob_last_accessed=1763591391.95018, blob_last_modified=1758648269.3956594), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7137/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/703fe9f8552adcbdfb5e640509cb62df2fa734b43fe1e98fc1fd342415f823d7'), size_on_disk=515398, blob_last_accessed=1763589053.7698598, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7237/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/110a743162ace7792a44dfe48d7afd40bb67c45c722038c981548477ebc5d8fe'), size_on_disk=313207, blob_last_accessed=1763591393.143172, blob_last_modified=1758648270.1426628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6551/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c3729692991cdcafe9baa3d2805b3866f72fe9bc89ba22c6d2cdae4d5b5bbf5f'), size_on_disk=545477, blob_last_accessed=1763591393.0611725, blob_last_modified=1758648268.8376567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7cb5cca74f1bda2f8f1c8ef962e54d34228361eaa9dd8b21e0d1dd7092ff8164'), size_on_disk=229703, blob_last_accessed=1763591392.555176, blob_last_modified=1758648267.0126483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6177/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b91ffef1dfcdf7952df46956d76a4b364aa0d8342453636cf09e6a343607184f'), size_on_disk=387618, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.424655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6506/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/986d9d3b4ed064d66ebaa0f2502b933f1650a20ac4bbbba804e2cde03b9454c0'), size_on_disk=378407, blob_last_accessed=1763591391.97318, blob_last_modified=1758648268.7656565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fa1a44bd33df22ec6b5a8baf3be17e51a11522b5fdd545a38e6e163cc5ae8207'), size_on_disk=241911, blob_last_accessed=1763591392.572176, blob_last_modified=1758648267.5226507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6513/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c8f9c31704c2f8732b4668e1e6410034b8adf2ada39ff55935aa0199e920d055'), size_on_disk=507367, blob_last_accessed=1763591392.1451788, blob_last_modified=1758648268.8406568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/128015caa722289251a45aa3d56cabac6b5946eb4e1be16eb950555fb24ac413'), size_on_disk=172328, blob_last_accessed=1763591393.735168, blob_last_modified=1758648267.2826495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6959/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c0cb6c2ec2bda19b0c1da5d5fc2229d0b6d6987d75646fa230c0c14d591cf1f1'), size_on_disk=537588, blob_last_accessed=1763591392.851174, blob_last_modified=1758648269.8686616), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=DS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1fa09a5a6e6e69198501d994050adfcad7ff39a3cc2fe94e7ea177414e1bc968'), size_on_disk=303532, blob_last_accessed=1763591393.7091682, blob_last_modified=1758648267.2776496), CachedFileInfo(file_name='annotated_features_meta.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features_meta.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/2be4b693d333698f7ada7ef40ffaf927c0160d7975ebcf6e215cbc4ba1be1604'), size_on_disk=11813, blob_last_accessed=1763587594.724117, blob_last_modified=1758648264.795638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6920/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/45122fcfb3abc3a43631023712c224dc6e00dba566fb2e15deb4ab4501ad2538'), size_on_disk=446998, blob_last_accessed=1763591392.8881738, blob_last_modified=1758648269.6416605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ddd58117dbedfe7e9dc3fd946187e7bd5d88fe8b0f38999c6e4b5660938bb787'), size_on_disk=359739, blob_last_accessed=1763591393.5981688, blob_last_modified=1758648267.2726495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6798/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/899cde55032cd593be0013e1f6ff33fe9a12f4616477b1410d86fae1b2008a09'), size_on_disk=342376, blob_last_accessed=1763591392.7951744, blob_last_modified=1758648269.4006593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6140/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/66c2da17957d7e6a8b159d3573879ac779d0094e35150b51e2643f7ddee36e1f'), size_on_disk=332958, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4236548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7256/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1af21cbdd7f6f6effff15a1b061a48f85132d582f08c8fd42e04ea58bde7ec00'), size_on_disk=206710, blob_last_accessed=1763591392.586176, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6677/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4ee6e1d3e2b9a9bbcc8851048d2b16f4aea6a1e2f752afb9e0bf75a56adf7042'), size_on_disk=322147, blob_last_accessed=1763591392.393177, blob_last_modified=1758648269.0826578), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6421/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1e3bacd3bcf1fcb46cfc5f9ab66c2af57e190f3d006031e7ef37864fbd36aaca'), size_on_disk=495452, blob_last_accessed=1763591392.2161784, blob_last_modified=1758648268.6366558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9f7663ccf72b0681ae5866c13500fbde98f35877fd677ac0d49c17447d17d6ce'), size_on_disk=332767, blob_last_accessed=1763591392.1731787, blob_last_modified=1758648266.744647), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6965/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9ae4546d58672f0632757c94b595af8b7c40da215611783fdc1a65000075ec98'), size_on_disk=509677, blob_last_accessed=1763591392.2731779, blob_last_modified=1758648269.933662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7115/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/205443fd87ce5c793dab86eb75c21dab7dc2ae2edac41d7e6314cfe491bdb01d'), size_on_disk=321850, blob_last_accessed=1763591393.2331715, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4d098c33564f99ec6aed77d585d1f6e871806f441f099790eef4d6478e322af'), size_on_disk=264459, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7486472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS011/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ea7cb0559b342dd0ec278e7d5846a5ba0c0d867ea04d545f72e0d1c9472a0cc'), size_on_disk=363979, blob_last_accessed=1763591392.9791732, blob_last_modified=1758648267.0116484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_5617/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bc01241e8363373c995a0fa7439aa1e432a60eb8a7805124fff302311f638361'), size_on_disk=351950, blob_last_accessed=1763591393.6541686, blob_last_modified=1758648268.1026535), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7276/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4e0d576cc4a6a85be64f2198e5001a9f5b02b506072df6f695cfdfc719cb041'), size_on_disk=204036, blob_last_accessed=1763591392.987173, blob_last_modified=1758648270.4646642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6354/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/80b129a46defc7b06e472a35935bfaf410bc5e456475b2885feb2c8a4934a81b'), size_on_disk=173225, blob_last_accessed=1763591393.4981697, blob_last_modified=1758648268.4986553), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=GJ004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/d1cd38451bc41ef14dbc1566c4704b004b2c0661f667210c81dba09bec90540f'), size_on_disk=244487, blob_last_accessed=1763591393.6301687, blob_last_modified=1758648267.3246498), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1c8a8cb23b1ec073827d6f5343a3bf03a9d7a04cf2ae3fd3ea4cc5ac37673b38'), size_on_disk=342987, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.6226559), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6448/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/182dc134914a5ddb02992ab6a4f1c12d2450aa331820bf7bd95693dc999bcfae'), size_on_disk=390210, blob_last_accessed=1763591392.9591732, blob_last_modified=1758648268.672656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e91fd764a83947c14f35d7a2d8c0d156c0a61a32a1da96273718ef4be720787'), size_on_disk=361855, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.7196517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02313d4d85859a7353e56d89b6e14bcec5160849b5c0089ccbecaa1cda5012cf'), size_on_disk=301213, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4356549), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=PR002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bbe701ffd546a2f4359f821262f7b44232bdc328d9d4719bfdaa8f2aedc6d2e9'), size_on_disk=346079, blob_last_accessed=1763591393.0861723, blob_last_modified=1758648267.589651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS006b/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7eedae811a5b3dd4389e7e814d35b022bfaf01fc4d2cfb47be6e6786d6d58c64'), size_on_disk=315912, blob_last_accessed=1763591393.2571712, blob_last_modified=1758648266.7546473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6443/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4deb4634e8b4bf141a2410e94b2d99e2b31a0e8f037de9a1c6e14696025bffae'), size_on_disk=494775, blob_last_accessed=1763591392.991173, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=CS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4e8332a5db6260f6357f940e62e3e7b622785f386e81cdb9c75b538d318f4a6'), size_on_disk=158978, blob_last_accessed=1763591393.5471692, blob_last_modified=1758648266.7626472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_6954/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/aeed3c55d81a4eb0c568486e476211c599ce2bf37f9dff22c7dfca23f5d80495'), size_on_disk=476054, blob_last_accessed=1763591393.011173, blob_last_modified=1758648269.943662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/9f504054065d0aea4b3e52ed5c33c3b4c3fb69e0/annotated_features/batch=run_7209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ab338795b8bbf334650386a123f6e0df41363cb5dcbb778d485ce58804e96b29'), size_on_disk=375506, blob_last_accessed=1763591392.9711733, blob_last_modified=1758648270.1326628)}), refs=frozenset(), last_modified=1763588875.9700668), CachedRevisionInfo(commit_hash='27becd0ab489579a50c38ba896dd03680c5fd2da', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da'), size_on_disk=49585603, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d4c0cbbf6075a8b5732e3cef2ba154d95d21aa0efc7e49d947cceebd8742b30'), size_on_disk=269007, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4106548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7237/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/110a743162ace7792a44dfe48d7afd40bb67c45c722038c981548477ebc5d8fe'), size_on_disk=313207, blob_last_accessed=1763591393.143172, blob_last_modified=1758648270.1426628), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a88564c359f73b5ac3f70a4635f33a1ec57f527f'), size_on_disk=6139, blob_last_accessed=1763587733.4135532, blob_last_modified=1758648264.1986353), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9f7663ccf72b0681ae5866c13500fbde98f35877fd677ac0d49c17447d17d6ce'), size_on_disk=332767, blob_last_accessed=1763591392.1731787, blob_last_modified=1758648266.744647), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=composite/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1f8adf8fb4992283714df8587126211d54e6a0580b8be88c1d5e911196aed783'), size_on_disk=4814573, blob_last_accessed=1763591392.0561793, blob_last_modified=1758648268.0256531), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7297/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4bd4088cbfcccdf96def916bb8fd9d673327d3e57c2a4a3e57299743ce4aca7'), size_on_disk=261344, blob_last_accessed=1763591393.588169, blob_last_modified=1758648270.6516652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/128015caa722289251a45aa3d56cabac6b5946eb4e1be16eb950555fb24ac413'), size_on_disk=172328, blob_last_accessed=1763591393.735168, blob_last_modified=1758648267.2826495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4b2ff192aa4808cb50b1c1b1667060f98505dd63c1aa58c7d8354e41194957e4'), size_on_disk=289767, blob_last_accessed=1763591393.4871697, blob_last_modified=1758648266.7706473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7063/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9427d8f8c54e3c0cc9d720958ef15d3859f00cdfb678559e0268387a36a8ef37'), size_on_disk=404051, blob_last_accessed=1763591392.5301762, blob_last_modified=1758648269.8976617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6454/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c35b60861d2fc7a3f4256869e14e629aed07cd2ccce76608578ceb8c6ec423e1'), size_on_disk=498326, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.7226562), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e91fd764a83947c14f35d7a2d8c0d156c0a61a32a1da96273718ef4be720787'), size_on_disk=361855, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.7196517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7351/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cc708adf186f28db09dfdd4ac43865a90482b2192306949eab01492598cf23e7'), size_on_disk=198070, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.610665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6532/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3041f19cf0dcb3e0bd9b1178994351a22f3297ccd43c5e291df8bfe0ad8d5d02'), size_on_disk=516437, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.8236568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6448/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/182dc134914a5ddb02992ab6a4f1c12d2450aa331820bf7bd95693dc999bcfae'), size_on_disk=390210, blob_last_accessed=1763591392.9591732, blob_last_modified=1758648268.672656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5654/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38961219eb66dec31505ebcc5088f16684be9891b96fffa8c9a13cd3f13254d0'), size_on_disk=447782, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.1876538), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/71a33d11c3ff714ce0730145c740eec4fe88812c6136b8d10b86fa8d4054991f'), size_on_disk=186500, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7286518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e871cf06f596bfe74896b62aa28d6b1e6f2f0b8408630fb146c7e29cfd88fbf0'), size_on_disk=232721, blob_last_accessed=1763591392.7871745, blob_last_modified=1758648267.991653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5986/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f1179c75ac6e91de03b67a73882f3b9c07c8e435d938d3059de489403adde7a'), size_on_disk=328749, blob_last_accessed=1763591392.691175, blob_last_modified=1758648268.228654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5614/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1aa702ff57d34ebed961043e0efae064bfdb62f589cbb515311ae8ea37ba8c76'), size_on_disk=429423, blob_last_accessed=1763591393.4671698, blob_last_modified=1758648268.0406532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a76f0849e518faedfe50e6f0c76908a3f6beed34fd0b607603db1859493595d0'), size_on_disk=292312, blob_last_accessed=1763587658.6168654, blob_last_modified=1758648266.7676473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7185/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/14f67668aca8d8f8aa2b8af98835da683a7fc3f22aa09cdcd7f943a37632391e'), size_on_disk=201650, blob_last_accessed=1763591393.2191715, blob_last_modified=1758648270.1076627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7cb5cca74f1bda2f8f1c8ef962e54d34228361eaa9dd8b21e0d1dd7092ff8164'), size_on_disk=229703, blob_last_accessed=1763591392.555176, blob_last_modified=1758648267.0126483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5961/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/49cac5c2df1adf790a9ac0209db6c0945de50b8191d403e8b1ad386ee2dcb35e'), size_on_disk=321368, blob_last_accessed=1763591392.560176, blob_last_modified=1758648268.215654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5301_5088/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/65ef775934339b80eda59a141cf791a74e1c6f12513d8143307cebe2f11c3738'), size_on_disk=410106, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.9516528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5a70fa6f22aeda3246d17646cc4ec7c5a46e1993f9981b19d9fd5a8cc99bf11f'), size_on_disk=308858, blob_last_accessed=1763591391.9411802, blob_last_modified=1758648267.0186484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dc74862f02ff12b8c906046f1f03953d33f5516091d58397b0f9a0fc05bb4b5c'), size_on_disk=315435, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.5006506), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6872/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02fabe6e32f52fe6fca923beb0a6cc2898d991304e23a3e75f3ae8a6bcf361c6'), size_on_disk=274362, blob_last_accessed=1763591393.6231687, blob_last_modified=1758648269.6966608), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6527/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/10081f44b1c2f5ad32489da51fc9ac5b5289b54688a7e78725db247a644b7e4d'), size_on_disk=494640, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.882657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e20f616f6e340b76f6446d2cdffb6cc9feb81297bd0307733f835acf4142cfd'), size_on_disk=269523, blob_last_accessed=1763591392.3461773, blob_last_modified=1758648267.2696495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bbe701ffd546a2f4359f821262f7b44232bdc328d9d4719bfdaa8f2aedc6d2e9'), size_on_disk=346079, blob_last_accessed=1763591393.0861723, blob_last_modified=1758648267.589651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6689/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/90ea249116dc6a9be86974e1ec0acf79558dfd56a5666be2e05d2107b8c4ee60'), size_on_disk=366787, blob_last_accessed=1763591393.6661685, blob_last_modified=1758648269.1406581), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6677/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4ee6e1d3e2b9a9bbcc8851048d2b16f4aea6a1e2f752afb9e0bf75a56adf7042'), size_on_disk=322147, blob_last_accessed=1763591392.393177, blob_last_modified=1758648269.0826578), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ab338795b8bbf334650386a123f6e0df41363cb5dcbb778d485ce58804e96b29'), size_on_disk=375506, blob_last_accessed=1763591392.9711733, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6920/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/45122fcfb3abc3a43631023712c224dc6e00dba566fb2e15deb4ab4501ad2538'), size_on_disk=446998, blob_last_accessed=1763591392.8881738, blob_last_modified=1758648269.6416605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0230df4f1d3748337e5315bb1d22a0373fdab95f9bc900041b0e34966d868718'), size_on_disk=311116, blob_last_accessed=1763591392.9241736, blob_last_modified=1758648267.2506495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6573/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e435989051765cf2955d8cd733904e8b60e5f3121e1c2fdc987f4e6f71fe3970'), size_on_disk=265022, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9586573), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5801/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/12e91a415cc2a149af4c34c3c8b032821ff301e2828dfb9f4636bd6b2913ea3d'), size_on_disk=360485, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.1636536), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7370/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ff4dce26afb34b291220c17625be4a28078f9721a8b000ea177c4add3bf1a039'), size_on_disk=241010, blob_last_accessed=1763591393.7701678, blob_last_modified=1758648270.6726654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c9b19726dc45896e4f86a5acc52d07cc422f895bfaaaddb332aaceedff36560c'), size_on_disk=178793, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0266485), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6421/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1e3bacd3bcf1fcb46cfc5f9ab66c2af57e190f3d006031e7ef37864fbd36aaca'), size_on_disk=495452, blob_last_accessed=1763591392.2161784, blob_last_modified=1758648268.6366558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ef036874e53ee98dfe1b5d84c42749fc40da9279cd573174321ac83201c1651'), size_on_disk=349762, blob_last_accessed=1763591393.0791724, blob_last_modified=1758648267.4966507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7115/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/205443fd87ce5c793dab86eb75c21dab7dc2ae2edac41d7e6314cfe491bdb01d'), size_on_disk=321850, blob_last_accessed=1763591393.2331715, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ac8c590c19e7b95b9aac571b1394976039f25fdd66e62a68ecb99925433f8e54'), size_on_disk=355706, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0226483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6437/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/556084dbbc174db5450872d6f5e66cc72091380ea32006977dd176e23c105a7f'), size_on_disk=389840, blob_last_accessed=1763591392.714175, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7155/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/521fc1bea6963640645c21cbdb8d1de07793f11213c23ac71d249f3252b32f54'), size_on_disk=304554, blob_last_accessed=1763591393.5841691, blob_last_modified=1758648270.1366627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5610/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a86512ae1000a994c86e9aebaf30cf9ce1a59160155f3dc1afa6cbde0a1fc47e'), size_on_disk=307382, blob_last_accessed=1763591393.5291693, blob_last_modified=1758648267.994653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4d098c33564f99ec6aed77d585d1f6e871806f441f099790eef4d6478e322af'), size_on_disk=264459, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7486472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7211/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73d7747698fa1168c89f12c105bbe900d5969d27a82ae2302d0720d761fbfdab'), size_on_disk=298482, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.1276627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6354/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/80b129a46defc7b06e472a35935bfaf410bc5e456475b2885feb2c8a4934a81b'), size_on_disk=173225, blob_last_accessed=1763591393.4981697, blob_last_modified=1758648268.4986553), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6177/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b91ffef1dfcdf7952df46956d76a4b364aa0d8342453636cf09e6a343607184f'), size_on_disk=387618, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.424655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1c8a8cb23b1ec073827d6f5343a3bf03a9d7a04cf2ae3fd3ea4cc5ac37673b38'), size_on_disk=342987, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.6226559), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=MAG001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/30b63d014686a103b5e7fc6571e902fa9c08630a96557cc12ec9dfcf60871306'), size_on_disk=347333, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.559651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7276/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4e0d576cc4a6a85be64f2198e5001a9f5b02b506072df6f695cfdfc719cb041'), size_on_disk=204036, blob_last_accessed=1763591392.987173, blob_last_modified=1758648270.4646642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6651/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7644af4cc956ea08809ab6c80c65095e3fdbd36e056040c0eaded3773d546359'), size_on_disk=559951, blob_last_accessed=1763591393.6361687, blob_last_modified=1758648269.1856585), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7367/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a7778bb24506047963575fa96126157895c7f329996c729c3e7d6070595955c3'), size_on_disk=324361, blob_last_accessed=1763591392.1631787, blob_last_modified=1758648270.628665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6770/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/082f14f7b0d5fc47e088efc40ae85a8ec41f99437c66da96ca69af210bce61cc'), size_on_disk=318118, blob_last_accessed=1763591392.8081744, blob_last_modified=1758648269.3776593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/74cbce50c258bdddb7e42c8fd5b0b2a9da74fe3cf89a9ab5a53ee4fde7c1dee6'), size_on_disk=186142, blob_last_accessed=1763591392.842174, blob_last_modified=1758648266.9966483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7118/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/be8bd409b83526d5671501de6ef2980177955fdcc3a13d5e153d2593493b2a1e'), size_on_disk=346308, blob_last_accessed=1763591392.9281735, blob_last_modified=1758648270.1336627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f427ea0f4ded3b6ff841153d8ad87d72a26a964c189246fb6fd5c439bd8b0c1e'), size_on_disk=353623, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.812652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/62e9a627f20607bc4887ebc9277131f974ec80ff39ac386775f4b8f2f5cb9d1d'), size_on_disk=346782, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7476518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bd2017e75947ead2c06f9d1936347f7fdcfac7fbff620e0b39077208a02c3bd9'), size_on_disk=303094, blob_last_accessed=1763591393.1271722, blob_last_modified=1758648269.9296618), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/11485fd7bb94d0093ce2508d541fea3ce64f934197d8a5f7bb26ecbdb59f8dbb'), size_on_disk=124642, blob_last_accessed=1763591392.3321776, blob_last_modified=1758648267.0666487), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d50648ed5c9f593f830aff661d13b3d34bd14f0bf93b16169df4765c9f47fc8'), size_on_disk=295924, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7426472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6545/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/260c4a280f52024cb12d44559a6189641ee02efbaf20168435196efbfc69d741'), size_on_disk=404812, blob_last_accessed=1763591392.3761773, blob_last_modified=1758648268.8256567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6924/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5e9b3a94d73c1720e6b05ea55000054905a8672edea7f950736ce3fe191f8faf'), size_on_disk=412939, blob_last_accessed=1763591393.748168, blob_last_modified=1758648269.7066607), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/530b5650d91dae3bf169f967af8e800611e3e01daeb1811d6fd21c2612ac4ad9'), size_on_disk=315170, blob_last_accessed=1763591393.6121688, blob_last_modified=1758648266.7956474), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6853/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ed47ecb2aa870fdbf12920fd4486933262c0a24318c9c8c5531e359a3c416c90'), size_on_disk=515114, blob_last_accessed=1763591393.2661712, blob_last_modified=1758648269.4276595), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6567/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0e9713a94594dfcbc80134af52449e12ef207ca9149f5a96c43b6cb457b2431c'), size_on_disk=478809, blob_last_accessed=1763591392.6241755, blob_last_modified=1758648268.893657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6635/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6c5b7f1fda3a7f5679870f438d13a45558f7f8da0f479c5144d7751bc8f7fbbc'), size_on_disk=420658, blob_last_accessed=1763591391.95018, blob_last_modified=1758648269.3956594), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7332/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f7f62ee008fa2b5cc688cbd80c888f1bf9da538caa245b63581520d485338a99'), size_on_disk=309595, blob_last_accessed=1763591393.1021724, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6506/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/986d9d3b4ed064d66ebaa0f2502b933f1650a20ac4bbbba804e2cde03b9454c0'), size_on_disk=378407, blob_last_accessed=1763591391.97318, blob_last_modified=1758648268.7656565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6513/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c8f9c31704c2f8732b4668e1e6410034b8adf2ada39ff55935aa0199e920d055'), size_on_disk=507367, blob_last_accessed=1763591392.1451788, blob_last_modified=1758648268.8406568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7256/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1af21cbdd7f6f6effff15a1b061a48f85132d582f08c8fd42e04ea58bde7ec00'), size_on_disk=206710, blob_last_accessed=1763591392.586176, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6739/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/58b5ed63f2197f406dd3df6a569975d776722193007912718979eaed4d4e5f9c'), size_on_disk=399160, blob_last_accessed=1763591392.6551754, blob_last_modified=1758648269.331659), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6731/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/23ecd71c4e3c1c547295bb756d4654eb413ff288d10118ab286cd21ccd95b666'), size_on_disk=346187, blob_last_accessed=1763591392.1871786, blob_last_modified=1758648269.1906583), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6b92129c2fb99028b6cab9509cf1a4c40b142109cde34c9a130d375001dcd211'), size_on_disk=267273, blob_last_accessed=1763591392.7811744, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS011/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ea7cb0559b342dd0ec278e7d5846a5ba0c0d867ea04d545f72e0d1c9472a0cc'), size_on_disk=363979, blob_last_accessed=1763591392.9791732, blob_last_modified=1758648267.0116484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5617/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bc01241e8363373c995a0fa7439aa1e432a60eb8a7805124fff302311f638361'), size_on_disk=351950, blob_last_accessed=1763591393.6541686, blob_last_modified=1758648268.1026535), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5399/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b65d0aa7ddc98940f91f2ae5f07e1d3f81dda688792552b91fcc39fb4f9ededa'), size_on_disk=256729, blob_last_accessed=1763591392.5471761, blob_last_modified=1758648267.972653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6140/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/66c2da17957d7e6a8b159d3573879ac779d0094e35150b51e2643f7ddee36e1f'), size_on_disk=332958, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4236548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6965/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9ae4546d58672f0632757c94b595af8b7c40da215611783fdc1a65000075ec98'), size_on_disk=509677, blob_last_accessed=1763591392.2731779, blob_last_modified=1758648269.933662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS006b/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7eedae811a5b3dd4389e7e814d35b022bfaf01fc4d2cfb47be6e6786d6d58c64'), size_on_disk=315912, blob_last_accessed=1763591393.2571712, blob_last_modified=1758648266.7546473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/00e5602b87ac84670a98c081e6520ca744038f4e9b3a72a78cb61f5926be56b1'), size_on_disk=251087, blob_last_accessed=1763591392.5781758, blob_last_modified=1758648267.8286521), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/646c843611bf88c7f5dcd5b1a664d724c3b24b6434cc52d9a54de62ccaf6c542'), size_on_disk=255949, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.4506505), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6923/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/454ca6a52d4b305eddc4b6967ef9604be4861269fdcfff419883daed9b2fe48b'), size_on_disk=337423, blob_last_accessed=1763591393.5031695, blob_last_modified=1758648269.6476605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fa1a44bd33df22ec6b5a8baf3be17e51a11522b5fdd545a38e6e163cc5ae8207'), size_on_disk=241911, blob_last_accessed=1763591392.572176, blob_last_modified=1758648267.5226507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7269/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5bae329d2318610f627fc562a5163253c7d61e0ef83c53808b220133b2299264'), size_on_disk=203263, blob_last_accessed=1763591392.541176, blob_last_modified=1758648270.4096642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5690/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4831e3e037d421a14efbe1da8c369015ec7d01df351bb27f0ef0400c8b94c693'), size_on_disk=311754, blob_last_accessed=1763591392.9021738, blob_last_modified=1758648268.211654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6443/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4deb4634e8b4bf141a2410e94b2d99e2b31a0e8f037de9a1c6e14696025bffae'), size_on_disk=494775, blob_last_accessed=1763591392.991173, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=DS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1fa09a5a6e6e69198501d994050adfcad7ff39a3cc2fe94e7ea177414e1bc968'), size_on_disk=303532, blob_last_accessed=1763591393.7091682, blob_last_modified=1758648267.2776496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6374/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e410602f56bc94d9831a2bc8bdb865ee4ebe9862a65b603165b655fbcc4b9914'), size_on_disk=441531, blob_last_accessed=1763591393.1891718, blob_last_modified=1758648268.5356555), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6551/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c3729692991cdcafe9baa3d2805b3866f72fe9bc89ba22c6d2cdae4d5b5bbf5f'), size_on_disk=545477, blob_last_accessed=1763591393.0611725, blob_last_modified=1758648268.8376567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7283/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0dc91bfacca82c5ed23de7757d59f9f808d541d96dd79aab56f0c4252e7aabf3'), size_on_disk=265146, blob_last_accessed=1763591392.7731745, blob_last_modified=1758648270.401664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7266/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9aea2d6f5606d24e1999d450379b1f8c8b9d80eded9e784fb729faa516a27026'), size_on_disk=324480, blob_last_accessed=1763591392.0401795, blob_last_modified=1758648270.379664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4e8332a5db6260f6357f940e62e3e7b622785f386e81cdb9c75b538d318f4a6'), size_on_disk=158978, blob_last_accessed=1763591393.5471692, blob_last_modified=1758648266.7626472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6708/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/35520976736159652e3be8066e77b1371522f63a8b3564d9b0d9f5b1598a144d'), size_on_disk=253955, blob_last_accessed=1763591393.6881683, blob_last_modified=1758648269.1996584), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=CS012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d3e36418bd46acf9537a8f864b1a1361a062d72fec2b093789b88ed655ec117'), size_on_disk=377393, blob_last_accessed=1763591393.0451727, blob_last_modified=1758648267.0136483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6954/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/aeed3c55d81a4eb0c568486e476211c599ce2bf37f9dff22c7dfca23f5d80495'), size_on_disk=476054, blob_last_accessed=1763591393.011173, blob_last_modified=1758648269.943662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7331/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f159a9811b8ac1b5e252b52427979a3bddce2e7131b13f84046b31e47f7fa399'), size_on_disk=242821, blob_last_accessed=1763591393.2501714, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6073/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d68ca6411ba47eb63adad81e88fa1341a0ee269897268957ae936dcc9ba7788'), size_on_disk=450031, blob_last_accessed=1763591393.1341722, blob_last_modified=1758648268.3886547), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6061/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73e03845d0fbff1a5931e4f792e2c1965ade02fc622ffc08e05c267ac6cd3231'), size_on_disk=394930, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.447655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6055/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/31bb70b4c90654ecb276664f5c914679322b24985ec66d5b396a99f253ebfa17'), size_on_disk=377015, blob_last_accessed=1763591393.5701692, blob_last_modified=1758648268.3076544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6021/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eea0d101dd58c9619b41a94009fc0dff8156d2d36d7f2b88bb653e96123a032e'), size_on_disk=439545, blob_last_accessed=1763591393.5531693, blob_last_modified=1758648268.258654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e5b3174998823af40d055f46ca06e7bb44601cbe97b133062cd3e0491dd30ac2'), size_on_disk=323308, blob_last_accessed=1763591393.153172, blob_last_modified=1758648267.2686496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6657/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/95ad2225152f51e2e31c030ff6708aeebccee5e548035d6f43c9cc3554bdfe5e'), size_on_disk=507730, blob_last_accessed=1763591393.163172, blob_last_modified=1758648269.1676583), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7243/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cb59e645fff2683db692b4d2f514602ba210271b632cd113853febe941c6485f'), size_on_disk=281696, blob_last_accessed=1763591392.701175, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6572/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3488eaac5e4b9b3418b909138e6b50e3f9ebaf3e7345a4a10b6ced18b66152e4'), size_on_disk=440579, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9506574), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/d1cd38451bc41ef14dbc1566c4704b004b2c0661f667210c81dba09bec90540f'), size_on_disk=244487, blob_last_accessed=1763591393.6301687, blob_last_modified=1758648267.3246498), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=GJ001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ddd58117dbedfe7e9dc3fd946187e7bd5d88fe8b0f38999c6e4b5660938bb787'), size_on_disk=359739, blob_last_accessed=1763591393.5981688, blob_last_modified=1758648267.2726495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6983/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4ee29f84ab279c520521a6c5a5eed9e5e5581b2e39378bdd898eec67d13654f'), size_on_disk=410123, blob_last_accessed=1763591392.6751752, blob_last_modified=1758648269.8636615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_5935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/33d7303ed077f489ee28021daca0bbc859feb1cfd383dfc9a856ea13994801a9'), size_on_disk=331877, blob_last_accessed=1763591393.292171, blob_last_modified=1758648268.2226539), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6863/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec534c91cae4413d30477014131d39b51c9c236dfbf150874bd21ec3de821613'), size_on_disk=389025, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648269.6376605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR006B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fcc5e886ec7c6363a5ab51b03d3da189ac3e0cde0a0f79d4f0c46043a6b61838'), size_on_disk=253857, blob_last_accessed=1763591392.0501795, blob_last_modified=1758648267.785652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6855/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48e48378d24404e926bc1c44b10af60d4b12f82124ff2f80bc0b69b4a0f98740'), size_on_disk=609248, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6556606), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=MAG002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f7f35257d4c1f7fa5812ea31c1de791e08ab5e3a4e7ca42b2bb12b2c64f7ed7'), size_on_disk=490174, blob_last_accessed=1763591392.829174, blob_last_modified=1758648267.583651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6808/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c71f5371e8c803cfc7fd507d0b3c11f7548db5d7a0fa42c68977169369ea036e'), size_on_disk=422880, blob_last_accessed=1763591392.9491735, blob_last_modified=1758648269.3916593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7137/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/703fe9f8552adcbdfb5e640509cb62df2fa734b43fe1e98fc1fd342415f823d7'), size_on_disk=515398, blob_last_accessed=1763589053.7698598, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fc9311b65abb851fbbfd8bb0c0765af59069659f5a715aee546a8644b0af4ec2'), size_on_disk=210948, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648267.6766515), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=KB001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38c939d54d3ed77a09650d2ccf00a6ed0e20390b8be722f1c3ed7881a5904a60'), size_on_disk=351677, blob_last_accessed=1763591392.9151735, blob_last_modified=1758648267.5566509), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dcb33135a9d854d17a6b80348f6bee8becea9ca3808583a034f89c5e4f460c54'), size_on_disk=218643, blob_last_accessed=1763591393.4831698, blob_last_modified=1758648267.9636528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eb61968b5ec5eadd9087e42ead29333f91195dd5b3f9c5a616b41e94ca23c494'), size_on_disk=330245, blob_last_accessed=1763591393.6761684, blob_last_modified=1758648269.710661), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7151/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9969e4607d6bf42424f5d2ce8f0d16e3d2d89d069cae6d75997e99c88a68dc77'), size_on_disk=275572, blob_last_accessed=1763591393.7231681, blob_last_modified=1758648270.1476629), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=PR007B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6f0620b102e7cbfadced7d5530545a185036b53118d3e2b7f074c56403d80f4b'), size_on_disk=263185, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7616518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6738/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e6d612d16538a06c985f1134f3fdfd367944b2a85cedfb8b0ddad1679004c940'), size_on_disk=333560, blob_last_accessed=1763591392.8651738, blob_last_modified=1758648269.3676593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7240/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4460d188b4cd3b362f33acf0f0b5312a267b37f8fb2dab4b2577c6c3bbb1231'), size_on_disk=307039, blob_last_accessed=1763591392.6101756, blob_last_modified=1758648270.403664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7258/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7b1860ba3be7dbf053203fa5690da2002f7841186940231acea6064e70f0bdc8'), size_on_disk=171331, blob_last_accessed=1763591392.6171756, blob_last_modified=1758648270.3746638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6959/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c0cb6c2ec2bda19b0c1da5d5fc2229d0b6d6987d75646fa230c0c14d591cf1f1'), size_on_disk=537588, blob_last_accessed=1763591392.851174, blob_last_modified=1758648269.8686616), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02313d4d85859a7353e56d89b6e14bcec5160849b5c0089ccbecaa1cda5012cf'), size_on_disk=301213, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4356549), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6861/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6955b1a1746fe41162ea1d7a098482bbfdb3d0e411c809b52d698e285627ce03'), size_on_disk=385103, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6146603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6640/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48339729112d22aa5aabf411674bfb90a567ec4c7a07ea7930738645a96823ef'), size_on_disk=329042, blob_last_accessed=1763591392.6391754, blob_last_modified=1758648269.090658), CachedFileInfo(file_name='annotated_features_meta.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features_meta.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/2be4b693d333698f7ada7ef40ffaf927c0160d7975ebcf6e215cbc4ba1be1604'), size_on_disk=11813, blob_last_accessed=1763587594.724117, blob_last_modified=1758648264.795638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6390/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f324cdf73148c8b0fa23456bc4f51cff3032d08ed42e44dd9fcc4815a6d969c'), size_on_disk=560601, blob_last_accessed=1763591392.7391748, blob_last_modified=1758648268.6206558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6798/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/899cde55032cd593be0013e1f6ff33fe9a12f4616477b1410d86fae1b2008a09'), size_on_disk=342376, blob_last_accessed=1763591392.7951744, blob_last_modified=1758648269.4006593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_7295/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/19bd07f913e0af3460e6b4b5091971ceed8f4aa510cf18b82ec36dabb67c68b8'), size_on_disk=272619, blob_last_accessed=1763591393.1151721, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/27becd0ab489579a50c38ba896dd03680c5fd2da/annotated_features/batch=run_6764/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec87eef9e2e8cdf3c3a10ead28dcf1bfa76e94e12a42422a46c8559fb18207c2'), size_on_disk=75684, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.4036593)}), refs=frozenset(), last_modified=1758648270.6726654), CachedRevisionInfo(commit_hash='6864b2582ce35f92a8dde59851786319c2c494bc', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc'), size_on_disk=49590351, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS011/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ea7cb0559b342dd0ec278e7d5846a5ba0c0d867ea04d545f72e0d1c9472a0cc'), size_on_disk=363979, blob_last_accessed=1763591392.9791732, blob_last_modified=1758648267.0116484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6635/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6c5b7f1fda3a7f5679870f438d13a45558f7f8da0f479c5144d7751bc8f7fbbc'), size_on_disk=420658, blob_last_accessed=1763591391.95018, blob_last_modified=1758648269.3956594), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e20f616f6e340b76f6446d2cdffb6cc9feb81297bd0307733f835acf4142cfd'), size_on_disk=269523, blob_last_accessed=1763591392.3461773, blob_last_modified=1758648267.2696495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6872/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02fabe6e32f52fe6fca923beb0a6cc2898d991304e23a3e75f3ae8a6bcf361c6'), size_on_disk=274362, blob_last_accessed=1763591393.6231687, blob_last_modified=1758648269.6966608), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e5b3174998823af40d055f46ca06e7bb44601cbe97b133062cd3e0491dd30ac2'), size_on_disk=323308, blob_last_accessed=1763591393.153172, blob_last_modified=1758648267.2686496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/74cbce50c258bdddb7e42c8fd5b0b2a9da74fe3cf89a9ab5a53ee4fde7c1dee6'), size_on_disk=186142, blob_last_accessed=1763591392.842174, blob_last_modified=1758648266.9966483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ab338795b8bbf334650386a123f6e0df41363cb5dcbb778d485ce58804e96b29'), size_on_disk=375506, blob_last_accessed=1763591392.9711733, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e871cf06f596bfe74896b62aa28d6b1e6f2f0b8408630fb146c7e29cfd88fbf0'), size_on_disk=232721, blob_last_accessed=1763591392.7871745, blob_last_modified=1758648267.991653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f427ea0f4ded3b6ff841153d8ad87d72a26a964c189246fb6fd5c439bd8b0c1e'), size_on_disk=353623, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.812652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5654/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38961219eb66dec31505ebcc5088f16684be9891b96fffa8c9a13cd3f13254d0'), size_on_disk=447782, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.1876538), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7063/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9427d8f8c54e3c0cc9d720958ef15d3859f00cdfb678559e0268387a36a8ef37'), size_on_disk=404051, blob_last_accessed=1763591392.5301762, blob_last_modified=1758648269.8976617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5690/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4831e3e037d421a14efbe1da8c369015ec7d01df351bb27f0ef0400c8b94c693'), size_on_disk=311754, blob_last_accessed=1763591392.9021738, blob_last_modified=1758648268.211654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d4c0cbbf6075a8b5732e3cef2ba154d95d21aa0efc7e49d947cceebd8742b30'), size_on_disk=269007, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4106548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bbe701ffd546a2f4359f821262f7b44232bdc328d9d4719bfdaa8f2aedc6d2e9'), size_on_disk=346079, blob_last_accessed=1763591393.0861723, blob_last_modified=1758648267.589651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6853/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ed47ecb2aa870fdbf12920fd4486933262c0a24318c9c8c5531e359a3c416c90'), size_on_disk=515114, blob_last_accessed=1763591393.2661712, blob_last_modified=1758648269.4276595), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6764/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec87eef9e2e8cdf3c3a10ead28dcf1bfa76e94e12a42422a46c8559fb18207c2'), size_on_disk=75684, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.4036593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6855/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48e48378d24404e926bc1c44b10af60d4b12f82124ff2f80bc0b69b4a0f98740'), size_on_disk=609248, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6556606), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6640/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/48339729112d22aa5aabf411674bfb90a567ec4c7a07ea7930738645a96823ef'), size_on_disk=329042, blob_last_accessed=1763591392.6391754, blob_last_modified=1758648269.090658), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7332/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f7f62ee008fa2b5cc688cbd80c888f1bf9da538caa245b63581520d485338a99'), size_on_disk=309595, blob_last_accessed=1763591393.1021724, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7243/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cb59e645fff2683db692b4d2f514602ba210271b632cd113853febe941c6485f'), size_on_disk=281696, blob_last_accessed=1763591392.701175, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7295/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/19bd07f913e0af3460e6b4b5091971ceed8f4aa510cf18b82ec36dabb67c68b8'), size_on_disk=272619, blob_last_accessed=1763591393.1151721, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/530b5650d91dae3bf169f967af8e800611e3e01daeb1811d6fd21c2612ac4ad9'), size_on_disk=315170, blob_last_accessed=1763591393.6121688, blob_last_modified=1758648266.7956474), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5301_5088/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/65ef775934339b80eda59a141cf791a74e1c6f12513d8143307cebe2f11c3738'), size_on_disk=410106, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.9516528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7283/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0dc91bfacca82c5ed23de7757d59f9f808d541d96dd79aab56f0c4252e7aabf3'), size_on_disk=265146, blob_last_accessed=1763591392.7731745, blob_last_modified=1758648270.401664), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fc266bb938fa146c46d1a7bd5446d5d27b3bf202'), size_on_disk=10887, blob_last_accessed=1763649507.5438576, blob_last_modified=1763649507.5418575), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fa1a44bd33df22ec6b5a8baf3be17e51a11522b5fdd545a38e6e163cc5ae8207'), size_on_disk=241911, blob_last_accessed=1763591392.572176, blob_last_modified=1758648267.5226507), CachedFileInfo(file_name='annotated_features_meta.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features_meta.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/2be4b693d333698f7ada7ef40ffaf927c0160d7975ebcf6e215cbc4ba1be1604'), size_on_disk=11813, blob_last_accessed=1763587594.724117, blob_last_modified=1758648264.795638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=MAG002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f7f35257d4c1f7fa5812ea31c1de791e08ab5e3a4e7ca42b2bb12b2c64f7ed7'), size_on_disk=490174, blob_last_accessed=1763591392.829174, blob_last_modified=1758648267.583651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7118/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/be8bd409b83526d5671501de6ef2980177955fdcc3a13d5e153d2593493b2a1e'), size_on_disk=346308, blob_last_accessed=1763591392.9281735, blob_last_modified=1758648270.1336627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4e8332a5db6260f6357f940e62e3e7b622785f386e81cdb9c75b538d318f4a6'), size_on_disk=158978, blob_last_accessed=1763591393.5471692, blob_last_modified=1758648266.7626472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6920/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/45122fcfb3abc3a43631023712c224dc6e00dba566fb2e15deb4ab4501ad2538'), size_on_disk=446998, blob_last_accessed=1763591392.8881738, blob_last_modified=1758648269.6416605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR006B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fcc5e886ec7c6363a5ab51b03d3da189ac3e0cde0a0f79d4f0c46043a6b61838'), size_on_disk=253857, blob_last_accessed=1763591392.0501795, blob_last_modified=1758648267.785652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5801/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/12e91a415cc2a149af4c34c3c8b032821ff301e2828dfb9f4636bd6b2913ea3d'), size_on_disk=360485, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.1636536), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6390/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f324cdf73148c8b0fa23456bc4f51cff3032d08ed42e44dd9fcc4815a6d969c'), size_on_disk=560601, blob_last_accessed=1763591392.7391748, blob_last_modified=1758648268.6206558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=MAG001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/30b63d014686a103b5e7fc6571e902fa9c08630a96557cc12ec9dfcf60871306'), size_on_disk=347333, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.559651), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6983/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4ee29f84ab279c520521a6c5a5eed9e5e5581b2e39378bdd898eec67d13654f'), size_on_disk=410123, blob_last_accessed=1763591392.6751752, blob_last_modified=1758648269.8636615), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7370/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ff4dce26afb34b291220c17625be4a28078f9721a8b000ea177c4add3bf1a039'), size_on_disk=241010, blob_last_accessed=1763591393.7701678, blob_last_modified=1758648270.6726654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6532/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3041f19cf0dcb3e0bd9b1178994351a22f3297ccd43c5e291df8bfe0ad8d5d02'), size_on_disk=516437, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.8236568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6423/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1c8a8cb23b1ec073827d6f5343a3bf03a9d7a04cf2ae3fd3ea4cc5ac37673b38'), size_on_disk=342987, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.6226559), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/8e91fd764a83947c14f35d7a2d8c0d156c0a61a32a1da96273718ef4be720787'), size_on_disk=361855, blob_last_accessed=1763591391.518183, blob_last_modified=1758648267.7196517), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/11485fd7bb94d0093ce2508d541fea3ce64f934197d8a5f7bb26ecbdb59f8dbb'), size_on_disk=124642, blob_last_accessed=1763591392.3321776, blob_last_modified=1758648267.0666487), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6572/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3488eaac5e4b9b3418b909138e6b50e3f9ebaf3e7345a4a10b6ced18b66152e4'), size_on_disk=440579, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9506574), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c9b19726dc45896e4f86a5acc52d07cc422f895bfaaaddb332aaceedff36560c'), size_on_disk=178793, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0266485), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7331/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/f159a9811b8ac1b5e252b52427979a3bddce2e7131b13f84046b31e47f7fa399'), size_on_disk=242821, blob_last_accessed=1763591393.2501714, blob_last_modified=1758648270.605665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6437/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/556084dbbc174db5450872d6f5e66cc72091380ea32006977dd176e23c105a7f'), size_on_disk=389840, blob_last_accessed=1763591392.714175, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/fc9311b65abb851fbbfd8bb0c0765af59069659f5a715aee546a8644b0af4ec2'), size_on_disk=210948, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648267.6766515), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7351/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/cc708adf186f28db09dfdd4ac43865a90482b2192306949eab01492598cf23e7'), size_on_disk=198070, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.610665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS003/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4d098c33564f99ec6aed77d585d1f6e871806f441f099790eef4d6478e322af'), size_on_disk=264459, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7486472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6861/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6955b1a1746fe41162ea1d7a098482bbfdb3d0e411c809b52d698e285627ce03'), size_on_disk=385103, blob_last_accessed=1763591391.517183, blob_last_modified=1758648269.6146603), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6959/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c0cb6c2ec2bda19b0c1da5d5fc2229d0b6d6987d75646fa230c0c14d591cf1f1'), size_on_disk=537588, blob_last_accessed=1763591392.851174, blob_last_modified=1758648269.8686616), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6923/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/454ca6a52d4b305eddc4b6967ef9604be4861269fdcfff419883daed9b2fe48b'), size_on_disk=337423, blob_last_accessed=1763591393.5031695, blob_last_modified=1758648269.6476605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7137/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/703fe9f8552adcbdfb5e640509cb62df2fa734b43fe1e98fc1fd342415f823d7'), size_on_disk=515398, blob_last_accessed=1763589053.7698598, blob_last_modified=1758648270.1326628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9f7663ccf72b0681ae5866c13500fbde98f35877fd677ac0d49c17447d17d6ce'), size_on_disk=332767, blob_last_accessed=1763591392.1731787, blob_last_modified=1758648266.744647), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6021/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eea0d101dd58c9619b41a94009fc0dff8156d2d36d7f2b88bb653e96123a032e'), size_on_disk=439545, blob_last_accessed=1763591393.5531693, blob_last_modified=1758648268.258654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/71a33d11c3ff714ce0730145c740eec4fe88812c6136b8d10b86fa8d4054991f'), size_on_disk=186500, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7286518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6965/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9ae4546d58672f0632757c94b595af8b7c40da215611783fdc1a65000075ec98'), size_on_disk=509677, blob_last_accessed=1763591392.2731779, blob_last_modified=1758648269.933662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5986/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4f1179c75ac6e91de03b67a73882f3b9c07c8e435d938d3059de489403adde7a'), size_on_disk=328749, blob_last_accessed=1763591392.691175, blob_last_modified=1758648268.228654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6551/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c3729692991cdcafe9baa3d2805b3866f72fe9bc89ba22c6d2cdae4d5b5bbf5f'), size_on_disk=545477, blob_last_accessed=1763591393.0611725, blob_last_modified=1758648268.8376567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/128015caa722289251a45aa3d56cabac6b5946eb4e1be16eb950555fb24ac413'), size_on_disk=172328, blob_last_accessed=1763591393.735168, blob_last_modified=1758648267.2826495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6443/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4deb4634e8b4bf141a2410e94b2d99e2b31a0e8f037de9a1c6e14696025bffae'), size_on_disk=494775, blob_last_accessed=1763591392.991173, blob_last_modified=1758648268.6296558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5614/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1aa702ff57d34ebed961043e0efae064bfdb62f589cbb515311ae8ea37ba8c76'), size_on_disk=429423, blob_last_accessed=1763591393.4671698, blob_last_modified=1758648268.0406532), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6448/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/182dc134914a5ddb02992ab6a4f1c12d2450aa331820bf7bd95693dc999bcfae'), size_on_disk=390210, blob_last_accessed=1763591392.9591732, blob_last_modified=1758648268.672656), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7367/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a7778bb24506047963575fa96126157895c7f329996c729c3e7d6070595955c3'), size_on_disk=324361, blob_last_accessed=1763591392.1631787, blob_last_modified=1758648270.628665), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7ef036874e53ee98dfe1b5d84c42749fc40da9279cd573174321ac83201c1651'), size_on_disk=349762, blob_last_accessed=1763591393.0791724, blob_last_modified=1758648267.4966507), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7100/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6b92129c2fb99028b6cab9509cf1a4c40b142109cde34c9a130d375001dcd211'), size_on_disk=267273, blob_last_accessed=1763591392.7811744, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6506/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/986d9d3b4ed064d66ebaa0f2502b933f1650a20ac4bbbba804e2cde03b9454c0'), size_on_disk=378407, blob_last_accessed=1763591391.97318, blob_last_modified=1758648268.7656565), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7155/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/521fc1bea6963640645c21cbdb8d1de07793f11213c23ac71d249f3252b32f54'), size_on_disk=304554, blob_last_accessed=1763591393.5841691, blob_last_modified=1758648270.1366627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6924/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5e9b3a94d73c1720e6b05ea55000054905a8672edea7f950736ce3fe191f8faf'), size_on_disk=412939, blob_last_accessed=1763591393.748168, blob_last_modified=1758648269.7066607), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/646c843611bf88c7f5dcd5b1a664d724c3b24b6434cc52d9a54de62ccaf6c542'), size_on_disk=255949, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.4506505), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/00e5602b87ac84670a98c081e6520ca744038f4e9b3a72a78cb61f5926be56b1'), size_on_disk=251087, blob_last_accessed=1763591392.5781758, blob_last_modified=1758648267.8286521), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ddd58117dbedfe7e9dc3fd946187e7bd5d88fe8b0f38999c6e4b5660938bb787'), size_on_disk=359739, blob_last_accessed=1763591393.5981688, blob_last_modified=1758648267.2726495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6677/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4ee6e1d3e2b9a9bbcc8851048d2b16f4aea6a1e2f752afb9e0bf75a56adf7042'), size_on_disk=322147, blob_last_accessed=1763591392.393177, blob_last_modified=1758648269.0826578), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7240/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a4460d188b4cd3b362f33acf0f0b5312a267b37f8fb2dab4b2577c6c3bbb1231'), size_on_disk=307039, blob_last_accessed=1763591392.6101756, blob_last_modified=1758648270.403664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6454/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c35b60861d2fc7a3f4256869e14e629aed07cd2ccce76608578ceb8c6ec423e1'), size_on_disk=498326, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.7226562), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6374/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e410602f56bc94d9831a2bc8bdb865ee4ebe9862a65b603165b655fbcc4b9914'), size_on_disk=441531, blob_last_accessed=1763591393.1891718, blob_last_modified=1758648268.5356555), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7185/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/14f67668aca8d8f8aa2b8af98835da683a7fc3f22aa09cdcd7f943a37632391e'), size_on_disk=201650, blob_last_accessed=1763591393.2191715, blob_last_modified=1758648270.1076627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS006b/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7eedae811a5b3dd4389e7e814d35b022bfaf01fc4d2cfb47be6e6786d6d58c64'), size_on_disk=315912, blob_last_accessed=1763591393.2571712, blob_last_modified=1758648266.7546473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/62e9a627f20607bc4887ebc9277131f974ec80ff39ac386775f4b8f2f5cb9d1d'), size_on_disk=346782, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7476518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6708/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/35520976736159652e3be8066e77b1371522f63a8b3564d9b0d9f5b1598a144d'), size_on_disk=253955, blob_last_accessed=1763591393.6881683, blob_last_modified=1758648269.1996584), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7d3e36418bd46acf9537a8f864b1a1361a062d72fec2b093789b88ed655ec117'), size_on_disk=377393, blob_last_accessed=1763591393.0451727, blob_last_modified=1758648267.0136483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6061/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73e03845d0fbff1a5931e4f792e2c1965ade02fc622ffc08e05c267ac6cd3231'), size_on_disk=394930, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648268.447655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6731/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/23ecd71c4e3c1c547295bb756d4654eb413ff288d10118ab286cd21ccd95b666'), size_on_disk=346187, blob_last_accessed=1763591392.1871786, blob_last_modified=1758648269.1906583), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6573/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e435989051765cf2955d8cd733904e8b60e5f3121e1c2fdc987f4e6f71fe3970'), size_on_disk=265022, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.9586573), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=GJ004/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/d1cd38451bc41ef14dbc1566c4704b004b2c0661f667210c81dba09bec90540f'), size_on_disk=244487, blob_last_accessed=1763591393.6301687, blob_last_modified=1758648267.3246498), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7276/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b4e0d576cc4a6a85be64f2198e5001a9f5b02b506072df6f695cfdfc719cb041'), size_on_disk=204036, blob_last_accessed=1763591392.987173, blob_last_modified=1758648270.4646642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6738/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e6d612d16538a06c985f1134f3fdfd367944b2a85cedfb8b0ddad1679004c940'), size_on_disk=333560, blob_last_accessed=1763591392.8651738, blob_last_modified=1758648269.3676593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6055/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/31bb70b4c90654ecb276664f5c914679322b24985ec66d5b396a99f253ebfa17'), size_on_disk=377015, blob_last_accessed=1763591393.5701692, blob_last_modified=1758648268.3076544), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7269/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5bae329d2318610f627fc562a5163253c7d61e0ef83c53808b220133b2299264'), size_on_disk=203263, blob_last_accessed=1763591392.541176, blob_last_modified=1758648270.4096642), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dcb33135a9d854d17a6b80348f6bee8becea9ca3808583a034f89c5e4f460c54'), size_on_disk=218643, blob_last_accessed=1763591393.4831698, blob_last_modified=1758648267.9636528), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6689/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/90ea249116dc6a9be86974e1ec0acf79558dfd56a5666be2e05d2107b8c4ee60'), size_on_disk=366787, blob_last_accessed=1763591393.6661685, blob_last_modified=1758648269.1406581), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/dc74862f02ff12b8c906046f1f03953d33f5516091d58397b0f9a0fc05bb4b5c'), size_on_disk=315435, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.5006506), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7266/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9aea2d6f5606d24e1999d450379b1f8c8b9d80eded9e784fb729faa516a27026'), size_on_disk=324480, blob_last_accessed=1763591392.0401795, blob_last_modified=1758648270.379664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6863/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ec534c91cae4413d30477014131d39b51c9c236dfbf150874bd21ec3de821613'), size_on_disk=389025, blob_last_accessed=1763591391.5161831, blob_last_modified=1758648269.6376605), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7297/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/e4bd4088cbfcccdf96def916bb8fd9d673327d3e57c2a4a3e57299743ce4aca7'), size_on_disk=261344, blob_last_accessed=1763591393.588169, blob_last_modified=1758648270.6516652), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6938/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/eb61968b5ec5eadd9087e42ead29333f91195dd5b3f9c5a616b41e94ca23c494'), size_on_disk=330245, blob_last_accessed=1763591393.6761684, blob_last_modified=1758648269.710661), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6140/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/66c2da17957d7e6a8b159d3573879ac779d0094e35150b51e2643f7ddee36e1f'), size_on_disk=332958, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4236548), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6513/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c8f9c31704c2f8732b4668e1e6410034b8adf2ada39ff55935aa0199e920d055'), size_on_disk=507367, blob_last_accessed=1763591392.1451788, blob_last_modified=1758648268.8406568), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7256/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1af21cbdd7f6f6effff15a1b061a48f85132d582f08c8fd42e04ea58bde7ec00'), size_on_disk=206710, blob_last_accessed=1763591392.586176, blob_last_modified=1758648270.375664), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6421/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1e3bacd3bcf1fcb46cfc5f9ab66c2af57e190f3d006031e7ef37864fbd36aaca'), size_on_disk=495452, blob_last_accessed=1763591392.2161784, blob_last_modified=1758648268.6366558), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS008/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1fa09a5a6e6e69198501d994050adfcad7ff39a3cc2fe94e7ea177414e1bc968'), size_on_disk=303532, blob_last_accessed=1763591393.7091682, blob_last_modified=1758648267.2776496), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6567/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0e9713a94594dfcbc80134af52449e12ef207ca9149f5a96c43b6cb457b2431c'), size_on_disk=478809, blob_last_accessed=1763591392.6241755, blob_last_modified=1758648268.893657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS005/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d50648ed5c9f593f830aff661d13b3d34bd14f0bf93b16169df4765c9f47fc8'), size_on_disk=295924, blob_last_accessed=1763591391.517183, blob_last_modified=1758648266.7426472), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7cb5cca74f1bda2f8f1c8ef962e54d34228361eaa9dd8b21e0d1dd7092ff8164'), size_on_disk=229703, blob_last_accessed=1763591392.555176, blob_last_modified=1758648267.0126483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/02313d4d85859a7353e56d89b6e14bcec5160849b5c0089ccbecaa1cda5012cf'), size_on_disk=301213, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.4356549), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6527/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/10081f44b1c2f5ad32489da51fc9ac5b5289b54688a7e78725db247a644b7e4d'), size_on_disk=494640, blob_last_accessed=1763591391.517183, blob_last_modified=1758648268.882657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6739/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/58b5ed63f2197f406dd3df6a569975d776722193007912718979eaed4d4e5f9c'), size_on_disk=399160, blob_last_accessed=1763591392.6551754, blob_last_modified=1758648269.331659), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6177/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b91ffef1dfcdf7952df46956d76a4b364aa0d8342453636cf09e6a343607184f'), size_on_disk=387618, blob_last_accessed=1763591391.518183, blob_last_modified=1758648268.424655), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5961/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/49cac5c2df1adf790a9ac0209db6c0945de50b8191d403e8b1ad386ee2dcb35e'), size_on_disk=321368, blob_last_accessed=1763591392.560176, blob_last_modified=1758648268.215654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6545/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/260c4a280f52024cb12d44559a6189641ee02efbaf20168435196efbfc69d741'), size_on_disk=404812, blob_last_accessed=1763591392.3761773, blob_last_modified=1758648268.8256567), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5935/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/33d7303ed077f489ee28021daca0bbc859feb1cfd383dfc9a856ea13994801a9'), size_on_disk=331877, blob_last_accessed=1763591393.292171, blob_last_modified=1758648268.2226539), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=PR007B/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/6f0620b102e7cbfadced7d5530545a185036b53118d3e2b7f074c56403d80f4b'), size_on_disk=263185, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.7616518), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6808/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/c71f5371e8c803cfc7fd507d0b3c11f7548db5d7a0fa42c68977169369ea036e'), size_on_disk=422880, blob_last_accessed=1763591392.9491735, blob_last_modified=1758648269.3916593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6651/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7644af4cc956ea08809ab6c80c65095e3fdbd36e056040c0eaded3773d546359'), size_on_disk=559951, blob_last_accessed=1763591393.6361687, blob_last_modified=1758648269.1856585), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=composite/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/1f8adf8fb4992283714df8587126211d54e6a0580b8be88c1d5e911196aed783'), size_on_disk=4814573, blob_last_accessed=1763591392.0561793, blob_last_modified=1758648268.0256531), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7237/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/110a743162ace7792a44dfe48d7afd40bb67c45c722038c981548477ebc5d8fe'), size_on_disk=313207, blob_last_accessed=1763591393.143172, blob_last_modified=1758648270.1426628), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=DS006/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/0230df4f1d3748337e5315bb1d22a0373fdab95f9bc900041b0e34966d868718'), size_on_disk=311116, blob_last_accessed=1763591392.9241736, blob_last_modified=1758648267.2506495), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7258/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/7b1860ba3be7dbf053203fa5690da2002f7841186940231acea6064e70f0bdc8'), size_on_disk=171331, blob_last_accessed=1763591392.6171756, blob_last_modified=1758648270.3746638), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS010/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/ac8c590c19e7b95b9aac571b1394976039f25fdd66e62a68ecb99925433f8e54'), size_on_disk=355706, blob_last_accessed=1763591391.517183, blob_last_modified=1758648267.0226483), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS007/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/4b2ff192aa4808cb50b1c1b1667060f98505dd63c1aa58c7d8354e41194957e4'), size_on_disk=289767, blob_last_accessed=1763591393.4871697, blob_last_modified=1758648266.7706473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7211/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/73d7747698fa1168c89f12c105bbe900d5969d27a82ae2302d0720d761fbfdab'), size_on_disk=298482, blob_last_accessed=1763591391.517183, blob_last_modified=1758648270.1276627), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5610/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a86512ae1000a994c86e9aebaf30cf9ce1a59160155f3dc1afa6cbde0a1fc47e'), size_on_disk=307382, blob_last_accessed=1763591393.5291693, blob_last_modified=1758648267.994653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7012/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bd2017e75947ead2c06f9d1936347f7fdcfac7fbff620e0b39077208a02c3bd9'), size_on_disk=303094, blob_last_accessed=1763591393.1271722, blob_last_modified=1758648269.9296618), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7151/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/9969e4607d6bf42424f5d2ce8f0d16e3d2d89d069cae6d75997e99c88a68dc77'), size_on_disk=275572, blob_last_accessed=1763591393.7231681, blob_last_modified=1758648270.1476629), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6798/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/899cde55032cd593be0013e1f6ff33fe9a12f4616477b1410d86fae1b2008a09'), size_on_disk=342376, blob_last_accessed=1763591392.7951744, blob_last_modified=1758648269.4006593), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6073/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/3d68ca6411ba47eb63adad81e88fa1341a0ee269897268957ae936dcc9ba7788'), size_on_disk=450031, blob_last_accessed=1763591393.1341722, blob_last_modified=1758648268.3886547), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS009/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/5a70fa6f22aeda3246d17646cc4ec7c5a46e1993f9981b19d9fd5a8cc99bf11f'), size_on_disk=308858, blob_last_accessed=1763591391.9411802, blob_last_modified=1758648267.0186484), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5617/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/bc01241e8363373c995a0fa7439aa1e432a60eb8a7805124fff302311f638361'), size_on_disk=351950, blob_last_accessed=1763591393.6541686, blob_last_modified=1758648268.1026535), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=KB001/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/38c939d54d3ed77a09650d2ccf00a6ed0e20390b8be722f1c3ed7881a5904a60'), size_on_disk=351677, blob_last_accessed=1763591392.9151735, blob_last_modified=1758648267.5566509), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_7115/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/205443fd87ce5c793dab86eb75c21dab7dc2ae2edac41d7e6314cfe491bdb01d'), size_on_disk=321850, blob_last_accessed=1763591393.2331715, blob_last_modified=1758648269.9106617), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6657/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/95ad2225152f51e2e31c030ff6708aeebccee5e548035d6f43c9cc3554bdfe5e'), size_on_disk=507730, blob_last_accessed=1763591393.163172, blob_last_modified=1758648269.1676583), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6954/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/aeed3c55d81a4eb0c568486e476211c599ce2bf37f9dff22c7dfca23f5d80495'), size_on_disk=476054, blob_last_accessed=1763591393.011173, blob_last_modified=1758648269.943662), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_5399/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/b65d0aa7ddc98940f91f2ae5f07e1d3f81dda688792552b91fcc39fb4f9ededa'), size_on_disk=256729, blob_last_accessed=1763591392.5471761, blob_last_modified=1758648267.972653), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=CS002/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/a76f0849e518faedfe50e6f0c76908a3f6beed34fd0b607603db1859493595d0'), size_on_disk=292312, blob_last_accessed=1763587658.6168654, blob_last_modified=1758648266.7676473), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6354/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/80b129a46defc7b06e472a35935bfaf410bc5e456475b2885feb2c8a4934a81b'), size_on_disk=173225, blob_last_accessed=1763591393.4981697, blob_last_modified=1758648268.4986553), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/snapshots/6864b2582ce35f92a8dde59851786319c2c494bc/annotated_features/batch=run_6770/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--callingcards/blobs/082f14f7b0d5fc47e088efc40ae85a8ec41f99437c66da96ca69af210bce61cc'), size_on_disk=318118, blob_last_accessed=1763591392.8081744, blob_last_modified=1758648269.3776593)}), refs=frozenset({'main'}), last_modified=1763649507.5418575)}), last_accessed=1763649507.5438576, last_modified=1763649507.5418575), CachedRepoInfo(repo_id='BrentLab/rossi_2021', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021'), size_on_disk=213734041, nb_files=48, revisions=frozenset({CachedRevisionInfo(commit_hash='22ebbfcf62a7ed665fb8eceaea53b58b45a1709e', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/22ebbfcf62a7ed665fb8eceaea53b58b45a1709e'), size_on_disk=4602, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/22ebbfcf62a7ed665fb8eceaea53b58b45a1709e/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/e03b8d10ba59c549907a309ad8343133b7b902fc'), size_on_disk=4602, blob_last_accessed=1757613581.062093, blob_last_modified=1757613581.0610929)}), refs=frozenset(), last_modified=1757613581.0610929), CachedRevisionInfo(commit_hash='3ce77eb2070d7bb43049ba26bb462fded0ff7863', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/3ce77eb2070d7bb43049ba26bb462fded0ff7863'), size_on_disk=15562566, files=frozenset({CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/3ce77eb2070d7bb43049ba26bb462fded0ff7863/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1762805126.6580298, blob_last_modified=1756944310.9395182), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/3ce77eb2070d7bb43049ba26bb462fded0ff7863/genome_map/accession=SRR11466106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/04bacc0f8c03ee000ba3b69320522d0b1be909686474b6e8a126173a0ceae8c4'), size_on_disk=15547870, blob_last_accessed=1762805142.7999256, blob_last_modified=1756944357.1972551)}), refs=frozenset(), last_modified=1756944357.1972551), CachedRevisionInfo(commit_hash='6aac54b4b3fbb414561a8b6cc7acf87880c3c3c2', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6aac54b4b3fbb414561a8b6cc7acf87880c3c3c2'), size_on_disk=4665, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6aac54b4b3fbb414561a8b6cc7acf87880c3c3c2/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/14e1e02ad3804c0a4146a52509460e9cbb759d93'), size_on_disk=4665, blob_last_accessed=1758306843.4906857, blob_last_modified=1758156479.1516545)}), refs=frozenset(), last_modified=1758156479.1516545), CachedRevisionInfo(commit_hash='824bf517ff0722a085c86ac7924df0ab1278c8bf', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/824bf517ff0722a085c86ac7924df0ab1278c8bf'), size_on_disk=14696, files=frozenset({CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/824bf517ff0722a085c86ac7924df0ab1278c8bf/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1762805126.6580298, blob_last_modified=1756944310.9395182)}), refs=frozenset(), last_modified=1756944310.9395182), CachedRevisionInfo(commit_hash='dcf529428b66f5efc8c44ea06fb4733c41952e03', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/dcf529428b66f5efc8c44ea06fb4733c41952e03'), size_on_disk=15570962, files=frozenset({CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/dcf529428b66f5efc8c44ea06fb4733c41952e03/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1762805126.6580298, blob_last_modified=1756944310.9395182), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/dcf529428b66f5efc8c44ea06fb4733c41952e03/genome_map/accession=SRR11466106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/04bacc0f8c03ee000ba3b69320522d0b1be909686474b6e8a126173a0ceae8c4'), size_on_disk=15547870, blob_last_accessed=1762805142.7999256, blob_last_modified=1756944357.1972551), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/dcf529428b66f5efc8c44ea06fb4733c41952e03/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/192e51c15075f65f15da70558261e4c95bb1392a'), size_on_disk=8396, blob_last_accessed=1762805108.5211468, blob_last_modified=1762805108.5201466)}), refs=frozenset({'main'}), last_modified=1762805108.5201466), CachedRevisionInfo(commit_hash='664d95cdd482d753bc139d26119061c2fa6eaa76', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/664d95cdd482d753bc139d26119061c2fa6eaa76'), size_on_disk=4679, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/664d95cdd482d753bc139d26119061c2fa6eaa76/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/ff6f75db42c0690f0e0285e56c1aa95bc5cf1300'), size_on_disk=4679, blob_last_accessed=1758157110.1705706, blob_last_modified=1758157110.1695707)}), refs=frozenset(), last_modified=1758157110.1695707), CachedRevisionInfo(commit_hash='1acaa5629922414e8efe23a5d023bd44965824c8', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1acaa5629922414e8efe23a5d023bd44965824c8'), size_on_disk=4683, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1acaa5629922414e8efe23a5d023bd44965824c8/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/bb690d71edd44885609ed660d926f3cf2c4c473e'), size_on_disk=4683, blob_last_accessed=1758306349.7238934, blob_last_modified=1758156991.1235294)}), refs=frozenset(), last_modified=1758156991.1235294), CachedRevisionInfo(commit_hash='6b29baae54286d5fbdd1c70f499c03319badad51', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51'), size_on_disk=213707016, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466139/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/25c7d3ffbacca76fd998edffe0c762e220f448140019aa2d389facff5117ce48'), size_on_disk=176811, blob_last_accessed=1757598691.2478209, blob_last_modified=1757104412.6266806), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466143/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/54bd4af6ed8caf52a5810598d96c9b235266542586a692112cb247a209c2649f'), size_on_disk=10801408, blob_last_accessed=1757598691.2688208, blob_last_modified=1757104413.3726768), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466136/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/f5e7fd644d30ae0c8e2c72bc341155bfe5df2ce102b68b08ff4d243470026fcc'), size_on_disk=343033, blob_last_accessed=1757598691.1958208, blob_last_modified=1757104411.7116852), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466129/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/b290d4c9436756b444d0da1af1229b0a2a6e877fd968c5d01b73efa96e62cff8'), size_on_disk=6955953, blob_last_accessed=1757598691.196821, blob_last_modified=1757104410.78769), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466123/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/57b7ca5d419676590538573f295b99db05a952e20cb4c6b2a1618974611d5051'), size_on_disk=3672899, blob_last_accessed=1757598691.073821, blob_last_modified=1757104409.590696), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466109/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/6588704c47423e671054df9cb2318c045138b0018f2eefff61ef91a25848eb58'), size_on_disk=5259413, blob_last_accessed=1757598690.8298213, blob_last_modified=1757104404.9987195), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466125/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/0bba21490ea0a6297031b0ff3219fc0158d9bbcf15099906c86a770b1a1312fc'), size_on_disk=2624759, blob_last_accessed=1757598691.081821, blob_last_modified=1757104409.4426968), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466120/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/4220a31e6a7be79341828d41da77ef77fb5a8026980d4445fec18c57b5229c1f'), size_on_disk=3644577, blob_last_accessed=1757598691.034821, blob_last_modified=1757104408.3467023), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466124/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/39ef32412cae257b724c3b797ea482eeffb5b29afef4246d997ea525a2124054'), size_on_disk=7162800, blob_last_accessed=1757598691.075821, blob_last_modified=1757104409.5966961), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466116/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/587a7b86dffe3d7ade9adeeb94029ea1de54e61fb2c2445dc527979b7f1c24ac'), size_on_disk=5596311, blob_last_accessed=1757598690.988821, blob_last_modified=1757104407.3507075), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466122/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/6362da9e07f02d4c9dc05f15f638ca1b1bf0de5989da4ee8c6f0768f2453f98f'), size_on_disk=1249990, blob_last_accessed=1757598691.0398211, blob_last_modified=1757104411.9016843), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466108/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/56875f81a3cc7c1d40b3042b2c443058eb0f9a2b7e7ebc2e1704c098be612628'), size_on_disk=1765819, blob_last_accessed=1757598690.8238213, blob_last_modified=1757104407.1947083), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466132/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/cc41509e63473d565341ed36fd6881fecfd537e59c003f4779d4c4ec7b49cb54'), size_on_disk=1906184, blob_last_accessed=1757598691.161821, blob_last_modified=1757104413.4366765), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466142/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/3dc044b5cc1b7bc8070d8de4b7b870524b296f26b4f43b22dce2dcbf161d4c74'), size_on_disk=1739520, blob_last_accessed=1757598691.2588208, blob_last_modified=1757104415.5896657), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466131/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/bdce0842b5568e406a799dbe36b4b9645a30fa58eb0a707f24ed3bb3900fbf67'), size_on_disk=11759994, blob_last_accessed=1757598691.157821, blob_last_modified=1757104411.1146884), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466114/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/2c07f671cca471d6ad3e10e071cd1b976a89a45ff32920913d8fc36dbeaa1678'), size_on_disk=10992797, blob_last_accessed=1757598690.9498212, blob_last_modified=1757104406.3287127), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466128/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/f631797d2bc13a580f570637d94a0b9ea37deb2fd7ef92e32c66fd9014260525'), size_on_disk=1059587, blob_last_accessed=1757598691.118821, blob_last_modified=1757104410.6996903), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466121/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/78d0f377f8a756b28f2f5296932368c9c6b764d2972dc93aa6dd354591d29860'), size_on_disk=7053621, blob_last_accessed=1757598691.0328212, blob_last_modified=1757104408.5097015), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466126/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/34292b90d99b5a5be542c7dd10b11336605330639d92e93b092e567a56fe7d93'), size_on_disk=2850164, blob_last_accessed=1757598691.086821, blob_last_modified=1757104410.0976934), CachedFileInfo(file_name='rossi_2021_metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/rossi_2021_metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8dbda57bbf680e0706bf9d7cb298848dfb09a7dfa744e79d7e9e5b6208a64e09'), size_on_disk=14696, blob_last_accessed=1762805126.6580298, blob_last_modified=1756944310.9395182), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466117/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/49065d4cee74c20f34303405e34e284e29f19082b76fa883a38aeab24cadf674'), size_on_disk=1742958, blob_last_accessed=1757598690.995821, blob_last_modified=1757104411.8276846), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466140/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/25f98a891820b5a91c11629ed5615d40ffe1fe4a6cba1b2c6642067d76b5be87'), size_on_disk=68174, blob_last_accessed=1757598691.2588208, blob_last_modified=1757104415.6456654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466144/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/57b3d2c7917bfa29312cc51c921beff914858c61762e28a658eb29d8eaf3348b'), size_on_disk=918271, blob_last_accessed=1757598691.2758207, blob_last_modified=1757104413.554676), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466112/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/8f3441964a046d3c148b41f335948a085d26884e1a890e515711a2d1b48a40be'), size_on_disk=4883601, blob_last_accessed=1757598690.9128213, blob_last_modified=1757104408.5027015), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466118/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/192057e6004dd5cd303e8b1abf54cb72005e211c315665e28eec7b617a84d95a'), size_on_disk=728586, blob_last_accessed=1757598691.0068212, blob_last_modified=1757104407.0917087), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466110/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/d5283e08e898b4f59837f7ff2064f79c8a828994f868c637894f4e07df36daa5'), size_on_disk=13123795, blob_last_accessed=1757598690.8318212, blob_last_modified=1757104407.6787057), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466149/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/ae4d5237fde214a92d3267fbe11f6f1f88569cc7f3f7ccb40fed200871bf33b1'), size_on_disk=10933582, blob_last_accessed=1757598691.3518207, blob_last_modified=1757104415.1316679), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466119/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/0157d9136c2c2bb5640866449adf4bab8c62b3d7f7fb4262981908c77986f89e'), size_on_disk=12516152, blob_last_accessed=1757598691.0068212, blob_last_modified=1757104412.3166823), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466106/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/04bacc0f8c03ee000ba3b69320522d0b1be909686474b6e8a126173a0ceae8c4'), size_on_disk=15547870, blob_last_accessed=1762805142.7999256, blob_last_modified=1756944357.1972551), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466127/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/1565c5175e05c322f160529cb4bb74d5e8468458f150f228d6aadf2b670c3b4a'), size_on_disk=11670434, blob_last_accessed=1757598691.092821, blob_last_modified=1757104411.0776885), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466134/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/0b23434181eb1fc864e858cfca82d2d8fed1524a8f7dbea3bf576f6f452cf08b'), size_on_disk=12398132, blob_last_accessed=1757598691.169821, blob_last_modified=1757104412.552681), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1757598690.9228213, blob_last_modified=1757104403.6017265), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466111/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/ba431fc9a05f40581e450429d467226a8e3c49195de355437f9044bb0fdfe524'), size_on_disk=5749460, blob_last_accessed=1757598690.9028213, blob_last_modified=1757104404.9707196), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466130/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/73db821736e3e47c09da250e369fa06812e02e5abe7a4a3ceab5d1020422b66c'), size_on_disk=1920920, blob_last_accessed=1757598691.1328208, blob_last_modified=1757104410.9306893), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466150/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/80cd2d325e5d7a50c6ecbf79a052f45c54a93b9423e90735bf8989aada9bb9eb'), size_on_disk=3234303, blob_last_accessed=1757598691.3798206, blob_last_modified=1757104417.837654), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466135/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/d44d381db0b8c0cb4960796e48d3d16e3d766aff87cbc686789a0c5799ba0a98'), size_on_disk=5204256, blob_last_accessed=1757598691.180821, blob_last_modified=1757104412.4376817), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466133/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/b6d6913e14b52d5d51dab2f254815da89e03cebe75e80356b27cf18e31eb048f'), size_on_disk=5090722, blob_last_accessed=1757598691.169821, blob_last_modified=1757104412.180683), CachedFileInfo(file_name='genome_map.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/51978dc2125afb40a41a29672f8ae750308d9a63'), size_on_disk=5311747, blob_last_accessed=1757598690.9468212, blob_last_modified=1757104406.477712), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466113/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/acec3b041a74ab45dbeb03e7f183c2b3d16479aadd64a81f80a74aa42d56368d'), size_on_disk=5866774, blob_last_accessed=1757598690.921821, blob_last_modified=1757104409.0226989), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466141/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/e9efb807ce35fc3e43b1ff0d27b80646c7d137c587abd9d6247393e44d4f30a0'), size_on_disk=986450, blob_last_accessed=1757598691.2628207, blob_last_modified=1757104413.129678), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466115/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/c08ce1194dd98cb028a6038944317e70ac1545e32b21dd323eafa57b16611e6e'), size_on_disk=3503431, blob_last_accessed=1757598690.9468212, blob_last_modified=1757104405.9427147), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/genome_map/accession=SRR11466107/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/2f924bc58d82a21b621b051addd1913c77369afaa738171ef0962548fc1a1021'), size_on_disk=5670198, blob_last_accessed=1757598690.8608212, blob_last_modified=1757104405.0417192), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/6b29baae54286d5fbdd1c70f499c03319badad51/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/09f6dce90ec0e64d8f18614091dd18ad2e86213a'), size_on_disk=4403, blob_last_accessed=1757598690.987821, blob_last_modified=1757104409.9196944)}), refs=frozenset(), last_modified=1757104417.837654), CachedRevisionInfo(commit_hash='1e07e46fd0b5348243487ac4a573b570a2a3946b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1e07e46fd0b5348243487ac4a573b570a2a3946b'), size_on_disk=4683, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/snapshots/1e07e46fd0b5348243487ac4a573b570a2a3946b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--rossi_2021/blobs/bb690d71edd44885609ed660d926f3cf2c4c473e'), size_on_disk=4683, blob_last_accessed=1758306349.7238934, blob_last_modified=1758156991.1235294)}), refs=frozenset(), last_modified=1758156991.1235294)}), last_accessed=1762805142.7999256, last_modified=1762805108.5201466), CachedRepoInfo(repo_id='BrentLab/harbison_2004', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004'), size_on_disk=70377892, nb_files=10, revisions=frozenset({CachedRevisionInfo(commit_hash='2916ad316cc0d8a1ce2e7f35d3707b831b203e87', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/2916ad316cc0d8a1ce2e7f35d3707b831b203e87'), size_on_disk=7160, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/2916ad316cc0d8a1ce2e7f35d3707b831b203e87/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/7e3ec66f5479346ca9d082545ff0b6a960fb60d5'), size_on_disk=7160, blob_last_accessed=1758649475.233073, blob_last_modified=1758155946.7669883)}), refs=frozenset(), last_modified=1758155946.7669883), CachedRevisionInfo(commit_hash='4e54b7fafd79829bcd179b508efd11a3dc7182cc', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/4e54b7fafd79829bcd179b508efd11a3dc7182cc'), size_on_disk=44900498, files=frozenset({CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/4e54b7fafd79829bcd179b508efd11a3dc7182cc/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b89f865a471fbf3a8054871f8fe79507f6f4be5c2291dcd19030ec8fd4a5325c'), size_on_disk=44886395, blob_last_accessed=1767812938.7903292, blob_last_modified=1765314558.8410568), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/4e54b7fafd79829bcd179b508efd11a3dc7182cc/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/ef196364c79e1a0ec01b3ab6db8dae798f0f8fd5'), size_on_disk=14103, blob_last_accessed=1765911961.5656407, blob_last_modified=1765819563.7734888)}), refs=frozenset(), last_modified=1765819563.7734888), CachedRevisionInfo(commit_hash='a33c34b373e379dfa9bd4922d281790180bb1217', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a33c34b373e379dfa9bd4922d281790180bb1217'), size_on_disk=44899466, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a33c34b373e379dfa9bd4922d281790180bb1217/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/3cb3997d01aa1c91d0719f954e1cf207976c8a7d'), size_on_disk=13071, blob_last_accessed=1767811669.8189607, blob_last_modified=1765916907.339037), CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a33c34b373e379dfa9bd4922d281790180bb1217/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b89f865a471fbf3a8054871f8fe79507f6f4be5c2291dcd19030ec8fd4a5325c'), size_on_disk=44886395, blob_last_accessed=1767812938.7903292, blob_last_modified=1765314558.8410568)}), refs=frozenset({'main'}), last_modified=1765916907.339037), CachedRevisionInfo(commit_hash='3ff5f32dfeef3c0b2b7d6cc860b259b0ad095829', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/3ff5f32dfeef3c0b2b7d6cc860b259b0ad095829'), size_on_disk=25421759, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/3ff5f32dfeef3c0b2b7d6cc860b259b0ad095829/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/342343ab9f71e7ce0394c540bed737de39f2cf08'), size_on_disk=5893, blob_last_accessed=1764345374.74204, blob_last_modified=1763587990.060371), CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/3ff5f32dfeef3c0b2b7d6cc860b259b0ad095829/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/91904b3e84754bd6011cd4b385bbc99d69a899e5c7562f039a121a744ba95a0a'), size_on_disk=25415866, blob_last_accessed=1763588775.480192, blob_last_modified=1758649664.1292322)}), refs=frozenset(), last_modified=1763587990.060371), CachedRevisionInfo(commit_hash='073cdeb1b541c28b1a97df9dbc6be6af1d9c23b6', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/073cdeb1b541c28b1a97df9dbc6be6af1d9c23b6'), size_on_disk=5145, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/073cdeb1b541c28b1a97df9dbc6be6af1d9c23b6/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/f7a6cf3dd4477f08e508f38665350e8d11589963'), size_on_disk=5145, blob_last_accessed=1755876214.0380862, blob_last_modified=1755876188.1642818)}), refs=frozenset(), last_modified=1755876188.1642818), CachedRevisionInfo(commit_hash='a0f6f1b23505c4e6a471e65a43a33ff5cced0733', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a0f6f1b23505c4e6a471e65a43a33ff5cced0733'), size_on_disk=25423545, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a0f6f1b23505c4e6a471e65a43a33ff5cced0733/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b5fbd9e98fd8ddadeeb5631e3b6f5055e917c98d'), size_on_disk=7679, blob_last_accessed=1759345152.2162483, blob_last_modified=1758649637.161202), CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/a0f6f1b23505c4e6a471e65a43a33ff5cced0733/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/91904b3e84754bd6011cd4b385bbc99d69a899e5c7562f039a121a744ba95a0a'), size_on_disk=25415866, blob_last_accessed=1763588775.480192, blob_last_modified=1758649664.1292322)}), refs=frozenset(), last_modified=1758649664.1292322), CachedRevisionInfo(commit_hash='95dace55563820030e6bcac2d4b2a9a386a2369b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/95dace55563820030e6bcac2d4b2a9a386a2369b'), size_on_disk=44900425, files=frozenset({CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/95dace55563820030e6bcac2d4b2a9a386a2369b/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b89f865a471fbf3a8054871f8fe79507f6f4be5c2291dcd19030ec8fd4a5325c'), size_on_disk=44886395, blob_last_accessed=1767812938.7903292, blob_last_modified=1765314558.8410568), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/95dace55563820030e6bcac2d4b2a9a386a2369b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/1cfc9a691644fd13ba4e3c4d141caf8bc8dadc92'), size_on_disk=14030, blob_last_accessed=1765809787.9431984, blob_last_modified=1765415607.8811436)}), refs=frozenset(), last_modified=1765415607.8811436), CachedRevisionInfo(commit_hash='5eeb06e389a648a36087e20eabad1f961e22dc5a', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/5eeb06e389a648a36087e20eabad1f961e22dc5a'), size_on_disk=44894945, files=frozenset({CachedFileInfo(file_name='harbison_2004.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/5eeb06e389a648a36087e20eabad1f961e22dc5a/harbison_2004.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b89f865a471fbf3a8054871f8fe79507f6f4be5c2291dcd19030ec8fd4a5325c'), size_on_disk=44886395, blob_last_accessed=1767812938.7903292, blob_last_modified=1765314558.8410568), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/snapshots/5eeb06e389a648a36087e20eabad1f961e22dc5a/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/aa43ea37112a84a60d21ce254c1cc1e433def6dc'), size_on_disk=8550, blob_last_accessed=1765406568.606891, blob_last_modified=1765292615.6690626)}), refs=frozenset(), last_modified=1765314558.8410568)}), last_accessed=1767812938.7903292, last_modified=1765916907.339037), CachedRepoInfo(repo_id='BrentLab/hughes_2006', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006'), size_on_disk=11969274, nb_files=11, revisions=frozenset({CachedRevisionInfo(commit_hash='0de73d0932e423cfbbfbc1b21d029c96490dc200', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/0de73d0932e423cfbbfbc1b21d029c96490dc200'), size_on_disk=8791, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/0de73d0932e423cfbbfbc1b21d029c96490dc200/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/76efc047ec3c08e362cfcaac776b7e05c02f8a6b'), size_on_disk=8791, blob_last_accessed=1764712744.7980804, blob_last_modified=1764712744.7960804)}), refs=frozenset(), last_modified=1764712744.7960804), CachedRevisionInfo(commit_hash='cc20720c4fe7de9151a051aafbd41e6de2b4cd84', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84'), size_on_disk=11937630, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/842b551fb3d1ba1e6f1daa35157a0c7c616f7655'), size_on_disk=8514, blob_last_accessed=1756855276.4871445, blob_last_modified=1756855276.4861445), CachedFileInfo(file_name='parse_hughes_2006.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/scripts/parse_hughes_2006.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/0c2645bdc0ab7f6be38a428a0a92752e9d3f88d5'), size_on_disk=9255, blob_last_accessed=1756856028.5803509, blob_last_modified=1756856028.6393504), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756856028.69735, blob_last_modified=1756856028.7793496), CachedFileInfo(file_name='knockout.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/knockout.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/5e000e2e9401011c9ecc81655c94d55b7f9469ed856216fa21cef2883a904c93'), size_on_disk=5571518, blob_last_accessed=1756856029.2493467, blob_last_modified=1756856029.2143471), CachedFileInfo(file_name='metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/e27a67137876123c68c229705cfbb8e06ed3ee76a3a868b66d80d624f8d03671'), size_on_disk=7942, blob_last_accessed=1756856028.533351, blob_last_modified=1756856028.9083488), CachedFileInfo(file_name='overexpression.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/overexpression.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/9b8cc1feede780efe0e8537555ed1cbbf067e7fa9aa7a7edc74e5a343c64a443'), size_on_disk=6337781, blob_last_accessed=1756856028.536351, blob_last_modified=1756856029.230347), CachedFileInfo(file_name='checksum.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/cc20720c4fe7de9151a051aafbd41e6de2b4cd84/checksum.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/973b686313e32c96b6e0ef1dbc84020857c073ef'), size_on_disk=159, blob_last_accessed=1756856028.5883508, blob_last_modified=1756856028.6513503)}), refs=frozenset(), last_modified=1756856029.230347), CachedRevisionInfo(commit_hash='1009546961df1cf9743a03383826d8a5e010bb38', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/1009546961df1cf9743a03383826d8a5e010bb38'), size_on_disk=17152, files=frozenset({CachedFileInfo(file_name='metadata.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/1009546961df1cf9743a03383826d8a5e010bb38/metadata.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/54173aa9fff98e5b077e2b24e139ad600a6d7cea74a47b0305cb0fbdf1c14d9b'), size_on_disk=8362, blob_last_accessed=1764713263.750364, blob_last_modified=1764713263.741364), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/1009546961df1cf9743a03383826d8a5e010bb38/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/572115c6e031637a3ac876795a6f91d3653bf5e2'), size_on_disk=8790, blob_last_accessed=1764713262.7733643, blob_last_modified=1764713223.5823674)}), refs=frozenset({'main'}), last_modified=1764713263.741364), CachedRevisionInfo(commit_hash='51ae7addf429c955a97934a15b242ff8a2ccd653', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/51ae7addf429c955a97934a15b242ff8a2ccd653'), size_on_disk=5701, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/snapshots/51ae7addf429c955a97934a15b242ff8a2ccd653/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/f799807a7bf81229b0d063e6c939339c1f5c90f4'), size_on_disk=5701, blob_last_accessed=1756855057.8437808, blob_last_modified=1756855057.8417807)}), refs=frozenset(), last_modified=1756855057.8417807)}), last_accessed=1764713263.750364, last_modified=1764713263.741364), CachedRepoInfo(repo_id='BrentLab/hackett_2020', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020'), size_on_disk=817026981, nb_files=22, revisions=frozenset({CachedRevisionInfo(commit_hash='6bea8e8497a12fec83927a941ba912a0ec781822', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/6bea8e8497a12fec83927a941ba912a0ec781822'), size_on_disk=8027, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/6bea8e8497a12fec83927a941ba912a0ec781822/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/159b3e518310d0bf928d14a691e41a77d5540aaa'), size_on_disk=8027, blob_last_accessed=1765299157.242701, blob_last_modified=1765299156.9627051)}), refs=frozenset(), last_modified=1765299156.9627051), CachedRevisionInfo(commit_hash='60b3ecf6a06e709f0397b327ece5c31016303b5c', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/60b3ecf6a06e709f0397b327ece5c31016303b5c'), size_on_disk=404558833, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/60b3ecf6a06e709f0397b327ece5c31016303b5c/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/6f54100c2bb84a6bc059c17e0954dad0ea451666'), size_on_disk=9543, blob_last_accessed=1760552529.9219792, blob_last_modified=1758155372.1328666), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/60b3ecf6a06e709f0397b327ece5c31016303b5c/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1760552530.4189773, blob_last_modified=1756843405.225893)}), refs=frozenset(), last_modified=1758155372.1328666), CachedRevisionInfo(commit_hash='1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43'), size_on_disk=2678425, files=frozenset({CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=10/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4a2eb1051cad96c5b39a014f19b092f042b03700cc1b1a40c2de9e7edd73edf9'), size_on_disk=352201, blob_last_accessed=1756145253.7915416, blob_last_modified=1756145254.6375287), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=30/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/a85bd6b418d9644d9adaa1269c27f97469a4aaee51af63cf1aa041f62cd8ba2c'), size_on_disk=350044, blob_last_accessed=1756145253.7915416, blob_last_modified=1756145254.6385286), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=90/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/ebd06af83cc386d9094ce6b8d795b155c76c47c96da1a458743348264f1638ae'), size_on_disk=349917, blob_last_accessed=1756145253.7845416, blob_last_modified=1756145254.7235274), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=0/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/409962e1914ac9e9d631e4ae73e04b167822d0915d8d8d4a87703d4c7281ffb9'), size_on_disk=209695, blob_last_accessed=1756145254.7305272, blob_last_modified=1756145254.608529), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=45/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/db8595cd20dd7440c890c46025cf0643da8ec6be3e15bc18a8759b3fecdc02af'), size_on_disk=349256, blob_last_accessed=1756145253.7795417, blob_last_modified=1756145254.6225288), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=5/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/ed331d9d2b27a0958b12368c7b5d2d3383b111d3d1c39ed0e77b78561dc78b6a'), size_on_disk=348626, blob_last_accessed=1756145253.7845416, blob_last_modified=1756145254.6395288), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=15/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/51dfb68ed2518f0dd8ab4d4070d4896b6eca912ecb7e4aa773d857e3096020b5'), size_on_disk=351273, blob_last_accessed=1756145217.972108, blob_last_modified=1756142799.7054222), CachedFileInfo(file_name='part-0.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/data/regulator_locus_tag=YAL051W/time=20/mechanism=GEV/restriction=P/date=20151026/strain=yRSM209/part-0.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/97adc0bb2b436c325555d82bb7f0ffb0e250d73092fcbf03ac0c00fa2b762e51'), size_on_disk=351379, blob_last_accessed=1756145253.7895415, blob_last_modified=1756145254.6265287), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/1613b522d34bc2d1e9bfb1a6e496a3d5e1282e43/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/45cb0687c3ea72e7b32ad17dd799bc38ad02f80b'), size_on_disk=16034, blob_last_accessed=1756138543.6766906, blob_last_modified=1756138543.6746907)}), refs=frozenset(), last_modified=1756145254.7235274), CachedRevisionInfo(commit_hash='ca2500351e8aa9fc5b996516cd25a60e8298734d', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/ca2500351e8aa9fc5b996516cd25a60e8298734d'), size_on_disk=6294, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/ca2500351e8aa9fc5b996516cd25a60e8298734d/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/ebb31f16ba92f3c0a9eec2aaf4897b3a7662cfad'), size_on_disk=6294, blob_last_accessed=1764169619.6288424, blob_last_modified=1764169578.8420532)}), refs=frozenset(), last_modified=1764169578.8420532), CachedRevisionInfo(commit_hash='5b09fb31bac250b0bfc099c1bdb32bc2673e754b', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/5b09fb31bac250b0bfc099c1bdb32bc2673e754b'), size_on_disk=409731660, files=frozenset({CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/5b09fb31bac250b0bfc099c1bdb32bc2673e754b/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/10f544e6c4e4da0b411f1084cbe7f9188a842bc6578b699bdf02f3507aff6592'), size_on_disk=409722077, blob_last_accessed=1765311990.5457778, blob_last_modified=1765311990.540778), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/5b09fb31bac250b0bfc099c1bdb32bc2673e754b/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/dacac6a8023596c4c46dce14e80f861d4e4a7401'), size_on_disk=9583, blob_last_accessed=1765309535.4347303, blob_last_modified=1765308942.1208022)}), refs=frozenset(), last_modified=1765311990.540778), CachedRevisionInfo(commit_hash='7192b0e2275ba5aa3ffb3752b87f1c3595f2331a', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a'), size_on_disk=404565656, files=frozenset({CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1760552530.4189773, blob_last_modified=1756843405.225893), CachedFileInfo(file_name='hackett_2020.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/hackett_2020.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4f47951c65c799b5c74ee3782b49c16cbb038d50'), size_on_disk=55, blob_last_accessed=1757105553.3228161, blob_last_modified=1756843395.5719483), CachedFileInfo(file_name='parse_mcisaac_data.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/scripts/parse_mcisaac_data.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0'), size_on_disk=4691, blob_last_accessed=1756843395.7579472, blob_last_modified=1756843395.820947), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756843395.5059488, blob_last_modified=1756843395.5679483), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/7192b0e2275ba5aa3ffb3752b87f1c3595f2331a/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/f3899cd97da03a3543db10d76c15ee0442bb136e'), size_on_disk=9159, blob_last_accessed=1758145252.5929751, blob_last_modified=1757541416.40237)}), refs=frozenset(), last_modified=1757541416.40237), CachedRevisionInfo(commit_hash='4fef197cd055065207d66ea42aec9746b23f38c8', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/4fef197cd055065207d66ea42aec9746b23f38c8'), size_on_disk=9431, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/4fef197cd055065207d66ea42aec9746b23f38c8/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/0a3330d889673abc635b58a78c221ba5b3e36200'), size_on_disk=9431, blob_last_accessed=1765809790.786152, blob_last_modified=1765650535.7166286)}), refs=frozenset({'main'}), last_modified=1765650535.7166286), CachedRevisionInfo(commit_hash='2c196866d4f057cb5cf0adf1fe35f2c712c61025', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025'), size_on_disk=404565376, files=frozenset({CachedFileInfo(file_name='hackett_2020.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/hackett_2020.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4f47951c65c799b5c74ee3782b49c16cbb038d50'), size_on_disk=55, blob_last_accessed=1757105553.3228161, blob_last_modified=1756843395.5719483), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1760552530.4189773, blob_last_modified=1756843405.225893), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c3e72ccb1b8deba4bbfd18abe6081de7ec3914d9'), size_on_disk=8879, blob_last_accessed=1757105552.290822, blob_last_modified=1757104206.2667632), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756843395.5059488, blob_last_modified=1756843395.5679483), CachedFileInfo(file_name='parse_mcisaac_data.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/2c196866d4f057cb5cf0adf1fe35f2c712c61025/scripts/parse_mcisaac_data.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0'), size_on_disk=4691, blob_last_accessed=1756843395.7579472, blob_last_modified=1756843395.820947)}), refs=frozenset(), last_modified=1757104206.2667632), CachedRevisionInfo(commit_hash='b9d39535e175295d9cba23489a49fafebbac5ca0', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0'), size_on_disk=404565563, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/03e579e00c632cdb393c0ffafd9d15b069390e5c'), size_on_disk=9066, blob_last_accessed=1757439136.0598524, blob_last_modified=1757105945.6476595), CachedFileInfo(file_name='.gitattributes', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/.gitattributes'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/1ef325f1b111266a6b26e0196871bd78baa8c2f3'), size_on_disk=2461, blob_last_accessed=1756843395.5059488, blob_last_modified=1756843395.5679483), CachedFileInfo(file_name='parse_mcisaac_data.R', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/scripts/parse_mcisaac_data.R'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0'), size_on_disk=4691, blob_last_accessed=1756843395.7579472, blob_last_modified=1756843395.820947), CachedFileInfo(file_name='hackett_2020.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/hackett_2020.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/573bae5eeb209750fa92fd01c1ef98c50ee76ee75618783b988e36311a4dc81f'), size_on_disk=404549290, blob_last_accessed=1760552530.4189773, blob_last_modified=1756843405.225893), CachedFileInfo(file_name='hackett_2020.parquet.md5', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/snapshots/b9d39535e175295d9cba23489a49fafebbac5ca0/hackett_2020.parquet.md5'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/4f47951c65c799b5c74ee3782b49c16cbb038d50'), size_on_disk=55, blob_last_accessed=1757105553.3228161, blob_last_modified=1756843395.5719483)}), refs=frozenset(), last_modified=1757105945.6476595)}), last_accessed=1765809790.786152, last_modified=1765650535.7166286), CachedRepoInfo(repo_id='BrentLab/mahendrawada_2025', repo_type='dataset', repo_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025'), size_on_disk=94317969, nb_files=8, revisions=frozenset({CachedRevisionInfo(commit_hash='874a5dfe4052a1e71b6544a2e5b2c99ad4286c04', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/874a5dfe4052a1e71b6544a2e5b2c99ad4286c04'), size_on_disk=18603, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/874a5dfe4052a1e71b6544a2e5b2c99ad4286c04/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/685c0e56e0c68e20e3c2af8e38267095d686c03c'), size_on_disk=18603, blob_last_accessed=1758569041.2889845, blob_last_modified=1758569041.2879846)}), refs=frozenset(), last_modified=1758569041.2879846), CachedRevisionInfo(commit_hash='af5ac9dc922b7fbd14f460c2fe94b727db1e1245', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/af5ac9dc922b7fbd14f460c2fe94b727db1e1245'), size_on_disk=92323432, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/af5ac9dc922b7fbd14f460c2fe94b727db1e1245/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/8f0cf5e3dd2bb5bdc2f8fde970e31a2404481fec'), size_on_disk=20435, blob_last_accessed=1764298314.9501038, blob_last_modified=1763578711.307058), CachedFileInfo(file_name='reprocess_diffcontrol_5prime.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/af5ac9dc922b7fbd14f460c2fe94b727db1e1245/reprocess_diffcontrol_5prime.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/eba528776f8fe8da63c39ee0660527bc35f989ac6a976daf09270860cad6d735'), size_on_disk=92302997, blob_last_accessed=1763665270.5143101, blob_last_modified=1763578870.280984)}), refs=frozenset({'main'}), last_modified=1763578870.280984), CachedRevisionInfo(commit_hash='8bea431be57e21633be4174df291540b1fae212a', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/8bea431be57e21633be4174df291540b1fae212a'), size_on_disk=18603, files=frozenset({CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/8bea431be57e21633be4174df291540b1fae212a/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/6a2a5a6a8006e386c0e47a2a98151442046d3d41'), size_on_disk=18603, blob_last_accessed=1758568089.249362, blob_last_modified=1758568089.246362)}), refs=frozenset(), last_modified=1758568089.246362), CachedRevisionInfo(commit_hash='3b912489743a1797199beb023a49b14f7e3ba19d', snapshot_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/3b912489743a1797199beb023a49b14f7e3ba19d'), size_on_disk=1957331, files=frozenset({CachedFileInfo(file_name='chec_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/3b912489743a1797199beb023a49b14f7e3ba19d/chec_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/dd3e967d8c54c6056dc9fd6bdb921c3eed9d10df3a449273bd08faeb1b936765'), size_on_disk=1220601, blob_last_accessed=1759345153.538238, blob_last_modified=1758569549.9902885), CachedFileInfo(file_name='rnaseq_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/3b912489743a1797199beb023a49b14f7e3ba19d/rnaseq_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/4411789f50aab449658c100f2758ceb410022a0a30f956fd5fe41152915580f9'), size_on_disk=286605, blob_last_accessed=1759345153.5952375, blob_last_modified=1758569588.8621461), CachedFileInfo(file_name='features_mahendrawada_2025.parquet', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/3b912489743a1797199beb023a49b14f7e3ba19d/features_mahendrawada_2025.parquet'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/618ea89d880c8a1b4c6d05cc3c13a70cbb05784d10bf2b6c4fc954705203096c'), size_on_disk=431436, blob_last_accessed=1759345156.897212, blob_last_modified=1758652945.6395862), CachedFileInfo(file_name='README.md', file_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/snapshots/3b912489743a1797199beb023a49b14f7e3ba19d/README.md'), blob_path=PosixPath('/home/chase/.cache/huggingface/hub/datasets--BrentLab--mahendrawada_2025/blobs/c8cbc56ef9fad47193120cce2affd35374b8373d'), size_on_disk=18689, blob_last_accessed=1759345153.4962385, blob_last_modified=1758569549.1302917)}), refs=frozenset(), last_modified=1758652945.6395862)}), last_accessed=1764298314.9501038, last_modified=1763578870.280984)}), warnings=[CorruptedCacheException(\"Snapshots dir doesn't exist in cached repo: /home/chase/.cache/huggingface/hub/datasets--BrentLab--yeast_dto/snapshots\")])" ] }, "execution_count": 2, @@ -268,20 +268,20 @@ "text": [ "Current HuggingFace Cache Overview:\n", "========================================\n", - "Total cache size: 5.2G\n", - "Number of repositories: 10\n", + "Total cache size: 5.5G\n", + "Number of repositories: 11\n", "\n", "Largest repositories (top 5):\n", " • BrentLab/barkai_compendium: 3.6G (1 revisions)\n", " • BrentLab/hackett_2020: 817.0M (9 revisions)\n", - " • BrentLab/kemmeren_2014: 319.2M (3 revisions)\n", + " • BrentLab/kemmeren_2014: 646.2M (3 revisions)\n", " • BrentLab/rossi_2021: 213.7M (9 revisions)\n", " • BrentLab/mahendrawada_2025: 94.3M (4 revisions)\n", - " ... and 5 more repositories\n", + " ... and 6 more repositories\n", "\n", - "Total revisions across all repos: 49\n", - "Revisions older than 30 days: 34\n", - "Recent revisions (≤30 days): 15\n" + "Total revisions across all repos: 50\n", + "Revisions older than 30 days: 41\n", + "Recent revisions (≤30 days): 9\n" ] } ], @@ -563,16 +563,16 @@ "text": [ "Current HuggingFace Cache Status:\n", "===================================\n", - "Total size: 5.2G\n", - "Number of repositories: 10\n", + "Total size: 5.5G\n", + "Number of repositories: 11\n", "\n", "Repository breakdown:\n", + " • BrentLab/yeast_comparative_analysis: 166.1K (1 revisions)\n", + " • BrentLab/yeast_genome_resources: 114.5K (7 revisions)\n", " • BrentLab/barkai_compendium: 3.6G (1 revisions)\n", - " • BrentLab/mahendrawada_2025: 94.3M (4 revisions)\n", - " • BrentLab/hughes_2006: 12.0M (4 revisions)\n", - " • BrentLab/callingcards: 49.6M (3 revisions)\n", - " • BrentLab/kemmeren_2014: 319.2M (3 revisions)\n", - " ... and 5 more repositories\n", + " • BrentLab/kemmeren_2014: 646.2M (3 revisions)\n", + " • BrentLab/hu_2007_reimand_2010: 42.7M (1 revisions)\n", + " ... and 6 more repositories\n", "\n", "Target repository (BrentLab/mahendrawada_2025) cache info:\n", " Size: 94.3M\n", @@ -644,7 +644,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "INFO:__main__:Found 34 old revisions. Will free 4.6G\n", + "INFO:__main__:Found 41 old revisions. Will free 4.7G\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" ] }, @@ -654,20 +654,20 @@ "text": [ "\n", "Cleanup strategy created:\n", - "Expected space freed: 4.6G\n", - "Items to delete: 59\n", + "Expected space freed: 4.7G\n", + "Items to delete: 46\n", "\n", "Breakdown of items to delete:\n", - " • Blob files: 39\n", + " • Blob files: 27\n", " • Reference files: 0\n", - " • Repository directories: 4\n", - " • Snapshot directories: 16\n", + " • Repository directories: 7\n", + " • Snapshot directories: 12\n", "\n", "Sample blob files to delete:\n", - " • /home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c1f07d2acd1b9e5fe0679a472610fdb6b08483e0\n", - " • /home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/51dfb68ed2518f0dd8ab4d4070d4896b6eca912ecb7e4aa773d857e3096020b5\n", - " • /home/chase/.cache/huggingface/hub/datasets--BrentLab--hughes_2006/blobs/f799807a7bf81229b0d063e6c939339c1f5c90f4\n", - " ... and 36 more blob files\n" + " • /home/chase/.cache/huggingface/hub/datasets--BrentLab--harbison_2004/blobs/b5fbd9e98fd8ddadeeb5631e3b6f5055e917c98d\n", + " • /home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/a85bd6b418d9644d9adaa1269c27f97469a4aaee51af63cf1aa041f62cd8ba2c\n", + " • /home/chase/.cache/huggingface/hub/datasets--BrentLab--hackett_2020/blobs/c3e72ccb1b8deba4bbfd18abe6081de7ec3914d9\n", + " ... and 24 more blob files\n" ] } ], @@ -731,7 +731,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "INFO:__main__:Selected 16 revisions for deletion. Will free 3.6G\n", + "INFO:__main__:Selected 17 revisions for deletion. Will free 3.8G\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" ] }, @@ -741,8 +741,8 @@ "text": [ "\n", "Size-based cleanup strategy:\n", - "Expected space freed: 3.6G\n", - "Items to delete: 43\n", + "Expected space freed: 3.8G\n", + "Items to delete: 85\n", "\n", "Comparing cleanup strategies for 1GB:\n" ] @@ -751,9 +751,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "INFO:__main__:Selected 16 revisions for deletion. Will free 3.6G\n", - "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n", - "INFO:__main__:Selected 3 revisions for deletion. Will free 4.0G\n", + "INFO:__main__:Selected 17 revisions for deletion. Will free 3.8G\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" ] }, @@ -761,15 +759,14 @@ "name": "stdout", "output_type": "stream", "text": [ - " • oldest_first : 3.6G (43 items)\n", - " • largest_first : 4.0G (6 items)\n" + " • oldest_first : 3.8G (85 items)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "INFO:__main__:Selected 16 revisions for deletion. Will free 3.6G\n", + "INFO:__main__:Selected 4 revisions for deletion. Will free 4.0G\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" ] }, @@ -777,7 +774,22 @@ "name": "stdout", "output_type": "stream", "text": [ - " • least_used : 3.6G (43 items)\n" + " • largest_first : 4.0G (8 items)\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:__main__:Selected 17 revisions for deletion. Will free 3.8G\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " • least_used : 3.8G (85 items)\n" ] } ], @@ -864,27 +876,25 @@ "\n", "Per-repository revision analysis:\n", "\n", - " • BrentLab/barkai_compendium:\n", + " • BrentLab/yeast_comparative_analysis:\n", " Total revisions: 1\n", " Would keep: 1\n", " Would delete: 0\n", - " Keep: a987ef37 (modified: 1756926783.3167186)\n", + " Keep: ac03d065 (modified: 1767824941.5531375)\n", "\n", - " • BrentLab/mahendrawada_2025:\n", - " Total revisions: 4\n", + " • BrentLab/yeast_genome_resources:\n", + " Total revisions: 7\n", " Would keep: 2\n", - " Would delete: 2\n", - " Keep: af5ac9dc (modified: 1763578870.280984)\n", - " Keep: 3b912489 (modified: 1758652945.6395862)\n", - " Delete: 874a5dfe (modified: 1758569041.2879846)\n", + " Would delete: 5\n", + " Keep: 42beb284 (modified: 1758155946.5549896)\n", + " Keep: 15fdb72f (modified: 1755819093.2306638)\n", + " Delete: 7441b9a8 (modified: 1755816785.6988702)\n", "\n", - " • BrentLab/hughes_2006:\n", - " Total revisions: 4\n", - " Would keep: 2\n", - " Would delete: 2\n", - " Keep: 10095469 (modified: 1764713263.741364)\n", - " Keep: 0de73d09 (modified: 1764712744.7960804)\n", - " Delete: cc20720c (modified: 1756856029.230347)\n" + " • BrentLab/barkai_compendium:\n", + " Total revisions: 1\n", + " Would keep: 1\n", + " Would delete: 0\n", + " Keep: a987ef37 (modified: 1756926783.3167186)\n" ] } ], @@ -963,11 +973,13 @@ "name": "stderr", "output_type": "stream", "text": [ - "INFO:__main__:Found 34 old revisions. Will free 4.6G\n", + "INFO:__main__:Found 41 old revisions. Will free 4.7G\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n", "INFO:__main__:Found 31 unused revisions. Will free 642.9M\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n", - "INFO:__main__:Automated cleanup complete. Total freed: 4.9GB\n" + "INFO:__main__:Selected 9 revisions for deletion. Will free 2.8M\n", + "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n", + "INFO:__main__:Automated cleanup complete. Total freed: 5.0GB\n" ] }, { @@ -975,12 +987,13 @@ "output_type": "stream", "text": [ "\n", - "Automated cleanup executed 2 strategies:\n", - " 1. Strategy freed: 4.6G\n", + "Automated cleanup executed 3 strategies:\n", + " 1. Strategy freed: 4.7G\n", " 2. Strategy freed: 642.9M\n", + " 3. Strategy freed: 2.8M\n", "\n", - "Total space that would be freed: 4.9GB\n", - "Cache size after cleanup: 0B\n" + "Total space that would be freed: 5.0GB\n", + "Cache size after cleanup: 129.8MB\n" ] } ], @@ -1042,9 +1055,9 @@ "Demonstrating cache cleanup performance...\n", "\n", "1. Cache scanning performance:\n", - " Time to scan cache: 0.095 seconds\n", - " Repositories found: 10\n", - " Total cache size: 5.2G\n", + " Time to scan cache: 0.096 seconds\n", + " Repositories found: 11\n", + " Total cache size: 5.5G\n", "\n", "2. Cleanup strategy creation performance:\n" ] @@ -1053,7 +1066,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "INFO:__main__:Found 34 old revisions. Will free 4.6G\n", + "INFO:__main__:Found 41 old revisions. Will free 4.7G\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" ] }, @@ -1061,14 +1074,14 @@ "name": "stdout", "output_type": "stream", "text": [ - " Age cleanup strategy: 0.099 seconds\n" + " Age cleanup strategy: 0.094 seconds\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "INFO:__main__:Selected 16 revisions for deletion. Will free 3.6G\n", + "INFO:__main__:Selected 17 revisions for deletion. Will free 3.8G\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n", "INFO:__main__:Found 31 unused revisions. Will free 642.9M\n", "INFO:__main__:Dry run completed. Use dry_run=False to execute deletion\n" @@ -1078,11 +1091,11 @@ "name": "stdout", "output_type": "stream", "text": [ - " Size cleanup strategy: 0.097 seconds\n", - " Revision cleanup strategy: 0.096 seconds\n", + " Size cleanup strategy: 0.093 seconds\n", + " Revision cleanup strategy: 0.100 seconds\n", "\n", "Performance insights:\n", - "• Cache scanning is fast: 0.095s for 10 repos\n", + "• Cache scanning is fast: 0.096s for 11 repos\n", "• Cleanup strategy creation is efficient\n", "• Dry runs allow safe preview of cleanup operations\n", "• Multiple strategies can be compared quickly\n" @@ -1252,8 +1265,8 @@ "Current Session State:\n", "Repository: BrentLab/mahendrawada_2025\n", "DuckDB tables: 0\n", - "HF cache size: 5.2G\n", - "Cache repositories: 10\n" + "HF cache size: 5.5G\n", + "Cache repositories: 11\n" ] } ], @@ -1326,7 +1339,7 @@ " • Check repository access permissions\n", "\n", "2. Cache Space and Performance Issues:\n", - " Current cache size: 5.2G\n", + " Current cache size: 5.5G\n", " • Use auto_clean_cache() for automated management\n", " • Monitor cache growth with scan_cache_dir()\n", " • Set appropriate size limits for your system\n", @@ -1343,7 +1356,7 @@ "\n", "Cache Health Check:\n", "✓ DuckDB connection: DuckDB OK\n", - "✓ Cache access: 10 repositories found\n" + "✓ Cache access: 11 repositories found\n" ] }, { @@ -1432,75 +1445,6 @@ "print(f\"Logger configured: {cache_manager.logger is not None}\")\n", "print(f\"Cache management ready: ✓\")" ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 10. Summary and Best Practices\n", - "\n", - "The HfCacheManager provides a sophisticated and efficient way to manage metadata and cache for HuggingFace genomics datasets." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Key HfCacheManager Features:\n", - "\n", - "1. **3-Case Metadata Strategy**: Intelligent caching that checks DuckDB → HF cache → download\n", - "2. **Automated Cache Cleanup**: Multiple strategies (age, size, revision-based) for cache management\n", - "3. **Cache Monitoring**: Real-time cache analysis and diagnostics\n", - "4. **DuckDB Integration**: Efficient metadata storage and querying without data duplication\n", - "5. **Embedded Metadata Extraction**: Extract metadata fields from data tables into queryable views\n", - "\n", - "### Cache Management Best Practices:\n", - "\n", - "1. **Monitor Cache Size**: Regular `scan_cache_dir()` checks prevent disk space issues\n", - "2. **Proactive Cleanup**: Use `auto_clean_cache()` before large data operations\n", - "3. **Strategic Retention**: Keep recent revisions, clean old ones with `clean_unused_revisions()`\n", - "4. **Size Management**: Set cache size limits appropriate for your system resources\n", - "5. **Integration with DataCard**: Use DataCard for exploration, HfCacheManager for cache management\n", - "\n", - "### Recommended Cache Management Workflow:\n", - "\n", - "```python\n", - "# 1. Initialize cache manager\n", - "from tfbpapi.HfCacheManager import HfCacheManager\n", - "import duckdb\n", - "\n", - "conn = duckdb.connect(':memory:')\n", - "cache_mgr = HfCacheManager(repo_id, conn)\n", - "\n", - "# 2. Check current cache status\n", - "from huggingface_hub import scan_cache_dir\n", - "cache_info = scan_cache_dir()\n", - "print(f\"Current cache: {cache_info.size_on_disk_str}\")\n", - "\n", - "# 3. Automated cleanup before heavy operations\n", - "cache_mgr.auto_clean_cache(\n", - " max_total_size=\"10GB\",\n", - " max_age_days=30,\n", - " keep_latest_per_repo=2,\n", - " dry_run=False\n", - ")\n", - "\n", - "# 4. Load data (uses 3-case strategy automatically)\n", - "from tfbpapi import HfQueryAPI\n", - "query_api = HfQueryAPI(repo_id, conn)\n", - "data = query_api.get_pandas('config_name')\n", - "\n", - "# 5. Extract embedded metadata if needed\n", - "cache_mgr._extract_embedded_metadata_field(\n", - " 'data_table', 'metadata_field', 'metadata_table'\n", - ")\n", - "\n", - "# 6. Regular maintenance\n", - "cache_mgr.clean_cache_by_age(max_age_days=30, dry_run=False)\n", - "```\n", - "\n", - "This approach ensures optimal cache performance while maintaining efficient access to genomics datasets." - ] } ], "metadata": { diff --git a/docs/tutorials/datacard_tutorial.ipynb b/docs/tutorials/datacard_tutorial.ipynb index e3e8ba8..1556a1c 100644 --- a/docs/tutorials/datacard_tutorial.ipynb +++ b/docs/tutorials/datacard_tutorial.ipynb @@ -25,7 +25,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -55,7 +55,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 15, "metadata": {}, "outputs": [ { @@ -90,7 +90,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -130,7 +130,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 17, "metadata": {}, "outputs": [ { @@ -162,40 +162,6 @@ " print(f\" Features: {len(config.dataset_info.features)}\")" ] }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "ename": "AttributeError", - "evalue": "'DataCard' object has no attribute 'explore_config'", - "output_type": "error", - "traceback": [ - "\u001b[31m---------------------------------------------------------------------------\u001b[39m", - "\u001b[31mAttributeError\u001b[39m Traceback (most recent call last)", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[6]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;66;03m# Explore the main data configuration in detail\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m config_info = \u001b[43mcard\u001b[49m\u001b[43m.\u001b[49m\u001b[43mexplore_config\u001b[49m(\u001b[33m'\u001b[39m\u001b[33mharbison_2004\u001b[39m\u001b[33m'\u001b[39m)\n\u001b[32m 4\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33mMain Configuration Details:\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 5\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33m=\u001b[39m\u001b[33m\"\u001b[39m * \u001b[32m40\u001b[39m)\n", - "\u001b[31mAttributeError\u001b[39m: 'DataCard' object has no attribute 'explore_config'" - ] - } - ], - "source": [ - "# Explore the main data configuration in detail\n", - "config_info = card.explore_config('harbison_2004')\n", - "\n", - "print(\"Main Configuration Details:\")\n", - "print(\"=\" * 40)\n", - "print(f\"Config name: {config_info['config_name']}\")\n", - "print(f\"Dataset type: {config_info['dataset_type']}\")\n", - "print(f\"Number of features: {config_info['num_features']}\")\n", - "\n", - "print(\"\\nFeatures:\")\n", - "for feature in config_info['features']:\n", - " role = f\" [{feature.get('role', 'no role')}]\" if 'role' in feature else \"\"\n", - " print(f\" • {feature['name']:25} {role}\")\n", - " print(f\" {feature['description']}\")" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -222,7 +188,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 18, "metadata": {}, "outputs": [ { @@ -262,7 +228,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 19, "metadata": {}, "outputs": [ { @@ -302,31 +268,7 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Fields with Experimental Conditions:\n", - "========================================\n", - "Found 1 condition field(s): ['condition']\n" - ] - } - ], - "source": [ - "# List all experimental condition fields\n", - "condition_fields = card.list_experimental_condition_fields('harbison_2004')\n", - "\n", - "print(\"Fields with Experimental Conditions:\")\n", - "print(\"=\" * 40)\n", - "print(f\"Found {len(condition_fields)} condition field(s): {condition_fields}\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, + "execution_count": 20, "metadata": {}, "outputs": [ { @@ -370,7 +312,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 21, "metadata": {}, "outputs": [ { @@ -382,7 +324,6 @@ "{\n", " \"description\": \"Rich media baseline condition\",\n", " \"temperature_celsius\": 30,\n", - " \"cultivation_method\": \"unspecified\",\n", " \"growth_phase_at_harvest\": {\n", " \"od600\": 0.8\n", " },\n", @@ -423,7 +364,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 22, "metadata": {}, "outputs": [ { @@ -437,7 +378,6 @@ " \"initial_temperature_celsius\": 30,\n", " \"temperature_shift_celsius\": 37,\n", " \"temperature_shift_duration_minutes\": 45,\n", - " \"cultivation_method\": \"unspecified\",\n", " \"growth_phase_at_harvest\": {\n", " \"od600\": 0.5\n", " },\n", @@ -484,7 +424,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 23, "metadata": {}, "outputs": [ { @@ -525,7 +465,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 24, "metadata": {}, "outputs": [ { @@ -533,7 +473,6 @@ "text/plain": [ "{'description': 'Rich media baseline condition',\n", " 'temperature_celsius': 30,\n", - " 'cultivation_method': 'unspecified',\n", " 'growth_phase_at_harvest': {'od600': 0.8},\n", " 'media': {'name': 'YPD',\n", " 'carbon_source': [{'compound': 'D-glucose', 'concentration_percent': 2}],\n", @@ -542,7 +481,7 @@ " {'compound': 'peptone', 'concentration_percent': 2}]}}" ] }, - "execution_count": 13, + "execution_count": 24, "metadata": {}, "output_type": "execute_result" } @@ -553,7 +492,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 25, "metadata": {}, "outputs": [ { @@ -609,7 +548,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 26, "metadata": {}, "outputs": [ { diff --git a/docs/tutorials/virtual_db_tutorial.ipynb b/docs/tutorials/virtual_db_tutorial.ipynb index 2ea0f72..c5f87f6 100644 --- a/docs/tutorials/virtual_db_tutorial.ipynb +++ b/docs/tutorials/virtual_db_tutorial.ipynb @@ -1,1267 +1,3087 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# VirtualDB Tutorial: Unified Cross-Dataset Queries\n", - "\n", - "The `VirtualDB` class provides a unified query interface across heterogeneous datasets with different experimental condition structures and terminologies. Each dataset defines conditions in its own way, with properties at different hierarchy levels and using different naming conventions. VirtualDB uses external YAML configuration to:\n", - "\n", - "- Map varying structures to a common schema\n", - "- Normalize factor level names (e.g., \"D-glucose\", \"dextrose\", \"glu\" all become \"glucose\")\n", - "- Enable cross-dataset queries with standardized field names and values\n", - "\n", - "In this tutorial, we'll explore how to use VirtualDB to query and compare data across multiple datasets." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 1. Introduction: Why VirtualDB?\n", - "\n", - "### The Problem\n", - "\n", - "When working with multiple genomic datasets, you'll encounter:\n", - "\n", - "1. **Inconsistent terminology**: One dataset uses \"D-glucose\", another uses \"glucose\", yet another uses \"glu\"\n", - "2. **Different hierarchies**: Conditions stored at repo-level vs config-level vs field-level\n", - "3. **Varying structures**: Different nesting patterns for the same logical information\n", - "\n", - "### The Solution\n", - "\n", - "VirtualDB abstracts these differences:\n", - "\n", - "- **Common schema**: Query all datasets using standardized field names\n", - "- **Automatic normalization**: \"D-glucose\" and \"glucose\" both match `carbon_source=\"glucose\"`\n", - "- **Unified interface**: Single query retrieves data from multiple datasets" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2. Basic Setup\n", - "\n", - "VirtualDB requires a YAML configuration file that defines:\n", - "- Which datasets to include\n", - "- How to map their fields to common names\n", - "- How to normalize varying terminologies" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ + "cells": [ { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chase/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", - " from .autonotebook import tqdm as notebook_tqdm\n" - ] + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# VirtualDB Tutorial: Unified Cross-Dataset Queries\n", + "\n", + "The `VirtualDB` class provides a unified query interface across heterogeneous datasets with different experimental condition structures and terminologies. Each dataset defines conditions in its own way, with properties at different hierarchy levels and using different naming conventions. VirtualDB uses external YAML configuration to:\n", + "\n", + "- Map varying structures to a common schema\n", + "- Normalize factor level names (e.g., \"D-glucose\", \"dextrose\", \"glu\" all become \"glucose\")\n", + "- Enable cross-dataset queries with standardized field names and values\n", + "\n", + "In this tutorial, we'll explore how to use VirtualDB to query and compare data across multiple datasets." + ] }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "Configuration saved to: /tmp/tmpi90ps0mq/vdb_config.yaml\n" - ] - } - ], - "source": [ - "from tfbpapi.virtual_db import VirtualDB\n", - "import pandas as pd\n", - "\n", - "# For this tutorial, we'll create a sample configuration\n", - "# In practice, you'd load this from a YAML file\n", - "config_yaml = \"\"\"\n", - "repositories:\n", - " BrentLab/harbison_2004:\n", - " dataset:\n", - " harbison_2004:\n", - " carbon_source:\n", - " field: condition\n", - " path: media.carbon_source.compound\n", - " temperature_celsius:\n", - " field: condition\n", - " path: temperature_celsius\n", - " environmental_condition:\n", - " field: condition\n", - "\n", - " BrentLab/kemmeren_2014:\n", - " dataset:\n", - " kemmeren_2014:\n", - " carbon_source:\n", - " path: media.carbon_source.compound\n", - " temperature_celsius:\n", - " path: temperature_celsius\n", - "\n", - "factor_aliases:\n", - " carbon_source:\n", - " glucose: [D-glucose, dextrose, glu]\n", - " galactose: [D-galactose, gal]\n", - " raffinose: [D-raffinose]\n", - "\n", - "missing_value_labels:\n", - " carbon_source: \"unspecified\"\n", - "\n", - "description:\n", - " carbon_source: The carbon source provided during growth\n", - " temperature_celsius: Growth temperature in degrees Celsius\n", - " environmental_condition: Named environmental condition\n", - "\"\"\"\n", - "\n", - "# Save config to temporary file\n", - "import tempfile\n", - "from pathlib import Path\n", - "\n", - "temp_config = Path(tempfile.mkdtemp()) / \"vdb_config.yaml\"\n", - "temp_config.write_text(config_yaml)\n", - "\n", - "print(f\"Configuration saved to: {temp_config}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "VirtualDB initialized successfully!\n", - "Configured repositories: 2\n" - ] - } - ], - "source": [ - "# Initialize VirtualDB with the configuration\n", - "vdb = VirtualDB(str(temp_config))\n", - "\n", - "print(\"VirtualDB initialized successfully!\")\n", - "print(f\"Configured repositories: {len(vdb.config.repositories)}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 3. Schema Discovery\n", - "\n", - "Before querying, let's explore what fields are available." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "All available fields:\n", - " - carbon_source\n", - " - environmental_condition\n", - " - temperature_celsius\n" - ] - } - ], - "source": [ - "# Get all fields defined in any dataset\n", - "all_fields = vdb.get_fields()\n", - "\n", - "print(\"All available fields:\")\n", - "for field in sorted(all_fields):\n", - " print(f\" - {field}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating a VirtualDB Specification\n", + "\n", + "VirtualDB requires a YAML configuration file that defines:\n", + "- Which datasets to include\n", + "- How to map their fields to common names\n", + "- How to normalize factor levels" + ] + }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "Common fields (present in all datasets):\n", - " - carbon_source\n", - " - temperature_celsius\n" - ] - } - ], - "source": [ - "# Get fields present in ALL datasets (common fields)\n", - "common_fields = vdb.get_common_fields()\n", - "\n", - "print(\"Common fields (present in all datasets):\")\n", - "for field in sorted(common_fields):\n", - " print(f\" - {field}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Configuration saved to: /tmp/tmpnetd9hv1/vdb_config.yaml\n" + ] + } + ], + "source": [ + "# For this tutorial, we'll create a sample configuration\n", + "# In practice, you'd load this from a YAML file\n", + "config_yaml = \"\"\"\n", + "repositories:\n", + " BrentLab/harbison_2004:\n", + " dataset:\n", + " harbison_2004:\n", + " sample_id:\n", + " field: sample_id\n", + " carbon_source:\n", + " field: condition\n", + " path: media.carbon_source.compound\n", + " temperature_celsius:\n", + " field: condition\n", + " path: temperature_celsius\n", + " dtype: numeric\n", + " environmental_condition:\n", + " field: condition\n", + "\n", + " comparative_analyses:\n", + " - repo: BrentLab/yeast_comparative_analysis\n", + " dataset: dto\n", + " via_field: binding_id\n", + "\n", + " BrentLab/kemmeren_2014:\n", + " dataset:\n", + " kemmeren_2014:\n", + " sample_id:\n", + " field: sample_id\n", + " carbon_source:\n", + " path: media.carbon_source.compound\n", + " temperature_celsius:\n", + " path: temperature_celsius\n", + " dtype: numeric\n", + "\n", + " comparative_analyses:\n", + " - repo: BrentLab/yeast_comparative_analysis\n", + " dataset: dto\n", + " via_field: perturbation_id\n", + "\n", + "factor_aliases:\n", + " carbon_source:\n", + " glucose: [D-glucose, dextrose, glu]\n", + " galactose: [D-galactose, gal]\n", + " raffinose: [D-raffinose]\n", + "\n", + "missing_value_labels:\n", + " carbon_source: \"unspecified\"\n", + "\n", + "description:\n", + " carbon_source: The carbon source provided during growth\n", + " temperature_celsius: Growth temperature in degrees Celsius\n", + " environmental_condition: Named environmental condition\n", + "\"\"\"\n", + "\n", + "# Save config to temporary file\n", + "import tempfile\n", + "from pathlib import Path\n", + "\n", + "temp_config = Path(tempfile.mkdtemp()) / \"vdb_config.yaml\"\n", + "temp_config.write_text(config_yaml)\n", + "\n", + "print(f\"Configuration saved to: {temp_config}\")" + ] + }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "Fields in harbison_2004:\n", - " - carbon_source\n", - " - environmental_condition\n", - " - temperature_celsius\n" - ] - } - ], - "source": [ - "# Get fields for a specific dataset\n", - "harbison_fields = vdb.get_fields(\"BrentLab/harbison_2004\", \"harbison_2004\")\n", - "\n", - "print(\"Fields in harbison_2004:\")\n", - "for field in sorted(harbison_fields):\n", - " print(f\" - {field}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Discovering Valid Values\n", - "\n", - "VirtualDB can tell you what values exist for each field." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chase/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "VirtualDB initialized successfully!\n", + "Configured repositories: 2\n" + ] + } + ], + "source": [ + "from tfbpapi.virtual_db import VirtualDB\n", + "\n", + "# Initialize VirtualDB with the configuration\n", + "vdb = VirtualDB(str(temp_config))\n", + "\n", + "print(\"VirtualDB initialized successfully!\")\n", + "print(f\"Configured repositories: {len(vdb.config.repositories)}\")" + ] + }, { - "name": "stderr", - "output_type": "stream", - "text": [ - "Fetching 1 files: 100%|██████████| 1/1 [00:09<00:00, 9.60s/it]" - ] + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Schema Discovery\n", + "\n", + "The VirtualDB class provides methods to inspect the unified schema after loading the\n", + "configuration." + ] }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "Unique carbon sources (normalized):\n", - " - unspecified\n" - ] + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "All available fields:\n", + " - carbon_source\n", + " - environmental_condition\n", + " - sample_id\n", + " - temperature_celsius\n" + ] + } + ], + "source": [ + "# Get all fields defined in any dataset\n", + "all_fields = vdb.get_fields()\n", + "\n", + "print(\"All available fields:\")\n", + "for field in sorted(all_fields):\n", + " print(f\" - {field}\")" + ] }, { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], - "source": [ - "# Get all unique values for a field (normalized)\n", - "carbon_sources = vdb.get_unique_values(\"carbon_source\")\n", - "\n", - "print(\"Unique carbon sources (normalized):\")\n", - "for source in sorted(carbon_sources):\n", - " print(f\" - {source}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Common fields (present in all datasets):\n", + " - carbon_source\n", + " - temperature_celsius\n" + ] + } + ], + "source": [ + "# Get fields present in ALL datasets (common fields)\n", + "common_fields = vdb.get_common_fields()\n", + "\n", + "print(\"Common fields (present in all datasets):\")\n", + "for field in sorted(common_fields):\n", + " print(f\" - {field}\")" + ] + }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "Carbon sources by dataset:\n", - "\n", - "BrentLab/kemmeren_2014/kemmeren_2014:\n", - " - unspecified\n" - ] - } - ], - "source": [ - "# Get values broken down by dataset\n", - "carbon_by_dataset = vdb.get_unique_values(\"carbon_source\", by_dataset=True)\n", - "\n", - "print(\"Carbon sources by dataset:\")\n", - "for dataset, sources in carbon_by_dataset.items():\n", - " print(f\"\\n{dataset}:\")\n", - " for source in sorted(sources):\n", - " print(f\" - {source}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 4. Simple Queries\n", - "\n", - "Now let's start querying data. The `query()` method is the primary interface." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Basic Query: All Samples with Glucose\n", - "\n", - "By default, queries return sample-level data (one row per sample) with all configured fields." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Datasets with comparative data\n", + "\n", + "\n", + "BrentLab/harbison_2004/harbison_2004:\n", + " - BrentLab/yeast_comparative_analysis/dto\n", + " via field: binding_id\n", + " fields available: 8\n", + "\n", + "BrentLab/kemmeren_2014/kemmeren_2014:\n", + " - BrentLab/yeast_comparative_analysis/dto\n", + " via field: perturbation_id\n", + " fields available: 8\n", + "Comparative data fields\n", + "\n", + "BrentLab/yeast_comparative_analysis/dto:\n", + " binding_id binding_rank_threshold binding_set_size \n", + " dto_empirical_pvalue dto_fdr perturbation_id \n", + " perturbation_rank_threshold perturbation_set_size \n" + ] + } + ], + "source": [ + "# Get fields that may be used to filter two or more datasets at a time\n", + "comp_info = vdb.get_comparative_analyses()\n", + "\n", + "print(\"Datasets with comparative data\\n\")\n", + "for primary_dataset, comparatives in sorted(comp_info[\"primary_to_comparative\"].items()):\n", + " print(f\"\\n{primary_dataset}:\")\n", + " for comp in comparatives:\n", + " comp_key = f\"{comp['comparative_repo']}/{comp['comparative_dataset']}\"\n", + " print(f\" - {comp_key}\")\n", + " print(f\" via field: {comp['via_field']}\")\n", + " num_fields = len(comp_info[\"comparative_fields\"].get(comp_key, []))\n", + " print(f\" fields available: {num_fields}\")\n", + "\n", + "# Show fields available from comparative datasets\n", + "print(\"Comparative data fields\")\n", + "for comp_dataset, fields in sorted(comp_info[\"comparative_fields\"].items()):\n", + " print(f\"\\n{comp_dataset}:\")\n", + " if fields:\n", + " # Print in columns for better readability\n", + " fields_sorted = sorted(fields)\n", + " for i in range(0, len(fields_sorted), 3):\n", + " row_fields = fields_sorted[i:i + 3]\n", + " print(\" \" + \" \".join(f\"{f:<28}\" for f in row_fields))\n", + " else:\n", + " print(\" (no fields found)\")" + ] + }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "Found 0 samples with glucose\n", - "\n", - "Columns: ['sample_id', 'regulator_locus_tag', 'regulator_symbol', 'carbon_source', 'dataset_id']\n", - "\n", - "First few rows:\n" - ] + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Discovering Valid Values\n", + "\n", + "VirtualDB can tell you what values exist for each field." + ] }, { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ - { - "name": "index", - "rawType": "int64", - "type": "integer" - }, + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ { - "name": "sample_id", - "rawType": "int32", - "type": "integer" - }, - { - "name": "regulator_locus_tag", - "rawType": "object", - "type": "string" - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "Unique carbon sources (normalized):\n", + " - galactose\n", + " - glucose\n", + " - raffinose\n", + " - unspecified\n" + ] + } + ], + "source": [ + "# Get all unique values for a field (normalized)\n", + "carbon_source_factor_levels = vdb.get_unique_values(\"carbon_source\")\n", + "\n", + "print(\"Unique carbon sources (normalized):\")\n", + "for source in sorted(carbon_source_factor_levels):\n", + " print(f\" - {source}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ { - "name": "regulator_symbol", - "rawType": "object", - "type": "string" - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "Carbon sources by dataset:\n", + "\n", + "BrentLab/harbison_2004/harbison_2004:\n", + " - galactose\n", + " - glucose\n", + " - raffinose\n", + " - unspecified\n", + "\n", + "BrentLab/kemmeren_2014/kemmeren_2014:\n", + " - glucose\n" + ] + } + ], + "source": [ + "# Get values broken down by dataset\n", + "carbon_by_dataset = vdb.get_unique_values(\"carbon_source\", by_dataset=True)\n", + "\n", + "print(\"Carbon sources by dataset:\")\n", + "for dataset, sources in carbon_by_dataset.items():\n", + " print(f\"\\n{dataset}:\")\n", + " for source in sorted(sources):\n", + " print(f\" - {source}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4. Simple Queries\n", + "\n", + "Now let's start querying data. The `query()` method is the primary interface." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Basic Query: All Samples with Glucose\n", + "\n", + "By default, queries return sample-level data (one row per sample) with all configured fields." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ { - "name": "carbon_source", - "rawType": "object", - "type": "string" + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 1797 samples with glucose\n", + "\n", + "Columns: ['sample_id', 'regulator_locus_tag', 'regulator_symbol', 'condition', 'carbon_source', 'temperature_celsius', 'dataset_id']\n", + "\n", + "First few rows:\n" + ] }, { - "name": "dataset_id", - "rawType": "object", - "type": "string" + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "sample_id", + "rawType": "int32", + "type": "integer" + }, + { + "name": "regulator_locus_tag", + "rawType": "object", + "type": "string" + }, + { + "name": "regulator_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "condition", + "rawType": "object", + "type": "string" + }, + { + "name": "carbon_source", + "rawType": "object", + "type": "string" + }, + { + "name": "temperature_celsius", + "rawType": "float64", + "type": "float" + }, + { + "name": "dataset_id", + "rawType": "object", + "type": "string" + } + ], + "ref": "b22d63b4-294f-452a-b64e-f79320f4da61", + "rows": [ + [ + "0", + "1", + "YSC0017", + "MATA1", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "1", + "2", + "YAL051W", + "OAF1", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "2", + "3", + "YBL005W", + "PDR3", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "3", + "4", + "YBL008W", + "HIR1", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "4", + "5", + "YBL021C", + "HAP3", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ] + ], + "shape": { + "columns": 7, + "rows": 5 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sample_idregulator_locus_tagregulator_symbolconditioncarbon_sourcetemperature_celsiusdataset_id
01YSC0017MATA1YPDglucose30.0BrentLab/harbison_2004/harbison_2004
12YAL051WOAF1YPDglucose30.0BrentLab/harbison_2004/harbison_2004
23YBL005WPDR3YPDglucose30.0BrentLab/harbison_2004/harbison_2004
34YBL008WHIR1YPDglucose30.0BrentLab/harbison_2004/harbison_2004
45YBL021CHAP3YPDglucose30.0BrentLab/harbison_2004/harbison_2004
\n", + "
" + ], + "text/plain": [ + " sample_id regulator_locus_tag regulator_symbol condition carbon_source \\\n", + "0 1 YSC0017 MATA1 YPD glucose \n", + "1 2 YAL051W OAF1 YPD glucose \n", + "2 3 YBL005W PDR3 YPD glucose \n", + "3 4 YBL008W HIR1 YPD glucose \n", + "4 5 YBL021C HAP3 YPD glucose \n", + "\n", + " temperature_celsius dataset_id \n", + "0 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "1 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "2 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "3 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "4 30.0 BrentLab/harbison_2004/harbison_2004 " + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" } - ], - "ref": "57bb029a-d940-4e06-ae20-6eeae41e836a", - "rows": [], - "shape": { - "columns": 5, - "rows": 0 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sample_idregulator_locus_tagregulator_symbolcarbon_sourcedataset_id
\n", - "
" ], - "text/plain": [ - "Empty DataFrame\n", - "Columns: [sample_id, regulator_locus_tag, regulator_symbol, carbon_source, dataset_id]\n", - "Index: []" + "source": [ + "# Query all datasets for samples grown on glucose\n", + "glucose_samples = vdb.query(filters={\"carbon_source\": \"glucose\"})\n", + "\n", + "print(f\"Found {len(glucose_samples)} samples with glucose\")\n", + "print(f\"\\nColumns: {list(glucose_samples.columns)}\")\n", + "print(f\"\\nFirst few rows:\")\n", + "glucose_samples.head()" ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Query all datasets for samples grown on glucose\n", - "glucose_samples = vdb.query(filters={\"carbon_source\": \"glucose\"})\n", - "\n", - "print(f\"Found {len(glucose_samples)} samples with glucose\")\n", - "print(f\"\\nColumns: {list(glucose_samples.columns)}\")\n", - "print(f\"\\nFirst few rows:\")\n", - "glucose_samples.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Query Specific Datasets\n", - "\n", - "Limit your query to specific datasets using the `datasets` parameter." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ + }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "Found 0 samples from harbison_2004\n" - ] + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Query Specific Datasets\n", + "\n", + "Limit your query to specific datasets using the `datasets` parameter." + ] }, { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ { - "name": "index", - "rawType": "int64", - "type": "integer" + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 310 samples from harbison_2004\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "sample_id", + "rawType": "int32", + "type": "integer" + }, + { + "name": "regulator_locus_tag", + "rawType": "object", + "type": "string" + }, + { + "name": "regulator_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "condition", + "rawType": "object", + "type": "string" + }, + { + "name": "carbon_source", + "rawType": "object", + "type": "string" + }, + { + "name": "temperature_celsius", + "rawType": "float64", + "type": "float" + }, + { + "name": "dataset_id", + "rawType": "object", + "type": "string" + } + ], + "ref": "0c9ebf95-0bf1-46d7-83ee-57b87c5def44", + "rows": [ + [ + "0", + "1", + "YSC0017", + "MATA1", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "1", + "2", + "YAL051W", + "OAF1", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "2", + "3", + "YBL005W", + "PDR3", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "3", + "4", + "YBL008W", + "HIR1", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "4", + "5", + "YBL021C", + "HAP3", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ] + ], + "shape": { + "columns": 7, + "rows": 5 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sample_idregulator_locus_tagregulator_symbolconditioncarbon_sourcetemperature_celsiusdataset_id
01YSC0017MATA1YPDglucose30.0BrentLab/harbison_2004/harbison_2004
12YAL051WOAF1YPDglucose30.0BrentLab/harbison_2004/harbison_2004
23YBL005WPDR3YPDglucose30.0BrentLab/harbison_2004/harbison_2004
34YBL008WHIR1YPDglucose30.0BrentLab/harbison_2004/harbison_2004
45YBL021CHAP3YPDglucose30.0BrentLab/harbison_2004/harbison_2004
\n", + "
" + ], + "text/plain": [ + " sample_id regulator_locus_tag regulator_symbol condition carbon_source \\\n", + "0 1 YSC0017 MATA1 YPD glucose \n", + "1 2 YAL051W OAF1 YPD glucose \n", + "2 3 YBL005W PDR3 YPD glucose \n", + "3 4 YBL008W HIR1 YPD glucose \n", + "4 5 YBL021C HAP3 YPD glucose \n", + "\n", + " temperature_celsius dataset_id \n", + "0 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "1 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "2 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "3 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "4 30.0 BrentLab/harbison_2004/harbison_2004 " + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" } - ], - "ref": "a0d7c9a2-a3e0-4146-86ec-12e51a2e6968", - "rows": [], - "shape": { - "columns": 0, - "rows": 0 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
\n", - "
" ], - "text/plain": [ - "Empty DataFrame\n", - "Columns: []\n", - "Index: []" + "source": [ + "# Query only harbison_2004\n", + "harbison_glucose = vdb.query(\n", + " filters={\"carbon_source\": \"glucose\"},\n", + " datasets=[(\"BrentLab/harbison_2004\", \"harbison_2004\")]\n", + ")\n", + "\n", + "print(f\"Found {len(harbison_glucose)} samples from harbison_2004\")\n", + "harbison_glucose.head()" ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Query only harbison_2004\n", - "harbison_glucose = vdb.query(\n", - " filters={\"carbon_source\": \"glucose\"},\n", - " datasets=[(\"BrentLab/harbison_2004\", \"harbison_2004\")]\n", - ")\n", - "\n", - "print(f\"Found {len(harbison_glucose)} samples from harbison_2004\")\n", - "harbison_glucose.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Select Specific Fields\n", - "\n", - "Return only the fields you need with the `fields` parameter." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ + }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "Columns: ['sample_id', 'carbon_source', 'dataset_id']\n" - ] + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Select Specific Fields\n", + "\n", + "Return only the fields you need with the `fields` parameter." + ] }, { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ { - "name": "index", - "rawType": "int64", - "type": "integer" + "name": "stdout", + "output_type": "stream", + "text": [ + "Columns: ['sample_id', 'carbon_source', 'temperature_celsius', 'dataset_id']\n" + ] }, { - "name": "sample_id", - "rawType": "int32", - "type": "integer" - }, + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "sample_id", + "rawType": "int32", + "type": "integer" + }, + { + "name": "carbon_source", + "rawType": "object", + "type": "string" + }, + { + "name": "temperature_celsius", + "rawType": "float64", + "type": "float" + }, + { + "name": "dataset_id", + "rawType": "object", + "type": "string" + } + ], + "ref": "38c6fa44-08cf-4751-9476-7bca3cb1c41c", + "rows": [ + [ + "0", + "1", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "1", + "2", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "2", + "3", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "3", + "4", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "4", + "5", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ] + ], + "shape": { + "columns": 4, + "rows": 5 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sample_idcarbon_sourcetemperature_celsiusdataset_id
01glucose30.0BrentLab/harbison_2004/harbison_2004
12glucose30.0BrentLab/harbison_2004/harbison_2004
23glucose30.0BrentLab/harbison_2004/harbison_2004
34glucose30.0BrentLab/harbison_2004/harbison_2004
45glucose30.0BrentLab/harbison_2004/harbison_2004
\n", + "
" + ], + "text/plain": [ + " sample_id carbon_source temperature_celsius \\\n", + "0 1 glucose 30.0 \n", + "1 2 glucose 30.0 \n", + "2 3 glucose 30.0 \n", + "3 4 glucose 30.0 \n", + "4 5 glucose 30.0 \n", + "\n", + " dataset_id \n", + "0 BrentLab/harbison_2004/harbison_2004 \n", + "1 BrentLab/harbison_2004/harbison_2004 \n", + "2 BrentLab/harbison_2004/harbison_2004 \n", + "3 BrentLab/harbison_2004/harbison_2004 \n", + "4 BrentLab/harbison_2004/harbison_2004 " + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Get just sample_id, carbon_source, and temperature\n", + "minimal_data = vdb.query(\n", + " filters={\"carbon_source\": \"glucose\"},\n", + " fields=[\"sample_id\", \"carbon_source\", \"temperature_celsius\"]\n", + ")\n", + "\n", + "print(f\"Columns: {list(minimal_data.columns)}\")\n", + "minimal_data.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 5. Advanced Queries\n", + "\n", + "VirtualDB supports more sophisticated query patterns." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Multiple Filter Conditions" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ { - "name": "carbon_source", - "rawType": "object", - "type": "string" + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 1791 samples with glucose at 30C\n" + ] }, { - "name": "dataset_id", - "rawType": "object", - "type": "string" + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "sample_id", + "rawType": "int32", + "type": "integer" + }, + { + "name": "regulator_locus_tag", + "rawType": "object", + "type": "string" + }, + { + "name": "regulator_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "condition", + "rawType": "object", + "type": "string" + }, + { + "name": "carbon_source", + "rawType": "object", + "type": "string" + }, + { + "name": "temperature_celsius", + "rawType": "float64", + "type": "float" + }, + { + "name": "dataset_id", + "rawType": "object", + "type": "string" + } + ], + "ref": "357f69ed-ad79-4401-8458-5a1cc48f14c5", + "rows": [ + [ + "0", + "1", + "YSC0017", + "MATA1", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "1", + "2", + "YAL051W", + "OAF1", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "2", + "3", + "YBL005W", + "PDR3", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "3", + "4", + "YBL008W", + "HIR1", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "4", + "5", + "YBL021C", + "HAP3", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ] + ], + "shape": { + "columns": 7, + "rows": 5 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sample_idregulator_locus_tagregulator_symbolconditioncarbon_sourcetemperature_celsiusdataset_id
01YSC0017MATA1YPDglucose30.0BrentLab/harbison_2004/harbison_2004
12YAL051WOAF1YPDglucose30.0BrentLab/harbison_2004/harbison_2004
23YBL005WPDR3YPDglucose30.0BrentLab/harbison_2004/harbison_2004
34YBL008WHIR1YPDglucose30.0BrentLab/harbison_2004/harbison_2004
45YBL021CHAP3YPDglucose30.0BrentLab/harbison_2004/harbison_2004
\n", + "
" + ], + "text/plain": [ + " sample_id regulator_locus_tag regulator_symbol condition carbon_source \\\n", + "0 1 YSC0017 MATA1 YPD glucose \n", + "1 2 YAL051W OAF1 YPD glucose \n", + "2 3 YBL005W PDR3 YPD glucose \n", + "3 4 YBL008W HIR1 YPD glucose \n", + "4 5 YBL021C HAP3 YPD glucose \n", + "\n", + " temperature_celsius dataset_id \n", + "0 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "1 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "2 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "3 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "4 30.0 BrentLab/harbison_2004/harbison_2004 " + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" } - ], - "ref": "6dffa2f6-a87e-4ee2-ba81-22829fb14d26", - "rows": [], - "shape": { - "columns": 3, - "rows": 0 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sample_idcarbon_sourcedataset_id
\n", - "
" ], - "text/plain": [ - "Empty DataFrame\n", - "Columns: [sample_id, carbon_source, dataset_id]\n", - "Index: []" + "source": [ + "# Samples with glucose at 30C\n", + "glucose_30c = vdb.query(\n", + " filters={\n", + " \"carbon_source\": \"glucose\",\n", + " \"temperature_celsius\": 30\n", + " }\n", + ")\n", + "\n", + "print(f\"Found {len(glucose_30c)} samples with glucose at 30C\")\n", + "glucose_30c.head()" ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Get just sample_id, carbon_source, and temperature\n", - "minimal_data = vdb.query(\n", - " filters={\"carbon_source\": \"glucose\"},\n", - " fields=[\"sample_id\", \"carbon_source\", \"temperature_celsius\"]\n", - ")\n", - "\n", - "print(f\"Columns: {list(minimal_data.columns)}\")\n", - "minimal_data.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 5. Advanced Queries\n", - "\n", - "VirtualDB supports more sophisticated query patterns." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Multiple Filter Conditions" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ + }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "Found 0 samples with glucose at 30C\n" - ] + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Numeric Range Queries" + ] }, { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ { - "name": "index", - "rawType": "int64", - "type": "integer" - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 1833 samples at >= 30C\n", + "Found 1833 samples between 28-32C\n" + ] + } + ], + "source": [ + "# Samples at temperature >= 30C\n", + "warm_samples = vdb.query(\n", + " filters={\"temperature_celsius\": (\">=\", 30)}\n", + ")\n", + "\n", + "print(f\"Found {len(warm_samples)} samples at >= 30C\")\n", + "\n", + "# Samples between 28C and 32C\n", + "moderate_temp = vdb.query(\n", + " filters={\"temperature_celsius\": (\"between\", 28, 32)}\n", + ")\n", + "\n", + "print(f\"Found {len(moderate_temp)} samples between 28-32C\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Factor Alias Expansion\n", + "\n", + "When you query for a normalized value, VirtualDB automatically expands to all original aliases." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ { - "name": "sample_id", - "rawType": "int32", - "type": "integer" - }, + "name": "stdout", + "output_type": "stream", + "text": [ + " sample_id regulator_locus_tag regulator_symbol condition carbon_source \\\n", + "0 68 YDR277C MTH1 GAL galactose \n", + "1 112 YGL035C MIG1 GAL galactose \n", + "2 197 YKL038W RGT1 GAL galactose \n", + "3 335 YPL248C GAL4 GAL galactose \n", + "\n", + " temperature_celsius dataset_id \n", + "0 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "1 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "2 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "3 30.0 BrentLab/harbison_2004/harbison_2004 \n" + ] + } + ], + "source": [ + "# Query for \"galactose\" matches \"D-galactose\", \"gal\", and \"galactose\"\n", + "galactose_samples = vdb.query(filters={\"carbon_source\": \"galactose\"})\n", + "\n", + "print(galactose_samples)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Complete Data Retrieval\n", + "\n", + "By default, `query()` returns sample-level metadata (one row per sample). \n", + "Set `complete=True` to get all measurements (many rows per sample)." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ { - "name": "regulator_locus_tag", - "rawType": "object", - "type": "string" + "name": "stdout", + "output_type": "stream", + "text": [ + "Complete data: 1930060 rows\n", + "Columns: ['sample_id', 'db_id', 'target_locus_tag', 'target_symbol', 'effect', 'pvalue', 'regulator_locus_tag', 'regulator_symbol', 'condition', 'carbon_source', 'temperature_celsius', 'dataset_id']\n", + "\n", + "First few measurements:\n" + ] }, { - "name": "regulator_symbol", - "rawType": "object", - "type": "string" - }, + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "sample_id", + "rawType": "int32", + "type": "integer" + }, + { + "name": "db_id", + "rawType": "float64", + "type": "float" + }, + { + "name": "target_locus_tag", + "rawType": "object", + "type": "string" + }, + { + "name": "target_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "effect", + "rawType": "float64", + "type": "float" + }, + { + "name": "pvalue", + "rawType": "float64", + "type": "float" + }, + { + "name": "regulator_locus_tag", + "rawType": "object", + "type": "string" + }, + { + "name": "regulator_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "condition", + "rawType": "object", + "type": "string" + }, + { + "name": "carbon_source", + "rawType": "object", + "type": "string" + }, + { + "name": "temperature_celsius", + "rawType": "float64", + "type": "float" + }, + { + "name": "dataset_id", + "rawType": "object", + "type": "string" + } + ], + "ref": "0b10b74e-6f1a-42af-8654-7811d039bfac", + "rows": [ + [ + "0", + "1", + "0.0", + "YAL001C", + "TFC3", + "1.697754", + "0.068704735", + "YSC0017", + "MATA1", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "1", + "1", + "0.0", + "YAL002W", + "VPS8", + null, + null, + "YSC0017", + "MATA1", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "2", + "1", + "0.0", + "YAL003W", + "EFB1", + null, + null, + "YSC0017", + "MATA1", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "3", + "1", + "0.0", + "YAL004W", + "YAL004W", + "0.74534215", + "0.83592938", + "YSC0017", + "MATA1", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "4", + "1", + "0.0", + "YAL005C", + "SSA1", + null, + null, + "YSC0017", + "MATA1", + "YPD", + "glucose", + "30.0", + "BrentLab/harbison_2004/harbison_2004" + ] + ], + "shape": { + "columns": 12, + "rows": 5 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sample_iddb_idtarget_locus_tagtarget_symboleffectpvalueregulator_locus_tagregulator_symbolconditioncarbon_sourcetemperature_celsiusdataset_id
010.0YAL001CTFC31.6977540.068705YSC0017MATA1YPDglucose30.0BrentLab/harbison_2004/harbison_2004
110.0YAL002WVPS8NaNNaNYSC0017MATA1YPDglucose30.0BrentLab/harbison_2004/harbison_2004
210.0YAL003WEFB1NaNNaNYSC0017MATA1YPDglucose30.0BrentLab/harbison_2004/harbison_2004
310.0YAL004WYAL004W0.7453420.835929YSC0017MATA1YPDglucose30.0BrentLab/harbison_2004/harbison_2004
410.0YAL005CSSA1NaNNaNYSC0017MATA1YPDglucose30.0BrentLab/harbison_2004/harbison_2004
\n", + "
" + ], + "text/plain": [ + " sample_id db_id target_locus_tag target_symbol effect pvalue \\\n", + "0 1 0.0 YAL001C TFC3 1.697754 0.068705 \n", + "1 1 0.0 YAL002W VPS8 NaN NaN \n", + "2 1 0.0 YAL003W EFB1 NaN NaN \n", + "3 1 0.0 YAL004W YAL004W 0.745342 0.835929 \n", + "4 1 0.0 YAL005C SSA1 NaN NaN \n", + "\n", + " regulator_locus_tag regulator_symbol condition carbon_source \\\n", + "0 YSC0017 MATA1 YPD glucose \n", + "1 YSC0017 MATA1 YPD glucose \n", + "2 YSC0017 MATA1 YPD glucose \n", + "3 YSC0017 MATA1 YPD glucose \n", + "4 YSC0017 MATA1 YPD glucose \n", + "\n", + " temperature_celsius dataset_id \n", + "0 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "1 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "2 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "3 30.0 BrentLab/harbison_2004/harbison_2004 \n", + "4 30.0 BrentLab/harbison_2004/harbison_2004 " + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Get complete data with measurements\n", + "complete_data = vdb.query(\n", + " filters={\"carbon_source\": \"glucose\"},\n", + " datasets=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", + " complete=True\n", + ")\n", + "\n", + "print(f\"Complete data: {len(complete_data)} rows\")\n", + "print(f\"Columns: {list(complete_data.columns)}\")\n", + "print(\"\\nFirst few measurements:\")\n", + "complete_data.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ { - "name": "carbon_source", - "rawType": "object", - "type": "string" + "name": "stdout", + "output_type": "stream", + "text": [ + "Binding data: 1930060 measurements\n" + ] }, { - "name": "dataset_id", - "rawType": "object", - "type": "string" + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "sample_id", + "rawType": "int32", + "type": "integer" + }, + { + "name": "regulator_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "target_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "effect", + "rawType": "float64", + "type": "float" + }, + { + "name": "pvalue", + "rawType": "float64", + "type": "float" + }, + { + "name": "dataset_id", + "rawType": "object", + "type": "string" + } + ], + "ref": "0b7cb890-7e9c-44d2-9ef5-59374bcf3a8a", + "rows": [ + [ + "0", + "2", + "OAF1", + "TFC3", + "1.5895642", + "0.088986168", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "1", + "2", + "OAF1", + "VPS8", + "1.1413208", + "0.32480496", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "2", + "2", + "OAF1", + "EFB1", + "0.72911994", + "0.87882413", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "3", + "2", + "OAF1", + "YAL004W", + "1.1679044", + "0.28225283", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "4", + "2", + "OAF1", + "SSA1", + "0.72911994", + "0.87882413", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "5", + "2", + "OAF1", + "ERP2", + "1.0508274", + "0.43070675", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "6", + "2", + "OAF1", + "FUN14", + "1.3478761", + "0.15551056", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "7", + "2", + "OAF1", + "SPO7", + "0.93967306", + "0.57823415", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "8", + "2", + "OAF1", + "MDM10", + "0.93967306", + "0.57823415", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "9", + "2", + "OAF1", + "SWC3", + "0.86566703", + "0.6711192", + "BrentLab/harbison_2004/harbison_2004" + ] + ], + "shape": { + "columns": 6, + "rows": 10 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sample_idregulator_symboltarget_symboleffectpvaluedataset_id
02OAF1TFC31.5895640.088986BrentLab/harbison_2004/harbison_2004
12OAF1VPS81.1413210.324805BrentLab/harbison_2004/harbison_2004
22OAF1EFB10.7291200.878824BrentLab/harbison_2004/harbison_2004
32OAF1YAL004W1.1679040.282253BrentLab/harbison_2004/harbison_2004
42OAF1SSA10.7291200.878824BrentLab/harbison_2004/harbison_2004
52OAF1ERP21.0508270.430707BrentLab/harbison_2004/harbison_2004
62OAF1FUN141.3478760.155511BrentLab/harbison_2004/harbison_2004
72OAF1SPO70.9396730.578234BrentLab/harbison_2004/harbison_2004
82OAF1MDM100.9396730.578234BrentLab/harbison_2004/harbison_2004
92OAF1SWC30.8656670.671119BrentLab/harbison_2004/harbison_2004
\n", + "
" + ], + "text/plain": [ + " sample_id regulator_symbol target_symbol effect pvalue \\\n", + "0 2 OAF1 TFC3 1.589564 0.088986 \n", + "1 2 OAF1 VPS8 1.141321 0.324805 \n", + "2 2 OAF1 EFB1 0.729120 0.878824 \n", + "3 2 OAF1 YAL004W 1.167904 0.282253 \n", + "4 2 OAF1 SSA1 0.729120 0.878824 \n", + "5 2 OAF1 ERP2 1.050827 0.430707 \n", + "6 2 OAF1 FUN14 1.347876 0.155511 \n", + "7 2 OAF1 SPO7 0.939673 0.578234 \n", + "8 2 OAF1 MDM10 0.939673 0.578234 \n", + "9 2 OAF1 SWC3 0.865667 0.671119 \n", + "\n", + " dataset_id \n", + "0 BrentLab/harbison_2004/harbison_2004 \n", + "1 BrentLab/harbison_2004/harbison_2004 \n", + "2 BrentLab/harbison_2004/harbison_2004 \n", + "3 BrentLab/harbison_2004/harbison_2004 \n", + "4 BrentLab/harbison_2004/harbison_2004 \n", + "5 BrentLab/harbison_2004/harbison_2004 \n", + "6 BrentLab/harbison_2004/harbison_2004 \n", + "7 BrentLab/harbison_2004/harbison_2004 \n", + "8 BrentLab/harbison_2004/harbison_2004 \n", + "9 BrentLab/harbison_2004/harbison_2004 " + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" } - ], - "ref": "8309daf0-f866-44e1-8ca4-3b5fa071dc9b", - "rows": [], - "shape": { - "columns": 5, - "rows": 0 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sample_idregulator_locus_tagregulator_symbolcarbon_sourcedataset_id
\n", - "
" ], - "text/plain": [ - "Empty DataFrame\n", - "Columns: [sample_id, regulator_locus_tag, regulator_symbol, carbon_source, dataset_id]\n", - "Index: []" + "source": [ + "# You can combine complete=True with field selection\n", + "# Get just the binding data columns\n", + "binding_data = vdb.query(\n", + " filters={\"carbon_source\": \"glucose\"},\n", + " datasets=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", + " fields=[\"sample_id\", \"regulator_symbol\", \"target_symbol\", \"effect\", \"pvalue\"],\n", + " complete=True\n", + ")\n", + "\n", + "print(f\"Binding data: {len(binding_data)} measurements\")\n", + "binding_data.head(10)" ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Samples with glucose at 30C\n", - "glucose_30c = vdb.query(\n", - " filters={\n", - " \"carbon_source\": \"glucose\",\n", - " \"temperature_celsius\": 30\n", - " }\n", - ")\n", - "\n", - "print(f\"Found {len(glucose_30c)} samples with glucose at 30C\")\n", - "glucose_30c.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Numeric Range Queries" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ + }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "Found 1487 samples at >= 30C\n", - "Found 1487 samples between 28-32C\n" - ] - } - ], - "source": [ - "# Samples at temperature >= 30C\n", - "warm_samples = vdb.query(\n", - " filters={\"temperature_celsius\": (\">=\", 30)}\n", - ")\n", - "\n", - "print(f\"Found {len(warm_samples)} samples at >= 30C\")\n", - "\n", - "# Samples between 28C and 32C\n", - "moderate_temp = vdb.query(\n", - " filters={\"temperature_celsius\": (\"between\", 28, 32)}\n", - ")\n", - "\n", - "print(f\"Found {len(moderate_temp)} samples between 28-32C\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Factor Alias Expansion\n", - "\n", - "When you query for a normalized value, VirtualDB automatically expands to all original aliases." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example analysis\n", + "\n", + "The following is an example of using VirtualDB to extract and summarize data across\n", + "datasets." + ] + }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "Found 0 galactose samples\n", - "\n", - "This query internally expanded to:\n", - " WHERE carbon_source IN ('D-galactose', 'gal', 'galactose')\n" - ] - } - ], - "source": [ - "# Query for \"galactose\" matches \"D-galactose\", \"gal\", and \"galactose\"\n", - "galactose_samples = vdb.query(filters={\"carbon_source\": \"galactose\"})\n", - "\n", - "print(f\"Found {len(galactose_samples)} galactose samples\")\n", - "print(\"\\nThis query internally expanded to:\")\n", - "print(\" WHERE carbon_source IN ('D-galactose', 'gal', 'galactose')\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Complete Data Retrieval\n", - "\n", - "By default, `query()` returns sample-level metadata (one row per sample). \n", - "Set `complete=True` to get all measurements (many rows per sample)." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Sample counts by dataset and carbon source:\n", + " dataset_id carbon_source num_samples\n", + "BrentLab/harbison_2004/harbison_2004 galactose 4\n", + "BrentLab/harbison_2004/harbison_2004 glucose 310\n", + "BrentLab/harbison_2004/harbison_2004 raffinose 1\n", + "BrentLab/harbison_2004/harbison_2004 unspecified 37\n", + "BrentLab/kemmeren_2014/kemmeren_2014 glucose 1487\n" + ] + } + ], + "source": [ + "# Compare number of samples by carbon source across datasets\n", + "\n", + "# Get all samples\n", + "all_samples = vdb.query()\n", + "\n", + "# Count by dataset and carbon source\n", + "summary = all_samples.groupby(['dataset_id', 'carbon_source']).size()\n", + "summary = summary.reset_index(name='num_samples')\n", + "\n", + "print(\"Sample counts by dataset and carbon source:\")\n", + "print(summary.to_string(index=False))" + ] + }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "Complete data: 0 rows\n", - "Columns: []\n", - "\n", - "First few measurements:\n" - ] + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Glucose samples by temperature:\n", + " 30.0C: 1791 samples\n" + ] + } + ], + "source": [ + "# Compare glucose experiments at different temperatures\n", + "\n", + "glucose_by_temp = vdb.query(\n", + " filters={\"carbon_source\": \"glucose\"},\n", + " fields=[\"sample_id\", \"temperature_celsius\", \"environmental_condition\"]\n", + ")\n", + "\n", + "# Count samples by temperature\n", + "temp_counts = glucose_by_temp['temperature_celsius'].value_counts().sort_index()\n", + "\n", + "print(\"Glucose samples by temperature:\")\n", + "for temp, count in temp_counts.items():\n", + " print(f\" {temp}C: {count} samples\")" + ] }, { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ { - "name": "index", - "rawType": "int64", - "type": "integer" + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 18678 FHL1 binding measurements in glucose\n", + "Significant targets: 379\n", + "\n", + "Top 10 targets by effect size:\n", + "target_symbol effect pvalue\n", + " RPS5 24.145013 9.739702e-09\n", + " RPL11A 20.585725 1.232356e-08\n", + " PRE2 20.585725 1.232356e-08\n", + " SRF1 20.342898 1.226799e-08\n", + " SLX8 20.057080 1.513076e-08\n", + " RPL23B 20.057080 1.513076e-08\n", + " RPL40A 19.262139 1.761808e-08\n", + " MLP2 19.262139 1.761808e-08\n", + " RPS6A 18.704379 1.544172e-08\n", + " RPL22A 17.926705 1.560357e-08\n" + ] } - ], - "ref": "a3896349-b6ba-4827-b7be-e4cfa318f2b2", - "rows": [], - "shape": { - "columns": 0, - "rows": 0 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
\n", - "
" ], - "text/plain": [ - "Empty DataFrame\n", - "Columns: []\n", - "Index: []" + "source": [ + "# Get binding data for a specific regulator across datasets\n", + "\n", + "# Query for FHL1 binding in glucose conditions\n", + "fhl1_binding = vdb.query(\n", + " filters={\n", + " \"carbon_source\": \"glucose\",\n", + " \"regulator_symbol\": \"FHL1\"\n", + " },\n", + " fields=[\"sample_id\", \"regulator_symbol\", \"target_symbol\", \"effect\", \"pvalue\"],\n", + " complete=True\n", + ")\n", + "\n", + "print(f\"Found {len(fhl1_binding)} FHL1 binding measurements in glucose\")\n", + "\n", + "# Find significant targets (p < 0.001)\n", + "significant = fhl1_binding[fhl1_binding['pvalue'] < 0.001]\n", + "print(f\"Significant targets: {len(significant)}\")\n", + "\n", + "# Top 10 by effect size\n", + "top_targets = significant.nlargest(10, 'effect')[['target_symbol', 'effect', 'pvalue']]\n", + "print(\"\\nTop 10 targets by effect size:\")\n", + "print(top_targets.to_string(index=False))" ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Get complete data with measurements\n", - "complete_data = vdb.query(\n", - " filters={\"carbon_source\": \"glucose\"},\n", - " datasets=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", - " complete=True\n", - ")\n", - "\n", - "print(f\"Complete data: {len(complete_data)} rows\")\n", - "print(f\"Columns: {list(complete_data.columns)}\")\n", - "print(\"\\nFirst few measurements:\")\n", - "complete_data.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ + }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "Binding data: 0 measurements\n" - ] + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Querying Comparative Datasets\n", + "\n", + "Comparative datasets like DTO (Direct Target Overlap) contain analysis results that relate samples across multiple datasets. These datasets can be queried directly to find significant cross-dataset relationships." + ] }, { - "data": { - "application/vnd.microsoft.datawrangler.viewer.v0+json": { - "columns": [ + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Fetching 6 files: 100%|██████████| 6/6 [00:00<00:00, 65536.00it/s]\n", + "Fetching 6 files: 100%|██████████| 6/6 [00:00<00:00, 57325.34it/s]" + ] + }, { - "name": "index", - "rawType": "int64", - "type": "integer" + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 32 FHL1 binding measurements\n", + "\n", + "Columns: ['sample_id', 'regulator_symbol', 'condition', 'dto_fdr', 'perturbation_id', 'dataset_id']\n", + "\n", + "Rows with DTO data: 4\n", + "\n", + "First few results:\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "sample_id", + "rawType": "int32", + "type": "integer" + }, + { + "name": "regulator_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "condition", + "rawType": "object", + "type": "string" + }, + { + "name": "dto_fdr", + "rawType": "float64", + "type": "float" + }, + { + "name": "perturbation_id", + "rawType": "object", + "type": "string" + }, + { + "name": "dataset_id", + "rawType": "object", + "type": "string" + } + ], + "ref": "a0eb6112-b457-4642-add7-4bcd5068e495", + "rows": [ + [ + "0", + "345", + "FHL1", + "H2O2Hi", + "0.4549087454017032", + "BrentLab/Hackett_2020;hackett_2020;1666", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "1", + "345", + "FHL1", + "H2O2Hi", + null, + "BrentLab/Hackett_2020;hackett_2020;1665", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "2", + "345", + "FHL1", + "H2O2Hi", + null, + "BrentLab/Hackett_2020;hackett_2020;1667", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "3", + "345", + "FHL1", + "H2O2Hi", + null, + "BrentLab/Hackett_2020;hackett_2020;1669", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "4", + "345", + "FHL1", + "H2O2Hi", + null, + "BrentLab/Hackett_2020;hackett_2020;1663", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "5", + "345", + "FHL1", + "H2O2Hi", + null, + "BrentLab/Hackett_2020;hackett_2020;1664", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "6", + "345", + "FHL1", + "H2O2Hi", + null, + "BrentLab/Hackett_2020;hackett_2020;1670", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "7", + "345", + "FHL1", + "H2O2Hi", + null, + "BrentLab/Hackett_2020;hackett_2020;1668", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "8", + "346", + "FHL1", + "RAPA", + null, + "BrentLab/Hackett_2020;hackett_2020;1667", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "9", + "346", + "FHL1", + "RAPA", + null, + "BrentLab/Hackett_2020;hackett_2020;1663", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "10", + "346", + "FHL1", + "RAPA", + null, + "BrentLab/Hackett_2020;hackett_2020;1670", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "11", + "346", + "FHL1", + "RAPA", + null, + "BrentLab/Hackett_2020;hackett_2020;1668", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "12", + "346", + "FHL1", + "RAPA", + "0.0", + "BrentLab/Hackett_2020;hackett_2020;1666", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "13", + "346", + "FHL1", + "RAPA", + null, + "BrentLab/Hackett_2020;hackett_2020;1669", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "14", + "346", + "FHL1", + "RAPA", + null, + "BrentLab/Hackett_2020;hackett_2020;1664", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "15", + "346", + "FHL1", + "RAPA", + null, + "BrentLab/Hackett_2020;hackett_2020;1665", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "16", + "347", + "FHL1", + "SM", + null, + "BrentLab/Hackett_2020;hackett_2020;1667", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "17", + "347", + "FHL1", + "SM", + "0.0221957781456953", + "BrentLab/Hackett_2020;hackett_2020;1666", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "18", + "347", + "FHL1", + "SM", + null, + "BrentLab/Hackett_2020;hackett_2020;1669", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "19", + "347", + "FHL1", + "SM", + null, + "BrentLab/Hackett_2020;hackett_2020;1664", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "20", + "347", + "FHL1", + "SM", + null, + "BrentLab/Hackett_2020;hackett_2020;1663", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "21", + "347", + "FHL1", + "SM", + null, + "BrentLab/Hackett_2020;hackett_2020;1670", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "22", + "347", + "FHL1", + "SM", + null, + "BrentLab/Hackett_2020;hackett_2020;1668", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "23", + "347", + "FHL1", + "SM", + null, + "BrentLab/Hackett_2020;hackett_2020;1665", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "24", + "348", + "FHL1", + "YPD", + null, + "BrentLab/Hackett_2020;hackett_2020;1664", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "25", + "348", + "FHL1", + "YPD", + "0.089578429724277", + "BrentLab/Hackett_2020;hackett_2020;1666", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "26", + "348", + "FHL1", + "YPD", + null, + "BrentLab/Hackett_2020;hackett_2020;1663", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "27", + "348", + "FHL1", + "YPD", + null, + "BrentLab/Hackett_2020;hackett_2020;1667", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "28", + "348", + "FHL1", + "YPD", + null, + "BrentLab/Hackett_2020;hackett_2020;1669", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "29", + "348", + "FHL1", + "YPD", + null, + "BrentLab/Hackett_2020;hackett_2020;1665", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "30", + "348", + "FHL1", + "YPD", + null, + "BrentLab/Hackett_2020;hackett_2020;1670", + "BrentLab/harbison_2004/harbison_2004" + ], + [ + "31", + "348", + "FHL1", + "YPD", + null, + "BrentLab/Hackett_2020;hackett_2020;1668", + "BrentLab/harbison_2004/harbison_2004" + ] + ], + "shape": { + "columns": 6, + "rows": 32 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sample_idregulator_symbolconditiondto_fdrperturbation_iddataset_id
0345FHL1H2O2Hi0.454909BrentLab/Hackett_2020;hackett_2020;1666BrentLab/harbison_2004/harbison_2004
1345FHL1H2O2HiNaNBrentLab/Hackett_2020;hackett_2020;1665BrentLab/harbison_2004/harbison_2004
2345FHL1H2O2HiNaNBrentLab/Hackett_2020;hackett_2020;1667BrentLab/harbison_2004/harbison_2004
3345FHL1H2O2HiNaNBrentLab/Hackett_2020;hackett_2020;1669BrentLab/harbison_2004/harbison_2004
4345FHL1H2O2HiNaNBrentLab/Hackett_2020;hackett_2020;1663BrentLab/harbison_2004/harbison_2004
5345FHL1H2O2HiNaNBrentLab/Hackett_2020;hackett_2020;1664BrentLab/harbison_2004/harbison_2004
6345FHL1H2O2HiNaNBrentLab/Hackett_2020;hackett_2020;1670BrentLab/harbison_2004/harbison_2004
7345FHL1H2O2HiNaNBrentLab/Hackett_2020;hackett_2020;1668BrentLab/harbison_2004/harbison_2004
8346FHL1RAPANaNBrentLab/Hackett_2020;hackett_2020;1667BrentLab/harbison_2004/harbison_2004
9346FHL1RAPANaNBrentLab/Hackett_2020;hackett_2020;1663BrentLab/harbison_2004/harbison_2004
10346FHL1RAPANaNBrentLab/Hackett_2020;hackett_2020;1670BrentLab/harbison_2004/harbison_2004
11346FHL1RAPANaNBrentLab/Hackett_2020;hackett_2020;1668BrentLab/harbison_2004/harbison_2004
12346FHL1RAPA0.000000BrentLab/Hackett_2020;hackett_2020;1666BrentLab/harbison_2004/harbison_2004
13346FHL1RAPANaNBrentLab/Hackett_2020;hackett_2020;1669BrentLab/harbison_2004/harbison_2004
14346FHL1RAPANaNBrentLab/Hackett_2020;hackett_2020;1664BrentLab/harbison_2004/harbison_2004
15346FHL1RAPANaNBrentLab/Hackett_2020;hackett_2020;1665BrentLab/harbison_2004/harbison_2004
16347FHL1SMNaNBrentLab/Hackett_2020;hackett_2020;1667BrentLab/harbison_2004/harbison_2004
17347FHL1SM0.022196BrentLab/Hackett_2020;hackett_2020;1666BrentLab/harbison_2004/harbison_2004
18347FHL1SMNaNBrentLab/Hackett_2020;hackett_2020;1669BrentLab/harbison_2004/harbison_2004
19347FHL1SMNaNBrentLab/Hackett_2020;hackett_2020;1664BrentLab/harbison_2004/harbison_2004
20347FHL1SMNaNBrentLab/Hackett_2020;hackett_2020;1663BrentLab/harbison_2004/harbison_2004
21347FHL1SMNaNBrentLab/Hackett_2020;hackett_2020;1670BrentLab/harbison_2004/harbison_2004
22347FHL1SMNaNBrentLab/Hackett_2020;hackett_2020;1668BrentLab/harbison_2004/harbison_2004
23347FHL1SMNaNBrentLab/Hackett_2020;hackett_2020;1665BrentLab/harbison_2004/harbison_2004
24348FHL1YPDNaNBrentLab/Hackett_2020;hackett_2020;1664BrentLab/harbison_2004/harbison_2004
25348FHL1YPD0.089578BrentLab/Hackett_2020;hackett_2020;1666BrentLab/harbison_2004/harbison_2004
26348FHL1YPDNaNBrentLab/Hackett_2020;hackett_2020;1663BrentLab/harbison_2004/harbison_2004
27348FHL1YPDNaNBrentLab/Hackett_2020;hackett_2020;1667BrentLab/harbison_2004/harbison_2004
28348FHL1YPDNaNBrentLab/Hackett_2020;hackett_2020;1669BrentLab/harbison_2004/harbison_2004
29348FHL1YPDNaNBrentLab/Hackett_2020;hackett_2020;1665BrentLab/harbison_2004/harbison_2004
30348FHL1YPDNaNBrentLab/Hackett_2020;hackett_2020;1670BrentLab/harbison_2004/harbison_2004
31348FHL1YPDNaNBrentLab/Hackett_2020;hackett_2020;1668BrentLab/harbison_2004/harbison_2004
\n", + "
" + ], + "text/plain": [ + " sample_id regulator_symbol condition dto_fdr \\\n", + "0 345 FHL1 H2O2Hi 0.454909 \n", + "1 345 FHL1 H2O2Hi NaN \n", + "2 345 FHL1 H2O2Hi NaN \n", + "3 345 FHL1 H2O2Hi NaN \n", + "4 345 FHL1 H2O2Hi NaN \n", + "5 345 FHL1 H2O2Hi NaN \n", + "6 345 FHL1 H2O2Hi NaN \n", + "7 345 FHL1 H2O2Hi NaN \n", + "8 346 FHL1 RAPA NaN \n", + "9 346 FHL1 RAPA NaN \n", + "10 346 FHL1 RAPA NaN \n", + "11 346 FHL1 RAPA NaN \n", + "12 346 FHL1 RAPA 0.000000 \n", + "13 346 FHL1 RAPA NaN \n", + "14 346 FHL1 RAPA NaN \n", + "15 346 FHL1 RAPA NaN \n", + "16 347 FHL1 SM NaN \n", + "17 347 FHL1 SM 0.022196 \n", + "18 347 FHL1 SM NaN \n", + "19 347 FHL1 SM NaN \n", + "20 347 FHL1 SM NaN \n", + "21 347 FHL1 SM NaN \n", + "22 347 FHL1 SM NaN \n", + "23 347 FHL1 SM NaN \n", + "24 348 FHL1 YPD NaN \n", + "25 348 FHL1 YPD 0.089578 \n", + "26 348 FHL1 YPD NaN \n", + "27 348 FHL1 YPD NaN \n", + "28 348 FHL1 YPD NaN \n", + "29 348 FHL1 YPD NaN \n", + "30 348 FHL1 YPD NaN \n", + "31 348 FHL1 YPD NaN \n", + "\n", + " perturbation_id \\\n", + "0 BrentLab/Hackett_2020;hackett_2020;1666 \n", + "1 BrentLab/Hackett_2020;hackett_2020;1665 \n", + "2 BrentLab/Hackett_2020;hackett_2020;1667 \n", + "3 BrentLab/Hackett_2020;hackett_2020;1669 \n", + "4 BrentLab/Hackett_2020;hackett_2020;1663 \n", + "5 BrentLab/Hackett_2020;hackett_2020;1664 \n", + "6 BrentLab/Hackett_2020;hackett_2020;1670 \n", + "7 BrentLab/Hackett_2020;hackett_2020;1668 \n", + "8 BrentLab/Hackett_2020;hackett_2020;1667 \n", + "9 BrentLab/Hackett_2020;hackett_2020;1663 \n", + "10 BrentLab/Hackett_2020;hackett_2020;1670 \n", + "11 BrentLab/Hackett_2020;hackett_2020;1668 \n", + "12 BrentLab/Hackett_2020;hackett_2020;1666 \n", + "13 BrentLab/Hackett_2020;hackett_2020;1669 \n", + "14 BrentLab/Hackett_2020;hackett_2020;1664 \n", + "15 BrentLab/Hackett_2020;hackett_2020;1665 \n", + "16 BrentLab/Hackett_2020;hackett_2020;1667 \n", + "17 BrentLab/Hackett_2020;hackett_2020;1666 \n", + "18 BrentLab/Hackett_2020;hackett_2020;1669 \n", + "19 BrentLab/Hackett_2020;hackett_2020;1664 \n", + "20 BrentLab/Hackett_2020;hackett_2020;1663 \n", + "21 BrentLab/Hackett_2020;hackett_2020;1670 \n", + "22 BrentLab/Hackett_2020;hackett_2020;1668 \n", + "23 BrentLab/Hackett_2020;hackett_2020;1665 \n", + "24 BrentLab/Hackett_2020;hackett_2020;1664 \n", + "25 BrentLab/Hackett_2020;hackett_2020;1666 \n", + "26 BrentLab/Hackett_2020;hackett_2020;1663 \n", + "27 BrentLab/Hackett_2020;hackett_2020;1667 \n", + "28 BrentLab/Hackett_2020;hackett_2020;1669 \n", + "29 BrentLab/Hackett_2020;hackett_2020;1665 \n", + "30 BrentLab/Hackett_2020;hackett_2020;1670 \n", + "31 BrentLab/Hackett_2020;hackett_2020;1668 \n", + "\n", + " dataset_id \n", + "0 BrentLab/harbison_2004/harbison_2004 \n", + "1 BrentLab/harbison_2004/harbison_2004 \n", + "2 BrentLab/harbison_2004/harbison_2004 \n", + "3 BrentLab/harbison_2004/harbison_2004 \n", + "4 BrentLab/harbison_2004/harbison_2004 \n", + "5 BrentLab/harbison_2004/harbison_2004 \n", + "6 BrentLab/harbison_2004/harbison_2004 \n", + "7 BrentLab/harbison_2004/harbison_2004 \n", + "8 BrentLab/harbison_2004/harbison_2004 \n", + "9 BrentLab/harbison_2004/harbison_2004 \n", + "10 BrentLab/harbison_2004/harbison_2004 \n", + "11 BrentLab/harbison_2004/harbison_2004 \n", + "12 BrentLab/harbison_2004/harbison_2004 \n", + "13 BrentLab/harbison_2004/harbison_2004 \n", + "14 BrentLab/harbison_2004/harbison_2004 \n", + "15 BrentLab/harbison_2004/harbison_2004 \n", + "16 BrentLab/harbison_2004/harbison_2004 \n", + "17 BrentLab/harbison_2004/harbison_2004 \n", + "18 BrentLab/harbison_2004/harbison_2004 \n", + "19 BrentLab/harbison_2004/harbison_2004 \n", + "20 BrentLab/harbison_2004/harbison_2004 \n", + "21 BrentLab/harbison_2004/harbison_2004 \n", + "22 BrentLab/harbison_2004/harbison_2004 \n", + "23 BrentLab/harbison_2004/harbison_2004 \n", + "24 BrentLab/harbison_2004/harbison_2004 \n", + "25 BrentLab/harbison_2004/harbison_2004 \n", + "26 BrentLab/harbison_2004/harbison_2004 \n", + "27 BrentLab/harbison_2004/harbison_2004 \n", + "28 BrentLab/harbison_2004/harbison_2004 \n", + "29 BrentLab/harbison_2004/harbison_2004 \n", + "30 BrentLab/harbison_2004/harbison_2004 \n", + "31 BrentLab/harbison_2004/harbison_2004 " + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" } - ], - "ref": "fc59efba-e365-4b1c-b9ff-31828897cde2", - "rows": [], - "shape": { - "columns": 0, - "rows": 0 - } - }, - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
\n", - "
" ], - "text/plain": [ - "Empty DataFrame\n", - "Columns: []\n", - "Index: []" + "source": [ + "# Query harbison_2004 binding data enriched with DTO metrics\n", + "# This demonstrates field-based joins: requesting dto_fdr field\n", + "# while querying the primary binding dataset\n", + "\n", + "binding_with_dto = vdb.query(\n", + " datasets=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", + " filters={\"regulator_symbol\": \"FHL1\"},\n", + " fields=[\"sample_id\", \"regulator_symbol\", \"condition\", \"dto_fdr\", \"binding_id\", \"perturbation_id\"],\n", + ")\n", + "\n", + "print(f\"Found {len(binding_with_dto)} FHL1 binding measurements\")\n", + "print(f\"\\nColumns: {list(binding_with_dto.columns)}\")\n", + "print(f\"\\nRows with DTO data: {binding_with_dto['dto_fdr'].notna().sum()}\")\n", + "print(f\"\\nFirst few results:\")\n", + "binding_with_dto" ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# You can combine complete=True with field selection\n", - "# Get just the binding data columns\n", - "binding_data = vdb.query(\n", - " filters={\"carbon_source\": \"glucose\"},\n", - " datasets=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", - " fields=[\"sample_id\", \"regulator_symbol\", \"target_symbol\", \"effect\", \"pvalue\"],\n", - " complete=True\n", - ")\n", - "\n", - "print(f\"Binding data: {len(binding_data)} measurements\")\n", - "binding_data.head(10)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 6. Understanding the Configuration\n", - "\n", - "Let's examine the configuration structure in more detail." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Property Mapping Levels\n", - "\n", - "Properties can be extracted at three hierarchy levels:\n", - "\n", - "1. **Repository-wide**: Common to all datasets\n", - "```yaml\n", - "repositories:\n", - " BrentLab/some_repo:\n", - " nitrogen_source: # Applies to all datasets in this repo\n", - " path: media.nitrogen_source.name\n", - "```\n", - "\n", - "2. **Dataset-specific**: Specific to one config\n", - "```yaml\n", - " dataset:\n", - " config_name:\n", - " phosphate_source: # Only for this dataset\n", - " path: media.phosphate_source.compound\n", - "```\n", - "\n", - "3. **Field-level**: Varies per sample\n", - "```yaml\n", - " carbon_source:\n", - " field: condition # Extract from 'condition' field\n", - " path: media.carbon_source.compound\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Field Aliases (Column Renaming)\n", - "\n", - "When you specify just a `field` without a `path`, it creates a column alias:\n", - "\n", - "```yaml\n", - "environmental_condition:\n", - " field: condition # Renames 'condition' to 'environmental_condition'\n", - "```\n", - "\n", - "This is useful for:\n", - "- Standardizing field names across datasets\n", - "- Enabling normalization of the field values" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Factor Aliases (Value Normalization)\n", - "\n", - "The `factor_aliases` section maps varying terminologies to standard values:\n", - "\n", - "```yaml\n", - "factor_aliases:\n", - " carbon_source:\n", - " glucose: [D-glucose, dextrose, glu]\n", - " galactose: [D-galactose, gal]\n", - "```\n", - "\n", - "This means:\n", - "- Querying for `\"glucose\"` matches `\"D-glucose\"`, `\"dextrose\"`, and `\"glu\"`\n", - "- All returned values are normalized to the canonical form (`\"glucose\"`)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 7. Real-World Example: Cross-Dataset Comparison\n", - "\n", - "Let's do a practical analysis comparing experiments across datasets." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ + }, { - "ename": "KeyError", - "evalue": "'_repo_id'", - "output_type": "error", - "traceback": [ - "\u001b[31m---------------------------------------------------------------------------\u001b[39m", - "\u001b[31mKeyError\u001b[39m Traceback (most recent call last)", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[16]\u001b[39m\u001b[32m, line 7\u001b[39m\n\u001b[32m 4\u001b[39m all_samples = vdb.query()\n\u001b[32m 6\u001b[39m \u001b[38;5;66;03m# Count by dataset and carbon source\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m7\u001b[39m summary = \u001b[43mall_samples\u001b[49m\u001b[43m.\u001b[49m\u001b[43mgroupby\u001b[49m\u001b[43m(\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43m_repo_id\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43m_config_name\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43mcarbon_source\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m.size()\n\u001b[32m 8\u001b[39m summary = summary.reset_index(name=\u001b[33m'\u001b[39m\u001b[33mnum_samples\u001b[39m\u001b[33m'\u001b[39m)\n\u001b[32m 10\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33mSample counts by dataset and carbon source:\u001b[39m\u001b[33m\"\u001b[39m)\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/pandas/core/frame.py:9210\u001b[39m, in \u001b[36mDataFrame.groupby\u001b[39m\u001b[34m(self, by, axis, level, as_index, sort, group_keys, observed, dropna)\u001b[39m\n\u001b[32m 9207\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m level \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m by \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m 9208\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[33m\"\u001b[39m\u001b[33mYou have to supply one of \u001b[39m\u001b[33m'\u001b[39m\u001b[33mby\u001b[39m\u001b[33m'\u001b[39m\u001b[33m and \u001b[39m\u001b[33m'\u001b[39m\u001b[33mlevel\u001b[39m\u001b[33m'\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m-> \u001b[39m\u001b[32m9210\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mDataFrameGroupBy\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 9211\u001b[39m \u001b[43m \u001b[49m\u001b[43mobj\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 9212\u001b[39m \u001b[43m \u001b[49m\u001b[43mkeys\u001b[49m\u001b[43m=\u001b[49m\u001b[43mby\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9213\u001b[39m \u001b[43m \u001b[49m\u001b[43maxis\u001b[49m\u001b[43m=\u001b[49m\u001b[43maxis\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9214\u001b[39m \u001b[43m \u001b[49m\u001b[43mlevel\u001b[49m\u001b[43m=\u001b[49m\u001b[43mlevel\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9215\u001b[39m \u001b[43m \u001b[49m\u001b[43mas_index\u001b[49m\u001b[43m=\u001b[49m\u001b[43mas_index\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9216\u001b[39m \u001b[43m \u001b[49m\u001b[43msort\u001b[49m\u001b[43m=\u001b[49m\u001b[43msort\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9217\u001b[39m \u001b[43m \u001b[49m\u001b[43mgroup_keys\u001b[49m\u001b[43m=\u001b[49m\u001b[43mgroup_keys\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9218\u001b[39m \u001b[43m \u001b[49m\u001b[43mobserved\u001b[49m\u001b[43m=\u001b[49m\u001b[43mobserved\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9219\u001b[39m \u001b[43m \u001b[49m\u001b[43mdropna\u001b[49m\u001b[43m=\u001b[49m\u001b[43mdropna\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 9220\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/pandas/core/groupby/groupby.py:1331\u001b[39m, in \u001b[36mGroupBy.__init__\u001b[39m\u001b[34m(self, obj, keys, axis, level, grouper, exclusions, selection, as_index, sort, group_keys, observed, dropna)\u001b[39m\n\u001b[32m 1328\u001b[39m \u001b[38;5;28mself\u001b[39m.dropna = dropna\n\u001b[32m 1330\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m grouper \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m-> \u001b[39m\u001b[32m1331\u001b[39m grouper, exclusions, obj = \u001b[43mget_grouper\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 1332\u001b[39m \u001b[43m \u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1333\u001b[39m \u001b[43m \u001b[49m\u001b[43mkeys\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1334\u001b[39m \u001b[43m \u001b[49m\u001b[43maxis\u001b[49m\u001b[43m=\u001b[49m\u001b[43maxis\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1335\u001b[39m \u001b[43m \u001b[49m\u001b[43mlevel\u001b[49m\u001b[43m=\u001b[49m\u001b[43mlevel\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1336\u001b[39m \u001b[43m \u001b[49m\u001b[43msort\u001b[49m\u001b[43m=\u001b[49m\u001b[43msort\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1337\u001b[39m \u001b[43m \u001b[49m\u001b[43mobserved\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mobserved\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mis\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mlib\u001b[49m\u001b[43m.\u001b[49m\u001b[43mno_default\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01melse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mobserved\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1338\u001b[39m \u001b[43m \u001b[49m\u001b[43mdropna\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mdropna\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1339\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1341\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m observed \u001b[38;5;129;01mis\u001b[39;00m lib.no_default:\n\u001b[32m 1342\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28many\u001b[39m(ping._passed_categorical \u001b[38;5;28;01mfor\u001b[39;00m ping \u001b[38;5;129;01min\u001b[39;00m grouper.groupings):\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/code/tfbp/tfbpapi/.venv/lib/python3.11/site-packages/pandas/core/groupby/grouper.py:1043\u001b[39m, in \u001b[36mget_grouper\u001b[39m\u001b[34m(obj, key, axis, level, sort, observed, validate, dropna)\u001b[39m\n\u001b[32m 1041\u001b[39m in_axis, level, gpr = \u001b[38;5;28;01mFalse\u001b[39;00m, gpr, \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m 1042\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m-> \u001b[39m\u001b[32m1043\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m(gpr)\n\u001b[32m 1044\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(gpr, Grouper) \u001b[38;5;129;01mand\u001b[39;00m gpr.key \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m 1045\u001b[39m \u001b[38;5;66;03m# Add key to exclusions\u001b[39;00m\n\u001b[32m 1046\u001b[39m exclusions.add(gpr.key)\n", - "\u001b[31mKeyError\u001b[39m: '_repo_id'" - ] + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Fetching 6 files: 100%|██████████| 6/6 [00:00<00:00, 122760.12it/s]\n", + "Fetching 6 files: 100%|██████████| 6/6 [00:00<00:00, 35951.18it/s]\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "sample_id", + "rawType": "int32", + "type": "integer" + }, + { + "name": "regulator_symbol", + "rawType": "object", + "type": "string" + }, + { + "name": "perturbation_id", + "rawType": "object", + "type": "string" + }, + { + "name": "dto_empirical_pvalue", + "rawType": "float64", + "type": "float" + }, + { + "name": "dataset_id", + "rawType": "object", + "type": "string" + } + ], + "ref": "f666fc22-ce67-46fc-80bb-c44baafdf799", + "rows": [ + [ + "0", + "347", + "FHL1", + "BrentLab/Hackett_2020;hackett_2020;1666", + "0.297", + "BrentLab/harbison_2004/harbison_2004" + ] + ], + "shape": { + "columns": 5, + "rows": 1 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sample_idregulator_symbolperturbation_iddto_empirical_pvaluedataset_id
0347FHL1BrentLab/Hackett_2020;hackett_2020;16660.297BrentLab/harbison_2004/harbison_2004
\n", + "
" + ], + "text/plain": [ + " sample_id regulator_symbol perturbation_id \\\n", + "0 347 FHL1 BrentLab/Hackett_2020;hackett_2020;1666 \n", + "\n", + " dto_empirical_pvalue dataset_id \n", + "0 0.297 BrentLab/harbison_2004/harbison_2004 " + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# You can also filter on comparative dataset fields\n", + "# This returns only binding measurements with significant DTO results\n", + "\n", + "significant_dtos = vdb.query(\n", + " datasets=[(\"BrentLab/harbison_2004\", \"harbison_2004\")],\n", + " filters={\n", + " \"regulator_symbol\": \"FHL1\",\n", + " # the threshold is high here b/c FHL1 didn't have significant results in harbison\n", + " \"dto_empirical_pvalue\": (\"<\", 0.5)\n", + " },\n", + " fields=[\"sample_id\", \"regulator_symbol\", \"target_symbol\", \"perturbation_id\", \"dto_empirical_pvalue\"],\n", + ")\n", + "\n", + "significant_dtos" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "tfbpapi-py3.11", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" } - ], - "source": [ - "# Compare number of samples by carbon source across datasets\n", - "\n", - "# Get all samples\n", - "all_samples = vdb.query()\n", - "\n", - "# Count by dataset and carbon source\n", - "summary = all_samples.groupby(['_repo_id', '_config_name', 'carbon_source']).size()\n", - "summary = summary.reset_index(name='num_samples')\n", - "\n", - "print(\"Sample counts by dataset and carbon source:\")\n", - "print(summary.to_string(index=False))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Compare glucose experiments at different temperatures\n", - "\n", - "glucose_by_temp = vdb.query(\n", - " filters={\"carbon_source\": \"glucose\"},\n", - " fields=[\"sample_id\", \"temperature_celsius\", \"environmental_condition\"]\n", - ")\n", - "\n", - "# Count samples by temperature\n", - "temp_counts = glucose_by_temp['temperature_celsius'].value_counts().sort_index()\n", - "\n", - "print(\"Glucose samples by temperature:\")\n", - "for temp, count in temp_counts.items():\n", - " print(f\" {temp}C: {count} samples\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Get binding data for a specific regulator across datasets\n", - "\n", - "# Query for FHL1 binding in glucose conditions\n", - "fhl1_binding = vdb.query(\n", - " filters={\n", - " \"carbon_source\": \"glucose\",\n", - " \"regulator_symbol\": \"FHL1\"\n", - " },\n", - " fields=[\"sample_id\", \"regulator_symbol\", \"target_symbol\", \"effect\", \"pvalue\"],\n", - " complete=True\n", - ")\n", - "\n", - "print(f\"Found {len(fhl1_binding)} FHL1 binding measurements in glucose\")\n", - "\n", - "# Find significant targets (p < 0.001)\n", - "significant = fhl1_binding[fhl1_binding['pvalue'] < 0.001]\n", - "print(f\"Significant targets: {len(significant)}\")\n", - "\n", - "# Top 10 by effect size\n", - "top_targets = significant.nlargest(10, 'effect')[['target_symbol', 'effect', 'pvalue']]\n", - "print(\"\\nTop 10 targets by effect size:\")\n", - "print(top_targets.to_string(index=False))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 8. Best Practices\n", - "\n", - "### 1. Use Field Selection\n", - "\n", - "Only request the fields you need - it's more efficient:\n", - "\n", - "```python\n", - "# Good: Only get what you need\n", - "df = vdb.query(\n", - " filters={\"carbon_source\": \"glucose\"},\n", - " fields=[\"sample_id\", \"temperature_celsius\"]\n", - ")\n", - "\n", - "# Less efficient: Get all fields\n", - "df = vdb.query(filters={\"carbon_source\": \"glucose\"})\n", - "```\n", - "\n", - "### 2. Filter Before Complete Data\n", - "\n", - "Use filters to reduce the dataset before getting complete data:\n", - "\n", - "```python\n", - "# Good: Filter first\n", - "df = vdb.query(\n", - " filters={\"carbon_source\": \"glucose\", \"temperature_celsius\": 30},\n", - " complete=True\n", - ")\n", - "\n", - "# Less efficient: Get all data then filter in pandas\n", - "df = vdb.query(complete=True)\n", - "df = df[(df['carbon_source'] == 'glucose') & (df['temperature_celsius'] == 30)]\n", - "```\n", - "\n", - "### 3. Use Normalized Values\n", - "\n", - "Always query using the normalized (canonical) values from your config:\n", - "\n", - "```python\n", - "# Good: Use canonical value\n", - "df = vdb.query(filters={\"carbon_source\": \"glucose\"})\n", - "\n", - "# Might miss data: Using original value\n", - "df = vdb.query(filters={\"carbon_source\": \"D-glucose\"}) # Only matches exact string\n", - "```\n", - "\n", - "### 4. Check Available Values First\n", - "\n", - "Use `get_unique_values()` to discover what values exist:\n", - "\n", - "```python\n", - "# See what carbon sources are available\n", - "sources = vdb.get_unique_values(\"carbon_source\")\n", - "print(sources)\n", - "\n", - "# Then query for specific ones\n", - "df = vdb.query(filters={\"carbon_source\": \"galactose\"})\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Summary\n", - "\n", - "VirtualDB provides a powerful abstraction for querying heterogeneous genomic datasets:\n", - "\n", - "1. **Schema Discovery**: Explore available fields and values with `get_fields()` and `get_unique_values()`\n", - "2. **Unified Queries**: Query across datasets using standardized field names\n", - "3. **Automatic Normalization**: Varying terminologies automatically mapped to common values\n", - "4. **Flexible Filtering**: Support for exact matches, ranges, and complex conditions\n", - "5. **Sample or Complete Data**: Choose between metadata-only or full measurements\n", - "\n", - "For more details on the configuration format and design principles, see the [Virtual Database Design](../virtual_database_concepts.md) documentation." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "tfbpapi-py3.11", - "language": "python", - "name": "python3" }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.9" - } - }, - "nbformat": 4, - "nbformat_minor": 4 + "nbformat": 4, + "nbformat_minor": 4 } diff --git a/docs/virtual_database_concepts.md b/docs/virtual_database_concepts.md index 093502a..55fe6c9 100644 --- a/docs/virtual_database_concepts.md +++ b/docs/virtual_database_concepts.md @@ -11,13 +11,16 @@ queries with standardized field names and values. ## Configuration Structure -A configuration file defines the virtual database schema with four sections: +This is a basic example of a VirtualDB configuration YAML file: ```yaml -# ===== Repository Configurations ===== repositories: # Each repository defines a "table" in the virtual database BrentLab/harbison_2004: + # REQUIRED: Specify which field is the sample identifier. At this level, it means + # that all datasets have a field `sample_id` that uniquely identifies samples. + sample_id: + field: sample_id # Repository-wide properties (apply to all datasets in this repository) nitrogen_source: path: media.nitrogen_source.name @@ -33,19 +36,42 @@ repositories: carbon_source: field: condition path: media.carbon_source.compound + dtype: string # Optional: specify data type # Field without path (column alias with normalization) environmental_condition: field: condition + # if there is a `comparative_analysis` dataset that you want to link to + # a given dataset, you can declare it at the dataset level + # For more information on this section, see the section + # 'Comparative Datasets in VirtualDB' + comparative_analyses: + # specify the comparative analysis repo + - repo: BrentLab/yeast_comparative_analysis + # and dataset + dataset: dto + # and the field in the comparative analysis that links back tot this + # dataset. Note that this field should have role `source_sample`, and it + # should therefore be formated as `repo_id;config_name;sample_id` where the + # sample_id is derived from the field in this dataset that is specified + # for this dataset in the `sample_id` field above. + via_field: perturbation_id + BrentLab/kemmeren_2014: dataset: kemmeren_2014: + # REQUIRED: If `sample_id` isn't defined at the repo level, then it must be + # defined at the dataset level for each dataset in the repo + sample_id: + field: sample_id # Same logical fields, different physical paths carbon_source: path: media.carbon_source.compound + dtype: string temperature_celsius: path: temperature_celsius + dtype: numeric # Enables numeric filtering with comparison operators # ===== Normalization Rules ===== # Map varying terminologies to standardized values @@ -96,18 +122,93 @@ Paths use dot notation to navigate nested structures: - `field: condition, path: media.carbon_source.compound` to looks in field `condition`'s definitions to navigates to `media.carbon_source.compound` +### Data Type Specifications + +Field mappings support an optional `dtype` parameter to ensure proper type handling +during metadata extraction and query filtering. + +**Supported dtypes**: +- `string` - Text data (default if not specified) +- `numeric` - Numeric values (integers or floating-point numbers) +- `bool` - Boolean values (true/false) + +**When to use dtype**: + +1. **Numeric filtering**: Required for fields used with comparison operators + (`<`, `>`, `<=`, `>=`, `between`) +2. **Type consistency**: When source data might be extracted with incorrect type +3. **Performance**: Helps with query optimization and prevents type mismatches + +**Type conversion process**: + +Type conversion happens during metadata extraction: +1. Extract value from source using path +2. Convert to specified dtype if provided +3. Store in metadata DataFrame with correct type + +**Example - The problem**: +```python +# Without dtype: temperature extracted as string "30" +# Comparison fails or produces incorrect results +df = vdb.query(filters={"temperature_celsius": (">", 25)}) +# String comparison: "30" > 25 evaluates incorrectly +``` + +**Example - The solution**: +```yaml +temperature_celsius: + path: temperature_celsius + dtype: numeric # Ensures numeric type for proper comparison +``` + +```python +# With dtype: temperature extracted as numeric 30.0 +# Comparison works correctly +df = vdb.query(filters={"temperature_celsius": (">", 25)}) +# Numeric comparison: 30.0 > 25 is True (correct!) +``` + +**Usage examples**: +```yaml +repositories: + BrentLab/example: + dataset: + example_dataset: + # String field for categorical data + strain_background: + path: strain_background + dtype: string + + # Numeric field for quantitative filtering + temperature_celsius: + path: temperature_celsius + dtype: numeric + + # Numeric field for concentration measurements + drug_concentration_um: + path: drug_treatment.concentration_um + dtype: numeric + + # Boolean field + is_heat_shock: + path: is_heat_shock + dtype: bool +``` + ## VirtualDB Structure VirtualDB maintains a collection of dataset-specific metadata tables, one per configured dataset. Each table has the same structure (standardized schema) but -contains data specific to that dataset. +contains data specific to that dataset. -### Internal Structure +Unless directed, these tables are not stored on desk and instead generated via +query against the source parquet files. Think of them as a typical database view. -When materialized (or conceptually if not materialized), VirtualDB contains: +### Internal Structure ```python { + # Primary datasets with sample_id ("BrentLab/harbison_2004", "harbison_2004"): DataFrame( # Columns: sample_id, carbon_source, temperature_celsius, nitrogen_source, ... # Values: Normalized according to factor_aliases @@ -124,14 +225,32 @@ When materialized (or conceptually if not materialized), VirtualDB contains: # sample_id carbon_source temperature_celsius # kemmeren_001 glucose 30 # kemmeren_002 raffinose 30 + ), + + # Comparative datasets with parsed composite identifiers + ("BrentLab/yeast_comparative_analysis", "dto"): DataFrame( + # Original composite ID columns preserved + # Columns: binding_id, perturbation_id, dto_fdr, dto_empirical_pvalue, ... + # Example rows: + # binding_id perturbation_id dto_fdr + # BrentLab/harbison_2004;harbison_2004;harbison_001 BrentLab/kemmeren_2014;kemmeren_2014;sample_42 0.001 + # BrentLab/harbison_2004;harbison_2004;harbison_002 BrentLab/kemmeren_2014;kemmeren_2014;sample_43 0.045 + # + # When materialized with foreign keys, additional parsed columns are created: + # Columns: binding_id, binding_repo_id, binding_config_name, binding_sample_id, + # perturbation_id, perturbation_repo_id, perturbation_config_name, perturbation_sample_id, + # dto_fdr, dto_empirical_pvalue, ... + # Example rows: + # binding_repo_id binding_config_name binding_sample_id dto_fdr + # BrentLab/harbison_2004 harbison_2004 harbison_001 0.001 + # BrentLab/harbison_2004 harbison_2004 harbison_002 0.045 ) } ``` ### View Materialization -By default, VirtualDB computes views on-demand. For performance, views can be -materialized (cached): +Tables can be cached for faster subsequent queries via materialization: ```python # Cache all views for faster subsequent queries @@ -145,7 +264,7 @@ vdb.invalidate_cache() vdb.invalidate_cache([("BrentLab/harbison_2004", "harbison_2004")]) ``` -Materialized views are stored locally and reused for queries until invalidated. +Materialized views are stored locally and reused for queries. ## VirtualDB Interface @@ -239,7 +358,7 @@ df = vdb.query( ### Factor Alias Expansion When querying with aliased values, VirtualDB automatically expands to all -original values: +original values specified in the configuration: ```python # User queries for normalized value @@ -249,8 +368,6 @@ df = vdb.query(filters={"carbon_source": "galactose"}) # WHERE carbon_source IN ('D-galactose', 'gal', 'galactose') ``` -This ensures all samples are retrieved regardless of original terminology. - ### Numeric Field Filtering Numeric fields support exact matching and range queries: @@ -261,35 +378,148 @@ df = vdb.query(filters={"temperature_celsius": 30}) # Range queries df = vdb.query(filters={"temperature_celsius": (">=", 28)}) +# inclusive of the boundaries, ie [28, 32] df = vdb.query(filters={"temperature_celsius": ("between", 28, 32)}) -# Missing value labels (from missing_value_labels config) +# Missing value labels. This analogous to how factor_aliases work. In this case, it +# will return where the temprature_celsius is missing/None/Null/NaN/etc and/or the +# value matches the specified label, in this case "room". If the missing value label +# is a character value and the field is a numeric field, then only missing values will +# be matched. df = vdb.query(filters={"temperature_celsius": "room"}) # Matches samples where temperature is None/missing ``` -## Design Principles +## Comparative Datasets in VirtualDB -### Virtual Views by Default +Comparative datasets differ from other dataset types in that they represent +relationships between samples across datasets rather than individual samples. +Each row relates 2+ samples from other datasets. -Views are computed on-demand unless explicitly materialized, but optionally cached, -This provides: +### Structure -- Reduces setup time when a new instance instantiates MetadataBuilder/VirtualDB -- No storage overhead for casual queries +Comparative datasets use `source_sample` fields instead of a single `sample_id`: +- Multiple fields with `role: source_sample` +- Each contains composite identifier: `"repo_id;config_name;sample_id"` +- Example: `binding_id = "BrentLab/harbison_2004;harbison_2004;42"` -### External Configuration as Schema +### Querying Comparative Data -The YAML configuration serves as a **database schema definition**: +Comparative datasets can be queried in two ways: **direct queries** for analysis +results, and **field-based queries** to enrich primary dataset queries with +comparative metrics. -- Defines what fields exist (logical schema) -- Maps to physical data structures (via paths) -- Specifies normalization rules (via aliases) -- Documents field semantics (via descriptions) +#### Direct Queries -This separation enables: +Query the comparative dataset directly to find analysis results: -- Schema updates without code changes +```python +# Find significant DTO results across all experiments +dto_results = vdb.query( + datasets=[("BrentLab/yeast_comparative_analysis", "dto")], + filters={"dto_fdr": ("<", 0.05)}, + complete=True +) +# Returns: binding_id, perturbation_id, dto_fdr, dto_empirical_pvalue, +# binding_rank_threshold, perturbation_rank_threshold, ... + +# Filter by source dataset +dto_for_harbison = vdb.query( + datasets=[("BrentLab/yeast_comparative_analysis", "dto")], + filters={"binding_id": ("contains", "harbison_2004")}, + complete=True +) + +# Combine filters on both metrics and source samples +high_quality_dto = vdb.query( + datasets=[("BrentLab/yeast_comparative_analysis", "dto")], + filters={ + "dto_fdr": ("<", 0.01), + "binding_id": ("contains", "callingcards") + }, + complete=True +) +``` + +#### Field-based Queries + +```python +# Query binding data, automatically include DTO metrics +binding_with_dto = vdb.query( + datasets=[("BrentLab/callingcards", "annotated_features")], + filters={"regulator_locus_tag": "YJR060W"}, + fields=["sample_id", "target_locus_tag", "binding_score", "dto_fdr"], + complete=True +) +# Returns binding data WITH dto_fdr joined automatically via composite ID + +# Query perturbation data, include derived significance field +perturbation_with_significance = vdb.query( + datasets=[("BrentLab/hackett_2020", "hackett_2020")], + filters={"regulator_locus_tag": "YJR060W"}, + fields=["sample_id", "target_locus_tag", "log2fc", "is_significant"], + complete=True +) +# Returns perturbation data WITH is_significant (computed from dto_fdr < 0.05) +``` + +### Configuration + +Comparative datasets work differently - +**primary datasets declare which comparative datasets reference them**: + +```yaml +repositories: + # Primary dataset (e.g., binding data) + BrentLab/callingcards: + dataset: + annotated_features: + # REQUIRED: Specify which field is the sample identifier + sample_id: + field: sample_id + + # OPTIONAL: Declare comparative analyses that include this dataset + comparative_analyses: + - repo: BrentLab/yeast_comparative_analysis + dataset: dto + via_field: binding_id + # VirtualDB knows composite format: "BrentLab/callingcards;annotated_features;" + + # Regular fields + regulator_locus_tag: + field: regulator_locus_tag + # ... other fields + + # Another primary dataset (e.g., perturbation data) + BrentLab/hu_2007_reimand_2010: + dataset: + data: + sample_id: + field: sample_id + + comparative_analyses: + - repo: BrentLab/yeast_comparative_analysis + dataset: dto + via_field: perturbation_id + + # Regular fields + # ... other fields + + # Comparative dataset - OPTIONAL field mappings for renaming/aliasing + BrentLab/yeast_comparative_analysis: + dataset: + dto: + # Optional: Rename fields for clarity or add derived columns + fdr: + field: dto_fdr # Rename dto_fdr to fdr + + empirical_pvalue: + field: dto_empirical_pvalue # Rename for consistency + + is_significant: + # Derived field: computed from dto_fdr + expression: "dto_fdr < 0.05" +``` -See: +## See Also - [DataCard Documentation](huggingface_datacard.md) diff --git a/docs/virtual_db.md b/docs/virtual_db.md index bec3ab4..8fe590e 100644 --- a/docs/virtual_db.md +++ b/docs/virtual_db.md @@ -14,3 +14,8 @@ ::: tfbpapi.virtual_db.normalize_value options: show_root_heading: true + +## Usage + +For comprehensive usage documentation including comparative datasets, see +[Virtual Database Concepts](virtual_database_concepts.md). diff --git a/pyproject.toml b/pyproject.toml index b8024b0..e3710f2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,18 +9,8 @@ readme = "README.md" [tool.poetry.dependencies] python = "^3.11" requests = "^2.32.3" -aiohttp = "^3.11.18" -cachetools = "^5.5.2" -scikit-learn = "^1.6.1" -requests-toolbelt = "^1.0.0" -responses = "^0.25.7" -aioresponses = "^0.7.8" -numpy = "^2.2.5" -dotenv = "^0.9.9" pandas = "^2.3.1" huggingface-hub = "^0.34.4" -types-requests = "^2.32.4.20250809" -datasets = "^4.0.0" duckdb = "^1.3.2" pydantic = "^2.11.9" diff --git a/tfbpapi/datacard.py b/tfbpapi/datacard.py index bc4cf72..b8798fc 100644 --- a/tfbpapi/datacard.py +++ b/tfbpapi/datacard.py @@ -22,11 +22,14 @@ from pydantic import ValidationError from tfbpapi.errors import DataCardError, DataCardValidationError, HfDataFetchError -from tfbpapi.fetchers import HfDataCardFetcher, HfRepoStructureFetcher, HfSizeInfoFetcher +from tfbpapi.fetchers import ( + HfDataCardFetcher, + HfRepoStructureFetcher, + HfSizeInfoFetcher, +) from tfbpapi.models import ( DatasetCard, DatasetConfig, - DatasetType, ExtractedMetadata, FeatureInfo, MetadataRelationship, @@ -37,14 +40,9 @@ class DataCard: """ Parser and explorer for HuggingFace dataset metadata. - DataCard parses HuggingFace dataset cards into flexible Python objects, - enabling users to drill down into the YAML structure to understand dataset - organization, experimental conditions, and metadata relationships. - The parsed structure uses Pydantic models with `extra="allow"` to accept arbitrary fields (like experimental_conditions) without requiring code - changes. This makes the system flexible enough to handle domain-specific - metadata variations. + changes. Key capabilities: - Parse dataset card YAML into structured objects @@ -53,7 +51,7 @@ class DataCard: - Extract metadata schema for table design - Discover metadata relationships - Example (new API): + Example: >>> card = DataCard("BrentLab/harbison_2004") >>> # Use context manager for config exploration >>> with card.config("harbison_2004") as cfg: @@ -174,48 +172,6 @@ def get_features(self, config_name: str) -> list[FeatureInfo]: return config.dataset_info.features - def get_features_by_role( - self, config_name: str, role: str | None = None - ) -> dict[str, list[str]]: - """ - Get features grouped by role. - - If role is specified, returns only features with that role. - If role is None, returns all features grouped by role. - - :param config_name: Configuration name - :param role: Optional specific role to filter by - :return: Dict mapping role -> list of field names - :raises DataCardError: If config not found - - Example: - >>> # Get all features by role - >>> by_role = card.get_features_by_role("config_name") - >>> # {'regulator_identifier': ['regulator_locus_tag'], - >>> # 'target_identifier': ['target_locus_tag'], ...} - >>> - >>> # Get only experimental condition features - >>> cond_fields = card.get_features_by_role("config_name", - ... "experimental_condition") - >>> # {'experimental_condition': ['condition', 'treatment']} - - """ - features = self.get_features(config_name) - - # Group by role - by_role: dict[str, list[str]] = {} - for feature in features: - feature_role = feature.role if feature.role else "no_role" - if feature_role not in by_role: - by_role[feature_role] = [] - by_role[feature_role].append(feature.name) - - # Filter by specific role if requested - if role is not None: - return {role: by_role.get(role, [])} - - return by_role - def _extract_partition_values( self, config: DatasetConfig, field_name: str ) -> set[str]: @@ -321,7 +277,8 @@ def extract_metadata_schema(self, config_name: str) -> dict[str, Any]: - **Field roles**: Which fields are regulators, targets, conditions, etc. - **Top-level conditions**: Repo-wide conditions (constant for all samples) - - **Config-level conditions**: Config-specific conditions (constant for this config) + - **Config-level conditions**: Config-specific conditions + (constant for this config) - **Field-level definitions**: Per-sample condition definitions The returned schema provides all the information needed to: @@ -410,7 +367,8 @@ def get_experimental_conditions( All conditions are returned as flexible dicts that preserve the original YAML structure. Navigate nested dicts to access specific values. - :param config_name: Optional config name. If provided, merges top and config levels + :param config_name: Optional config name. If provided, merges top + and config levels :return: Dict of experimental conditions (empty dict if none defined) Example: @@ -471,7 +429,8 @@ def get_field_definitions( :param config_name: Configuration name :param field_name: Field name (typically has role=experimental_condition) - :return: Dict mapping field values to their definition dicts (empty if no definitions) + :return: Dict mapping field values to their definition dicts + (empty if no definitions) :raises DataCardError: If config or field not found Example: diff --git a/tfbpapi/hf_cache_manager.py b/tfbpapi/hf_cache_manager.py index 0a4378c..26ca708 100644 --- a/tfbpapi/hf_cache_manager.py +++ b/tfbpapi/hf_cache_manager.py @@ -135,7 +135,9 @@ def _load_metadata_from_cache(self, config, table_name: str) -> bool: return False # Load cached parquet files into DuckDB - self._create_duckdb_table_from_files(cached_files, table_name) + self._create_duckdb_table_from_files( + cached_files, table_name, config.config_name + ) self.logger.info( f"Loaded {len(cached_files)} cached files into {table_name}" ) @@ -190,7 +192,9 @@ def _download_and_load_metadata(self, config, table_name: str) -> bool: return False # Load downloaded files into DuckDB - self._create_duckdb_table_from_files(downloaded_files, table_name) + self._create_duckdb_table_from_files( + downloaded_files, table_name, config.config_name + ) self.logger.info( f"Downloaded and loaded {len(downloaded_files)} files into {table_name}" ) @@ -203,7 +207,7 @@ def _download_and_load_metadata(self, config, table_name: str) -> bool: return False def _create_duckdb_table_from_files( - self, file_paths: list[str], table_name: str + self, file_paths: list[str], table_name: str, config_name: str ) -> None: """Create DuckDB table/view from parquet files.""" if len(file_paths) == 1: @@ -225,6 +229,50 @@ def _create_duckdb_table_from_files( f"Created DuckDB view {table_name} from {len(file_paths)} files" ) + # Validate source_sample fields if they exist + self._validate_source_sample_fields(table_name, config_name) + + def _validate_source_sample_fields(self, table_name: str, config_name: str) -> None: + """ + Validate source_sample fields have correct format. + + Composite sample identifiers must be in the format: + "repo_id;config_name;sample_id" (exactly 3 semicolon-separated parts) + + """ + config = self.get_config(config_name) + + # Find all source_sample fields + source_sample_fields = [ + f.name + for f in config.dataset_info.features # type: ignore + if f.role == "source_sample" + ] + + if not source_sample_fields: + return # No validation needed + + # For each field, validate format + for field_name in source_sample_fields: + query = f""" + SELECT {field_name}, + LENGTH({field_name}) - LENGTH(REPLACE({field_name}, ';', '')) + AS semicolon_count + FROM {table_name} + WHERE semicolon_count != 2 + LIMIT 1 + """ + result = self.duckdb_conn.execute(query).fetchone() + + if result: + raise ValueError( + f"Invalid format in field '{field_name}' " + f"with role='source_sample'. " + f"Expected 'repo_id;config_name;sample_id' " + f"(3 semicolon-separated parts), " + f"but found: '{result[0]}'" + ) + def _extract_embedded_metadata_field( self, data_table_name: str, field_name: str, metadata_table_name: str ) -> bool: @@ -519,9 +567,7 @@ def _format_bytes(self, bytes_size: int) -> str: size /= 1024.0 return f"{size:.1f}TB" - def query( - self, sql: str, config_name: str, refresh_cache: bool = False - ) -> Any: + def query(self, sql: str, config_name: str, refresh_cache: bool = False) -> Any: """ Execute SQL query against a specific dataset configuration. @@ -543,6 +589,7 @@ def query( "SELECT DISTINCT sample_id FROM harbison_2004", "harbison_2004" ) + """ # Validate config exists if config_name not in [c.config_name for c in self.configs]: diff --git a/tfbpapi/models.py b/tfbpapi/models.py index a1489f7..bb86f2e 100644 --- a/tfbpapi/models.py +++ b/tfbpapi/models.py @@ -24,7 +24,7 @@ class DatasetType(str, Enum): ANNOTATED_FEATURES = "annotated_features" GENOME_MAP = "genome_map" METADATA = "metadata" - QC_DATA = "qc_data" + COMPARATIVE = "comparative" class FeatureInfo(BaseModel): @@ -43,7 +43,8 @@ class FeatureInfo(BaseModel): description: str = Field(..., description="Description of the field") role: str | None = Field( default=None, - description="Optional semantic role. 'experimental_condition' has special behavior.", + description="Optional semantic role. 'experimental_condition' " + "has special behavior.", ) definitions: dict[str, Any] | None = Field( default=None, @@ -107,13 +108,13 @@ class DatasetConfig(BaseModel): @field_validator("applies_to") @classmethod def applies_to_only_for_metadata(cls, v, info): - """Validate that applies_to is only used for metadata or qc_data configs.""" + """Validate that applies_to is only used for metadata or comparative configs.""" if v is not None: dataset_type = info.data.get("dataset_type") - if dataset_type not in (DatasetType.METADATA, DatasetType.QC_DATA): + if dataset_type not in (DatasetType.METADATA, DatasetType.COMPARATIVE): raise ValueError( "applies_to field is only valid " - "for metadata and qc_data dataset types" + "for metadata and comparative dataset types" ) return v @@ -231,6 +232,42 @@ class MetadataRelationship(BaseModel): # ============================================================================ +class ComparativeAnalysis(BaseModel): + """ + Reference to a comparative dataset that includes this dataset. + + Comparative datasets relate samples across multiple source datasets. + This model specifies which comparative dataset references the current + dataset and through which field (via_field). + + Attributes: + repo: HuggingFace repository ID of the comparative dataset + dataset: Config name of the comparative dataset + via_field: Field in the comparative dataset containing composite + identifiers that reference this dataset's samples. + Format: "repo_id;config_name;sample_id" + + Example: + ```python + # In BrentLab/callingcards config + ComparativeAnalysis( + repo="BrentLab/yeast_comparative_analysis", + dataset="dto", + via_field="binding_id" + ) + # Means: dto dataset has a binding_id field with values like: + # "BrentLab/callingcards;annotated_features;123" + ``` + + """ + + repo: str = Field(..., description="Comparative dataset repository ID") + dataset: str = Field(..., description="Comparative dataset config name") + via_field: str = Field( + ..., description="Field containing composite sample identifiers" + ) + + class PropertyMapping(BaseModel): """ Mapping specification for a single property. @@ -243,6 +280,12 @@ class PropertyMapping(BaseModel): field: Optional field name for field-level properties. When specified, looks in this field's definitions. When omitted, looks in repo/config-level experimental_conditions. + expression: Optional SQL expression for derived/computed fields. + When specified, creates a computed column. + Cannot be used with field or path. + dtype: Optional data type specification for type conversion. + Supported values: 'string', 'numeric', 'bool'. + When specified, extracted values are converted to this type. Examples: Field-level property with path: @@ -254,10 +297,19 @@ class PropertyMapping(BaseModel): Field-level column alias (no path): PropertyMapping(field="condition") + Derived field with expression: + PropertyMapping(expression="dto_fdr < 0.05") + """ field: str | None = Field(None, description="Field name for field-level properties") path: str | None = Field(None, description="Dot-notation path to property") + expression: str | None = Field( + None, description="SQL expression for derived fields" + ) + dtype: str | None = Field( + None, description="Data type for conversion: 'string', 'numeric', or 'bool'" + ) @field_validator("path") @classmethod @@ -275,21 +327,106 @@ def validate_field(cls, v: str | None) -> str | None: raise ValueError("field cannot be empty or whitespace") return v.strip() if v else None + @field_validator("expression") + @classmethod + def validate_expression(cls, v: str | None) -> str | None: + """Ensure expression is not empty string if provided.""" + if v is not None and not v.strip(): + raise ValueError("expression cannot be empty or whitespace") + return v.strip() if v else None + @model_validator(mode="after") def validate_at_least_one_specified(self) -> "PropertyMapping": - """Ensure at least field or path is specified.""" - if self.field is None and self.path is None: - raise ValueError("At least one of 'field' or 'path' must be specified") + """Ensure at least one field type is specified and mutually exclusive.""" + if self.expression is not None: + if self.field is not None or self.path is not None: + raise ValueError( + "expression cannot be used with field or path - " + "derived fields are computed, not extracted" + ) + elif self.field is None and self.path is None: + raise ValueError( + "At least one of 'field', 'path', or 'expression' must be specified" + ) return self +class DatasetVirtualDBConfig(BaseModel): + """ + VirtualDB configuration for a specific dataset within a repository. + + Attributes: + sample_id: Mapping for the sample identifier field (required for + primary datasets) + comparative_analyses: Optional list of comparative datasets that + reference this dataset + properties: Property mappings for this specific dataset (field names to + PropertyMapping) + + Example: + ```yaml + # In BrentLab/callingcards config + annotated_features: + sample_id: + field: sample_id + comparative_analyses: + - repo: BrentLab/yeast_comparative_analysis + dataset: dto + via_field: binding_id + regulator_locus_tag: + field: regulator_locus_tag + dto_fdr: # Field from comparative dataset, optional renaming + field: dto_fdr + ``` + + """ + + sample_id: PropertyMapping | None = Field( + None, description="Mapping for sample identifier field" + ) + comparative_analyses: list[ComparativeAnalysis] = Field( + default_factory=list, + description="Comparative datasets referencing this dataset", + ) + # Allow additional property mappings via extra fields + model_config = ConfigDict(extra="allow") + + @model_validator(mode="before") + @classmethod + def parse_property_mappings(cls, data: Any) -> Any: + """Parse extra fields as PropertyMapping objects.""" + if not isinstance(data, dict): + return data + + # Process all fields except sample_id and comparative_analyses + result = {} + for key, value in data.items(): + if key in ("sample_id", "comparative_analyses"): + # These are typed fields, let Pydantic handle them + result[key] = value + elif isinstance(value, dict): + # Assume it's a PropertyMapping + try: + result[key] = PropertyMapping.model_validate(value) + except Exception as e: + raise ValueError( + f"Invalid PropertyMapping for field '{key}': {e}" + ) from e + else: + # Already parsed or wrong type + result[key] = value + + return result + + class RepositoryConfig(BaseModel): """ Configuration for a single repository. Eg BrentLab/harbison_2004. Attributes: properties: Repo-wide property mappings that apply to all datasets - dataset: Dataset-specific property mappings (override repo-wide) + dataset: Dataset-specific configurations including sample_id, + comparative_analyses, and property mappings Example: ```python @@ -298,12 +435,21 @@ class RepositoryConfig(BaseModel): "temperature_celsius": PropertyMapping(path="temperature_celsius") }, dataset={ - "dataset_name": { - "carbon_source": PropertyMapping( + "dataset_name": DatasetVirtualDBConfig( + sample_id=PropertyMapping(field="sample_id"), + comparative_analyses=[ + ComparativeAnalysis( + repo="BrentLab/yeast_comparative_analysis", + dataset="dto", + via_field="binding_id" + ) + ], + # Additional property mappings via extra fields + **{"carbon_source": PropertyMapping( field="condition", path="media.carbon_source" - ) - } + )} + ) } ) ``` @@ -313,44 +459,43 @@ class RepositoryConfig(BaseModel): properties: dict[str, PropertyMapping] = Field( default_factory=dict, description="Repo-wide property mappings" ) - dataset: dict[str, dict[str, PropertyMapping]] | None = Field( - None, description="Dataset-specific property mappings" + dataset: dict[str, DatasetVirtualDBConfig] | None = Field( + None, description="Dataset-specific configurations" ) @model_validator(mode="before") @classmethod def parse_structure(cls, data: Any) -> Any: - """Parse raw dict structure into typed PropertyMapping objects.""" + """Parse raw dict structure into typed objects.""" if not isinstance(data, dict): return data # Extract and parse dataset section dataset_section = data.get("dataset") - parsed_datasets: dict[str, dict[str, PropertyMapping]] | None = None + parsed_datasets: dict[str, DatasetVirtualDBConfig] | None = None if dataset_section: if not isinstance(dataset_section, dict): raise ValueError("'dataset' key must contain a dict") parsed_datasets = {} - for dataset_name, properties in dataset_section.items(): - if not isinstance(properties, dict): - raise ValueError( - f"Dataset '{dataset_name}' must contain a dict of properties" + for dataset_name, config_dict in dataset_section.items(): + if not isinstance(config_dict, dict): + raise ValueError(f"Dataset '{dataset_name}' must contain a dict") + + # Parse DatasetVirtualDBConfig + # The config_dict may contain: + # - sample_id (PropertyMapping) + # - comparative_analyses (list[ComparativeAnalysis]) + # - Other fields as PropertyMappings (via extra="allow") + try: + parsed_datasets[dataset_name] = ( + DatasetVirtualDBConfig.model_validate(config_dict) ) - - # Parse each property mapping into PropertyMapping object - parsed_datasets[dataset_name] = {} - for prop_name, mapping in properties.items(): - try: - parsed_datasets[dataset_name][prop_name] = ( - PropertyMapping.model_validate(mapping) - ) - except Exception as e: - raise ValueError( - f"Invalid property '{prop_name}' in dataset " - f"'{dataset_name}': {e}" - ) from e + except Exception as e: + raise ValueError( + f"Invalid configuration for dataset '{dataset_name}': {e}" + ) from e # Parse repo-wide properties (all keys except 'dataset') parsed_properties = {} @@ -375,7 +520,8 @@ class MetadataConfig(BaseModel): Attributes: factor_aliases: Optional mappings of standardized names to actual values. - Example: {"carbon_source": {"glucose": ["D-glucose", "dextrose"]}} + Example: {"carbon_source": + {"glucose": ["D-glucose", "dextrose"]}} missing_value_labels: Labels for missing values by property name description: Human-readable descriptions for each property repositories: Dict mapping repository IDs to their configurations @@ -500,7 +646,8 @@ def parse_repositories(cls, data: Any) -> Any: if not repositories_data: raise ValueError( - "Configuration must have a 'repositories' key with at least one repository" + "Configuration must have a 'repositories' key " + "with at least one repository" ) if not isinstance(repositories_data, dict): @@ -579,6 +726,9 @@ def get_property_mappings( # Override with dataset-specific properties if repo_config.dataset and config_name in repo_config.dataset: - mappings.update(repo_config.dataset[config_name]) + dataset_config = repo_config.dataset[config_name] + # DatasetVirtualDBConfig stores property mappings in model_extra + if hasattr(dataset_config, "model_extra") and dataset_config.model_extra: + mappings.update(dataset_config.model_extra) return mappings diff --git a/tfbpapi/tests/example_datacards.py b/tfbpapi/tests/example_datacards.py index 1d732de..36b023f 100644 --- a/tfbpapi/tests/example_datacards.py +++ b/tfbpapi/tests/example_datacards.py @@ -1,3 +1,4 @@ +# flake8: noqa """ Three diverse datacard examples for testing datacard parsing and database construction. @@ -468,11 +469,11 @@ - config_name: qc_metrics description: Quality control metrics for all genome coverage samples - dataset_type: qc_data + dataset_type: comparative applies_to: ["genome_coverage"] data_files: - split: train - path: qc_data.parquet + path: comparative_data.parquet dataset_info: features: - name: sample_id diff --git a/tfbpapi/tests/test_hf_cache_manager.py b/tfbpapi/tests/test_hf_cache_manager.py index 94fb568..aa395df 100644 --- a/tfbpapi/tests/test_hf_cache_manager.py +++ b/tfbpapi/tests/test_hf_cache_manager.py @@ -96,8 +96,11 @@ def test_create_duckdb_table_from_files_single_file( mock_conn = Mock() test_cache_manager = HfCacheManager("test/repo", mock_conn) + # Mock the validation method since we're testing table creation + test_cache_manager._validate_source_sample_fields = Mock() # type: ignore + test_cache_manager._create_duckdb_table_from_files( - [str(parquet_file)], "test_table" + [str(parquet_file)], "test_table", "test_config" ) mock_conn.execute.assert_called_once() @@ -122,7 +125,12 @@ def test_create_duckdb_table_from_files_multiple_files( mock_conn = Mock() test_cache_manager = HfCacheManager("test/repo", mock_conn) - test_cache_manager._create_duckdb_table_from_files(files, "test_table") + # Mock the validation method since we're testing table creation + test_cache_manager._validate_source_sample_fields = Mock() # type: ignore + + test_cache_manager._create_duckdb_table_from_files( + files, "test_table", "test_config" + ) mock_conn.execute.assert_called_once() sql_call = mock_conn.execute.call_args[0][0] @@ -381,9 +389,12 @@ def test_metadata_workflow_integration(self, tmpdir): mock_conn = Mock() test_cache_manager = HfCacheManager("test/repo", mock_conn) + # Mock the validation method since we're testing table creation + test_cache_manager._validate_source_sample_fields = Mock() # type: ignore + # Test _create_duckdb_table_from_files directly test_cache_manager._create_duckdb_table_from_files( - [str(metadata_file)], "metadata_test_metadata" + [str(metadata_file)], "metadata_test_metadata", "test_metadata" ) # Verify the SQL was generated correctly @@ -516,3 +527,257 @@ def mock_delete_revisions(*revision_hashes): cache_info.delete_revisions = mock_delete_revisions return cache_info + + +class TestSourceSampleValidation: + """Test validation of source_sample field format.""" + + def setup_method(self): + """Set up test fixtures.""" + self.conn = duckdb.connect(":memory:") + self.repo_id = "test/repo" + + def test_valid_source_sample_format(self, tmpdir): + """Test that valid source_sample format passes validation.""" + # Create parquet file with valid composite identifiers + parquet_file = tmpdir.join("valid_data.parquet") + self.conn.execute( + f""" + COPY ( + SELECT + 'BrentLab/harbison_2004;harbison_2004;CBF1_YPD' + as binding_sample_ref, + 'gene_' || (row_number() OVER()) as target_locus_tag, + random() * 100 as binding_score + FROM range(5) + ) TO '{parquet_file}' (FORMAT PARQUET) + """ + ) + + # Create mock datacard with source_sample field + mock_feature = Mock() + mock_feature.name = "binding_sample_ref" + mock_feature.role = "source_sample" + + mock_dataset_info = Mock() + mock_dataset_info.features = [mock_feature] + + mock_config = Mock() + mock_config.config_name = "test_config" + mock_config.dataset_info = mock_dataset_info + + with patch("tfbpapi.hf_cache_manager.DataCard.__init__", return_value=None): + cache_manager = HfCacheManager(self.repo_id, self.conn) + cache_manager.get_config = Mock(return_value=mock_config) # type: ignore + + # Should not raise any error + cache_manager._create_duckdb_table_from_files( + [str(parquet_file)], "test_table", "test_config" + ) + + def test_invalid_source_sample_two_parts(self, tmpdir): + """Test that source_sample with only 2 parts raises ValueError.""" + # Create parquet file with invalid format (only 2 parts) + parquet_file = tmpdir.join("invalid_data.parquet") + self.conn.execute( + f""" + COPY ( + SELECT + 'BrentLab/harbison_2004;CBF1_YPD' as binding_sample_ref, + 'gene_' || (row_number() OVER()) as target_locus_tag, + random() * 100 as binding_score + FROM range(5) + ) TO '{parquet_file}' (FORMAT PARQUET) + """ + ) + + # Create mock datacard with source_sample field + mock_feature = Mock() + mock_feature.name = "binding_sample_ref" + mock_feature.role = "source_sample" + + mock_dataset_info = Mock() + mock_dataset_info.features = [mock_feature] + + mock_config = Mock() + mock_config.config_name = "test_config" + mock_config.dataset_info = mock_dataset_info + + with patch("tfbpapi.hf_cache_manager.DataCard.__init__", return_value=None): + cache_manager = HfCacheManager(self.repo_id, self.conn) + cache_manager.get_config = Mock(return_value=mock_config) # type: ignore + + # Should raise ValueError with clear message + with pytest.raises(ValueError) as exc_info: + cache_manager._create_duckdb_table_from_files( + [str(parquet_file)], "test_table", "test_config" + ) + + error_msg = str(exc_info.value) + assert "Invalid format in field 'binding_sample_ref'" in error_msg + assert "role='source_sample'" in error_msg + assert "3 semicolon-separated parts" in error_msg + assert "BrentLab/harbison_2004;CBF1_YPD" in error_msg + + def test_invalid_source_sample_one_part(self, tmpdir): + """Test that source_sample with only 1 part raises ValueError.""" + # Create parquet file with invalid format (only 1 part) + parquet_file = tmpdir.join("invalid_data.parquet") + self.conn.execute( + f""" + COPY ( + SELECT + 'CBF1_YPD' as binding_sample_ref, + 'gene_' || (row_number() OVER()) as target_locus_tag, + random() * 100 as binding_score + FROM range(5) + ) TO '{parquet_file}' (FORMAT PARQUET) + """ + ) + + # Create mock datacard with source_sample field + mock_feature = Mock() + mock_feature.name = "binding_sample_ref" + mock_feature.role = "source_sample" + + mock_dataset_info = Mock() + mock_dataset_info.features = [mock_feature] + + mock_config = Mock() + mock_config.config_name = "test_config" + mock_config.dataset_info = mock_dataset_info + + with patch("tfbpapi.hf_cache_manager.DataCard.__init__", return_value=None): + cache_manager = HfCacheManager(self.repo_id, self.conn) + cache_manager.get_config = Mock(return_value=mock_config) # type: ignore + + # Should raise ValueError + with pytest.raises(ValueError) as exc_info: + cache_manager._create_duckdb_table_from_files( + [str(parquet_file)], "test_table", "test_config" + ) + + error_msg = str(exc_info.value) + assert "Invalid format in field 'binding_sample_ref'" in error_msg + assert "CBF1_YPD" in error_msg + + def test_invalid_source_sample_four_parts(self, tmpdir): + """Test that source_sample with 4 parts raises ValueError.""" + # Create parquet file with invalid format (4 parts) + parquet_file = tmpdir.join("invalid_data.parquet") + self.conn.execute( + f""" + COPY ( + SELECT + 'a;b;c;d' as binding_sample_ref, + 'gene_' || (row_number() OVER()) as target_locus_tag, + random() * 100 as binding_score + FROM range(5) + ) TO '{parquet_file}' (FORMAT PARQUET) + """ + ) + + # Create mock datacard with source_sample field + mock_feature = Mock() + mock_feature.name = "binding_sample_ref" + mock_feature.role = "source_sample" + + mock_dataset_info = Mock() + mock_dataset_info.features = [mock_feature] + + mock_config = Mock() + mock_config.config_name = "test_config" + mock_config.dataset_info = mock_dataset_info + + with patch("tfbpapi.hf_cache_manager.DataCard.__init__", return_value=None): + cache_manager = HfCacheManager(self.repo_id, self.conn) + cache_manager.get_config = Mock(return_value=mock_config) # type: ignore + + # Should raise ValueError + with pytest.raises(ValueError) as exc_info: + cache_manager._create_duckdb_table_from_files( + [str(parquet_file)], "test_table", "test_config" + ) + + error_msg = str(exc_info.value) + assert "Invalid format in field 'binding_sample_ref'" in error_msg + assert "a;b;c;d" in error_msg + + def test_no_source_sample_fields(self, tmpdir): + """Test that validation is skipped when no source_sample fields exist.""" + # Create parquet file with normal data + parquet_file = tmpdir.join("normal_data.parquet") + self.conn.execute( + f""" + COPY ( + SELECT + 'gene_' || (row_number() OVER()) as target_locus_tag, + random() * 100 as expression_value + FROM range(5) + ) TO '{parquet_file}' (FORMAT PARQUET) + """ + ) + + # Create mock datacard without source_sample fields + mock_feature = Mock() + mock_feature.name = "target_locus_tag" + mock_feature.role = "target_identifier" + + mock_dataset_info = Mock() + mock_dataset_info.features = [mock_feature] + + mock_config = Mock() + mock_config.config_name = "test_config" + mock_config.dataset_info = mock_dataset_info + + with patch("tfbpapi.hf_cache_manager.DataCard.__init__", return_value=None): + cache_manager = HfCacheManager(self.repo_id, self.conn) + cache_manager.get_config = Mock(return_value=mock_config) # type: ignore + + # Should not raise any error + cache_manager._create_duckdb_table_from_files( + [str(parquet_file)], "test_table", "test_config" + ) + + def test_multiple_source_sample_fields(self, tmpdir): + """Test validation with multiple source_sample fields.""" + # Create parquet file with multiple composite identifier fields + parquet_file = tmpdir.join("multi_ref_data.parquet") + self.conn.execute( + f""" + COPY ( + SELECT + 'BrentLab/harbison_2004;harbison_2004;CBF1_YPD' + as binding_sample_ref, + 'BrentLab/kemmeren_2014;kemmeren_2014;sample_42' + as expression_sample_ref, + 'gene_' || (row_number() OVER()) as target_locus_tag + FROM range(5) + ) TO '{parquet_file}' (FORMAT PARQUET) + """ + ) + + # Create mock datacard with multiple source_sample fields + mock_feature1 = Mock() + mock_feature1.name = "binding_sample_ref" + mock_feature1.role = "source_sample" + + mock_feature2 = Mock() + mock_feature2.name = "expression_sample_ref" + mock_feature2.role = "source_sample" + + mock_dataset_info = Mock() + mock_dataset_info.features = [mock_feature1, mock_feature2] + + mock_config = Mock() + mock_config.config_name = "test_config" + mock_config.dataset_info = mock_dataset_info + + with patch("tfbpapi.hf_cache_manager.DataCard.__init__", return_value=None): + cache_manager = HfCacheManager(self.repo_id, self.conn) + cache_manager.get_config = Mock(return_value=mock_config) # type: ignore + + # Both fields are valid - should not raise + cache_manager._create_duckdb_table_from_files( + [str(parquet_file)], "test_table", "test_config" + ) diff --git a/tfbpapi/tests/test_metadata_config_models.py b/tfbpapi/tests/test_metadata_config_models.py index 09d6523..1697930 100644 --- a/tfbpapi/tests/test_metadata_config_models.py +++ b/tfbpapi/tests/test_metadata_config_models.py @@ -5,10 +5,8 @@ """ -from pathlib import Path - import pytest -import yaml +import yaml # type: ignore from pydantic import ValidationError from tfbpapi.models import ( @@ -63,10 +61,97 @@ def test_valid_field_only_mapping(self): assert mapping.path is None def test_invalid_neither_field_nor_path(self): - """Test that at least one of field or path is required.""" + """Test that at least one of field, path, or expression is required.""" with pytest.raises(ValidationError) as exc_info: PropertyMapping() - assert "At least one of 'field' or 'path' must be specified" in str(exc_info.value) + assert ( + "At least one of 'field', 'path', or 'expression' must be specified" + in str(exc_info.value) + ) + + def test_valid_expression_only(self): + """Test valid expression-only mapping (derived field).""" + mapping = PropertyMapping(expression="dto_fdr < 0.05") + assert mapping.expression == "dto_fdr < 0.05" + assert mapping.field is None + assert mapping.path is None + + def test_invalid_expression_with_field(self): + """Test that expression cannot be combined with field.""" + with pytest.raises(ValidationError) as exc_info: + PropertyMapping(expression="dto_fdr < 0.05", field="sample_id") + assert "expression cannot be used with field or path" in str(exc_info.value) + + def test_invalid_expression_with_path(self): + """Test that expression cannot be combined with path.""" + with pytest.raises(ValidationError) as exc_info: + PropertyMapping(expression="dto_fdr < 0.05", path="media.carbon_source") + assert "expression cannot be used with field or path" in str(exc_info.value) + + +class TestComparativeAnalysis: + """Tests for ComparativeAnalysis model.""" + + def test_valid_comparative_analysis(self): + """Test valid comparative analysis configuration.""" + from tfbpapi.models import ComparativeAnalysis + + ca = ComparativeAnalysis( + repo="BrentLab/yeast_comparative_analysis", + dataset="dto", + via_field="binding_id", + ) + assert ca.repo == "BrentLab/yeast_comparative_analysis" + assert ca.dataset == "dto" + assert ca.via_field == "binding_id" + + +class TestDatasetVirtualDBConfig: + """Tests for DatasetVirtualDBConfig model.""" + + def test_valid_config_with_sample_id(self): + """Test valid dataset config with sample_id.""" + from tfbpapi.models import DatasetVirtualDBConfig, PropertyMapping + + config = DatasetVirtualDBConfig(sample_id=PropertyMapping(field="sample_id")) + assert config.sample_id is not None + assert config.sample_id.field == "sample_id" + + def test_valid_config_with_comparative_analyses(self): + """Test valid dataset config with comparative analyses.""" + from tfbpapi.models import DatasetVirtualDBConfig + + config_dict = { + "sample_id": {"field": "sample_id"}, + "comparative_analyses": [ + { + "repo": "BrentLab/yeast_comparative_analysis", + "dataset": "dto", + "via_field": "binding_id", + } + ], + } + config = DatasetVirtualDBConfig.model_validate(config_dict) + assert config.sample_id is not None + assert len(config.comparative_analyses) == 1 + assert ( + config.comparative_analyses[0].repo == "BrentLab/yeast_comparative_analysis" + ) + + def test_config_with_extra_property_mappings(self): + """Test that extra fields are parsed as PropertyMappings.""" + from tfbpapi.models import DatasetVirtualDBConfig + + config_dict = { + "sample_id": {"field": "sample_id"}, + "regulator_locus_tag": {"field": "regulator_locus_tag"}, + "dto_fdr": {"expression": "dto_fdr < 0.05"}, + } + config = DatasetVirtualDBConfig.model_validate(config_dict) + + # Access extra fields via model_extra + assert "regulator_locus_tag" in config.model_extra + assert "dto_fdr" in config.model_extra class TestRepositoryConfig: @@ -110,8 +195,11 @@ def test_valid_field_only_property(self): config = RepositoryConfig.model_validate(config_data) assert config.dataset is not None assert "dataset1" in config.dataset - assert config.dataset["dataset1"]["carbon_source"].field == "condition" - assert config.dataset["dataset1"]["carbon_source"].path is None + # Access extra field via model_extra + dataset_config = config.dataset["dataset1"] + assert "carbon_source" in dataset_config.model_extra + assert dataset_config.model_extra["carbon_source"].field == "condition" + assert dataset_config.model_extra["carbon_source"].path is None def test_valid_repo_wide_field_only_property(self): """Test that repo-wide field-only properties are valid.""" @@ -136,7 +224,9 @@ def test_valid_config_with_aliases(self, tmp_path): }, "repositories": { "BrentLab/test": { - "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + "dataset": { + "test": {"carbon_source": {"path": "media.carbon_source"}} + } } }, } @@ -158,7 +248,9 @@ def test_valid_config_without_aliases(self, tmp_path): config_data = { "repositories": { "BrentLab/test": { - "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + "dataset": { + "test": {"carbon_source": {"path": "media.carbon_source"}} + } } } } @@ -176,7 +268,9 @@ def test_valid_config_empty_aliases(self, tmp_path): "factor_aliases": {}, "repositories": { "BrentLab/test": { - "dataset": {"test": {"carbon_source": {"path": "media.carbon_source"}}} + "dataset": { + "test": {"carbon_source": {"path": "media.carbon_source"}} + } } }, } @@ -248,7 +342,9 @@ def test_aliases_allow_numeric_values(self): }, "repositories": { "BrentLab/test": { - "dataset": {"test": {"temperature": {"path": "temperature_celsius"}}} + "dataset": { + "test": {"temperature": {"path": "temperature_celsius"}} + } } }, } @@ -310,7 +406,9 @@ def test_get_property_mappings(self, tmp_path): "BrentLab/kemmeren_2014": { "temperature": {"path": "temperature_celsius"}, # Repo-wide "dataset": { - "kemmeren_2014": {"carbon_source": {"path": "media.carbon_source"}} + "kemmeren_2014": { + "carbon_source": {"path": "media.carbon_source"} + } }, } }, diff --git a/tfbpapi/tests/test_models.py b/tfbpapi/tests/test_models.py index bf52f75..1771c4d 100644 --- a/tfbpapi/tests/test_models.py +++ b/tfbpapi/tests/test_models.py @@ -30,7 +30,7 @@ def test_dataset_type_values(self): assert DatasetType.ANNOTATED_FEATURES == "annotated_features" assert DatasetType.GENOME_MAP == "genome_map" assert DatasetType.METADATA == "metadata" - assert DatasetType.QC_DATA == "qc_data" + assert DatasetType.COMPARATIVE == "comparative" def test_dataset_type_from_string(self): """Test creating DatasetType from string.""" @@ -157,7 +157,7 @@ def test_dataset_info_with_partitioning(self): partitioning=PartitioningInfo(enabled=True, partition_by=["chr"]), ) assert len(dataset_info.features) == 2 - assert dataset_info.partitioning.enabled is True + assert dataset_info.partitioning.enabled is True # type: ignore class TestDatasetConfig: diff --git a/tfbpapi/tests/test_virtual_db.py b/tfbpapi/tests/test_virtual_db.py index 3728e3e..1293bf9 100644 --- a/tfbpapi/tests/test_virtual_db.py +++ b/tfbpapi/tests/test_virtual_db.py @@ -5,11 +5,12 @@ """ -from pathlib import Path import tempfile -import pytest +from pathlib import Path + import pandas as pd -import yaml +import pytest +import yaml # type: ignore from tfbpapi.virtual_db import VirtualDB, get_nested_value, normalize_value @@ -33,10 +34,7 @@ def test_get_nested_value_list_extraction(self): """Test extracting property from list of dicts.""" data = { "media": { - "carbon_source": [ - {"compound": "glucose"}, - {"compound": "galactose"} - ] + "carbon_source": [{"compound": "glucose"}, {"compound": "galactose"}] } } result = get_nested_value(data, "media.carbon_source.compound") @@ -44,7 +42,7 @@ def test_get_nested_value_list_extraction(self): def test_get_nested_value_non_dict(self): """Test that non-dict input returns None.""" - result = get_nested_value("not a dict", "path") + result = get_nested_value("not a dict", "path") # type: ignore assert result is None def test_normalize_value_exact_match(self): @@ -90,15 +88,11 @@ def create_test_config(self, **overrides): "factor_aliases": { "carbon_source": { "glucose": ["D-glucose", "dextrose"], - "galactose": ["D-galactose", "Galactose"] + "galactose": ["D-galactose", "Galactose"], } }, - "missing_value_labels": { - "carbon_source": "unspecified" - }, - "description": { - "carbon_source": "Carbon source in growth media" - }, + "missing_value_labels": {"carbon_source": "unspecified"}, + "description": {"carbon_source": "Carbon source in growth media"}, "repositories": { "BrentLab/test_repo": { "temperature_celsius": {"path": "temperature_celsius"}, @@ -106,19 +100,19 @@ def create_test_config(self, **overrides): "test_dataset": { "carbon_source": { "field": "condition", - "path": "media.carbon_source.compound" + "path": "media.carbon_source.compound", } } - } + }, } - } + }, } config.update(overrides) return config def test_init_with_valid_config(self): """Test VirtualDB initialization with valid config.""" - with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: yaml.dump(self.create_test_config(), f) config_path = f.name @@ -132,7 +126,7 @@ def test_init_with_valid_config(self): def test_init_with_token(self): """Test VirtualDB initialization with HF token.""" - with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: yaml.dump(self.create_test_config(), f) config_path = f.name @@ -149,7 +143,7 @@ def test_init_missing_config_file(self): def test_repr(self): """Test string representation.""" - with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: yaml.dump(self.create_test_config(), f) config_path = f.name @@ -176,25 +170,28 @@ def create_multi_dataset_config(self): "temperature_celsius": {"path": "temperature_celsius"}, "dataset": { "dataset1": { - "carbon_source": {"field": "condition", "path": "media.carbon_source"} + "carbon_source": { + "field": "condition", + "path": "media.carbon_source", + } } - } + }, }, "BrentLab/repo2": { "nitrogen_source": {"path": "media.nitrogen_source"}, "dataset": { "dataset2": { "carbon_source": {"path": "media.carbon_source"}, - "temperature_celsius": {"path": "temperature_celsius"} + "temperature_celsius": {"path": "temperature_celsius"}, } - } - } - } + }, + }, + }, } def test_get_fields_all_datasets(self): """Test getting all fields across all datasets.""" - with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: yaml.dump(self.create_multi_dataset_config(), f) config_path = f.name @@ -210,7 +207,7 @@ def test_get_fields_all_datasets(self): def test_get_fields_specific_dataset(self): """Test getting fields for specific dataset.""" - with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: yaml.dump(self.create_multi_dataset_config(), f) config_path = f.name @@ -226,7 +223,7 @@ def test_get_fields_specific_dataset(self): def test_get_fields_invalid_partial_args(self): """Test error when only one of repo_id/config_name provided.""" - with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: yaml.dump(self.create_multi_dataset_config(), f) config_path = f.name @@ -239,7 +236,7 @@ def test_get_fields_invalid_partial_args(self): def test_get_common_fields(self): """Test getting fields common to all datasets.""" - with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: yaml.dump(self.create_multi_dataset_config(), f) config_path = f.name @@ -254,14 +251,6 @@ def test_get_common_fields(self): finally: Path(config_path).unlink() - def test_get_common_fields_empty_config(self): - """Test getting common fields with no repositories.""" - config = {"factor_aliases": {}} - with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: - # This will fail validation (needs at least one repo) - # So we skip this test - pass - class TestCaching: """Tests for view materialization and caching.""" @@ -278,12 +267,12 @@ def create_simple_config(self): } } } - } + }, } def test_invalidate_cache_all(self): """Test invalidating all cache.""" - with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: yaml.dump(self.create_simple_config(), f) config_path = f.name @@ -300,7 +289,7 @@ def test_invalidate_cache_all(self): def test_invalidate_cache_specific(self): """Test invalidating specific dataset cache.""" - with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: yaml.dump(self.create_simple_config(), f) config_path = f.name @@ -323,13 +312,15 @@ class TestFiltering: def test_apply_filters_exact_match(self): """Test exact value matching in filters.""" - df = pd.DataFrame({ - "sample_id": ["s1", "s2", "s3"], - "carbon_source": ["glucose", "galactose", "glucose"] - }) + df = pd.DataFrame( + { + "sample_id": ["s1", "s2", "s3"], + "carbon_source": ["glucose", "galactose", "glucose"], + } + ) # Create minimal VirtualDB instance - with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: config = { "repositories": { "BrentLab/test": { @@ -354,17 +345,18 @@ def test_apply_filters_exact_match(self): def test_apply_filters_numeric_range(self): """Test numeric range filtering.""" - df = pd.DataFrame({ - "sample_id": ["s1", "s2", "s3"], - "temperature_celsius": [25, 30, 37] - }) + df = pd.DataFrame( + {"sample_id": ["s1", "s2", "s3"], "temperature_celsius": [25, 30, 37]} + ) - with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: config = { "repositories": { "BrentLab/test": { "dataset": { - "test": {"temperature_celsius": {"path": "temperature_celsius"}} + "test": { + "temperature_celsius": {"path": "temperature_celsius"} + } } } } @@ -384,7 +376,10 @@ def test_apply_filters_numeric_range(self): # Test between operator filtered = vdb._apply_filters( - df, {"temperature_celsius": ("between", 28, 32)}, "BrentLab/test", "test" + df, + {"temperature_celsius": ("between", 28, 32)}, + "BrentLab/test", + "test", ) assert len(filtered) == 1 assert filtered.iloc[0]["temperature_celsius"] == 30 @@ -393,17 +388,17 @@ def test_apply_filters_numeric_range(self): def test_apply_filters_with_alias_expansion(self): """Test filter with alias expansion.""" - df = pd.DataFrame({ - "sample_id": ["s1", "s2", "s3"], - "carbon_source": ["glucose", "D-glucose", "galactose"] - }) + df = pd.DataFrame( + { + "sample_id": ["s1", "s2", "s3"], + "carbon_source": ["glucose", "D-glucose", "galactose"], + } + ) - with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: config = { "factor_aliases": { - "carbon_source": { - "glucose": ["D-glucose", "dextrose", "glucose"] - } + "carbon_source": {"glucose": ["D-glucose", "dextrose", "glucose"]} }, "repositories": { "BrentLab/test": { @@ -411,7 +406,7 @@ def test_apply_filters_with_alias_expansion(self): "test": {"carbon_source": {"path": "media.carbon_source"}} } } - } + }, } yaml.dump(config, f) config_path = f.name @@ -432,23 +427,14 @@ class TestExtraction: def test_add_field_metadata(self): """Test adding field-level metadata to DataFrame.""" - df = pd.DataFrame({ - "sample_id": ["s1", "s2"], - "condition": ["YPD", "YPG"] - }) + df = pd.DataFrame({"sample_id": ["s1", "s2"], "condition": ["YPD", "YPG"]}) field_metadata = { - "YPD": { - "carbon_source": ["glucose"], - "growth_media": ["YPD"] - }, - "YPG": { - "carbon_source": ["glycerol"], - "growth_media": ["YPG"] - } + "YPD": {"carbon_source": ["glucose"], "growth_media": ["YPD"]}, + "YPG": {"carbon_source": ["glycerol"], "growth_media": ["YPG"]}, } - with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: config = { "repositories": { "BrentLab/test": { @@ -467,8 +453,14 @@ def test_add_field_metadata(self): assert "carbon_source" in result.columns assert "growth_media" in result.columns - assert result.loc[result["condition"] == "YPD", "carbon_source"].iloc[0] == "glucose" - assert result.loc[result["condition"] == "YPG", "carbon_source"].iloc[0] == "glycerol" + assert ( + result.loc[result["condition"] == "YPD", "carbon_source"].iloc[0] + == "glucose" + ) + assert ( + result.loc[result["condition"] == "YPG", "carbon_source"].iloc[0] + == "glycerol" + ) finally: Path(config_path).unlink() @@ -478,7 +470,7 @@ class TestQuery: def test_query_empty_result(self): """Test query with no matching datasets.""" - with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: config = { "repositories": { "BrentLab/test": { @@ -501,6 +493,202 @@ def test_query_empty_result(self): Path(config_path).unlink() +class TestComparativeDatasets: + """Tests for comparative dataset field-based joins.""" + + def test_parse_composite_identifier(self): + """Test parsing composite identifiers.""" + composite_id = "BrentLab/harbison_2004;harbison_2004;sample_42" + repo, config, sample = VirtualDB._parse_composite_identifier(composite_id) + assert repo == "BrentLab/harbison_2004" + assert config == "harbison_2004" + assert sample == "sample_42" + + def test_parse_composite_identifier_invalid(self): + """Test that invalid composite IDs raise errors.""" + with pytest.raises(ValueError, match="Invalid composite ID format"): + VirtualDB._parse_composite_identifier("invalid:format") + + def test_get_comparative_fields_for_dataset(self): + """Test getting comparative fields mapping.""" + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: + config = { + "repositories": { + "BrentLab/primary": { + "dataset": { + "primary_data": { + "sample_id": {"field": "sample_id"}, + "comparative_analyses": [ + { + "repo": "BrentLab/comparative", + "dataset": "comp_data", + "via_field": "binding_id", + } + ], + } + } + }, + "BrentLab/comparative": { + "dataset": { + "comp_data": { + "dto_fdr": {"field": "dto_fdr"}, + "dto_pvalue": {"field": "dto_empirical_pvalue"}, + } + } + }, + } + } + yaml.dump(config, f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + field_mapping = vdb._get_comparative_fields_for_dataset( + "BrentLab/primary", "primary_data" + ) + + # Should have dto_fdr and dto_pvalue, but NOT binding_id (via_field) + assert "dto_fdr" in field_mapping + assert "dto_pvalue" in field_mapping + assert "binding_id" not in field_mapping + + # Check mapping structure + assert field_mapping["dto_fdr"]["comp_repo"] == "BrentLab/comparative" + assert field_mapping["dto_fdr"]["comp_dataset"] == "comp_data" + assert field_mapping["dto_fdr"]["via_field"] == "binding_id" + finally: + Path(config_path).unlink() + + def test_get_comparative_fields_no_links(self): + """Test that datasets without comparative links return empty mapping.""" + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: + config = { + "repositories": { + "BrentLab/primary": { + "dataset": { + "primary_data": {"sample_id": {"field": "sample_id"}} + } + } + } + } + yaml.dump(config, f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + field_mapping = vdb._get_comparative_fields_for_dataset( + "BrentLab/primary", "primary_data" + ) + assert field_mapping == {} + finally: + Path(config_path).unlink() + + def test_get_comparative_analyses(self): + """Test getting comparative analysis relationships.""" + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: + config = { + "repositories": { + "BrentLab/primary": { + "dataset": { + "primary_data": { + "sample_id": {"field": "sample_id"}, + "comparative_analyses": [ + { + "repo": "BrentLab/comparative", + "dataset": "comp_data", + "via_field": "binding_id", + } + ], + } + } + }, + "BrentLab/comparative": { + "dataset": {"comp_data": {"dto_fdr": {"field": "dto_fdr"}}} + }, + } + } + yaml.dump(config, f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + info = vdb.get_comparative_analyses() + + # Check primary to comparative mapping + assert "BrentLab/primary/primary_data" in info["primary_to_comparative"] + links = info["primary_to_comparative"]["BrentLab/primary/primary_data"] + assert len(links) == 1 + assert links[0]["comparative_repo"] == "BrentLab/comparative" + assert links[0]["comparative_dataset"] == "comp_data" + assert links[0]["via_field"] == "binding_id" + + # Check comparative fields + assert "BrentLab/comparative/comp_data" in info["comparative_fields"] + assert ( + "dto_fdr" + in info["comparative_fields"]["BrentLab/comparative/comp_data"] + ) + finally: + Path(config_path).unlink() + + def test_get_comparative_analyses_filtered(self): + """Test filtering comparative analyses by repo and config.""" + with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f: + config = { + "repositories": { + "BrentLab/primary1": { + "dataset": { + "data1": { + "sample_id": {"field": "sample_id"}, + "comparative_analyses": [ + { + "repo": "BrentLab/comp", + "dataset": "comp_data", + "via_field": "id1", + } + ], + } + } + }, + "BrentLab/primary2": { + "dataset": { + "data2": { + "sample_id": {"field": "sample_id"}, + "comparative_analyses": [ + { + "repo": "BrentLab/comp", + "dataset": "comp_data", + "via_field": "id2", + } + ], + } + } + }, + } + } + yaml.dump(config, f) + config_path = f.name + + try: + vdb = VirtualDB(config_path) + + # Get all + all_info = vdb.get_comparative_analyses() + assert len(all_info["primary_to_comparative"]) == 2 + + # Filter by repo and config + filtered = vdb.get_comparative_analyses("BrentLab/primary1", "data1") + assert len(filtered["primary_to_comparative"]) == 1 + assert "BrentLab/primary1/data1" in filtered["primary_to_comparative"] + + # Filter by repo only + repo_filtered = vdb.get_comparative_analyses("BrentLab/primary2") + assert len(repo_filtered["primary_to_comparative"]) == 1 + assert "BrentLab/primary2/data2" in repo_filtered["primary_to_comparative"] + finally: + Path(config_path).unlink() + + # Note: Full integration tests with real HuggingFace datasets would go here # but are excluded as they require network access and specific test datasets. # These tests cover the core logic and would be supplemented with integration diff --git a/tfbpapi/virtual_db.py b/tfbpapi/virtual_db.py index 245bf18..f6dd12e 100644 --- a/tfbpapi/virtual_db.py +++ b/tfbpapi/virtual_db.py @@ -64,7 +64,8 @@ def get_nested_value(data: dict, path: str) -> Any: List of dicts - extract property from each item: get_nested_value( - {"media": {"carbon_source": [{"compound": "glucose"}, {"compound": "galactose"}]}}, + {"media": {"carbon_source": [{"compound": "glucose"}, + {"compound": "galactose"}]}}, "media.carbon_source.compound" ) Returns: ["glucose", "galactose"] @@ -82,7 +83,8 @@ def get_nested_value(data: dict, path: str) -> Any: return None current = current[key] elif isinstance(current, list): - # If current is a list and we have more keys, extract property from each item + # If current is a list and we have more keys, + # extract property from each item if i < len(keys): # Extract the remaining path from each list item remaining_path = ".".join(keys[i:]) @@ -191,6 +193,8 @@ def __init__(self, config_path: Path | str, token: str | None = None): self.config = MetadataConfig.from_yaml(config_path) self.token = token self.cache: dict[tuple[str, str], pd.DataFrame] = {} + # Build mapping of comparative dataset references + self._comparative_links = self._build_comparative_links() def get_fields( self, repo_id: str | None = None, config_name: str | None = None @@ -216,7 +220,9 @@ def get_fields( return sorted(mappings.keys()) if repo_id is not None or config_name is not None: - raise ValueError("Both repo_id and config_name must be provided, or neither") + raise ValueError( + "Both repo_id and config_name must be provided, or neither" + ) # Get all fields across all datasets all_fields: set[str] = set() @@ -225,8 +231,16 @@ def get_fields( all_fields.update(repo_config.properties.keys()) # Add dataset-specific fields if repo_config.dataset: - for dataset_props in repo_config.dataset.values(): - all_fields.update(dataset_props.keys()) + for dataset_config in repo_config.dataset.values(): + # DatasetVirtualDBConfig stores property mappings in model_extra + if ( + hasattr(dataset_config, "model_extra") + and dataset_config.model_extra + ): + all_fields.update(dataset_config.model_extra.keys()) + # Also include special fields if they exist + if dataset_config.sample_id: + all_fields.add("sample_id") return sorted(all_fields) @@ -312,6 +326,101 @@ def get_unique_values( else: return sorted(all_values) + def get_comparative_analyses( + self, repo_id: str | None = None, config_name: str | None = None + ) -> dict[str, Any]: + """ + Get information about comparative analysis relationships. + + Returns information about which comparative datasets are available + and how they link to primary datasets. Useful for discovering + what cross-dataset analyses can be performed. + + :param repo_id: Optional repository ID to filter to specific repo + :param config_name: Optional config name (requires repo_id) + :return: Dictionary with two keys: + - "primary_to_comparative": Maps primary datasets to their + comparative analyses + - "comparative_fields": Maps comparative datasets to fields + available for joining + :raises ValueError: If config_name provided without repo_id + + Examples: + Get all comparative analysis relationships: + info = vdb.get_comparative_analyses() + + Get relationships for specific primary dataset: + info = vdb.get_comparative_analyses( + "BrentLab/callingcards", "annotated_features" + ) + + """ + if config_name and not repo_id: + raise ValueError("repo_id required when config_name is specified") + + primary_to_comparative: dict[str, list[dict[str, str]]] = {} + comparative_fields: dict[str, list[str]] = {} + + # Filter links based on parameters + if repo_id and config_name: + # Specific dataset requested + links_to_process = { + (repo_id, config_name): self._comparative_links.get( + (repo_id, config_name), {} + ) + } + elif repo_id: + # All configs in specific repo + links_to_process = { + k: v for k, v in self._comparative_links.items() if k[0] == repo_id + } + else: + # All links + links_to_process = self._comparative_links + + # Build primary to comparative mapping + for (prim_repo, prim_config), link_info in links_to_process.items(): + if "comparative_analyses" not in link_info: + continue + + dataset_key = f"{prim_repo}/{prim_config}" + primary_to_comparative[dataset_key] = [] + + for ca in link_info["comparative_analyses"]: + primary_to_comparative[dataset_key].append( + { + "comparative_repo": ca["repo"], + "comparative_dataset": ca["dataset"], + "via_field": ca["via_field"], + } + ) + + # Track which fields are available from comparative datasets + comp_key = f"{ca['repo']}/{ca['dataset']}" + if comp_key not in comparative_fields: + # Get fields from the comparative dataset + # First try config mappings + comp_fields = self.get_fields(ca["repo"], ca["dataset"]) + + # If no mappings, get actual fields from DataCard + if not comp_fields: + try: + card = DataCard(ca["repo"], token=self.token) + config = card.get_config(ca["dataset"]) + if config and config.dataset_info: + comp_fields = [ + f.name for f in config.dataset_info.features + ] + except Exception: + comp_fields = [] + + comparative_fields[comp_key] = comp_fields + + return { + "primary_to_comparative": primary_to_comparative, + "comparative_fields": comparative_fields, + } + def query( self, filters: dict[str, Any] | None = None, @@ -366,11 +475,59 @@ def query( if metadata_df.empty: continue - # Apply filters + # Separate filters into primary and comparative + primary_filters = {} + comparative_filters = {} if filters: - metadata_df = self._apply_filters(metadata_df, filters, repo_id, config_name) + # Get comparative field mapping + comp_field_mapping = self._get_comparative_fields_for_dataset( + repo_id, config_name + ) + for field, value in filters.items(): + if field in comp_field_mapping: + comparative_filters[field] = value + else: + primary_filters[field] = value + + # Apply primary filters first + if primary_filters: + metadata_df = self._apply_filters( + metadata_df, primary_filters, repo_id, config_name + ) + + # Enrich with comparative data if needed + # IMPORTANT: Do this BEFORE getting complete data so comparative fields + # are joined at the sample level, not measurement level + # This happens when: fields are requested from comparative datasets + # OR when filtering on comparative fields + if fields or comparative_filters: + comp_field_mapping = self._get_comparative_fields_for_dataset( + repo_id, config_name + ) + if fields: + requested_comp_fields = [ + f for f in fields if f in comp_field_mapping + ] + # Also need fields that are filtered on + filtered_comp_fields = [ + f for f in comparative_filters.keys() if f in comp_field_mapping + ] + all_comp_fields = list( + set(requested_comp_fields + filtered_comp_fields) + ) + if all_comp_fields: + metadata_df = self._enrich_with_comparative_data( + metadata_df, repo_id, config_name, all_comp_fields + ) + + # Apply comparative filters after enrichment + if comparative_filters: + metadata_df = self._apply_filters( + metadata_df, comparative_filters, repo_id, config_name + ) # If complete=True, join with full data + # Do this AFTER comparative enrichment so DTO fields are already added if complete: sample_ids = metadata_df["sample_id"].tolist() if sample_ids: @@ -390,10 +547,11 @@ def query( for field in fields: if field in metadata_df.columns and field not in keep_cols: keep_cols.append(field) - metadata_df = metadata_df[keep_cols] + metadata_df = metadata_df[keep_cols].copy() # Add dataset identifier if "dataset_id" not in metadata_df.columns: + metadata_df = metadata_df.copy() metadata_df["dataset_id"] = f"{repo_id}/{config_name}" results.append(metadata_df) @@ -404,9 +562,7 @@ def query( # Concatenate results, filling NaN for missing columns return pd.concat(results, ignore_index=True, sort=False) - def materialize_views( - self, datasets: list[tuple[str, str]] | None = None - ) -> None: + def materialize_views(self, datasets: list[tuple[str, str]] | None = None) -> None: """ Build and cache metadata DataFrames for faster subsequent queries. @@ -430,9 +586,7 @@ def materialize_views( # Build and cache self._build_metadata_table(repo_id, config_name, use_cache=False) - def invalidate_cache( - self, datasets: list[tuple[str, str]] | None = None - ) -> None: + def invalidate_cache(self, datasets: list[tuple[str, str]] | None = None) -> None: """ Clear cached metadata DataFrames. @@ -451,14 +605,304 @@ def invalidate_cache( if dataset_key in self.cache: del self.cache[dataset_key] + def _build_comparative_links(self) -> dict[tuple[str, str], dict[str, Any]]: + """ + Build mapping of primary datasets to their comparative dataset references. + + Returns dict keyed by (repo_id, config_name) with value being dict: { + "comparative_analyses": [ { "repo": comparative_repo_id, + "dataset": comparative_config_name, "via_field": + field_name_with_composite_ids } ] } + + """ + links: dict[tuple[str, str], dict[str, Any]] = {} + + for repo_id, repo_config in self.config.repositories.items(): + if not repo_config.dataset: + continue + + for config_name, dataset_config in repo_config.dataset.items(): + if dataset_config.comparative_analyses: + links[(repo_id, config_name)] = { + "comparative_analyses": [ + { + "repo": ca.repo, + "dataset": ca.dataset, + "via_field": ca.via_field, + } + for ca in dataset_config.comparative_analyses + ] + } + + return links + + def _get_comparative_fields_for_dataset( + self, repo_id: str, config_name: str + ) -> dict[str, dict[str, str]]: + """ + Get mapping of comparative fields available for a primary dataset. + + :param repo_id: Primary dataset repository ID + :param config_name: Primary dataset config name + :return: Dict mapping field_name to comparative dataset info + {field_name: { + "comp_repo": comparative_repo_id, + "comp_dataset": comparative_dataset_name, + "via_field": field_with_composite_ids + }} + + Example: + For callingcards dataset linked to DTO via binding_id: + { + "dto_fdr": { + "comp_repo": "BrentLab/yeast_comparative_analysis", + "comp_dataset": "dto", + "via_field": "binding_id" + }, + "dto_empirical_pvalue": {...} + } + + """ + field_mapping: dict[str, dict[str, str]] = {} + + # Get comparative analyses for this dataset + links = self._comparative_links.get((repo_id, config_name), {}) + if "comparative_analyses" not in links: + return field_mapping + + # For each comparative dataset, get its fields + for ca in links["comparative_analyses"]: + comp_repo = ca["repo"] + comp_dataset = ca["dataset"] + via_field = ca["via_field"] + + # Get fields from comparative dataset + comp_fields = self.get_fields(comp_repo, comp_dataset) + + # If no fields from config, try DataCard + if not comp_fields: + try: + from tfbpapi.datacard import DataCard + + card = DataCard(comp_repo, token=self.token) + config = card.get_config(comp_dataset) + if config and config.dataset_info: + comp_fields = [f.name for f in config.dataset_info.features] + except Exception: + comp_fields = [] + + # Map each field to this comparative dataset + for field_name in comp_fields: + # Skip the via_field itself (it's the join key) + if field_name == via_field: + continue + + field_mapping[field_name] = { + "comp_repo": comp_repo, + "comp_dataset": comp_dataset, + "via_field": via_field, + } + + return field_mapping + + def _enrich_with_comparative_data( + self, + primary_df: pd.DataFrame, + repo_id: str, + config_name: str, + requested_fields: list[str], + ) -> pd.DataFrame: + """ + Enrich primary dataset with fields from comparative datasets. + + :param primary_df: Primary dataset DataFrame with sample_id column + :param repo_id: Primary dataset repository ID + :param config_name: Primary dataset config name + :param requested_fields: List of field names requested by user + :return: DataFrame enriched with comparative fields + + """ + # Get mapping of which fields come from which comparative datasets + comp_field_mapping = self._get_comparative_fields_for_dataset( + repo_id, config_name + ) + + if not comp_field_mapping: + return primary_df + + # Find which requested fields are from comparative datasets + comp_fields_to_fetch = [f for f in requested_fields if f in comp_field_mapping] + + if not comp_fields_to_fetch: + return primary_df + + # Group fields by comparative dataset to minimize queries + by_comp_dataset: dict[tuple[str, str, str], list[str]] = {} + for field in comp_fields_to_fetch: + info = comp_field_mapping[field] + key = (info["comp_repo"], info["comp_dataset"], info["via_field"]) + if key not in by_comp_dataset: + by_comp_dataset[key] = [] + by_comp_dataset[key].append(field) + + # For each comparative dataset, load and join + result_df = primary_df.copy() + + for (comp_repo, comp_dataset, via_field), fields in by_comp_dataset.items(): + try: + # Load comparative dataset using HfCacheManager + # but query the raw data table instead of metadata view + from tfbpapi.hf_cache_manager import HfCacheManager + + comp_cache_mgr = HfCacheManager( + comp_repo, duckdb_conn=duckdb.connect(":memory:"), token=self.token + ) + + # Get the config to load data + comp_config = comp_cache_mgr.get_config(comp_dataset) + if not comp_config: + continue + + # Load the data (this will download and register parquet files) + result = comp_cache_mgr._get_metadata_for_config(comp_config) + if not result.get("success", False): + continue + + # Now query the raw data table directly (not the metadata view) + # The raw table name is config_name without "metadata_" prefix + select_fields = [via_field] + fields + columns = ", ".join(select_fields) + + # Query the actual parquet data by creating a view from the files + try: + # Get file paths that were loaded + import glob + + from huggingface_hub import snapshot_download + + cache_dir = snapshot_download( + repo_id=comp_repo, + repo_type="dataset", + allow_patterns=f"{comp_dataset}/**/*.parquet", + token=self.token, + ) + + parquet_files = glob.glob( + f"{cache_dir}/{comp_dataset}/**/*.parquet", recursive=True + ) + + if not parquet_files: + continue + + # Create a temporary view from parquet files + temp_view = f"temp_{comp_dataset}_raw" + files_sql = ", ".join([f"'{f}'" for f in parquet_files]) + comp_cache_mgr.duckdb_conn.execute( + f"CREATE OR REPLACE VIEW {temp_view} AS " + f"SELECT * FROM read_parquet([{files_sql}])" + ) + + # Query the view + sql = f"SELECT {columns} FROM {temp_view}" + comp_df = comp_cache_mgr.duckdb_conn.execute(sql).fetchdf() + + except Exception: + # If direct parquet loading fails, skip this comparative dataset + continue + + if comp_df.empty: + continue + + # Parse composite identifiers to extract sample_id + # via_field contains values like + # "BrentLab/harbison_2004;harbison_2004;123" + # We need to extract the third component and match on + # current repo/config + def extract_sample_id(composite_id: str) -> str | None: + """Extract sample_id if composite matches current dataset.""" + if pd.isna(composite_id): + return None + try: + parts = composite_id.split(";") + if len(parts) != 3: + return None + # Check if this composite ID references our dataset + if parts[0] == repo_id and parts[1] == config_name: + return parts[2] + return None + except Exception: + return None + + comp_df["_join_sample_id"] = comp_df[via_field].apply(extract_sample_id) + + # Convert _join_sample_id to match primary_df sample_id dtype + # This handles cases where sample_id is int but composite has string + if "_join_sample_id" in comp_df.columns: + primary_dtype = primary_df["sample_id"].dtype + if pd.api.types.is_integer_dtype(primary_dtype): + # Convert to numeric, coercing errors to NaN + comp_df["_join_sample_id"] = pd.to_numeric( + comp_df["_join_sample_id"], errors="coerce" + ) + elif pd.api.types.is_string_dtype(primary_dtype): + comp_df["_join_sample_id"] = comp_df["_join_sample_id"].astype( + str + ) + + # Filter to only rows that match our dataset + comp_df = comp_df[comp_df["_join_sample_id"].notna()].copy() + + if comp_df.empty: + continue + + # Drop the via_field column (we don't need it in results) + comp_df = comp_df.drop(columns=[via_field]) + + # Merge with primary data + result_df = result_df.merge( + comp_df, left_on="sample_id", right_on="_join_sample_id", how="left" + ) + + # Drop the temporary join column + result_df = result_df.drop(columns=["_join_sample_id"]) + + except Exception: + # If enrichment fails for this comparative dataset, continue + continue + + return result_df + + @staticmethod + def _parse_composite_identifier(composite_id: str) -> tuple[str, str, str]: + """ + Parse composite sample identifier into components. + + :param composite_id: Composite ID in format "repo_id;config_name;sample_id" + :return: Tuple of (repo_id, config_name, sample_id) + + Example: + _parse_composite_identifier( + "BrentLab/harbison_2004;harbison_2004;sample_42" + ) + Returns: ("BrentLab/harbison_2004", "harbison_2004", "sample_42") + + """ + parts = composite_id.split(";") + if len(parts) != 3: + raise ValueError( + f"Invalid composite ID format: {composite_id}. " + "Expected 'repo_id;config_name;sample_id'" + ) + return parts[0], parts[1], parts[2] + def _build_metadata_table( self, repo_id: str, config_name: str, use_cache: bool = True ) -> pd.DataFrame: """ Build metadata table for a single dataset. - Extracts sample-level metadata from experimental conditions hierarchy - and field definitions, with normalization and missing value handling. + Extracts sample-level metadata from experimental conditions hierarchy and field + definitions, with normalization and missing value handling. :param repo_id: Repository ID :param config_name: Configuration name @@ -485,17 +929,31 @@ def _build_metadata_table( return pd.DataFrame() # Extract repo/config-level metadata - repo_metadata = self._extract_repo_level(card, config_name, property_mappings) + repo_metadata = self._extract_repo_level( + card, config_name, property_mappings + ) # Extract field-level metadata - field_metadata = self._extract_field_level(card, config_name, property_mappings) + field_metadata = self._extract_field_level( + card, config_name, property_mappings + ) # Get sample-level data from HuggingFace config = card.get_config(config_name) + + # Check if this is a comparative dataset + from tfbpapi.models import DatasetType + + is_comparative = ( + config + and hasattr(config, "dataset_type") + and config.dataset_type == DatasetType.COMPARATIVE + ) + if config and hasattr(config, "metadata_fields") and config.metadata_fields: # Select only metadata fields columns = ", ".join(config.metadata_fields) - if "sample_id" not in config.metadata_fields: + if not is_comparative and "sample_id" not in config.metadata_fields: columns = f"sample_id, {columns}" sql = f"SELECT DISTINCT {columns} FROM {config_name}" else: @@ -504,8 +962,9 @@ def _build_metadata_table( df = cache_mgr.query(sql, config_name) - # One row per sample_id - if "sample_id" in df.columns: + # For non-comparative datasets: one row per sample_id + # For comparative datasets: keep all rows (each row is a relationship) + if not is_comparative and "sample_id" in df.columns: df = df.groupby("sample_id").first().reset_index() # Add repo-level metadata as columns @@ -517,6 +976,9 @@ def _build_metadata_table( if field_metadata: df = self._add_field_metadata(df, field_metadata) + # Apply dtype conversions to DataFrame columns + df = self._apply_column_dtypes(df, property_mappings) + # Cache result if use_cache: self.cache[cache_key] = df @@ -524,9 +986,72 @@ def _build_metadata_table( return df except Exception as e: + # Log error for debugging with full traceback + import traceback + + print(f"Error downloading metadata for {config_name}: {e}") + traceback.print_exc() # Return empty DataFrame on error return pd.DataFrame() + def _apply_column_dtypes( + self, df: pd.DataFrame, property_mappings: dict[str, PropertyMapping] + ) -> pd.DataFrame: + """ + Apply dtype conversions to DataFrame columns based on property mappings. + + :param df: DataFrame to apply conversions to + :param property_mappings: Property mappings with dtype specifications + :return: DataFrame with converted column dtypes + + """ + for prop_name, mapping in property_mappings.items(): + # Skip if no dtype specified or column doesn't exist + if not mapping.dtype or prop_name not in df.columns: + continue + + # Convert column dtype + try: + if mapping.dtype == "numeric": + df[prop_name] = pd.to_numeric(df[prop_name], errors="coerce") + elif mapping.dtype == "bool": + df[prop_name] = df[prop_name].astype(bool) + elif mapping.dtype == "string": + df[prop_name] = df[prop_name].astype(str) + except (ValueError, TypeError): + # Conversion failed, leave as is + pass + + return df + + def _convert_dtype(self, value: Any, dtype: str) -> Any: + """ + Convert value to specified data type. + + :param value: The value to convert to a given `dtype` + :param dtype: Target data type ("numeric", "bool", "string") + + :return: Converted value or None if conversion fails + + """ + if value is None: + return None + + try: + if dtype == "numeric": + # Try float first (handles both int and float) + return float(value) + elif dtype == "bool": + return bool(value) + elif dtype == "string": + return str(value) + else: + # Unknown dtype, pass through unchanged + return value + except (ValueError, TypeError): + # Conversion failed, return None + return None + def _extract_repo_level( self, card: DataCard, @@ -560,10 +1085,12 @@ def _extract_repo_level( continue # Build full path - full_path = f"experimental_conditions.{mapping.path}" + # Note: `conditions` is already the experimental_conditions dict, + # so we don't add the prefix + full_path = mapping.path # Get value at path - value = get_nested_value(conditions, full_path) + value = get_nested_value(conditions, full_path) # type: ignore # Handle missing values missing_label = self.config.missing_value_labels.get(prop_name) @@ -575,6 +1102,12 @@ def _extract_repo_level( # Ensure value is a list actual_values = [value] if not isinstance(value, list) else value + # Apply dtype conversion if specified + if mapping.dtype: + actual_values = [ + self._convert_dtype(v, mapping.dtype) for v in actual_values + ] + # Normalize using aliases aliases = self.config.factor_aliases.get(prop_name) normalized_values = [ @@ -603,16 +1136,18 @@ def _extract_field_level( field_metadata: dict[str, dict[str, Any]] = {} # Group property mappings by field - field_mappings: dict[str, dict[str, str]] = {} + field_mappings: dict[str, dict[str, PropertyMapping]] = {} for prop_name, mapping in property_mappings.items(): - if mapping.field is not None: + # Only process if field is specified AND path exists + # (no path means it's just a column alias, not metadata extraction) + if mapping.field is not None and mapping.path is not None: field_name = mapping.field if field_name not in field_mappings: field_mappings[field_name] = {} - field_mappings[field_name][prop_name] = mapping.path + field_mappings[field_name][prop_name] = mapping # Process each field that has mappings - for field_name, prop_paths in field_mappings.items(): + for field_name, prop_mappings_dict in field_mappings.items(): # Get field definitions definitions = card.get_field_definitions(config_name, field_name) if not definitions: @@ -623,9 +1158,9 @@ def _extract_field_level( if field_value not in field_metadata: field_metadata[field_value] = {} - for prop_name, path in prop_paths.items(): + for prop_name, mapping in prop_mappings_dict.items(): # Get value at path - value = get_nested_value(definition, path) + value = get_nested_value(definition, mapping.path) # type: ignore # Handle missing values missing_label = self.config.missing_value_labels.get(prop_name) @@ -637,10 +1172,17 @@ def _extract_field_level( # Ensure value is a list actual_values = [value] if not isinstance(value, list) else value + # Apply dtype conversion if specified + if mapping.dtype: + actual_values = [ + self._convert_dtype(v, mapping.dtype) for v in actual_values + ] + # Normalize using aliases aliases = self.config.factor_aliases.get(prop_name) normalized_values = [ - normalize_value(v, aliases, missing_label) for v in actual_values + normalize_value(v, aliases, missing_label) + for v in actual_values ] field_metadata[field_value][prop_name] = normalized_values @@ -702,7 +1244,9 @@ def _apply_filters( if isinstance(filter_value, tuple): operator = filter_value[0] if operator == "between" and len(filter_value) == 3: - df = df[(df[field] >= filter_value[1]) & (df[field] <= filter_value[2])] + df = df[ + (df[field] >= filter_value[1]) & (df[field] <= filter_value[2]) + ] elif operator in (">=", ">", "<=", "<", "==", "!="): if operator == ">=": df = df[df[field] >= filter_value[1]] @@ -770,9 +1314,14 @@ def _get_complete_data( # Merge with metadata (metadata_df has normalized fields) # Drop metadata columns from full_df to avoid duplicates metadata_cols = [ - col for col in metadata_df.columns if col not in ["sample_id", "dataset_id"] + col + for col in metadata_df.columns + if col not in ["sample_id", "dataset_id"] ] - full_df = full_df.drop(columns=[c for c in metadata_cols if c in full_df.columns], errors="ignore") + full_df = full_df.drop( + columns=[c for c in metadata_cols if c in full_df.columns], + errors="ignore", + ) # Merge on sample_id result = full_df.merge(metadata_df, on="sample_id", how="left")